source: mainline/uspace/srv/loader/main.c@ 8aa2b3b

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

Merge mainline changes.

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