~jsvoboda/helenos/dnsr

« back to all changes in this revision

Viewing changes to uspace/srv/loader/main.c

  • Committer: Martin Decky
  • Date: 2009-08-04 11:19:19 UTC
  • Revision ID: martin@uranus.dsrg.hide.ms.mff.cuni.cz-20090804111919-evyclddlr3v5lhmp
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <ipc/ns.h>
 
56
#include <macros.h>
 
57
#include <loader/pcb.h>
 
58
#include <errno.h>
 
59
#include <async.h>
 
60
#include <string.h>
 
61
#include <as.h>
 
62
 
 
63
#include <elf.h>
 
64
#include <elf_load.h>
 
65
 
 
66
#define DPRINTF(...)
 
67
 
 
68
/** Pathname of the file that will be loaded */
 
69
static char *pathname = NULL;
 
70
 
 
71
/** The Program control block */
 
72
static pcb_t pcb;
 
73
 
 
74
/** Number of arguments */
 
75
static int argc = 0;
 
76
/** Argument vector */
 
77
static char **argv = NULL;
 
78
/** Buffer holding all arguments */
 
79
static char *arg_buf = NULL;
 
80
 
 
81
/** Number of preset files */
 
82
static int filc = 0;
 
83
/** Preset files vector */
 
84
static fdi_node_t **filv = NULL;
 
85
/** Buffer holding all preset files */
 
86
static fdi_node_t *fil_buf = NULL;
 
87
 
 
88
static elf_info_t prog_info;
 
89
static elf_info_t interp_info;
 
90
 
 
91
static bool is_dyn_linked;
 
92
 
 
93
/** Used to limit number of connections to one. */
 
94
static bool connected;
 
95
 
 
96
static void ldr_get_taskid(ipc_callid_t rid, ipc_call_t *request)
 
97
{
 
98
        ipc_callid_t callid;
 
99
        task_id_t task_id;
 
100
        size_t len;
 
101
        
 
102
        task_id = task_get_id();
 
103
        
 
104
        if (!ipc_data_read_receive(&callid, &len)) {
 
105
                ipc_answer_0(callid, EINVAL);
 
106
                ipc_answer_0(rid, EINVAL);
 
107
                return;
 
108
        }
 
109
        
 
110
        if (len > sizeof(task_id))
 
111
                len = sizeof(task_id);
 
112
        
 
113
        ipc_data_read_finalize(callid, &task_id, len);
 
114
        ipc_answer_0(rid, EOK);
 
115
}
 
116
 
 
117
 
 
118
/** Receive a call setting pathname of the program to execute.
 
119
 *
 
120
 * @param rid
 
121
 * @param request
 
122
 */
 
123
static void ldr_set_pathname(ipc_callid_t rid, ipc_call_t *request)
 
124
{
 
125
        ipc_callid_t callid;
 
126
        size_t len;
 
127
        char *name_buf;
 
128
        
 
129
        if (!ipc_data_write_receive(&callid, &len)) {
 
130
                ipc_answer_0(callid, EINVAL);
 
131
                ipc_answer_0(rid, EINVAL);
 
132
                return;
 
133
        }
 
134
        
 
135
        name_buf = malloc(len + 1);
 
136
        if (!name_buf) {
 
137
                ipc_answer_0(callid, ENOMEM);
 
138
                ipc_answer_0(rid, ENOMEM);
 
139
                return;
 
140
        }
 
141
        
 
142
        ipc_data_write_finalize(callid, name_buf, len);
 
143
        ipc_answer_0(rid, EOK);
 
144
        
 
145
        if (pathname != NULL) {
 
146
                free(pathname);
 
147
                pathname = NULL;
 
148
        }
 
149
        
 
150
        name_buf[len] = '\0';
 
151
        pathname = name_buf;
 
152
}
 
153
 
 
154
/** Receive a call setting arguments of the program to execute.
 
155
 *
 
156
 * @param rid
 
157
 * @param request
 
158
 */
 
159
static void ldr_set_args(ipc_callid_t rid, ipc_call_t *request)
 
160
{
 
161
        ipc_callid_t callid;
 
162
        size_t buf_size, arg_size;
 
163
        char *p;
 
164
        int n;
 
165
        
 
166
        if (!ipc_data_write_receive(&callid, &buf_size)) {
 
167
                ipc_answer_0(callid, EINVAL);
 
168
                ipc_answer_0(rid, EINVAL);
 
169
                return;
 
170
        }
 
171
        
 
172
        if (arg_buf != NULL) {
 
173
                free(arg_buf);
 
174
                arg_buf = NULL;
 
175
        }
 
176
        
 
177
        if (argv != NULL) {
 
178
                free(argv);
 
179
                argv = NULL;
 
180
        }
 
181
        
 
182
        arg_buf = malloc(buf_size + 1);
 
183
        if (!arg_buf) {
 
184
                ipc_answer_0(callid, ENOMEM);
 
185
                ipc_answer_0(rid, ENOMEM);
 
186
                return;
 
187
        }
 
188
        
 
189
        ipc_data_write_finalize(callid, arg_buf, buf_size);
 
190
        
 
191
        arg_buf[buf_size] = '\0';
 
192
        
 
193
        /*
 
194
         * Count number of arguments
 
195
         */
 
196
        p = arg_buf;
 
197
        n = 0;
 
198
        while (p < arg_buf + buf_size) {
 
199
                arg_size = str_size(p);
 
200
                p = p + arg_size + 1;
 
201
                ++n;
 
202
        }
 
203
        
 
204
        /* Allocate argv */
 
205
        argv = malloc((n + 1) * sizeof(char *));
 
206
        
 
207
        if (argv == NULL) {
 
208
                free(arg_buf);
 
209
                ipc_answer_0(rid, ENOMEM);
 
210
                return;
 
211
        }
 
212
 
 
213
        /*
 
214
         * Fill argv with argument pointers
 
215
         */
 
216
        p = arg_buf;
 
217
        n = 0;
 
218
        while (p < arg_buf + buf_size) {
 
219
                argv[n] = p;
 
220
                
 
221
                arg_size = str_size(p);
 
222
                p = p + arg_size + 1;
 
223
                ++n;
 
224
        }
 
225
        
 
226
        argc = n;
 
227
        argv[n] = NULL;
 
228
 
 
229
        ipc_answer_0(rid, EOK);
 
230
}
 
231
 
 
232
/** Receive a call setting preset files of the program to execute.
 
233
 *
 
234
 * @param rid
 
235
 * @param request
 
236
 */
 
237
static void ldr_set_files(ipc_callid_t rid, ipc_call_t *request)
 
238
{
 
239
        ipc_callid_t callid;
 
240
        size_t buf_size;
 
241
        if (!ipc_data_write_receive(&callid, &buf_size)) {
 
242
                ipc_answer_0(callid, EINVAL);
 
243
                ipc_answer_0(rid, EINVAL);
 
244
                return;
 
245
        }
 
246
        
 
247
        if ((buf_size % sizeof(fdi_node_t)) != 0) {
 
248
                ipc_answer_0(callid, EINVAL);
 
249
                ipc_answer_0(rid, EINVAL);
 
250
                return;
 
251
        }
 
252
        
 
253
        if (fil_buf != NULL) {
 
254
                free(fil_buf);
 
255
                fil_buf = NULL;
 
256
        }
 
257
        
 
258
        if (filv != NULL) {
 
259
                free(filv);
 
260
                filv = NULL;
 
261
        }
 
262
        
 
263
        fil_buf = malloc(buf_size);
 
264
        if (!fil_buf) {
 
265
                ipc_answer_0(callid, ENOMEM);
 
266
                ipc_answer_0(rid, ENOMEM);
 
267
                return;
 
268
        }
 
269
        
 
270
        ipc_data_write_finalize(callid, fil_buf, buf_size);
 
271
        
 
272
        int count = buf_size / sizeof(fdi_node_t);
 
273
        
 
274
        /* Allocate filvv */
 
275
        filv = malloc((count + 1) * sizeof(fdi_node_t *));
 
276
        
 
277
        if (filv == NULL) {
 
278
                free(fil_buf);
 
279
                ipc_answer_0(rid, ENOMEM);
 
280
                return;
 
281
        }
 
282
        
 
283
        /*
 
284
         * Fill filv with argument pointers
 
285
         */
 
286
        int i;
 
287
        for (i = 0; i < count; i++)
 
288
                filv[i] = &fil_buf[i];
 
289
        
 
290
        filc = count;
 
291
        filv[count] = NULL;
 
292
        
 
293
        ipc_answer_0(rid, EOK);
 
294
}
 
295
 
 
296
/** Load the previously selected program.
 
297
 *
 
298
 * @param rid
 
299
 * @param request
 
300
 * @return 0 on success, !0 on error.
 
301
 */
 
302
static int ldr_load(ipc_callid_t rid, ipc_call_t *request)
 
303
{
 
304
        int rc;
 
305
        
 
306
        rc = elf_load_file(pathname, 0, &prog_info);
 
307
        if (rc != EE_OK) {
 
308
                DPRINTF("Failed to load executable '%s'.\n", pathname);
 
309
                ipc_answer_0(rid, EINVAL);
 
310
                return 1;
 
311
        }
 
312
        
 
313
        elf_create_pcb(&prog_info, &pcb);
 
314
        
 
315
        pcb.argc = argc;
 
316
        pcb.argv = argv;
 
317
        
 
318
        pcb.filc = filc;
 
319
        pcb.filv = filv;
 
320
        
 
321
        if (prog_info.interp == NULL) {
 
322
                /* Statically linked program */
 
323
                is_dyn_linked = false;
 
324
                ipc_answer_0(rid, EOK);
 
325
                return 0;
 
326
        }
 
327
        
 
328
        rc = elf_load_file(prog_info.interp, 0, &interp_info);
 
329
        if (rc != EE_OK) {
 
330
                DPRINTF("Failed to load interpreter '%s.'\n",
 
331
                    prog_info.interp);
 
332
                ipc_answer_0(rid, EINVAL);
 
333
                return 1;
 
334
        }
 
335
        
 
336
        is_dyn_linked = true;
 
337
        ipc_answer_0(rid, EOK);
 
338
        
 
339
        return 0;
 
340
}
 
341
 
 
342
 
 
343
/** Run the previously loaded program.
 
344
 *
 
345
 * @param rid
 
346
 * @param request
 
347
 * @return 0 on success, !0 on error.
 
348
 */
 
349
static void ldr_run(ipc_callid_t rid, ipc_call_t *request)
 
350
{
 
351
        const char *cp;
 
352
        
 
353
        /* Set the task name. */
 
354
        cp = str_rchr(pathname, '/');
 
355
        cp = (cp == NULL) ? pathname : (cp + 1);
 
356
        task_set_name(cp);
 
357
        
 
358
        if (is_dyn_linked == true) {
 
359
                /* Dynamically linked program */
 
360
                DPRINTF("Run ELF interpreter.\n");
 
361
                DPRINTF("Entry point: 0x%lx\n", interp_info.entry);
 
362
                
 
363
                ipc_answer_0(rid, EOK);
 
364
                elf_run(&interp_info, &pcb);
 
365
        } else {
 
366
                /* Statically linked program */
 
367
                ipc_answer_0(rid, EOK);
 
368
                elf_run(&prog_info, &pcb);
 
369
        }
 
370
        
 
371
        /* Not reached */
 
372
}
 
373
 
 
374
/** Handle loader connection.
 
375
 *
 
376
 * Receive and carry out commands (of which the last one should be
 
377
 * to execute the loaded program).
 
378
 */
 
379
static void ldr_connection(ipc_callid_t iid, ipc_call_t *icall)
 
380
{
 
381
        ipc_callid_t callid;
 
382
        ipc_call_t call;
 
383
        int retval;
 
384
        
 
385
        /* Already have a connection? */
 
386
        if (connected) {
 
387
                ipc_answer_0(iid, ELIMIT);
 
388
                return;
 
389
        }
 
390
        
 
391
        connected = true;
 
392
        
 
393
        /* Accept the connection */
 
394
        ipc_answer_0(iid, EOK);
 
395
        
 
396
        /* Ignore parameters, the connection is already open */
 
397
        (void) iid;
 
398
        (void) icall;
 
399
        
 
400
        while (1) {
 
401
                callid = async_get_call(&call);
 
402
                
 
403
                switch (IPC_GET_METHOD(call)) {
 
404
                case IPC_M_PHONE_HUNGUP:
 
405
                        exit(0);
 
406
                case LOADER_GET_TASKID:
 
407
                        ldr_get_taskid(callid, &call);
 
408
                        continue;
 
409
                case LOADER_SET_PATHNAME:
 
410
                        ldr_set_pathname(callid, &call);
 
411
                        continue;
 
412
                case LOADER_SET_ARGS:
 
413
                        ldr_set_args(callid, &call);
 
414
                        continue;
 
415
                case LOADER_SET_FILES:
 
416
                        ldr_set_files(callid, &call);
 
417
                        continue;
 
418
                case LOADER_LOAD:
 
419
                        ldr_load(callid, &call);
 
420
                        continue;
 
421
                case LOADER_RUN:
 
422
                        ldr_run(callid, &call);
 
423
                        /* Not reached */
 
424
                default:
 
425
                        retval = ENOENT;
 
426
                        break;
 
427
                }
 
428
                if ((callid & IPC_CALLID_NOTIFICATION) == 0 &&
 
429
                    IPC_GET_METHOD(call) != IPC_M_PHONE_HUNGUP) {
 
430
                        DPRINTF("Responding EINVAL to method %d.\n",
 
431
                            IPC_GET_METHOD(call));
 
432
                        ipc_answer_0(callid, EINVAL);
 
433
                }
 
434
        }
 
435
}
 
436
 
 
437
/** Program loader main function.
 
438
 */
 
439
int main(int argc, char *argv[])
 
440
{
 
441
        ipcarg_t phonead;
 
442
        task_id_t id;
 
443
        int rc;
 
444
 
 
445
        connected = false;
 
446
 
 
447
        /* Introduce this task to the NS (give it our task ID). */
 
448
        id = task_get_id();
 
449
        rc = async_req_2_0(PHONE_NS, NS_ID_INTRO, LOWER32(id), UPPER32(id));
 
450
        if (rc != EOK)
 
451
                return -1;
 
452
 
 
453
        /* Set a handler of incomming connections. */
 
454
        async_set_client_connection(ldr_connection);
 
455
        
 
456
        /* Register at naming service. */
 
457
        if (ipc_connect_to_me(PHONE_NS, SERVICE_LOAD, 0, 0, &phonead) != 0) 
 
458
                return -2;
 
459
 
 
460
        async_manager();
 
461
        
 
462
        /* Never reached */
 
463
        return 0;
 
464
}
 
465
 
 
466
/** @}
 
467
 */