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 //This works pretty well now, and is not on by default.
30 #define BB_FEATURE_SH_ENVIRONMENT
32 //Backtick support has some problems, use at your own risk!
33 //#define BB_FEATURE_SH_BACKTICKS
35 //If, then, else, etc. support is really, really broken. Don't even
36 //bother to mess with this yet, since you will not be happy with it.
37 //#define BB_FEATURE_SH_IF_EXPRESSIONS
39 //For debugging/development on the shell only...
52 #include <sys/ioctl.h>
58 #define MAX_LINE 256 /* size of input buffer for `read' builtin */
59 #define MAX_READ 128 /* size of input buffer for `read' builtin */
60 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
61 extern size_t NUM_APPLETS;
66 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
70 static const unsigned int REGULAR_JOB_CONTEXT=0x1;
71 static const unsigned int IF_TRUE_CONTEXT=0x2;
72 static const unsigned int IF_FALSE_CONTEXT=0x4;
73 static const unsigned int THEN_EXP_CONTEXT=0x8;
74 static const unsigned int ELSE_EXP_CONTEXT=0x10;
78 struct job *head; /* head of list of running jobs */
79 struct job *fg; /* current foreground job */
82 struct redirectionSpecifier {
83 enum redirectionType type; /* type of redirection */
84 int fd; /* file descriptor being redirected */
85 char *filename; /* file to redirect fd to */
89 pid_t pid; /* 0 if exited */
90 char **argv; /* program name and arguments */
91 int numRedirections; /* elements in redirection array */
92 struct redirectionSpecifier *redirections; /* I/O redirections */
93 glob_t globResult; /* result of parameter globbing */
94 int freeGlob; /* should we globfree(&globResult)? */
95 int isStopped; /* is the program currently running? */
99 int jobId; /* job number */
100 int numProgs; /* total number of programs in job */
101 int runningProgs; /* number of programs running */
102 char *text; /* name of job */
103 char *cmdBuf; /* buffer various argv's point into */
104 pid_t pgrp; /* process group ID for the job */
105 struct childProgram *progs; /* array of programs in job */
106 struct job *next; /* to track background commands */
107 int stoppedProgs; /* number of programs alive, but stopped */
108 int jobContext; /* bitmask defining current context */
111 struct builtInCommand {
112 char *cmd; /* name */
113 char *descr; /* description */
114 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
117 /* function prototypes for builtins */
118 static int builtin_cd(struct job *cmd, struct jobSet *junk);
119 static int builtin_env(struct job *dummy, struct jobSet *junk);
120 static int builtin_exec(struct job *cmd, struct jobSet *junk);
121 static int builtin_exit(struct job *cmd, struct jobSet *junk);
122 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
123 static int builtin_help(struct job *cmd, struct jobSet *junk);
124 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
125 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
126 static int builtin_export(struct job *cmd, struct jobSet *junk);
127 static int builtin_source(struct job *cmd, struct jobSet *jobList);
128 static int builtin_unset(struct job *cmd, struct jobSet *junk);
129 static int builtin_read(struct job *cmd, struct jobSet *junk);
130 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
131 static int builtin_if(struct job *cmd, struct jobSet *junk);
132 static int builtin_then(struct job *cmd, struct jobSet *junk);
133 static int builtin_else(struct job *cmd, struct jobSet *junk);
134 static int builtin_fi(struct job *cmd, struct jobSet *junk);
138 /* function prototypes for shell stuff */
139 static void checkJobs(struct jobSet *jobList);
140 static int getCommand(FILE * source, char *command);
141 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
142 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
143 static int busy_loop(FILE * input);
146 /* Table of built-in functions (these are non-forking builtins, meaning they
147 * can change global variables in the parent shell process but they will not
148 * work with pipes and redirects; 'unset foo | whatever' will not work) */
149 static struct builtInCommand bltins[] = {
150 {"bg", "Resume a job in the background", builtin_fg_bg},
151 {"cd", "Change working directory", builtin_cd},
152 {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
153 {"exit", "Exit from shell()", builtin_exit},
154 {"fg", "Bring job into the foreground", builtin_fg_bg},
155 {"jobs", "Lists the active jobs", builtin_jobs},
156 {"export", "Set environment variable", builtin_export},
157 {"unset", "Unset environment variable", builtin_unset},
158 {"read", "Input environment variable", builtin_read},
159 {".", "Source-in and run commands in a file", builtin_source},
160 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
161 {"if", NULL, builtin_if},
162 {"then", NULL, builtin_then},
163 {"else", NULL, builtin_else},
164 {"fi", NULL, builtin_fi},
169 /* Table of forking built-in functions (things that fork cannot change global
170 * variables in the parent process, such as the current working directory) */
171 static struct builtInCommand bltins_forking[] = {
172 {"env", "Print all environment variables", builtin_env},
173 {"pwd", "Print current directory", builtin_pwd},
174 {"help", "List shell built-in commands", builtin_help},
178 static char prompt[3];
180 static char *local_pending_command = NULL;
181 static char *promptStr = NULL;
182 static struct jobSet jobList = { NULL, NULL };
185 #ifdef BB_FEATURE_SH_ENVIRONMENT
186 static int lastBgPid=-1;
187 static int lastReturnCode=-1;
188 static int showXtrace=FALSE;
192 static inline void debug_printf(const char *format, ...)
195 va_start(args, format);
196 vfprintf(stderr, s, p);
200 static inline void debug_printf(const char *format, ...) { }
203 #ifdef BB_FEATURE_SH_COMMAND_EDITING
204 static inline void win_changed(int junk)
206 struct winsize win = { 0, 0, 0, 0 };
207 ioctl(0, TIOCGWINSZ, &win);
208 if (win.ws_col > 0) {
209 cmdedit_setwidth( win.ws_col - 1);
213 static inline void win_changed(int junk) {}
217 /* built-in 'cd <path>' handler */
218 static int builtin_cd(struct job *cmd, struct jobSet *junk)
222 if (!cmd->progs[0].argv[1] == 1)
223 newdir = getenv("HOME");
225 newdir = cmd->progs[0].argv[1];
227 printf("cd: %s: %s\n", newdir, strerror(errno));
230 getcwd(cwd, sizeof(char)*MAX_LINE);
235 /* built-in 'env' handler */
236 static int builtin_env(struct job *dummy, struct jobSet *junk)
240 for (e = environ; *e; e++) {
241 fprintf(stdout, "%s\n", *e);
246 /* built-in 'exec' handler */
247 static int builtin_exec(struct job *cmd, struct jobSet *junk)
249 if (cmd->progs[0].argv[1])
251 cmd->progs[0].argv++;
252 execvp(cmd->progs[0].argv[0], cmd->progs[0].argv);
253 error_msg_and_die("Exec to %s failed: %s\n", cmd->progs[0].argv[0],
259 /* built-in 'exit' handler */
260 static int builtin_exit(struct job *cmd, struct jobSet *junk)
262 if (!cmd->progs[0].argv[1] == 1)
265 exit (atoi(cmd->progs[0].argv[1]));
268 /* built-in 'fg' and 'bg' handler */
269 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
272 struct job *job=NULL;
274 if (!jobList->head) {
275 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
276 error_msg("%s: exactly one argument is expected\n",
277 cmd->progs[0].argv[0]);
280 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
281 error_msg("%s: bad argument '%s'\n",
282 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
284 for (job = jobList->head; job; job = job->next) {
285 if (job->jobId == jobNum) {
295 error_msg("%s: unknown job %d\n",
296 cmd->progs[0].argv[0], jobNum);
300 if (*cmd->progs[0].argv[0] == 'f') {
301 /* Make this job the foreground job */
302 /* suppress messages when run from /linuxrc mag@sysgo.de */
303 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
308 /* Restart the processes in the job */
309 for (i = 0; i < job->numProgs; i++)
310 job->progs[i].isStopped = 0;
312 kill(-job->pgrp, SIGCONT);
314 job->stoppedProgs = 0;
319 /* built-in 'help' handler */
320 static int builtin_help(struct job *dummy, struct jobSet *junk)
322 struct builtInCommand *x;
324 fprintf(stdout, "\nBuilt-in commands:\n");
325 fprintf(stdout, "-------------------\n");
326 for (x = bltins; x->cmd; x++) {
329 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
331 for (x = bltins_forking; x->cmd; x++) {
334 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
336 fprintf(stdout, "\n\n");
340 /* built-in 'jobs' handler */
341 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
346 for (job = jobList->head; job; job = job->next) {
347 if (job->runningProgs == job->stoppedProgs)
348 statusString = "Stopped";
350 statusString = "Running";
352 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
358 /* built-in 'pwd' handler */
359 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
361 getcwd(cwd, sizeof(char)*MAX_LINE);
362 fprintf(stdout, "%s\n", cwd);
366 /* built-in 'export VAR=value' handler */
367 static int builtin_export(struct job *cmd, struct jobSet *junk)
371 if (!cmd->progs[0].argv[1] == 1) {
372 return (builtin_env(cmd, junk));
374 res = putenv(cmd->progs[0].argv[1]);
376 fprintf(stdout, "export: %s\n", strerror(errno));
380 /* built-in 'read VAR' handler */
381 static int builtin_read(struct job *cmd, struct jobSet *junk)
383 int res = 0, len, newlen;
385 char string[MAX_READ];
387 if (cmd->progs[0].argv[1]) {
388 /* argument (VAR) given: put "VAR=" into buffer */
389 strcpy(string, cmd->progs[0].argv[1]);
390 len = strlen(string);
393 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
394 newlen = strlen(string);
396 string[--newlen] = '\0'; /* chomp trailing newline */
398 ** string should now contain "VAR=<value>"
399 ** copy it (putenv() won't do that, so we must make sure
400 ** the string resides in a static buffer!)
403 if((s = strdup(string)))
406 fprintf(stdout, "read: %s\n", strerror(errno));
409 fgets(string, sizeof(string), stdin);
414 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
415 /* Built-in handler for 'if' commands */
416 static int builtin_if(struct job *cmd, struct jobSet *jobList)
419 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
421 /* Now run the 'if' command */
422 status=strlen(charptr1);
423 local_pending_command = xmalloc(status+1);
424 strncpy(local_pending_command, charptr1, status);
425 local_pending_command[status]='\0';
426 debug_printf(stderr, "'if' now testing '%s'\n", local_pending_command);
427 status = busy_loop(NULL); /* Frees local_pending_command */
428 debug_printf(stderr, "if test returned ");
430 debug_printf(stderr, "TRUE\n");
431 cmd->jobContext |= IF_TRUE_CONTEXT;
433 debug_printf(stderr, "FALSE\n");
434 cmd->jobContext |= IF_FALSE_CONTEXT;
440 /* Built-in handler for 'then' (part of the 'if' command) */
441 static int builtin_then(struct job *cmd, struct jobSet *junk)
444 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
446 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
447 error_msg("unexpected token `then'\n");
450 /* If the if result was FALSE, skip the 'then' stuff */
451 if (cmd->jobContext & IF_FALSE_CONTEXT) {
455 cmd->jobContext |= THEN_EXP_CONTEXT;
456 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
458 /* Now run the 'then' 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';
463 debug_printf(stderr, "'then' now running '%s'\n", charptr1);
464 return( busy_loop(NULL));
467 /* Built-in handler for 'else' (part of the 'if' command) */
468 static int builtin_else(struct job *cmd, struct jobSet *junk)
471 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
473 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
474 error_msg("unexpected token `else'\n");
477 /* If the if result was TRUE, skip the 'else' stuff */
478 if (cmd->jobContext & IF_TRUE_CONTEXT) {
482 cmd->jobContext |= ELSE_EXP_CONTEXT;
483 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
485 /* Now run the 'else' command */
486 status=strlen(charptr1);
487 local_pending_command = xmalloc(status+1);
488 strncpy(local_pending_command, charptr1, status);
489 local_pending_command[status]='\0';
490 debug_printf(stderr, "'else' now running '%s'\n", charptr1);
491 return( busy_loop(NULL));
494 /* Built-in handler for 'fi' (part of the 'if' command) */
495 static int builtin_fi(struct job *cmd, struct jobSet *junk)
497 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
498 error_msg("unexpected token `fi'\n");
501 /* Clear out the if and then context bits */
502 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
503 debug_printf(stderr, "Hit an fi -- jobContext=%d\n", cmd->jobContext);
508 /* Built-in '.' handler (read-in and execute commands from file) */
509 static int builtin_source(struct job *cmd, struct jobSet *junk)
514 if (!cmd->progs[0].argv[1] == 1)
517 input = fopen(cmd->progs[0].argv[1], "r");
519 fprintf(stdout, "Couldn't open file '%s'\n",
520 cmd->progs[0].argv[1]);
524 /* Now run the file */
525 status = busy_loop(input);
530 /* built-in 'unset VAR' handler */
531 static int builtin_unset(struct job *cmd, struct jobSet *junk)
533 if (!cmd->progs[0].argv[1] == 1) {
534 fprintf(stdout, "unset: parameter required.\n");
537 unsetenv(cmd->progs[0].argv[1]);
541 /* free up all memory from a job */
542 static void freeJob(struct job *cmd)
546 for (i = 0; i < cmd->numProgs; i++) {
547 free(cmd->progs[i].argv);
548 if (cmd->progs[i].redirections)
549 free(cmd->progs[i].redirections);
550 if (cmd->progs[i].freeGlob)
551 globfree(&cmd->progs[i].globResult);
557 memset(cmd, 0, sizeof(struct job));
560 /* remove a job from the jobList */
561 static void removeJob(struct jobSet *jobList, struct job *job)
566 if (job == jobList->head) {
567 jobList->head = job->next;
569 prevJob = jobList->head;
570 while (prevJob->next != job)
571 prevJob = prevJob->next;
572 prevJob->next = job->next;
578 /* Checks to see if any background processes have exited -- if they
579 have, figure out why and see if a job has completed */
580 static void checkJobs(struct jobSet *jobList)
587 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
588 for (job = jobList->head; job; job = job->next) {
590 while (progNum < job->numProgs &&
591 job->progs[progNum].pid != childpid) progNum++;
592 if (progNum < job->numProgs)
596 /* This happens on backticked commands */
600 if (WIFEXITED(status) || WIFSIGNALED(status)) {
603 job->progs[progNum].pid = 0;
605 if (!job->runningProgs) {
606 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
607 removeJob(jobList, job);
612 job->progs[progNum].isStopped = 1;
614 if (job->stoppedProgs == job->numProgs) {
615 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
621 if (childpid == -1 && errno != ECHILD)
625 static int setupRedirections(struct childProgram *prog)
630 struct redirectionSpecifier *redir = prog->redirections;
632 for (i = 0; i < prog->numRedirections; i++, redir++) {
633 switch (redir->type) {
637 case REDIRECT_OVERWRITE:
638 mode = O_WRONLY | O_CREAT | O_TRUNC;
640 case REDIRECT_APPEND:
641 mode = O_WRONLY | O_CREAT | O_APPEND;
645 openfd = open(redir->filename, mode, 0666);
647 /* this could get lost if stderr has been redirected, but
648 bash and ash both lose it as well (though zsh doesn't!) */
649 error_msg("error opening %s: %s\n", redir->filename,
654 if (openfd != redir->fd) {
655 dup2(openfd, redir->fd);
664 static int getCommand(FILE * source, char *command)
666 char *user,buf[255],*s;
668 if (source == NULL) {
669 if (local_pending_command) {
670 /* a command specified (-c option): return it & mark it done */
671 strcpy(command, local_pending_command);
672 free(local_pending_command);
673 local_pending_command = NULL;
679 /* get User Name and setup prompt */
680 strcpy(prompt,( geteuid() != 0 ) ? "$ ":"# ");
681 user=xcalloc(sizeof(int), 9);
682 my_getpwuid(user, geteuid());
685 gethostname(buf, 255);
686 s = strchr(buf, '.');
690 if (source == stdin) {
691 #ifdef BB_FEATURE_SH_COMMAND_EDITING
695 ** enable command line editing only while a command line
696 ** is actually being read; otherwise, we'll end up bequeathing
697 ** atexit() handlers and other unwanted stuff to our
698 ** child processes (rob@sysgo.de)
701 signal(SIGWINCH, win_changed);
702 len=fprintf(stdout, "[%s@%s %s]%s", user, buf,
703 get_last_path_component(cwd), prompt);
705 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
706 sprintf(promptStr, "[%s@%s %s]%s", user, buf,
707 get_last_path_component(cwd), prompt);
708 cmdedit_read_input(promptStr, command);
711 signal(SIGWINCH, SIG_DFL);
717 while ((i>0) && (*(cwd+i)!='/') ) i--;
718 if (*(cwd+i)=='/') i++;
721 fprintf(stdout, "[%s@%s %s]%s",user, buf, (cwd+i), prompt);
726 /* don't leak memory */
729 if (!fgets(command, BUFSIZ - 2, source)) {
735 /* remove trailing newline */
736 command[strlen(command) - 1] = '\0';
741 #ifdef BB_FEATURE_SH_ENVIRONMENT
742 static char* itoa(register int i)
744 static char a[7]; /* Max 7 ints */
745 register char *b = a + sizeof(a) - 1;
753 *--b = '0' + (i % 10);
763 static void globLastArgument(struct childProgram *prog, int *argcPtr,
766 int argc_l = *argcPtr;
767 int argcAlloced = *argcAllocedPtr;
771 char *src, *dst, *var;
773 if (argc_l > 1) { /* cmd->globResult is already initialized */
775 i = prog->globResult.gl_pathc;
781 /* do shell variable substitution */
782 if(*prog->argv[argc_l - 1] == '$') {
783 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
784 prog->argv[argc_l - 1] = var;
786 #ifdef BB_FEATURE_SH_ENVIRONMENT
788 switch(*(prog->argv[argc_l - 1] + 1)) {
790 prog->argv[argc_l - 1] = itoa(lastReturnCode);
793 prog->argv[argc_l - 1] = itoa(getpid());
796 prog->argv[argc_l - 1] = itoa(argc-1);
800 *(prog->argv[argc_l - 1])='\0';
802 prog->argv[argc_l - 1] = itoa(lastBgPid);
804 case '0':case '1':case '2':case '3':case '4':
805 case '5':case '6':case '7':case '8':case '9':
807 int index=*(prog->argv[argc_l - 1] + 1)-48;
809 *(prog->argv[argc_l - 1])='\0';
811 prog->argv[argc_l - 1] = argv[index];
820 if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
821 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
822 if (rc == GLOB_NOSPACE) {
823 error_msg("out of space during glob operation\n");
825 } else if (rc == GLOB_NOMATCH ||
826 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
827 strcmp(prog->argv[argc_l - 1],
828 prog->globResult.gl_pathv[i]) == 0)) {
829 /* we need to remove whatever \ quoting is still present */
830 src = dst = prog->argv[argc_l - 1];
834 *dst++ = process_escape_sequence(&src);
842 argcAlloced += (prog->globResult.gl_pathc - i);
843 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
844 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
845 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
846 argc_l += (prog->globResult.gl_pathc - i - 1);
849 src = dst = prog->argv[argc_l - 1];
853 *dst++ = process_escape_sequence(&src);
861 prog->globResult.gl_pathc=0;
863 prog->globResult.gl_pathv=NULL;
865 *argcAllocedPtr = argcAlloced;
869 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
870 line). If a valid command is found, commandPtr is set to point to
871 the beginning of the next command (if the original command had more
872 then one job associated with it) or NULL if no more commands are
874 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
877 char *returnCommand = NULL;
878 char *src, *buf, *chptr;
885 struct childProgram *prog;
887 /* skip leading white space */
888 while (**commandPtr && isspace(**commandPtr))
891 /* this handles empty lines or leading '#' characters */
892 if (!**commandPtr || (**commandPtr == '#')) {
899 job->progs = xmalloc(sizeof(*job->progs));
901 /* We set the argv elements to point inside of this string. The
902 memory is freed by freeJob(). Allocate twice the original
903 length in case we need to quote every single character.
905 Getting clean memory relieves us of the task of NULL
906 terminating things and makes the rest of this look a bit
907 cleaner (though it is, admittedly, a tad less efficient) */
908 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
912 prog->numRedirections = 0;
913 prog->redirections = NULL;
918 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
919 prog->argv[0] = job->cmdBuf;
923 while (*src && !done) {
930 error_msg("character expected after \\\n");
935 /* in shell, "\'" should yield \' */
938 } else if (*src == '*' || *src == '?' || *src == '[' ||
939 *src == ']') *buf++ = '\\';
941 } else if (isspace(*src)) {
942 if (*prog->argv[argc_l]) {
944 /* +1 here leaves room for the NULL which ends argv */
945 if ((argc_l + 1) == argvAlloced) {
947 prog->argv = xrealloc(prog->argv,
948 sizeof(*prog->argv) *
951 globLastArgument(prog, &argc_l, &argvAlloced);
952 prog->argv[argc_l] = buf;
961 case '#': /* comment */
968 case '>': /* redirections */
970 i = prog->numRedirections++;
971 prog->redirections = xrealloc(prog->redirections,
972 sizeof(*prog->redirections) *
975 prog->redirections[i].fd = -1;
976 if (buf != prog->argv[argc_l]) {
977 /* the stuff before this character may be the file number
979 prog->redirections[i].fd =
980 strtol(prog->argv[argc_l], &chptr, 10);
982 if (*chptr && *prog->argv[argc_l]) {
984 globLastArgument(prog, &argc_l, &argvAlloced);
985 prog->argv[argc_l] = buf;
989 if (prog->redirections[i].fd == -1) {
991 prog->redirections[i].fd = 1;
993 prog->redirections[i].fd = 0;
998 prog->redirections[i].type =
999 REDIRECT_APPEND, src++;
1001 prog->redirections[i].type = REDIRECT_OVERWRITE;
1003 prog->redirections[i].type = REDIRECT_INPUT;
1006 /* This isn't POSIX sh compliant. Oh well. */
1008 while (isspace(*chptr))
1012 error_msg("file name expected after %c\n", *src);
1018 prog->redirections[i].filename = buf;
1019 while (*chptr && !isspace(*chptr))
1022 src = chptr - 1; /* we src++ later */
1023 prog->argv[argc_l] = ++buf;
1026 case '|': /* pipe */
1027 /* finish this command */
1028 if (*prog->argv[argc_l])
1031 error_msg("empty command in pipe\n");
1036 prog->argv[argc_l] = NULL;
1038 /* and start the next */
1040 job->progs = xrealloc(job->progs,
1041 sizeof(*job->progs) * job->numProgs);
1042 prog = job->progs + (job->numProgs - 1);
1043 prog->numRedirections = 0;
1044 prog->redirections = NULL;
1046 prog->isStopped = 0;
1050 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
1051 prog->argv[0] = ++buf;
1054 while (*src && isspace(*src))
1058 error_msg("empty command in pipe\n");
1063 src--; /* we'll ++ it at the end of the loop */
1067 case '&': /* background */
1069 case ';': /* multiple commands */
1071 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1074 #ifdef BB_FEATURE_SH_BACKTICKS
1076 /* Exec a backtick-ed command */
1078 char* charptr1=NULL, *charptr2;
1081 struct jobSet njobList = { NULL, NULL };
1085 ptr=strchr(++src, '`');
1087 fprintf(stderr, "Unmatched '`' in command\n");
1092 /* Make some space to hold just the backticked command */
1093 charptr1 = charptr2 = xmalloc(1+ptr-src);
1094 memcpy(charptr1, src, ptr-src);
1095 charptr1[ptr-src] = '\0';
1096 newJob = xmalloc(sizeof(struct job));
1097 /* Now parse and run the backticked command */
1098 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
1099 && newJob->numProgs) {
1101 runCommand(newJob, &njobList, 0, pipefd);
1107 /* Make a copy of any stuff left over in the command
1108 * line after the second backtick */
1109 charptr2 = xmalloc(strlen(ptr)+1);
1110 memcpy(charptr2, ptr+1, strlen(ptr));
1113 /* Copy the output from the backtick-ed command into the
1114 * command line, making extra room as needed */
1116 charptr1 = xmalloc(BUFSIZ);
1117 while ( (size=full_read(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1118 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1119 if (newSize > BUFSIZ) {
1120 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
1121 size + 1 + strlen(charptr2));
1123 memcpy(src, charptr1, size);
1131 /* Now paste into the *commandPtr all the stuff
1132 * leftover after the second backtick */
1133 memcpy(src, charptr2, strlen(charptr2)+1);
1136 /* Now recursively call parseCommand to deal with the new
1137 * and improved version of the command line with the backtick
1138 * results expanded in place... */
1140 return(parseCommand(commandPtr, job, jobList, inBg));
1143 #endif // BB_FEATURE_SH_BACKTICKS
1148 error_msg("character expected after \\\n");
1152 if (*src == '*' || *src == '[' || *src == ']'
1153 || *src == '?') *buf++ = '\\';
1162 if (*prog->argv[argc_l]) {
1164 globLastArgument(prog, &argc_l, &argvAlloced);
1170 prog->argv[argc_l] = NULL;
1172 if (!returnCommand) {
1173 job->text = xmalloc(strlen(*commandPtr) + 1);
1174 strcpy(job->text, *commandPtr);
1176 /* This leaves any trailing spaces, which is a bit sloppy */
1177 count = returnCommand - *commandPtr;
1178 job->text = xmalloc(count + 1);
1179 strncpy(job->text, *commandPtr, count);
1180 job->text[count] = '\0';
1183 *commandPtr = returnCommand;
1188 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1192 int nextin, nextout;
1193 int pipefds[2]; /* pipefd[0] is for reading */
1194 struct builtInCommand *x;
1195 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1196 struct BB_applet search_applet, *applet;
1199 nextin = 0, nextout = 1;
1200 for (i = 0; i < newJob->numProgs; i++) {
1201 if ((i + 1) < newJob->numProgs) {
1203 nextout = pipefds[1];
1205 if (outPipe[1]!=-1) {
1206 nextout = outPipe[1];
1212 #ifdef BB_FEATURE_SH_ENVIRONMENT
1213 if (showXtrace==TRUE) {
1215 fprintf(stderr, "+ ");
1216 for (j = 0; newJob->progs[i].argv[j]; j++)
1217 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1218 fprintf(stderr, "\n");
1222 /* Check if the command matches any non-forking builtins */
1223 for (x = bltins; x->cmd; x++) {
1224 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1225 return(x->function(newJob, jobList));
1229 if (!(newJob->progs[i].pid = fork())) {
1230 signal(SIGTTOU, SIG_DFL);
1232 if (outPipe[1]!=-1) {
1247 /* explicit redirections override pipes */
1248 setupRedirections(newJob->progs + i);
1250 /* Check if the command matches any of the other builtins */
1251 for (x = bltins_forking; x->cmd; x++) {
1252 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1254 exit (x->function(newJob, jobList));
1257 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1258 /* Check if the command matches any busybox internal
1259 * commands ("applets") here. Following discussions from
1260 * November 2000 on busybox@opensource.lineo.com, don't use
1261 * get_last_path_component(). This way explicit (with
1262 * slashes) filenames will never be interpreted as an
1263 * applet, just like with builtins. This way the user can
1264 * override an applet with an explicit filename reference.
1265 * The only downside to this change is that an explicit
1266 * /bin/foo invocation fill fork and exec /bin/foo, even if
1267 * /bin/foo is a symlink to busybox.
1269 search_applet.name = newJob->progs[i].argv[0];
1271 #ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN
1272 /* If you enable BB_FEATURE_SH_APPLETS_ALWAYS_WIN, then
1273 * if you run /bin/cat, it will use BusyBox cat even if
1274 * /bin/cat exists on the filesystem and is _not_ busybox.
1275 * Some systems want this, others do not. Choose wisely. :-)
1277 search_applet.name = get_last_path_component(search_applet.name);
1280 /* Do a binary search to find the applet entry given the name. */
1281 applet = bsearch(&search_applet, applets, NUM_APPLETS,
1282 sizeof(struct BB_applet), applet_name_compare);
1283 if (applet != NULL) {
1285 char** argv=newJob->progs[i].argv;
1286 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1287 applet_name=applet->name;
1289 exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
1293 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1294 error_msg_and_die("%s: %s\n", newJob->progs[i].argv[0],
1297 if (outPipe[1]!=-1) {
1301 /* put our child in the process group whose leader is the
1302 first process in this pipe */
1303 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1309 /* If there isn't another process, nextin is garbage
1310 but it doesn't matter */
1311 nextin = pipefds[0];
1314 newJob->pgrp = newJob->progs[0].pid;
1316 /* find the ID for the theJob to use */
1318 for (theJob = jobList->head; theJob; theJob = theJob->next)
1319 if (theJob->jobId >= newJob->jobId)
1320 newJob->jobId = theJob->jobId + 1;
1322 /* add the theJob to the list of running jobs */
1323 if (!jobList->head) {
1324 theJob = jobList->head = xmalloc(sizeof(*theJob));
1326 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1327 theJob->next = xmalloc(sizeof(*theJob));
1328 theJob = theJob->next;
1332 theJob->next = NULL;
1333 theJob->runningProgs = theJob->numProgs;
1334 theJob->stoppedProgs = 0;
1337 /* we don't wait for background theJobs to return -- append it
1338 to the list of backgrounded theJobs and leave it alone */
1339 printf("[%d] %d\n", theJob->jobId,
1340 newJob->progs[newJob->numProgs - 1].pid);
1341 #ifdef BB_FEATURE_SH_ENVIRONMENT
1342 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1345 jobList->fg = theJob;
1347 /* move the new process group into the foreground */
1348 /* suppress messages when run from /linuxrc mag@sysgo.de */
1349 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1350 perror("tcsetpgrp");
1356 static int busy_loop(FILE * input)
1359 char *nextCommand = NULL;
1365 newJob.jobContext = REGULAR_JOB_CONTEXT;
1367 /* save current owner of TTY so we can restore it on exit */
1368 parent_pgrp = tcgetpgrp(0);
1370 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1372 /* don't pay any attention to this signal; it just confuses
1373 things and isn't really meant for shells anyway */
1374 signal(SIGTTOU, SIG_IGN);
1378 /* no job is in the foreground */
1380 /* see if any background processes have exited */
1381 checkJobs(&jobList);
1384 if (getCommand(input, command))
1386 nextCommand = command;
1389 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1391 int pipefds[2] = {-1,-1};
1392 runCommand(&newJob, &jobList, inBg, pipefds);
1396 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1400 /* a job is running in the foreground; wait for it */
1402 while (!jobList.fg->progs[i].pid ||
1403 jobList.fg->progs[i].isStopped == 1) i++;
1405 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1407 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1408 /* the child exited */
1409 jobList.fg->runningProgs--;
1410 jobList.fg->progs[i].pid = 0;
1412 #ifdef BB_FEATURE_SH_ENVIRONMENT
1413 lastReturnCode=WEXITSTATUS(status);
1415 debug_printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1416 if (!jobList.fg->runningProgs) {
1419 removeJob(&jobList, jobList.fg);
1423 /* the child was stopped */
1424 jobList.fg->stoppedProgs++;
1425 jobList.fg->progs[i].isStopped = 1;
1427 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1428 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1429 "Stopped", jobList.fg->text);
1435 /* move the shell to the foreground */
1436 /* suppress messages when run from /linuxrc mag@sysgo.de */
1437 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1438 perror("tcsetpgrp");
1444 /* return controlling TTY back to parent process group before exiting */
1445 if (tcsetpgrp(0, parent_pgrp))
1446 perror("tcsetpgrp");
1448 /* return exit status if called with "-c" */
1449 if (input == NULL && WIFEXITED(status))
1450 return WEXITSTATUS(status);
1456 #ifdef BB_FEATURE_CLEAN_UP
1457 void free_memory(void)
1463 if (local_pending_command)
1464 free(local_pending_command);
1466 if (jobList.fg && !jobList.fg->runningProgs) {
1467 removeJob(&jobList, jobList.fg);
1473 int shell_main(int argc_l, char **argv_l)
1475 int opt, interactive=FALSE;
1476 FILE *input = stdin;
1481 if (argv[0] && argv[0][0] == '-') {
1483 input = fopen("/etc/profile", "r");
1485 fprintf(stdout, "Couldn't open file '/etc/profile'\n");
1487 /* Now run the file */
1493 while ((opt = getopt(argc_l, argv_l, "cxi")) > 0) {
1497 if (local_pending_command != 0)
1498 error_msg_and_die("multiple -c arguments\n");
1499 local_pending_command = xstrdup(argv[optind]);
1503 #ifdef BB_FEATURE_SH_ENVIRONMENT
1515 /* A shell is interactive if the `-i' flag was given, or if all of
1516 * the following conditions are met:
1518 * no arguments remaining or the -s flag given
1519 * standard input is a terminal
1520 * standard output is a terminal
1521 * Refer to Posix.2, the description of the `sh' utility. */
1522 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1523 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1524 /* Looks like they want an interactive shell */
1525 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1526 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1527 } else if (local_pending_command==NULL) {
1528 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1529 input = xfopen(argv[optind], "r");
1532 /* initialize the cwd -- this is never freed...*/
1533 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1534 getcwd(cwd, sizeof(char)*MAX_LINE);
1536 #ifdef BB_FEATURE_CLEAN_UP
1537 atexit(free_memory);
1541 return (busy_loop(input));