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