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
37 #include <sys/ioctl.h>
40 #ifdef BB_FEATURE_SH_COMMAND_EDITING
44 #define MAX_READ 128 /* size of input buffer for `read' builtin */
45 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
48 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
53 struct job *head; /* head of list of running jobs */
54 struct job *fg; /* current foreground job */
57 struct redirectionSpecifier {
58 enum redirectionType type; /* type of redirection */
59 int fd; /* file descriptor being redirected */
60 char *filename; /* file to redirect fd to */
64 pid_t pid; /* 0 if exited */
65 char **argv; /* program name and arguments */
66 int numRedirections; /* elements in redirection array */
67 struct redirectionSpecifier *redirections; /* I/O redirections */
68 glob_t globResult; /* result of parameter globbing */
69 int freeGlob; /* should we globfree(&globResult)? */
70 int isStopped; /* is the program currently running? */
74 int jobId; /* job number */
75 int numProgs; /* total number of programs in job */
76 int runningProgs; /* number of programs running */
77 char *text; /* name of job */
78 char *cmdBuf; /* buffer various argv's point into */
79 pid_t pgrp; /* process group ID for the job */
80 struct childProgram *progs; /* array of programs in job */
81 struct job *next; /* to track background commands */
82 int stoppedProgs; /* number of programs alive, but stopped */
85 struct builtInCommand {
87 char *descr; /* description */
88 char *usage; /* usage */
89 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
92 /* function prototypes for builtins */
93 static int builtin_cd(struct job *cmd, struct jobSet *junk);
94 static int builtin_env(struct job *dummy, struct jobSet *junk);
95 static int builtin_exit(struct job *cmd, struct jobSet *junk);
96 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
97 static int builtin_help(struct job *cmd, struct jobSet *junk);
98 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
99 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
100 static int builtin_export(struct job *cmd, struct jobSet *junk);
101 static int builtin_source(struct job *cmd, struct jobSet *jobList);
102 static int builtin_unset(struct job *cmd, struct jobSet *junk);
103 static int builtin_read(struct job *cmd, struct jobSet *junk);
106 /* function prototypes for shell stuff */
107 static void checkJobs(struct jobSet *jobList);
108 static int getCommand(FILE * source, char *command);
109 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
110 static int setupRedirections(struct childProgram *prog);
111 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg);
112 static int busy_loop(FILE * input);
115 /* Table of built-in functions (these are non-forking builtins, meaning they
116 * can change global variables in the parent shell process but they will not
117 * work with pipes and redirects; 'unset foo | whatever' will not work) */
118 static struct builtInCommand bltins[] = {
119 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
120 {"cd", "Change working directory", "cd [dir]", builtin_cd},
121 {"exit", "Exit from shell()", "exit", builtin_exit},
122 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
123 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
124 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
125 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
126 {"read", "Input environment variable", "read [VAR]", builtin_read},
127 {NULL, NULL, NULL, NULL}
130 /* Table of forking built-in functions (things that fork cannot change global
131 * variables in the parent process, such as the current working directory) */
132 static struct builtInCommand bltins_forking[] = {
133 {"env", "Print all environment variables", "env", builtin_env},
134 {"pwd", "Print current directory", "pwd", builtin_pwd},
135 {".", "Source-in and run commands in a file", ". filename", builtin_source},
136 {"help", "List shell built-in commands", "help", builtin_help},
137 {NULL, NULL, NULL, NULL}
140 static const char shell_usage[] =
142 " or: sh -c command [args]...\n"
143 #ifndef BB_FEATURE_TRIVIAL_HELP
144 "\nlash: The BusyBox command interpreter (shell).\n\n"
148 static char *prompt = "# ";
149 static char *cwd = NULL;
150 static char *local_pending_command = NULL;
152 #ifdef BB_FEATURE_SH_COMMAND_EDITING
153 void win_changed(int junk)
155 struct winsize win = { 0, 0, 0, 0 };
156 ioctl(0, TIOCGWINSZ, &win);
157 if (win.ws_col > 0) {
158 cmdedit_setwidth( win.ws_col - 1);
164 /* built-in 'cd <path>' handler */
165 static int builtin_cd(struct job *cmd, struct jobSet *junk)
169 if (!cmd->progs[0].argv[1] == 1)
170 newdir = getenv("HOME");
172 newdir = cmd->progs[0].argv[1];
174 printf("cd: %s: %s\n", newdir, strerror(errno));
177 getcwd(cwd, sizeof(cwd));
182 /* built-in 'env' handler */
183 static int builtin_env(struct job *dummy, struct jobSet *junk)
187 for (e = environ; *e; e++) {
188 fprintf(stdout, "%s\n", *e);
193 /* built-in 'exit' handler */
194 static int builtin_exit(struct job *cmd, struct jobSet *junk)
196 if (!cmd->progs[0].argv[1] == 1)
199 return(atoi(cmd->progs[0].argv[1]));
202 /* built-in 'fg' and 'bg' handler */
203 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
206 struct job *job=NULL;
208 if (!jobList->head) {
209 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
210 errorMsg("%s: exactly one argument is expected\n",
211 cmd->progs[0].argv[0]);
214 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
215 errorMsg("%s: bad argument '%s'\n",
216 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
218 for (job = jobList->head; job; job = job->next) {
219 if (job->jobId == jobNum) {
229 errorMsg("%s: unknown job %d\n",
230 cmd->progs[0].argv[0], jobNum);
234 if (*cmd->progs[0].argv[0] == 'f') {
235 /* Make this job the foreground job */
236 /* suppress messages when run from /linuxrc mag@sysgo.de */
237 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
242 /* Restart the processes in the job */
243 for (i = 0; i < job->numProgs; i++)
244 job->progs[i].isStopped = 0;
246 kill(-job->pgrp, SIGCONT);
248 job->stoppedProgs = 0;
253 /* built-in 'help' handler */
254 static int builtin_help(struct job *dummy, struct jobSet *junk)
256 struct builtInCommand *x;
258 fprintf(stdout, "\nBuilt-in commands:\n");
259 fprintf(stdout, "-------------------\n");
260 for (x = bltins; x->cmd; x++) {
261 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
263 for (x = bltins_forking; x->cmd; x++) {
264 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
266 fprintf(stdout, "\n\n");
270 /* built-in 'jobs' handler */
271 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
276 for (job = jobList->head; job; job = job->next) {
277 if (job->runningProgs == job->stoppedProgs)
278 statusString = "Stopped";
280 statusString = "Running";
282 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
288 /* built-in 'pwd' handler */
289 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
291 getcwd(cwd, sizeof(cwd));
292 fprintf(stdout, "%s\n", cwd);
296 /* built-in 'export VAR=value' handler */
297 static int builtin_export(struct job *cmd, struct jobSet *junk)
301 if (!cmd->progs[0].argv[1] == 1) {
302 return (builtin_env(cmd, junk));
304 res = putenv(cmd->progs[0].argv[1]);
306 fprintf(stdout, "export: %s\n", strerror(errno));
310 /* built-in 'read VAR' handler */
311 static int builtin_read(struct job *cmd, struct jobSet *junk)
313 int res = 0, len, newlen;
315 char string[MAX_READ];
317 if (cmd->progs[0].argv[1]) {
318 /* argument (VAR) given: put "VAR=" into buffer */
319 strcpy(string, cmd->progs[0].argv[1]);
320 len = strlen(string);
323 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
324 newlen = strlen(string);
326 string[--newlen] = '\0'; /* chomp trailing newline */
328 ** string should now contain "VAR=<value>"
329 ** copy it (putenv() won't do that, so we must make sure
330 ** the string resides in a static buffer!)
333 if((s = strdup(string)))
336 fprintf(stdout, "read: %s\n", strerror(errno));
339 fgets(string, sizeof(string), stdin);
344 /* Built-in '.' handler (read-in and execute commands from file) */
345 static int builtin_source(struct job *cmd, struct jobSet *junk)
350 if (!cmd->progs[0].argv[1] == 1)
353 input = fopen(cmd->progs[0].argv[1], "r");
355 fprintf(stdout, "Couldn't open file '%s'\n",
356 cmd->progs[0].argv[1]);
360 /* Now run the file */
361 status = busy_loop(input);
365 /* built-in 'unset VAR' handler */
366 static int builtin_unset(struct job *cmd, struct jobSet *junk)
368 if (!cmd->progs[0].argv[1] == 1) {
369 fprintf(stdout, "unset: parameter required.\n");
372 unsetenv(cmd->progs[0].argv[1]);
376 /* free up all memory from a job */
377 static void freeJob(struct job *cmd)
381 for (i = 0; i < cmd->numProgs; i++) {
382 free(cmd->progs[i].argv);
383 if (cmd->progs[i].redirections)
384 free(cmd->progs[i].redirections);
385 if (cmd->progs[i].freeGlob)
386 globfree(&cmd->progs[i].globResult);
392 memset(cmd, 0, sizeof(struct job));
395 /* remove a job from the jobList */
396 static void removeJob(struct jobSet *jobList, struct job *job)
401 if (job == jobList->head) {
402 jobList->head = job->next;
404 prevJob = jobList->head;
405 while (prevJob->next != job)
406 prevJob = prevJob->next;
407 prevJob->next = job->next;
413 /* Checks to see if any background processes have exited -- if they
414 have, figure out why and see if a job has completed */
415 static void checkJobs(struct jobSet *jobList)
422 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
423 for (job = jobList->head; job; job = job->next) {
425 while (progNum < job->numProgs &&
426 job->progs[progNum].pid != childpid) progNum++;
427 if (progNum < job->numProgs)
431 if (WIFEXITED(status) || WIFSIGNALED(status)) {
434 job->progs[progNum].pid = 0;
436 if (!job->runningProgs) {
437 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
438 removeJob(jobList, job);
443 job->progs[progNum].isStopped = 1;
445 if (job->stoppedProgs == job->numProgs) {
446 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
452 if (childpid == -1 && errno != ECHILD)
456 static int getCommand(FILE * source, char *command)
458 if (source == NULL) {
459 if (local_pending_command) {
460 /* a command specified (-c option): return it & mark it done */
461 strcpy(command, local_pending_command);
462 free(local_pending_command);
463 local_pending_command = NULL;
469 if (source == stdin) {
470 #ifdef BB_FEATURE_SH_COMMAND_EDITING
473 len=fprintf(stdout, "%s %s", cwd, prompt);
475 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
476 sprintf(promptStr, "%s %s", cwd, prompt);
477 cmdedit_read_input(promptStr, command);
481 fprintf(stdout, "%s %s", cwd, prompt);
486 if (!fgets(command, BUFSIZ - 2, source)) {
492 /* remove trailing newline */
493 command[strlen(command) - 1] = '\0';
498 static void globLastArgument(struct childProgram *prog, int *argcPtr,
502 int argcAlloced = *argcAllocedPtr;
506 char *src, *dst, *var;
508 if (argc > 1) { /* cmd->globResult is already initialized */
510 i = prog->globResult.gl_pathc;
516 /* do shell variable substitution */
517 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
518 prog->argv[argc - 1] = var;
520 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
521 if (rc == GLOB_NOSPACE) {
522 errorMsg("out of space during glob operation\n");
524 } else if (rc == GLOB_NOMATCH ||
525 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
526 !strcmp(prog->argv[argc - 1],
527 prog->globResult.gl_pathv[i]))) {
528 /* we need to remove whatever \ quoting is still present */
529 src = dst = prog->argv[argc - 1];
537 argcAlloced += (prog->globResult.gl_pathc - i);
539 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
540 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
541 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
542 argc += (prog->globResult.gl_pathc - i - 1);
545 *argcAllocedPtr = argcAlloced;
549 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
550 line). If a valid command is found, commandPtr is set to point to
551 the beginning of the next command (if the original command had more
552 then one job associated with it) or NULL if no more commands are
554 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
557 char *returnCommand = NULL;
558 char *src, *buf, *chptr;
565 struct childProgram *prog;
567 /* skip leading white space */
568 while (**commandPtr && isspace(**commandPtr))
571 /* this handles empty lines or leading '#' characters */
572 if (!**commandPtr || (**commandPtr == '#')) {
579 job->progs = xmalloc(sizeof(*job->progs));
581 /* We set the argv elements to point inside of this string. The
582 memory is freed by freeJob(). Allocate twice the original
583 length in case we need to quote every single character.
585 Getting clean memory relieves us of the task of NULL
586 terminating things and makes the rest of this look a bit
587 cleaner (though it is, admittedly, a tad less efficient) */
588 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
592 prog->numRedirections = 0;
593 prog->redirections = NULL;
598 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
599 prog->argv[0] = job->cmdBuf;
603 while (*src && !done) {
610 errorMsg("character expected after \\\n");
615 /* in shell, "\'" should yield \' */
618 } else if (*src == '*' || *src == '?' || *src == '[' ||
619 *src == ']') *buf++ = '\\';
621 } else if (isspace(*src)) {
622 if (*prog->argv[argc]) {
624 /* +1 here leaves room for the NULL which ends argv */
625 if ((argc + 1) == argvAlloced) {
627 prog->argv = realloc(prog->argv,
628 sizeof(*prog->argv) *
631 globLastArgument(prog, &argc, &argvAlloced);
632 prog->argv[argc] = buf;
641 case '#': /* comment */
645 case '>': /* redirections */
647 i = prog->numRedirections++;
648 prog->redirections = realloc(prog->redirections,
649 sizeof(*prog->redirections) *
652 prog->redirections[i].fd = -1;
653 if (buf != prog->argv[argc]) {
654 /* the stuff before this character may be the file number
656 prog->redirections[i].fd =
657 strtol(prog->argv[argc], &chptr, 10);
659 if (*chptr && *prog->argv[argc]) {
661 globLastArgument(prog, &argc, &argvAlloced);
662 prog->argv[argc] = buf;
666 if (prog->redirections[i].fd == -1) {
668 prog->redirections[i].fd = 1;
670 prog->redirections[i].fd = 0;
675 prog->redirections[i].type =
676 REDIRECT_APPEND, src++;
678 prog->redirections[i].type = REDIRECT_OVERWRITE;
680 prog->redirections[i].type = REDIRECT_INPUT;
683 /* This isn't POSIX sh compliant. Oh well. */
685 while (isspace(*chptr))
689 errorMsg("file name expected after %c\n", *src);
695 prog->redirections[i].filename = buf;
696 while (*chptr && !isspace(*chptr))
699 src = chptr - 1; /* we src++ later */
700 prog->argv[argc] = ++buf;
704 /* finish this command */
705 if (*prog->argv[argc])
708 errorMsg("empty command in pipe1\n");
713 prog->argv[argc] = NULL;
715 /* and start the next */
717 job->progs = realloc(job->progs,
718 sizeof(*job->progs) * job->numProgs);
719 prog = job->progs + (job->numProgs - 1);
720 prog->numRedirections = 0;
721 prog->redirections = NULL;
726 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
727 prog->argv[0] = ++buf;
730 while (*src && isspace(*src))
734 errorMsg("empty command in pipe2\n");
739 src--; /* we'll ++ it at the end of the loop */
743 case '&': /* background */
745 case ';': /* multiple commands */
747 returnCommand = *commandPtr + (src - *commandPtr) + 1;
753 errorMsg("character expected after \\\n");
757 if (*src == '*' || *src == '[' || *src == ']'
758 || *src == '?') *buf++ = '\\';
761 /* Exec a backtick-ed command */
767 ptr=strchr(++src, '`');
769 fprintf(stderr, "Unmatched '`' in command\n");
774 newcmd = xmalloc(1+ptr-src);
775 snprintf(newcmd, 1+ptr-src, src);
777 if (!parseCommand(&newcmd, &newJob, jobList, isBg) &&
779 runCommand(&newJob, jobList, *isBg);
782 /* Clip out the the backticked command from the string */
783 memmove(--src, ptr, strlen(ptr)+1);
794 if (*prog->argv[argc]) {
796 globLastArgument(prog, &argc, &argvAlloced);
802 prog->argv[argc] = NULL;
804 if (!returnCommand) {
805 job->text = xmalloc(strlen(*commandPtr) + 1);
806 strcpy(job->text, *commandPtr);
808 /* This leaves any trailing spaces, which is a bit sloppy */
809 count = returnCommand - *commandPtr;
810 job->text = xmalloc(count + 1);
811 strncpy(job->text, *commandPtr, count);
812 job->text[count] = '\0';
815 *commandPtr = returnCommand;
821 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg)
826 int pipefds[2]; /* pipefd[0] is for reading */
827 struct builtInCommand *x;
828 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
829 const struct BB_applet *a = applets;
833 nextin = 0, nextout = 1;
834 for (i = 0; i < newJob->numProgs; i++) {
835 if ((i + 1) < newJob->numProgs) {
837 nextout = pipefds[1];
842 /* Check if the command matches any non-forking builtins */
843 for (x = bltins; x->cmd; x++) {
844 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
845 return (x->function(newJob, jobList));
849 if (!(newJob->progs[i].pid = fork())) {
850 signal(SIGTTOU, SIG_DFL);
862 /* explicit redirections override pipes */
863 setupRedirections(newJob->progs + i);
865 /* Check if the command matches any of the other builtins */
866 for (x = bltins_forking; x->cmd; x++) {
867 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
868 exit (x->function(newJob, jobList));
871 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
872 /* Check if the command matches any busybox internal commands here */
873 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
874 * works, but '/bin/cat' doesn't ) */
875 while (a->name != 0) {
876 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
878 char** argv=newJob->progs[i].argv;
879 for(argc=0;*argv!=NULL; argv++, argc++);
880 exit((*(a->main)) (argc, newJob->progs[i].argv));
886 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
887 fatalError("%s: %s\n", newJob->progs[i].argv[0],
891 /* put our child in the process group whose leader is the
892 first process in this pipe */
893 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
900 /* If there isn't another process, nextin is garbage
901 but it doesn't matter */
905 newJob->pgrp = newJob->progs[0].pid;
907 /* find the ID for the job to use */
909 for (job = jobList->head; job; job = job->next)
910 if (job->jobId >= newJob->jobId)
911 newJob->jobId = job->jobId + 1;
913 /* add the job to the list of running jobs */
914 if (!jobList->head) {
915 job = jobList->head = xmalloc(sizeof(*job));
917 for (job = jobList->head; job->next; job = job->next);
918 job->next = xmalloc(sizeof(*job));
924 job->runningProgs = job->numProgs;
925 job->stoppedProgs = 0;
928 /* we don't wait for background jobs to return -- append it
929 to the list of backgrounded jobs and leave it alone */
930 printf("[%d] %d\n", job->jobId,
931 newJob->progs[newJob->numProgs - 1].pid);
935 /* move the new process group into the foreground */
936 /* suppress messages when run from /linuxrc mag@sysgo.de */
937 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
944 static int setupRedirections(struct childProgram *prog)
949 struct redirectionSpecifier *redir = prog->redirections;
951 for (i = 0; i < prog->numRedirections; i++, redir++) {
952 switch (redir->type) {
956 case REDIRECT_OVERWRITE:
957 mode = O_RDWR | O_CREAT | O_TRUNC;
959 case REDIRECT_APPEND:
960 mode = O_RDWR | O_CREAT | O_APPEND;
964 openfd = open(redir->filename, mode, 0666);
966 /* this could get lost if stderr has been redirected, but
967 bash and ash both lose it as well (though zsh doesn't!) */
968 errorMsg("error opening %s: %s\n", redir->filename,
973 if (openfd != redir->fd) {
974 dup2(openfd, redir->fd);
983 static int busy_loop(FILE * input)
986 char *nextCommand = NULL;
987 struct jobSet jobList = { NULL, NULL };
994 /* save current owner of TTY so we can restore it on exit */
995 parent_pgrp = tcgetpgrp(0);
997 command = (char *) calloc(BUFSIZ, sizeof(char));
999 /* don't pay any attention to this signal; it just confuses
1000 things and isn't really meant for shells anyway */
1001 signal(SIGTTOU, SIG_IGN);
1005 /* no job is in the foreground */
1007 /* see if any background processes have exited */
1008 checkJobs(&jobList);
1011 if (getCommand(input, command))
1013 nextCommand = command;
1016 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1018 runCommand(&newJob, &jobList, inBg);
1023 /* a job is running in the foreground; wait for it */
1025 while (!jobList.fg->progs[i].pid ||
1026 jobList.fg->progs[i].isStopped) i++;
1028 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1030 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1031 /* the child exited */
1032 jobList.fg->runningProgs--;
1033 jobList.fg->progs[i].pid = 0;
1035 if (!jobList.fg->runningProgs) {
1038 removeJob(&jobList, jobList.fg);
1042 /* the child was stopped */
1043 jobList.fg->stoppedProgs++;
1044 jobList.fg->progs[i].isStopped = 1;
1046 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1047 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1048 "Stopped", jobList.fg->text);
1054 /* move the shell to the foreground */
1055 /* suppress messages when run from /linuxrc mag@sysgo.de */
1056 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1057 perror("tcsetpgrp");
1063 /* return controlling TTY back to parent process group before exiting */
1064 if (tcsetpgrp(0, parent_pgrp))
1065 perror("tcsetpgrp");
1067 /* return exit status if called with "-c" */
1068 if (input == NULL && WIFEXITED(status))
1069 return WEXITSTATUS(status);
1075 int shell_main(int argc, char **argv)
1077 FILE *input = stdin;
1079 /* initialize the cwd */
1080 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1082 fatalError("out of memory\n");
1084 getcwd(cwd, sizeof(char)*BUFSIZ);
1086 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1088 signal(SIGWINCH, win_changed);
1092 //if (argv[0] && argv[0][0] == '-') {
1093 // builtin_source("/etc/profile");
1097 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1098 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1100 if (argv[1][0]=='-' && argv[1][1]=='c') {
1102 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1103 if (local_pending_command == 0) {
1104 fatalError("out of memory\n");
1106 for(i=2; i<argc; i++)
1108 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1109 local_pending_command = realloc(local_pending_command,
1110 strlen(local_pending_command) + strlen(argv[i]));
1111 if (local_pending_command==NULL)
1112 fatalError("commands for -c option too long\n");
1114 strcat(local_pending_command, argv[i]);
1115 if ( (i + 1) < argc)
1116 strcat(local_pending_command, " ");
1121 else if (argv[1][0]=='-') {
1125 input = fopen(argv[1], "r");
1127 fatalError("Couldn't open file '%s': %s\n", argv[1],
1133 return (busy_loop(input));