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_READ 128 /* size of input buffer for `read' builtin */
50 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
53 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
58 struct job *head; /* head of list of running jobs */
59 struct job *fg; /* current foreground job */
62 struct redirectionSpecifier {
63 enum redirectionType type; /* type of redirection */
64 int fd; /* file descriptor being redirected */
65 char *filename; /* file to redirect fd to */
69 pid_t pid; /* 0 if exited */
70 char **argv; /* program name and arguments */
71 int numRedirections; /* elements in redirection array */
72 struct redirectionSpecifier *redirections; /* I/O redirections */
73 glob_t globResult; /* result of parameter globbing */
74 int freeGlob; /* should we globfree(&globResult)? */
75 int isStopped; /* is the program currently running? */
79 int jobId; /* job number */
80 int numProgs; /* total number of programs in job */
81 int runningProgs; /* number of programs running */
82 char *text; /* name of job */
83 char *cmdBuf; /* buffer various argv's point into */
84 pid_t pgrp; /* process group ID for the job */
85 struct childProgram *progs; /* array of programs in job */
86 struct job *next; /* to track background commands */
87 int stoppedProgs; /* number of programs alive, but stopped */
90 struct builtInCommand {
92 char *descr; /* description */
93 char *usage; /* usage */
94 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
97 /* function prototypes for builtins */
98 static int builtin_cd(struct job *cmd, struct jobSet *junk);
99 static int builtin_env(struct job *dummy, struct jobSet *junk);
100 static int builtin_exit(struct job *cmd, struct jobSet *junk);
101 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
102 static int builtin_help(struct job *cmd, struct jobSet *junk);
103 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
104 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
105 static int builtin_export(struct job *cmd, struct jobSet *junk);
106 static int builtin_source(struct job *cmd, struct jobSet *jobList);
107 static int builtin_unset(struct job *cmd, struct jobSet *junk);
108 static int builtin_read(struct job *cmd, struct jobSet *junk);
111 /* function prototypes for shell stuff */
112 static void checkJobs(struct jobSet *jobList);
113 static int getCommand(FILE * source, char *command);
114 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
115 static int setupRedirections(struct childProgram *prog);
116 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
117 static int busy_loop(FILE * input);
120 /* Table of built-in functions (these are non-forking builtins, meaning they
121 * can change global variables in the parent shell process but they will not
122 * work with pipes and redirects; 'unset foo | whatever' will not work) */
123 static struct builtInCommand bltins[] = {
124 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
125 {"cd", "Change working directory", "cd [dir]", builtin_cd},
126 {"exit", "Exit from shell()", "exit", builtin_exit},
127 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
128 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
129 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
130 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
131 {"read", "Input environment variable", "read [VAR]", builtin_read},
132 {NULL, NULL, NULL, NULL}
135 /* Table of forking built-in functions (things that fork cannot change global
136 * variables in the parent process, such as the current working directory) */
137 static struct builtInCommand bltins_forking[] = {
138 {"env", "Print all environment variables", "env", builtin_env},
139 {"pwd", "Print current directory", "pwd", builtin_pwd},
140 {".", "Source-in and run commands in a file", ". filename", builtin_source},
141 {"help", "List shell built-in commands", "help", builtin_help},
142 {NULL, NULL, NULL, NULL}
145 static char *prompt = "# ";
146 static char *cwd = NULL;
147 static char *local_pending_command = NULL;
149 #ifdef BB_FEATURE_SH_COMMAND_EDITING
150 void win_changed(int junk)
152 struct winsize win = { 0, 0, 0, 0 };
153 ioctl(0, TIOCGWINSZ, &win);
154 if (win.ws_col > 0) {
155 cmdedit_setwidth( win.ws_col - 1);
161 /* built-in 'cd <path>' handler */
162 static int builtin_cd(struct job *cmd, struct jobSet *junk)
166 if (!cmd->progs[0].argv[1] == 1)
167 newdir = getenv("HOME");
169 newdir = cmd->progs[0].argv[1];
171 printf("cd: %s: %s\n", newdir, strerror(errno));
174 getcwd(cwd, sizeof(cwd));
179 /* built-in 'env' handler */
180 static int builtin_env(struct job *dummy, struct jobSet *junk)
184 for (e = environ; *e; e++) {
185 fprintf(stdout, "%s\n", *e);
190 /* built-in 'exit' handler */
191 static int builtin_exit(struct job *cmd, struct jobSet *junk)
193 if (!cmd->progs[0].argv[1] == 1)
196 return(atoi(cmd->progs[0].argv[1]));
199 /* built-in 'fg' and 'bg' handler */
200 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
203 struct job *job=NULL;
205 if (!jobList->head) {
206 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
207 errorMsg("%s: exactly one argument is expected\n",
208 cmd->progs[0].argv[0]);
211 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
212 errorMsg("%s: bad argument '%s'\n",
213 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
215 for (job = jobList->head; job; job = job->next) {
216 if (job->jobId == jobNum) {
226 errorMsg("%s: unknown job %d\n",
227 cmd->progs[0].argv[0], jobNum);
231 if (*cmd->progs[0].argv[0] == 'f') {
232 /* Make this job the foreground job */
233 /* suppress messages when run from /linuxrc mag@sysgo.de */
234 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
239 /* Restart the processes in the job */
240 for (i = 0; i < job->numProgs; i++)
241 job->progs[i].isStopped = 0;
243 kill(-job->pgrp, SIGCONT);
245 job->stoppedProgs = 0;
250 /* built-in 'help' handler */
251 static int builtin_help(struct job *dummy, struct jobSet *junk)
253 struct builtInCommand *x;
255 fprintf(stdout, "\nBuilt-in commands:\n");
256 fprintf(stdout, "-------------------\n");
257 for (x = bltins; x->cmd; x++) {
258 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
260 for (x = bltins_forking; x->cmd; x++) {
261 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
263 fprintf(stdout, "\n\n");
267 /* built-in 'jobs' handler */
268 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
273 for (job = jobList->head; job; job = job->next) {
274 if (job->runningProgs == job->stoppedProgs)
275 statusString = "Stopped";
277 statusString = "Running";
279 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
285 /* built-in 'pwd' handler */
286 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
288 getcwd(cwd, sizeof(cwd));
289 fprintf(stdout, "%s\n", cwd);
293 /* built-in 'export VAR=value' handler */
294 static int builtin_export(struct job *cmd, struct jobSet *junk)
298 if (!cmd->progs[0].argv[1] == 1) {
299 return (builtin_env(cmd, junk));
301 res = putenv(cmd->progs[0].argv[1]);
303 fprintf(stdout, "export: %s\n", strerror(errno));
307 /* built-in 'read VAR' handler */
308 static int builtin_read(struct job *cmd, struct jobSet *junk)
310 int res = 0, len, newlen;
312 char string[MAX_READ];
314 if (cmd->progs[0].argv[1]) {
315 /* argument (VAR) given: put "VAR=" into buffer */
316 strcpy(string, cmd->progs[0].argv[1]);
317 len = strlen(string);
320 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
321 newlen = strlen(string);
323 string[--newlen] = '\0'; /* chomp trailing newline */
325 ** string should now contain "VAR=<value>"
326 ** copy it (putenv() won't do that, so we must make sure
327 ** the string resides in a static buffer!)
330 if((s = strdup(string)))
333 fprintf(stdout, "read: %s\n", strerror(errno));
336 fgets(string, sizeof(string), stdin);
341 /* Built-in '.' handler (read-in and execute commands from file) */
342 static int builtin_source(struct job *cmd, struct jobSet *junk)
347 if (!cmd->progs[0].argv[1] == 1)
350 input = fopen(cmd->progs[0].argv[1], "r");
352 fprintf(stdout, "Couldn't open file '%s'\n",
353 cmd->progs[0].argv[1]);
357 /* Now run the file */
358 status = busy_loop(input);
362 /* built-in 'unset VAR' handler */
363 static int builtin_unset(struct job *cmd, struct jobSet *junk)
365 if (!cmd->progs[0].argv[1] == 1) {
366 fprintf(stdout, "unset: parameter required.\n");
369 unsetenv(cmd->progs[0].argv[1]);
373 /* free up all memory from a job */
374 static void freeJob(struct job *cmd)
378 for (i = 0; i < cmd->numProgs; i++) {
379 free(cmd->progs[i].argv);
380 if (cmd->progs[i].redirections)
381 free(cmd->progs[i].redirections);
382 if (cmd->progs[i].freeGlob)
383 globfree(&cmd->progs[i].globResult);
389 memset(cmd, 0, sizeof(struct job));
392 /* remove a job from the jobList */
393 static void removeJob(struct jobSet *jobList, struct job *job)
398 if (job == jobList->head) {
399 jobList->head = job->next;
401 prevJob = jobList->head;
402 while (prevJob->next != job)
403 prevJob = prevJob->next;
404 prevJob->next = job->next;
410 /* Checks to see if any background processes have exited -- if they
411 have, figure out why and see if a job has completed */
412 static void checkJobs(struct jobSet *jobList)
419 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
420 for (job = jobList->head; job; job = job->next) {
422 while (progNum < job->numProgs &&
423 job->progs[progNum].pid != childpid) progNum++;
424 if (progNum < job->numProgs)
428 /* This happens on backticked commands */
432 if (WIFEXITED(status) || WIFSIGNALED(status)) {
435 job->progs[progNum].pid = 0;
437 if (!job->runningProgs) {
438 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
439 removeJob(jobList, job);
444 job->progs[progNum].isStopped = 1;
446 if (job->stoppedProgs == job->numProgs) {
447 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
453 if (childpid == -1 && errno != ECHILD)
457 static int getCommand(FILE * source, char *command)
459 if (source == NULL) {
460 if (local_pending_command) {
461 /* a command specified (-c option): return it & mark it done */
462 strcpy(command, local_pending_command);
463 free(local_pending_command);
464 local_pending_command = NULL;
470 if (source == stdin) {
471 #ifdef BB_FEATURE_SH_COMMAND_EDITING
474 len=fprintf(stdout, "%s %s", cwd, prompt);
476 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
477 sprintf(promptStr, "%s %s", cwd, prompt);
478 cmdedit_read_input(promptStr, command);
482 fprintf(stdout, "%s %s", cwd, prompt);
487 if (!fgets(command, BUFSIZ - 2, source)) {
493 /* remove trailing newline */
494 command[strlen(command) - 1] = '\0';
499 static void globLastArgument(struct childProgram *prog, int *argcPtr,
503 int argcAlloced = *argcAllocedPtr;
507 char *src, *dst, *var;
509 if (argc > 1) { /* cmd->globResult is already initialized */
511 i = prog->globResult.gl_pathc;
517 /* do shell variable substitution */
518 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
519 prog->argv[argc - 1] = var;
521 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
522 if (rc == GLOB_NOSPACE) {
523 errorMsg("out of space during glob operation\n");
525 } else if (rc == GLOB_NOMATCH ||
526 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
527 !strcmp(prog->argv[argc - 1],
528 prog->globResult.gl_pathv[i]))) {
529 /* we need to remove whatever \ quoting is still present */
530 src = dst = prog->argv[argc - 1];
538 argcAlloced += (prog->globResult.gl_pathc - i);
540 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
541 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
542 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
543 argc += (prog->globResult.gl_pathc - i - 1);
546 *argcAllocedPtr = argcAlloced;
550 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
551 line). If a valid command is found, commandPtr is set to point to
552 the beginning of the next command (if the original command had more
553 then one job associated with it) or NULL if no more commands are
555 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
558 char *returnCommand = NULL;
559 char *src, *buf, *chptr;
566 struct childProgram *prog;
568 /* skip leading white space */
569 while (**commandPtr && isspace(**commandPtr))
572 /* this handles empty lines or leading '#' characters */
573 if (!**commandPtr || (**commandPtr == '#')) {
580 job->progs = xmalloc(sizeof(*job->progs));
582 /* We set the argv elements to point inside of this string. The
583 memory is freed by freeJob(). Allocate twice the original
584 length in case we need to quote every single character.
586 Getting clean memory relieves us of the task of NULL
587 terminating things and makes the rest of this look a bit
588 cleaner (though it is, admittedly, a tad less efficient) */
589 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
593 prog->numRedirections = 0;
594 prog->redirections = NULL;
599 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
600 prog->argv[0] = job->cmdBuf;
604 while (*src && !done) {
611 errorMsg("character expected after \\\n");
616 /* in shell, "\'" should yield \' */
619 } else if (*src == '*' || *src == '?' || *src == '[' ||
620 *src == ']') *buf++ = '\\';
622 } else if (isspace(*src)) {
623 if (*prog->argv[argc]) {
625 /* +1 here leaves room for the NULL which ends argv */
626 if ((argc + 1) == argvAlloced) {
628 prog->argv = realloc(prog->argv,
629 sizeof(*prog->argv) *
632 globLastArgument(prog, &argc, &argvAlloced);
633 prog->argv[argc] = buf;
642 case '#': /* comment */
646 case '>': /* redirections */
648 i = prog->numRedirections++;
649 prog->redirections = realloc(prog->redirections,
650 sizeof(*prog->redirections) *
653 prog->redirections[i].fd = -1;
654 if (buf != prog->argv[argc]) {
655 /* the stuff before this character may be the file number
657 prog->redirections[i].fd =
658 strtol(prog->argv[argc], &chptr, 10);
660 if (*chptr && *prog->argv[argc]) {
662 globLastArgument(prog, &argc, &argvAlloced);
663 prog->argv[argc] = buf;
667 if (prog->redirections[i].fd == -1) {
669 prog->redirections[i].fd = 1;
671 prog->redirections[i].fd = 0;
676 prog->redirections[i].type =
677 REDIRECT_APPEND, src++;
679 prog->redirections[i].type = REDIRECT_OVERWRITE;
681 prog->redirections[i].type = REDIRECT_INPUT;
684 /* This isn't POSIX sh compliant. Oh well. */
686 while (isspace(*chptr))
690 errorMsg("file name expected after %c\n", *src);
696 prog->redirections[i].filename = buf;
697 while (*chptr && !isspace(*chptr))
700 src = chptr - 1; /* we src++ later */
701 prog->argv[argc] = ++buf;
705 /* finish this command */
706 if (*prog->argv[argc])
709 errorMsg("empty command in pipe\n");
714 prog->argv[argc] = NULL;
716 /* and start the next */
718 job->progs = realloc(job->progs,
719 sizeof(*job->progs) * job->numProgs);
720 prog = job->progs + (job->numProgs - 1);
721 prog->numRedirections = 0;
722 prog->redirections = NULL;
727 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
728 prog->argv[0] = ++buf;
731 while (*src && isspace(*src))
735 errorMsg("empty command in pipe\n");
740 src--; /* we'll ++ it at the end of the loop */
744 case '&': /* background */
746 case ';': /* multiple commands */
748 returnCommand = *commandPtr + (src - *commandPtr) + 1;
754 errorMsg("character expected after \\\n");
758 if (*src == '*' || *src == '[' || *src == ']'
759 || *src == '?') *buf++ = '\\';
761 #ifdef BB_FEATURE_SH_BACKTICKS
763 /* Exec a backtick-ed command */
765 char* charptr1=NULL, *charptr2;
768 struct jobSet njobList = { NULL, NULL };
772 ptr=strchr(++src, '`');
774 fprintf(stderr, "Unmatched '`' in command\n");
779 /* Make a copy of any stuff left over in the command
780 * line after the second backtick */
781 charptr2 = xmalloc(strlen(ptr)+1);
782 memcpy(charptr2, ptr+1, strlen(ptr));
784 /* Make some space to hold just the backticked command */
785 charptr1 = xmalloc(1+ptr-src);
786 snprintf(charptr1, 1+ptr-src, src);
787 newJob = xmalloc(sizeof(struct job));
788 /* Now parse and run the backticked command */
789 if (!parseCommand(&charptr1, newJob, &njobList, isBg)
790 && newJob->numProgs) {
792 runCommand(newJob, &njobList, 0, pipefd);
797 /* Copy the output from the backtick-ed command into the
798 * command line, making extra room as needed */
800 charptr1 = xmalloc(BUFSIZ);
801 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
802 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
803 if (newSize > BUFSIZ) {
804 *commandPtr=realloc(*commandPtr, src - *commandPtr +
805 size + 1 + strlen(charptr2));
807 memcpy(src, charptr1, size);
815 /* Now paste into the *commandPtr all the stuff
816 * leftover after the second backtick */
817 memcpy(src, charptr2, strlen(charptr2));
818 fprintf(stderr,"*commandPtr='%s'\n", *commandPtr);
822 /* Now recursively call parseCommand to deal with the new
823 * and improved version of the command line with the backtick
824 * results expanded in place... */
825 return(parseCommand(commandPtr, job, jobList, isBg));
828 #endif // BB_FEATURE_SH_BACKTICKS
836 if (*prog->argv[argc]) {
838 globLastArgument(prog, &argc, &argvAlloced);
844 prog->argv[argc] = NULL;
846 if (!returnCommand) {
847 job->text = xmalloc(strlen(*commandPtr) + 1);
848 strcpy(job->text, *commandPtr);
850 /* This leaves any trailing spaces, which is a bit sloppy */
851 count = returnCommand - *commandPtr;
852 job->text = xmalloc(count + 1);
853 strncpy(job->text, *commandPtr, count);
854 job->text[count] = '\0';
857 *commandPtr = returnCommand;
863 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
866 int nextin=0, nextout, stdoutfd=fileno(stdout);
868 int pipefds[2]; /* pipefd[0] is for reading */
869 struct builtInCommand *x;
870 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
871 const struct BB_applet *a = applets;
874 for (i = 0; i < newJob->numProgs; i++) {
875 if ((i + 1) < newJob->numProgs) {
877 nextout = pipefds[1];
882 /* Check if the command matches any non-forking builtins */
883 for (x = bltins; x->cmd; x++) {
884 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
885 return (x->function(newJob, jobList));
889 if (!(newJob->progs[i].pid = fork())) {
890 signal(SIGTTOU, SIG_DFL);
892 if (outPipe[1]!=-1) {
894 nextout = stdoutfd = outPipe[1];
903 /* explicit redirections override pipes */
904 setupRedirections(newJob->progs + i);
906 /* Check if the command matches any of the other builtins */
907 for (x = bltins_forking; x->cmd; x++) {
908 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
909 exit (x->function(newJob, jobList));
912 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
913 /* Check if the command matches any busybox internal commands here */
914 /* TODO: Add matching on commands with paths appended (i.e. 'cat'
915 * currently works, but '/bin/cat' doesn't ) */
916 while (a->name != 0) {
917 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
919 char** argv=newJob->progs[i].argv;
920 for(argc=0;*argv!=NULL; argv++, argc++);
921 exit((*(a->main)) (argc, newJob->progs[i].argv));
927 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
928 fatalError("%s: %s\n", newJob->progs[i].argv[0],
931 if (outPipe[1]!=-1) {
935 /* put our child in the process group whose leader is the
936 first process in this pipe */
937 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
941 if (nextout != stdoutfd)
944 /* If there isn't another process, nextin is garbage
945 but it doesn't matter */
949 newJob->pgrp = newJob->progs[0].pid;
951 /* find the ID for the job to use */
953 for (job = jobList->head; job; job = job->next)
954 if (job->jobId >= newJob->jobId)
955 newJob->jobId = job->jobId + 1;
957 /* add the job to the list of running jobs */
958 if (!jobList->head) {
959 job = jobList->head = xmalloc(sizeof(*job));
961 for (job = jobList->head; job->next; job = job->next);
962 job->next = xmalloc(sizeof(*job));
968 job->runningProgs = job->numProgs;
969 job->stoppedProgs = 0;
972 /* we don't wait for background jobs to return -- append it
973 to the list of backgrounded jobs and leave it alone */
974 printf("[%d] %d\n", job->jobId,
975 newJob->progs[newJob->numProgs - 1].pid);
979 /* move the new process group into the foreground */
980 /* suppress messages when run from /linuxrc mag@sysgo.de */
981 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
988 static int setupRedirections(struct childProgram *prog)
993 struct redirectionSpecifier *redir = prog->redirections;
995 for (i = 0; i < prog->numRedirections; i++, redir++) {
996 switch (redir->type) {
1000 case REDIRECT_OVERWRITE:
1001 mode = O_RDWR | O_CREAT | O_TRUNC;
1003 case REDIRECT_APPEND:
1004 mode = O_RDWR | O_CREAT | O_APPEND;
1008 openfd = open(redir->filename, mode, 0666);
1010 /* this could get lost if stderr has been redirected, but
1011 bash and ash both lose it as well (though zsh doesn't!) */
1012 errorMsg("error opening %s: %s\n", redir->filename,
1017 if (openfd != redir->fd) {
1018 dup2(openfd, redir->fd);
1027 static int busy_loop(FILE * input)
1030 char *nextCommand = NULL;
1031 struct jobSet jobList = { NULL, NULL };
1038 /* save current owner of TTY so we can restore it on exit */
1039 parent_pgrp = tcgetpgrp(0);
1041 command = (char *) calloc(BUFSIZ, sizeof(char));
1043 /* don't pay any attention to this signal; it just confuses
1044 things and isn't really meant for shells anyway */
1045 signal(SIGTTOU, SIG_IGN);
1049 /* no job is in the foreground */
1051 /* see if any background processes have exited */
1052 checkJobs(&jobList);
1055 if (getCommand(input, command))
1057 nextCommand = command;
1060 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1062 int pipefds[2] = {-1,-1};
1063 runCommand(&newJob, &jobList, inBg, pipefds);
1067 command = (char *) calloc(BUFSIZ, sizeof(char));
1070 /* a job is running in the foreground; wait for it */
1072 while (!jobList.fg->progs[i].pid ||
1073 jobList.fg->progs[i].isStopped) i++;
1075 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1077 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1078 /* the child exited */
1079 jobList.fg->runningProgs--;
1080 jobList.fg->progs[i].pid = 0;
1082 if (!jobList.fg->runningProgs) {
1085 removeJob(&jobList, jobList.fg);
1089 /* the child was stopped */
1090 jobList.fg->stoppedProgs++;
1091 jobList.fg->progs[i].isStopped = 1;
1093 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1094 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1095 "Stopped", jobList.fg->text);
1101 /* move the shell to the foreground */
1102 /* suppress messages when run from /linuxrc mag@sysgo.de */
1103 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1104 perror("tcsetpgrp");
1110 /* return controlling TTY back to parent process group before exiting */
1111 if (tcsetpgrp(0, parent_pgrp))
1112 perror("tcsetpgrp");
1114 /* return exit status if called with "-c" */
1115 if (input == NULL && WIFEXITED(status))
1116 return WEXITSTATUS(status);
1122 int shell_main(int argc, char **argv)
1124 FILE *input = stdin;
1126 /* initialize the cwd */
1127 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1129 fatalError("out of memory\n");
1131 getcwd(cwd, sizeof(char)*BUFSIZ);
1133 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1135 signal(SIGWINCH, win_changed);
1139 //if (argv[0] && argv[0][0] == '-') {
1140 // builtin_source("/etc/profile");
1144 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1145 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1147 if (argv[1][0]=='-' && argv[1][1]=='c') {
1149 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1150 if (local_pending_command == 0) {
1151 fatalError("out of memory\n");
1153 for(i=2; i<argc; i++)
1155 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1156 local_pending_command = realloc(local_pending_command,
1157 strlen(local_pending_command) + strlen(argv[i]));
1158 if (local_pending_command==NULL)
1159 fatalError("commands for -c option too long\n");
1161 strcat(local_pending_command, argv[i]);
1162 if ( (i + 1) < argc)
1163 strcat(local_pending_command, " ");
1168 else if (argv[1][0]=='-') {
1172 input = fopen(argv[1], "r");
1174 fatalError("Couldn't open file '%s': %s\n", argv[1],
1180 return (busy_loop(input));