source: mainline/uspace/srv/loader/main.c@ 012dd8e

Last change on this file since 012dd8e was 012dd8e, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

taskman: Handle INIT_TASKS as tasks spawned by loader

  • everyone is connected to its spawner, except for INIT_TASKS, they are connected to taskman (first binary)
  • taskman is now aware even of INIT_TASKS and taskman itself
  • refactored taskman handshake — NS session is created lazily
  • refactored async.c with usage of create_session
  • changed EINVAL to EINTR on lost waits
  • removed TODOs from taskman and related libc TODOs

Conflicts:

abi/include/abi/ipc/methods.h
boot/Makefile.common
uspace/lib/c/generic/async.c
uspace/lib/c/generic/libc.c
uspace/lib/c/generic/loader.c
uspace/lib/c/generic/ns.c
uspace/lib/c/generic/private/async.h
uspace/lib/c/generic/private/taskman.h
uspace/lib/c/generic/task.c
uspace/lib/c/include/async.h
uspace/lib/c/include/task.h
uspace/srv/loader/main.c
uspace/srv/ns/ns.c

  • Property mode set to 100644
File size: 9.8 KB
Line 
1/*
2 * Copyright (c) 2008 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup loader
30 * @{
31 */
32/**
33 * @file
34 *
35 * The program loader is a special init binary. Its image is used
36 * to create a new task upon a @c task_spawn syscall. It has a phone connected
37 * to the caller of the syscall. The formal caller (taskman) performs a
38 * handshake with loader so that apparent caller can communicate with the
39 * loader.
40 *
41 * The apparent caller uses his phone to send the pathname and various other
42 * information to the loader. This is normally done by the C library
43 * and completely hidden from applications.
44 */
45
46
47#include <stdio.h>
48#include <stdlib.h>
49#include <stdbool.h>
50#include <stddef.h>
51#include <ipc/services.h>
52#include <as.h>
53#include <async.h>
54#include <elf/elf.h>
55#include <elf/elf_load.h>
56#include <entry_point.h>
57#include <errno.h>
58#include <fcntl.h>
59#include <fibril_synch.h>
60#include <ipc/loader.h>
61#include <loader/pcb.h>
62#include <ns.h>
63#include <str.h>
64#include <sys/types.h>
65#include <task.h>
66#include <taskman.h>
67#include <unistd.h>
68#include <vfs/vfs.h>
69#include <vfs/inbox.h>
70#include <libc.h>
71
72#ifdef CONFIG_RTLD
73#include <rtld/rtld.h>
74#endif
75
76#define NAME "loader"
77#define DPRINTF(...) ((void) 0)
78
79/** File that will be loaded */
80static char *progname = NULL;
81static int program_fd = -1;
82
83/** The Program control block */
84static pcb_t pcb;
85
86/** Current working directory */
87static char *cwd = NULL;
88
89/** Number of arguments */
90static int argc = 0;
91/** Argument vector */
92static char **argv = NULL;
93/** Buffer holding all arguments */
94static char *arg_buf = NULL;
95
96/** Inbox entries. */
97static struct pcb_inbox_entry inbox[INBOX_MAX_ENTRIES];
98static int inbox_entries = 0;
99
100static elf_info_t prog_info;
101
102/** Used to limit number of connections to one. */
103static bool connected = false;
104
105static void ldr_get_taskid(ipc_call_t *req)
106{
107 ipc_call_t call;
108 task_id_t task_id;
109 size_t len;
110
111 task_id = task_get_id();
112
113 if (!async_data_read_receive(&call, &len)) {
114 async_answer_0(&call, EINVAL);
115 async_answer_0(req, EINVAL);
116 return;
117 }
118
119 if (len > sizeof(task_id))
120 len = sizeof(task_id);
121
122 DPRINTF("LOADER_GET_TASKID() = %lu\n", (unsigned long) task_id);
123 async_data_read_finalize(&call, &task_id, len);
124 async_answer_0(req, EOK);
125}
126
127/** Receive a call setting the current working directory.
128 *
129 */
130static void ldr_set_cwd(ipc_call_t *req)
131{
132 char *buf;
133 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
134
135 if (rc == EOK) {
136 if (cwd != NULL)
137 free(cwd);
138
139 cwd = buf;
140 }
141
142 DPRINTF("LOADER_SET_CWD('%s')\n", cwd);
143 async_answer_0(req, rc);
144}
145
146/** Receive a call setting the program to execute.
147 *
148 */
149static void ldr_set_program(ipc_call_t *req)
150{
151 ipc_call_t call;
152 size_t namesize;
153 if (!async_data_write_receive(&call, &namesize)) {
154 async_answer_0(req, EINVAL);
155 return;
156 }
157
158 char *name = malloc(namesize);
159 // FIXME: check return value
160
161 errno_t rc = async_data_write_finalize(&call, name, namesize);
162 if (rc != EOK) {
163 async_answer_0(req, EINVAL);
164 return;
165 }
166
167 int file;
168 rc = vfs_receive_handle(true, &file);
169 if (rc != EOK) {
170 async_answer_0(req, EINVAL);
171 return;
172 }
173
174 DPRINTF("LOADER_SET_PROGRAM('%s')\n", name);
175
176 progname = name;
177 program_fd = file;
178 async_answer_0(req, EOK);
179}
180
181/** Receive a call setting arguments of the program to execute.
182 *
183 */
184static void ldr_set_args(ipc_call_t *req)
185{
186 char *buf;
187 size_t buf_size;
188 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
189
190 if (rc == EOK) {
191 /*
192 * Count number of arguments
193 */
194 char *cur = buf;
195 int count = 0;
196
197 while (cur < buf + buf_size) {
198 size_t arg_size = str_size(cur);
199 cur += arg_size + 1;
200 count++;
201 }
202
203 /*
204 * Allocate new argv
205 */
206 char **_argv = (char **) malloc((count + 1) * sizeof(char *));
207 if (_argv == NULL) {
208 free(buf);
209 async_answer_0(req, ENOMEM);
210 return;
211 }
212
213 /*
214 * Fill the new argv with argument pointers
215 */
216 cur = buf;
217 count = 0;
218 while (cur < buf + buf_size) {
219 _argv[count] = cur;
220
221 size_t arg_size = str_size(cur);
222 cur += arg_size + 1;
223 count++;
224 }
225 _argv[count] = NULL;
226
227 /*
228 * Copy temporary data to global variables
229 */
230 if (arg_buf != NULL)
231 free(arg_buf);
232
233 if (argv != NULL)
234 free(argv);
235
236 for (int i = 0; i < count; i++)
237 DPRINTF("LOADER_SET_ARGS('%s')\n", _argv[i]);
238
239 argc = count;
240 arg_buf = buf;
241 argv = _argv;
242 }
243
244 async_answer_0(req, rc);
245}
246
247/** Receive a call setting inbox files of the program to execute.
248 *
249 */
250static void ldr_add_inbox(ipc_call_t *req)
251{
252 if (inbox_entries == INBOX_MAX_ENTRIES) {
253 async_answer_0(req, ERANGE);
254 return;
255 }
256
257 ipc_call_t call;
258 size_t namesize;
259 if (!async_data_write_receive(&call, &namesize)) {
260 async_answer_0(req, EINVAL);
261 return;
262 }
263
264 char *name = malloc(namesize);
265 errno_t rc = async_data_write_finalize(&call, name, namesize);
266 if (rc != EOK) {
267 async_answer_0(req, EINVAL);
268 return;
269 }
270
271 int file;
272 rc = vfs_receive_handle(true, &file);
273 if (rc != EOK) {
274 async_answer_0(req, EINVAL);
275 return;
276 }
277
278 DPRINTF("LOADER_ADD_INBOX('%s')\n", name);
279
280 /*
281 * We need to set the root early for dynamically linked binaries so
282 * that the loader can use it too.
283 */
284 if (str_cmp(name, "root") == 0)
285 vfs_root_set(file);
286
287 inbox[inbox_entries].name = name;
288 inbox[inbox_entries].file = file;
289 inbox_entries++;
290 async_answer_0(req, EOK);
291}
292
293/** Load the previously selected program.
294 *
295 * @return 0 on success, !0 on error.
296 *
297 */
298static int ldr_load(ipc_call_t *req)
299{
300 DPRINTF("LOADER_LOAD()\n");
301
302 errno_t rc = elf_load(program_fd, &prog_info);
303 if (rc != EOK) {
304 DPRINTF("Failed to load executable for '%s'.\n", progname);
305 async_answer_0(req, EINVAL);
306 return 1;
307 }
308
309 DPRINTF("Loaded.\n");
310
311#ifdef CONFIG_RTLD
312 if (prog_info.env) {
313 pcb.tcb = rtld_tls_make(prog_info.env);
314 } else {
315 pcb.tcb = tls_make(prog_info.finfo.base);
316 }
317#else
318 pcb.tcb = tls_make(prog_info.finfo.base);
319#endif
320
321 if (!pcb.tcb) {
322 DPRINTF("Failed to make TLS for '%s'.\n", progname);
323 async_answer_0(req, ENOMEM);
324 return 1;
325 }
326
327 elf_set_pcb(&prog_info, &pcb);
328
329 DPRINTF("PCB set.\n");
330
331 pcb.session_taskman = taskman_get_session();
332
333 pcb.cwd = cwd;
334
335 pcb.argc = argc;
336 pcb.argv = argv;
337
338 pcb.inbox = inbox;
339 pcb.inbox_entries = inbox_entries;
340
341 DPRINTF("Answering.\n");
342 async_answer_0(req, EOK);
343 return 0;
344}
345
346/** Run the previously loaded program.
347 *
348 * @return 0 on success, !0 on error.
349 *
350 */
351static __attribute__((noreturn)) void ldr_run(ipc_call_t *req)
352{
353 DPRINTF("Set task name\n");
354
355 /* Set the task name. */
356 task_set_name(progname);
357
358 /* Run program */
359 DPRINTF("Reply OK\n");
360 async_answer_0(req, EOK);
361
362 /*
363 * Wait for the hangup from the other side in order not to leave any
364 * unanswered IPC_M_PHONE_HUNGUP messages behind.
365 */
366 async_get_call(req);
367 assert(!ipc_get_imethod(req));
368 async_answer_0(req, EOK);
369
370 DPRINTF("Jump to entry point at %p\n", pcb.entry);
371
372 __libc_fini();
373 __tcb_reset();
374 entry_point_jmp(prog_info.finfo.entry, &pcb);
375
376 /* Not reached */
377}
378
379/** Handle loader connection.
380 *
381 * Receive and carry out commands (of which the last one should be
382 * to execute the loaded program).
383 *
384 */
385static void ldr_connection(ipc_call_t *icall, void *arg)
386{
387 /* Already have a connection? */
388 if (connected) {
389 async_answer_0(icall, ELIMIT);
390 return;
391 }
392
393 connected = true;
394
395 /* Accept the connection */
396 async_accept_0(icall);
397
398 /* Ignore parameters, the connection is already open */
399 (void) icall;
400
401 while (true) {
402 errno_t retval;
403 ipc_call_t call;
404 async_get_call(&call);
405
406 if (!ipc_get_imethod(&call)) {
407 async_answer_0(&call, EOK);
408 exit(0);
409 }
410
411 switch (ipc_get_imethod(&call)) {
412 case LOADER_GET_TASKID:
413 ldr_get_taskid(&call);
414 continue;
415 case LOADER_SET_CWD:
416 ldr_set_cwd(&call);
417 continue;
418 case LOADER_SET_PROGRAM:
419 ldr_set_program(&call);
420 continue;
421 case LOADER_SET_ARGS:
422 ldr_set_args(&call);
423 continue;
424 case LOADER_ADD_INBOX:
425 ldr_add_inbox(&call);
426 continue;
427 case LOADER_LOAD:
428 ldr_load(&call);
429 continue;
430 case LOADER_RUN:
431 ldr_run(&call);
432 /* Not reached */
433 default:
434 retval = EINVAL;
435 break;
436 }
437
438 async_answer_0(&call, retval);
439 }
440}
441
442/** Program loader main function.
443 */
444int main(int argc, char *argv[])
445{
446 /* Set a handler of incomming connections. */
447 async_set_fallback_port_handler(ldr_connection, NULL);
448
449 /* Announce to taskman. */
450 errno_t rc = taskman_intro_loader();
451 if (rc != EOK) {
452 printf("%s: did not receive connectin from taskman (%i)\n",
453 NAME, rc);
454 return rc;
455 }
456
457 /*
458 * We are not a regular server, thus no retval is set, just wait for
459 * forwarded connections by taskman.
460 */
461 async_manager();
462
463 /* Never reached */
464 return 0;
465}
466
467/** @}
468 */
Note: See TracBrowser for help on using the repository browser.