source: mainline/uspace/srv/loader/main.c@ 102f641

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

Correcting syntax according to ccheck

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