source: mainline/uspace/srv/loader/main.c@ fe86d9d

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

Create taskman server (extracts task-related operations from naming service)

  • Exploits initial phones connected to spawn parent instead of NS.
  • session_ns changed to session_primary (setup during taskman-loader handshake).
  • Task creation moved from NS to taskman (no clonable services anymore).
  • Other task-related operations implementation is to come (task_retval is temporarily dummy).
  • Async framework: implicit connections — create fibrils for calls that arrived through initial phone.

Conflicts:

abi/include/abi/ipc/methods.h
boot/Makefile.common
uspace/Makefile
uspace/app/trace/ipcp.c
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/ns.h
uspace/lib/c/generic/task.c
uspace/lib/c/include/async.h
uspace/lib/c/include/ipc/services.h
uspace/lib/c/include/ipc/taskman.h
uspace/lib/c/include/loader/pcb.h
uspace/lib/c/include/ns.h
uspace/srv/loader/main.c
uspace/srv/ns/clonable.c
uspace/srv/ns/ns.c

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