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
30 //#define BB_FEATURE_SH_IF_EXPRESSIONS
43 #include <sys/ioctl.h>
46 #ifdef BB_FEATURE_SH_COMMAND_EDITING
50 #define MAX_LINE 256 /* size of input buffer for `read' builtin */
51 #define MAX_READ 128 /* size of input buffer for `read' builtin */
52 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
55 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
59 static const unsigned int REGULAR_JOB_CONTEXT=0x1;
60 static const unsigned int IF_EXP_CONTEXT=0x2;
61 static const unsigned int THEN_EXP_CONTEXT=0x4;
62 static const unsigned int ELSE_EXP_CONTEXT=0x8;
65 enum jobContext { REGULAR_APP, IF_CONTEXT, THEN_CONTEXT
69 struct job *head; /* head of list of running jobs */
70 struct job *fg; /* current foreground job */
73 struct redirectionSpecifier {
74 enum redirectionType type; /* type of redirection */
75 int fd; /* file descriptor being redirected */
76 char *filename; /* file to redirect fd to */
80 pid_t pid; /* 0 if exited */
81 char **argv; /* program name and arguments */
82 int numRedirections; /* elements in redirection array */
83 struct redirectionSpecifier *redirections; /* I/O redirections */
84 glob_t globResult; /* result of parameter globbing */
85 int freeGlob; /* should we globfree(&globResult)? */
86 int isStopped; /* is the program currently running? */
90 int jobId; /* job number */
91 int numProgs; /* total number of programs in job */
92 int runningProgs; /* number of programs running */
93 char *text; /* name of job */
94 char *cmdBuf; /* buffer various argv's point into */
95 pid_t pgrp; /* process group ID for the job */
96 struct childProgram *progs; /* array of programs in job */
97 struct job *next; /* to track background commands */
98 int stoppedProgs; /* number of programs alive, but stopped */
99 int jobContext; /* bitmask defining current context */
102 struct builtInCommand {
103 char *cmd; /* name */
104 char *descr; /* description */
105 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
108 /* function prototypes for builtins */
109 static int builtin_cd(struct job *cmd, struct jobSet *junk);
110 static int builtin_env(struct job *dummy, struct jobSet *junk);
111 static int builtin_exit(struct job *cmd, struct jobSet *junk);
112 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
113 static int builtin_help(struct job *cmd, struct jobSet *junk);
114 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
115 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
116 static int builtin_export(struct job *cmd, struct jobSet *junk);
117 static int builtin_source(struct job *cmd, struct jobSet *jobList);
118 static int builtin_unset(struct job *cmd, struct jobSet *junk);
119 static int builtin_read(struct job *cmd, struct jobSet *junk);
120 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
121 static int builtin_if(struct job *cmd, struct jobSet *junk);
122 static int builtin_then(struct job *cmd, struct jobSet *junk);
123 static int builtin_else(struct job *cmd, struct jobSet *junk);
124 static int builtin_fi(struct job *cmd, struct jobSet *junk);
128 /* function prototypes for shell stuff */
129 static void checkJobs(struct jobSet *jobList);
130 static int getCommand(FILE * source, char *command);
131 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
132 static int setupRedirections(struct childProgram *prog);
133 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
134 static int busy_loop(FILE * input);
137 /* Table of built-in functions (these are non-forking builtins, meaning they
138 * can change global variables in the parent shell process but they will not
139 * work with pipes and redirects; 'unset foo | whatever' will not work) */
140 static struct builtInCommand bltins[] = {
141 {"bg", "Resume a job in the background", builtin_fg_bg},
142 {"cd", "Change working directory", builtin_cd},
143 {"exit", "Exit from shell()", builtin_exit},
144 {"fg", "Bring job into the foreground", builtin_fg_bg},
145 {"jobs", "Lists the active jobs", builtin_jobs},
146 {"export", "Set environment variable", builtin_export},
147 {"unset", "Unset environment variable", builtin_unset},
148 {"read", "Input environment variable", builtin_read},
152 /* Table of forking built-in functions (things that fork cannot change global
153 * variables in the parent process, such as the current working directory) */
154 static struct builtInCommand bltins_forking[] = {
155 {"env", "Print all environment variables", builtin_env},
156 {"pwd", "Print current directory", builtin_pwd},
157 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
158 {"if", NULL, builtin_if},
159 {"then", NULL, builtin_then},
160 {"else", NULL, builtin_else},
161 {"fi", NULL, builtin_fi},
163 {".", "Source-in and run commands in a file", builtin_source},
164 {"help", "List shell built-in commands", builtin_help},
168 static char *prompt = "# ";
170 static char *local_pending_command = NULL;
171 static char *promptStr = NULL;
172 static struct jobSet jobList = { NULL, NULL };
174 #ifdef BB_FEATURE_SH_COMMAND_EDITING
175 void win_changed(int junk)
177 struct winsize win = { 0, 0, 0, 0 };
178 ioctl(0, TIOCGWINSZ, &win);
179 if (win.ws_col > 0) {
180 cmdedit_setwidth( win.ws_col - 1);
186 /* built-in 'cd <path>' handler */
187 static int builtin_cd(struct job *cmd, struct jobSet *junk)
191 if (!cmd->progs[0].argv[1] == 1)
192 newdir = getenv("HOME");
194 newdir = cmd->progs[0].argv[1];
196 printf("cd: %s: %s\n", newdir, strerror(errno));
199 getcwd(cwd, sizeof(char)*MAX_LINE);
204 /* built-in 'env' handler */
205 static int builtin_env(struct job *dummy, struct jobSet *junk)
209 for (e = environ; *e; e++) {
210 fprintf(stdout, "%s\n", *e);
215 /* built-in 'exit' handler */
216 static int builtin_exit(struct job *cmd, struct jobSet *junk)
218 if (!cmd->progs[0].argv[1] == 1)
221 return(atoi(cmd->progs[0].argv[1]));
224 /* built-in 'fg' and 'bg' handler */
225 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
228 struct job *job=NULL;
230 if (!jobList->head) {
231 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
232 errorMsg("%s: exactly one argument is expected\n",
233 cmd->progs[0].argv[0]);
236 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
237 errorMsg("%s: bad argument '%s'\n",
238 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
240 for (job = jobList->head; job; job = job->next) {
241 if (job->jobId == jobNum) {
251 errorMsg("%s: unknown job %d\n",
252 cmd->progs[0].argv[0], jobNum);
256 if (*cmd->progs[0].argv[0] == 'f') {
257 /* Make this job the foreground job */
258 /* suppress messages when run from /linuxrc mag@sysgo.de */
259 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
264 /* Restart the processes in the job */
265 for (i = 0; i < job->numProgs; i++)
266 job->progs[i].isStopped = 0;
268 kill(-job->pgrp, SIGCONT);
270 job->stoppedProgs = 0;
275 /* built-in 'help' handler */
276 static int builtin_help(struct job *dummy, struct jobSet *junk)
278 struct builtInCommand *x;
280 fprintf(stdout, "\nBuilt-in commands:\n");
281 fprintf(stdout, "-------------------\n");
282 for (x = bltins; x->cmd; x++) {
285 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
287 for (x = bltins_forking; x->cmd; x++) {
290 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
292 fprintf(stdout, "\n\n");
296 /* built-in 'jobs' handler */
297 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
302 for (job = jobList->head; job; job = job->next) {
303 if (job->runningProgs == job->stoppedProgs)
304 statusString = "Stopped";
306 statusString = "Running";
308 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
314 /* built-in 'pwd' handler */
315 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
317 getcwd(cwd, sizeof(char)*MAX_LINE);
318 fprintf(stdout, "%s\n", cwd);
322 /* built-in 'export VAR=value' handler */
323 static int builtin_export(struct job *cmd, struct jobSet *junk)
327 if (!cmd->progs[0].argv[1] == 1) {
328 return (builtin_env(cmd, junk));
330 res = putenv(cmd->progs[0].argv[1]);
332 fprintf(stdout, "export: %s\n", strerror(errno));
336 /* built-in 'read VAR' handler */
337 static int builtin_read(struct job *cmd, struct jobSet *junk)
339 int res = 0, len, newlen;
341 char string[MAX_READ];
343 if (cmd->progs[0].argv[1]) {
344 /* argument (VAR) given: put "VAR=" into buffer */
345 strcpy(string, cmd->progs[0].argv[1]);
346 len = strlen(string);
349 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
350 newlen = strlen(string);
352 string[--newlen] = '\0'; /* chomp trailing newline */
354 ** string should now contain "VAR=<value>"
355 ** copy it (putenv() won't do that, so we must make sure
356 ** the string resides in a static buffer!)
359 if((s = strdup(string)))
362 fprintf(stdout, "read: %s\n", strerror(errno));
365 fgets(string, sizeof(string), stdin);
370 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
371 /* Built-in handler for 'if' commands */
372 static int builtin_if(struct job *cmd, struct jobSet *junk)
374 cmd->jobContext |= IF_EXP_CONTEXT;
375 printf("Hit an if -- jobContext=%d\n", cmd->jobContext);
379 /* Built-in handler for 'then' (part of the 'if' command) */
380 static int builtin_then(struct job *cmd, struct jobSet *junk)
382 if (cmd->jobContext & IF_EXP_CONTEXT) {
383 fprintf(stderr, "unexpected token `then'\n");
387 cmd->jobContext |= THEN_EXP_CONTEXT;
388 printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
392 /* Built-in handler for 'else' (part of the 'if' command) */
393 static int builtin_else(struct job *cmd, struct jobSet *junk)
395 printf("Hit an else\n");
396 cmd->jobContext |= ELSE_EXP_CONTEXT;
400 /* Built-in handler for 'fi' (part of the 'if' command) */
401 static int builtin_fi(struct job *cmd, struct jobSet *junk)
403 printf("Hit an fi\n");
408 /* Built-in '.' handler (read-in and execute commands from file) */
409 static int builtin_source(struct job *cmd, struct jobSet *junk)
414 if (!cmd->progs[0].argv[1] == 1)
417 input = fopen(cmd->progs[0].argv[1], "r");
419 fprintf(stdout, "Couldn't open file '%s'\n",
420 cmd->progs[0].argv[1]);
424 /* Now run the file */
425 status = busy_loop(input);
429 /* built-in 'unset VAR' handler */
430 static int builtin_unset(struct job *cmd, struct jobSet *junk)
432 if (!cmd->progs[0].argv[1] == 1) {
433 fprintf(stdout, "unset: parameter required.\n");
436 unsetenv(cmd->progs[0].argv[1]);
440 /* free up all memory from a job */
441 static void freeJob(struct job *cmd)
445 for (i = 0; i < cmd->numProgs; i++) {
446 free(cmd->progs[i].argv);
447 if (cmd->progs[i].redirections)
448 free(cmd->progs[i].redirections);
449 if (cmd->progs[i].freeGlob)
450 globfree(&cmd->progs[i].globResult);
456 memset(cmd, 0, sizeof(struct job));
459 /* remove a job from the jobList */
460 static void removeJob(struct jobSet *jobList, struct job *job)
465 if (job == jobList->head) {
466 jobList->head = job->next;
468 prevJob = jobList->head;
469 while (prevJob->next != job)
470 prevJob = prevJob->next;
471 prevJob->next = job->next;
477 /* Checks to see if any background processes have exited -- if they
478 have, figure out why and see if a job has completed */
479 static void checkJobs(struct jobSet *jobList)
486 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
487 for (job = jobList->head; job; job = job->next) {
489 while (progNum < job->numProgs &&
490 job->progs[progNum].pid != childpid) progNum++;
491 if (progNum < job->numProgs)
495 /* This happens on backticked commands */
499 if (WIFEXITED(status) || WIFSIGNALED(status)) {
502 job->progs[progNum].pid = 0;
504 if (!job->runningProgs) {
505 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
506 removeJob(jobList, job);
511 job->progs[progNum].isStopped = 1;
513 if (job->stoppedProgs == job->numProgs) {
514 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
520 if (childpid == -1 && errno != ECHILD)
524 static int getCommand(FILE * source, char *command)
526 if (source == NULL) {
527 if (local_pending_command) {
528 /* a command specified (-c option): return it & mark it done */
529 strcpy(command, local_pending_command);
530 free(local_pending_command);
531 local_pending_command = NULL;
537 if (source == stdin) {
538 #ifdef BB_FEATURE_SH_COMMAND_EDITING
540 len=fprintf(stdout, "%s %s", cwd, prompt);
542 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
543 sprintf(promptStr, "%s %s", cwd, prompt);
544 cmdedit_read_input(promptStr, command);
548 fprintf(stdout, "%s %s", cwd, prompt);
553 if (!fgets(command, BUFSIZ - 2, source)) {
559 /* remove trailing newline */
560 command[strlen(command) - 1] = '\0';
565 static void globLastArgument(struct childProgram *prog, int *argcPtr,
569 int argcAlloced = *argcAllocedPtr;
573 char *src, *dst, *var;
575 if (argc > 1) { /* cmd->globResult is already initialized */
577 i = prog->globResult.gl_pathc;
583 /* do shell variable substitution */
584 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
585 prog->argv[argc - 1] = var;
587 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
588 if (rc == GLOB_NOSPACE) {
589 errorMsg("out of space during glob operation\n");
591 } else if (rc == GLOB_NOMATCH ||
592 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
593 strcmp(prog->argv[argc - 1],
594 prog->globResult.gl_pathv[i]) == 0)) {
595 /* we need to remove whatever \ quoting is still present */
596 src = dst = prog->argv[argc - 1];
604 argcAlloced += (prog->globResult.gl_pathc - i);
606 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
607 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
608 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
609 argc += (prog->globResult.gl_pathc - i - 1);
612 *argcAllocedPtr = argcAlloced;
616 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
617 line). If a valid command is found, commandPtr is set to point to
618 the beginning of the next command (if the original command had more
619 then one job associated with it) or NULL if no more commands are
621 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
624 char *returnCommand = NULL;
625 char *src, *buf, *chptr;
632 struct childProgram *prog;
634 /* skip leading white space */
635 while (**commandPtr && isspace(**commandPtr))
638 /* this handles empty lines or leading '#' characters */
639 if (!**commandPtr || (**commandPtr == '#')) {
646 job->progs = xmalloc(sizeof(*job->progs));
648 /* We set the argv elements to point inside of this string. The
649 memory is freed by freeJob(). Allocate twice the original
650 length in case we need to quote every single character.
652 Getting clean memory relieves us of the task of NULL
653 terminating things and makes the rest of this look a bit
654 cleaner (though it is, admittedly, a tad less efficient) */
655 job->cmdBuf = command = calloc(2*strlen(*commandPtr) + 1, sizeof(char));
657 job->jobContext = REGULAR_JOB_CONTEXT;
660 prog->numRedirections = 0;
661 prog->redirections = NULL;
666 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
667 prog->argv[0] = job->cmdBuf;
671 while (*src && !done) {
678 errorMsg("character expected after \\\n");
683 /* in shell, "\'" should yield \' */
686 } else if (*src == '*' || *src == '?' || *src == '[' ||
687 *src == ']') *buf++ = '\\';
689 } else if (isspace(*src)) {
690 if (*prog->argv[argc]) {
692 /* +1 here leaves room for the NULL which ends argv */
693 if ((argc + 1) == argvAlloced) {
695 prog->argv = realloc(prog->argv,
696 sizeof(*prog->argv) *
699 globLastArgument(prog, &argc, &argvAlloced);
700 prog->argv[argc] = buf;
709 case '#': /* comment */
713 case '>': /* redirections */
715 i = prog->numRedirections++;
716 prog->redirections = realloc(prog->redirections,
717 sizeof(*prog->redirections) *
720 prog->redirections[i].fd = -1;
721 if (buf != prog->argv[argc]) {
722 /* the stuff before this character may be the file number
724 prog->redirections[i].fd =
725 strtol(prog->argv[argc], &chptr, 10);
727 if (*chptr && *prog->argv[argc]) {
729 globLastArgument(prog, &argc, &argvAlloced);
730 prog->argv[argc] = buf;
734 if (prog->redirections[i].fd == -1) {
736 prog->redirections[i].fd = 1;
738 prog->redirections[i].fd = 0;
743 prog->redirections[i].type =
744 REDIRECT_APPEND, src++;
746 prog->redirections[i].type = REDIRECT_OVERWRITE;
748 prog->redirections[i].type = REDIRECT_INPUT;
751 /* This isn't POSIX sh compliant. Oh well. */
753 while (isspace(*chptr))
757 errorMsg("file name expected after %c\n", *src);
763 prog->redirections[i].filename = buf;
764 while (*chptr && !isspace(*chptr))
767 src = chptr - 1; /* we src++ later */
768 prog->argv[argc] = ++buf;
772 /* finish this command */
773 if (*prog->argv[argc])
776 errorMsg("empty command in pipe\n");
781 prog->argv[argc] = NULL;
783 /* and start the next */
785 job->progs = realloc(job->progs,
786 sizeof(*job->progs) * job->numProgs);
787 prog = job->progs + (job->numProgs - 1);
788 prog->numRedirections = 0;
789 prog->redirections = NULL;
794 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
795 prog->argv[0] = ++buf;
798 while (*src && isspace(*src))
802 errorMsg("empty command in pipe\n");
807 src--; /* we'll ++ it at the end of the loop */
811 case '&': /* background */
813 case ';': /* multiple commands */
815 returnCommand = *commandPtr + (src - *commandPtr) + 1;
821 errorMsg("character expected after \\\n");
825 if (*src == '*' || *src == '[' || *src == ']'
826 || *src == '?') *buf++ = '\\';
828 #ifdef BB_FEATURE_SH_BACKTICKS
830 /* Exec a backtick-ed command */
832 char* charptr1=NULL, *charptr2;
835 struct jobSet njobList = { NULL, NULL };
839 ptr=strchr(++src, '`');
841 fprintf(stderr, "Unmatched '`' in command\n");
846 /* Make some space to hold just the backticked command */
847 charptr1 = charptr2 = xmalloc(1+ptr-src);
848 snprintf(charptr1, 1+ptr-src, src);
849 newJob = xmalloc(sizeof(struct job));
850 /* Now parse and run the backticked command */
851 if (!parseCommand(&charptr1, newJob, &njobList, isBg)
852 && newJob->numProgs) {
854 runCommand(newJob, &njobList, 0, pipefd);
860 /* Make a copy of any stuff left over in the command
861 * line after the second backtick */
862 charptr2 = xmalloc(strlen(ptr)+1);
863 memcpy(charptr2, ptr+1, strlen(ptr));
866 /* Copy the output from the backtick-ed command into the
867 * command line, making extra room as needed */
869 charptr1 = xmalloc(BUFSIZ);
870 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
871 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
872 if (newSize > BUFSIZ) {
873 *commandPtr=realloc(*commandPtr, src - *commandPtr +
874 size + 1 + strlen(charptr2));
876 memcpy(src, charptr1, size);
884 /* Now paste into the *commandPtr all the stuff
885 * leftover after the second backtick */
886 memcpy(src, charptr2, strlen(charptr2));
889 /* Now recursively call parseCommand to deal with the new
890 * and improved version of the command line with the backtick
891 * results expanded in place... */
893 return(parseCommand(commandPtr, job, jobList, isBg));
896 #endif // BB_FEATURE_SH_BACKTICKS
904 if (*prog->argv[argc]) {
906 globLastArgument(prog, &argc, &argvAlloced);
912 prog->argv[argc] = NULL;
914 if (!returnCommand) {
915 job->text = xmalloc(strlen(*commandPtr) + 1);
916 strcpy(job->text, *commandPtr);
918 /* This leaves any trailing spaces, which is a bit sloppy */
919 count = returnCommand - *commandPtr;
920 job->text = xmalloc(count + 1);
921 strncpy(job->text, *commandPtr, count);
922 job->text[count] = '\0';
925 *commandPtr = returnCommand;
930 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
935 int pipefds[2]; /* pipefd[0] is for reading */
936 struct builtInCommand *x;
937 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
938 const struct BB_applet *a = applets;
942 nextin = 0, nextout = 1;
943 for (i = 0; i < newJob->numProgs; i++) {
944 if ((i + 1) < newJob->numProgs) {
946 nextout = pipefds[1];
948 if (outPipe[1]!=-1) {
949 nextout = outPipe[1];
955 /* Check if the command matches any non-forking builtins */
956 for (x = bltins; x->cmd; x++) {
957 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
958 return (x->function(newJob, jobList));
962 if (!(newJob->progs[i].pid = fork())) {
963 signal(SIGTTOU, SIG_DFL);
965 if (outPipe[1]!=-1) {
979 /* explicit redirections override pipes */
980 setupRedirections(newJob->progs + i);
982 /* Check if the command matches any of the other builtins */
983 for (x = bltins_forking; x->cmd; x++) {
984 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
985 exit (x->function(newJob, jobList));
988 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
989 /* Check if the command matches any busybox internal commands here */
990 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
991 * works, but '/bin/cat' doesn't ) */
992 while (a->name != 0) {
993 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
995 char** argv=newJob->progs[i].argv;
996 for(argc=0;*argv!=NULL; argv++, argc++);
997 exit((*(a->main)) (argc, newJob->progs[i].argv));
1003 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1004 fatalError("sh: %s: %s\n", newJob->progs[i].argv[0],
1007 if (outPipe[1]!=-1) {
1011 /* put our child in the process group whose leader is the
1012 first process in this pipe */
1013 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1019 /* If there isn't another process, nextin is garbage
1020 but it doesn't matter */
1021 nextin = pipefds[0];
1024 newJob->pgrp = newJob->progs[0].pid;
1026 /* find the ID for the theJob to use */
1028 for (theJob = jobList->head; theJob; theJob = theJob->next)
1029 if (theJob->jobId >= newJob->jobId)
1030 newJob->jobId = theJob->jobId + 1;
1032 /* add the theJob to the list of running jobs */
1033 if (!jobList->head) {
1034 theJob = jobList->head = malloc(sizeof(*theJob));
1036 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1037 theJob->next = malloc(sizeof(*theJob));
1038 theJob = theJob->next;
1042 theJob->next = NULL;
1043 theJob->runningProgs = theJob->numProgs;
1044 theJob->stoppedProgs = 0;
1047 /* we don't wait for background theJobs to return -- append it
1048 to the list of backgrounded theJobs and leave it alone */
1049 printf("[%d] %d\n", theJob->jobId,
1050 newJob->progs[newJob->numProgs - 1].pid);
1052 jobList->fg = theJob;
1054 /* move the new process group into the foreground */
1055 /* suppress messages when run from /linuxrc mag@sysgo.de */
1056 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1057 perror("tcsetpgrp");
1063 static int setupRedirections(struct childProgram *prog)
1067 int mode = O_RDONLY;
1068 struct redirectionSpecifier *redir = prog->redirections;
1070 for (i = 0; i < prog->numRedirections; i++, redir++) {
1071 switch (redir->type) {
1072 case REDIRECT_INPUT:
1075 case REDIRECT_OVERWRITE:
1076 mode = O_RDWR | O_CREAT | O_TRUNC;
1078 case REDIRECT_APPEND:
1079 mode = O_RDWR | O_CREAT | O_APPEND;
1083 openfd = open(redir->filename, mode, 0666);
1085 /* this could get lost if stderr has been redirected, but
1086 bash and ash both lose it as well (though zsh doesn't!) */
1087 errorMsg("error opening %s: %s\n", redir->filename,
1092 if (openfd != redir->fd) {
1093 dup2(openfd, redir->fd);
1102 static int busy_loop(FILE * input)
1105 char *nextCommand = NULL;
1112 /* save current owner of TTY so we can restore it on exit */
1113 parent_pgrp = tcgetpgrp(0);
1115 command = (char *) calloc(BUFSIZ, sizeof(char));
1117 /* don't pay any attention to this signal; it just confuses
1118 things and isn't really meant for shells anyway */
1119 signal(SIGTTOU, SIG_IGN);
1123 /* no job is in the foreground */
1125 /* see if any background processes have exited */
1126 checkJobs(&jobList);
1129 if (getCommand(input, command))
1131 nextCommand = command;
1134 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1136 int pipefds[2] = {-1,-1};
1137 runCommand(&newJob, &jobList, inBg, pipefds);
1141 command = (char *) calloc(BUFSIZ, sizeof(char));
1145 /* a job is running in the foreground; wait for it */
1147 while (!jobList.fg->progs[i].pid ||
1148 jobList.fg->progs[i].isStopped == 1) i++;
1150 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1152 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1153 /* the child exited */
1154 jobList.fg->runningProgs--;
1155 jobList.fg->progs[i].pid = 0;
1157 if (!jobList.fg->runningProgs) {
1160 removeJob(&jobList, jobList.fg);
1164 /* the child was stopped */
1165 jobList.fg->stoppedProgs++;
1166 jobList.fg->progs[i].isStopped = 1;
1168 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1169 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1170 "Stopped", jobList.fg->text);
1176 /* move the shell to the foreground */
1177 /* suppress messages when run from /linuxrc mag@sysgo.de */
1178 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1179 perror("tcsetpgrp");
1185 /* return controlling TTY back to parent process group before exiting */
1186 if (tcsetpgrp(0, parent_pgrp))
1187 perror("tcsetpgrp");
1189 /* return exit status if called with "-c" */
1190 if (input == NULL && WIFEXITED(status))
1191 return WEXITSTATUS(status);
1197 #ifdef BB_FEATURE_CLEAN_UP
1198 void free_memory(void)
1204 if (local_pending_command)
1205 free(local_pending_command);
1207 if (jobList.fg && !jobList.fg->runningProgs) {
1208 removeJob(&jobList, jobList.fg);
1214 int shell_main(int argc, char **argv)
1216 FILE *input = stdin;
1218 /* initialize the cwd -- this is never freed...*/
1219 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1220 getcwd(cwd, sizeof(char)*MAX_LINE);
1222 #ifdef BB_FEATURE_CLEAN_UP
1223 atexit(free_memory);
1226 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1228 signal(SIGWINCH, win_changed);
1232 //if (argv[0] && argv[0][0] == '-') {
1233 // builtin_source("/etc/profile");
1238 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1239 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1241 if (argv[1][0]=='-' && argv[1][1]=='c') {
1243 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1244 if (local_pending_command == 0) {
1245 fatalError("out of memory\n");
1247 for(i=2; i<argc; i++)
1249 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1250 local_pending_command = realloc(local_pending_command,
1251 strlen(local_pending_command) + strlen(argv[i]));
1252 if (local_pending_command==NULL)
1253 fatalError("commands for -c option too long\n");
1255 strcat(local_pending_command, argv[i]);
1256 if ( (i + 1) < argc)
1257 strcat(local_pending_command, " ");
1262 else if (argv[1][0]=='-') {
1266 input = fopen(argv[1], "r");
1268 fatalError("Couldn't open file '%s': %s\n", argv[1],
1274 return (busy_loop(input));