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[9],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 my_getpwuid(user, geteuid());
684 gethostname(buf, 255);
685 s = strchr(buf, '.');
689 if (source == stdin) {
690 #ifdef BB_FEATURE_SH_COMMAND_EDITING
694 ** enable command line editing only while a command line
695 ** is actually being read; otherwise, we'll end up bequeathing
696 ** atexit() handlers and other unwanted stuff to our
697 ** child processes (rob@sysgo.de)
700 signal(SIGWINCH, win_changed);
701 len=fprintf(stdout, "[%s@%s %s]%s", user, buf,
702 get_last_path_component(cwd), prompt);
704 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
705 sprintf(promptStr, "[%s@%s %s]%s", user, buf,
706 get_last_path_component(cwd), prompt);
707 cmdedit_read_input(promptStr, command);
710 signal(SIGWINCH, SIG_DFL);
716 while ((i>0) && (*(cwd+i)!='/') ) i--;
717 if (*(cwd+i)=='/') i++;
720 fprintf(stdout, "[%s@%s %s]%s",user, buf, (cwd+i), prompt);
725 if (!fgets(command, BUFSIZ - 2, source)) {
731 /* remove trailing newline */
732 command[strlen(command) - 1] = '\0';
737 #ifdef BB_FEATURE_SH_ENVIRONMENT
738 static char* itoa(register int i)
740 static char a[7]; /* Max 7 ints */
741 register char *b = a + sizeof(a) - 1;
749 *--b = '0' + (i % 10);
759 static void globLastArgument(struct childProgram *prog, int *argcPtr,
762 int argc_l = *argcPtr;
763 int argcAlloced = *argcAllocedPtr;
767 char *src, *dst, *var;
769 if (argc_l > 1) { /* cmd->globResult is already initialized */
771 i = prog->globResult.gl_pathc;
777 /* do shell variable substitution */
778 if(*prog->argv[argc_l - 1] == '$') {
779 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
780 prog->argv[argc_l - 1] = var;
782 #ifdef BB_FEATURE_SH_ENVIRONMENT
784 switch(*(prog->argv[argc_l - 1] + 1)) {
786 prog->argv[argc_l - 1] = itoa(lastReturnCode);
789 prog->argv[argc_l - 1] = itoa(getpid());
792 prog->argv[argc_l - 1] = itoa(argc-1);
796 *(prog->argv[argc_l - 1])='\0';
798 prog->argv[argc_l - 1] = itoa(lastBgPid);
800 case '0':case '1':case '2':case '3':case '4':
801 case '5':case '6':case '7':case '8':case '9':
803 int index=*(prog->argv[argc_l - 1] + 1)-48;
805 *(prog->argv[argc_l - 1])='\0';
807 prog->argv[argc_l - 1] = argv[index];
816 if (strpbrk(prog->argv[argc_l - 1],"*[]?")!= NULL){
817 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
818 if (rc == GLOB_NOSPACE) {
819 error_msg("out of space during glob operation\n");
821 } else if (rc == GLOB_NOMATCH ||
822 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
823 strcmp(prog->argv[argc_l - 1],
824 prog->globResult.gl_pathv[i]) == 0)) {
825 /* we need to remove whatever \ quoting is still present */
826 src = dst = prog->argv[argc_l - 1];
830 *dst++ = process_escape_sequence(&src);
838 argcAlloced += (prog->globResult.gl_pathc - i);
839 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
840 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
841 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
842 argc_l += (prog->globResult.gl_pathc - i - 1);
845 src = dst = prog->argv[argc_l - 1];
849 *dst++ = process_escape_sequence(&src);
857 prog->globResult.gl_pathc=0;
859 prog->globResult.gl_pathv=NULL;
861 *argcAllocedPtr = argcAlloced;
865 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
866 line). If a valid command is found, commandPtr is set to point to
867 the beginning of the next command (if the original command had more
868 then one job associated with it) or NULL if no more commands are
870 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
873 char *returnCommand = NULL;
874 char *src, *buf, *chptr;
881 struct childProgram *prog;
883 /* skip leading white space */
884 while (**commandPtr && isspace(**commandPtr))
887 /* this handles empty lines or leading '#' characters */
888 if (!**commandPtr || (**commandPtr == '#')) {
895 job->progs = xmalloc(sizeof(*job->progs));
897 /* We set the argv elements to point inside of this string. The
898 memory is freed by freeJob(). Allocate twice the original
899 length in case we need to quote every single character.
901 Getting clean memory relieves us of the task of NULL
902 terminating things and makes the rest of this look a bit
903 cleaner (though it is, admittedly, a tad less efficient) */
904 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
908 prog->numRedirections = 0;
909 prog->redirections = NULL;
914 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
915 prog->argv[0] = job->cmdBuf;
919 while (*src && !done) {
926 error_msg("character expected after \\\n");
931 /* in shell, "\'" should yield \' */
936 } else if (*src == '*' || *src == '?' || *src == '[' ||
937 *src == ']') *buf++ = '\\';
939 } else if (isspace(*src)) {
940 if (*prog->argv[argc_l]) {
942 /* +1 here leaves room for the NULL which ends argv */
943 if ((argc_l + 1) == argvAlloced) {
945 prog->argv = xrealloc(prog->argv,
946 sizeof(*prog->argv) *
949 globLastArgument(prog, &argc_l, &argvAlloced);
950 prog->argv[argc_l] = buf;
959 case '#': /* comment */
966 case '>': /* redirections */
968 i = prog->numRedirections++;
969 prog->redirections = xrealloc(prog->redirections,
970 sizeof(*prog->redirections) *
973 prog->redirections[i].fd = -1;
974 if (buf != prog->argv[argc_l]) {
975 /* the stuff before this character may be the file number
977 prog->redirections[i].fd =
978 strtol(prog->argv[argc_l], &chptr, 10);
980 if (*chptr && *prog->argv[argc_l]) {
982 globLastArgument(prog, &argc_l, &argvAlloced);
983 prog->argv[argc_l] = buf;
987 if (prog->redirections[i].fd == -1) {
989 prog->redirections[i].fd = 1;
991 prog->redirections[i].fd = 0;
996 prog->redirections[i].type =
997 REDIRECT_APPEND, src++;
999 prog->redirections[i].type = REDIRECT_OVERWRITE;
1001 prog->redirections[i].type = REDIRECT_INPUT;
1004 /* This isn't POSIX sh compliant. Oh well. */
1006 while (isspace(*chptr))
1010 error_msg("file name expected after %c\n", *src);
1016 prog->redirections[i].filename = buf;
1017 while (*chptr && !isspace(*chptr))
1020 src = chptr - 1; /* we src++ later */
1021 prog->argv[argc_l] = ++buf;
1024 case '|': /* pipe */
1025 /* finish this command */
1026 if (*prog->argv[argc_l])
1029 error_msg("empty command in pipe\n");
1034 prog->argv[argc_l] = NULL;
1036 /* and start the next */
1038 job->progs = xrealloc(job->progs,
1039 sizeof(*job->progs) * job->numProgs);
1040 prog = job->progs + (job->numProgs - 1);
1041 prog->numRedirections = 0;
1042 prog->redirections = NULL;
1044 prog->isStopped = 0;
1048 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
1049 prog->argv[0] = ++buf;
1052 while (*src && isspace(*src))
1056 error_msg("empty command in pipe\n");
1061 src--; /* we'll ++ it at the end of the loop */
1065 case '&': /* background */
1067 case ';': /* multiple commands */
1069 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1072 #ifdef BB_FEATURE_SH_BACKTICKS
1074 /* Exec a backtick-ed command */
1076 char* charptr1=NULL, *charptr2;
1079 struct jobSet njobList = { NULL, NULL };
1083 ptr=strchr(++src, '`');
1085 fprintf(stderr, "Unmatched '`' in command\n");
1090 /* Make some space to hold just the backticked command */
1091 charptr1 = charptr2 = xmalloc(1+ptr-src);
1092 memcpy(charptr1, src, ptr-src);
1093 charptr1[ptr-src] = '\0';
1094 newJob = xmalloc(sizeof(struct job));
1095 /* Now parse and run the backticked command */
1096 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
1097 && newJob->numProgs) {
1099 runCommand(newJob, &njobList, 0, pipefd);
1105 /* Make a copy of any stuff left over in the command
1106 * line after the second backtick */
1107 charptr2 = xmalloc(strlen(ptr)+1);
1108 memcpy(charptr2, ptr+1, strlen(ptr));
1111 /* Copy the output from the backtick-ed command into the
1112 * command line, making extra room as needed */
1114 charptr1 = xmalloc(BUFSIZ);
1115 while ( (size=full_read(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1116 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1117 if (newSize > BUFSIZ) {
1118 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
1119 size + 1 + strlen(charptr2));
1121 memcpy(src, charptr1, size);
1129 /* Now paste into the *commandPtr all the stuff
1130 * leftover after the second backtick */
1131 memcpy(src, charptr2, strlen(charptr2)+1);
1134 /* Now recursively call parseCommand to deal with the new
1135 * and improved version of the command line with the backtick
1136 * results expanded in place... */
1138 return(parseCommand(commandPtr, job, jobList, inBg));
1141 #endif // BB_FEATURE_SH_BACKTICKS
1146 error_msg("character expected after \\\n");
1150 if (*src == '*' || *src == '[' || *src == ']'
1151 || *src == '?') *buf++ = '\\';
1160 if (*prog->argv[argc_l]) {
1162 globLastArgument(prog, &argc_l, &argvAlloced);
1168 prog->argv[argc_l] = NULL;
1170 if (!returnCommand) {
1171 job->text = xmalloc(strlen(*commandPtr) + 1);
1172 strcpy(job->text, *commandPtr);
1174 /* This leaves any trailing spaces, which is a bit sloppy */
1175 count = returnCommand - *commandPtr;
1176 job->text = xmalloc(count + 1);
1177 strncpy(job->text, *commandPtr, count);
1178 job->text[count] = '\0';
1181 *commandPtr = returnCommand;
1186 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1190 int nextin, nextout;
1191 int pipefds[2]; /* pipefd[0] is for reading */
1192 struct builtInCommand *x;
1193 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1194 struct BB_applet search_applet, *applet;
1197 nextin = 0, nextout = 1;
1198 for (i = 0; i < newJob->numProgs; i++) {
1199 if ((i + 1) < newJob->numProgs) {
1201 nextout = pipefds[1];
1203 if (outPipe[1]!=-1) {
1204 nextout = outPipe[1];
1210 #ifdef BB_FEATURE_SH_ENVIRONMENT
1211 if (showXtrace==TRUE) {
1213 fprintf(stderr, "+ ");
1214 for (j = 0; newJob->progs[i].argv[j]; j++)
1215 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1216 fprintf(stderr, "\n");
1220 /* Check if the command matches any non-forking builtins */
1221 for (x = bltins; x->cmd; x++) {
1222 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1223 return(x->function(newJob, jobList));
1227 if (!(newJob->progs[i].pid = fork())) {
1228 signal(SIGTTOU, SIG_DFL);
1230 if (outPipe[1]!=-1) {
1245 /* explicit redirections override pipes */
1246 setupRedirections(newJob->progs + i);
1248 /* Check if the command matches any of the other builtins */
1249 for (x = bltins_forking; x->cmd; x++) {
1250 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1252 exit (x->function(newJob, jobList));
1255 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1256 /* Check if the command matches any busybox internal
1257 * commands ("applets") here. Following discussions from
1258 * November 2000 on busybox@opensource.lineo.com, don't use
1259 * get_last_path_component(). This way explicit (with
1260 * slashes) filenames will never be interpreted as an
1261 * applet, just like with builtins. This way the user can
1262 * override an applet with an explicit filename reference.
1263 * The only downside to this change is that an explicit
1264 * /bin/foo invocation fill fork and exec /bin/foo, even if
1265 * /bin/foo is a symlink to busybox.
1267 search_applet.name = newJob->progs[i].argv[0];
1269 #ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN
1270 /* If you enable BB_FEATURE_SH_APPLETS_ALWAYS_WIN, then
1271 * if you run /bin/cat, it will use BusyBox cat even if
1272 * /bin/cat exists on the filesystem and is _not_ busybox.
1273 * Some systems want this, others do not. Choose wisely. :-)
1275 search_applet.name = get_last_path_component(search_applet.name);
1278 /* Do a binary search to find the applet entry given the name. */
1279 applet = bsearch(&search_applet, applets, NUM_APPLETS,
1280 sizeof(struct BB_applet), applet_name_compare);
1281 if (applet != NULL) {
1283 char** argv=newJob->progs[i].argv;
1284 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1285 applet_name=applet->name;
1287 exit((*(applet->main)) (argc_l, newJob->progs[i].argv));
1291 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1292 error_msg_and_die("%s: %s\n", newJob->progs[i].argv[0],
1295 if (outPipe[1]!=-1) {
1299 /* put our child in the process group whose leader is the
1300 first process in this pipe */
1301 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1307 /* If there isn't another process, nextin is garbage
1308 but it doesn't matter */
1309 nextin = pipefds[0];
1312 newJob->pgrp = newJob->progs[0].pid;
1314 /* find the ID for the theJob to use */
1316 for (theJob = jobList->head; theJob; theJob = theJob->next)
1317 if (theJob->jobId >= newJob->jobId)
1318 newJob->jobId = theJob->jobId + 1;
1320 /* add the theJob to the list of running jobs */
1321 if (!jobList->head) {
1322 theJob = jobList->head = xmalloc(sizeof(*theJob));
1324 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1325 theJob->next = xmalloc(sizeof(*theJob));
1326 theJob = theJob->next;
1330 theJob->next = NULL;
1331 theJob->runningProgs = theJob->numProgs;
1332 theJob->stoppedProgs = 0;
1335 /* we don't wait for background theJobs to return -- append it
1336 to the list of backgrounded theJobs and leave it alone */
1337 printf("[%d] %d\n", theJob->jobId,
1338 newJob->progs[newJob->numProgs - 1].pid);
1339 #ifdef BB_FEATURE_SH_ENVIRONMENT
1340 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1343 jobList->fg = theJob;
1345 /* move the new process group into the foreground */
1346 /* suppress messages when run from /linuxrc mag@sysgo.de */
1347 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1348 perror("tcsetpgrp");
1354 static int busy_loop(FILE * input)
1357 char *nextCommand = NULL;
1363 newJob.jobContext = REGULAR_JOB_CONTEXT;
1365 /* save current owner of TTY so we can restore it on exit */
1366 parent_pgrp = tcgetpgrp(0);
1368 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1370 /* don't pay any attention to this signal; it just confuses
1371 things and isn't really meant for shells anyway */
1372 signal(SIGTTOU, SIG_IGN);
1376 /* no job is in the foreground */
1378 /* see if any background processes have exited */
1379 checkJobs(&jobList);
1382 if (getCommand(input, command))
1384 nextCommand = command;
1387 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1389 int pipefds[2] = {-1,-1};
1390 runCommand(&newJob, &jobList, inBg, pipefds);
1394 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1398 /* a job is running in the foreground; wait for it */
1400 while (!jobList.fg->progs[i].pid ||
1401 jobList.fg->progs[i].isStopped == 1) i++;
1403 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1405 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1406 /* the child exited */
1407 jobList.fg->runningProgs--;
1408 jobList.fg->progs[i].pid = 0;
1410 #ifdef BB_FEATURE_SH_ENVIRONMENT
1411 lastReturnCode=WEXITSTATUS(status);
1413 debug_printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1414 if (!jobList.fg->runningProgs) {
1417 removeJob(&jobList, jobList.fg);
1421 /* the child was stopped */
1422 jobList.fg->stoppedProgs++;
1423 jobList.fg->progs[i].isStopped = 1;
1425 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1426 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1427 "Stopped", jobList.fg->text);
1433 /* move the shell to the foreground */
1434 /* suppress messages when run from /linuxrc mag@sysgo.de */
1435 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1436 perror("tcsetpgrp");
1442 /* return controlling TTY back to parent process group before exiting */
1443 if (tcsetpgrp(0, parent_pgrp))
1444 perror("tcsetpgrp");
1446 /* return exit status if called with "-c" */
1447 if (input == NULL && WIFEXITED(status))
1448 return WEXITSTATUS(status);
1454 #ifdef BB_FEATURE_CLEAN_UP
1455 void free_memory(void)
1461 if (local_pending_command)
1462 free(local_pending_command);
1464 if (jobList.fg && !jobList.fg->runningProgs) {
1465 removeJob(&jobList, jobList.fg);
1471 int shell_main(int argc_l, char **argv_l)
1473 int opt, interactive=FALSE;
1474 FILE *input = stdin;
1479 if (argv[0] && argv[0][0] == '-') {
1481 input = fopen("/etc/profile", "r");
1483 fprintf(stdout, "Couldn't open file '/etc/profile'\n");
1485 /* Now run the file */
1491 while ((opt = getopt(argc_l, argv_l, "cxi")) > 0) {
1495 if (local_pending_command != 0)
1496 error_msg_and_die("multiple -c arguments\n");
1497 local_pending_command = xstrdup(argv[optind]);
1501 #ifdef BB_FEATURE_SH_ENVIRONMENT
1513 /* A shell is interactive if the `-i' flag was given, or if all of
1514 * the following conditions are met:
1516 * no arguments remaining or the -s flag given
1517 * standard input is a terminal
1518 * standard output is a terminal
1519 * Refer to Posix.2, the description of the `sh' utility. */
1520 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1521 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1522 /* Looks like they want an interactive shell */
1523 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1524 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1525 } else if (local_pending_command==NULL) {
1526 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1527 input = xfopen(argv[optind], "r");
1530 /* initialize the cwd -- this is never freed...*/
1531 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1532 getcwd(cwd, sizeof(char)*MAX_LINE);
1534 #ifdef BB_FEATURE_CLEAN_UP
1535 atexit(free_memory);
1539 return (busy_loop(input));