blob: 9e467dc5415a33cf101cf6a304c3d74c0cc2a053 [file] [log] [blame]
Erik Andersen3522eb12000-03-12 23:49:18 +00001/* vi: set sw=4 ts=4: */
2/*
3 * BusyBox Shell
4 *
5 * Copyright (C) 2000 by Lineo, inc.
6 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
7 *
8 * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
9 * under the following liberal license: "We have placed this source code in the
10 * public domain. Use it in any project, free or commercial."
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 *
26 */
27
28#include "internal.h"
29#include <stdio.h>
30#include <stdlib.h>
31#include <ctype.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <glob.h>
35#include <signal.h>
36#include <string.h>
37#include <sys/ioctl.h>
38#include <sys/wait.h>
39#include <unistd.h>
40
41
42#define MAX_COMMAND_LEN 250 /* max length of a single command
43 string */
44#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
45
46enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND };
47
48struct jobSet {
49 struct job * head; /* head of list of running jobs */
50 struct job * fg; /* current foreground job */
51};
52
53struct redirectionSpecifier {
54 enum redirectionType type; /* type of redirection */
55 int fd; /* file descriptor being redirected */
56 char * filename; /* file to redirect fd to */
57};
58
59struct childProgram {
60 pid_t pid; /* 0 if exited */
61 char ** argv; /* program name and arguments */
62 int numRedirections; /* elements in redirection array */
63 struct redirectionSpecifier * redirections; /* I/O redirections */
64 glob_t globResult; /* result of parameter globbing */
65 int freeGlob; /* should we globfree(&globResult)? */
66 int isStopped; /* is the program currently running? */
67};
68
69struct job {
70 int jobId; /* job number */
71 int numProgs; /* total number of programs in job */
72 int runningProgs; /* number of programs running */
73 char * text; /* name of job */
74 char * cmdBuf; /* buffer various argv's point into */
75 pid_t pgrp; /* process group ID for the job */
76 struct childProgram * progs; /* array of programs in job */
77 struct job * next; /* to track background commands */
78 int stoppedProgs; /* number of programs alive, but stopped */
79};
80
81struct builtInCommand {
82 char *cmd; /* name */
83 char *descr; /* description */
84 char *usage; /* usage */
85 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
86};
87
88/* Some function prototypes */
89static int shell_cd(struct job* cmd, struct jobSet* junk);
90static int shell_env(struct job* dummy, struct jobSet* junk);
91static int shell_exit(struct job* cmd, struct jobSet* junk);
92static int shell_fg_bg(struct job* cmd, struct jobSet* jobList);
93static int shell_help(struct job* cmd, struct jobSet* junk);
94static int shell_jobs(struct job* dummy, struct jobSet* jobList);
95static int shell_pwd(struct job* dummy, struct jobSet* junk);
96static int shell_set(struct job* cmd, struct jobSet* junk);
97static int shell_source(struct job* cmd, struct jobSet* jobList);
98static int shell_unset(struct job* cmd, struct jobSet* junk);
99
100static void checkJobs(struct jobSet * jobList);
101static int getCommand(FILE * source, char * command);
102static int parseCommand(char ** commandPtr, struct job * job, int * isBg);
103static int setupRedirections(struct childProgram * prog);
104static int runCommand(struct job newJob, struct jobSet * jobList, int inBg);
105static int busy_loop(FILE * input);
106
107
108/* Table of built-in functions */
109static struct builtInCommand bltins[] = {
110 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
111 {"cd", "Change working directory", "cd [dir]", shell_cd},
112 {"env", "Print all environment variables", "env", shell_env},
113 {"exit", "Exit from shell()", "exit", shell_exit},
114 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
115 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
116 {"pwd", "Print current directory", "pwd", shell_pwd},
117 {"set", "Set environment variable", "set [VAR=value]", shell_set},
118 {"unset", "Unset environment variable", "unset VAR", shell_unset},
119 //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
120 {".", "Source-in and run commands in a file", ". filename", shell_source},
121 {"help", "List shell built-in commands", "help", shell_help},
122 {NULL, NULL, NULL, NULL}
123};
124
125static const char shell_usage[] =
126 "sh [FILE]...\n\n"
127 "The BusyBox command interpreter (shell).\n\n";
128
129
130static char cwd[1024];
131static char *prompt = "# ";
132
133
134/* built-in 'cd <path>' handler */
135static int shell_cd(struct job* cmd, struct jobSet* junk)
136{
137 char *newdir;
138 if (!cmd->progs[0].argv[1] == 1)
139 newdir = getenv("HOME");
140 else
141 newdir = cmd->progs[0].argv[1];
142 if (chdir(newdir)) {
143 printf("cd: %s: %s\n", newdir, strerror(errno));
144 return FALSE;
145 }
146 getcwd(cwd, sizeof(cwd));
147
148 return TRUE;
149}
150
151/* built-in 'env' handler */
152static int shell_env(struct job* dummy, struct jobSet* junk)
153{
154 char **e;
155
156 for (e = environ ; *e ; e++) {
157 fprintf(stdout, "%s\n", *e);
158 }
159 return (0);
160}
161
162/* built-in 'exit' handler */
163static int shell_exit(struct job* cmd, struct jobSet* junk)
164{
165 if (!cmd->progs[0].argv[1] == 1)
166 exit TRUE;
167 else
168 exit(atoi(cmd->progs[0].argv[1]));
169}
170
171/* built-in 'fg' and 'bg' handler */
172static int shell_fg_bg(struct job* cmd, struct jobSet* jobList)
173{
174 int i, jobNum;
175 struct job* job;
176
177 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
178 fprintf(stderr, "%s: exactly one argument is expected\n",
179 cmd->progs[0].argv[0]);
180 return FALSE;
181 }
182
183 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
184 fprintf(stderr, "%s: bad argument '%s'\n",
185 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
186 return FALSE;
187 }
188
189 for (job = jobList->head; job; job = job->next)
190 if (job->jobId == jobNum) break;
191
192 if (!job) {
193 fprintf(stderr, "%s: unknown job %d\n",
194 cmd->progs[0].argv[0], jobNum);
195 return FALSE;
196 }
197
198 if (*cmd->progs[0].argv[0] == 'f') {
199 /* Make this job the foreground job */
200
201 if (tcsetpgrp(0, job->pgrp))
202 perror("tcsetpgrp");
203 jobList->fg = job;
204 }
205
206 /* Restart the processes in the job */
207 for (i = 0; i < job->numProgs; i++)
208 job->progs[i].isStopped = 0;
209
210 kill(-job->pgrp, SIGCONT);
211
212 job->stoppedProgs = 0;
213
214 return TRUE;
215}
216
217/* built-in 'help' handler */
218static int shell_help(struct job* cmd, struct jobSet* junk)
219{
220 struct builtInCommand *x;
221
222 fprintf(stdout, "\nBuilt-in commands:\n");
223 fprintf(stdout, "-------------------\n");
224 for ( x=bltins; x->cmd; x++) {
225 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
226 }
227 fprintf(stdout, "\n\n");
228 return TRUE;
229}
230
231/* built-in 'jobs' handler */
232static int shell_jobs(struct job* dummy, struct jobSet* jobList)
233{
234 struct job * job;
235 char * statusString;
236 for (job = jobList->head; job; job = job->next) {
237 if (job->runningProgs == job->stoppedProgs)
238 statusString = "Stopped";
239 else
240 statusString = "Running";
241
242 printf(JOB_STATUS_FORMAT, job->jobId, statusString,
243 job->text);
244 }
245 return TRUE;
246}
247
248
249/* built-in 'pwd' handler */
250static int shell_pwd(struct job* dummy, struct jobSet* junk)
251{
252 getcwd(cwd, sizeof(cwd));
253 fprintf(stdout, "%s\n", cwd);
254 return TRUE;
255}
256
257/* built-in 'set VAR=value' handler */
258static int shell_set(struct job* cmd, struct jobSet* junk)
259{
260 int res;
261
262 if (!cmd->progs[0].argv[1] == 1) {
263 return (shell_env(cmd, junk));
264 }
265 res = putenv(cmd->progs[0].argv[1]);
266 if (res)
267 fprintf(stdout, "set: %s\n", strerror(errno));
268 return (res);
269}
270
271/* Built-in '.' handler (read-in and execute commands from file) */
272static int shell_source(struct job* cmd, struct jobSet* junk)
273{
274 FILE *input;
275 int status;
276
277 if (!cmd->progs[0].argv[1] == 1)
278 return FALSE;
279
280 input = fopen(cmd->progs[0].argv[1], "r");
281 if (!input) {
282 fprintf(stdout, "Couldn't open file '%s'\n", cmd->progs[0].argv[1]);
283 return FALSE;
284 }
285
286 /* Now run the file */
287 status = busy_loop(input);
288 return (status);
289}
290
291/* built-in 'unset VAR' handler */
292static int shell_unset(struct job* cmd, struct jobSet* junk)
293{
294 if (!cmd->progs[0].argv[1] == 1) {
295 fprintf(stdout, "unset: parameter required.\n");
296 return FALSE;
297 }
298 unsetenv(cmd->progs[0].argv[1]);
299 return TRUE;
300}
301
302/* free up all memory from a job */
303static void freeJob(struct job * cmd)
304{
305 int i;
306
307 for (i = 0; i < cmd->numProgs; i++) {
308 free(cmd->progs[i].argv);
309 if (cmd->progs[i].redirections) free(cmd->progs[i].redirections);
310 if (cmd->progs[i].freeGlob) globfree(&cmd->progs[i].globResult);
311 }
312 free(cmd->progs);
313 if (cmd->text) free(cmd->text);
314 free(cmd->cmdBuf);
315}
316
317/* remove a job from the jobList */
318static void removeJob(struct jobSet * jobList, struct job * job)
319{
320 struct job * prevJob;
321
322 freeJob(job);
323 if (job == jobList->head) {
324 jobList->head = job->next;
325 } else {
326 prevJob = jobList->head;
327 while (prevJob->next != job) prevJob = prevJob->next;
328 prevJob->next = job->next;
329 }
330
331 free(job);
332}
333
334/* Checks to see if any background processes have exited -- if they
335 have, figure out why and see if a job has completed */
336static void checkJobs(struct jobSet * jobList)
337{
338 struct job * job;
339 pid_t childpid;
340 int status;
341 int progNum=0;
342
343 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
344 for (job = jobList->head; job; job = job->next) {
345 progNum = 0;
346 while (progNum < job->numProgs &&
347 job->progs[progNum].pid != childpid)
348 progNum++;
349 if (progNum < job->numProgs) break;
350 }
351
352 if (WIFEXITED(status) || WIFSIGNALED(status)) {
353 /* child exited */
354 job->runningProgs--;
355 job->progs[progNum].pid = 0;
356
357 if (!job->runningProgs) {
358 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
359 removeJob(jobList, job);
360 }
361 } else {
362 /* child stopped */
363 job->stoppedProgs++;
364 job->progs[progNum].isStopped = 1;
365
366 if (job->stoppedProgs == job->numProgs) {
367 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", job->text);
368 }
369 }
370 }
371
372 if (childpid == -1 && errno != ECHILD)
373 perror("waitpid");
374}
375
376static int getCommand(FILE * source, char * command)
377{
378 if (source == stdin) {
379 fprintf(stdout, "%s %s", cwd, prompt);
380 fflush(stdout);
381 }
382
383 if (!fgets(command, MAX_COMMAND_LEN, source)) {
384 if (source == stdin) printf("\n");
385 return 1;
386 }
387
388 /* remove trailing newline */
389 command[strlen(command) - 1] = '\0';
390
391 return 0;
392}
393
394static void globLastArgument(struct childProgram * prog, int * argcPtr,
395 int * argcAllocedPtr)
396{
397 int argc = *argcPtr;
398 int argcAlloced = *argcAllocedPtr;
399 int rc;
400 int flags;
401 int i;
402 char * src, * dst;
403
404 if (argc > 1) { /* cmd->globResult is already initialized */
405 flags = GLOB_APPEND;
406 i = prog->globResult.gl_pathc;
407 } else {
408 prog->freeGlob = 1;
409 flags = 0;
410 i = 0;
411 }
412
413 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
414 if (rc == GLOB_NOSPACE) {
415 fprintf(stderr, "out of space during glob operation\n");
416 return;
417 } else if (rc == GLOB_NOMATCH ||
418 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
419 !strcmp(prog->argv[argc - 1],
420 prog->globResult.gl_pathv[i]))) {
421 /* we need to remove whatever \ quoting is still present */
422 src = dst = prog->argv[argc - 1];
423 while (*src) {
424 if (*src != '\\') *dst++ = *src;
425 src++;
426 }
427 *dst = '\0';
428 } else if (!rc) {
429 argcAlloced += (prog->globResult.gl_pathc - i);
430 prog->argv = realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
431 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
432 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
433 argc += (prog->globResult.gl_pathc - i - 1);
434 }
435
436 *argcAllocedPtr = argcAlloced;
437 *argcPtr = argc;
438}
439
440/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
441 line). If a valid command is found, commandPtr is set to point to
442 the beginning of the next command (if the original command had more
443 then one job associated with it) or NULL if no more commands are
444 present. */
445static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
446{
447 char * command;
448 char * returnCommand = NULL;
449 char * src, * buf, * chptr;
450 int argc = 0;
451 int done = 0;
452 int argvAlloced;
453 int i;
454 char quote = '\0';
455 int count;
456 struct childProgram * prog;
457
458 /* skip leading white space */
459 while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++;
460
461 /* this handles empty lines or leading '#' characters */
462 if (!**commandPtr || (**commandPtr=='#')) {
463 job->numProgs = 0;
464 *commandPtr = NULL;
465 return 0;
466 }
467
468 *isBg = 0;
469 job->numProgs = 1;
470 job->progs = malloc(sizeof(*job->progs));
471
472 /* We set the argv elements to point inside of this string. The
473 memory is freed by freeJob().
474
475 Getting clean memory relieves us of the task of NULL
476 terminating things and makes the rest of this look a bit
477 cleaner (though it is, admittedly, a tad less efficient) */
478 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
479 job->text = NULL;
480
481 prog = job->progs;
482 prog->numRedirections = 0;
483 prog->redirections = NULL;
484 prog->freeGlob = 0;
485 prog->isStopped = 0;
486
487 argvAlloced = 5;
488 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
489 prog->argv[0] = job->cmdBuf;
490
491 buf = command;
492 src = *commandPtr;
493 while (*src && !done) {
494 if (quote == *src) {
495 quote = '\0';
496 } else if (quote) {
497 if (*src == '\\') {
498 src++;
499 if (!*src) {
500 fprintf(stderr, "character expected after \\\n");
501 freeJob(job);
502 return 1;
503 }
504
505 /* in shell, "\'" should yield \' */
506 if (*src != quote) *buf++ = '\\';
507 } else if (*src == '*' || *src == '?' || *src == '[' ||
508 *src == ']')
509 *buf++ = '\\';
510 *buf++ = *src;
511 } else if (isspace(*src)) {
512 if (*prog->argv[argc]) {
513 buf++, argc++;
514 /* +1 here leaves room for the NULL which ends argv */
515 if ((argc + 1) == argvAlloced) {
516 argvAlloced += 5;
517 prog->argv = realloc(prog->argv,
518 sizeof(*prog->argv) * argvAlloced);
519 }
520 prog->argv[argc] = buf;
521
522 globLastArgument(prog, &argc, &argvAlloced);
523 }
524 } else switch (*src) {
525 case '"':
526 case '\'':
527 quote = *src;
528 break;
529
530 case '#': /* comment */
531 done = 1;
532 break;
533
534 case '>': /* redirections */
535 case '<':
536 i = prog->numRedirections++;
537 prog->redirections = realloc(prog->redirections,
538 sizeof(*prog->redirections) * (i + 1));
539
540 prog->redirections[i].fd = -1;
541 if (buf != prog->argv[argc]) {
542 /* the stuff before this character may be the file number
543 being redirected */
544 prog->redirections[i].fd = strtol(prog->argv[argc], &chptr, 10);
545
546 if (*chptr && *prog->argv[argc]) {
547 buf++, argc++;
548 globLastArgument(prog, &argc, &argvAlloced);
549 }
550 }
551
552 if (prog->redirections[i].fd == -1) {
553 if (*src == '>')
554 prog->redirections[i].fd = 1;
555 else
556 prog->redirections[i].fd = 0;
557 }
558
559 if (*src++ == '>') {
560 if (*src == '>')
561 prog->redirections[i].type = REDIRECT_APPEND, src++;
562 else
563 prog->redirections[i].type = REDIRECT_OVERWRITE;
564 } else {
565 prog->redirections[i].type = REDIRECT_INPUT;
566 }
567
568 /* This isn't POSIX sh compliant. Oh well. */
569 chptr = src;
570 while (isspace(*chptr)) chptr++;
571
572 if (!*chptr) {
573 fprintf(stderr, "file name expected after %c\n", *src);
574 freeJob(job);
575 return 1;
576 }
577
578 prog->redirections[i].filename = buf;
579 while (*chptr && !isspace(*chptr))
580 *buf++ = *chptr++;
581
582 src = chptr - 1; /* we src++ later */
583 prog->argv[argc] = ++buf;
584 break;
585
586 case '|': /* pipe */
587 /* finish this command */
588 if (*prog->argv[argc]) argc++;
589 if (!argc) {
590 fprintf(stderr, "empty command in pipe\n");
591 freeJob(job);
592 return 1;
593 }
594 prog->argv[argc] = NULL;
595
596 /* and start the next */
597 job->numProgs++;
598 job->progs = realloc(job->progs,
599 sizeof(*job->progs) * job->numProgs);
600 prog = job->progs + (job->numProgs - 1);
601 prog->numRedirections = 0;
602 prog->redirections = NULL;
603 prog->freeGlob = 0;
604 argc = 0;
605
606 argvAlloced = 5;
607 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
608 prog->argv[0] = ++buf;
609
610 src++;
611 while (*src && isspace(*src)) src++;
612
613 if (!*src) {
614 fprintf(stderr, "empty command in pipe\n");
615 return 1;
616 }
617 src--; /* we'll ++ it at the end of the loop */
618
619 break;
620
621 case '&': /* background */
622 *isBg = 1;
623 case ';': /* multiple commands */
624 done = 1;
625 returnCommand = *commandPtr + (src - *commandPtr) + 1;
626 break;
627
628 case '\\':
629 src++;
630 if (!*src) {
631 freeJob(job);
632 fprintf(stderr, "character expected after \\\n");
633 return 1;
634 }
635 if (*src == '*' || *src == '[' || *src == ']' || *src == '?')
636 *buf++ = '\\';
637 /* fallthrough */
638 default:
639 *buf++ = *src;
640 }
641
642 src++;
643 }
644
645 if (*prog->argv[argc]) {
646 argc++;
647 globLastArgument(prog, &argc, &argvAlloced);
648 }
649 if (!argc) {
650 freeJob(job);
651 return 0;
652 }
653 prog->argv[argc] = NULL;
654
655 if (!returnCommand) {
656 job->text = malloc(strlen(*commandPtr) + 1);
657 strcpy(job->text, *commandPtr);
658 } else {
659 /* This leaves any trailing spaces, which is a bit sloppy */
660
661 count = returnCommand - *commandPtr;
662 job->text = malloc(count + 1);
663 strncpy(job->text, *commandPtr, count);
664 job->text[count] = '\0';
665 }
666
667 *commandPtr = returnCommand;
668
669 return 0;
670}
671
672static int runCommand(struct job newJob, struct jobSet * jobList,
673 int inBg)
674{
675 struct job * job;
676 int i;
677 int nextin, nextout;
678 int pipefds[2]; /* pipefd[0] is for reading */
679 struct builtInCommand *x;
680
681 /* handle built-ins here -- we don't fork() so we can't background
682 these very easily */
683 for( x=bltins ; x->cmd ; x++) {
684 if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
685 return(x->function(&newJob, jobList));
686 }
687 }
688
689 nextin = 0, nextout = 1;
690 for (i = 0; i < newJob.numProgs; i++) {
691 if ((i + 1) < newJob.numProgs) {
692 pipe(pipefds);
693 nextout = pipefds[1];
694 } else {
695 nextout = 1;
696 }
697
698 if (!(newJob.progs[i].pid = fork())) {
699 signal(SIGTTOU, SIG_DFL);
700
701 if (nextin != 0) {
702 dup2(nextin, 0);
703 close(nextin);
704 }
705
706 if (nextout != 1) {
707 dup2(nextout, 1);
708 close(nextout);
709 }
710
711 /* explicit redirections override pipes */
712 setupRedirections(newJob.progs + i);
713
714 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
715 fatalError( "sh: %s: %s\n", newJob.progs[i].argv[0],
716 strerror(errno));
717 }
718
719 /* put our child in the process group whose leader is the
720 first process in this pipe */
721 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
722
723 if (nextin != 0) close(nextin);
724 if (nextout != 1) close(nextout);
725
726 /* If there isn't another process, nextin is garbage
727 but it doesn't matter */
728 nextin = pipefds[0];
729 }
730
731 newJob.pgrp = newJob.progs[0].pid;
732
733 /* find the ID for the job to use */
734 newJob.jobId = 1;
735 for (job = jobList->head; job; job = job->next)
736 if (job->jobId >= newJob.jobId)
737 newJob.jobId = job->jobId + 1;
738
739 /* add the job to the list of running jobs */
740 if (!jobList->head) {
741 job = jobList->head = malloc(sizeof(*job));
742 } else {
743 for (job = jobList->head; job->next; job = job->next);
744 job->next = malloc(sizeof(*job));
745 job = job->next;
746 }
747
748 *job = newJob;
749 job->next = NULL;
750 job->runningProgs = job->numProgs;
751 job->stoppedProgs = 0;
752
753 if (inBg) {
754 /* we don't wait for background jobs to return -- append it
755 to the list of backgrounded jobs and leave it alone */
756
757 printf("[%d] %d\n", job->jobId,
758 newJob.progs[newJob.numProgs - 1].pid);
759 } else {
760 jobList->fg = job;
761
762 /* move the new process group into the foreground */
763
764 if (tcsetpgrp(0, newJob.pgrp))
765 perror("tcsetpgrp");
766 }
767
768 return 0;
769}
770
771static int setupRedirections(struct childProgram * prog)
772{
773 int i;
774 int openfd;
775 int mode=O_RDONLY;
776 struct redirectionSpecifier * redir = prog->redirections;
777
778 for (i = 0; i < prog->numRedirections; i++, redir++) {
779 switch (redir->type) {
780 case REDIRECT_INPUT:
781 mode = O_RDONLY;
782 break;
783 case REDIRECT_OVERWRITE:
784 mode = O_RDWR | O_CREAT | O_TRUNC;
785 break;
786 case REDIRECT_APPEND:
787 mode = O_RDWR | O_CREAT | O_APPEND;
788 break;
789 }
790
791 openfd = open(redir->filename, mode, 0666);
792 if (openfd < 0) {
793 /* this could get lost if stderr has been redirected, but
794 bash and ash both lose it as well (though zsh doesn't!) */
795 fprintf(stderr, "error opening %s: %s\n", redir->filename,
796 strerror(errno));
797 return 1;
798 }
799
800 if (openfd != redir->fd) {
801 dup2(openfd, redir->fd);
802 close(openfd);
803 }
804 }
805
806 return 0;
807}
808
809
810static int busy_loop(FILE * input)
811{
812 char command[MAX_COMMAND_LEN + 1];
813 char * nextCommand = NULL;
814 struct jobSet jobList = { NULL, NULL };
815 struct job newJob;
816 int i;
817 int status;
818 int inBg;
819
820 /* don't pay any attention to this signal; it just confuses
821 things and isn't really meant for shells anyway */
822 signal(SIGTTOU, SIG_IGN);
823
824 while (1) {
825 if (!jobList.fg) {
826 /* no job is in the foreground */
827
828 /* see if any background processes have exited */
829 checkJobs(&jobList);
830
831 if (!nextCommand) {
832 if (getCommand(input, command)) break;
833 nextCommand = command;
834 }
835
836 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
837 newJob.numProgs) {
838 runCommand(newJob, &jobList, inBg);
839 }
840 } else {
841 /* a job is running in the foreground; wait for it */
842 i = 0;
843 while (!jobList.fg->progs[i].pid ||
844 jobList.fg->progs[i].isStopped) i++;
845
846 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
847
848 if (WIFEXITED(status) || WIFSIGNALED(status)) {
849 /* the child exited */
850 jobList.fg->runningProgs--;
851 jobList.fg->progs[i].pid = 0;
852
853 if (!jobList.fg->runningProgs) {
854 /* child exited */
855
856 removeJob(&jobList, jobList.fg);
857 jobList.fg = NULL;
858
859 /* move the shell to the foreground */
860 if (tcsetpgrp(0, getpid()))
861 perror("tcsetpgrp");
862 }
863 } else {
864 /* the child was stopped */
865 jobList.fg->stoppedProgs++;
866 jobList.fg->progs[i].isStopped = 1;
867
868 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
869 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
870 "Stopped", jobList.fg->text);
871 jobList.fg = NULL;
872 }
873 }
874
875 if (!jobList.fg) {
876 /* move the shell to the foreground */
877 if (tcsetpgrp(0, getpid()))
878 perror("tcsetpgrp");
879 }
880 }
881 }
882
883 return 0;
884}
885
886
887int shell_main(int argc, char ** argv)
888{
889 FILE * input = stdin;
890
891 if (argc > 2) {
892 usage( shell_usage);
893 }
894 /* initialize the cwd */
895 getcwd(cwd, sizeof(cwd));
896
897
898 //if (argv[0] && argv[0][0] == '-') {
899 // shell_source("/etc/profile");
900 //}
901
902 if (argc < 2) {
903 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
904 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
905 } else {
906 input = fopen(argv[1], "r");
907 if (!input)
908 fatalError("A: Couldn't open file '%s': %s\n", argv[1], strerror(errno));
909// else
910// fatalError("Got it.\n");
911 //exit(shell_source(argv[1]));
912 }
913
914 return (busy_loop( input));
915}