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>
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"
53 extern size_t NUM_APPLETS;
58 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
62 static const unsigned int REGULAR_JOB_CONTEXT=0x1;
63 static const unsigned int IF_TRUE_CONTEXT=0x2;
64 static const unsigned int IF_FALSE_CONTEXT=0x4;
65 static const unsigned int THEN_EXP_CONTEXT=0x8;
66 static const unsigned int ELSE_EXP_CONTEXT=0x10;
70 struct job *head; /* head of list of running jobs */
71 struct job *fg; /* current foreground job */
74 struct redirectionSpecifier {
75 enum redirectionType type; /* type of redirection */
76 int fd; /* file descriptor being redirected */
77 char *filename; /* file to redirect fd to */
81 pid_t pid; /* 0 if exited */
82 char **argv; /* program name and arguments */
83 int numRedirections; /* elements in redirection array */
84 struct redirectionSpecifier *redirections; /* I/O redirections */
85 glob_t globResult; /* result of parameter globbing */
86 int freeGlob; /* should we globfree(&globResult)? */
87 int isStopped; /* is the program currently running? */
91 int jobId; /* job number */
92 int numProgs; /* total number of programs in job */
93 int runningProgs; /* number of programs running */
94 char *text; /* name of job */
95 char *cmdBuf; /* buffer various argv's point into */
96 pid_t pgrp; /* process group ID for the job */
97 struct childProgram *progs; /* array of programs in job */
98 struct job *next; /* to track background commands */
99 int stoppedProgs; /* number of programs alive, but stopped */
100 int jobContext; /* bitmask defining current context */
103 struct builtInCommand {
104 char *cmd; /* name */
105 char *descr; /* description */
106 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
109 /* function prototypes for builtins */
110 static int builtin_cd(struct job *cmd, struct jobSet *junk);
111 static int builtin_env(struct job *dummy, struct jobSet *junk);
112 static int builtin_exec(struct job *cmd, struct jobSet *junk);
113 static int builtin_exit(struct job *cmd, struct jobSet *junk);
114 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
115 static int builtin_help(struct job *cmd, struct jobSet *junk);
116 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
117 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
118 static int builtin_export(struct job *cmd, struct jobSet *junk);
119 static int builtin_source(struct job *cmd, struct jobSet *jobList);
120 static int builtin_unset(struct job *cmd, struct jobSet *junk);
121 static int builtin_read(struct job *cmd, struct jobSet *junk);
122 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
123 static int builtin_if(struct job *cmd, struct jobSet *junk);
124 static int builtin_then(struct job *cmd, struct jobSet *junk);
125 static int builtin_else(struct job *cmd, struct jobSet *junk);
126 static int builtin_fi(struct job *cmd, struct jobSet *junk);
130 /* function prototypes for shell stuff */
131 static void checkJobs(struct jobSet *jobList);
132 static int getCommand(FILE * source, char *command);
133 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
134 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
135 static int busy_loop(FILE * input);
138 /* Table of built-in functions (these are non-forking builtins, meaning they
139 * can change global variables in the parent shell process but they will not
140 * work with pipes and redirects; 'unset foo | whatever' will not work) */
141 static struct builtInCommand bltins[] = {
142 {"bg", "Resume a job in the background", builtin_fg_bg},
143 {"cd", "Change working directory", builtin_cd},
144 {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
145 {"exit", "Exit from shell()", builtin_exit},
146 {"fg", "Bring job into the foreground", builtin_fg_bg},
147 {"jobs", "Lists the active jobs", builtin_jobs},
148 {"export", "Set environment variable", builtin_export},
149 {"unset", "Unset environment variable", builtin_unset},
150 {"read", "Input environment variable", builtin_read},
151 {".", "Source-in and run commands in a file", builtin_source},
152 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
153 {"if", NULL, builtin_if},
154 {"then", NULL, builtin_then},
155 {"else", NULL, builtin_else},
156 {"fi", NULL, builtin_fi},
161 /* Table of forking built-in functions (things that fork cannot change global
162 * variables in the parent process, such as the current working directory) */
163 static struct builtInCommand bltins_forking[] = {
164 {"env", "Print all environment variables", builtin_env},
165 {"pwd", "Print current directory", builtin_pwd},
166 {"help", "List shell built-in commands", builtin_help},
170 static char prompt[3];
172 static char *local_pending_command = NULL;
173 static char *promptStr = NULL;
174 static struct jobSet jobList = { NULL, NULL };
177 #ifdef BB_FEATURE_SH_ENVIRONMENT
178 static int lastBgPid=-1;
179 static int lastReturnCode=-1;
180 static int showXtrace=FALSE;
184 static inline void debug_printf(const char *format, ...)
187 va_start(args, format);
188 vfprintf(stderr, s, p);
192 static inline void debug_printf(const char *format, ...) { }
195 #ifdef BB_FEATURE_SH_COMMAND_EDITING
196 static inline void win_changed(int junk)
198 struct winsize win = { 0, 0, 0, 0 };
199 ioctl(0, TIOCGWINSZ, &win);
200 if (win.ws_col > 0) {
201 cmdedit_setwidth( win.ws_col - 1);
205 static inline void win_changed(int junk) {}
209 /* built-in 'cd <path>' handler */
210 static int builtin_cd(struct job *cmd, struct jobSet *junk)
214 if (!cmd->progs[0].argv[1] == 1)
215 newdir = getenv("HOME");
217 newdir = cmd->progs[0].argv[1];
219 printf("cd: %s: %s\n", newdir, strerror(errno));
222 getcwd(cwd, sizeof(char)*MAX_LINE);
227 /* built-in 'env' handler */
228 static int builtin_env(struct job *dummy, struct jobSet *junk)
232 for (e = environ; *e; e++) {
233 fprintf(stdout, "%s\n", *e);
238 /* built-in 'exec' handler */
239 static int builtin_exec(struct job *cmd, struct jobSet *junk)
241 if (cmd->progs[0].argv[1])
243 cmd->progs[0].argv++;
244 execvp(cmd->progs[0].argv[0], cmd->progs[0].argv);
245 fatalError("Exec to %s failed: %s\n", cmd->progs[0].argv[0],
251 /* built-in 'exit' handler */
252 static int builtin_exit(struct job *cmd, struct jobSet *junk)
254 if (!cmd->progs[0].argv[1] == 1)
257 exit (atoi(cmd->progs[0].argv[1]));
260 /* built-in 'fg' and 'bg' handler */
261 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
264 struct job *job=NULL;
266 if (!jobList->head) {
267 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
268 errorMsg("%s: exactly one argument is expected\n",
269 cmd->progs[0].argv[0]);
272 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
273 errorMsg("%s: bad argument '%s'\n",
274 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
276 for (job = jobList->head; job; job = job->next) {
277 if (job->jobId == jobNum) {
287 errorMsg("%s: unknown job %d\n",
288 cmd->progs[0].argv[0], jobNum);
292 if (*cmd->progs[0].argv[0] == 'f') {
293 /* Make this job the foreground job */
294 /* suppress messages when run from /linuxrc mag@sysgo.de */
295 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
300 /* Restart the processes in the job */
301 for (i = 0; i < job->numProgs; i++)
302 job->progs[i].isStopped = 0;
304 kill(-job->pgrp, SIGCONT);
306 job->stoppedProgs = 0;
311 /* built-in 'help' handler */
312 static int builtin_help(struct job *dummy, struct jobSet *junk)
314 struct builtInCommand *x;
316 fprintf(stdout, "\nBuilt-in commands:\n");
317 fprintf(stdout, "-------------------\n");
318 for (x = bltins; x->cmd; x++) {
321 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
323 for (x = bltins_forking; x->cmd; x++) {
326 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
328 fprintf(stdout, "\n\n");
332 /* built-in 'jobs' handler */
333 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
338 for (job = jobList->head; job; job = job->next) {
339 if (job->runningProgs == job->stoppedProgs)
340 statusString = "Stopped";
342 statusString = "Running";
344 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
350 /* built-in 'pwd' handler */
351 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
353 getcwd(cwd, sizeof(char)*MAX_LINE);
354 fprintf(stdout, "%s\n", cwd);
358 /* built-in 'export VAR=value' handler */
359 static int builtin_export(struct job *cmd, struct jobSet *junk)
363 if (!cmd->progs[0].argv[1] == 1) {
364 return (builtin_env(cmd, junk));
366 res = putenv(cmd->progs[0].argv[1]);
368 fprintf(stdout, "export: %s\n", strerror(errno));
372 /* built-in 'read VAR' handler */
373 static int builtin_read(struct job *cmd, struct jobSet *junk)
375 int res = 0, len, newlen;
377 char string[MAX_READ];
379 if (cmd->progs[0].argv[1]) {
380 /* argument (VAR) given: put "VAR=" into buffer */
381 strcpy(string, cmd->progs[0].argv[1]);
382 len = strlen(string);
385 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
386 newlen = strlen(string);
388 string[--newlen] = '\0'; /* chomp trailing newline */
390 ** string should now contain "VAR=<value>"
391 ** copy it (putenv() won't do that, so we must make sure
392 ** the string resides in a static buffer!)
395 if((s = strdup(string)))
398 fprintf(stdout, "read: %s\n", strerror(errno));
401 fgets(string, sizeof(string), stdin);
406 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
407 /* Built-in handler for 'if' commands */
408 static int builtin_if(struct job *cmd, struct jobSet *jobList)
411 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
413 /* Now run the 'if' command */
414 status=strlen(charptr1);
415 local_pending_command = xmalloc(status+1);
416 strncpy(local_pending_command, charptr1, status);
417 local_pending_command[status]='\0';
418 debug_printf(stderr, "'if' now testing '%s'\n", local_pending_command);
419 status = busy_loop(NULL); /* Frees local_pending_command */
420 debug_printf(stderr, "if test returned ");
422 debug_printf(stderr, "TRUE\n");
423 cmd->jobContext |= IF_TRUE_CONTEXT;
425 debug_printf(stderr, "FALSE\n");
426 cmd->jobContext |= IF_FALSE_CONTEXT;
432 /* Built-in handler for 'then' (part of the 'if' command) */
433 static int builtin_then(struct job *cmd, struct jobSet *junk)
436 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
438 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
439 errorMsg("unexpected token `then'\n");
442 /* If the if result was FALSE, skip the 'then' stuff */
443 if (cmd->jobContext & IF_FALSE_CONTEXT) {
447 cmd->jobContext |= THEN_EXP_CONTEXT;
448 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
450 /* Now run the 'then' command */
451 status=strlen(charptr1);
452 local_pending_command = xmalloc(status+1);
453 strncpy(local_pending_command, charptr1, status);
454 local_pending_command[status]='\0';
455 debug_printf(stderr, "'then' now running '%s'\n", charptr1);
456 return( busy_loop(NULL));
459 /* Built-in handler for 'else' (part of the 'if' command) */
460 static int builtin_else(struct job *cmd, struct jobSet *junk)
463 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
465 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
466 errorMsg("unexpected token `else'\n");
469 /* If the if result was TRUE, skip the 'else' stuff */
470 if (cmd->jobContext & IF_TRUE_CONTEXT) {
474 cmd->jobContext |= ELSE_EXP_CONTEXT;
475 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
477 /* Now run the 'else' command */
478 status=strlen(charptr1);
479 local_pending_command = xmalloc(status+1);
480 strncpy(local_pending_command, charptr1, status);
481 local_pending_command[status]='\0';
482 debug_printf(stderr, "'else' now running '%s'\n", charptr1);
483 return( busy_loop(NULL));
486 /* Built-in handler for 'fi' (part of the 'if' command) */
487 static int builtin_fi(struct job *cmd, struct jobSet *junk)
489 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
490 errorMsg("unexpected token `fi'\n");
493 /* Clear out the if and then context bits */
494 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
495 debug_printf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext);
500 /* Built-in '.' handler (read-in and execute commands from file) */
501 static int builtin_source(struct job *cmd, struct jobSet *junk)
506 if (!cmd->progs[0].argv[1] == 1)
509 input = fopen(cmd->progs[0].argv[1], "r");
511 fprintf(stdout, "Couldn't open file '%s'\n",
512 cmd->progs[0].argv[1]);
516 /* Now run the file */
517 status = busy_loop(input);
522 /* built-in 'unset VAR' handler */
523 static int builtin_unset(struct job *cmd, struct jobSet *junk)
525 if (!cmd->progs[0].argv[1] == 1) {
526 fprintf(stdout, "unset: parameter required.\n");
529 unsetenv(cmd->progs[0].argv[1]);
533 /* free up all memory from a job */
534 static void freeJob(struct job *cmd)
538 for (i = 0; i < cmd->numProgs; i++) {
539 free(cmd->progs[i].argv);
540 if (cmd->progs[i].redirections)
541 free(cmd->progs[i].redirections);
542 if (cmd->progs[i].freeGlob)
543 globfree(&cmd->progs[i].globResult);
549 memset(cmd, 0, sizeof(struct job));
552 /* remove a job from the jobList */
553 static void removeJob(struct jobSet *jobList, struct job *job)
558 if (job == jobList->head) {
559 jobList->head = job->next;
561 prevJob = jobList->head;
562 while (prevJob->next != job)
563 prevJob = prevJob->next;
564 prevJob->next = job->next;
570 /* Checks to see if any background processes have exited -- if they
571 have, figure out why and see if a job has completed */
572 static void checkJobs(struct jobSet *jobList)
579 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
580 for (job = jobList->head; job; job = job->next) {
582 while (progNum < job->numProgs &&
583 job->progs[progNum].pid != childpid) progNum++;
584 if (progNum < job->numProgs)
588 /* This happens on backticked commands */
592 if (WIFEXITED(status) || WIFSIGNALED(status)) {
595 job->progs[progNum].pid = 0;
597 if (!job->runningProgs) {
598 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
599 removeJob(jobList, job);
604 job->progs[progNum].isStopped = 1;
606 if (job->stoppedProgs == job->numProgs) {
607 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
613 if (childpid == -1 && errno != ECHILD)
617 static int setupRedirections(struct childProgram *prog)
622 struct redirectionSpecifier *redir = prog->redirections;
624 for (i = 0; i < prog->numRedirections; i++, redir++) {
625 switch (redir->type) {
629 case REDIRECT_OVERWRITE:
630 mode = O_WRONLY | O_CREAT | O_TRUNC;
632 case REDIRECT_APPEND:
633 mode = O_WRONLY | O_CREAT | O_APPEND;
637 openfd = open(redir->filename, mode, 0666);
639 /* this could get lost if stderr has been redirected, but
640 bash and ash both lose it as well (though zsh doesn't!) */
641 errorMsg("error opening %s: %s\n", redir->filename,
646 if (openfd != redir->fd) {
647 dup2(openfd, redir->fd);
656 static int getCommand(FILE * source, char *command)
658 char *user,buf[255],*s;
660 if (source == NULL) {
661 if (local_pending_command) {
662 /* a command specified (-c option): return it & mark it done */
663 strcpy(command, local_pending_command);
664 free(local_pending_command);
665 local_pending_command = NULL;
671 /* get User Name and setup prompt */
672 strcpy(prompt,( geteuid() != 0 ) ? "$ ":"# ");
673 user=xcalloc(sizeof(int), 9);
674 my_getpwuid(user, geteuid());
677 gethostname(buf, 255);
678 s = strchr(buf, '.');
682 if (source == stdin) {
683 #ifdef BB_FEATURE_SH_COMMAND_EDITING
687 ** enable command line editing only while a command line
688 ** is actually being read; otherwise, we'll end up bequeathing
689 ** atexit() handlers and other unwanted stuff to our
690 ** child processes (rob@sysgo.de)
693 signal(SIGWINCH, win_changed);
694 len=fprintf(stdout, "[%s@%s %s]%s", user, buf,
695 get_last_path_component(cwd), prompt);
697 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
698 sprintf(promptStr, "[%s@%s %s]%s", user, buf,
699 get_last_path_component(cwd), prompt);
700 cmdedit_read_input(promptStr, command);
703 signal(SIGWINCH, SIG_DFL);
709 while ((i>0) && (*(cwd+i)!='/') ) i--;
710 if (*(cwd+i)=='/') i++;
713 fprintf(stdout, "[%s@%s %s]%s",user, buf, (cwd+i), prompt);
718 /* don't leak memory */
721 if (!fgets(command, BUFSIZ - 2, source)) {
727 /* remove trailing newline */
728 command[strlen(command) - 1] = '\0';
733 #ifdef BB_FEATURE_SH_ENVIRONMENT
734 static char* itoa(register int i)
736 static char a[7]; /* Max 7 ints */
737 register char *b = a + sizeof(a) - 1;
745 *--b = '0' + (i % 10);
755 static void globLastArgument(struct childProgram *prog, int *argcPtr,
758 int argc_l = *argcPtr;
759 int argcAlloced = *argcAllocedPtr;
763 char *src, *dst, *var;
765 if (argc_l > 1) { /* cmd->globResult is already initialized */
767 i = prog->globResult.gl_pathc;
773 /* do shell variable substitution */
774 if(*prog->argv[argc_l - 1] == '$') {
775 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
776 prog->argv[argc_l - 1] = var;
778 #ifdef BB_FEATURE_SH_ENVIRONMENT
780 switch(*(prog->argv[argc_l - 1] + 1)) {
782 prog->argv[argc_l - 1] = itoa(lastReturnCode);
785 prog->argv[argc_l - 1] = itoa(getpid());
788 prog->argv[argc_l - 1] = itoa(argc-1);
792 *(prog->argv[argc_l - 1])='\0';
794 prog->argv[argc_l - 1] = itoa(lastBgPid);
796 case '0':case '1':case '2':case '3':case '4':
797 case '5':case '6':case '7':case '8':case '9':
799 int index=*(prog->argv[argc_l - 1] + 1)-48;
801 *(prog->argv[argc_l - 1])='\0';
803 prog->argv[argc_l - 1] = argv[index];
812 if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
813 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
814 if (rc == GLOB_NOSPACE) {
815 errorMsg("out of space during glob operation\n");
817 } else if (rc == GLOB_NOMATCH ||
818 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
819 strcmp(prog->argv[argc_l - 1],
820 prog->globResult.gl_pathv[i]) == 0)) {
821 /* we need to remove whatever \ quoting is still present */
822 src = dst = prog->argv[argc_l - 1];
826 *dst++ = process_escape_sequence(&src);
834 argcAlloced += (prog->globResult.gl_pathc - i);
835 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
836 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
837 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
838 argc_l += (prog->globResult.gl_pathc - i - 1);
841 src = dst = prog->argv[argc_l - 1];
845 *dst++ = process_escape_sequence(&src);
853 prog->globResult.gl_pathc=0;
855 prog->globResult.gl_pathv=NULL;
857 *argcAllocedPtr = argcAlloced;
861 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
862 line). If a valid command is found, commandPtr is set to point to
863 the beginning of the next command (if the original command had more
864 then one job associated with it) or NULL if no more commands are
866 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
869 char *returnCommand = NULL;
870 char *src, *buf, *chptr;
877 struct childProgram *prog;
879 /* skip leading white space */
880 while (**commandPtr && isspace(**commandPtr))
883 /* this handles empty lines or leading '#' characters */
884 if (!**commandPtr || (**commandPtr == '#')) {
891 job->progs = xmalloc(sizeof(*job->progs));
893 /* We set the argv elements to point inside of this string. The
894 memory is freed by freeJob(). Allocate twice the original
895 length in case we need to quote every single character.
897 Getting clean memory relieves us of the task of NULL
898 terminating things and makes the rest of this look a bit
899 cleaner (though it is, admittedly, a tad less efficient) */
900 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
904 prog->numRedirections = 0;
905 prog->redirections = NULL;
910 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
911 prog->argv[0] = job->cmdBuf;
915 while (*src && !done) {
922 errorMsg("character expected after \\\n");
927 /* in shell, "\'" should yield \' */
930 } else if (*src == '*' || *src == '?' || *src == '[' ||
931 *src == ']') *buf++ = '\\';
933 } else if (isspace(*src)) {
934 if (*prog->argv[argc_l]) {
936 /* +1 here leaves room for the NULL which ends argv */
937 if ((argc_l + 1) == argvAlloced) {
939 prog->argv = xrealloc(prog->argv,
940 sizeof(*prog->argv) *
943 globLastArgument(prog, &argc_l, &argvAlloced);
944 prog->argv[argc_l] = buf;
953 case '#': /* comment */
960 case '>': /* redirections */
962 i = prog->numRedirections++;
963 prog->redirections = xrealloc(prog->redirections,
964 sizeof(*prog->redirections) *
967 prog->redirections[i].fd = -1;
968 if (buf != prog->argv[argc_l]) {
969 /* the stuff before this character may be the file number
971 prog->redirections[i].fd =
972 strtol(prog->argv[argc_l], &chptr, 10);
974 if (*chptr && *prog->argv[argc_l]) {
976 globLastArgument(prog, &argc_l, &argvAlloced);
977 prog->argv[argc_l] = buf;
981 if (prog->redirections[i].fd == -1) {
983 prog->redirections[i].fd = 1;
985 prog->redirections[i].fd = 0;
990 prog->redirections[i].type =
991 REDIRECT_APPEND, src++;
993 prog->redirections[i].type = REDIRECT_OVERWRITE;
995 prog->redirections[i].type = REDIRECT_INPUT;
998 /* This isn't POSIX sh compliant. Oh well. */
1000 while (isspace(*chptr))
1004 errorMsg("file name expected after %c\n", *src);
1010 prog->redirections[i].filename = buf;
1011 while (*chptr && !isspace(*chptr))
1014 src = chptr - 1; /* we src++ later */
1015 prog->argv[argc_l] = ++buf;
1018 case '|': /* pipe */
1019 /* finish this command */
1020 if (*prog->argv[argc_l])
1023 errorMsg("empty command in pipe\n");
1028 prog->argv[argc_l] = NULL;
1030 /* and start the next */
1032 job->progs = xrealloc(job->progs,
1033 sizeof(*job->progs) * job->numProgs);
1034 prog = job->progs + (job->numProgs - 1);
1035 prog->numRedirections = 0;
1036 prog->redirections = NULL;
1038 prog->isStopped = 0;
1042 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
1043 prog->argv[0] = ++buf;
1046 while (*src && isspace(*src))
1050 errorMsg("empty command in pipe\n");
1055 src--; /* we'll ++ it at the end of the loop */
1059 case '&': /* background */
1061 case ';': /* multiple commands */
1063 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1066 #ifdef BB_FEATURE_SH_BACKTICKS
1068 /* Exec a backtick-ed command */
1070 char* charptr1=NULL, *charptr2;
1073 struct jobSet njobList = { NULL, NULL };
1077 ptr=strchr(++src, '`');
1079 fprintf(stderr, "Unmatched '`' in command\n");
1084 /* Make some space to hold just the backticked command */
1085 charptr1 = charptr2 = xmalloc(1+ptr-src);
1086 memcpy(charptr1, src, ptr-src);
1087 charptr1[ptr-src] = '\0';
1088 newJob = xmalloc(sizeof(struct job));
1089 /* Now parse and run the backticked command */
1090 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
1091 && newJob->numProgs) {
1093 runCommand(newJob, &njobList, 0, pipefd);
1099 /* Make a copy of any stuff left over in the command
1100 * line after the second backtick */
1101 charptr2 = xmalloc(strlen(ptr)+1);
1102 memcpy(charptr2, ptr+1, strlen(ptr));
1105 /* Copy the output from the backtick-ed command into the
1106 * command line, making extra room as needed */
1108 charptr1 = xmalloc(BUFSIZ);
1109 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1110 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1111 if (newSize > BUFSIZ) {
1112 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
1113 size + 1 + strlen(charptr2));
1115 memcpy(src, charptr1, size);
1123 /* Now paste into the *commandPtr all the stuff
1124 * leftover after the second backtick */
1125 memcpy(src, charptr2, strlen(charptr2)+1);
1128 /* Now recursively call parseCommand to deal with the new
1129 * and improved version of the command line with the backtick
1130 * results expanded in place... */
1132 return(parseCommand(commandPtr, job, jobList, inBg));
1135 #endif // BB_FEATURE_SH_BACKTICKS
1140 errorMsg("character expected after \\\n");
1144 if (*src == '*' || *src == '[' || *src == ']'
1145 || *src == '?') *buf++ = '\\';
1154 if (*prog->argv[argc_l]) {
1156 globLastArgument(prog, &argc_l, &argvAlloced);
1162 prog->argv[argc_l] = NULL;
1164 if (!returnCommand) {
1165 job->text = xmalloc(strlen(*commandPtr) + 1);
1166 strcpy(job->text, *commandPtr);
1168 /* This leaves any trailing spaces, which is a bit sloppy */
1169 count = returnCommand - *commandPtr;
1170 job->text = xmalloc(count + 1);
1171 strncpy(job->text, *commandPtr, count);
1172 job->text[count] = '\0';
1175 *commandPtr = returnCommand;
1180 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1184 int nextin, nextout;
1185 int pipefds[2]; /* pipefd[0] is for reading */
1186 struct builtInCommand *x;
1187 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1188 struct BB_applet search_applet, *applet;
1191 nextin = 0, nextout = 1;
1192 for (i = 0; i < newJob->numProgs; i++) {
1193 if ((i + 1) < newJob->numProgs) {
1195 nextout = pipefds[1];
1197 if (outPipe[1]!=-1) {
1198 nextout = outPipe[1];
1204 #ifdef BB_FEATURE_SH_ENVIRONMENT
1205 if (showXtrace==TRUE) {
1207 fprintf(stderr, "+ ");
1208 for (j = 0; newJob->progs[i].argv[j]; j++)
1209 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1210 fprintf(stderr, "\n");
1214 /* Check if the command matches any non-forking builtins */
1215 for (x = bltins; x->cmd; x++) {
1216 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1217 return(x->function(newJob, jobList));
1221 if (!(newJob->progs[i].pid = fork())) {
1222 signal(SIGTTOU, SIG_DFL);
1224 if (outPipe[1]!=-1) {
1239 /* explicit redirections override pipes */
1240 setupRedirections(newJob->progs + i);
1242 /* Check if the command matches any of the other builtins */
1243 for (x = bltins_forking; x->cmd; x++) {
1244 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1246 exit (x->function(newJob, jobList));
1249 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1250 /* Check if the command matches any busybox internal
1251 * commands ("applets") here. Following discussions from
1252 * November 2000 on busybox@opensource.lineo.com, don't use
1253 * get_last_path_component(). This way explicit (with
1254 * slashes) filenames will never be interpreted as an
1255 * applet, just like with builtins. This way the user can
1256 * override an applet with an explicit filename reference.
1257 * The only downside to this change is that an explicit
1258 * /bin/foo invocation fill fork and exec /bin/foo, even if
1259 * /bin/foo is a symlink to busybox.
1261 search_applet.name = newJob->progs[i].argv[0];
1263 #ifdef BB_FEATURE_SH_BUILTINS_ALWAYS_WIN
1264 /* If you enable BB_FEATURE_SH_BUILTINS_ALWAYS_WIN, then
1265 * if you run /bin/cat, it will use BusyBox cat even if
1266 * /bin/cat exists on the filesystem and is _not_ busybox.
1267 * Some systems want this, others do not. Choose wisely. :-)
1269 search_applet.name = get_last_path_component(search_applet.name);
1272 /* Do a binary search to find the applet entry given the name. */
1273 applet = bsearch(&search_applet, applets, NUM_APPLETS,
1274 sizeof(struct BB_applet), applet_name_compare);
1275 if (applet != NULL) {
1277 char** argv=newJob->progs[i].argv;
1278 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1279 applet_name=applet->name;
1281 exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
1285 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1286 fatalError("%s: %s\n", newJob->progs[i].argv[0],
1289 if (outPipe[1]!=-1) {
1293 /* put our child in the process group whose leader is the
1294 first process in this pipe */
1295 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1301 /* If there isn't another process, nextin is garbage
1302 but it doesn't matter */
1303 nextin = pipefds[0];
1306 newJob->pgrp = newJob->progs[0].pid;
1308 /* find the ID for the theJob to use */
1310 for (theJob = jobList->head; theJob; theJob = theJob->next)
1311 if (theJob->jobId >= newJob->jobId)
1312 newJob->jobId = theJob->jobId + 1;
1314 /* add the theJob to the list of running jobs */
1315 if (!jobList->head) {
1316 theJob = jobList->head = xmalloc(sizeof(*theJob));
1318 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1319 theJob->next = xmalloc(sizeof(*theJob));
1320 theJob = theJob->next;
1324 theJob->next = NULL;
1325 theJob->runningProgs = theJob->numProgs;
1326 theJob->stoppedProgs = 0;
1329 /* we don't wait for background theJobs to return -- append it
1330 to the list of backgrounded theJobs and leave it alone */
1331 printf("[%d] %d\n", theJob->jobId,
1332 newJob->progs[newJob->numProgs - 1].pid);
1333 #ifdef BB_FEATURE_SH_ENVIRONMENT
1334 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1337 jobList->fg = theJob;
1339 /* move the new process group into the foreground */
1340 /* suppress messages when run from /linuxrc mag@sysgo.de */
1341 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1342 perror("tcsetpgrp");
1348 static int busy_loop(FILE * input)
1351 char *nextCommand = NULL;
1357 newJob.jobContext = REGULAR_JOB_CONTEXT;
1359 /* save current owner of TTY so we can restore it on exit */
1360 parent_pgrp = tcgetpgrp(0);
1362 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1364 /* don't pay any attention to this signal; it just confuses
1365 things and isn't really meant for shells anyway */
1366 signal(SIGTTOU, SIG_IGN);
1370 /* no job is in the foreground */
1372 /* see if any background processes have exited */
1373 checkJobs(&jobList);
1376 if (getCommand(input, command))
1378 nextCommand = command;
1381 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1383 int pipefds[2] = {-1,-1};
1384 runCommand(&newJob, &jobList, inBg, pipefds);
1388 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1392 /* a job is running in the foreground; wait for it */
1394 while (!jobList.fg->progs[i].pid ||
1395 jobList.fg->progs[i].isStopped == 1) i++;
1397 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1399 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1400 /* the child exited */
1401 jobList.fg->runningProgs--;
1402 jobList.fg->progs[i].pid = 0;
1404 #ifdef BB_FEATURE_SH_ENVIRONMENT
1405 lastReturnCode=WEXITSTATUS(status);
1407 debug_printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1408 if (!jobList.fg->runningProgs) {
1411 removeJob(&jobList, jobList.fg);
1415 /* the child was stopped */
1416 jobList.fg->stoppedProgs++;
1417 jobList.fg->progs[i].isStopped = 1;
1419 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1420 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1421 "Stopped", jobList.fg->text);
1427 /* move the shell to the foreground */
1428 /* suppress messages when run from /linuxrc mag@sysgo.de */
1429 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1430 perror("tcsetpgrp");
1436 /* return controlling TTY back to parent process group before exiting */
1437 if (tcsetpgrp(0, parent_pgrp))
1438 perror("tcsetpgrp");
1440 /* return exit status if called with "-c" */
1441 if (input == NULL && WIFEXITED(status))
1442 return WEXITSTATUS(status);
1448 #ifdef BB_FEATURE_CLEAN_UP
1449 void free_memory(void)
1455 if (local_pending_command)
1456 free(local_pending_command);
1458 if (jobList.fg && !jobList.fg->runningProgs) {
1459 removeJob(&jobList, jobList.fg);
1465 int shell_main(int argc_l, char **argv_l)
1467 int opt, interactive=FALSE;
1468 FILE *input = stdin;
1473 if (argv[0] && argv[0][0] == '-') {
1475 input = fopen("/etc/profile", "r");
1477 fprintf(stdout, "Couldn't open file '/etc/profile'\n");
1479 /* Now run the file */
1485 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
1489 if (local_pending_command != 0)
1490 fatalError("multiple -c arguments\n");
1491 local_pending_command = xstrdup(argv[optind]);
1495 #ifdef BB_FEATURE_SH_ENVIRONMENT
1507 /* A shell is interactive if the `-i' flag was given, or if all of
1508 * the following conditions are met:
1510 * no arguments remaining or the -s flag given
1511 * standard input is a terminal
1512 * standard output is a terminal
1513 * Refer to Posix.2, the description of the `sh' utility. */
1514 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1515 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1516 /* Looks like they want an interactive shell */
1517 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1518 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1519 } else if (local_pending_command==NULL) {
1520 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1521 input = xfopen(argv[optind], "r");
1524 /* initialize the cwd -- this is never freed...*/
1525 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1526 getcwd(cwd, sizeof(char)*MAX_LINE);
1528 #ifdef BB_FEATURE_CLEAN_UP
1529 atexit(free_memory);
1533 return (busy_loop(input));