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

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

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

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