Statistics
| Branch: | Revision:

scoutos / prex-0.9.0 / usr / bin / sh / sh.c @ 03e9c04a

History | View | Annotate | Download (9.12 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
#include <sys/prex.h>
31
#include <sys/keycode.h>
32
#include <sys/syslog.h>
33
#include <sys/wait.h>
34
#include <sys/fcntl.h>
35

    
36
#include <limits.h>
37
#include <dirent.h>
38
#include <termios.h>
39
#include <ctype.h>
40
#include <unistd.h>
41
#include <string.h>
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <errno.h>
45
#include <setjmp.h>
46
#include <libgen.h>        /* for basename() */
47

    
48
#include "sh.h"
49

    
50
#define ARGMAX                32
51

    
52
#define        CMD_PIPE        1
53
#define        CMD_BACKGND        2
54
#define        CMD_BUILTIN        4
55

    
56
extern const struct cmdentry shell_cmds[];
57
#ifdef CMDBOX
58
extern const struct cmdentry builtin_cmds[];
59
#define main(argc, argv)        sh_main(argc, argv)
60
#endif
61

    
62
static pid_t        shpid = 0;        /* pid of shell */
63
char                retval;                /* return value for shell */
64
unsigned        interact;        /* if shell reads from stdin */
65

    
66
jmp_buf        jmpbuf;
67

    
68
static void
69
error(const char *msg, ...)
70
{
71
        va_list ap;
72

    
73
        va_start(ap, msg);
74
        if (msg != NULL) {
75
                vfprintf(stderr, msg, ap);
76
                fprintf(stderr, "\n");
77
        }
78
        va_end(ap);
79
        if (getpid() != shpid)
80
                _exit(1);
81

    
82
        if (!interact)
83
                _exit(1);
84
        retval = 1;
85
}
86

    
87
static void
88
showsignal(int pid, int s)
89
{
90
        int signo = WTERMSIG(s);
91

    
92
        signo &= 0x7f;
93
        if (signo < NSIG && sys_siglist[signo])
94
                error(" %d: %s", pid, sys_siglist[signo]);
95
        else
96
                error(" %d: Signal %d", pid, signo);
97

    
98
        retval = signo + 0200;
99
}
100

    
101
static void
102
showprompt(void)
103
{
104
        static char cwd[PATH_MAX];
105
        static char prompt[PATH_MAX+20];
106

    
107
        getcwd(cwd, PATH_MAX);
108
        sprintf(prompt, "\033[32m[prex:%s]\033[0m# ", cwd);
109
        write(1, prompt, strlen(prompt));
110
}
111

    
112
static void
113
execute(int argc, char *argv[], int *redir, int flags, cmdfn_t cmdfn)
114
{
115
        int pid, i;
116
        int status;
117
        static char **arg;
118
        char *file;
119
        char spid[20];
120

    
121
        arg = argc > 1 ? &argv[1] : NULL;
122
        file = argv[0];
123
        pid = vfork();
124
        if (pid == -1) {
125
                for (i = 0; i < 2; i++)
126
                        if (redir[i] != -1)
127
                                close(redir[i]);
128
                error("Cannot fork");
129
                return;
130
        }
131
        if (pid == 0) {
132
                /* Child only */
133
                setpgid(0, 0);
134
                tcsetpgrp(2, getpgrp());
135

    
136
                for (i = 0; i < 2; i++) {
137
                        if (redir[i] != -1) {
138
                                if (dup2(redir[i], i) == -1)
139
                                        error("Cannot redirect %d", i);
140
                                close(redir[i]);
141
                        }
142
                }
143
                signal(SIGINT, SIG_DFL);
144
                signal(SIGQUIT, SIG_DFL);
145
                signal(SIGTERM, SIG_DFL);
146

    
147
                if (flags & CMD_BACKGND) {
148
                        signal(SIGINT, SIG_IGN);
149
                        signal(SIGQUIT, SIG_IGN);
150
                        if (redir[0] == -1) {
151
                                close(0);
152
                                open("/dev/null", O_RDWR);
153
                        }
154
                }
155
                errno = 0;
156
                if (cmdfn) {
157
                        task_setname(task_self(), basename(file));
158
                        if (cmdfn(argc, argv) != 0)
159
                                fprintf(stderr, "%s: %s\n", argv[0],
160
                                        strerror(errno));
161
                } else {
162
                        execv(file, arg);
163
                        /* Try $PATH */
164
                        if (errno == ENOENT)
165
                                execvp(file, arg);
166
                        if (errno == ENOENT || errno == ENOTDIR)
167
                                error("%s: command not found", argv[0]);
168
                        else if (errno == EACCES)
169
                                error("Permission denied");
170
                        else
171
                                error("%s cannot execute", argv[0]);
172
                }
173
                exit(1);
174
                /* NOTREACHED */
175
        }
176
        /* Parent */
177
        for (i = 0; i < 2; i++) {
178
                if (redir[i] != -1)
179
                        close(redir[i]);
180
        }
181
        if (flags & CMD_PIPE)
182
                return;
183
        if (flags & CMD_BACKGND) {
184
                sprintf(spid, "%u\n", pid);
185
                write(1, spid, strlen(spid));
186
                return;
187
        }
188

    
189
        while (wait(&status) != pid);
190
        if (status) {
191
                if (WIFSIGNALED(status))
192
                        showsignal(pid, status);
193
                else if (WIFEXITED(status))
194
                        retval = WEXITSTATUS(status);
195
        } else
196
                retval = 0;
197
        return;
198
}
199

    
200
static int
201
redirect(char **args, int *redir)
202
{
203
        unsigned int i, io, append = 0;
204
        int fd, argc;
205
        char *p, *file;
206

    
207
        for (i = 0; args[i] != NULL; i++) {
208
                p = args[i];
209
                switch (*p) {
210
                case '<':
211
                        io = 0;
212
                         break;
213
                case '>':
214
                        io = 1;
215
                        if (*(p + 1) == '>') {
216
                                append = 1;
217
                                p++;
218
                        }
219
                         break;
220
                default:
221
                        continue;
222
                }
223

    
224
                /* get file name */
225
                args[i] = (char *)-1;
226
                if (*(p + 1) == '\0') {
227
                        file = args[++i];
228
                        args[i] = (char *)-1;
229
                } else
230
                        file = p + 1;
231

    
232
                /* if redirected from pipe, ignore */
233
                if (redir[io] == -1) {
234
                        if (io == 1) {
235
                                if (append)
236
                                        fd = open(file, O_WRONLY | O_APPEND);
237
                                else
238
                                        fd = creat(file, 0666);
239
                        } else
240
                                fd = open(file, O_RDONLY);
241

    
242
                        if (fd == -1) {
243
                                error("%s: cannot open", file);
244
                                return -1;
245
                        }
246
                        redir[io] = fd;
247
                }
248
        }
249

    
250
        /* strip redirection info */
251
        argc = 0;
252
        for (i = 0; args[i]; i++) {
253
                if (args[i] != (char *)-1)
254
                        args[argc++] = args[i];
255
        }
256
        args[argc] = NULL;
257
        return argc;
258
}
259

    
260
static cmdfn_t
261
findcmd(const struct cmdentry cmds[], char *cmd)
262
{
263
        int i = 0;
264

    
265
        while (cmds[i].cmd != NULL) {
266
                if (!strcmp(cmd, cmds[i].cmd))
267
                        return cmds[i].func;
268
                i++;
269
        }
270
        return 0;
271
}
272

    
273
static void
274
parsecmd(char *cmds, int *redir, int flags)
275
{
276
        static char cmdbox[] = "cmdbox";
277
        static char *args[ARGMAX];
278
        char *p, *word = NULL;
279
        cmdfn_t fn;
280
        int i, argc = 0;
281

    
282
        optind = 1;        /* for nommu */
283

    
284
        if (cmds[0] != ' ' && cmds[0] != '\t')
285
                word = cmds;
286

    
287
        p = cmds;
288
        while (*p) {
289
                if (word == NULL) {
290
                        /* Skip white space. */
291
                        if (*p != ' ' && *p != '\t')
292
                                word = p;
293
                } else {
294
                        if (*p == ' ' || *p == '\t') {
295
                                *p = '\0';
296
                                args[argc++] = word;
297
                                word = NULL;
298
                                if (argc >= ARGMAX - 1) {
299
                                        error("Too many args");
300
                                        return;
301
                                }
302
                        }
303
                }
304
                p++;
305
        }
306
        if (argc == 0 && word == NULL)
307
                return;
308

    
309
        if (word)
310
                args[argc++] = word;
311
        args[argc] = NULL;
312

    
313
        /* Handle variable */
314
        if ((p = strchr(args[0], '=')) != NULL) {
315
                *p++ = '\0';
316
                if (*p == '\0')
317
                        unsetvar(args[0]);
318
                else
319
                        setvar(args[0], p);
320
                return;
321
        }
322

    
323
        fn = findcmd(shell_cmds, args[0]);
324
        if (fn) {
325
                /* Run as shell internal command */
326
                if ((*fn)(argc, args) != 0)
327
                        error("%s: %s", args[0], strerror(errno));
328
                return;
329
        }
330
        argc = redirect(args, redir);
331
        if (argc == -1)
332
                return;
333

    
334
        fn = findcmd(builtin_cmds, args[0]);
335

    
336
        /*
337
         * Alias: 'sh' => 'cmdbox sh'
338
         */
339
        if (fn == NULL && !strcmp(args[0], "sh")) {
340
                for (i = argc; i >= 0; i--)
341
                        args[i + 1] = args[i];
342
                args[0] = cmdbox;
343
                argc++;
344
        }
345
        execute(argc, args, redir, flags, fn);
346
}
347

    
348
static void
349
parsepipe(char *str, int flags)
350
{
351
        int pip[2] = { -1, -1 };
352
        int redir[2] = { -1, -1 };
353
        char *p, *cmds;
354

    
355
        p = cmds = str;
356
        while (*cmds) {
357
                switch (*p) {
358
                case '|':
359
                        *p = '\0';
360
                        redir[0] = pip[0];
361
                        if (pipe(pip) == -1) {
362
                                error("Cannot pipe");
363
                                return;
364
                        }
365
                        redir[1] = pip[1];
366
                        parsecmd(cmds, redir, flags | CMD_PIPE);
367
                        cmds = p + 1;
368
                        break;
369
                case '\0':
370
                        redir[0] = pip[0];
371
                        redir[1] = -1;
372
                        parsecmd(cmds, redir, flags);
373
                        return;
374
                }
375
                p++;
376
        }
377
}
378

    
379
static void
380
parseline(char *line)
381
{
382
        char *p, *cmds;
383

    
384
        p = cmds = line;
385
        while (*cmds) {
386
                switch (*p) {
387
                case ';':
388
                        *p = '\0';
389
                        parsepipe(cmds, 0);
390
                        cmds = p + 1;
391
                        break;
392
                case '&':
393
                        *p = '\0';
394
                        parsepipe(cmds, CMD_BACKGND);
395
                        cmds = p + 1;
396
                        break;
397
                case '\0':
398
                case '\n':
399
                case '#':
400
                        *p = '\0';
401
                        parsepipe(cmds, 0);
402
                        return;
403
                }
404
                p++;
405
        }
406
}
407

    
408
static char *
409
readline(int fd, char *line, int len)
410
{
411
        char *p = line;
412
        int nleft = len;
413
        int cnt;
414

    
415
        while (--nleft > 0) {
416
                cnt = read(fd, p, 1);
417
                if (cnt == -1)
418
                        return (char *)-1;        /* error */
419
                if (cnt == 0) {
420
                        if (p == line)
421
                                return NULL;        /* EOF */
422
                        break;
423
                }
424
                if (*p == '\n')
425
                        break;
426
                p++;
427
        }
428
        *p = '\0';
429
        return line;
430
}
431

    
432
static void
433
cmdloop(int fd)
434
{
435
        static char line[LINE_MAX];
436
        char *p;
437

    
438
        for (;;) {
439
                if (interact)
440
                        showprompt();
441

    
442
                line[0] = '\0';
443
                p = readline(fd, line, sizeof(line));
444
                if (p == (char *)-1)
445
                        continue;
446
                if (p == NULL)
447
                        break;
448
                parseline(line);
449
                tcsetpgrp(2, shpid);
450
        }
451
}
452

    
453
int
454
main(int argc, char **argv)
455
{
456
        int input;
457

    
458
        if (shpid == 0)
459
                shpid = getpid();
460

    
461
        if (setjmp(jmpbuf)) {
462
                argv = (char **)0;
463
                argc = 1;
464
        }
465
        interact = 1;
466
        initvar();
467

    
468
        if (argc == 1) {
469
                input = 0;
470
                if (isatty(0) && isatty(1)) {
471
                        interact = 1;
472
                        signal(SIGINT, SIG_IGN);
473
                        signal(SIGQUIT, SIG_IGN);
474
                        signal(SIGTERM, SIG_IGN);
475
                }
476
        } else {
477
                interact = 0;
478
                close(0);
479
                input = open(argv[1], O_RDONLY);
480
                if (input < 0) {
481
                        fprintf(stderr, "%s: cannot open\n", argv[1]);
482
                        interact = 1;
483
                        exit(1);
484
                }
485
        }
486
        cmdloop(input);
487
        exit(retval);
488
        return 0;
489
}