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
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
[012dd8e]37 * to the caller of the syscall. The formal caller (taskman) performs a
[0a8f070]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>
[012dd8e]62#include <ns.h>
[0a8f070]63#include <str.h>
64#include <sys/types.h>
[012dd8e]65#include <task.h>
[0a8f070]66#include <taskman.h>
67#include <unistd.h>
[7171760]68#include <vfs/vfs.h>
[bb9ec2d]69#include <vfs/inbox.h>
[25f6bddb]70#include <libc.h>
[c98e6ee]71
[40abf56a]72#ifdef CONFIG_RTLD
73#include <rtld/rtld.h>
74#endif
75
[012dd8e]76#define NAME "loader"
[e3787a0]77#define DPRINTF(...) ((void) 0)
[1ea99cc]78
[bb9ec2d]79/** File that will be loaded */
80static char *progname = NULL;
81static int program_fd = -1;
[c98e6ee]82
83/** The Program control block */
84static pcb_t pcb;
85
[622cdbe]86/** Current working directory */
87static char *cwd = NULL;
88
[c98e6ee]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
[bb9ec2d]96/** Inbox entries. */
97static struct pcb_inbox_entry inbox[INBOX_MAX_ENTRIES];
98static int inbox_entries = 0;
[bbdbf86]99
[4470e26]100static elf_info_t prog_info;
101
[bfd1546]102/** Used to limit number of connections to one. */
[007e6efa]103static bool connected = false;
[4470e26]104
[984a9ba]105static void ldr_get_taskid(ipc_call_t *req)
[47e0a05b]106{
[984a9ba]107 ipc_call_t call;
[47e0a05b]108 task_id_t task_id;
109 size_t len;
[a35b458]110
[47e0a05b]111 task_id = task_get_id();
[a35b458]112
[984a9ba]113 if (!async_data_read_receive(&call, &len)) {
114 async_answer_0(&call, EINVAL);
115 async_answer_0(req, EINVAL);
[47e0a05b]116 return;
117 }
[a35b458]118
[2f57690]119 if (len > sizeof(task_id))
120 len = sizeof(task_id);
[a35b458]121
[e3787a0]122 DPRINTF("LOADER_GET_TASKID() = %lu\n", (unsigned long) task_id);
[984a9ba]123 async_data_read_finalize(&call, &task_id, len);
124 async_answer_0(req, EOK);
[47e0a05b]125}
126
[622cdbe]127/** Receive a call setting the current working directory.
[c98e6ee]128 *
129 */
[984a9ba]130static void ldr_set_cwd(ipc_call_t *req)
[c98e6ee]131{
[472c09d]132 char *buf;
[b7fd2a0]133 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
[a35b458]134
[472c09d]135 if (rc == EOK) {
136 if (cwd != NULL)
137 free(cwd);
[a35b458]138
[472c09d]139 cwd = buf;
[c98e6ee]140 }
[a35b458]141
[e3787a0]142 DPRINTF("LOADER_SET_CWD('%s')\n", cwd);
[984a9ba]143 async_answer_0(req, rc);
[622cdbe]144}
[47e0a05b]145
[bb9ec2d]146/** Receive a call setting the program to execute.
[c98e6ee]147 *
148 */
[984a9ba]149static void ldr_set_program(ipc_call_t *req)
[c98e6ee]150{
[984a9ba]151 ipc_call_t call;
[bb9ec2d]152 size_t namesize;
[984a9ba]153 if (!async_data_write_receive(&call, &namesize)) {
154 async_answer_0(req, EINVAL);
[bb9ec2d]155 return;
156 }
157
[3bacee1]158 char *name = malloc(namesize);
[984a9ba]159 // FIXME: check return value
160
161 errno_t rc = async_data_write_finalize(&call, name, namesize);
[bb9ec2d]162 if (rc != EOK) {
[984a9ba]163 async_answer_0(req, EINVAL);
[bb9ec2d]164 return;
165 }
166
[f77c1c9]167 int file;
168 rc = vfs_receive_handle(true, &file);
169 if (rc != EOK) {
[984a9ba]170 async_answer_0(req, EINVAL);
[bb9ec2d]171 return;
[c98e6ee]172 }
[a35b458]173
[e3787a0]174 DPRINTF("LOADER_SET_PROGRAM('%s')\n", name);
175
[bb9ec2d]176 progname = name;
177 program_fd = file;
[984a9ba]178 async_answer_0(req, EOK);
[c98e6ee]179}
180
181/** Receive a call setting arguments of the program to execute.
182 *
183 */
[984a9ba]184static void ldr_set_args(ipc_call_t *req)
[c98e6ee]185{
[472c09d]186 char *buf;
187 size_t buf_size;
[b7fd2a0]188 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
[a35b458]189
[472c09d]190 if (rc == EOK) {
191 /*
192 * Count number of arguments
193 */
194 char *cur = buf;
195 int count = 0;
[a35b458]196
[472c09d]197 while (cur < buf + buf_size) {
198 size_t arg_size = str_size(cur);
199 cur += arg_size + 1;
200 count++;
201 }
[a35b458]202
[472c09d]203 /*
204 * Allocate new argv
205 */
206 char **_argv = (char **) malloc((count + 1) * sizeof(char *));
207 if (_argv == NULL) {
208 free(buf);
[984a9ba]209 async_answer_0(req, ENOMEM);
[472c09d]210 return;
211 }
[a35b458]212
[472c09d]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;
[a35b458]220
[472c09d]221 size_t arg_size = str_size(cur);
222 cur += arg_size + 1;
223 count++;
224 }
225 _argv[count] = NULL;
[a35b458]226
[472c09d]227 /*
228 * Copy temporary data to global variables
229 */
230 if (arg_buf != NULL)
231 free(arg_buf);
[a35b458]232
[472c09d]233 if (argv != NULL)
234 free(argv);
[a35b458]235
[e3787a0]236 for (int i = 0; i < count; i++)
237 DPRINTF("LOADER_SET_ARGS('%s')\n", _argv[i]);
238
[472c09d]239 argc = count;
240 arg_buf = buf;
241 argv = _argv;
[c98e6ee]242 }
[a35b458]243
[984a9ba]244 async_answer_0(req, rc);
[c98e6ee]245}
246
[bb9ec2d]247/** Receive a call setting inbox files of the program to execute.
[bbdbf86]248 *
249 */
[984a9ba]250static void ldr_add_inbox(ipc_call_t *req)
[bbdbf86]251{
[bb9ec2d]252 if (inbox_entries == INBOX_MAX_ENTRIES) {
[984a9ba]253 async_answer_0(req, ERANGE);
[b7f69f2]254 return;
[bb9ec2d]255 }
[7171760]256
[984a9ba]257 ipc_call_t call;
[bb9ec2d]258 size_t namesize;
[984a9ba]259 if (!async_data_write_receive(&call, &namesize)) {
260 async_answer_0(req, EINVAL);
[bb9ec2d]261 return;
262 }
263
[3bacee1]264 char *name = malloc(namesize);
[984a9ba]265 errno_t rc = async_data_write_finalize(&call, name, namesize);
[bb9ec2d]266 if (rc != EOK) {
[984a9ba]267 async_answer_0(req, EINVAL);
[bb9ec2d]268 return;
269 }
270
[f77c1c9]271 int file;
272 rc = vfs_receive_handle(true, &file);
273 if (rc != EOK) {
[984a9ba]274 async_answer_0(req, EINVAL);
[bb9ec2d]275 return;
[bbdbf86]276 }
[7171760]277
[e3787a0]278 DPRINTF("LOADER_ADD_INBOX('%s')\n", name);
279
[ea56098]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
[bb9ec2d]287 inbox[inbox_entries].name = name;
288 inbox[inbox_entries].file = file;
289 inbox_entries++;
[984a9ba]290 async_answer_0(req, EOK);
[bbdbf86]291}
292
[4470e26]293/** Load the previously selected program.
[c98e6ee]294 *
295 * @return 0 on success, !0 on error.
[984a9ba]296 *
[c98e6ee]297 */
[984a9ba]298static int ldr_load(ipc_call_t *req)
[c98e6ee]299{
[e3787a0]300 DPRINTF("LOADER_LOAD()\n");
301
[bdca26a]302 errno_t rc = elf_load(program_fd, &prog_info);
303 if (rc != EOK) {
[bb9ec2d]304 DPRINTF("Failed to load executable for '%s'.\n", progname);
[984a9ba]305 async_answer_0(req, EINVAL);
[c98e6ee]306 return 1;
307 }
[a35b458]308
[e3787a0]309 DPRINTF("Loaded.\n");
310
[40abf56a]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
[c74b9de]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
[17341d4]327 elf_set_pcb(&prog_info, &pcb);
[a35b458]328
[e3787a0]329 DPRINTF("PCB set.\n");
330
[012dd8e]331 pcb.session_taskman = taskman_get_session();
[0a8f070]332
[622cdbe]333 pcb.cwd = cwd;
[a35b458]334
[c98e6ee]335 pcb.argc = argc;
336 pcb.argv = argv;
[a35b458]337
[bb9ec2d]338 pcb.inbox = inbox;
339 pcb.inbox_entries = inbox_entries;
[a35b458]340
[e3787a0]341 DPRINTF("Answering.\n");
[984a9ba]342 async_answer_0(req, EOK);
[c98e6ee]343 return 0;
344}
345
[4470e26]346/** Run the previously loaded program.
347 *
348 * @return 0 on success, !0 on error.
[984a9ba]349 *
[4470e26]350 */
[984a9ba]351static __attribute__((noreturn)) void ldr_run(ipc_call_t *req)
[4470e26]352{
[a6dffb8]353 DPRINTF("Set task name\n");
354
[bc18d63]355 /* Set the task name. */
[bb9ec2d]356 task_set_name(progname);
[a35b458]357
[a6dffb8]358 /* Run program */
359 DPRINTF("Reply OK\n");
[984a9ba]360 async_answer_0(req, EOK);
[40abf56a]361
[faf19d4]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);
[fafb8e5]367 assert(!ipc_get_imethod(req));
[faf19d4]368 async_answer_0(req, EOK);
369
[a6dffb8]370 DPRINTF("Jump to entry point at %p\n", pcb.entry);
[40abf56a]371
[25f6bddb]372 __libc_fini();
[40abf56a]373 __tcb_reset();
[17341d4]374 entry_point_jmp(prog_info.finfo.entry, &pcb);
[a35b458]375
[4470e26]376 /* Not reached */
377}
378
[c98e6ee]379/** Handle loader connection.
380 *
381 * Receive and carry out commands (of which the last one should be
382 * to execute the loaded program).
[984a9ba]383 *
[c98e6ee]384 */
[984a9ba]385static void ldr_connection(ipc_call_t *icall, void *arg)
[c98e6ee]386{
[bfd1546]387 /* Already have a connection? */
388 if (connected) {
[984a9ba]389 async_answer_0(icall, ELIMIT);
[bfd1546]390 return;
391 }
[a35b458]392
[bfd1546]393 connected = true;
[a35b458]394
[bfd1546]395 /* Accept the connection */
[beb83c1]396 async_accept_0(icall);
[a35b458]397
[c98e6ee]398 /* Ignore parameters, the connection is already open */
[2f57690]399 (void) icall;
[a35b458]400
[79ae36dd]401 while (true) {
[b7fd2a0]402 errno_t retval;
[79ae36dd]403 ipc_call_t call;
[984a9ba]404 async_get_call(&call);
[a35b458]405
[fafb8e5]406 if (!ipc_get_imethod(&call)) {
[889cdb1]407 async_answer_0(&call, EOK);
[86e3d62]408 exit(0);
[889cdb1]409 }
[a35b458]410
[fafb8e5]411 switch (ipc_get_imethod(&call)) {
[47e0a05b]412 case LOADER_GET_TASKID:
[984a9ba]413 ldr_get_taskid(&call);
[47e0a05b]414 continue;
[622cdbe]415 case LOADER_SET_CWD:
[984a9ba]416 ldr_set_cwd(&call);
[622cdbe]417 continue;
[bb9ec2d]418 case LOADER_SET_PROGRAM:
[984a9ba]419 ldr_set_program(&call);
[c98e6ee]420 continue;
421 case LOADER_SET_ARGS:
[984a9ba]422 ldr_set_args(&call);
[bbdbf86]423 continue;
[bb9ec2d]424 case LOADER_ADD_INBOX:
[984a9ba]425 ldr_add_inbox(&call);
[4470e26]426 continue;
427 case LOADER_LOAD:
[984a9ba]428 ldr_load(&call);
[4470e26]429 continue;
[c98e6ee]430 case LOADER_RUN:
[984a9ba]431 ldr_run(&call);
[4470e26]432 /* Not reached */
[c98e6ee]433 default:
[8c3bc75]434 retval = EINVAL;
[c98e6ee]435 break;
436 }
[a35b458]437
[984a9ba]438 async_answer_0(&call, retval);
[c98e6ee]439 }
440}
441
442/** Program loader main function.
443 */
444int main(int argc, char *argv[])
445{
[0a8f070]446 /* Set a handler of incomming connections. */
447 async_set_fallback_port_handler(ldr_connection, NULL);
448
[012dd8e]449 /* Announce to taskman. */
450 errno_t rc = taskman_intro_loader();
[0a8f070]451 if (rc != EOK) {
[012dd8e]452 printf("%s: did not receive connectin from taskman (%i)\n",
453 NAME, rc);
[b39b5cb]454 return rc;
[0a8f070]455 }
[a35b458]456
[012dd8e]457 /*
458 * We are not a regular server, thus no retval is set, just wait for
459 * forwarded connections by taskman.
460 */
[c98e6ee]461 async_manager();
[0a8f070]462
[bfd1546]463 /* Never reached */
[c98e6ee]464 return 0;
465}
466
467/** @}
468 */
Note: See TracBrowser for help on using the repository browser.