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
RevLine 
[c98e6ee]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 * @{
[2f57690]31 */
[c98e6ee]32/**
33 * @file
34 *
35 * The program loader is a special init binary. Its image is used
[0a8f070]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.
[c98e6ee]40 *
[0a8f070]41 * The apparent caller uses his phone to send the pathname and various other
[c98e6ee]42 * information to the loader. This is normally done by the C library
43 * and completely hidden from applications.
44 */
45
[0a8f070]46
[c98e6ee]47#include <stdio.h>
48#include <stdlib.h>
[3e6a98c5]49#include <stdbool.h>
[8d2dd7f2]50#include <stddef.h>
[bfd1546]51#include <ipc/services.h>
[c98e6ee]52#include <as.h>
[0a8f070]53#include <async.h>
[90b8d58]54#include <elf/elf.h>
[bfdb5af1]55#include <elf/elf_load.h>
[0a8f070]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>
[7171760]66#include <vfs/vfs.h>
[bb9ec2d]67#include <vfs/inbox.h>
[25f6bddb]68#include <libc.h>
[c98e6ee]69
[40abf56]70#ifdef CONFIG_RTLD
71#include <rtld/rtld.h>
72#endif
73
[e3787a0]74#define DPRINTF(...) ((void) 0)
[1ea99cc]75
[bb9ec2d]76/** File that will be loaded */
77static char *progname = NULL;
78static int program_fd = -1;
[c98e6ee]79
80/** The Program control block */
81static pcb_t pcb;
82
[0a8f070]83/** Primary IPC session */
84static async_sess_t *session_primary = NULL;
85
[1be7bee]86/** Session to taskman (typically our spawner) */
87static async_sess_t *session_taskman = NULL;
88
[622cdbe]89/** Current working directory */
90static char *cwd = NULL;
91
[c98e6ee]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
[bb9ec2d]99/** Inbox entries. */
100static struct pcb_inbox_entry inbox[INBOX_MAX_ENTRIES];
101static int inbox_entries = 0;
[bbdbf86]102
[4470e26]103static elf_info_t prog_info;
104
[bfd1546]105/** Used to limit number of connections to one. */
[007e6efa]106static bool connected = false;
[4470e26]107
[0a8f070]108/** Ensure synchronization of handshake and connection fibrils. */
109static bool handshake_complete = false;
110FIBRIL_MUTEX_INITIALIZE(handshake_mtx);
111FIBRIL_CONDVAR_INITIALIZE(handshake_cv);
112
[984a9ba]113static void ldr_get_taskid(ipc_call_t *req)
[47e0a05b]114{
[984a9ba]115 ipc_call_t call;
[47e0a05b]116 task_id_t task_id;
117 size_t len;
[a35b458]118
[47e0a05b]119 task_id = task_get_id();
[a35b458]120
[984a9ba]121 if (!async_data_read_receive(&call, &len)) {
122 async_answer_0(&call, EINVAL);
123 async_answer_0(req, EINVAL);
[47e0a05b]124 return;
125 }
[a35b458]126
[2f57690]127 if (len > sizeof(task_id))
128 len = sizeof(task_id);
[a35b458]129
[e3787a0]130 DPRINTF("LOADER_GET_TASKID() = %lu\n", (unsigned long) task_id);
[984a9ba]131 async_data_read_finalize(&call, &task_id, len);
132 async_answer_0(req, EOK);
[47e0a05b]133}
134
[622cdbe]135/** Receive a call setting the current working directory.
[c98e6ee]136 *
137 */
[984a9ba]138static void ldr_set_cwd(ipc_call_t *req)
[c98e6ee]139{
[472c09d]140 char *buf;
[b7fd2a0]141 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
[a35b458]142
[472c09d]143 if (rc == EOK) {
144 if (cwd != NULL)
145 free(cwd);
[a35b458]146
[472c09d]147 cwd = buf;
[c98e6ee]148 }
[a35b458]149
[e3787a0]150 DPRINTF("LOADER_SET_CWD('%s')\n", cwd);
[984a9ba]151 async_answer_0(req, rc);
[622cdbe]152}
[47e0a05b]153
[bb9ec2d]154/** Receive a call setting the program to execute.
[c98e6ee]155 *
156 */
[984a9ba]157static void ldr_set_program(ipc_call_t *req)
[c98e6ee]158{
[984a9ba]159 ipc_call_t call;
[bb9ec2d]160 size_t namesize;
[984a9ba]161 if (!async_data_write_receive(&call, &namesize)) {
162 async_answer_0(req, EINVAL);
[bb9ec2d]163 return;
164 }
165
[3bacee1]166 char *name = malloc(namesize);
[984a9ba]167 // FIXME: check return value
168
169 errno_t rc = async_data_write_finalize(&call, name, namesize);
[bb9ec2d]170 if (rc != EOK) {
[984a9ba]171 async_answer_0(req, EINVAL);
[bb9ec2d]172 return;
173 }
174
[f77c1c9]175 int file;
176 rc = vfs_receive_handle(true, &file);
177 if (rc != EOK) {
[984a9ba]178 async_answer_0(req, EINVAL);
[bb9ec2d]179 return;
[c98e6ee]180 }
[a35b458]181
[e3787a0]182 DPRINTF("LOADER_SET_PROGRAM('%s')\n", name);
183
[bb9ec2d]184 progname = name;
185 program_fd = file;
[984a9ba]186 async_answer_0(req, EOK);
[c98e6ee]187}
188
189/** Receive a call setting arguments of the program to execute.
190 *
191 */
[984a9ba]192static void ldr_set_args(ipc_call_t *req)
[c98e6ee]193{
[472c09d]194 char *buf;
195 size_t buf_size;
[b7fd2a0]196 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
[a35b458]197
[472c09d]198 if (rc == EOK) {
199 /*
200 * Count number of arguments
201 */
202 char *cur = buf;
203 int count = 0;
[a35b458]204
[472c09d]205 while (cur < buf + buf_size) {
206 size_t arg_size = str_size(cur);
207 cur += arg_size + 1;
208 count++;
209 }
[a35b458]210
[472c09d]211 /*
212 * Allocate new argv
213 */
214 char **_argv = (char **) malloc((count + 1) * sizeof(char *));
215 if (_argv == NULL) {
216 free(buf);
[984a9ba]217 async_answer_0(req, ENOMEM);
[472c09d]218 return;
219 }
[a35b458]220
[472c09d]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;
[a35b458]228
[472c09d]229 size_t arg_size = str_size(cur);
230 cur += arg_size + 1;
231 count++;
232 }
233 _argv[count] = NULL;
[a35b458]234
[472c09d]235 /*
236 * Copy temporary data to global variables
237 */
238 if (arg_buf != NULL)
239 free(arg_buf);
[a35b458]240
[472c09d]241 if (argv != NULL)
242 free(argv);
[a35b458]243
[e3787a0]244 for (int i = 0; i < count; i++)
245 DPRINTF("LOADER_SET_ARGS('%s')\n", _argv[i]);
246
[472c09d]247 argc = count;
248 arg_buf = buf;
249 argv = _argv;
[c98e6ee]250 }
[a35b458]251
[984a9ba]252 async_answer_0(req, rc);
[c98e6ee]253}
254
[bb9ec2d]255/** Receive a call setting inbox files of the program to execute.
[bbdbf86]256 *
257 */
[984a9ba]258static void ldr_add_inbox(ipc_call_t *req)
[bbdbf86]259{
[bb9ec2d]260 if (inbox_entries == INBOX_MAX_ENTRIES) {
[984a9ba]261 async_answer_0(req, ERANGE);
[b7f69f2]262 return;
[bb9ec2d]263 }
[7171760]264
[984a9ba]265 ipc_call_t call;
[bb9ec2d]266 size_t namesize;
[984a9ba]267 if (!async_data_write_receive(&call, &namesize)) {
268 async_answer_0(req, EINVAL);
[bb9ec2d]269 return;
270 }
271
[3bacee1]272 char *name = malloc(namesize);
[984a9ba]273 errno_t rc = async_data_write_finalize(&call, name, namesize);
[bb9ec2d]274 if (rc != EOK) {
[984a9ba]275 async_answer_0(req, EINVAL);
[bb9ec2d]276 return;
277 }
278
[f77c1c9]279 int file;
280 rc = vfs_receive_handle(true, &file);
281 if (rc != EOK) {
[984a9ba]282 async_answer_0(req, EINVAL);
[bb9ec2d]283 return;
[bbdbf86]284 }
[7171760]285
[e3787a0]286 DPRINTF("LOADER_ADD_INBOX('%s')\n", name);
287
[ea56098]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
[bb9ec2d]295 inbox[inbox_entries].name = name;
296 inbox[inbox_entries].file = file;
297 inbox_entries++;
[984a9ba]298 async_answer_0(req, EOK);
[bbdbf86]299}
300
[4470e26]301/** Load the previously selected program.
[c98e6ee]302 *
303 * @return 0 on success, !0 on error.
[984a9ba]304 *
[c98e6ee]305 */
[984a9ba]306static int ldr_load(ipc_call_t *req)
[c98e6ee]307{
[e3787a0]308 DPRINTF("LOADER_LOAD()\n");
309
[bdca26a]310 errno_t rc = elf_load(program_fd, &prog_info);
311 if (rc != EOK) {
[bb9ec2d]312 DPRINTF("Failed to load executable for '%s'.\n", progname);
[984a9ba]313 async_answer_0(req, EINVAL);
[c98e6ee]314 return 1;
315 }
[a35b458]316
[e3787a0]317 DPRINTF("Loaded.\n");
318
[40abf56]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
[c74b9de]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
[17341d4]335 elf_set_pcb(&prog_info, &pcb);
[a35b458]336
[e3787a0]337 DPRINTF("PCB set.\n");
338
[0a8f070]339 pcb.session_primary = session_primary;
[1be7bee]340 pcb.session_taskman = session_taskman;
[0a8f070]341
[622cdbe]342 pcb.cwd = cwd;
[a35b458]343
[c98e6ee]344 pcb.argc = argc;
345 pcb.argv = argv;
[a35b458]346
[bb9ec2d]347 pcb.inbox = inbox;
348 pcb.inbox_entries = inbox_entries;
[a35b458]349
[e3787a0]350 DPRINTF("Answering.\n");
[984a9ba]351 async_answer_0(req, EOK);
[c98e6ee]352 return 0;
353}
354
[4470e26]355/** Run the previously loaded program.
356 *
357 * @return 0 on success, !0 on error.
[984a9ba]358 *
[4470e26]359 */
[984a9ba]360static __attribute__((noreturn)) void ldr_run(ipc_call_t *req)
[4470e26]361{
[a6dffb8]362 DPRINTF("Set task name\n");
363
[bc18d63]364 /* Set the task name. */
[bb9ec2d]365 task_set_name(progname);
[a35b458]366
[a6dffb8]367 /* Run program */
368 DPRINTF("Reply OK\n");
[984a9ba]369 async_answer_0(req, EOK);
[40abf56]370
[faf19d4]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);
[fafb8e5]376 assert(!ipc_get_imethod(req));
[faf19d4]377 async_answer_0(req, EOK);
378
[a6dffb8]379 DPRINTF("Jump to entry point at %p\n", pcb.entry);
[40abf56]380
[25f6bddb]381 __libc_fini();
[40abf56]382 __tcb_reset();
[17341d4]383 entry_point_jmp(prog_info.finfo.entry, &pcb);
[a35b458]384
[4470e26]385 /* Not reached */
386}
387
[c98e6ee]388/** Handle loader connection.
389 *
390 * Receive and carry out commands (of which the last one should be
391 * to execute the loaded program).
[984a9ba]392 *
[c98e6ee]393 */
[984a9ba]394static void ldr_connection(ipc_call_t *icall, void *arg)
[c98e6ee]395{
[0a8f070]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
[bfd1546]403 /* Already have a connection? */
404 if (connected) {
[984a9ba]405 async_answer_0(icall, ELIMIT);
[bfd1546]406 return;
407 }
[a35b458]408
[bfd1546]409 connected = true;
[a35b458]410
[bfd1546]411 /* Accept the connection */
[beb83c1]412 async_accept_0(icall);
[a35b458]413
[c98e6ee]414 /* Ignore parameters, the connection is already open */
[2f57690]415 (void) icall;
[a35b458]416
[79ae36dd]417 while (true) {
[b7fd2a0]418 errno_t retval;
[79ae36dd]419 ipc_call_t call;
[984a9ba]420 async_get_call(&call);
[a35b458]421
[fafb8e5]422 if (!ipc_get_imethod(&call)) {
[889cdb1]423 async_answer_0(&call, EOK);
[86e3d62]424 exit(0);
[889cdb1]425 }
[a35b458]426
[fafb8e5]427 switch (ipc_get_imethod(&call)) {
[47e0a05b]428 case LOADER_GET_TASKID:
[984a9ba]429 ldr_get_taskid(&call);
[47e0a05b]430 continue;
[622cdbe]431 case LOADER_SET_CWD:
[984a9ba]432 ldr_set_cwd(&call);
[622cdbe]433 continue;
[bb9ec2d]434 case LOADER_SET_PROGRAM:
[984a9ba]435 ldr_set_program(&call);
[c98e6ee]436 continue;
437 case LOADER_SET_ARGS:
[984a9ba]438 ldr_set_args(&call);
[bbdbf86]439 continue;
[bb9ec2d]440 case LOADER_ADD_INBOX:
[984a9ba]441 ldr_add_inbox(&call);
[4470e26]442 continue;
443 case LOADER_LOAD:
[984a9ba]444 ldr_load(&call);
[4470e26]445 continue;
[c98e6ee]446 case LOADER_RUN:
[984a9ba]447 ldr_run(&call);
[4470e26]448 /* Not reached */
[c98e6ee]449 default:
[8c3bc75]450 retval = EINVAL;
[c98e6ee]451 break;
452 }
[a35b458]453
[984a9ba]454 async_answer_0(&call, retval);
[c98e6ee]455 }
456}
457
[1be7bee]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 */
[0a8f070]466static errno_t ldr_taskman_handshake(void)
467{
[1be7bee]468 assert(session_primary == NULL);
469 assert(session_taskman == NULL);
470
[0a8f070]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
[1be7bee]480 session_taskman = async_session_primary_swap(session_primary);
[0a8f070]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
[c98e6ee]491/** Program loader main function.
492 */
493int main(int argc, char *argv[])
494{
[0a8f070]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);
[b39b5cb]502 return rc;
[0a8f070]503 }
[a35b458]504
[0a8f070]505 /* Handle client connections */
[c98e6ee]506 async_manager();
[0a8f070]507 //TODO retval?
508
[bfd1546]509 /* Never reached */
[c98e6ee]510 return 0;
511}
512
513/** @}
514 */
Note: See TracBrowser for help on using the repository browser.