source: mainline/uspace/srv/loader/main.c@ 53ee7a0

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

Use user-defined labels instead of phone hashes

This commit changes the way how the async framework maps incomming calls
to connections. Instead of abusing the kernel addresses of attached
phones as identifiers, the IPC_M_CONNECT_TO_ME and IPC_M_CONNECT_ME_TO
messages allow the server to specify an arbitrary label which is
remembered in the connected phone and consequently imprinted on each
call which is routed through this phone.

The async framework uses the address of the connection structure as the
label. This removes the need for a connection hash table because each
incoming call already remembers the connection in its label.

To disambiguate this new label and the other user-defined label used for
answers, the call structure now has the request_label member for the
former and answer_label member for the latter.

This commit also moves the kernel definition of ipc_data_t to abi/ and
removes the uspace redefinition thereof. Finally, when forwarding the
IPC_M_CONNECT_TO_ME call, the phone capability and the kernel object
allocated in request_process are now correctly disposed of.

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