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 {".", "Source-in and run commands in a file", builtin_source},
149 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
150 {"if", NULL, builtin_if},
151 {"then", NULL, builtin_then},
152 {"else", NULL, builtin_else},
153 {"fi", NULL, builtin_fi},
158 /* Table of forking built-in functions (things that fork cannot change global
159 * variables in the parent process, such as the current working directory) */
160 static struct builtInCommand bltins_forking[] = {
161 {"env", "Print all environment variables", builtin_env},
162 {"pwd", "Print current directory", builtin_pwd},
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 exit (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);
507 /* built-in 'unset VAR' handler */
508 static int builtin_unset(struct job *cmd, struct jobSet *junk)
510 if (!cmd->progs[0].argv[1] == 1) {
511 fprintf(stdout, "unset: parameter required.\n");
514 unsetenv(cmd->progs[0].argv[1]);
518 /* free up all memory from a job */
519 static void freeJob(struct job *cmd)
523 for (i = 0; i < cmd->numProgs; i++) {
524 free(cmd->progs[i].argv);
525 if (cmd->progs[i].redirections)
526 free(cmd->progs[i].redirections);
527 if (cmd->progs[i].freeGlob)
528 globfree(&cmd->progs[i].globResult);
534 memset(cmd, 0, sizeof(struct job));
537 /* remove a job from the jobList */
538 static void removeJob(struct jobSet *jobList, struct job *job)
543 if (job == jobList->head) {
544 jobList->head = job->next;
546 prevJob = jobList->head;
547 while (prevJob->next != job)
548 prevJob = prevJob->next;
549 prevJob->next = job->next;
555 /* Checks to see if any background processes have exited -- if they
556 have, figure out why and see if a job has completed */
557 static void checkJobs(struct jobSet *jobList)
564 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
565 for (job = jobList->head; job; job = job->next) {
567 while (progNum < job->numProgs &&
568 job->progs[progNum].pid != childpid) progNum++;
569 if (progNum < job->numProgs)
573 /* This happens on backticked commands */
577 if (WIFEXITED(status) || WIFSIGNALED(status)) {
580 job->progs[progNum].pid = 0;
582 if (!job->runningProgs) {
583 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
584 removeJob(jobList, job);
589 job->progs[progNum].isStopped = 1;
591 if (job->stoppedProgs == job->numProgs) {
592 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
598 if (childpid == -1 && errno != ECHILD)
602 static int setupRedirections(struct childProgram *prog)
607 struct redirectionSpecifier *redir = prog->redirections;
609 for (i = 0; i < prog->numRedirections; i++, redir++) {
610 switch (redir->type) {
614 case REDIRECT_OVERWRITE:
615 mode = O_RDWR | O_CREAT | O_TRUNC;
617 case REDIRECT_APPEND:
618 mode = O_RDWR | O_CREAT | O_APPEND;
622 openfd = open(redir->filename, mode, 0666);
624 /* this could get lost if stderr has been redirected, but
625 bash and ash both lose it as well (though zsh doesn't!) */
626 errorMsg("error opening %s: %s\n", redir->filename,
631 if (openfd != redir->fd) {
632 dup2(openfd, redir->fd);
641 static int getCommand(FILE * source, char *command)
643 if (source == NULL) {
644 if (local_pending_command) {
645 /* a command specified (-c option): return it & mark it done */
646 strcpy(command, local_pending_command);
647 free(local_pending_command);
648 local_pending_command = NULL;
654 if (source == stdin) {
655 #ifdef BB_FEATURE_SH_COMMAND_EDITING
659 ** enable command line editing only while a command line
660 ** is actually being read; otherwise, we'll end up bequeathing
661 ** atexit() handlers and other unwanted stuff to our
662 ** child processes (rob@sysgo.de)
665 signal(SIGWINCH, win_changed);
666 len=fprintf(stdout, "%s %s", cwd, prompt);
668 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
669 sprintf(promptStr, "%s %s", cwd, prompt);
670 cmdedit_read_input(promptStr, command);
673 signal(SIGWINCH, SIG_DFL);
676 fprintf(stdout, "%s %s", cwd, prompt);
681 if (!fgets(command, BUFSIZ - 2, source)) {
687 /* remove trailing newline */
688 command[strlen(command) - 1] = '\0';
693 #ifdef BB_FEATURE_SH_ENVIRONMENT
694 #define __MAX_INT_CHARS 7
695 static char* itoa(register int i)
697 static char a[__MAX_INT_CHARS];
698 register char *b = a + sizeof(a) - 1;
706 *--b = '0' + (i % 10);
716 static void globLastArgument(struct childProgram *prog, int *argcPtr,
719 int argc_l = *argcPtr;
720 int argcAlloced = *argcAllocedPtr;
724 char *src, *dst, *var;
726 if (argc_l > 1) { /* cmd->globResult is already initialized */
728 i = prog->globResult.gl_pathc;
734 /* do shell variable substitution */
735 if(*prog->argv[argc_l - 1] == '$') {
736 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
737 prog->argv[argc_l - 1] = var;
739 #ifdef BB_FEATURE_SH_ENVIRONMENT
741 switch(*(prog->argv[argc_l - 1] + 1)) {
743 prog->argv[argc_l - 1] = itoa(lastReturnCode);
746 prog->argv[argc_l - 1] = itoa(getpid());
749 prog->argv[argc_l - 1] = itoa(argc-1);
753 *(prog->argv[argc_l - 1])='\0';
755 prog->argv[argc_l - 1] = itoa(lastBgPid);
757 case '0':case '1':case '2':case '3':case '4':
758 case '5':case '6':case '7':case '8':case '9':
760 int index=*(prog->argv[argc_l - 1] + 1)-48;
762 *(prog->argv[argc_l - 1])='\0';
764 prog->argv[argc_l - 1] = argv[index];
773 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
774 if (rc == GLOB_NOSPACE) {
775 errorMsg("out of space during glob operation\n");
777 } else if (rc == GLOB_NOMATCH ||
778 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
779 strcmp(prog->argv[argc_l - 1],
780 prog->globResult.gl_pathv[i]) == 0)) {
781 /* we need to remove whatever \ quoting is still present */
782 src = dst = prog->argv[argc_l - 1];
790 argcAlloced += (prog->globResult.gl_pathc - i);
791 prog->argv = xrealloc(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 = xcalloc(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 = xrealloc(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 = xrealloc(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 = xrealloc(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;
1006 #ifdef BB_FEATURE_SH_BACKTICKS
1008 /* Exec a backtick-ed command */
1010 char* charptr1=NULL, *charptr2;
1013 struct jobSet njobList = { NULL, NULL };
1017 ptr=strchr(++src, '`');
1019 fprintf(stderr, "Unmatched '`' in command\n");
1024 /* Make some space to hold just the backticked command */
1025 charptr1 = charptr2 = xmalloc(1+ptr-src);
1026 memcpy(charptr1, src, ptr-src);
1027 charptr1[ptr-src] = '\0';
1028 newJob = xmalloc(sizeof(struct job));
1029 /* Now parse and run the backticked command */
1030 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
1031 && newJob->numProgs) {
1033 runCommand(newJob, &njobList, 0, pipefd);
1039 /* Make a copy of any stuff left over in the command
1040 * line after the second backtick */
1041 charptr2 = xmalloc(strlen(ptr)+1);
1042 memcpy(charptr2, ptr+1, strlen(ptr));
1045 /* Copy the output from the backtick-ed command into the
1046 * command line, making extra room as needed */
1048 charptr1 = xmalloc(BUFSIZ);
1049 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1050 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1051 if (newSize > BUFSIZ) {
1052 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
1053 size + 1 + strlen(charptr2));
1055 memcpy(src, charptr1, size);
1063 /* Now paste into the *commandPtr all the stuff
1064 * leftover after the second backtick */
1065 memcpy(src, charptr2, strlen(charptr2)+1);
1068 /* Now recursively call parseCommand to deal with the new
1069 * and improved version of the command line with the backtick
1070 * results expanded in place... */
1072 return(parseCommand(commandPtr, job, jobList, inBg));
1075 #endif // BB_FEATURE_SH_BACKTICKS
1080 errorMsg("character expected after \\\n");
1084 if (*src == '*' || *src == '[' || *src == ']'
1085 || *src == '?') *buf++ = '\\';
1094 if (*prog->argv[argc_l]) {
1096 globLastArgument(prog, &argc_l, &argvAlloced);
1102 prog->argv[argc_l] = NULL;
1104 if (!returnCommand) {
1105 job->text = xmalloc(strlen(*commandPtr) + 1);
1106 strcpy(job->text, *commandPtr);
1108 /* This leaves any trailing spaces, which is a bit sloppy */
1109 count = returnCommand - *commandPtr;
1110 job->text = xmalloc(count + 1);
1111 strncpy(job->text, *commandPtr, count);
1112 job->text[count] = '\0';
1115 *commandPtr = returnCommand;
1120 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1124 int nextin, nextout;
1125 int pipefds[2]; /* pipefd[0] is for reading */
1126 struct builtInCommand *x;
1127 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1128 const struct BB_applet *a = applets;
1132 nextin = 0, nextout = 1;
1133 for (i = 0; i < newJob->numProgs; i++) {
1134 if ((i + 1) < newJob->numProgs) {
1136 nextout = pipefds[1];
1138 if (outPipe[1]!=-1) {
1139 nextout = outPipe[1];
1145 #ifdef BB_FEATURE_SH_ENVIRONMENT
1146 if (showXtrace==TRUE) {
1148 fprintf(stderr, "+ ");
1149 for (j = 0; newJob->progs[i].argv[j]; j++)
1150 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1151 fprintf(stderr, "\n");
1155 /* Check if the command matches any non-forking builtins */
1156 for (x = bltins; x->cmd; x++) {
1157 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1158 return(x->function(newJob, jobList));
1162 if (!(newJob->progs[i].pid = fork())) {
1163 signal(SIGTTOU, SIG_DFL);
1165 if (outPipe[1]!=-1) {
1180 /* explicit redirections override pipes */
1181 setupRedirections(newJob->progs + i);
1183 /* Check if the command matches any of the other builtins */
1184 for (x = bltins_forking; x->cmd; x++) {
1185 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1187 exit (x->function(newJob, jobList));
1190 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1191 /* Check if the command matches any busybox internal commands here */
1192 while (a->name != 0) {
1193 if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) {
1195 char** argv=newJob->progs[i].argv;
1196 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1197 applet_name=a->name;
1199 exit((*(a->main)) (argc_l, newJob->progs[i].argv));
1205 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1206 fatalError("%s: %s\n", newJob->progs[i].argv[0],
1209 if (outPipe[1]!=-1) {
1213 /* put our child in the process group whose leader is the
1214 first process in this pipe */
1215 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1221 /* If there isn't another process, nextin is garbage
1222 but it doesn't matter */
1223 nextin = pipefds[0];
1226 newJob->pgrp = newJob->progs[0].pid;
1228 /* find the ID for the theJob to use */
1230 for (theJob = jobList->head; theJob; theJob = theJob->next)
1231 if (theJob->jobId >= newJob->jobId)
1232 newJob->jobId = theJob->jobId + 1;
1234 /* add the theJob to the list of running jobs */
1235 if (!jobList->head) {
1236 theJob = jobList->head = xmalloc(sizeof(*theJob));
1238 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1239 theJob->next = xmalloc(sizeof(*theJob));
1240 theJob = theJob->next;
1244 theJob->next = NULL;
1245 theJob->runningProgs = theJob->numProgs;
1246 theJob->stoppedProgs = 0;
1249 /* we don't wait for background theJobs to return -- append it
1250 to the list of backgrounded theJobs and leave it alone */
1251 printf("[%d] %d\n", theJob->jobId,
1252 newJob->progs[newJob->numProgs - 1].pid);
1253 #ifdef BB_FEATURE_SH_ENVIRONMENT
1254 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1257 jobList->fg = theJob;
1259 /* move the new process group into the foreground */
1260 /* suppress messages when run from /linuxrc mag@sysgo.de */
1261 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1262 perror("tcsetpgrp");
1268 static int busy_loop(FILE * input)
1271 char *nextCommand = NULL;
1277 newJob.jobContext = REGULAR_JOB_CONTEXT;
1279 /* save current owner of TTY so we can restore it on exit */
1280 parent_pgrp = tcgetpgrp(0);
1282 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1284 /* don't pay any attention to this signal; it just confuses
1285 things and isn't really meant for shells anyway */
1286 signal(SIGTTOU, SIG_IGN);
1290 /* no job is in the foreground */
1292 /* see if any background processes have exited */
1293 checkJobs(&jobList);
1296 if (getCommand(input, command))
1298 nextCommand = command;
1301 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1303 int pipefds[2] = {-1,-1};
1304 runCommand(&newJob, &jobList, inBg, pipefds);
1308 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1312 /* a job is running in the foreground; wait for it */
1314 while (!jobList.fg->progs[i].pid ||
1315 jobList.fg->progs[i].isStopped == 1) i++;
1317 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1319 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1320 /* the child exited */
1321 jobList.fg->runningProgs--;
1322 jobList.fg->progs[i].pid = 0;
1324 #ifdef BB_FEATURE_SH_ENVIRONMENT
1325 lastReturnCode=WEXITSTATUS(status);
1328 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1330 if (!jobList.fg->runningProgs) {
1333 removeJob(&jobList, jobList.fg);
1337 /* the child was stopped */
1338 jobList.fg->stoppedProgs++;
1339 jobList.fg->progs[i].isStopped = 1;
1341 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1342 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1343 "Stopped", jobList.fg->text);
1349 /* move the shell to the foreground */
1350 /* suppress messages when run from /linuxrc mag@sysgo.de */
1351 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1352 perror("tcsetpgrp");
1358 /* return controlling TTY back to parent process group before exiting */
1359 if (tcsetpgrp(0, parent_pgrp))
1360 perror("tcsetpgrp");
1362 /* return exit status if called with "-c" */
1363 if (input == NULL && WIFEXITED(status))
1364 return WEXITSTATUS(status);
1370 #ifdef BB_FEATURE_CLEAN_UP
1371 void free_memory(void)
1377 if (local_pending_command)
1378 free(local_pending_command);
1380 if (jobList.fg && !jobList.fg->runningProgs) {
1381 removeJob(&jobList, jobList.fg);
1387 int shell_main(int argc_l, char **argv_l)
1389 int opt, interactive=FALSE;
1390 FILE *input = stdin;
1395 //if (argv[0] && argv[0][0] == '-') {
1396 // builtin_source("/etc/profile");
1399 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
1403 if (local_pending_command != 0)
1404 fatalError("multiple -c arguments\n");
1405 local_pending_command = xstrdup(argv[optind]);
1409 #ifdef BB_FEATURE_SH_ENVIRONMENT
1421 /* A shell is interactive if the `-i' flag was given, or if all of
1422 * the following conditions are met:
1424 * no arguments remaining or the -s flag given
1425 * standard input is a terminal
1426 * standard output is a terminal
1427 * Refer to Posix.2, the description of the `sh' utility. */
1428 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1429 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1430 /* Looks like they want an interactive shell */
1431 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1432 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1433 } else if (local_pending_command==NULL) {
1434 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1435 input = fopen(argv[optind], "r");
1437 fatalError("%s: %s\n", argv[optind], strerror(errno));
1441 /* initialize the cwd -- this is never freed...*/
1442 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1443 getcwd(cwd, sizeof(char)*MAX_LINE);
1445 #ifdef BB_FEATURE_CLEAN_UP
1446 atexit(free_memory);
1449 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1453 return (busy_loop(input));