source: mainline/uspace/srv/loader/main.c@ 35b7d86e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 35b7d86e was ea56098, checked in by Jakub Jermar <jakub@…>, 9 years ago

Set VFS root for loader

This is important for the dynamically linked binaries that are loaded
from the file system by the loader. In other words, the loader needs
to know the VFS root before it can load the dynamically linked libc.

  • Property mode set to 100644
File size: 9.1 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 <stdbool.h>
50#include <fcntl.h>
51#include <sys/types.h>
52#include <ipc/services.h>
53#include <ipc/loader.h>
54#include <ns.h>
55#include <loader/pcb.h>
56#include <entry_point.h>
57#include <errno.h>
58#include <async.h>
59#include <str.h>
60#include <as.h>
61#include <elf/elf.h>
62#include <elf/elf_load.h>
63#include <vfs/vfs.h>
64#include <vfs/inbox.h>
65
66#define DPRINTF(...)
67
68/** File that will be loaded */
69static char *progname = NULL;
70static int program_fd = -1;
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/** Inbox entries. */
86static struct pcb_inbox_entry inbox[INBOX_MAX_ENTRIES];
87static int inbox_entries = 0;
88
89static elf_info_t prog_info;
90
91/** Used to limit number of connections to one. */
92static bool connected = false;
93
94static void ldr_get_taskid(ipc_callid_t rid, ipc_call_t *request)
95{
96 ipc_callid_t callid;
97 task_id_t task_id;
98 size_t len;
99
100 task_id = task_get_id();
101
102 if (!async_data_read_receive(&callid, &len)) {
103 async_answer_0(callid, EINVAL);
104 async_answer_0(rid, EINVAL);
105 return;
106 }
107
108 if (len > sizeof(task_id))
109 len = sizeof(task_id);
110
111 async_data_read_finalize(callid, &task_id, len);
112 async_answer_0(rid, EOK);
113}
114
115/** Receive a call setting the current working directory.
116 *
117 * @param rid
118 * @param request
119 */
120static void ldr_set_cwd(ipc_callid_t rid, ipc_call_t *request)
121{
122 char *buf;
123 int rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, NULL);
124
125 if (rc == EOK) {
126 if (cwd != NULL)
127 free(cwd);
128
129 cwd = buf;
130 }
131
132 async_answer_0(rid, rc);
133}
134
135/** Receive a call setting the program to execute.
136 *
137 * @param rid
138 * @param request
139 */
140static void ldr_set_program(ipc_callid_t rid, ipc_call_t *request)
141{
142 ipc_callid_t writeid;
143 size_t namesize;
144 if (!async_data_write_receive(&writeid, &namesize)) {
145 async_answer_0(rid, EINVAL);
146 return;
147 }
148
149 char* name = malloc(namesize);
150 int rc = async_data_write_finalize(writeid, name, namesize);
151 if (rc != EOK) {
152 async_answer_0(rid, EINVAL);
153 return;
154 }
155
156 int file = vfs_receive_handle(true);
157 if (file < 0) {
158 async_answer_0(rid, EINVAL);
159 return;
160 }
161
162 progname = name;
163 program_fd = file;
164 async_answer_0(rid, EOK);
165}
166
167/** Receive a call setting arguments of the program to execute.
168 *
169 * @param rid
170 * @param request
171 */
172static void ldr_set_args(ipc_callid_t rid, ipc_call_t *request)
173{
174 char *buf;
175 size_t buf_size;
176 int rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
177
178 if (rc == EOK) {
179 /*
180 * Count number of arguments
181 */
182 char *cur = buf;
183 int count = 0;
184
185 while (cur < buf + buf_size) {
186 size_t arg_size = str_size(cur);
187 cur += arg_size + 1;
188 count++;
189 }
190
191 /*
192 * Allocate new argv
193 */
194 char **_argv = (char **) malloc((count + 1) * sizeof(char *));
195 if (_argv == NULL) {
196 free(buf);
197 async_answer_0(rid, ENOMEM);
198 return;
199 }
200
201 /*
202 * Fill the new argv with argument pointers
203 */
204 cur = buf;
205 count = 0;
206 while (cur < buf + buf_size) {
207 _argv[count] = cur;
208
209 size_t arg_size = str_size(cur);
210 cur += arg_size + 1;
211 count++;
212 }
213 _argv[count] = NULL;
214
215 /*
216 * Copy temporary data to global variables
217 */
218 if (arg_buf != NULL)
219 free(arg_buf);
220
221 if (argv != NULL)
222 free(argv);
223
224 argc = count;
225 arg_buf = buf;
226 argv = _argv;
227 }
228
229 async_answer_0(rid, rc);
230}
231
232/** Receive a call setting inbox files of the program to execute.
233 *
234 * @param rid
235 * @param request
236 */
237static void ldr_add_inbox(ipc_callid_t rid, ipc_call_t *request)
238{
239 if (inbox_entries == INBOX_MAX_ENTRIES) {
240 async_answer_0(rid, ERANGE);
241 }
242
243 ipc_callid_t writeid;
244 size_t namesize;
245 if (!async_data_write_receive(&writeid, &namesize)) {
246 async_answer_0(rid, EINVAL);
247 return;
248 }
249
250 char* name = malloc(namesize);
251 int rc = async_data_write_finalize(writeid, name, namesize);
252 if (rc != EOK) {
253 async_answer_0(rid, EINVAL);
254 return;
255 }
256
257 int file = vfs_receive_handle(true);
258 if (file < 0) {
259 async_answer_0(rid, EINVAL);
260 return;
261 }
262
263 /*
264 * We need to set the root early for dynamically linked binaries so
265 * that the loader can use it too.
266 */
267 if (str_cmp(name, "root") == 0)
268 vfs_root_set(file);
269
270 inbox[inbox_entries].name = name;
271 inbox[inbox_entries].file = file;
272 inbox_entries++;
273 async_answer_0(rid, EOK);
274}
275
276/** Load the previously selected program.
277 *
278 * @param rid
279 * @param request
280 * @return 0 on success, !0 on error.
281 */
282static int ldr_load(ipc_callid_t rid, ipc_call_t *request)
283{
284 int rc = elf_load(program_fd, &prog_info);
285 if (rc != EE_OK) {
286 DPRINTF("Failed to load executable for '%s'.\n", progname);
287 async_answer_0(rid, EINVAL);
288 return 1;
289 }
290
291 elf_set_pcb(&prog_info, &pcb);
292
293 pcb.cwd = cwd;
294
295 pcb.argc = argc;
296 pcb.argv = argv;
297
298 pcb.inbox = inbox;
299 pcb.inbox_entries = inbox_entries;
300
301 async_answer_0(rid, rc);
302 return 0;
303}
304
305/** Run the previously loaded program.
306 *
307 * @param rid
308 * @param request
309 * @return 0 on success, !0 on error.
310 */
311static void ldr_run(ipc_callid_t rid, ipc_call_t *request)
312{
313 DPRINTF("Set task name\n");
314
315 /* Set the task name. */
316 task_set_name(progname);
317
318 /* Run program */
319 DPRINTF("Reply OK\n");
320 async_answer_0(rid, EOK);
321 DPRINTF("Jump to entry point at %p\n", pcb.entry);
322 entry_point_jmp(prog_info.finfo.entry, &pcb);
323
324 /* Not reached */
325}
326
327/** Handle loader connection.
328 *
329 * Receive and carry out commands (of which the last one should be
330 * to execute the loaded program).
331 */
332static void ldr_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
333{
334 /* Already have a connection? */
335 if (connected) {
336 async_answer_0(iid, ELIMIT);
337 return;
338 }
339
340 connected = true;
341
342 /* Accept the connection */
343 async_answer_0(iid, EOK);
344
345 /* Ignore parameters, the connection is already open */
346 (void) icall;
347
348 while (true) {
349 int retval;
350 ipc_call_t call;
351 ipc_callid_t callid = async_get_call(&call);
352
353 if (!IPC_GET_IMETHOD(call))
354 exit(0);
355
356 switch (IPC_GET_IMETHOD(call)) {
357 case LOADER_GET_TASKID:
358 ldr_get_taskid(callid, &call);
359 continue;
360 case LOADER_SET_CWD:
361 ldr_set_cwd(callid, &call);
362 continue;
363 case LOADER_SET_PROGRAM:
364 ldr_set_program(callid, &call);
365 continue;
366 case LOADER_SET_ARGS:
367 ldr_set_args(callid, &call);
368 continue;
369 case LOADER_ADD_INBOX:
370 ldr_add_inbox(callid, &call);
371 continue;
372 case LOADER_LOAD:
373 ldr_load(callid, &call);
374 continue;
375 case LOADER_RUN:
376 ldr_run(callid, &call);
377 /* Not reached */
378 default:
379 retval = EINVAL;
380 break;
381 }
382
383 async_answer_0(callid, retval);
384 }
385}
386
387/** Program loader main function.
388 */
389int main(int argc, char *argv[])
390{
391 async_set_fallback_port_handler(ldr_connection, NULL);
392
393 /* Introduce this task to the NS (give it our task ID). */
394 task_id_t id = task_get_id();
395 int rc = ns_intro(id);
396 if (rc != EOK)
397 return rc;
398
399 /* Create port */
400 port_id_t port;
401 rc = async_create_port(INTERFACE_LOADER, ldr_connection, NULL, &port);
402 if (rc != EOK)
403 return rc;
404
405 /* Register at naming service. */
406 rc = service_register(SERVICE_LOADER);
407 if (rc != EOK)
408 return rc;
409
410 async_manager();
411
412 /* Never reached */
413 return 0;
414}
415
416/** @}
417 */
Note: See TracBrowser for help on using the repository browser.