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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fb6f1a5 was fb6f1a5, checked in by Jiri Svoboda <jiri@…>, 15 years ago

Implement some of C99 inttypes.h header (formatting macros for stdint.h). Use in appropriate places. Also replace use of 0x%lx with %p.

  • Property mode set to 100644
File size: 10.4 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
[2f57690]30 * @brief Loads and runs programs from VFS.
[c98e6ee]31 * @{
[2f57690]32 */
[c98e6ee]33/**
34 * @file
[2f57690]35 * @brief Loads and runs programs from VFS.
[c98e6ee]36 *
37 * The program loader is a special init binary. Its image is used
38 * to create a new task upon a @c task_spawn syscall. The syscall
39 * returns the id of a phone connected to the newly created task.
40 *
41 * The caller uses this 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 <unistd.h>
[4470e26]49#include <bool.h>
[c98e6ee]50#include <fcntl.h>
51#include <sys/types.h>
52#include <ipc/ipc.h>
[bfd1546]53#include <ipc/services.h>
[c98e6ee]54#include <ipc/loader.h>
[5d96851b]55#include <ipc/ns.h>
56#include <macros.h>
[c98e6ee]57#include <loader/pcb.h>
58#include <errno.h>
59#include <async.h>
[bbdbf86]60#include <string.h>
[c98e6ee]61#include <as.h>
62
63#include <elf.h>
64#include <elf_load.h>
65
[06b2b7f]66#define DPRINTF(...)
67
[c98e6ee]68/** Pathname of the file that will be loaded */
69static char *pathname = NULL;
70
71/** The Program control block */
72static pcb_t pcb;
73
[622cdbe]74/** Current working directory */
75static char *cwd = NULL;
76
[c98e6ee]77/** Number of arguments */
78static int argc = 0;
79/** Argument vector */
80static char **argv = NULL;
81/** Buffer holding all arguments */
82static char *arg_buf = NULL;
83
[bbdbf86]84/** Number of preset files */
85static int filc = 0;
86/** Preset files vector */
[08c9f7d5]87static fdi_node_t **filv = NULL;
[bbdbf86]88/** Buffer holding all preset files */
[99272a3]89static fdi_node_t *fil_buf = NULL;
[bbdbf86]90
[4470e26]91static elf_info_t prog_info;
92static elf_info_t interp_info;
93
94static bool is_dyn_linked;
95
[bfd1546]96/** Used to limit number of connections to one. */
97static bool connected;
[4470e26]98
[bbdbf86]99static void ldr_get_taskid(ipc_callid_t rid, ipc_call_t *request)
[47e0a05b]100{
101 ipc_callid_t callid;
102 task_id_t task_id;
103 size_t len;
[2f57690]104
[47e0a05b]105 task_id = task_get_id();
[2f57690]106
[0da4e41]107 if (!async_data_read_receive(&callid, &len)) {
[47e0a05b]108 ipc_answer_0(callid, EINVAL);
109 ipc_answer_0(rid, EINVAL);
110 return;
111 }
[2f57690]112
113 if (len > sizeof(task_id))
114 len = sizeof(task_id);
115
[0da4e41]116 async_data_read_finalize(callid, &task_id, len);
[47e0a05b]117 ipc_answer_0(rid, EOK);
118}
119
[622cdbe]120/** Receive a call setting the current working directory.
121 *
122 * @param rid
123 * @param request
124 */
125static void ldr_set_cwd(ipc_callid_t rid, ipc_call_t *request)
126{
127 ipc_callid_t callid;
128 size_t len;
129
130 if (!async_data_write_receive(&callid, &len)) {
131 ipc_answer_0(callid, EINVAL);
132 ipc_answer_0(rid, EINVAL);
133 return;
134 }
135
136 cwd = malloc(len + 1);
137 if (!cwd) {
138 ipc_answer_0(callid, ENOMEM);
139 ipc_answer_0(rid, ENOMEM);
140 return;
141 }
142
143 async_data_write_finalize(callid, cwd, len);
144 cwd[len] = '\0';
145
146 ipc_answer_0(rid, EOK);
147}
[47e0a05b]148
[c98e6ee]149/** Receive a call setting pathname of the program to execute.
150 *
151 * @param rid
152 * @param request
153 */
[bbdbf86]154static void ldr_set_pathname(ipc_callid_t rid, ipc_call_t *request)
[c98e6ee]155{
156 ipc_callid_t callid;
157 size_t len;
158 char *name_buf;
[2f57690]159
[0da4e41]160 if (!async_data_write_receive(&callid, &len)) {
[c98e6ee]161 ipc_answer_0(callid, EINVAL);
162 ipc_answer_0(rid, EINVAL);
163 return;
164 }
[2f57690]165
[c98e6ee]166 name_buf = malloc(len + 1);
167 if (!name_buf) {
168 ipc_answer_0(callid, ENOMEM);
169 ipc_answer_0(rid, ENOMEM);
170 return;
171 }
[2f57690]172
[0da4e41]173 async_data_write_finalize(callid, name_buf, len);
[c98e6ee]174 ipc_answer_0(rid, EOK);
[2f57690]175
[c98e6ee]176 if (pathname != NULL) {
177 free(pathname);
178 pathname = NULL;
179 }
[2f57690]180
[c98e6ee]181 name_buf[len] = '\0';
182 pathname = name_buf;
183}
184
185/** Receive a call setting arguments of the program to execute.
186 *
187 * @param rid
188 * @param request
189 */
[bbdbf86]190static void ldr_set_args(ipc_callid_t rid, ipc_call_t *request)
[c98e6ee]191{
192 ipc_callid_t callid;
[92fd52d7]193 size_t buf_size, arg_size;
[c98e6ee]194 char *p;
195 int n;
[2f57690]196
[0da4e41]197 if (!async_data_write_receive(&callid, &buf_size)) {
[c98e6ee]198 ipc_answer_0(callid, EINVAL);
199 ipc_answer_0(rid, EINVAL);
200 return;
201 }
[2f57690]202
[c98e6ee]203 if (arg_buf != NULL) {
204 free(arg_buf);
205 arg_buf = NULL;
206 }
[2f57690]207
[c98e6ee]208 if (argv != NULL) {
209 free(argv);
210 argv = NULL;
211 }
[2f57690]212
[92fd52d7]213 arg_buf = malloc(buf_size + 1);
[c98e6ee]214 if (!arg_buf) {
215 ipc_answer_0(callid, ENOMEM);
216 ipc_answer_0(rid, ENOMEM);
217 return;
218 }
[2f57690]219
[0da4e41]220 async_data_write_finalize(callid, arg_buf, buf_size);
[2f57690]221
[92fd52d7]222 arg_buf[buf_size] = '\0';
[2f57690]223
[c98e6ee]224 /*
225 * Count number of arguments
226 */
227 p = arg_buf;
228 n = 0;
[92fd52d7]229 while (p < arg_buf + buf_size) {
230 arg_size = str_size(p);
231 p = p + arg_size + 1;
[c98e6ee]232 ++n;
233 }
[2f57690]234
[c98e6ee]235 /* Allocate argv */
236 argv = malloc((n + 1) * sizeof(char *));
[2f57690]237
[c98e6ee]238 if (argv == NULL) {
239 free(arg_buf);
240 ipc_answer_0(rid, ENOMEM);
241 return;
242 }
[482c86f]243
[c98e6ee]244 /*
245 * Fill argv with argument pointers
246 */
247 p = arg_buf;
248 n = 0;
[92fd52d7]249 while (p < arg_buf + buf_size) {
[c98e6ee]250 argv[n] = p;
[2f57690]251
[92fd52d7]252 arg_size = str_size(p);
253 p = p + arg_size + 1;
[c98e6ee]254 ++n;
255 }
[2f57690]256
[c98e6ee]257 argc = n;
258 argv[n] = NULL;
[482c86f]259
260 ipc_answer_0(rid, EOK);
[c98e6ee]261}
262
[bbdbf86]263/** Receive a call setting preset files of the program to execute.
264 *
265 * @param rid
266 * @param request
267 */
268static void ldr_set_files(ipc_callid_t rid, ipc_call_t *request)
269{
270 ipc_callid_t callid;
271 size_t buf_size;
[0da4e41]272 if (!async_data_write_receive(&callid, &buf_size)) {
[bbdbf86]273 ipc_answer_0(callid, EINVAL);
274 ipc_answer_0(rid, EINVAL);
275 return;
276 }
277
[99272a3]278 if ((buf_size % sizeof(fdi_node_t)) != 0) {
[bbdbf86]279 ipc_answer_0(callid, EINVAL);
280 ipc_answer_0(rid, EINVAL);
281 return;
282 }
283
284 if (fil_buf != NULL) {
285 free(fil_buf);
286 fil_buf = NULL;
287 }
288
289 if (filv != NULL) {
290 free(filv);
291 filv = NULL;
292 }
293
294 fil_buf = malloc(buf_size);
295 if (!fil_buf) {
296 ipc_answer_0(callid, ENOMEM);
297 ipc_answer_0(rid, ENOMEM);
298 return;
299 }
300
[0da4e41]301 async_data_write_finalize(callid, fil_buf, buf_size);
[bbdbf86]302
[99272a3]303 int count = buf_size / sizeof(fdi_node_t);
[bbdbf86]304
305 /* Allocate filvv */
[99272a3]306 filv = malloc((count + 1) * sizeof(fdi_node_t *));
[bbdbf86]307
308 if (filv == NULL) {
309 free(fil_buf);
310 ipc_answer_0(rid, ENOMEM);
311 return;
312 }
313
314 /*
315 * Fill filv with argument pointers
316 */
317 int i;
318 for (i = 0; i < count; i++)
319 filv[i] = &fil_buf[i];
320
321 filc = count;
322 filv[count] = NULL;
323
324 ipc_answer_0(rid, EOK);
325}
326
[4470e26]327/** Load the previously selected program.
[c98e6ee]328 *
329 * @param rid
330 * @param request
331 * @return 0 on success, !0 on error.
332 */
[bbdbf86]333static int ldr_load(ipc_callid_t rid, ipc_call_t *request)
[c98e6ee]334{
335 int rc;
[2f57690]336
[c98e6ee]337 rc = elf_load_file(pathname, 0, &prog_info);
[409b0d6]338 if (rc != EE_OK) {
[06b2b7f]339 DPRINTF("Failed to load executable '%s'.\n", pathname);
[c98e6ee]340 ipc_answer_0(rid, EINVAL);
341 return 1;
342 }
[2f57690]343
[c98e6ee]344 elf_create_pcb(&prog_info, &pcb);
[2f57690]345
[622cdbe]346 pcb.cwd = cwd;
347
[c98e6ee]348 pcb.argc = argc;
349 pcb.argv = argv;
[2f57690]350
[bbdbf86]351 pcb.filc = filc;
352 pcb.filv = filv;
353
[c98e6ee]354 if (prog_info.interp == NULL) {
355 /* Statically linked program */
[4470e26]356 is_dyn_linked = false;
[c98e6ee]357 ipc_answer_0(rid, EOK);
358 return 0;
359 }
[2f57690]360
[49093a4]361 rc = elf_load_file(prog_info.interp, 0, &interp_info);
[409b0d6]362 if (rc != EE_OK) {
[06b2b7f]363 DPRINTF("Failed to load interpreter '%s.'\n",
364 prog_info.interp);
[c98e6ee]365 ipc_answer_0(rid, EINVAL);
366 return 1;
367 }
[2f57690]368
[4470e26]369 is_dyn_linked = true;
[c98e6ee]370 ipc_answer_0(rid, EOK);
[2f57690]371
[c98e6ee]372 return 0;
373}
374
[4470e26]375
376/** Run the previously loaded program.
377 *
378 * @param rid
379 * @param request
380 * @return 0 on success, !0 on error.
381 */
[bbdbf86]382static void ldr_run(ipc_callid_t rid, ipc_call_t *request)
[4470e26]383{
[20f1597]384 const char *cp;
[2f57690]385
[bc18d63]386 /* Set the task name. */
[7afb4a5]387 cp = str_rchr(pathname, '/');
[20f1597]388 cp = (cp == NULL) ? pathname : (cp + 1);
389 task_set_name(cp);
[2f57690]390
[4470e26]391 if (is_dyn_linked == true) {
392 /* Dynamically linked program */
[06b2b7f]393 DPRINTF("Run ELF interpreter.\n");
[fb6f1a5]394 DPRINTF("Entry point: %p\n", interp_info.entry);
[2f57690]395
[4470e26]396 ipc_answer_0(rid, EOK);
397 elf_run(&interp_info, &pcb);
398 } else {
399 /* Statically linked program */
400 ipc_answer_0(rid, EOK);
401 elf_run(&prog_info, &pcb);
[bbdbf86]402 }
403
[4470e26]404 /* Not reached */
405}
406
[c98e6ee]407/** Handle loader connection.
408 *
409 * Receive and carry out commands (of which the last one should be
410 * to execute the loaded program).
411 */
[bbdbf86]412static void ldr_connection(ipc_callid_t iid, ipc_call_t *icall)
[c98e6ee]413{
414 ipc_callid_t callid;
415 ipc_call_t call;
416 int retval;
[2f57690]417
[bfd1546]418 /* Already have a connection? */
419 if (connected) {
420 ipc_answer_0(iid, ELIMIT);
421 return;
422 }
[2f57690]423
[bfd1546]424 connected = true;
425
426 /* Accept the connection */
427 ipc_answer_0(iid, EOK);
[2f57690]428
[c98e6ee]429 /* Ignore parameters, the connection is already open */
[2f57690]430 (void) iid;
431 (void) icall;
432
[c98e6ee]433 while (1) {
434 callid = async_get_call(&call);
[2f57690]435
[c98e6ee]436 switch (IPC_GET_METHOD(call)) {
[86e3d62]437 case IPC_M_PHONE_HUNGUP:
438 exit(0);
[47e0a05b]439 case LOADER_GET_TASKID:
[bbdbf86]440 ldr_get_taskid(callid, &call);
[47e0a05b]441 continue;
[622cdbe]442 case LOADER_SET_CWD:
443 ldr_set_cwd(callid, &call);
444 continue;
[c98e6ee]445 case LOADER_SET_PATHNAME:
[bbdbf86]446 ldr_set_pathname(callid, &call);
[c98e6ee]447 continue;
448 case LOADER_SET_ARGS:
[bbdbf86]449 ldr_set_args(callid, &call);
450 continue;
451 case LOADER_SET_FILES:
452 ldr_set_files(callid, &call);
[4470e26]453 continue;
454 case LOADER_LOAD:
[bbdbf86]455 ldr_load(callid, &call);
[4470e26]456 continue;
[c98e6ee]457 case LOADER_RUN:
[bbdbf86]458 ldr_run(callid, &call);
[4470e26]459 /* Not reached */
[c98e6ee]460 default:
461 retval = ENOENT;
462 break;
463 }
464 if ((callid & IPC_CALLID_NOTIFICATION) == 0 &&
465 IPC_GET_METHOD(call) != IPC_M_PHONE_HUNGUP) {
[06b2b7f]466 DPRINTF("Responding EINVAL to method %d.\n",
[c98e6ee]467 IPC_GET_METHOD(call));
468 ipc_answer_0(callid, EINVAL);
469 }
470 }
471}
472
473/** Program loader main function.
474 */
475int main(int argc, char *argv[])
476{
[bfd1546]477 ipcarg_t phonead;
[5d96851b]478 task_id_t id;
479 int rc;
480
[bfd1546]481 connected = false;
[5d96851b]482
483 /* Introduce this task to the NS (give it our task ID). */
484 id = task_get_id();
485 rc = async_req_2_0(PHONE_NS, NS_ID_INTRO, LOWER32(id), UPPER32(id));
486 if (rc != EOK)
487 return -1;
488
[bfd1546]489 /* Set a handler of incomming connections. */
[bbdbf86]490 async_set_client_connection(ldr_connection);
[2f57690]491
[bfd1546]492 /* Register at naming service. */
493 if (ipc_connect_to_me(PHONE_NS, SERVICE_LOAD, 0, 0, &phonead) != 0)
[5d96851b]494 return -2;
495
[c98e6ee]496 async_manager();
[2f57690]497
[bfd1546]498 /* Never reached */
[c98e6ee]499 return 0;
500}
501
502/** @}
503 */
Note: See TracBrowser for help on using the repository browser.