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

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

task_set_name() syscall so that we can have names (for userspace-loaded) tasks again.

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