source: mainline/uspace/srv/loader/main.c@ 228e490

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

initial modifications for supporting declarative IPC interfaces

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