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
44 #include <sys/ioctl.h>
48 #ifdef BB_FEATURE_SH_COMMAND_EDITING
52 #define MAX_LINE 256 /* size of input buffer for `read' builtin */
53 #define MAX_READ 128 /* size of input buffer for `read' builtin */
54 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
57 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
61 static const unsigned int REGULAR_JOB_CONTEXT=0x1;
62 static const unsigned int IF_TRUE_CONTEXT=0x2;
63 static const unsigned int IF_FALSE_CONTEXT=0x4;
64 static const unsigned int THEN_EXP_CONTEXT=0x8;
65 static const unsigned int ELSE_EXP_CONTEXT=0x10;
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 *inBg);
132 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
133 static int busy_loop(FILE * input);
136 /* Table of built-in functions (these are non-forking builtins, meaning they
137 * can change global variables in the parent shell process but they will not
138 * work with pipes and redirects; 'unset foo | whatever' will not work) */
139 static struct builtInCommand bltins[] = {
140 {"bg", "Resume a job in the background", builtin_fg_bg},
141 {"cd", "Change working directory", builtin_cd},
142 {"exit", "Exit from shell()", builtin_exit},
143 {"fg", "Bring job into the foreground", builtin_fg_bg},
144 {"jobs", "Lists the active jobs", builtin_jobs},
145 {"export", "Set environment variable", builtin_export},
146 {"unset", "Unset environment variable", builtin_unset},
147 {"read", "Input environment variable", builtin_read},
148 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
149 {"if", NULL, builtin_if},
150 {"then", NULL, builtin_then},
151 {"else", NULL, builtin_else},
152 {"fi", NULL, builtin_fi},
157 /* Table of forking built-in functions (things that fork cannot change global
158 * variables in the parent process, such as the current working directory) */
159 static struct builtInCommand bltins_forking[] = {
160 {"env", "Print all environment variables", builtin_env},
161 {"pwd", "Print current directory", builtin_pwd},
162 {".", "Source-in and run commands in a file", builtin_source},
163 {"help", "List shell built-in commands", builtin_help},
167 static char *prompt = "# ";
169 static char *local_pending_command = NULL;
170 static char *promptStr = NULL;
171 static struct jobSet jobList = { NULL, NULL };
174 #ifdef BB_FEATURE_SH_ENVIRONMENT
175 static int lastBgPid=-1;
176 static int lastReturnCode=-1;
177 static int showXtrace=FALSE;
181 #ifdef BB_FEATURE_SH_COMMAND_EDITING
182 void win_changed(int junk)
184 struct winsize win = { 0, 0, 0, 0 };
185 ioctl(0, TIOCGWINSZ, &win);
186 if (win.ws_col > 0) {
187 cmdedit_setwidth( win.ws_col - 1);
193 /* built-in 'cd <path>' handler */
194 static int builtin_cd(struct job *cmd, struct jobSet *junk)
198 if (!cmd->progs[0].argv[1] == 1)
199 newdir = getenv("HOME");
201 newdir = cmd->progs[0].argv[1];
203 printf("cd: %s: %s\n", newdir, strerror(errno));
206 getcwd(cwd, sizeof(char)*MAX_LINE);
211 /* built-in 'env' handler */
212 static int builtin_env(struct job *dummy, struct jobSet *junk)
216 for (e = environ; *e; e++) {
217 fprintf(stdout, "%s\n", *e);
222 /* built-in 'exit' handler */
223 static int builtin_exit(struct job *cmd, struct jobSet *junk)
225 if (!cmd->progs[0].argv[1] == 1)
228 return(atoi(cmd->progs[0].argv[1]));
231 /* built-in 'fg' and 'bg' handler */
232 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
235 struct job *job=NULL;
237 if (!jobList->head) {
238 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
239 errorMsg("%s: exactly one argument is expected\n",
240 cmd->progs[0].argv[0]);
243 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
244 errorMsg("%s: bad argument '%s'\n",
245 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
247 for (job = jobList->head; job; job = job->next) {
248 if (job->jobId == jobNum) {
258 errorMsg("%s: unknown job %d\n",
259 cmd->progs[0].argv[0], jobNum);
263 if (*cmd->progs[0].argv[0] == 'f') {
264 /* Make this job the foreground job */
265 /* suppress messages when run from /linuxrc mag@sysgo.de */
266 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
271 /* Restart the processes in the job */
272 for (i = 0; i < job->numProgs; i++)
273 job->progs[i].isStopped = 0;
275 kill(-job->pgrp, SIGCONT);
277 job->stoppedProgs = 0;
282 /* built-in 'help' handler */
283 static int builtin_help(struct job *dummy, struct jobSet *junk)
285 struct builtInCommand *x;
287 fprintf(stdout, "\nBuilt-in commands:\n");
288 fprintf(stdout, "-------------------\n");
289 for (x = bltins; x->cmd; x++) {
292 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
294 for (x = bltins_forking; x->cmd; x++) {
297 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
299 fprintf(stdout, "\n\n");
303 /* built-in 'jobs' handler */
304 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
309 for (job = jobList->head; job; job = job->next) {
310 if (job->runningProgs == job->stoppedProgs)
311 statusString = "Stopped";
313 statusString = "Running";
315 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
321 /* built-in 'pwd' handler */
322 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
324 getcwd(cwd, sizeof(char)*MAX_LINE);
325 fprintf(stdout, "%s\n", cwd);
329 /* built-in 'export VAR=value' handler */
330 static int builtin_export(struct job *cmd, struct jobSet *junk)
334 if (!cmd->progs[0].argv[1] == 1) {
335 return (builtin_env(cmd, junk));
337 res = putenv(cmd->progs[0].argv[1]);
339 fprintf(stdout, "export: %s\n", strerror(errno));
343 /* built-in 'read VAR' handler */
344 static int builtin_read(struct job *cmd, struct jobSet *junk)
346 int res = 0, len, newlen;
348 char string[MAX_READ];
350 if (cmd->progs[0].argv[1]) {
351 /* argument (VAR) given: put "VAR=" into buffer */
352 strcpy(string, cmd->progs[0].argv[1]);
353 len = strlen(string);
356 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
357 newlen = strlen(string);
359 string[--newlen] = '\0'; /* chomp trailing newline */
361 ** string should now contain "VAR=<value>"
362 ** copy it (putenv() won't do that, so we must make sure
363 ** the string resides in a static buffer!)
366 if((s = strdup(string)))
369 fprintf(stdout, "read: %s\n", strerror(errno));
372 fgets(string, sizeof(string), stdin);
377 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
378 /* Built-in handler for 'if' commands */
379 static int builtin_if(struct job *cmd, struct jobSet *jobList)
382 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
384 /* Now run the 'if' command */
385 status=strlen(charptr1);
386 local_pending_command = xmalloc(status+1);
387 strncpy(local_pending_command, charptr1, status);
388 local_pending_command[status]='\0';
390 fprintf(stderr, "'if' now testing '%s'\n", local_pending_command);
392 status = busy_loop(NULL); /* Frees local_pending_command */
394 fprintf(stderr, "if test returned ");
398 fprintf(stderr, "TRUE\n");
400 cmd->jobContext |= IF_TRUE_CONTEXT;
403 fprintf(stderr, "FALSE\n");
405 cmd->jobContext |= IF_FALSE_CONTEXT;
411 /* Built-in handler for 'then' (part of the 'if' command) */
412 static int builtin_then(struct job *cmd, struct jobSet *junk)
415 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
417 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
418 errorMsg("unexpected token `then'\n");
421 /* If the if result was FALSE, skip the 'then' stuff */
422 if (cmd->jobContext & IF_FALSE_CONTEXT) {
426 cmd->jobContext |= THEN_EXP_CONTEXT;
427 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
429 /* Now run the 'then' command */
430 status=strlen(charptr1);
431 local_pending_command = xmalloc(status+1);
432 strncpy(local_pending_command, charptr1, status);
433 local_pending_command[status]='\0';
435 fprintf(stderr, "'then' now running '%s'\n", charptr1);
437 return( busy_loop(NULL));
440 /* Built-in handler for 'else' (part of the 'if' command) */
441 static int builtin_else(struct job *cmd, struct jobSet *junk)
444 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
446 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
447 errorMsg("unexpected token `else'\n");
450 /* If the if result was TRUE, skip the 'else' stuff */
451 if (cmd->jobContext & IF_TRUE_CONTEXT) {
455 cmd->jobContext |= ELSE_EXP_CONTEXT;
456 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
458 /* Now run the 'else' command */
459 status=strlen(charptr1);
460 local_pending_command = xmalloc(status+1);
461 strncpy(local_pending_command, charptr1, status);
462 local_pending_command[status]='\0';
464 fprintf(stderr, "'else' now running '%s'\n", charptr1);
466 return( busy_loop(NULL));
469 /* Built-in handler for 'fi' (part of the 'if' command) */
470 static int builtin_fi(struct job *cmd, struct jobSet *junk)
472 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
473 errorMsg("unexpected token `fi'\n");
476 /* Clear out the if and then context bits */
477 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
479 fprintf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext);
485 /* Built-in '.' handler (read-in and execute commands from file) */
486 static int builtin_source(struct job *cmd, struct jobSet *junk)
491 if (!cmd->progs[0].argv[1] == 1)
494 input = fopen(cmd->progs[0].argv[1], "r");
496 fprintf(stdout, "Couldn't open file '%s'\n",
497 cmd->progs[0].argv[1]);
501 /* Now run the file */
502 status = busy_loop(input);
506 /* built-in 'unset VAR' handler */
507 static int builtin_unset(struct job *cmd, struct jobSet *junk)
509 if (!cmd->progs[0].argv[1] == 1) {
510 fprintf(stdout, "unset: parameter required.\n");
513 unsetenv(cmd->progs[0].argv[1]);
517 /* free up all memory from a job */
518 static void freeJob(struct job *cmd)
522 for (i = 0; i < cmd->numProgs; i++) {
523 free(cmd->progs[i].argv);
524 if (cmd->progs[i].redirections)
525 free(cmd->progs[i].redirections);
526 if (cmd->progs[i].freeGlob)
527 globfree(&cmd->progs[i].globResult);
533 memset(cmd, 0, sizeof(struct job));
536 /* remove a job from the jobList */
537 static void removeJob(struct jobSet *jobList, struct job *job)
542 if (job == jobList->head) {
543 jobList->head = job->next;
545 prevJob = jobList->head;
546 while (prevJob->next != job)
547 prevJob = prevJob->next;
548 prevJob->next = job->next;
554 /* Checks to see if any background processes have exited -- if they
555 have, figure out why and see if a job has completed */
556 static void checkJobs(struct jobSet *jobList)
563 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
564 for (job = jobList->head; job; job = job->next) {
566 while (progNum < job->numProgs &&
567 job->progs[progNum].pid != childpid) progNum++;
568 if (progNum < job->numProgs)
572 /* This happens on backticked commands */
576 if (WIFEXITED(status) || WIFSIGNALED(status)) {
579 job->progs[progNum].pid = 0;
581 if (!job->runningProgs) {
582 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
583 removeJob(jobList, job);
588 job->progs[progNum].isStopped = 1;
590 if (job->stoppedProgs == job->numProgs) {
591 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
597 if (childpid == -1 && errno != ECHILD)
601 static int setupRedirections(struct childProgram *prog)
606 struct redirectionSpecifier *redir = prog->redirections;
608 for (i = 0; i < prog->numRedirections; i++, redir++) {
609 switch (redir->type) {
613 case REDIRECT_OVERWRITE:
614 mode = O_RDWR | O_CREAT | O_TRUNC;
616 case REDIRECT_APPEND:
617 mode = O_RDWR | O_CREAT | O_APPEND;
621 openfd = open(redir->filename, mode, 0666);
623 /* this could get lost if stderr has been redirected, but
624 bash and ash both lose it as well (though zsh doesn't!) */
625 errorMsg("error opening %s: %s\n", redir->filename,
630 if (openfd != redir->fd) {
631 dup2(openfd, redir->fd);
640 static int getCommand(FILE * source, char *command)
642 if (source == NULL) {
643 if (local_pending_command) {
644 /* a command specified (-c option): return it & mark it done */
645 strcpy(command, local_pending_command);
646 free(local_pending_command);
647 local_pending_command = NULL;
653 if (source == stdin) {
654 #ifdef BB_FEATURE_SH_COMMAND_EDITING
658 ** enable command line editing only while a command line
659 ** is actually being read; otherwise, we'll end up bequeathing
660 ** atexit() handlers and other unwanted stuff to our
661 ** child processes (rob@sysgo.de)
664 signal(SIGWINCH, win_changed);
665 len=fprintf(stdout, "%s %s", cwd, prompt);
667 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
668 sprintf(promptStr, "%s %s", cwd, prompt);
669 cmdedit_read_input(promptStr, command);
672 signal(SIGWINCH, SIG_DFL);
675 fprintf(stdout, "%s %s", cwd, prompt);
680 if (!fgets(command, BUFSIZ - 2, source)) {
686 /* remove trailing newline */
687 command[strlen(command) - 1] = '\0';
692 #ifdef BB_FEATURE_SH_ENVIRONMENT
693 #define __MAX_INT_CHARS 7
694 static char* itoa(register int i)
696 static char a[__MAX_INT_CHARS];
697 register char *b = a + sizeof(a) - 1;
705 *--b = '0' + (i % 10);
715 static void globLastArgument(struct childProgram *prog, int *argcPtr,
718 int argc_l = *argcPtr;
719 int argcAlloced = *argcAllocedPtr;
723 char *src, *dst, *var;
725 if (argc_l > 1) { /* cmd->globResult is already initialized */
727 i = prog->globResult.gl_pathc;
733 /* do shell variable substitution */
734 if(*prog->argv[argc_l - 1] == '$') {
735 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
736 prog->argv[argc_l - 1] = var;
738 #ifdef BB_FEATURE_SH_ENVIRONMENT
740 switch(*(prog->argv[argc_l - 1] + 1)) {
742 prog->argv[argc_l - 1] = itoa(lastReturnCode);
745 prog->argv[argc_l - 1] = itoa(getpid());
748 prog->argv[argc_l - 1] = itoa(argc-1);
752 *(prog->argv[argc_l - 1])='\0';
754 prog->argv[argc_l - 1] = itoa(lastBgPid);
756 case '0':case '1':case '2':case '3':case '4':
757 case '5':case '6':case '7':case '8':case '9':
759 int index=*(prog->argv[argc_l - 1] + 1)-48;
761 *(prog->argv[argc_l - 1])='\0';
763 prog->argv[argc_l - 1] = argv[index];
772 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
773 if (rc == GLOB_NOSPACE) {
774 errorMsg("out of space during glob operation\n");
776 } else if (rc == GLOB_NOMATCH ||
777 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
778 strcmp(prog->argv[argc_l - 1],
779 prog->globResult.gl_pathv[i]) == 0)) {
780 /* we need to remove whatever \ quoting is still present */
781 src = dst = prog->argv[argc_l - 1];
789 argcAlloced += (prog->globResult.gl_pathc - i);
791 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
792 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
793 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
794 argc_l += (prog->globResult.gl_pathc - i - 1);
797 *argcAllocedPtr = argcAlloced;
801 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
802 line). If a valid command is found, commandPtr is set to point to
803 the beginning of the next command (if the original command had more
804 then one job associated with it) or NULL if no more commands are
806 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
809 char *returnCommand = NULL;
810 char *src, *buf, *chptr;
817 struct childProgram *prog;
819 /* skip leading white space */
820 while (**commandPtr && isspace(**commandPtr))
823 /* this handles empty lines or leading '#' characters */
824 if (!**commandPtr || (**commandPtr == '#')) {
831 job->progs = xmalloc(sizeof(*job->progs));
833 /* We set the argv elements to point inside of this string. The
834 memory is freed by freeJob(). Allocate twice the original
835 length in case we need to quote every single character.
837 Getting clean memory relieves us of the task of NULL
838 terminating things and makes the rest of this look a bit
839 cleaner (though it is, admittedly, a tad less efficient) */
840 job->cmdBuf = command = calloc(2*strlen(*commandPtr) + 1, sizeof(char));
844 prog->numRedirections = 0;
845 prog->redirections = NULL;
850 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
851 prog->argv[0] = job->cmdBuf;
855 while (*src && !done) {
862 errorMsg("character expected after \\\n");
867 /* in shell, "\'" should yield \' */
870 } else if (*src == '*' || *src == '?' || *src == '[' ||
871 *src == ']') *buf++ = '\\';
873 } else if (isspace(*src)) {
874 if (*prog->argv[argc_l]) {
876 /* +1 here leaves room for the NULL which ends argv */
877 if ((argc_l + 1) == argvAlloced) {
879 prog->argv = realloc(prog->argv,
880 sizeof(*prog->argv) *
883 globLastArgument(prog, &argc_l, &argvAlloced);
884 prog->argv[argc_l] = buf;
893 case '#': /* comment */
900 case '>': /* redirections */
902 i = prog->numRedirections++;
903 prog->redirections = realloc(prog->redirections,
904 sizeof(*prog->redirections) *
907 prog->redirections[i].fd = -1;
908 if (buf != prog->argv[argc_l]) {
909 /* the stuff before this character may be the file number
911 prog->redirections[i].fd =
912 strtol(prog->argv[argc_l], &chptr, 10);
914 if (*chptr && *prog->argv[argc_l]) {
916 globLastArgument(prog, &argc_l, &argvAlloced);
917 prog->argv[argc_l] = buf;
921 if (prog->redirections[i].fd == -1) {
923 prog->redirections[i].fd = 1;
925 prog->redirections[i].fd = 0;
930 prog->redirections[i].type =
931 REDIRECT_APPEND, src++;
933 prog->redirections[i].type = REDIRECT_OVERWRITE;
935 prog->redirections[i].type = REDIRECT_INPUT;
938 /* This isn't POSIX sh compliant. Oh well. */
940 while (isspace(*chptr))
944 errorMsg("file name expected after %c\n", *src);
950 prog->redirections[i].filename = buf;
951 while (*chptr && !isspace(*chptr))
954 src = chptr - 1; /* we src++ later */
955 prog->argv[argc_l] = ++buf;
959 /* finish this command */
960 if (*prog->argv[argc_l])
963 errorMsg("empty command in pipe\n");
968 prog->argv[argc_l] = NULL;
970 /* and start the next */
972 job->progs = realloc(job->progs,
973 sizeof(*job->progs) * job->numProgs);
974 prog = job->progs + (job->numProgs - 1);
975 prog->numRedirections = 0;
976 prog->redirections = NULL;
982 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
983 prog->argv[0] = ++buf;
986 while (*src && isspace(*src))
990 errorMsg("empty command in pipe\n");
995 src--; /* we'll ++ it at the end of the loop */
999 case '&': /* background */
1001 case ';': /* multiple commands */
1003 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1009 errorMsg("character expected after \\\n");
1013 if (*src == '*' || *src == '[' || *src == ']'
1014 || *src == '?') *buf++ = '\\';
1016 #ifdef BB_FEATURE_SH_BACKTICKS
1018 /* Exec a backtick-ed command */
1020 char* charptr1=NULL, *charptr2;
1023 struct jobSet njobList = { NULL, NULL };
1027 ptr=strchr(++src, '`');
1029 fprintf(stderr, "Unmatched '`' in command\n");
1034 /* Make some space to hold just the backticked command */
1035 charptr1 = charptr2 = xmalloc(1+ptr-src);
1036 snprintf(charptr1, 1+ptr-src, src);
1037 newJob = xmalloc(sizeof(struct job));
1038 /* Now parse and run the backticked command */
1039 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
1040 && newJob->numProgs) {
1042 runCommand(newJob, &njobList, 0, pipefd);
1048 /* Make a copy of any stuff left over in the command
1049 * line after the second backtick */
1050 charptr2 = xmalloc(strlen(ptr)+1);
1051 memcpy(charptr2, ptr+1, strlen(ptr));
1054 /* Copy the output from the backtick-ed command into the
1055 * command line, making extra room as needed */
1057 charptr1 = xmalloc(BUFSIZ);
1058 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1059 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1060 if (newSize > BUFSIZ) {
1061 *commandPtr=realloc(*commandPtr, src - *commandPtr +
1062 size + 1 + strlen(charptr2));
1064 memcpy(src, charptr1, size);
1072 /* Now paste into the *commandPtr all the stuff
1073 * leftover after the second backtick */
1074 memcpy(src, charptr2, strlen(charptr2));
1077 /* Now recursively call parseCommand to deal with the new
1078 * and improved version of the command line with the backtick
1079 * results expanded in place... */
1081 return(parseCommand(commandPtr, job, jobList, inBg));
1084 #endif // BB_FEATURE_SH_BACKTICKS
1092 if (*prog->argv[argc_l]) {
1094 globLastArgument(prog, &argc_l, &argvAlloced);
1100 prog->argv[argc_l] = NULL;
1102 if (!returnCommand) {
1103 job->text = xmalloc(strlen(*commandPtr) + 1);
1104 strcpy(job->text, *commandPtr);
1106 /* This leaves any trailing spaces, which is a bit sloppy */
1107 count = returnCommand - *commandPtr;
1108 job->text = xmalloc(count + 1);
1109 strncpy(job->text, *commandPtr, count);
1110 job->text[count] = '\0';
1113 *commandPtr = returnCommand;
1118 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1122 int nextin, nextout;
1123 int pipefds[2]; /* pipefd[0] is for reading */
1124 struct builtInCommand *x;
1125 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1126 const struct BB_applet *a = applets;
1130 nextin = 0, nextout = 1;
1131 for (i = 0; i < newJob->numProgs; i++) {
1132 if ((i + 1) < newJob->numProgs) {
1134 nextout = pipefds[1];
1136 if (outPipe[1]!=-1) {
1137 nextout = outPipe[1];
1143 #ifdef BB_FEATURE_SH_ENVIRONMENT
1144 if (showXtrace==TRUE) {
1146 fprintf(stderr, "+ ");
1147 for (j = 0; newJob->progs[i].argv[j]; j++)
1148 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1149 fprintf(stderr, "\n");
1153 /* Check if the command matches any non-forking builtins */
1154 for (x = bltins; x->cmd; x++) {
1155 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1156 return(x->function(newJob, jobList));
1160 if (!(newJob->progs[i].pid = fork())) {
1161 signal(SIGTTOU, SIG_DFL);
1163 if (outPipe[1]!=-1) {
1178 /* explicit redirections override pipes */
1179 setupRedirections(newJob->progs + i);
1181 /* Check if the command matches any of the other builtins */
1182 for (x = bltins_forking; x->cmd; x++) {
1183 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1185 exit (x->function(newJob, jobList));
1188 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1189 /* Check if the command matches any busybox internal commands here */
1190 while (a->name != 0) {
1191 if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) {
1193 char** argv=newJob->progs[i].argv;
1194 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1195 applet_name=a->name;
1197 exit((*(a->main)) (argc_l, newJob->progs[i].argv));
1203 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1204 fatalError("%s: %s\n", newJob->progs[i].argv[0],
1207 if (outPipe[1]!=-1) {
1211 /* put our child in the process group whose leader is the
1212 first process in this pipe */
1213 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1219 /* If there isn't another process, nextin is garbage
1220 but it doesn't matter */
1221 nextin = pipefds[0];
1224 newJob->pgrp = newJob->progs[0].pid;
1226 /* find the ID for the theJob to use */
1228 for (theJob = jobList->head; theJob; theJob = theJob->next)
1229 if (theJob->jobId >= newJob->jobId)
1230 newJob->jobId = theJob->jobId + 1;
1232 /* add the theJob to the list of running jobs */
1233 if (!jobList->head) {
1234 theJob = jobList->head = malloc(sizeof(*theJob));
1236 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1237 theJob->next = malloc(sizeof(*theJob));
1238 theJob = theJob->next;
1242 theJob->next = NULL;
1243 theJob->runningProgs = theJob->numProgs;
1244 theJob->stoppedProgs = 0;
1247 /* we don't wait for background theJobs to return -- append it
1248 to the list of backgrounded theJobs and leave it alone */
1249 printf("[%d] %d\n", theJob->jobId,
1250 newJob->progs[newJob->numProgs - 1].pid);
1251 #ifdef BB_FEATURE_SH_ENVIRONMENT
1252 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1255 jobList->fg = theJob;
1257 /* move the new process group into the foreground */
1258 /* suppress messages when run from /linuxrc mag@sysgo.de */
1259 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1260 perror("tcsetpgrp");
1266 static int busy_loop(FILE * input)
1269 char *nextCommand = NULL;
1275 newJob.jobContext = REGULAR_JOB_CONTEXT;
1277 /* save current owner of TTY so we can restore it on exit */
1278 parent_pgrp = tcgetpgrp(0);
1280 command = (char *) calloc(BUFSIZ, sizeof(char));
1282 /* don't pay any attention to this signal; it just confuses
1283 things and isn't really meant for shells anyway */
1284 signal(SIGTTOU, SIG_IGN);
1288 /* no job is in the foreground */
1290 /* see if any background processes have exited */
1291 checkJobs(&jobList);
1294 if (getCommand(input, command))
1296 nextCommand = command;
1299 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1301 int pipefds[2] = {-1,-1};
1302 runCommand(&newJob, &jobList, inBg, pipefds);
1306 command = (char *) calloc(BUFSIZ, sizeof(char));
1310 /* a job is running in the foreground; wait for it */
1312 while (!jobList.fg->progs[i].pid ||
1313 jobList.fg->progs[i].isStopped == 1) i++;
1315 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1317 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1318 /* the child exited */
1319 jobList.fg->runningProgs--;
1320 jobList.fg->progs[i].pid = 0;
1322 #ifdef BB_FEATURE_SH_ENVIRONMENT
1323 lastReturnCode=WEXITSTATUS(status);
1326 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1328 if (!jobList.fg->runningProgs) {
1331 removeJob(&jobList, jobList.fg);
1335 /* the child was stopped */
1336 jobList.fg->stoppedProgs++;
1337 jobList.fg->progs[i].isStopped = 1;
1339 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1340 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1341 "Stopped", jobList.fg->text);
1347 /* move the shell to the foreground */
1348 /* suppress messages when run from /linuxrc mag@sysgo.de */
1349 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1350 perror("tcsetpgrp");
1356 /* return controlling TTY back to parent process group before exiting */
1357 if (tcsetpgrp(0, parent_pgrp))
1358 perror("tcsetpgrp");
1360 /* return exit status if called with "-c" */
1361 if (input == NULL && WIFEXITED(status))
1362 return WEXITSTATUS(status);
1368 #ifdef BB_FEATURE_CLEAN_UP
1369 void free_memory(void)
1375 if (local_pending_command)
1376 free(local_pending_command);
1378 if (jobList.fg && !jobList.fg->runningProgs) {
1379 removeJob(&jobList, jobList.fg);
1385 int shell_main(int argc_l, char **argv_l)
1387 int opt, interactive=FALSE;
1388 FILE *input = stdin;
1393 //if (argv[0] && argv[0][0] == '-') {
1394 // builtin_source("/etc/profile");
1397 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
1401 if (local_pending_command != 0)
1402 fatalError("multiple -c arguments\n");
1403 local_pending_command = xstrdup(argv[optind]);
1407 #ifdef BB_FEATURE_SH_ENVIRONMENT
1419 /* A shell is interactive if the `-i' flag was given, or if all of
1420 * the following conditions are met:
1422 * no arguments remaining or the -s flag given
1423 * standard input is a terminal
1424 * standard output is a terminal
1425 * Refer to Posix.2, the description of the `sh' utility. */
1426 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1427 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1428 /* Looks like they want an interactive shell */
1429 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1430 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1431 } else if (local_pending_command==NULL) {
1432 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1433 input = fopen(argv[optind], "r");
1435 fatalError("%s: %s\n", argv[optind], strerror(errno));
1439 /* initialize the cwd -- this is never freed...*/
1440 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1441 getcwd(cwd, sizeof(char)*MAX_LINE);
1443 #ifdef BB_FEATURE_CLEAN_UP
1444 atexit(free_memory);
1447 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1451 return (busy_loop(input));