source: mainline/uspace/srv/loader/main.c@ 2f57690

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

cstyle

  • Property mode set to 100644
File size: 8.1 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>
55#include <loader/pcb.h>
56#include <errno.h>
57#include <async.h>
58#include <as.h>
59
60#include <elf.h>
61#include <elf_load.h>
62
[06b2b7f]63#define DPRINTF(...)
64
[c98e6ee]65/** Pathname of the file that will be loaded */
66static char *pathname = NULL;
67
68/** The Program control block */
69static pcb_t pcb;
70
71/** Number of arguments */
72static int argc = 0;
73/** Argument vector */
74static char **argv = NULL;
75/** Buffer holding all arguments */
76static char *arg_buf = NULL;
77
[4470e26]78static elf_info_t prog_info;
79static elf_info_t interp_info;
80
81static bool is_dyn_linked;
82
[bfd1546]83/** Used to limit number of connections to one. */
84static bool connected;
[4470e26]85
86static void loader_get_taskid(ipc_callid_t rid, ipc_call_t *request)
[47e0a05b]87{
88 ipc_callid_t callid;
89 task_id_t task_id;
90 size_t len;
[2f57690]91
[47e0a05b]92 task_id = task_get_id();
[2f57690]93
[47e0a05b]94 if (!ipc_data_read_receive(&callid, &len)) {
95 ipc_answer_0(callid, EINVAL);
96 ipc_answer_0(rid, EINVAL);
97 return;
98 }
[2f57690]99
100 if (len > sizeof(task_id))
101 len = sizeof(task_id);
102
[0993087]103 ipc_data_read_finalize(callid, &task_id, len);
[47e0a05b]104 ipc_answer_0(rid, EOK);
105}
106
107
[c98e6ee]108/** Receive a call setting pathname of the program to execute.
109 *
110 * @param rid
111 * @param request
112 */
113static void loader_set_pathname(ipc_callid_t rid, ipc_call_t *request)
114{
115 ipc_callid_t callid;
116 size_t len;
117 char *name_buf;
[2f57690]118
[c98e6ee]119 if (!ipc_data_write_receive(&callid, &len)) {
120 ipc_answer_0(callid, EINVAL);
121 ipc_answer_0(rid, EINVAL);
122 return;
123 }
[2f57690]124
[c98e6ee]125 name_buf = malloc(len + 1);
126 if (!name_buf) {
127 ipc_answer_0(callid, ENOMEM);
128 ipc_answer_0(rid, ENOMEM);
129 return;
130 }
[2f57690]131
[c98e6ee]132 ipc_data_write_finalize(callid, name_buf, len);
133 ipc_answer_0(rid, EOK);
[2f57690]134
[c98e6ee]135 if (pathname != NULL) {
136 free(pathname);
137 pathname = NULL;
138 }
[2f57690]139
[c98e6ee]140 name_buf[len] = '\0';
141 pathname = name_buf;
142}
143
144/** Receive a call setting arguments of the program to execute.
145 *
146 * @param rid
147 * @param request
148 */
149static void loader_set_args(ipc_callid_t rid, ipc_call_t *request)
150{
151 ipc_callid_t callid;
152 size_t buf_len, arg_len;
153 char *p;
154 int n;
[2f57690]155
[c98e6ee]156 if (!ipc_data_write_receive(&callid, &buf_len)) {
157 ipc_answer_0(callid, EINVAL);
158 ipc_answer_0(rid, EINVAL);
159 return;
160 }
[2f57690]161
[c98e6ee]162 if (arg_buf != NULL) {
163 free(arg_buf);
164 arg_buf = NULL;
165 }
[2f57690]166
[c98e6ee]167 if (argv != NULL) {
168 free(argv);
169 argv = NULL;
170 }
[2f57690]171
[c98e6ee]172 arg_buf = malloc(buf_len + 1);
173 if (!arg_buf) {
174 ipc_answer_0(callid, ENOMEM);
175 ipc_answer_0(rid, ENOMEM);
176 return;
177 }
[2f57690]178
[c98e6ee]179 ipc_data_write_finalize(callid, arg_buf, buf_len);
180 ipc_answer_0(rid, EOK);
[2f57690]181
[c98e6ee]182 arg_buf[buf_len] = '\0';
[2f57690]183
[c98e6ee]184 /*
185 * Count number of arguments
186 */
187 p = arg_buf;
188 n = 0;
189 while (p < arg_buf + buf_len) {
190 arg_len = strlen(p);
191 p = p + arg_len + 1;
192 ++n;
193 }
[2f57690]194
[c98e6ee]195 /* Allocate argv */
196 argv = malloc((n + 1) * sizeof(char *));
[2f57690]197
[c98e6ee]198 if (argv == NULL) {
199 free(arg_buf);
200 ipc_answer_0(callid, ENOMEM);
201 ipc_answer_0(rid, ENOMEM);
202 return;
203 }
[2f57690]204
[c98e6ee]205 /*
206 * Fill argv with argument pointers
207 */
208 p = arg_buf;
209 n = 0;
210 while (p < arg_buf + buf_len) {
211 argv[n] = p;
[2f57690]212
[c98e6ee]213 arg_len = strlen(p);
214 p = p + arg_len + 1;
215 ++n;
216 }
[2f57690]217
[c98e6ee]218 argc = n;
219 argv[n] = NULL;
220}
221
[4470e26]222/** Load the previously selected program.
[c98e6ee]223 *
224 * @param rid
225 * @param request
226 * @return 0 on success, !0 on error.
227 */
[4470e26]228static int loader_load(ipc_callid_t rid, ipc_call_t *request)
[c98e6ee]229{
230 int rc;
[2f57690]231
[c98e6ee]232 rc = elf_load_file(pathname, 0, &prog_info);
233 if (rc < 0) {
[06b2b7f]234 DPRINTF("Failed to load executable '%s'.\n", pathname);
[c98e6ee]235 ipc_answer_0(rid, EINVAL);
236 return 1;
237 }
[2f57690]238
[c98e6ee]239 elf_create_pcb(&prog_info, &pcb);
[2f57690]240
[c98e6ee]241 pcb.argc = argc;
242 pcb.argv = argv;
[2f57690]243
[c98e6ee]244 if (prog_info.interp == NULL) {
245 /* Statically linked program */
[4470e26]246 is_dyn_linked = false;
[c98e6ee]247 ipc_answer_0(rid, EOK);
248 return 0;
249 }
[2f57690]250
[49093a4]251 rc = elf_load_file(prog_info.interp, 0, &interp_info);
[c98e6ee]252 if (rc < 0) {
[06b2b7f]253 DPRINTF("Failed to load interpreter '%s.'\n",
254 prog_info.interp);
[c98e6ee]255 ipc_answer_0(rid, EINVAL);
256 return 1;
257 }
[2f57690]258
[4470e26]259 is_dyn_linked = true;
[c98e6ee]260 ipc_answer_0(rid, EOK);
[2f57690]261
[c98e6ee]262 return 0;
263}
264
[4470e26]265
266/** Run the previously loaded program.
267 *
268 * @param rid
269 * @param request
270 * @return 0 on success, !0 on error.
271 */
272static void loader_run(ipc_callid_t rid, ipc_call_t *request)
273{
[20f1597]274 const char *cp;
[2f57690]275
[bc18d63]276 /* Set the task name. */
[20f1597]277 cp = strrchr(pathname, '/');
278 cp = (cp == NULL) ? pathname : (cp + 1);
279 task_set_name(cp);
[2f57690]280
[4470e26]281 if (is_dyn_linked == true) {
282 /* Dynamically linked program */
[06b2b7f]283 DPRINTF("Run ELF interpreter.\n");
284 DPRINTF("Entry point: 0x%lx\n", interp_info.entry);
[4470e26]285 close_console();
[2f57690]286
[4470e26]287 ipc_answer_0(rid, EOK);
288 elf_run(&interp_info, &pcb);
289 } else {
290 /* Statically linked program */
291 close_console();
292 ipc_answer_0(rid, EOK);
293 elf_run(&prog_info, &pcb);
294 }
295
296 /* Not reached */
297}
298
[c98e6ee]299/** Handle loader connection.
300 *
301 * Receive and carry out commands (of which the last one should be
302 * to execute the loaded program).
303 */
304static void loader_connection(ipc_callid_t iid, ipc_call_t *icall)
305{
306 ipc_callid_t callid;
307 ipc_call_t call;
308 int retval;
[2f57690]309
[bfd1546]310 /* Already have a connection? */
311 if (connected) {
312 ipc_answer_0(iid, ELIMIT);
313 return;
314 }
[2f57690]315
[bfd1546]316 connected = true;
317
318 /* Accept the connection */
319 ipc_answer_0(iid, EOK);
[2f57690]320
[c98e6ee]321 /* Ignore parameters, the connection is already open */
[2f57690]322 (void) iid;
323 (void) icall;
324
[c98e6ee]325 while (1) {
326 callid = async_get_call(&call);
[2f57690]327
[c98e6ee]328 switch (IPC_GET_METHOD(call)) {
[86e3d62]329 case IPC_M_PHONE_HUNGUP:
330 exit(0);
[47e0a05b]331 case LOADER_GET_TASKID:
332 loader_get_taskid(callid, &call);
333 continue;
[c98e6ee]334 case LOADER_SET_PATHNAME:
335 loader_set_pathname(callid, &call);
336 continue;
337 case LOADER_SET_ARGS:
338 loader_set_args(callid, &call);
[4470e26]339 continue;
340 case LOADER_LOAD:
341 loader_load(callid, &call);
342 continue;
[c98e6ee]343 case LOADER_RUN:
344 loader_run(callid, &call);
[4470e26]345 /* Not reached */
[c98e6ee]346 default:
347 retval = ENOENT;
348 break;
349 }
350 if ((callid & IPC_CALLID_NOTIFICATION) == 0 &&
351 IPC_GET_METHOD(call) != IPC_M_PHONE_HUNGUP) {
[06b2b7f]352 DPRINTF("Responding EINVAL to method %d.\n",
[c98e6ee]353 IPC_GET_METHOD(call));
354 ipc_answer_0(callid, EINVAL);
355 }
356 }
357}
358
359/** Program loader main function.
360 */
361int main(int argc, char *argv[])
362{
[bfd1546]363 ipcarg_t phonead;
[2f57690]364
[bfd1546]365 connected = false;
366
367 /* Set a handler of incomming connections. */
368 async_set_client_connection(loader_connection);
[2f57690]369
[bfd1546]370 /* Register at naming service. */
371 if (ipc_connect_to_me(PHONE_NS, SERVICE_LOAD, 0, 0, &phonead) != 0)
372 return -1;
373
[c98e6ee]374 async_manager();
[2f57690]375
[bfd1546]376 /* Never reached */
[c98e6ee]377 return 0;
378}
379
380/** @}
381 */
Note: See TracBrowser for help on using the repository browser.