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
31 //#define BB_FEATURE_SH_ENVIRONMENT
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_TRUE_CONTEXT=0x2;
61 static const unsigned int IF_FALSE_CONTEXT=0x4;
62 static const unsigned int THEN_EXP_CONTEXT=0x8;
63 static const unsigned int ELSE_EXP_CONTEXT=0x10;
67 struct job *head; /* head of list of running jobs */
68 struct job *fg; /* current foreground job */
71 struct redirectionSpecifier {
72 enum redirectionType type; /* type of redirection */
73 int fd; /* file descriptor being redirected */
74 char *filename; /* file to redirect fd to */
78 pid_t pid; /* 0 if exited */
79 char **argv; /* program name and arguments */
80 int numRedirections; /* elements in redirection array */
81 struct redirectionSpecifier *redirections; /* I/O redirections */
82 glob_t globResult; /* result of parameter globbing */
83 int freeGlob; /* should we globfree(&globResult)? */
84 int isStopped; /* is the program currently running? */
88 int jobId; /* job number */
89 int numProgs; /* total number of programs in job */
90 int runningProgs; /* number of programs running */
91 char *text; /* name of job */
92 char *cmdBuf; /* buffer various argv's point into */
93 pid_t pgrp; /* process group ID for the job */
94 struct childProgram *progs; /* array of programs in job */
95 struct job *next; /* to track background commands */
96 int stoppedProgs; /* number of programs alive, but stopped */
97 int jobContext; /* bitmask defining current context */
100 struct builtInCommand {
101 char *cmd; /* name */
102 char *descr; /* description */
103 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
106 /* function prototypes for builtins */
107 static int builtin_cd(struct job *cmd, struct jobSet *junk);
108 static int builtin_env(struct job *dummy, struct jobSet *junk);
109 static int builtin_exit(struct job *cmd, struct jobSet *junk);
110 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
111 static int builtin_help(struct job *cmd, struct jobSet *junk);
112 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
113 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
114 static int builtin_export(struct job *cmd, struct jobSet *junk);
115 static int builtin_source(struct job *cmd, struct jobSet *jobList);
116 static int builtin_unset(struct job *cmd, struct jobSet *junk);
117 static int builtin_read(struct job *cmd, struct jobSet *junk);
118 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
119 static int builtin_if(struct job *cmd, struct jobSet *junk);
120 static int builtin_then(struct job *cmd, struct jobSet *junk);
121 static int builtin_else(struct job *cmd, struct jobSet *junk);
122 static int builtin_fi(struct job *cmd, struct jobSet *junk);
126 /* function prototypes for shell stuff */
127 static void checkJobs(struct jobSet *jobList);
128 static int getCommand(FILE * source, char *command);
129 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
130 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
131 static int busy_loop(FILE * input);
134 /* Table of built-in functions (these are non-forking builtins, meaning they
135 * can change global variables in the parent shell process but they will not
136 * work with pipes and redirects; 'unset foo | whatever' will not work) */
137 static struct builtInCommand bltins[] = {
138 {"bg", "Resume a job in the background", builtin_fg_bg},
139 {"cd", "Change working directory", builtin_cd},
140 {"exit", "Exit from shell()", builtin_exit},
141 {"fg", "Bring job into the foreground", builtin_fg_bg},
142 {"jobs", "Lists the active jobs", builtin_jobs},
143 {"export", "Set environment variable", builtin_export},
144 {"unset", "Unset environment variable", builtin_unset},
145 {"read", "Input environment variable", builtin_read},
146 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
147 {"if", NULL, builtin_if},
148 {"then", NULL, builtin_then},
149 {"else", NULL, builtin_else},
150 {"fi", NULL, builtin_fi},
155 /* Table of forking built-in functions (things that fork cannot change global
156 * variables in the parent process, such as the current working directory) */
157 static struct builtInCommand bltins_forking[] = {
158 {"env", "Print all environment variables", builtin_env},
159 {"pwd", "Print current directory", builtin_pwd},
160 {".", "Source-in and run commands in a file", builtin_source},
161 {"help", "List shell built-in commands", builtin_help},
165 static char *prompt = "# ";
167 static char *local_pending_command = NULL;
168 static char *promptStr = NULL;
169 static struct jobSet jobList = { NULL, NULL };
172 #ifdef BB_FEATURE_SH_ENVIRONMENT
173 static int lastBgPid=-1;
174 static int lastReturnCode=-1;
178 #ifdef BB_FEATURE_SH_COMMAND_EDITING
179 void win_changed(int junk)
181 struct winsize win = { 0, 0, 0, 0 };
182 ioctl(0, TIOCGWINSZ, &win);
183 if (win.ws_col > 0) {
184 cmdedit_setwidth( win.ws_col - 1);
190 /* built-in 'cd <path>' handler */
191 static int builtin_cd(struct job *cmd, struct jobSet *junk)
195 if (!cmd->progs[0].argv[1] == 1)
196 newdir = getenv("HOME");
198 newdir = cmd->progs[0].argv[1];
200 printf("cd: %s: %s\n", newdir, strerror(errno));
203 getcwd(cwd, sizeof(char)*MAX_LINE);
208 /* built-in 'env' handler */
209 static int builtin_env(struct job *dummy, struct jobSet *junk)
213 for (e = environ; *e; e++) {
214 fprintf(stdout, "%s\n", *e);
219 /* built-in 'exit' handler */
220 static int builtin_exit(struct job *cmd, struct jobSet *junk)
222 if (!cmd->progs[0].argv[1] == 1)
225 return(atoi(cmd->progs[0].argv[1]));
228 /* built-in 'fg' and 'bg' handler */
229 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
232 struct job *job=NULL;
234 if (!jobList->head) {
235 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
236 errorMsg("%s: exactly one argument is expected\n",
237 cmd->progs[0].argv[0]);
240 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
241 errorMsg("%s: bad argument '%s'\n",
242 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
244 for (job = jobList->head; job; job = job->next) {
245 if (job->jobId == jobNum) {
255 errorMsg("%s: unknown job %d\n",
256 cmd->progs[0].argv[0], jobNum);
260 if (*cmd->progs[0].argv[0] == 'f') {
261 /* Make this job the foreground job */
262 /* suppress messages when run from /linuxrc mag@sysgo.de */
263 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
268 /* Restart the processes in the job */
269 for (i = 0; i < job->numProgs; i++)
270 job->progs[i].isStopped = 0;
272 kill(-job->pgrp, SIGCONT);
274 job->stoppedProgs = 0;
279 /* built-in 'help' handler */
280 static int builtin_help(struct job *dummy, struct jobSet *junk)
282 struct builtInCommand *x;
284 fprintf(stdout, "\nBuilt-in commands:\n");
285 fprintf(stdout, "-------------------\n");
286 for (x = bltins; x->cmd; x++) {
289 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
291 for (x = bltins_forking; x->cmd; x++) {
294 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
296 fprintf(stdout, "\n\n");
300 /* built-in 'jobs' handler */
301 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
306 for (job = jobList->head; job; job = job->next) {
307 if (job->runningProgs == job->stoppedProgs)
308 statusString = "Stopped";
310 statusString = "Running";
312 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
318 /* built-in 'pwd' handler */
319 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
321 getcwd(cwd, sizeof(char)*MAX_LINE);
322 fprintf(stdout, "%s\n", cwd);
326 /* built-in 'export VAR=value' handler */
327 static int builtin_export(struct job *cmd, struct jobSet *junk)
331 if (!cmd->progs[0].argv[1] == 1) {
332 return (builtin_env(cmd, junk));
334 res = putenv(cmd->progs[0].argv[1]);
336 fprintf(stdout, "export: %s\n", strerror(errno));
340 /* built-in 'read VAR' handler */
341 static int builtin_read(struct job *cmd, struct jobSet *junk)
343 int res = 0, len, newlen;
345 char string[MAX_READ];
347 if (cmd->progs[0].argv[1]) {
348 /* argument (VAR) given: put "VAR=" into buffer */
349 strcpy(string, cmd->progs[0].argv[1]);
350 len = strlen(string);
353 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
354 newlen = strlen(string);
356 string[--newlen] = '\0'; /* chomp trailing newline */
358 ** string should now contain "VAR=<value>"
359 ** copy it (putenv() won't do that, so we must make sure
360 ** the string resides in a static buffer!)
363 if((s = strdup(string)))
366 fprintf(stdout, "read: %s\n", strerror(errno));
369 fgets(string, sizeof(string), stdin);
374 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
375 /* Built-in handler for 'if' commands */
376 static int builtin_if(struct job *cmd, struct jobSet *jobList)
379 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
381 /* Now run the 'if' command */
382 status=strlen(charptr1);
383 local_pending_command = xmalloc(status+1);
384 strncpy(local_pending_command, charptr1, status);
385 printf("'if' now running '%s'\n", charptr1);
386 status = busy_loop(NULL); /* Frees local_pending_command */
387 printf("if test returned ");
390 cmd->jobContext |= IF_TRUE_CONTEXT;
393 cmd->jobContext |= IF_FALSE_CONTEXT;
399 /* Built-in handler for 'then' (part of the 'if' command) */
400 static int builtin_then(struct job *cmd, struct jobSet *junk)
403 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
405 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
406 errorMsg("unexpected token `then'\n");
409 /* If the if result was FALSE, skip the 'then' stuff */
410 if (cmd->jobContext & IF_TRUE_CONTEXT) {
414 cmd->jobContext |= THEN_EXP_CONTEXT;
415 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
417 /* Now run the 'then' command */
418 status=strlen(charptr1);
419 local_pending_command = xmalloc(status+1);
420 strncpy(local_pending_command, charptr1, status);
421 printf("'then' now running '%s'\n", charptr1);
422 return( busy_loop(NULL));
425 /* Built-in handler for 'else' (part of the 'if' command) */
426 static int builtin_else(struct job *cmd, struct jobSet *junk)
429 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
431 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
432 errorMsg("unexpected token `else'\n");
435 /* If the if result was TRUE, skip the 'else' stuff */
436 if (cmd->jobContext & IF_FALSE_CONTEXT) {
440 cmd->jobContext |= ELSE_EXP_CONTEXT;
441 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
443 /* Now run the 'else' command */
444 status=strlen(charptr1);
445 local_pending_command = xmalloc(status+1);
446 strncpy(local_pending_command, charptr1, status);
447 printf("'else' now running '%s'\n", charptr1);
448 return( busy_loop(NULL));
451 /* Built-in handler for 'fi' (part of the 'if' command) */
452 static int builtin_fi(struct job *cmd, struct jobSet *junk)
454 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
455 errorMsg("unexpected token `fi'\n");
458 /* Clear out the if and then context bits */
459 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
460 printf("Hit an fi -- jobContext=%d\n", cmd->jobContext);
465 /* Built-in '.' handler (read-in and execute commands from file) */
466 static int builtin_source(struct job *cmd, struct jobSet *junk)
471 if (!cmd->progs[0].argv[1] == 1)
474 input = fopen(cmd->progs[0].argv[1], "r");
476 fprintf(stdout, "Couldn't open file '%s'\n",
477 cmd->progs[0].argv[1]);
481 /* Now run the file */
482 status = busy_loop(input);
486 /* built-in 'unset VAR' handler */
487 static int builtin_unset(struct job *cmd, struct jobSet *junk)
489 if (!cmd->progs[0].argv[1] == 1) {
490 fprintf(stdout, "unset: parameter required.\n");
493 unsetenv(cmd->progs[0].argv[1]);
497 /* free up all memory from a job */
498 static void freeJob(struct job *cmd)
502 for (i = 0; i < cmd->numProgs; i++) {
503 free(cmd->progs[i].argv);
504 if (cmd->progs[i].redirections)
505 free(cmd->progs[i].redirections);
506 if (cmd->progs[i].freeGlob)
507 globfree(&cmd->progs[i].globResult);
513 memset(cmd, 0, sizeof(struct job));
516 /* remove a job from the jobList */
517 static void removeJob(struct jobSet *jobList, struct job *job)
522 if (job == jobList->head) {
523 jobList->head = job->next;
525 prevJob = jobList->head;
526 while (prevJob->next != job)
527 prevJob = prevJob->next;
528 prevJob->next = job->next;
534 /* Checks to see if any background processes have exited -- if they
535 have, figure out why and see if a job has completed */
536 static void checkJobs(struct jobSet *jobList)
543 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
544 for (job = jobList->head; job; job = job->next) {
546 while (progNum < job->numProgs &&
547 job->progs[progNum].pid != childpid) progNum++;
548 if (progNum < job->numProgs)
552 /* This happens on backticked commands */
556 if (WIFEXITED(status) || WIFSIGNALED(status)) {
559 job->progs[progNum].pid = 0;
561 if (!job->runningProgs) {
562 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
563 removeJob(jobList, job);
568 job->progs[progNum].isStopped = 1;
570 if (job->stoppedProgs == job->numProgs) {
571 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
577 if (childpid == -1 && errno != ECHILD)
581 static int setupRedirections(struct childProgram *prog)
586 struct redirectionSpecifier *redir = prog->redirections;
588 for (i = 0; i < prog->numRedirections; i++, redir++) {
589 switch (redir->type) {
593 case REDIRECT_OVERWRITE:
594 mode = O_RDWR | O_CREAT | O_TRUNC;
596 case REDIRECT_APPEND:
597 mode = O_RDWR | O_CREAT | O_APPEND;
601 openfd = open(redir->filename, mode, 0666);
603 /* this could get lost if stderr has been redirected, but
604 bash and ash both lose it as well (though zsh doesn't!) */
605 errorMsg("error opening %s: %s\n", redir->filename,
610 if (openfd != redir->fd) {
611 dup2(openfd, redir->fd);
620 static int getCommand(FILE * source, char *command)
622 if (source == NULL) {
623 if (local_pending_command) {
624 /* a command specified (-c option): return it & mark it done */
625 strcpy(command, local_pending_command);
626 free(local_pending_command);
627 local_pending_command = NULL;
633 if (source == stdin) {
634 #ifdef BB_FEATURE_SH_COMMAND_EDITING
636 len=fprintf(stdout, "%s %s", cwd, prompt);
638 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
639 sprintf(promptStr, "%s %s", cwd, prompt);
640 cmdedit_read_input(promptStr, command);
644 fprintf(stdout, "%s %s", cwd, prompt);
649 if (!fgets(command, BUFSIZ - 2, source)) {
655 /* remove trailing newline */
656 command[strlen(command) - 1] = '\0';
661 #ifdef BB_FEATURE_SH_ENVIRONMENT
662 #define __MAX_INT_CHARS 7
663 static char* itoa(register int i)
665 static char a[__MAX_INT_CHARS];
666 register char *b = a + sizeof(a) - 1;
674 *--b = '0' + (i % 10);
684 static void globLastArgument(struct childProgram *prog, int *argcPtr,
687 int argc_l = *argcPtr;
688 int argcAlloced = *argcAllocedPtr;
692 char *src, *dst, *var;
694 if (argc_l > 1) { /* cmd->globResult is already initialized */
696 i = prog->globResult.gl_pathc;
702 /* do shell variable substitution */
703 if(*prog->argv[argc_l - 1] == '$') {
704 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
705 prog->argv[argc_l - 1] = var;
707 #ifdef BB_FEATURE_SH_ENVIRONMENT
709 switch(*(prog->argv[argc_l - 1] + 1)) {
711 prog->argv[argc_l - 1] = itoa(lastReturnCode);
714 prog->argv[argc_l - 1] = itoa(getpid());
717 prog->argv[argc_l - 1] = itoa(argc-1);
721 *(prog->argv[argc_l - 1])='\0';
723 prog->argv[argc_l - 1] = itoa(lastBgPid);
725 case '0':case '1':case '2':case '3':case '4':
726 case '5':case '6':case '7':case '8':case '9':
728 int index=*(prog->argv[argc_l - 1] + 1)-48;
730 *(prog->argv[argc_l - 1])='\0';
732 prog->argv[argc_l - 1] = argv[index];
741 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
742 if (rc == GLOB_NOSPACE) {
743 errorMsg("out of space during glob operation\n");
745 } else if (rc == GLOB_NOMATCH ||
746 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
747 strcmp(prog->argv[argc_l - 1],
748 prog->globResult.gl_pathv[i]) == 0)) {
749 /* we need to remove whatever \ quoting is still present */
750 src = dst = prog->argv[argc_l - 1];
758 argcAlloced += (prog->globResult.gl_pathc - i);
760 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
761 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
762 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
763 argc_l += (prog->globResult.gl_pathc - i - 1);
766 *argcAllocedPtr = argcAlloced;
770 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
771 line). If a valid command is found, commandPtr is set to point to
772 the beginning of the next command (if the original command had more
773 then one job associated with it) or NULL if no more commands are
775 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
778 char *returnCommand = NULL;
779 char *src, *buf, *chptr;
786 struct childProgram *prog;
788 /* skip leading white space */
789 while (**commandPtr && isspace(**commandPtr))
792 /* this handles empty lines or leading '#' characters */
793 if (!**commandPtr || (**commandPtr == '#')) {
800 job->progs = xmalloc(sizeof(*job->progs));
802 /* We set the argv elements to point inside of this string. The
803 memory is freed by freeJob(). Allocate twice the original
804 length in case we need to quote every single character.
806 Getting clean memory relieves us of the task of NULL
807 terminating things and makes the rest of this look a bit
808 cleaner (though it is, admittedly, a tad less efficient) */
809 job->cmdBuf = command = calloc(2*strlen(*commandPtr) + 1, sizeof(char));
813 prog->numRedirections = 0;
814 prog->redirections = NULL;
819 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
820 prog->argv[0] = job->cmdBuf;
824 while (*src && !done) {
831 errorMsg("character expected after \\\n");
836 /* in shell, "\'" should yield \' */
839 } else if (*src == '*' || *src == '?' || *src == '[' ||
840 *src == ']') *buf++ = '\\';
842 } else if (isspace(*src)) {
843 if (*prog->argv[argc_l]) {
845 /* +1 here leaves room for the NULL which ends argv */
846 if ((argc_l + 1) == argvAlloced) {
848 prog->argv = realloc(prog->argv,
849 sizeof(*prog->argv) *
852 globLastArgument(prog, &argc_l, &argvAlloced);
853 prog->argv[argc_l] = buf;
862 case '#': /* comment */
869 case '>': /* redirections */
871 i = prog->numRedirections++;
872 prog->redirections = realloc(prog->redirections,
873 sizeof(*prog->redirections) *
876 prog->redirections[i].fd = -1;
877 if (buf != prog->argv[argc_l]) {
878 /* the stuff before this character may be the file number
880 prog->redirections[i].fd =
881 strtol(prog->argv[argc_l], &chptr, 10);
883 if (*chptr && *prog->argv[argc_l]) {
885 globLastArgument(prog, &argc_l, &argvAlloced);
886 prog->argv[argc_l] = buf;
890 if (prog->redirections[i].fd == -1) {
892 prog->redirections[i].fd = 1;
894 prog->redirections[i].fd = 0;
899 prog->redirections[i].type =
900 REDIRECT_APPEND, src++;
902 prog->redirections[i].type = REDIRECT_OVERWRITE;
904 prog->redirections[i].type = REDIRECT_INPUT;
907 /* This isn't POSIX sh compliant. Oh well. */
909 while (isspace(*chptr))
913 errorMsg("file name expected after %c\n", *src);
919 prog->redirections[i].filename = buf;
920 while (*chptr && !isspace(*chptr))
923 src = chptr - 1; /* we src++ later */
924 prog->argv[argc_l] = ++buf;
928 /* finish this command */
929 if (*prog->argv[argc_l])
932 errorMsg("empty command in pipe\n");
937 prog->argv[argc_l] = NULL;
939 /* and start the next */
941 job->progs = realloc(job->progs,
942 sizeof(*job->progs) * job->numProgs);
943 prog = job->progs + (job->numProgs - 1);
944 prog->numRedirections = 0;
945 prog->redirections = NULL;
950 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
951 prog->argv[0] = ++buf;
954 while (*src && isspace(*src))
958 errorMsg("empty command in pipe\n");
963 src--; /* we'll ++ it at the end of the loop */
967 case '&': /* background */
969 case ';': /* multiple commands */
971 returnCommand = *commandPtr + (src - *commandPtr) + 1;
977 errorMsg("character expected after \\\n");
981 if (*src == '*' || *src == '[' || *src == ']'
982 || *src == '?') *buf++ = '\\';
984 #ifdef BB_FEATURE_SH_BACKTICKS
986 /* Exec a backtick-ed command */
988 char* charptr1=NULL, *charptr2;
991 struct jobSet njobList = { NULL, NULL };
995 ptr=strchr(++src, '`');
997 fprintf(stderr, "Unmatched '`' in command\n");
1002 /* Make some space to hold just the backticked command */
1003 charptr1 = charptr2 = xmalloc(1+ptr-src);
1004 snprintf(charptr1, 1+ptr-src, src);
1005 newJob = xmalloc(sizeof(struct job));
1006 /* Now parse and run the backticked command */
1007 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
1008 && newJob->numProgs) {
1010 runCommand(newJob, &njobList, 0, pipefd);
1016 /* Make a copy of any stuff left over in the command
1017 * line after the second backtick */
1018 charptr2 = xmalloc(strlen(ptr)+1);
1019 memcpy(charptr2, ptr+1, strlen(ptr));
1022 /* Copy the output from the backtick-ed command into the
1023 * command line, making extra room as needed */
1025 charptr1 = xmalloc(BUFSIZ);
1026 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1027 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1028 if (newSize > BUFSIZ) {
1029 *commandPtr=realloc(*commandPtr, src - *commandPtr +
1030 size + 1 + strlen(charptr2));
1032 memcpy(src, charptr1, size);
1040 /* Now paste into the *commandPtr all the stuff
1041 * leftover after the second backtick */
1042 memcpy(src, charptr2, strlen(charptr2));
1045 /* Now recursively call parseCommand to deal with the new
1046 * and improved version of the command line with the backtick
1047 * results expanded in place... */
1049 return(parseCommand(commandPtr, job, jobList, inBg));
1052 #endif // BB_FEATURE_SH_BACKTICKS
1060 if (*prog->argv[argc_l]) {
1062 globLastArgument(prog, &argc_l, &argvAlloced);
1068 prog->argv[argc_l] = NULL;
1070 if (!returnCommand) {
1071 job->text = xmalloc(strlen(*commandPtr) + 1);
1072 strcpy(job->text, *commandPtr);
1074 /* This leaves any trailing spaces, which is a bit sloppy */
1075 count = returnCommand - *commandPtr;
1076 job->text = xmalloc(count + 1);
1077 strncpy(job->text, *commandPtr, count);
1078 job->text[count] = '\0';
1081 *commandPtr = returnCommand;
1086 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1090 int nextin, nextout;
1091 int pipefds[2]; /* pipefd[0] is for reading */
1092 struct builtInCommand *x;
1093 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1094 const struct BB_applet *a = applets;
1098 nextin = 0, nextout = 1;
1099 for (i = 0; i < newJob->numProgs; i++) {
1100 if ((i + 1) < newJob->numProgs) {
1102 nextout = pipefds[1];
1104 if (outPipe[1]!=-1) {
1105 nextout = outPipe[1];
1111 /* Check if the command matches any non-forking builtins */
1112 for (x = bltins; x->cmd; x++) {
1113 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1114 return (x->function(newJob, jobList));
1118 if (!(newJob->progs[i].pid = fork())) {
1119 signal(SIGTTOU, SIG_DFL);
1121 if (outPipe[1]!=-1) {
1135 /* explicit redirections override pipes */
1136 setupRedirections(newJob->progs + i);
1138 /* Check if the command matches any of the other builtins */
1139 for (x = bltins_forking; x->cmd; x++) {
1140 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1141 exit (x->function(newJob, jobList));
1144 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1145 /* Check if the command matches any busybox internal commands here */
1146 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
1147 * works, but '/bin/cat' doesn't ) */
1148 while (a->name != 0) {
1149 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
1151 char** argv=newJob->progs[i].argv;
1152 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1153 exit((*(a->main)) (argc_l, newJob->progs[i].argv));
1159 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1160 fatalError("%s: %s\n", newJob->progs[i].argv[0],
1163 if (outPipe[1]!=-1) {
1167 /* put our child in the process group whose leader is the
1168 first process in this pipe */
1169 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1175 /* If there isn't another process, nextin is garbage
1176 but it doesn't matter */
1177 nextin = pipefds[0];
1180 newJob->pgrp = newJob->progs[0].pid;
1182 /* find the ID for the theJob to use */
1184 for (theJob = jobList->head; theJob; theJob = theJob->next)
1185 if (theJob->jobId >= newJob->jobId)
1186 newJob->jobId = theJob->jobId + 1;
1188 /* add the theJob to the list of running jobs */
1189 if (!jobList->head) {
1190 theJob = jobList->head = malloc(sizeof(*theJob));
1192 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1193 theJob->next = malloc(sizeof(*theJob));
1194 theJob = theJob->next;
1198 theJob->next = NULL;
1199 theJob->runningProgs = theJob->numProgs;
1200 theJob->stoppedProgs = 0;
1203 /* we don't wait for background theJobs to return -- append it
1204 to the list of backgrounded theJobs and leave it alone */
1205 printf("[%d] %d\n", theJob->jobId,
1206 newJob->progs[newJob->numProgs - 1].pid);
1207 #ifdef BB_FEATURE_SH_ENVIRONMENT
1208 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1211 jobList->fg = theJob;
1213 /* move the new process group into the foreground */
1214 /* suppress messages when run from /linuxrc mag@sysgo.de */
1215 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1216 perror("tcsetpgrp");
1222 static int busy_loop(FILE * input)
1225 char *nextCommand = NULL;
1231 newJob.jobContext = REGULAR_JOB_CONTEXT;
1233 /* save current owner of TTY so we can restore it on exit */
1234 parent_pgrp = tcgetpgrp(0);
1236 command = (char *) calloc(BUFSIZ, sizeof(char));
1238 /* don't pay any attention to this signal; it just confuses
1239 things and isn't really meant for shells anyway */
1240 signal(SIGTTOU, SIG_IGN);
1244 /* no job is in the foreground */
1246 /* see if any background processes have exited */
1247 checkJobs(&jobList);
1250 if (getCommand(input, command))
1252 nextCommand = command;
1255 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1257 int pipefds[2] = {-1,-1};
1258 runCommand(&newJob, &jobList, inBg, pipefds);
1262 command = (char *) calloc(BUFSIZ, sizeof(char));
1266 /* a job is running in the foreground; wait for it */
1268 while (!jobList.fg->progs[i].pid ||
1269 jobList.fg->progs[i].isStopped == 1) i++;
1271 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1273 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1274 /* the child exited */
1275 jobList.fg->runningProgs--;
1276 jobList.fg->progs[i].pid = 0;
1278 if (!jobList.fg->runningProgs) {
1281 removeJob(&jobList, jobList.fg);
1284 #ifdef BB_FEATURE_SH_ENVIRONMENT
1285 lastReturnCode=WEXITSTATUS(status);
1288 /* the child was stopped */
1289 jobList.fg->stoppedProgs++;
1290 jobList.fg->progs[i].isStopped = 1;
1292 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1293 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1294 "Stopped", jobList.fg->text);
1300 /* move the shell to the foreground */
1301 /* suppress messages when run from /linuxrc mag@sysgo.de */
1302 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1303 perror("tcsetpgrp");
1309 /* return controlling TTY back to parent process group before exiting */
1310 if (tcsetpgrp(0, parent_pgrp))
1311 perror("tcsetpgrp");
1313 /* return exit status if called with "-c" */
1314 if (input == NULL && WIFEXITED(status))
1315 return WEXITSTATUS(status);
1321 #ifdef BB_FEATURE_CLEAN_UP
1322 void free_memory(void)
1328 if (local_pending_command)
1329 free(local_pending_command);
1331 if (jobList.fg && !jobList.fg->runningProgs) {
1332 removeJob(&jobList, jobList.fg);
1338 int shell_main(int argc_l, char **argv_l)
1340 FILE *input = stdin;
1344 /* initialize the cwd -- this is never freed...*/
1345 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1346 getcwd(cwd, sizeof(char)*MAX_LINE);
1348 #ifdef BB_FEATURE_CLEAN_UP
1349 atexit(free_memory);
1352 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1354 signal(SIGWINCH, win_changed);
1358 //if (argv[0] && argv[0][0] == '-') {
1359 // builtin_source("/etc/profile");
1364 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1365 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1367 if (argv[1][0]=='-' && argv[1][1]=='c') {
1369 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1370 if (local_pending_command == 0) {
1371 fatalError("out of memory\n");
1373 for(i=2; i<argc; i++)
1375 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1376 local_pending_command = realloc(local_pending_command,
1377 strlen(local_pending_command) + strlen(argv[i]));
1378 if (local_pending_command==NULL)
1379 fatalError("commands for -c option too long\n");
1381 strcat(local_pending_command, argv[i]);
1382 if ( (i + 1) < argc)
1383 strcat(local_pending_command, " ");
1388 else if (argv[1][0]=='-') {
1392 input = fopen(argv[1], "r");
1394 fatalError("Couldn't open file '%s': %s\n", argv[1],
1400 return (busy_loop(input));