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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ffa2c8ef was ffa2c8ef, checked in by Martin Decky <martin@…>, 14 years ago

do not intermix low-level IPC methods with async framework methods

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