Statistics
| Branch: | Revision:

root / prex-0.9.0 / usr / server / exec / exec_execve.c @ 03e9c04a

History | View | Annotate | Download (9.51 KB)

1
/*-
2
 * Copyright (c) 2005-2009, Kohsuke Ohtani
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
 * 1. Redistributions of source code must retain the above copyright
9
 *    notice, this list of conditions and the following disclaimer.
10
 * 2. Redistributions in binary form must reproduce the above copyright
11
 *    notice, this list of conditions and the following disclaimer in the
12
 *    documentation and/or other materials provided with the distribution.
13
 * 3. Neither the name of the author nor the names of any co-contributors
14
 *    may be used to endorse or promote products derived from this software
15
 *    without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29

    
30
/*
31
 * exec_execve.c - execve support
32
 */
33

    
34
#include <sys/prex.h>
35
#include <sys/capability.h>
36
#include <ipc/fs.h>
37
#include <ipc/proc.h>
38
#include <ipc/ipc.h>
39
#include <sys/list.h>
40

    
41
#include <limits.h>
42
#include <unistd.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <stdio.h>
46
#include <fcntl.h>
47
#include <unistd.h>
48
#include <assert.h>
49
#include <errno.h>
50
#include <libgen.h>        /* for basename() */
51

    
52
#include "exec.h"
53

    
54
#define        SP_ALIGN(p)        ((unsigned)(p) &~ _ALIGNBYTES)
55

    
56
/* forward declarations */
57
static int        build_args(task_t, void *, char *, struct exec_msg *,
58
                           char *, char *, void **);
59
static int        conv_path(char *, char *, char *);
60
static void        notify_server(task_t, task_t, void *);
61
static int        read_header(char *);
62

    
63
/*
64
 * Buffer for file header
65
 */
66
static char hdrbuf[HEADER_SIZE];
67

    
68
/*
69
 * Execute program
70
 */
71
int
72
exec_execve(struct exec_msg *msg)
73
{
74
        struct exec_loader *ldr = NULL;
75
        int error, i;
76
        task_t old_task, new_task;
77
        thread_t t;
78
        void *stack, *sp;
79
        char path[PATH_MAX];
80
        struct exec exec;
81
        int rc;
82

    
83
        DPRINTF(("exec_execve: path=%s task=%x\n", msg->path, msg->hdr.task));
84

    
85
        old_task = msg->hdr.task;
86

    
87
        /*
88
         * Make it full path.
89
         */
90
        if ((error = conv_path(msg->cwd, msg->path, path)) != 0) {
91
                DPRINTF(("exec: invalid path\n"));
92
                goto err1;
93
        }
94

    
95
        /*
96
         * Check permission.
97
         */
98
        if (access(path, X_OK) == -1) {
99
                DPRINTF(("exec: no exec access\n"));
100
                error = errno;
101
                goto err1;
102
        }
103

    
104
        exec.path = path;
105
        exec.header = hdrbuf;
106
        exec.xarg1 = NULL;
107
        exec.xarg2 = NULL;
108

    
109
 again:
110
        /*
111
         * Read file header
112
         */
113
        DPRINTF(("exec: read header for %s\n", exec.path));
114
        if ((error = read_header(exec.path)) != 0)
115
                goto err1;
116

    
117
        /*
118
         * Find file loader
119
         */
120
        rc = PROBE_ERROR;
121
        for (i = 0; i < nloader; i++) {
122
                ldr = &loader_table[i];
123
                if ((rc = ldr->el_probe(&exec)) != PROBE_ERROR) {
124
                        break;
125
                }
126
        }
127
        if (rc == PROBE_ERROR) {
128
                DPRINTF(("exec: unsupported file format\n"));
129
                error = ENOEXEC;
130
                goto err1;
131
        }
132

    
133
        /*
134
         * Check file header again if indirect case.
135
         */
136
        if (rc == PROBE_INDIRECT)
137
                        goto again;
138

    
139
        DPRINTF(("exec: loader=%s\n", ldr->el_name));
140

    
141
        /*
142
         * Check file permission.
143
         */
144
        if (access(exec.path, X_OK) == -1) {
145
                DPRINTF(("exec: no exec access\n"));
146
                error = errno;
147
                goto err1;
148
        }
149

    
150
        /*
151
         * Suspend old task
152
         */
153
        if ((error = task_suspend(old_task)) != 0)
154
                goto err1;
155

    
156
        /*
157
         * Create new task
158
         */
159
        if ((error = task_create(old_task, VM_NEW, &new_task)) != 0) {
160
                DPRINTF(("exec: failed to crete task\n"));
161
                goto err1;
162
        }
163

    
164
        if (*exec.path != '\0')
165
                task_setname(new_task, basename(exec.path));
166

    
167
        /*
168
         * Bind capabilities.
169
         */
170
        bind_cap(exec.path, new_task);
171

    
172
        if ((error = thread_create(new_task, &t)) != 0)
173
                goto err3;
174

    
175
        /*
176
         * Allocate stack and build arguments on it.
177
         */
178
        error = vm_allocate(new_task, &stack, DFLSTKSZ, 1);
179
        if (error) {
180
                DPRINTF(("exec: failed to allocate stack\n"));
181
                goto err4;
182
        }
183
        if ((error = build_args(new_task, stack, exec.path, msg,
184
                                exec.xarg1, exec.xarg2, &sp)) != 0)
185
                goto err5;
186

    
187
        /*
188
         * Load file image.
189
         */
190
        DPRINTF(("exec: load file image\n"));
191
        exec.task = new_task;
192
        if ((error = ldr->el_load(&exec)) != 0)
193
                goto err5;
194
        if ((error = thread_load(t, (void (*)(void))exec.entry, sp)) != 0)
195
                goto err5;
196

    
197
        /*
198
         * Notify to servers.
199
         */
200
        notify_server(old_task, new_task, stack);
201

    
202
        /*
203
         * Terminate old task.
204
         */
205
        task_terminate(old_task);
206

    
207
        /*
208
         * Set him running.
209
         */
210
        thread_setpri(t, PRI_DEFAULT);
211
        thread_resume(t);
212

    
213
        DPRINTF(("exec done\n"));
214
        return 0;
215
 err5:
216
        vm_free(new_task, stack);
217
 err4:
218
        thread_terminate(t);
219
 err3:
220
        task_terminate(new_task);
221
 err1:
222
        DPRINTF(("exec failed error=%d\n", error));
223
        return error;
224
}
225

    
226
/*
227
 * Convert to full path from the cwd of task and path.
228
 * @cwd:  current working directory
229
 * @path: target path
230
 * @full: full path to be returned
231
 */
232
static int
233
conv_path(char *cwd, char *path, char *full)
234
{
235
        char *src, *tgt, *p, *end;
236
        size_t len = 0;
237

    
238
        path[PATH_MAX - 1] = '\0';
239
        len = strlen(path);
240
        if (len >= PATH_MAX)
241
                return ENAMETOOLONG;
242
        if (strlen(cwd) + len >= PATH_MAX)
243
                return ENAMETOOLONG;
244
        src = path;
245
        tgt = full;
246
        end = src + len;
247
        if (path[0] == '/') {
248
                *tgt++ = *src++;
249
                len++;
250
        } else {
251
                strlcpy(full, cwd, PATH_MAX);
252
                len = strlen(cwd);
253
                tgt += len;
254
                if (len > 1 && path[0] != '.') {
255
                        *tgt = '/';
256
                        tgt++;
257
                        len++;
258
                }
259
        }
260
        while (*src) {
261
                p = src;
262
                while (*p != '/' && *p != '\0')
263
                        p++;
264
                *p = '\0';
265
                if (!strcmp(src, "..")) {
266
                        if (len >= 2) {
267
                                len -= 2;
268
                                tgt -= 2;        /* skip previous '/' */
269
                                while (*tgt != '/') {
270
                                        tgt--;
271
                                        len--;
272
                                }
273
                                if (len == 0) {
274
                                        tgt++;
275
                                        len++;
276
                                }
277
                        }
278
                } else if (!strcmp(src, ".")) {
279
                        /* Ignore "." */
280
                } else {
281
                        while (*src != '\0') {
282
                                *tgt++ = *src++;
283
                                len++;
284
                        }
285
                }
286
                if (p == end)
287
                        break;
288
                if (len > 0 && *(tgt - 1) != '/') {
289
                        *tgt++ = '/';
290
                        len++;
291
                }
292
                src = p + 1;
293
        }
294
        *tgt = '\0';
295
        return 0;
296
}
297

    
298
/*
299
 * Build argument on stack.
300
 *
301
 * Stack layout:
302
 *    file name string
303
 *    env string
304
 *    arg string
305
 *    NULL
306
 *    envp[n]
307
 *    NULL
308
 *    argv[n]
309
 *    argc
310
 *
311
 * NOTE: This may depend on processor architecture.
312
 */
313
static int
314
build_args(task_t task, void *stack, char *path, struct exec_msg *msg,
315
           char *xarg1, char *xarg2, void **new_sp)
316
{
317
        int argc, envc;
318
        char *file;
319
        char **argv, **envp;
320
        int i, error;
321
        u_long arg_top, mapped, sp;
322
        int len;
323

    
324
        argc = msg->argc;
325
        envc = msg->envc;
326
        DPRINTF(("exec: argc=%d envc=%d\n", argc, envc));
327
        DPRINTF(("exec: xarg1=%s xarg2=%s\n", xarg1, xarg2));
328

    
329
        /*
330
         * Map target stack in current task.
331
         */
332
        error = vm_map(task, stack, DFLSTKSZ, (void *)&mapped);
333
        if (error)
334
                return ENOMEM;
335
        memset((void *)mapped, 0, DFLSTKSZ);
336
        sp = mapped + DFLSTKSZ - sizeof(int) * 3;
337

    
338
        /*
339
         * Copy items
340
         */
341

    
342
        /* File name */
343
        *(char *)sp = '\0';
344
        sp -= strlen(path);
345
        sp = SP_ALIGN(sp);
346
        strlcpy((char *)sp, path, PATH_MAX);
347
        file = (char *)sp;
348

    
349
        /* arg/env */
350
        sp -= msg->bufsz;
351
        sp = SP_ALIGN(sp);
352
        memcpy((char *)sp, (char *)&msg->buf, msg->bufsz);
353
        arg_top = sp;
354

    
355
        /*
356
         * Insert extra argument for indirect loader.
357
         */
358
        if (xarg2 != NULL) {
359
                len = strlen(xarg2);
360
                sp -= (len + 1);
361
                strlcpy((char *)sp, xarg2, len + 1);
362
                arg_top = sp;
363
                argc++;
364
        }
365
        if (xarg1 != NULL) {
366
                len = strlen(xarg1);
367
                sp -= (len + 1);
368
                strlcpy((char *)sp, xarg1, len + 1);
369
                arg_top = sp;
370
                argc++;
371
        }
372

    
373
        /* envp[] */
374
        sp -= ((envc + 1) * sizeof(char *));
375
        envp = (char **)sp;
376

    
377
        /* argv[] */
378
        sp -= ((argc + 1) * sizeof(char *));
379
        argv = (char **)sp;
380

    
381
        /* argc */
382
        sp -= sizeof(int);
383
        *(int *)(sp) = argc + 1;
384

    
385
        /*
386
         * Build argument list
387
         */
388
        argv[0] = (char *)((u_long)stack + (u_long)file - mapped);
389

    
390
        for (i = 1; i <= argc; i++) {
391
                argv[i] = (char *)((u_long)stack + (arg_top - mapped));
392
                while ((*(char *)arg_top++) != '\0');
393
        }
394
        argv[argc + 1] = NULL;
395

    
396
        for (i = 0; i < envc; i++) {
397
                envp[i] = (char *)((u_long)stack + (arg_top - mapped));
398
                while ((*(char *)arg_top++) != '\0');
399
        }
400
        envp[envc] = NULL;
401

    
402
        *new_sp = (void *)((u_long)stack + (sp - mapped));
403
        vm_free(task_self(), (void *)mapped);
404

    
405
        return 0;
406
}
407

    
408
/*
409
 * Notify exec() to servers.
410
 */
411
static void
412
notify_server(task_t org_task, task_t new_task, void *stack)
413
{
414
        struct msg m;
415
        int error;
416
        object_t fsobj, procobj;
417

    
418
        if (object_lookup("!fs", &fsobj) != 0)
419
                return;
420

    
421
        if (object_lookup("!proc", &procobj) != 0)
422
                return;
423

    
424
        /* Notify to file system server */
425
        do {
426
                m.hdr.code = FS_EXEC;
427
                m.data[0] = (int)org_task;
428
                m.data[1] = (int)new_task;
429
                error = msg_send(fsobj, &m, sizeof(m));
430
        } while (error == EINTR);
431

    
432
        /* Notify to process server */
433
        do {
434
                m.hdr.code = PS_EXEC;
435
                m.data[0] = (int)org_task;
436
                m.data[1] = (int)new_task;
437
                m.data[2] = (int)stack;
438
                error = msg_send(procobj, &m, sizeof(m));
439
        } while (error == EINTR);
440
}
441

    
442
static int
443
read_header(char *path)
444
{
445
        int fd;
446
        struct stat st;
447

    
448
        /*
449
         * Check target file type.
450
         */
451
        if ((fd = open(path, O_RDONLY)) == -1)
452
                return ENOENT;
453

    
454
        if (fstat(fd, &st) == -1) {
455
                close(fd);
456
                return EIO;
457
        }
458
        if (!S_ISREG(st.st_mode)) {
459
                DPRINTF(("exec: not regular file\n"));
460
                close(fd);
461
                return EACCES;        /* must be regular file */
462
        }
463
        /*
464
         * Read file header.
465
         */
466
        memset(hdrbuf, 0, HEADER_SIZE);
467
        if (read(fd, hdrbuf, HEADER_SIZE) == -1) {
468
                close(fd);
469
                return EIO;
470
        }
471
        close(fd);
472
        return 0;
473
}