source: mainline/uspace/srv/loader/main.c@ 08e103d4

Last change on this file since 08e103d4 was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • Property mode set to 100644
File size: 9.4 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 * @{
31 */
32/**
33 * @file
34 *
35 * The program loader is a special init binary. Its image is used
36 * to create a new task upon a @c task_spawn syscall. The syscall
37 * returns the id of a phone connected to the newly created task.
38 *
39 * The caller uses this phone to send the pathname and various other
40 * information to the loader. This is normally done by the C library
41 * and completely hidden from applications.
42 */
43
44#include <stdio.h>
45#include <stdlib.h>
46#include <stdbool.h>
47#include <stddef.h>
48#include <ipc/services.h>
49#include <ipc/loader.h>
50#include <ns.h>
51#include <loader/pcb.h>
52#include <entry_point.h>
53#include <errno.h>
54#include <async.h>
55#include <str.h>
56#include <as.h>
57#include <elf/elf.h>
58#include <elf/elf_load.h>
59#include <vfs/vfs.h>
60#include <vfs/inbox.h>
61#include <libc.h>
62
63#ifdef CONFIG_RTLD
64#include <rtld/rtld.h>
65#endif
66
67#define DPRINTF(...) ((void) 0)
68
69/** File that will be loaded */
70static char *progname = NULL;
71static int program_fd = -1;
72
73/** The Program control block */
74static pcb_t pcb;
75
76/** Current working directory */
77static char *cwd = NULL;
78
79/** Number of arguments */
80static int argc = 0;
81/** Argument vector */
82static char **argv = NULL;
83/** Buffer holding all arguments */
84static char *arg_buf = NULL;
85
86/** Inbox entries. */
87static struct pcb_inbox_entry inbox[INBOX_MAX_ENTRIES];
88static int inbox_entries = 0;
89
90static elf_info_t prog_info;
91
92/** Used to limit number of connections to one. */
93static bool connected = false;
94
95static void ldr_get_taskid(ipc_call_t *req)
96{
97 ipc_call_t call;
98 task_id_t task_id;
99 size_t len;
100
101 task_id = task_get_id();
102
103 if (!async_data_read_receive(&call, &len)) {
104 async_answer_0(&call, EINVAL);
105 async_answer_0(req, EINVAL);
106 return;
107 }
108
109 if (len > sizeof(task_id))
110 len = sizeof(task_id);
111
112 DPRINTF("LOADER_GET_TASKID() = %lu\n", (unsigned long) task_id);
113 async_data_read_finalize(&call, &task_id, len);
114 async_answer_0(req, EOK);
115}
116
117/** Receive a call setting the current working directory.
118 *
119 */
120static void ldr_set_cwd(ipc_call_t *req)
121{
122 char *buf;
123 errno_t 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 DPRINTF("LOADER_SET_CWD('%s')\n", cwd);
133 async_answer_0(req, rc);
134}
135
136/** Receive a call setting the program to execute.
137 *
138 */
139static void ldr_set_program(ipc_call_t *req)
140{
141 ipc_call_t call;
142 size_t namesize;
143 if (!async_data_write_receive(&call, &namesize)) {
144 async_answer_0(req, EINVAL);
145 return;
146 }
147
148 char *name = malloc(namesize);
149 // FIXME: check return value
150
151 errno_t rc = async_data_write_finalize(&call, name, namesize);
152 if (rc != EOK) {
153 async_answer_0(req, EINVAL);
154 return;
155 }
156
157 int file;
158 rc = vfs_receive_handle(true, &file);
159 if (rc != EOK) {
160 async_answer_0(req, EINVAL);
161 return;
162 }
163
164 DPRINTF("LOADER_SET_PROGRAM('%s')\n", name);
165
166 progname = name;
167 program_fd = file;
168 async_answer_0(req, EOK);
169}
170
171/** Receive a call setting arguments of the program to execute.
172 *
173 */
174static void ldr_set_args(ipc_call_t *req)
175{
176 char *buf;
177 size_t buf_size;
178 errno_t rc = async_data_write_accept((void **) &buf, true, 0, 0, 0, &buf_size);
179
180 if (rc == EOK) {
181 /*
182 * Count number of arguments
183 */
184 char *cur = buf;
185 int count = 0;
186
187 while (cur < buf + buf_size) {
188 size_t arg_size = str_bytes(cur);
189 cur += arg_size + 1;
190 count++;
191 }
192
193 /*
194 * Allocate new argv
195 */
196 char **_argv = (char **) malloc((count + 1) * sizeof(char *));
197 if (_argv == NULL) {
198 free(buf);
199 async_answer_0(req, ENOMEM);
200 return;
201 }
202
203 /*
204 * Fill the new argv with argument pointers
205 */
206 cur = buf;
207 count = 0;
208 while (cur < buf + buf_size) {
209 _argv[count] = cur;
210
211 size_t arg_size = str_bytes(cur);
212 cur += arg_size + 1;
213 count++;
214 }
215 _argv[count] = NULL;
216
217 /*
218 * Copy temporary data to global variables
219 */
220 if (arg_buf != NULL)
221 free(arg_buf);
222
223 if (argv != NULL)
224 free(argv);
225
226 for (int i = 0; i < count; i++)
227 DPRINTF("LOADER_SET_ARGS('%s')\n", _argv[i]);
228
229 argc = count;
230 arg_buf = buf;
231 argv = _argv;
232 }
233
234 async_answer_0(req, rc);
235}
236
237/** Receive a call setting inbox files of the program to execute.
238 *
239 */
240static void ldr_add_inbox(ipc_call_t *req)
241{
242 if (inbox_entries == INBOX_MAX_ENTRIES) {
243 async_answer_0(req, ERANGE);
244 return;
245 }
246
247 ipc_call_t call;
248 size_t namesize;
249 if (!async_data_write_receive(&call, &namesize)) {
250 async_answer_0(req, EINVAL);
251 return;
252 }
253
254 char *name = malloc(namesize);
255 errno_t rc = async_data_write_finalize(&call, name, namesize);
256 if (rc != EOK) {
257 async_answer_0(req, EINVAL);
258 return;
259 }
260
261 int file;
262 rc = vfs_receive_handle(true, &file);
263 if (rc != EOK) {
264 async_answer_0(req, EINVAL);
265 return;
266 }
267
268 DPRINTF("LOADER_ADD_INBOX('%s')\n", name);
269
270 /*
271 * We need to set the root early for dynamically linked binaries so
272 * that the loader can use it too.
273 */
274 if (str_cmp(name, "root") == 0)
275 vfs_root_set(file);
276
277 inbox[inbox_entries].name = name;
278 inbox[inbox_entries].file = file;
279 inbox_entries++;
280 async_answer_0(req, EOK);
281}
282
283/** Load the previously selected program.
284 *
285 * @return 0 on success, !0 on error.
286 *
287 */
288static int ldr_load(ipc_call_t *req)
289{
290 DPRINTF("LOADER_LOAD()\n");
291
292 int rc = elf_load(program_fd, &prog_info);
293 if (rc != EE_OK) {
294 DPRINTF("Failed to load executable for '%s'.\n", progname);
295 async_answer_0(req, EINVAL);
296 return 1;
297 }
298
299 DPRINTF("Loaded.\n");
300
301#ifdef CONFIG_RTLD
302 if (prog_info.env) {
303 pcb.tcb = rtld_tls_make(prog_info.env);
304 } else {
305 pcb.tcb = tls_make(prog_info.finfo.base);
306 }
307#else
308 pcb.tcb = tls_make(prog_info.finfo.base);
309#endif
310
311 if (!pcb.tcb) {
312 DPRINTF("Failed to make TLS for '%s'.\n", progname);
313 async_answer_0(req, ENOMEM);
314 return 1;
315 }
316
317 elf_set_pcb(&prog_info, &pcb);
318
319 DPRINTF("PCB set.\n");
320
321 pcb.cwd = cwd;
322
323 pcb.argc = argc;
324 pcb.argv = argv;
325
326 pcb.inbox = inbox;
327 pcb.inbox_entries = inbox_entries;
328
329 DPRINTF("Answering.\n");
330 async_answer_0(req, EOK);
331 return 0;
332}
333
334/** Run the previously loaded program.
335 *
336 * @return 0 on success, !0 on error.
337 *
338 */
339static __attribute__((noreturn)) void ldr_run(ipc_call_t *req)
340{
341 DPRINTF("Set task name\n");
342
343 /* Set the task name. */
344 task_set_name(progname);
345
346 /* Run program */
347 DPRINTF("Reply OK\n");
348 async_answer_0(req, EOK);
349
350 /*
351 * Wait for the hangup from the other side in order not to leave any
352 * unanswered IPC_M_PHONE_HUNGUP messages behind.
353 */
354 async_get_call(req);
355 assert(!IPC_GET_IMETHOD(*req));
356 async_answer_0(req, EOK);
357
358 DPRINTF("Jump to entry point at %p\n", pcb.entry);
359
360 __libc_fini();
361 __tcb_reset();
362 entry_point_jmp(prog_info.finfo.entry, &pcb);
363
364 /* Not reached */
365}
366
367/** Handle loader connection.
368 *
369 * Receive and carry out commands (of which the last one should be
370 * to execute the loaded program).
371 *
372 */
373static void ldr_connection(ipc_call_t *icall, void *arg)
374{
375 /* Already have a connection? */
376 if (connected) {
377 async_answer_0(icall, ELIMIT);
378 return;
379 }
380
381 connected = true;
382
383 /* Accept the connection */
384 async_accept_0(icall);
385
386 /* Ignore parameters, the connection is already open */
387 (void) icall;
388
389 while (true) {
390 errno_t retval;
391 ipc_call_t call;
392 async_get_call(&call);
393
394 if (!IPC_GET_IMETHOD(call)) {
395 async_answer_0(&call, EOK);
396 exit(0);
397 }
398
399 switch (IPC_GET_IMETHOD(call)) {
400 case LOADER_GET_TASKID:
401 ldr_get_taskid(&call);
402 continue;
403 case LOADER_SET_CWD:
404 ldr_set_cwd(&call);
405 continue;
406 case LOADER_SET_PROGRAM:
407 ldr_set_program(&call);
408 continue;
409 case LOADER_SET_ARGS:
410 ldr_set_args(&call);
411 continue;
412 case LOADER_ADD_INBOX:
413 ldr_add_inbox(&call);
414 continue;
415 case LOADER_LOAD:
416 ldr_load(&call);
417 continue;
418 case LOADER_RUN:
419 ldr_run(&call);
420 /* Not reached */
421 default:
422 retval = EINVAL;
423 break;
424 }
425
426 async_answer_0(&call, retval);
427 }
428}
429
430/** Program loader main function.
431 */
432int main(int argc, char *argv[])
433{
434 /* Introduce this task to the NS (give it our task ID). */
435 task_id_t id = task_get_id();
436 errno_t rc = ns_intro(id);
437 if (rc != EOK)
438 return rc;
439
440 /* Register at naming service. */
441 rc = service_register(SERVICE_LOADER, INTERFACE_LOADER,
442 ldr_connection, NULL);
443 if (rc != EOK)
444 return rc;
445
446 async_manager();
447
448 /* Never reached */
449 return 0;
450}
451
452/** @}
453 */
Note: See TracBrowser for help on using the repository browser.