source: mainline/uspace/srv/loader/main.c@ 1be7bee

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

sysman: Move task retval and waiting logic to taskman (partially)

  • two important sessions: NS and taskman
  • depending on boot task vs spawned task those sessions are initiated differently

Conflicts:

uspace/lib/c/generic/async.c
uspace/lib/c/generic/libc.c
uspace/lib/c/generic/task.c
uspace/lib/c/include/ipc/ns.h
uspace/lib/c/include/task.h
uspace/lib/posix/source/sys/wait.c
uspace/srv/loader/main.c
uspace/srv/ns/ns.c

  • Property mode set to 100644
File size: 11.0 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 te 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 <str.h>
63#include <sys/types.h>
64#include <taskman.h>
65#include <unistd.h>
66#include <vfs/vfs.h>
67#include <vfs/inbox.h>
68#include <libc.h>
69
70#ifdef CONFIG_RTLD
71#include <rtld/rtld.h>
72#endif
73
74#define DPRINTF(...) ((void) 0)
75
76/** File that will be loaded */
77static char *progname = NULL;
78static int program_fd = -1;
79
80/** The Program control block */
81static pcb_t pcb;
82
83/** Primary IPC session */
84static async_sess_t *session_primary = NULL;
85
86/** Session to taskman (typically our spawner) */
87static async_sess_t *session_taskman = NULL;
88
89/** Current working directory */
90static char *cwd = NULL;
91
92/** Number of arguments */
93static int argc = 0;
94/** Argument vector */
95static char **argv = NULL;
96/** Buffer holding all arguments */
97static char *arg_buf = NULL;
98
99/** Inbox entries. */
100static struct pcb_inbox_entry inbox[INBOX_MAX_ENTRIES];
101static int inbox_entries = 0;
102
103static elf_info_t prog_info;
104
105/** Used to limit number of connections to one. */
106static bool connected = false;
107
108/** Ensure synchronization of handshake and connection fibrils. */
109static bool handshake_complete = false;
110FIBRIL_MUTEX_INITIALIZE(handshake_mtx);
111FIBRIL_CONDVAR_INITIALIZE(handshake_cv);
112
113static void ldr_get_taskid(ipc_call_t *req)
114{
115 ipc_call_t call;
116 task_id_t task_id;
117 size_t len;
118
119 task_id = task_get_id();
120
121 if (!async_data_read_receive(&call, &len)) {
122 async_answer_0(&call, EINVAL);
123 async_answer_0(req, EINVAL);
124 return;
125 }
126
127 if (len > sizeof(task_id))
128 len = sizeof(task_id);
129
130 DPRINTF("LOADER_GET_TASKID() = %lu\n", (unsigned long) task_id);
131 async_data_read_finalize(&call, &task_id, len);
132 async_answer_0(req, EOK);
133}
134
135/** Receive a call setting the current working directory.
136 *
137 */
138static void ldr_set_cwd(ipc_call_t *req)
139{
140 char *buf;
141 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
142
143 if (rc == EOK) {
144 if (cwd != NULL)
145 free(cwd);
146
147 cwd = buf;
148 }
149
150 DPRINTF("LOADER_SET_CWD('%s')\n", cwd);
151 async_answer_0(req, rc);
152}
153
154/** Receive a call setting the program to execute.
155 *
156 */
157static void ldr_set_program(ipc_call_t *req)
158{
159 ipc_call_t call;
160 size_t namesize;
161 if (!async_data_write_receive(&call, &namesize)) {
162 async_answer_0(req, EINVAL);
163 return;
164 }
165
166 char *name = malloc(namesize);
167 // FIXME: check return value
168
169 errno_t rc = async_data_write_finalize(&call, name, namesize);
170 if (rc != EOK) {
171 async_answer_0(req, EINVAL);
172 return;
173 }
174
175 int file;
176 rc = vfs_receive_handle(true, &file);
177 if (rc != EOK) {
178 async_answer_0(req, EINVAL);
179 return;
180 }
181
182 DPRINTF("LOADER_SET_PROGRAM('%s')\n", name);
183
184 progname = name;
185 program_fd = file;
186 async_answer_0(req, EOK);
187}
188
189/** Receive a call setting arguments of the program to execute.
190 *
191 */
192static void ldr_set_args(ipc_call_t *req)
193{
194 char *buf;
195 size_t buf_size;
196 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
197
198 if (rc == EOK) {
199 /*
200 * Count number of arguments
201 */
202 char *cur = buf;
203 int count = 0;
204
205 while (cur < buf + buf_size) {
206 size_t arg_size = str_size(cur);
207 cur += arg_size + 1;
208 count++;
209 }
210
211 /*
212 * Allocate new argv
213 */
214 char **_argv = (char **) malloc((count + 1) * sizeof(char *));
215 if (_argv == NULL) {
216 free(buf);
217 async_answer_0(req, ENOMEM);
218 return;
219 }
220
221 /*
222 * Fill the new argv with argument pointers
223 */
224 cur = buf;
225 count = 0;
226 while (cur < buf + buf_size) {
227 _argv[count] = cur;
228
229 size_t arg_size = str_size(cur);
230 cur += arg_size + 1;
231 count++;
232 }
233 _argv[count] = NULL;
234
235 /*
236 * Copy temporary data to global variables
237 */
238 if (arg_buf != NULL)
239 free(arg_buf);
240
241 if (argv != NULL)
242 free(argv);
243
244 for (int i = 0; i < count; i++)
245 DPRINTF("LOADER_SET_ARGS('%s')\n", _argv[i]);
246
247 argc = count;
248 arg_buf = buf;
249 argv = _argv;
250 }
251
252 async_answer_0(req, rc);
253}
254
255/** Receive a call setting inbox files of the program to execute.
256 *
257 */
258static void ldr_add_inbox(ipc_call_t *req)
259{
260 if (inbox_entries == INBOX_MAX_ENTRIES) {
261 async_answer_0(req, ERANGE);
262 return;
263 }
264
265 ipc_call_t call;
266 size_t namesize;
267 if (!async_data_write_receive(&call, &namesize)) {
268 async_answer_0(req, EINVAL);
269 return;
270 }
271
272 char *name = malloc(namesize);
273 errno_t rc = async_data_write_finalize(&call, name, namesize);
274 if (rc != EOK) {
275 async_answer_0(req, EINVAL);
276 return;
277 }
278
279 int file;
280 rc = vfs_receive_handle(true, &file);
281 if (rc != EOK) {
282 async_answer_0(req, EINVAL);
283 return;
284 }
285
286 DPRINTF("LOADER_ADD_INBOX('%s')\n", name);
287
288 /*
289 * We need to set the root early for dynamically linked binaries so
290 * that the loader can use it too.
291 */
292 if (str_cmp(name, "root") == 0)
293 vfs_root_set(file);
294
295 inbox[inbox_entries].name = name;
296 inbox[inbox_entries].file = file;
297 inbox_entries++;
298 async_answer_0(req, EOK);
299}
300
301/** Load the previously selected program.
302 *
303 * @return 0 on success, !0 on error.
304 *
305 */
306static int ldr_load(ipc_call_t *req)
307{
308 DPRINTF("LOADER_LOAD()\n");
309
310 errno_t rc = elf_load(program_fd, &prog_info);
311 if (rc != EOK) {
312 DPRINTF("Failed to load executable for '%s'.\n", progname);
313 async_answer_0(req, EINVAL);
314 return 1;
315 }
316
317 DPRINTF("Loaded.\n");
318
319#ifdef CONFIG_RTLD
320 if (prog_info.env) {
321 pcb.tcb = rtld_tls_make(prog_info.env);
322 } else {
323 pcb.tcb = tls_make(prog_info.finfo.base);
324 }
325#else
326 pcb.tcb = tls_make(prog_info.finfo.base);
327#endif
328
329 if (!pcb.tcb) {
330 DPRINTF("Failed to make TLS for '%s'.\n", progname);
331 async_answer_0(req, ENOMEM);
332 return 1;
333 }
334
335 elf_set_pcb(&prog_info, &pcb);
336
337 DPRINTF("PCB set.\n");
338
339 pcb.session_primary = session_primary;
340 pcb.session_taskman = session_taskman;
341
342 pcb.cwd = cwd;
343
344 pcb.argc = argc;
345 pcb.argv = argv;
346
347 pcb.inbox = inbox;
348 pcb.inbox_entries = inbox_entries;
349
350 DPRINTF("Answering.\n");
351 async_answer_0(req, EOK);
352 return 0;
353}
354
355/** Run the previously loaded program.
356 *
357 * @return 0 on success, !0 on error.
358 *
359 */
360static __attribute__((noreturn)) void ldr_run(ipc_call_t *req)
361{
362 DPRINTF("Set task name\n");
363
364 /* Set the task name. */
365 task_set_name(progname);
366
367 /* Run program */
368 DPRINTF("Reply OK\n");
369 async_answer_0(req, EOK);
370
371 /*
372 * Wait for the hangup from the other side in order not to leave any
373 * unanswered IPC_M_PHONE_HUNGUP messages behind.
374 */
375 async_get_call(req);
376 assert(!ipc_get_imethod(req));
377 async_answer_0(req, EOK);
378
379 DPRINTF("Jump to entry point at %p\n", pcb.entry);
380
381 __libc_fini();
382 __tcb_reset();
383 entry_point_jmp(prog_info.finfo.entry, &pcb);
384
385 /* Not reached */
386}
387
388/** Handle loader connection.
389 *
390 * Receive and carry out commands (of which the last one should be
391 * to execute the loaded program).
392 *
393 */
394static void ldr_connection(ipc_call_t *icall, void *arg)
395{
396 /* Wait for handshake */
397 fibril_mutex_lock(&handshake_mtx);
398 while (!handshake_complete) {
399 fibril_condvar_wait(&handshake_cv, &handshake_mtx);
400 }
401 fibril_mutex_unlock(&handshake_mtx);
402
403 /* Already have a connection? */
404 if (connected) {
405 async_answer_0(icall, ELIMIT);
406 return;
407 }
408
409 connected = true;
410
411 /* Accept the connection */
412 async_accept_0(icall);
413
414 /* Ignore parameters, the connection is already open */
415 (void) icall;
416
417 while (true) {
418 errno_t retval;
419 ipc_call_t call;
420 async_get_call(&call);
421
422 if (!ipc_get_imethod(&call)) {
423 async_answer_0(&call, EOK);
424 exit(0);
425 }
426
427 switch (ipc_get_imethod(&call)) {
428 case LOADER_GET_TASKID:
429 ldr_get_taskid(&call);
430 continue;
431 case LOADER_SET_CWD:
432 ldr_set_cwd(&call);
433 continue;
434 case LOADER_SET_PROGRAM:
435 ldr_set_program(&call);
436 continue;
437 case LOADER_SET_ARGS:
438 ldr_set_args(&call);
439 continue;
440 case LOADER_ADD_INBOX:
441 ldr_add_inbox(&call);
442 continue;
443 case LOADER_LOAD:
444 ldr_load(&call);
445 continue;
446 case LOADER_RUN:
447 ldr_run(&call);
448 /* Not reached */
449 default:
450 retval = EINVAL;
451 break;
452 }
453
454 async_answer_0(&call, retval);
455 }
456}
457
458/** Handshake with taskman
459 *
460 * Taskman is our spawn parent, i.e. PHONE_INITIAL is connected to it.
461 * Goal of the handshake is to obtain phone to naming service and also keep the
462 * session to taskman.
463 *
464 * @return EOK on success, for errors see taskman_handshake()
465 */
466static errno_t ldr_taskman_handshake(void)
467{
468 assert(session_primary == NULL);
469 assert(session_taskman == NULL);
470
471 errno_t retval = EOK;
472
473 fibril_mutex_lock(&handshake_mtx);
474 session_primary = taskman_handshake();
475 if (session_primary == NULL) {
476 retval = errno;
477 goto finish;
478 }
479
480 session_taskman = async_session_primary_swap(session_primary);
481
482 handshake_complete = true;
483
484finish:
485 fibril_condvar_signal(&handshake_cv);
486 fibril_mutex_unlock(&handshake_mtx);
487
488 return retval;
489}
490
491/** Program loader main function.
492 */
493int main(int argc, char *argv[])
494{
495 /* Set a handler of incomming connections. */
496 async_set_fallback_port_handler(ldr_connection, NULL);
497
498 /* Handshake with taskman */
499 int rc = ldr_taskman_handshake();
500 if (rc != EOK) {
501 DPRINTF("Failed taskman handshake (%i).\n", errno);
502 return rc;
503 }
504
505 /* Handle client connections */
506 async_manager();
507 //TODO retval?
508
509 /* Never reached */
510 return 0;
511}
512
513/** @}
514 */
Note: See TracBrowser for help on using the repository browser.