scoutos / prex-0.9.0 / usr / bin / sh / sh.c @ 03e9c04a
History | View | Annotate | Download (9.12 KB)
1 | 03e9c04a | Brad Neuman | /*
|
---|---|---|---|
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 | } |