1 /* vi: set sw=4 ts=4: */
3 * lash -- the BusyBox Lame-Ass SHell
5 * Copyright (C) 2000 by Lineo, inc.
6 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
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."
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.
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.
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
29 #define BB_FEATURE_SH_BACKTICKS
42 #include <sys/ioctl.h>
45 #ifdef BB_FEATURE_SH_COMMAND_EDITING
49 #define MAX_LINE 256 /* size of input buffer for `read' builtin */
50 #define MAX_READ 128 /* size of input buffer for `read' builtin */
51 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
54 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
59 struct job *head; /* head of list of running jobs */
60 struct job *fg; /* current foreground job */
63 struct redirectionSpecifier {
64 enum redirectionType type; /* type of redirection */
65 int fd; /* file descriptor being redirected */
66 char *filename; /* file to redirect fd to */
70 pid_t pid; /* 0 if exited */
71 char **argv; /* program name and arguments */
72 int numRedirections; /* elements in redirection array */
73 struct redirectionSpecifier *redirections; /* I/O redirections */
74 glob_t globResult; /* result of parameter globbing */
75 int freeGlob; /* should we globfree(&globResult)? */
76 int isStopped; /* is the program currently running? */
80 int jobId; /* job number */
81 int numProgs; /* total number of programs in job */
82 int runningProgs; /* number of programs running */
83 char *text; /* name of job */
84 char *cmdBuf; /* buffer various argv's point into */
85 pid_t pgrp; /* process group ID for the job */
86 struct childProgram *progs; /* array of programs in job */
87 struct job *next; /* to track background commands */
88 int stoppedProgs; /* number of programs alive, but stopped */
91 struct builtInCommand {
93 char *descr; /* description */
94 char *usage; /* usage */
95 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
98 /* function prototypes for builtins */
99 static int builtin_cd(struct job *cmd, struct jobSet *junk);
100 static int builtin_env(struct job *dummy, struct jobSet *junk);
101 static int builtin_exit(struct job *cmd, struct jobSet *junk);
102 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
103 static int builtin_help(struct job *cmd, struct jobSet *junk);
104 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
105 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
106 static int builtin_export(struct job *cmd, struct jobSet *junk);
107 static int builtin_source(struct job *cmd, struct jobSet *jobList);
108 static int builtin_unset(struct job *cmd, struct jobSet *junk);
109 static int builtin_read(struct job *cmd, struct jobSet *junk);
112 /* function prototypes for shell stuff */
113 static void checkJobs(struct jobSet *jobList);
114 static int getCommand(FILE * source, char *command);
115 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
116 static int setupRedirections(struct childProgram *prog);
117 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
118 static int busy_loop(FILE * input);
121 /* Table of built-in functions (these are non-forking builtins, meaning they
122 * can change global variables in the parent shell process but they will not
123 * work with pipes and redirects; 'unset foo | whatever' will not work) */
124 static struct builtInCommand bltins[] = {
125 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
126 {"cd", "Change working directory", "cd [dir]", builtin_cd},
127 {"exit", "Exit from shell()", "exit", builtin_exit},
128 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
129 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
130 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
131 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
132 {"read", "Input environment variable", "read [VAR]", builtin_read},
133 {NULL, NULL, NULL, NULL}
136 /* Table of forking built-in functions (things that fork cannot change global
137 * variables in the parent process, such as the current working directory) */
138 static struct builtInCommand bltins_forking[] = {
139 {"env", "Print all environment variables", "env", builtin_env},
140 {"pwd", "Print current directory", "pwd", builtin_pwd},
141 {".", "Source-in and run commands in a file", ". filename", builtin_source},
142 {"help", "List shell built-in commands", "help", builtin_help},
143 {NULL, NULL, NULL, NULL}
146 static char *prompt = "# ";
148 static char *local_pending_command = NULL;
150 #ifdef BB_FEATURE_SH_COMMAND_EDITING
151 void win_changed(int junk)
153 struct winsize win = { 0, 0, 0, 0 };
154 ioctl(0, TIOCGWINSZ, &win);
155 if (win.ws_col > 0) {
156 cmdedit_setwidth( win.ws_col - 1);
162 /* built-in 'cd <path>' handler */
163 static int builtin_cd(struct job *cmd, struct jobSet *junk)
167 if (!cmd->progs[0].argv[1] == 1)
168 newdir = getenv("HOME");
170 newdir = cmd->progs[0].argv[1];
172 printf("cd: %s: %s\n", newdir, strerror(errno));
175 getcwd(cwd, sizeof(char)*MAX_LINE);
180 /* built-in 'env' handler */
181 static int builtin_env(struct job *dummy, struct jobSet *junk)
185 for (e = environ; *e; e++) {
186 fprintf(stdout, "%s\n", *e);
191 /* built-in 'exit' handler */
192 static int builtin_exit(struct job *cmd, struct jobSet *junk)
194 if (!cmd->progs[0].argv[1] == 1)
197 return(atoi(cmd->progs[0].argv[1]));
200 /* built-in 'fg' and 'bg' handler */
201 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
204 struct job *job=NULL;
206 if (!jobList->head) {
207 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
208 errorMsg("%s: exactly one argument is expected\n",
209 cmd->progs[0].argv[0]);
212 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
213 errorMsg("%s: bad argument '%s'\n",
214 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
216 for (job = jobList->head; job; job = job->next) {
217 if (job->jobId == jobNum) {
227 errorMsg("%s: unknown job %d\n",
228 cmd->progs[0].argv[0], jobNum);
232 if (*cmd->progs[0].argv[0] == 'f') {
233 /* Make this job the foreground job */
234 /* suppress messages when run from /linuxrc mag@sysgo.de */
235 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
240 /* Restart the processes in the job */
241 for (i = 0; i < job->numProgs; i++)
242 job->progs[i].isStopped = 0;
244 kill(-job->pgrp, SIGCONT);
246 job->stoppedProgs = 0;
251 /* built-in 'help' handler */
252 static int builtin_help(struct job *dummy, struct jobSet *junk)
254 struct builtInCommand *x;
256 fprintf(stdout, "\nBuilt-in commands:\n");
257 fprintf(stdout, "-------------------\n");
258 for (x = bltins; x->cmd; x++) {
259 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
261 for (x = bltins_forking; x->cmd; x++) {
262 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
264 fprintf(stdout, "\n\n");
268 /* built-in 'jobs' handler */
269 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
274 for (job = jobList->head; job; job = job->next) {
275 if (job->runningProgs == job->stoppedProgs)
276 statusString = "Stopped";
278 statusString = "Running";
280 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
286 /* built-in 'pwd' handler */
287 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
289 getcwd(cwd, sizeof(char)*MAX_LINE);
290 fprintf(stdout, "%s\n", cwd);
294 /* built-in 'export VAR=value' handler */
295 static int builtin_export(struct job *cmd, struct jobSet *junk)
299 if (!cmd->progs[0].argv[1] == 1) {
300 return (builtin_env(cmd, junk));
302 res = putenv(cmd->progs[0].argv[1]);
304 fprintf(stdout, "export: %s\n", strerror(errno));
308 /* built-in 'read VAR' handler */
309 static int builtin_read(struct job *cmd, struct jobSet *junk)
311 int res = 0, len, newlen;
313 char string[MAX_READ];
315 if (cmd->progs[0].argv[1]) {
316 /* argument (VAR) given: put "VAR=" into buffer */
317 strcpy(string, cmd->progs[0].argv[1]);
318 len = strlen(string);
321 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
322 newlen = strlen(string);
324 string[--newlen] = '\0'; /* chomp trailing newline */
326 ** string should now contain "VAR=<value>"
327 ** copy it (putenv() won't do that, so we must make sure
328 ** the string resides in a static buffer!)
331 if((s = strdup(string)))
334 fprintf(stdout, "read: %s\n", strerror(errno));
337 fgets(string, sizeof(string), stdin);
342 /* Built-in '.' handler (read-in and execute commands from file) */
343 static int builtin_source(struct job *cmd, struct jobSet *junk)
348 if (!cmd->progs[0].argv[1] == 1)
351 input = fopen(cmd->progs[0].argv[1], "r");
353 fprintf(stdout, "Couldn't open file '%s'\n",
354 cmd->progs[0].argv[1]);
358 /* Now run the file */
359 status = busy_loop(input);
363 /* built-in 'unset VAR' handler */
364 static int builtin_unset(struct job *cmd, struct jobSet *junk)
366 if (!cmd->progs[0].argv[1] == 1) {
367 fprintf(stdout, "unset: parameter required.\n");
370 unsetenv(cmd->progs[0].argv[1]);
374 /* free up all memory from a job */
375 static void freeJob(struct job *cmd)
379 for (i = 0; i < cmd->numProgs; i++) {
380 free(cmd->progs[i].argv);
381 if (cmd->progs[i].redirections)
382 free(cmd->progs[i].redirections);
383 if (cmd->progs[i].freeGlob)
384 globfree(&cmd->progs[i].globResult);
390 memset(cmd, 0, sizeof(struct job));
393 /* remove a job from the jobList */
394 static void removeJob(struct jobSet *jobList, struct job *job)
399 if (job == jobList->head) {
400 jobList->head = job->next;
402 prevJob = jobList->head;
403 while (prevJob->next != job)
404 prevJob = prevJob->next;
405 prevJob->next = job->next;
411 /* Checks to see if any background processes have exited -- if they
412 have, figure out why and see if a job has completed */
413 static void checkJobs(struct jobSet *jobList)
420 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
421 for (job = jobList->head; job; job = job->next) {
423 while (progNum < job->numProgs &&
424 job->progs[progNum].pid != childpid) progNum++;
425 if (progNum < job->numProgs)
429 /* This happens on backticked commands */
433 if (WIFEXITED(status) || WIFSIGNALED(status)) {
436 job->progs[progNum].pid = 0;
438 if (!job->runningProgs) {
439 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
440 removeJob(jobList, job);
445 job->progs[progNum].isStopped = 1;
447 if (job->stoppedProgs == job->numProgs) {
448 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
454 if (childpid == -1 && errno != ECHILD)
458 static int getCommand(FILE * source, char *command)
460 if (source == NULL) {
461 if (local_pending_command) {
462 /* a command specified (-c option): return it & mark it done */
463 strcpy(command, local_pending_command);
464 free(local_pending_command);
465 local_pending_command = NULL;
471 if (source == stdin) {
472 #ifdef BB_FEATURE_SH_COMMAND_EDITING
475 len=fprintf(stdout, "%s %s", cwd, prompt);
477 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
478 sprintf(promptStr, "%s %s", cwd, prompt);
479 cmdedit_read_input(promptStr, command);
483 fprintf(stdout, "%s %s", cwd, prompt);
488 if (!fgets(command, BUFSIZ - 2, source)) {
494 /* remove trailing newline */
495 command[strlen(command) - 1] = '\0';
500 static void globLastArgument(struct childProgram *prog, int *argcPtr,
504 int argcAlloced = *argcAllocedPtr;
508 char *src, *dst, *var;
510 if (argc > 1) { /* cmd->globResult is already initialized */
512 i = prog->globResult.gl_pathc;
518 /* do shell variable substitution */
519 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
520 prog->argv[argc - 1] = var;
522 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
523 if (rc == GLOB_NOSPACE) {
524 errorMsg("out of space during glob operation\n");
526 } else if (rc == GLOB_NOMATCH ||
527 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
528 !strcmp(prog->argv[argc - 1],
529 prog->globResult.gl_pathv[i]))) {
530 /* we need to remove whatever \ quoting is still present */
531 src = dst = prog->argv[argc - 1];
539 argcAlloced += (prog->globResult.gl_pathc - i);
541 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
542 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
543 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
544 argc += (prog->globResult.gl_pathc - i - 1);
547 *argcAllocedPtr = argcAlloced;
551 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
552 line). If a valid command is found, commandPtr is set to point to
553 the beginning of the next command (if the original command had more
554 then one job associated with it) or NULL if no more commands are
556 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
559 char *returnCommand = NULL;
560 char *src, *buf, *chptr;
567 struct childProgram *prog;
569 /* skip leading white space */
570 while (**commandPtr && isspace(**commandPtr))
573 /* this handles empty lines or leading '#' characters */
574 if (!**commandPtr || (**commandPtr == '#')) {
581 job->progs = xmalloc(sizeof(*job->progs));
583 /* We set the argv elements to point inside of this string. The
584 memory is freed by freeJob(). Allocate twice the original
585 length in case we need to quote every single character.
587 Getting clean memory relieves us of the task of NULL
588 terminating things and makes the rest of this look a bit
589 cleaner (though it is, admittedly, a tad less efficient) */
590 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
594 prog->numRedirections = 0;
595 prog->redirections = NULL;
600 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
601 prog->argv[0] = job->cmdBuf;
605 while (*src && !done) {
612 errorMsg("character expected after \\\n");
617 /* in shell, "\'" should yield \' */
620 } else if (*src == '*' || *src == '?' || *src == '[' ||
621 *src == ']') *buf++ = '\\';
623 } else if (isspace(*src)) {
624 if (*prog->argv[argc]) {
626 /* +1 here leaves room for the NULL which ends argv */
627 if ((argc + 1) == argvAlloced) {
629 prog->argv = realloc(prog->argv,
630 sizeof(*prog->argv) *
633 globLastArgument(prog, &argc, &argvAlloced);
634 prog->argv[argc] = buf;
643 case '#': /* comment */
647 case '>': /* redirections */
649 i = prog->numRedirections++;
650 prog->redirections = realloc(prog->redirections,
651 sizeof(*prog->redirections) *
654 prog->redirections[i].fd = -1;
655 if (buf != prog->argv[argc]) {
656 /* the stuff before this character may be the file number
658 prog->redirections[i].fd =
659 strtol(prog->argv[argc], &chptr, 10);
661 if (*chptr && *prog->argv[argc]) {
663 globLastArgument(prog, &argc, &argvAlloced);
664 prog->argv[argc] = buf;
668 if (prog->redirections[i].fd == -1) {
670 prog->redirections[i].fd = 1;
672 prog->redirections[i].fd = 0;
677 prog->redirections[i].type =
678 REDIRECT_APPEND, src++;
680 prog->redirections[i].type = REDIRECT_OVERWRITE;
682 prog->redirections[i].type = REDIRECT_INPUT;
685 /* This isn't POSIX sh compliant. Oh well. */
687 while (isspace(*chptr))
691 errorMsg("file name expected after %c\n", *src);
697 prog->redirections[i].filename = buf;
698 while (*chptr && !isspace(*chptr))
701 src = chptr - 1; /* we src++ later */
702 prog->argv[argc] = ++buf;
706 /* finish this command */
707 if (*prog->argv[argc])
710 errorMsg("empty command in pipe\n");
715 prog->argv[argc] = NULL;
717 /* and start the next */
719 job->progs = realloc(job->progs,
720 sizeof(*job->progs) * job->numProgs);
721 prog = job->progs + (job->numProgs - 1);
722 prog->numRedirections = 0;
723 prog->redirections = NULL;
728 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
729 prog->argv[0] = ++buf;
732 while (*src && isspace(*src))
736 errorMsg("empty command in pipe\n");
741 src--; /* we'll ++ it at the end of the loop */
745 case '&': /* background */
747 case ';': /* multiple commands */
749 returnCommand = *commandPtr + (src - *commandPtr) + 1;
755 errorMsg("character expected after \\\n");
759 if (*src == '*' || *src == '[' || *src == ']'
760 || *src == '?') *buf++ = '\\';
762 #ifdef BB_FEATURE_SH_BACKTICKS
764 /* Exec a backtick-ed command */
766 char* charptr1=NULL, *charptr2;
769 struct jobSet njobList = { NULL, NULL };
773 ptr=strchr(++src, '`');
775 fprintf(stderr, "Unmatched '`' in command\n");
780 /* Make some space to hold just the backticked command */
781 charptr1 = charptr2 = xmalloc(1+ptr-src);
782 snprintf(charptr1, 1+ptr-src, src);
783 newJob = xmalloc(sizeof(struct job));
784 /* Now parse and run the backticked command */
785 if (!parseCommand(&charptr1, newJob, &njobList, isBg)
786 && newJob->numProgs) {
788 runCommand(newJob, &njobList, 0, pipefd);
794 /* Make a copy of any stuff left over in the command
795 * line after the second backtick */
796 charptr2 = xmalloc(strlen(ptr)+1);
797 memcpy(charptr2, ptr+1, strlen(ptr));
800 /* Copy the output from the backtick-ed command into the
801 * command line, making extra room as needed */
803 charptr1 = xmalloc(BUFSIZ);
804 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
805 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
806 if (newSize > BUFSIZ) {
807 *commandPtr=realloc(*commandPtr, src - *commandPtr +
808 size + 1 + strlen(charptr2));
810 memcpy(src, charptr1, size);
818 /* Now paste into the *commandPtr all the stuff
819 * leftover after the second backtick */
820 memcpy(src, charptr2, strlen(charptr2));
823 /* Now recursively call parseCommand to deal with the new
824 * and improved version of the command line with the backtick
825 * results expanded in place... */
827 return(parseCommand(commandPtr, job, jobList, isBg));
830 #endif // BB_FEATURE_SH_BACKTICKS
838 if (*prog->argv[argc]) {
840 globLastArgument(prog, &argc, &argvAlloced);
846 prog->argv[argc] = NULL;
848 if (!returnCommand) {
849 job->text = xmalloc(strlen(*commandPtr) + 1);
850 strcpy(job->text, *commandPtr);
852 /* This leaves any trailing spaces, which is a bit sloppy */
853 count = returnCommand - *commandPtr;
854 job->text = xmalloc(count + 1);
855 strncpy(job->text, *commandPtr, count);
856 job->text[count] = '\0';
859 *commandPtr = returnCommand;
864 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
869 int pipefds[2]; /* pipefd[0] is for reading */
870 struct builtInCommand *x;
871 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
872 const struct BB_applet *a = applets;
876 nextin = 0, nextout = 1;
877 for (i = 0; i < newJob->numProgs; i++) {
878 if ((i + 1) < newJob->numProgs) {
880 nextout = pipefds[1];
882 if (outPipe[1]!=-1) {
883 nextout = outPipe[1];
889 /* Check if the command matches any non-forking builtins */
890 for (x = bltins; x->cmd; x++) {
891 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
892 return (x->function(newJob, jobList));
896 if (!(newJob->progs[i].pid = fork())) {
897 signal(SIGTTOU, SIG_DFL);
899 if (outPipe[1]!=-1) {
913 /* explicit redirections override pipes */
914 setupRedirections(newJob->progs + i);
916 /* Check if the command matches any of the other builtins */
917 for (x = bltins_forking; x->cmd; x++) {
918 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
919 exit (x->function(newJob, jobList));
922 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
923 /* Check if the command matches any busybox internal commands here */
924 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
925 * works, but '/bin/cat' doesn't ) */
926 while (a->name != 0) {
927 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
929 char** argv=newJob->progs[i].argv;
930 for(argc=0;*argv!=NULL; argv++, argc++);
931 exit((*(a->main)) (argc, newJob->progs[i].argv));
937 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
938 fatalError("sh: %s: %s\n", newJob->progs[i].argv[0],
941 if (outPipe[1]!=-1) {
945 /* put our child in the process group whose leader is the
946 first process in this pipe */
947 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
953 /* If there isn't another process, nextin is garbage
954 but it doesn't matter */
958 newJob->pgrp = newJob->progs[0].pid;
960 /* find the ID for the job to use */
962 for (job = jobList->head; job; job = job->next)
963 if (job->jobId >= newJob->jobId)
964 newJob->jobId = job->jobId + 1;
966 /* add the job to the list of running jobs */
967 if (!jobList->head) {
968 job = jobList->head = malloc(sizeof(*job));
970 for (job = jobList->head; job->next; job = job->next);
971 job->next = malloc(sizeof(*job));
977 job->runningProgs = job->numProgs;
978 job->stoppedProgs = 0;
981 /* we don't wait for background jobs to return -- append it
982 to the list of backgrounded jobs and leave it alone */
983 printf("[%d] %d\n", job->jobId,
984 newJob->progs[newJob->numProgs - 1].pid);
988 /* move the new process group into the foreground */
989 /* suppress messages when run from /linuxrc mag@sysgo.de */
990 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
997 static int setupRedirections(struct childProgram *prog)
1001 int mode = O_RDONLY;
1002 struct redirectionSpecifier *redir = prog->redirections;
1004 for (i = 0; i < prog->numRedirections; i++, redir++) {
1005 switch (redir->type) {
1006 case REDIRECT_INPUT:
1009 case REDIRECT_OVERWRITE:
1010 mode = O_RDWR | O_CREAT | O_TRUNC;
1012 case REDIRECT_APPEND:
1013 mode = O_RDWR | O_CREAT | O_APPEND;
1017 openfd = open(redir->filename, mode, 0666);
1019 /* this could get lost if stderr has been redirected, but
1020 bash and ash both lose it as well (though zsh doesn't!) */
1021 errorMsg("error opening %s: %s\n", redir->filename,
1026 if (openfd != redir->fd) {
1027 dup2(openfd, redir->fd);
1036 static int busy_loop(FILE * input)
1039 char *nextCommand = NULL;
1040 struct jobSet jobList = { NULL, NULL };
1047 /* save current owner of TTY so we can restore it on exit */
1048 parent_pgrp = tcgetpgrp(0);
1050 command = (char *) calloc(BUFSIZ, sizeof(char));
1052 /* don't pay any attention to this signal; it just confuses
1053 things and isn't really meant for shells anyway */
1054 signal(SIGTTOU, SIG_IGN);
1058 /* no job is in the foreground */
1060 /* see if any background processes have exited */
1061 checkJobs(&jobList);
1064 if (getCommand(input, command))
1066 nextCommand = command;
1069 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1071 int pipefds[2] = {-1,-1};
1072 runCommand(&newJob, &jobList, inBg, pipefds);
1075 command = (char *) calloc(BUFSIZ, sizeof(char));
1079 /* a job is running in the foreground; wait for it */
1081 while (!jobList.fg->progs[i].pid ||
1082 jobList.fg->progs[i].isStopped) i++;
1084 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1086 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1087 /* the child exited */
1088 jobList.fg->runningProgs--;
1089 jobList.fg->progs[i].pid = 0;
1091 if (!jobList.fg->runningProgs) {
1094 removeJob(&jobList, jobList.fg);
1098 /* the child was stopped */
1099 jobList.fg->stoppedProgs++;
1100 jobList.fg->progs[i].isStopped = 1;
1102 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1103 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1104 "Stopped", jobList.fg->text);
1110 /* move the shell to the foreground */
1111 /* suppress messages when run from /linuxrc mag@sysgo.de */
1112 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1113 perror("tcsetpgrp");
1119 /* return controlling TTY back to parent process group before exiting */
1120 if (tcsetpgrp(0, parent_pgrp))
1121 perror("tcsetpgrp");
1123 /* return exit status if called with "-c" */
1124 if (input == NULL && WIFEXITED(status))
1125 return WEXITSTATUS(status);
1132 int shell_main(int argc, char **argv)
1134 FILE *input = stdin;
1136 /* initialize the cwd -- this is never freed...*/
1137 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1138 getcwd(cwd, sizeof(char)*MAX_LINE);
1140 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1142 signal(SIGWINCH, win_changed);
1146 //if (argv[0] && argv[0][0] == '-') {
1147 // builtin_source("/etc/profile");
1152 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1153 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1155 if (argv[1][0]=='-' && argv[1][1]=='c') {
1157 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1158 if (local_pending_command == 0) {
1159 fatalError("out of memory\n");
1161 for(i=2; i<argc; i++)
1163 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1164 local_pending_command = realloc(local_pending_command,
1165 strlen(local_pending_command) + strlen(argv[i]));
1166 if (local_pending_command==NULL)
1167 fatalError("commands for -c option too long\n");
1169 strcat(local_pending_command, argv[i]);
1170 if ( (i + 1) < argc)
1171 strcat(local_pending_command, " ");
1176 else if (argv[1][0]=='-') {
1180 input = fopen(argv[1], "r");
1182 fatalError("Couldn't open file '%s': %s\n", argv[1],
1188 return (busy_loop(input));