1 /* vi: set sw=4 ts=4: */
3 * lash -- the BusyBox Lame-Ass SHell
5 * Copyright (C) 2000 by Lineo, inc.
6 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
8 * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
9 * under the following liberal license: "We have placed this source code in the
10 * public domain. Use it in any project, free or commercial."
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 //#define BB_FEATURE_SH_BACKTICKS
30 //#define BB_FEATURE_SH_IF_EXPRESSIONS
31 #define BB_FEATURE_SH_ENVIRONMENT
44 #include <sys/ioctl.h>
48 #ifdef BB_FEATURE_SH_COMMAND_EDITING
52 #define MAX_LINE 256 /* size of input buffer for `read' builtin */
53 #define MAX_READ 128 /* size of input buffer for `read' builtin */
54 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
57 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
61 static const unsigned int REGULAR_JOB_CONTEXT=0x1;
62 static const unsigned int IF_TRUE_CONTEXT=0x2;
63 static const unsigned int IF_FALSE_CONTEXT=0x4;
64 static const unsigned int THEN_EXP_CONTEXT=0x8;
65 static const unsigned int ELSE_EXP_CONTEXT=0x10;
69 struct job *head; /* head of list of running jobs */
70 struct job *fg; /* current foreground job */
73 struct redirectionSpecifier {
74 enum redirectionType type; /* type of redirection */
75 int fd; /* file descriptor being redirected */
76 char *filename; /* file to redirect fd to */
80 pid_t pid; /* 0 if exited */
81 char **argv; /* program name and arguments */
82 int numRedirections; /* elements in redirection array */
83 struct redirectionSpecifier *redirections; /* I/O redirections */
84 glob_t globResult; /* result of parameter globbing */
85 int freeGlob; /* should we globfree(&globResult)? */
86 int isStopped; /* is the program currently running? */
90 int jobId; /* job number */
91 int numProgs; /* total number of programs in job */
92 int runningProgs; /* number of programs running */
93 char *text; /* name of job */
94 char *cmdBuf; /* buffer various argv's point into */
95 pid_t pgrp; /* process group ID for the job */
96 struct childProgram *progs; /* array of programs in job */
97 struct job *next; /* to track background commands */
98 int stoppedProgs; /* number of programs alive, but stopped */
99 int jobContext; /* bitmask defining current context */
102 struct builtInCommand {
103 char *cmd; /* name */
104 char *descr; /* description */
105 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
108 /* function prototypes for builtins */
109 static int builtin_cd(struct job *cmd, struct jobSet *junk);
110 static int builtin_env(struct job *dummy, struct jobSet *junk);
111 static int builtin_exec(struct job *cmd, struct jobSet *junk);
112 static int builtin_exit(struct job *cmd, struct jobSet *junk);
113 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
114 static int builtin_help(struct job *cmd, struct jobSet *junk);
115 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
116 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
117 static int builtin_export(struct job *cmd, struct jobSet *junk);
118 static int builtin_source(struct job *cmd, struct jobSet *jobList);
119 static int builtin_unset(struct job *cmd, struct jobSet *junk);
120 static int builtin_read(struct job *cmd, struct jobSet *junk);
121 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
122 static int builtin_if(struct job *cmd, struct jobSet *junk);
123 static int builtin_then(struct job *cmd, struct jobSet *junk);
124 static int builtin_else(struct job *cmd, struct jobSet *junk);
125 static int builtin_fi(struct job *cmd, struct jobSet *junk);
129 /* function prototypes for shell stuff */
130 static void checkJobs(struct jobSet *jobList);
131 static int getCommand(FILE * source, char *command);
132 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg);
133 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
134 static int busy_loop(FILE * input);
137 /* Table of built-in functions (these are non-forking builtins, meaning they
138 * can change global variables in the parent shell process but they will not
139 * work with pipes and redirects; 'unset foo | whatever' will not work) */
140 static struct builtInCommand bltins[] = {
141 {"bg", "Resume a job in the background", builtin_fg_bg},
142 {"cd", "Change working directory", builtin_cd},
143 {"exec", "Exec command, replacing this shell with the exec'd process", builtin_exec},
144 {"exit", "Exit from shell()", builtin_exit},
145 {"fg", "Bring job into the foreground", builtin_fg_bg},
146 {"jobs", "Lists the active jobs", builtin_jobs},
147 {"export", "Set environment variable", builtin_export},
148 {"unset", "Unset environment variable", builtin_unset},
149 {"read", "Input environment variable", builtin_read},
150 {".", "Source-in and run commands in a file", builtin_source},
151 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
152 {"if", NULL, builtin_if},
153 {"then", NULL, builtin_then},
154 {"else", NULL, builtin_else},
155 {"fi", NULL, builtin_fi},
160 /* Table of forking built-in functions (things that fork cannot change global
161 * variables in the parent process, such as the current working directory) */
162 static struct builtInCommand bltins_forking[] = {
163 {"env", "Print all environment variables", builtin_env},
164 {"pwd", "Print current directory", builtin_pwd},
165 {"help", "List shell built-in commands", builtin_help},
169 static char *prompt = "# ";
171 static char *local_pending_command = NULL;
172 static char *promptStr = NULL;
173 static struct jobSet jobList = { NULL, NULL };
176 #ifdef BB_FEATURE_SH_ENVIRONMENT
177 static int lastBgPid=-1;
178 static int lastReturnCode=-1;
179 static int showXtrace=FALSE;
183 #ifdef BB_FEATURE_SH_COMMAND_EDITING
184 void win_changed(int junk)
186 struct winsize win = { 0, 0, 0, 0 };
187 ioctl(0, TIOCGWINSZ, &win);
188 if (win.ws_col > 0) {
189 cmdedit_setwidth( win.ws_col - 1);
195 /* built-in 'cd <path>' handler */
196 static int builtin_cd(struct job *cmd, struct jobSet *junk)
200 if (!cmd->progs[0].argv[1] == 1)
201 newdir = getenv("HOME");
203 newdir = cmd->progs[0].argv[1];
205 printf("cd: %s: %s\n", newdir, strerror(errno));
208 getcwd(cwd, sizeof(char)*MAX_LINE);
213 /* built-in 'env' handler */
214 static int builtin_env(struct job *dummy, struct jobSet *junk)
218 for (e = environ; *e; e++) {
219 fprintf(stdout, "%s\n", *e);
224 /* built-in 'exec' handler */
225 static int builtin_exec(struct job *cmd, struct jobSet *junk)
227 if (cmd->progs[0].argv[1])
229 cmd->progs[0].argv++;
230 execvp(cmd->progs[0].argv[0], cmd->progs[0].argv);
231 fatalError("Exec to %s failed: %s\n", cmd->progs[0].argv[0],
237 /* built-in 'exit' handler */
238 static int builtin_exit(struct job *cmd, struct jobSet *junk)
240 if (!cmd->progs[0].argv[1] == 1)
243 exit (atoi(cmd->progs[0].argv[1]));
246 /* built-in 'fg' and 'bg' handler */
247 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
250 struct job *job=NULL;
252 if (!jobList->head) {
253 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
254 errorMsg("%s: exactly one argument is expected\n",
255 cmd->progs[0].argv[0]);
258 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
259 errorMsg("%s: bad argument '%s'\n",
260 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
262 for (job = jobList->head; job; job = job->next) {
263 if (job->jobId == jobNum) {
273 errorMsg("%s: unknown job %d\n",
274 cmd->progs[0].argv[0], jobNum);
278 if (*cmd->progs[0].argv[0] == 'f') {
279 /* Make this job the foreground job */
280 /* suppress messages when run from /linuxrc mag@sysgo.de */
281 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
286 /* Restart the processes in the job */
287 for (i = 0; i < job->numProgs; i++)
288 job->progs[i].isStopped = 0;
290 kill(-job->pgrp, SIGCONT);
292 job->stoppedProgs = 0;
297 /* built-in 'help' handler */
298 static int builtin_help(struct job *dummy, struct jobSet *junk)
300 struct builtInCommand *x;
302 fprintf(stdout, "\nBuilt-in commands:\n");
303 fprintf(stdout, "-------------------\n");
304 for (x = bltins; x->cmd; x++) {
307 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
309 for (x = bltins_forking; x->cmd; x++) {
312 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
314 fprintf(stdout, "\n\n");
318 /* built-in 'jobs' handler */
319 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
324 for (job = jobList->head; job; job = job->next) {
325 if (job->runningProgs == job->stoppedProgs)
326 statusString = "Stopped";
328 statusString = "Running";
330 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
336 /* built-in 'pwd' handler */
337 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
339 getcwd(cwd, sizeof(char)*MAX_LINE);
340 fprintf(stdout, "%s\n", cwd);
344 /* built-in 'export VAR=value' handler */
345 static int builtin_export(struct job *cmd, struct jobSet *junk)
349 if (!cmd->progs[0].argv[1] == 1) {
350 return (builtin_env(cmd, junk));
352 res = putenv(cmd->progs[0].argv[1]);
354 fprintf(stdout, "export: %s\n", strerror(errno));
358 /* built-in 'read VAR' handler */
359 static int builtin_read(struct job *cmd, struct jobSet *junk)
361 int res = 0, len, newlen;
363 char string[MAX_READ];
365 if (cmd->progs[0].argv[1]) {
366 /* argument (VAR) given: put "VAR=" into buffer */
367 strcpy(string, cmd->progs[0].argv[1]);
368 len = strlen(string);
371 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
372 newlen = strlen(string);
374 string[--newlen] = '\0'; /* chomp trailing newline */
376 ** string should now contain "VAR=<value>"
377 ** copy it (putenv() won't do that, so we must make sure
378 ** the string resides in a static buffer!)
381 if((s = strdup(string)))
384 fprintf(stdout, "read: %s\n", strerror(errno));
387 fgets(string, sizeof(string), stdin);
392 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
393 /* Built-in handler for 'if' commands */
394 static int builtin_if(struct job *cmd, struct jobSet *jobList)
397 char* charptr1=cmd->text+3; /* skip over the leading 'if ' */
399 /* Now run the 'if' command */
400 status=strlen(charptr1);
401 local_pending_command = xmalloc(status+1);
402 strncpy(local_pending_command, charptr1, status);
403 local_pending_command[status]='\0';
405 fprintf(stderr, "'if' now testing '%s'\n", local_pending_command);
407 status = busy_loop(NULL); /* Frees local_pending_command */
409 fprintf(stderr, "if test returned ");
413 fprintf(stderr, "TRUE\n");
415 cmd->jobContext |= IF_TRUE_CONTEXT;
418 fprintf(stderr, "FALSE\n");
420 cmd->jobContext |= IF_FALSE_CONTEXT;
426 /* Built-in handler for 'then' (part of the 'if' command) */
427 static int builtin_then(struct job *cmd, struct jobSet *junk)
430 char* charptr1=cmd->text+5; /* skip over the leading 'then ' */
432 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
433 errorMsg("unexpected token `then'\n");
436 /* If the if result was FALSE, skip the 'then' stuff */
437 if (cmd->jobContext & IF_FALSE_CONTEXT) {
441 cmd->jobContext |= THEN_EXP_CONTEXT;
442 //printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
444 /* Now run the 'then' command */
445 status=strlen(charptr1);
446 local_pending_command = xmalloc(status+1);
447 strncpy(local_pending_command, charptr1, status);
448 local_pending_command[status]='\0';
450 fprintf(stderr, "'then' now running '%s'\n", charptr1);
452 return( busy_loop(NULL));
455 /* Built-in handler for 'else' (part of the 'if' command) */
456 static int builtin_else(struct job *cmd, struct jobSet *junk)
459 char* charptr1=cmd->text+5; /* skip over the leading 'else ' */
461 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
462 errorMsg("unexpected token `else'\n");
465 /* If the if result was TRUE, skip the 'else' stuff */
466 if (cmd->jobContext & IF_TRUE_CONTEXT) {
470 cmd->jobContext |= ELSE_EXP_CONTEXT;
471 //printf("Hit an else -- jobContext=%d\n", cmd->jobContext);
473 /* Now run the 'else' command */
474 status=strlen(charptr1);
475 local_pending_command = xmalloc(status+1);
476 strncpy(local_pending_command, charptr1, status);
477 local_pending_command[status]='\0';
479 fprintf(stderr, "'else' now running '%s'\n", charptr1);
481 return( busy_loop(NULL));
484 /* Built-in handler for 'fi' (part of the 'if' command) */
485 static int builtin_fi(struct job *cmd, struct jobSet *junk)
487 if (! (cmd->jobContext & (IF_TRUE_CONTEXT|IF_FALSE_CONTEXT))) {
488 errorMsg("unexpected token `fi'\n");
491 /* Clear out the if and then context bits */
492 cmd->jobContext &= ~(IF_TRUE_CONTEXT|IF_FALSE_CONTEXT|THEN_EXP_CONTEXT|ELSE_EXP_CONTEXT);
494 fprintf(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_RDWR | O_CREAT | O_TRUNC;
632 case REDIRECT_APPEND:
633 mode = O_RDWR | 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 if (source == NULL) {
659 if (local_pending_command) {
660 /* a command specified (-c option): return it & mark it done */
661 strcpy(command, local_pending_command);
662 free(local_pending_command);
663 local_pending_command = NULL;
669 if (source == stdin) {
670 #ifdef BB_FEATURE_SH_COMMAND_EDITING
674 ** enable command line editing only while a command line
675 ** is actually being read; otherwise, we'll end up bequeathing
676 ** atexit() handlers and other unwanted stuff to our
677 ** child processes (rob@sysgo.de)
680 signal(SIGWINCH, win_changed);
681 len=fprintf(stdout, "%s %s", cwd, prompt);
683 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
684 sprintf(promptStr, "%s %s", cwd, prompt);
685 cmdedit_read_input(promptStr, command);
688 signal(SIGWINCH, SIG_DFL);
691 fprintf(stdout, "%s %s", cwd, prompt);
696 if (!fgets(command, BUFSIZ - 2, source)) {
702 /* remove trailing newline */
703 command[strlen(command) - 1] = '\0';
708 #ifdef BB_FEATURE_SH_ENVIRONMENT
709 #define __MAX_INT_CHARS 7
710 static char* itoa(register int i)
712 static char a[__MAX_INT_CHARS];
713 register char *b = a + sizeof(a) - 1;
721 *--b = '0' + (i % 10);
731 static void globLastArgument(struct childProgram *prog, int *argcPtr,
734 int argc_l = *argcPtr;
735 int argcAlloced = *argcAllocedPtr;
739 char *src, *dst, *var;
741 if (argc_l > 1) { /* cmd->globResult is already initialized */
743 i = prog->globResult.gl_pathc;
749 /* do shell variable substitution */
750 if(*prog->argv[argc_l - 1] == '$') {
751 if ((var = getenv(prog->argv[argc_l - 1] + 1))) {
752 prog->argv[argc_l - 1] = var;
754 #ifdef BB_FEATURE_SH_ENVIRONMENT
756 switch(*(prog->argv[argc_l - 1] + 1)) {
758 prog->argv[argc_l - 1] = itoa(lastReturnCode);
761 prog->argv[argc_l - 1] = itoa(getpid());
764 prog->argv[argc_l - 1] = itoa(argc-1);
768 *(prog->argv[argc_l - 1])='\0';
770 prog->argv[argc_l - 1] = itoa(lastBgPid);
772 case '0':case '1':case '2':case '3':case '4':
773 case '5':case '6':case '7':case '8':case '9':
775 int index=*(prog->argv[argc_l - 1] + 1)-48;
777 *(prog->argv[argc_l - 1])='\0';
779 prog->argv[argc_l - 1] = argv[index];
788 rc = glob(prog->argv[argc_l - 1], flags, NULL, &prog->globResult);
789 if (rc == GLOB_NOSPACE) {
790 errorMsg("out of space during glob operation\n");
792 } else if (rc == GLOB_NOMATCH ||
793 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
794 strcmp(prog->argv[argc_l - 1],
795 prog->globResult.gl_pathv[i]) == 0)) {
796 /* we need to remove whatever \ quoting is still present */
797 src = dst = prog->argv[argc_l - 1];
805 argcAlloced += (prog->globResult.gl_pathc - i);
806 prog->argv = xrealloc(prog->argv, argcAlloced * sizeof(*prog->argv));
807 memcpy(prog->argv + (argc_l - 1), prog->globResult.gl_pathv + i,
808 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
809 argc_l += (prog->globResult.gl_pathc - i - 1);
812 *argcAllocedPtr = argcAlloced;
816 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
817 line). If a valid command is found, commandPtr is set to point to
818 the beginning of the next command (if the original command had more
819 then one job associated with it) or NULL if no more commands are
821 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *inBg)
824 char *returnCommand = NULL;
825 char *src, *buf, *chptr;
832 struct childProgram *prog;
834 /* skip leading white space */
835 while (**commandPtr && isspace(**commandPtr))
838 /* this handles empty lines or leading '#' characters */
839 if (!**commandPtr || (**commandPtr == '#')) {
846 job->progs = xmalloc(sizeof(*job->progs));
848 /* We set the argv elements to point inside of this string. The
849 memory is freed by freeJob(). Allocate twice the original
850 length in case we need to quote every single character.
852 Getting clean memory relieves us of the task of NULL
853 terminating things and makes the rest of this look a bit
854 cleaner (though it is, admittedly, a tad less efficient) */
855 job->cmdBuf = command = xcalloc(2*strlen(*commandPtr) + 1, sizeof(char));
859 prog->numRedirections = 0;
860 prog->redirections = NULL;
865 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
866 prog->argv[0] = job->cmdBuf;
870 while (*src && !done) {
877 errorMsg("character expected after \\\n");
882 /* in shell, "\'" should yield \' */
885 } else if (*src == '*' || *src == '?' || *src == '[' ||
886 *src == ']') *buf++ = '\\';
888 } else if (isspace(*src)) {
889 if (*prog->argv[argc_l]) {
891 /* +1 here leaves room for the NULL which ends argv */
892 if ((argc_l + 1) == argvAlloced) {
894 prog->argv = xrealloc(prog->argv,
895 sizeof(*prog->argv) *
898 globLastArgument(prog, &argc_l, &argvAlloced);
899 prog->argv[argc_l] = buf;
908 case '#': /* comment */
915 case '>': /* redirections */
917 i = prog->numRedirections++;
918 prog->redirections = xrealloc(prog->redirections,
919 sizeof(*prog->redirections) *
922 prog->redirections[i].fd = -1;
923 if (buf != prog->argv[argc_l]) {
924 /* the stuff before this character may be the file number
926 prog->redirections[i].fd =
927 strtol(prog->argv[argc_l], &chptr, 10);
929 if (*chptr && *prog->argv[argc_l]) {
931 globLastArgument(prog, &argc_l, &argvAlloced);
932 prog->argv[argc_l] = buf;
936 if (prog->redirections[i].fd == -1) {
938 prog->redirections[i].fd = 1;
940 prog->redirections[i].fd = 0;
945 prog->redirections[i].type =
946 REDIRECT_APPEND, src++;
948 prog->redirections[i].type = REDIRECT_OVERWRITE;
950 prog->redirections[i].type = REDIRECT_INPUT;
953 /* This isn't POSIX sh compliant. Oh well. */
955 while (isspace(*chptr))
959 errorMsg("file name expected after %c\n", *src);
965 prog->redirections[i].filename = buf;
966 while (*chptr && !isspace(*chptr))
969 src = chptr - 1; /* we src++ later */
970 prog->argv[argc_l] = ++buf;
974 /* finish this command */
975 if (*prog->argv[argc_l])
978 errorMsg("empty command in pipe\n");
983 prog->argv[argc_l] = NULL;
985 /* and start the next */
987 job->progs = xrealloc(job->progs,
988 sizeof(*job->progs) * job->numProgs);
989 prog = job->progs + (job->numProgs - 1);
990 prog->numRedirections = 0;
991 prog->redirections = NULL;
997 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
998 prog->argv[0] = ++buf;
1001 while (*src && isspace(*src))
1005 errorMsg("empty command in pipe\n");
1010 src--; /* we'll ++ it at the end of the loop */
1014 case '&': /* background */
1016 case ';': /* multiple commands */
1018 returnCommand = *commandPtr + (src - *commandPtr) + 1;
1021 #ifdef BB_FEATURE_SH_BACKTICKS
1023 /* Exec a backtick-ed command */
1025 char* charptr1=NULL, *charptr2;
1028 struct jobSet njobList = { NULL, NULL };
1032 ptr=strchr(++src, '`');
1034 fprintf(stderr, "Unmatched '`' in command\n");
1039 /* Make some space to hold just the backticked command */
1040 charptr1 = charptr2 = xmalloc(1+ptr-src);
1041 memcpy(charptr1, src, ptr-src);
1042 charptr1[ptr-src] = '\0';
1043 newJob = xmalloc(sizeof(struct job));
1044 /* Now parse and run the backticked command */
1045 if (!parseCommand(&charptr1, newJob, &njobList, inBg)
1046 && newJob->numProgs) {
1048 runCommand(newJob, &njobList, 0, pipefd);
1054 /* Make a copy of any stuff left over in the command
1055 * line after the second backtick */
1056 charptr2 = xmalloc(strlen(ptr)+1);
1057 memcpy(charptr2, ptr+1, strlen(ptr));
1060 /* Copy the output from the backtick-ed command into the
1061 * command line, making extra room as needed */
1063 charptr1 = xmalloc(BUFSIZ);
1064 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
1065 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
1066 if (newSize > BUFSIZ) {
1067 *commandPtr=xrealloc(*commandPtr, src - *commandPtr +
1068 size + 1 + strlen(charptr2));
1070 memcpy(src, charptr1, size);
1078 /* Now paste into the *commandPtr all the stuff
1079 * leftover after the second backtick */
1080 memcpy(src, charptr2, strlen(charptr2)+1);
1083 /* Now recursively call parseCommand to deal with the new
1084 * and improved version of the command line with the backtick
1085 * results expanded in place... */
1087 return(parseCommand(commandPtr, job, jobList, inBg));
1090 #endif // BB_FEATURE_SH_BACKTICKS
1095 errorMsg("character expected after \\\n");
1099 if (*src == '*' || *src == '[' || *src == ']'
1100 || *src == '?') *buf++ = '\\';
1109 if (*prog->argv[argc_l]) {
1111 globLastArgument(prog, &argc_l, &argvAlloced);
1117 prog->argv[argc_l] = NULL;
1119 if (!returnCommand) {
1120 job->text = xmalloc(strlen(*commandPtr) + 1);
1121 strcpy(job->text, *commandPtr);
1123 /* This leaves any trailing spaces, which is a bit sloppy */
1124 count = returnCommand - *commandPtr;
1125 job->text = xmalloc(count + 1);
1126 strncpy(job->text, *commandPtr, count);
1127 job->text[count] = '\0';
1130 *commandPtr = returnCommand;
1135 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
1139 int nextin, nextout;
1140 int pipefds[2]; /* pipefd[0] is for reading */
1141 struct builtInCommand *x;
1142 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1143 const struct BB_applet *a = applets;
1147 nextin = 0, nextout = 1;
1148 for (i = 0; i < newJob->numProgs; i++) {
1149 if ((i + 1) < newJob->numProgs) {
1151 nextout = pipefds[1];
1153 if (outPipe[1]!=-1) {
1154 nextout = outPipe[1];
1160 #ifdef BB_FEATURE_SH_ENVIRONMENT
1161 if (showXtrace==TRUE) {
1163 fprintf(stderr, "+ ");
1164 for (j = 0; newJob->progs[i].argv[j]; j++)
1165 fprintf(stderr, "%s ", newJob->progs[i].argv[j]);
1166 fprintf(stderr, "\n");
1170 /* Check if the command matches any non-forking builtins */
1171 for (x = bltins; x->cmd; x++) {
1172 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0 ) {
1173 return(x->function(newJob, jobList));
1177 if (!(newJob->progs[i].pid = fork())) {
1178 signal(SIGTTOU, SIG_DFL);
1180 if (outPipe[1]!=-1) {
1195 /* explicit redirections override pipes */
1196 setupRedirections(newJob->progs + i);
1198 /* Check if the command matches any of the other builtins */
1199 for (x = bltins_forking; x->cmd; x++) {
1200 if (strcmp(newJob->progs[i].argv[0], x->cmd) == 0) {
1202 exit (x->function(newJob, jobList));
1205 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
1206 /* Check if the command matches any busybox internal commands here */
1207 while (a->name != 0) {
1208 if (strcmp(get_last_path_component(newJob->progs[i].argv[0]), a->name) == 0) {
1210 char** argv=newJob->progs[i].argv;
1211 for(argc_l=0;*argv!=NULL; argv++, argc_l++);
1212 applet_name=a->name;
1214 exit((*(a->main)) (argc_l, newJob->progs[i].argv));
1220 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1221 fatalError("%s: %s\n", newJob->progs[i].argv[0],
1224 if (outPipe[1]!=-1) {
1228 /* put our child in the process group whose leader is the
1229 first process in this pipe */
1230 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1236 /* If there isn't another process, nextin is garbage
1237 but it doesn't matter */
1238 nextin = pipefds[0];
1241 newJob->pgrp = newJob->progs[0].pid;
1243 /* find the ID for the theJob to use */
1245 for (theJob = jobList->head; theJob; theJob = theJob->next)
1246 if (theJob->jobId >= newJob->jobId)
1247 newJob->jobId = theJob->jobId + 1;
1249 /* add the theJob to the list of running jobs */
1250 if (!jobList->head) {
1251 theJob = jobList->head = xmalloc(sizeof(*theJob));
1253 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1254 theJob->next = xmalloc(sizeof(*theJob));
1255 theJob = theJob->next;
1259 theJob->next = NULL;
1260 theJob->runningProgs = theJob->numProgs;
1261 theJob->stoppedProgs = 0;
1264 /* we don't wait for background theJobs to return -- append it
1265 to the list of backgrounded theJobs and leave it alone */
1266 printf("[%d] %d\n", theJob->jobId,
1267 newJob->progs[newJob->numProgs - 1].pid);
1268 #ifdef BB_FEATURE_SH_ENVIRONMENT
1269 lastBgPid=newJob->progs[newJob->numProgs - 1].pid;
1272 jobList->fg = theJob;
1274 /* move the new process group into the foreground */
1275 /* suppress messages when run from /linuxrc mag@sysgo.de */
1276 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1277 perror("tcsetpgrp");
1283 static int busy_loop(FILE * input)
1286 char *nextCommand = NULL;
1292 newJob.jobContext = REGULAR_JOB_CONTEXT;
1294 /* save current owner of TTY so we can restore it on exit */
1295 parent_pgrp = tcgetpgrp(0);
1297 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1299 /* don't pay any attention to this signal; it just confuses
1300 things and isn't really meant for shells anyway */
1301 signal(SIGTTOU, SIG_IGN);
1305 /* no job is in the foreground */
1307 /* see if any background processes have exited */
1308 checkJobs(&jobList);
1311 if (getCommand(input, command))
1313 nextCommand = command;
1316 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1318 int pipefds[2] = {-1,-1};
1319 runCommand(&newJob, &jobList, inBg, pipefds);
1323 command = (char *) xcalloc(BUFSIZ, sizeof(char));
1327 /* a job is running in the foreground; wait for it */
1329 while (!jobList.fg->progs[i].pid ||
1330 jobList.fg->progs[i].isStopped == 1) i++;
1332 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1334 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1335 /* the child exited */
1336 jobList.fg->runningProgs--;
1337 jobList.fg->progs[i].pid = 0;
1339 #ifdef BB_FEATURE_SH_ENVIRONMENT
1340 lastReturnCode=WEXITSTATUS(status);
1343 printf("'%s' exited -- return code %d\n", jobList.fg->text, lastReturnCode);
1345 if (!jobList.fg->runningProgs) {
1348 removeJob(&jobList, jobList.fg);
1352 /* the child was stopped */
1353 jobList.fg->stoppedProgs++;
1354 jobList.fg->progs[i].isStopped = 1;
1356 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1357 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1358 "Stopped", jobList.fg->text);
1364 /* move the shell to the foreground */
1365 /* suppress messages when run from /linuxrc mag@sysgo.de */
1366 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1367 perror("tcsetpgrp");
1373 /* return controlling TTY back to parent process group before exiting */
1374 if (tcsetpgrp(0, parent_pgrp))
1375 perror("tcsetpgrp");
1377 /* return exit status if called with "-c" */
1378 if (input == NULL && WIFEXITED(status))
1379 return WEXITSTATUS(status);
1385 #ifdef BB_FEATURE_CLEAN_UP
1386 void free_memory(void)
1392 if (local_pending_command)
1393 free(local_pending_command);
1395 if (jobList.fg && !jobList.fg->runningProgs) {
1396 removeJob(&jobList, jobList.fg);
1402 int shell_main(int argc_l, char **argv_l)
1404 int opt, interactive=FALSE;
1405 FILE *input = stdin;
1410 //if (argv[0] && argv[0][0] == '-') {
1411 // builtin_source("/etc/profile");
1414 while ((opt = getopt(argc_l, argv_l, "cx")) > 0) {
1418 if (local_pending_command != 0)
1419 fatalError("multiple -c arguments\n");
1420 local_pending_command = xstrdup(argv[optind]);
1424 #ifdef BB_FEATURE_SH_ENVIRONMENT
1436 /* A shell is interactive if the `-i' flag was given, or if all of
1437 * the following conditions are met:
1439 * no arguments remaining or the -s flag given
1440 * standard input is a terminal
1441 * standard output is a terminal
1442 * Refer to Posix.2, the description of the `sh' utility. */
1443 if (interactive==TRUE || ( argv[optind]==NULL && input==stdin && isatty(fileno(stdin)) && isatty(fileno(stdout)))) {
1444 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1445 /* Looks like they want an interactive shell */
1446 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1447 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1448 } else if (local_pending_command==NULL) {
1449 //fprintf(stdout, "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1450 input = xfopen(argv[optind], "r");
1453 /* initialize the cwd -- this is never freed...*/
1454 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1455 getcwd(cwd, sizeof(char)*MAX_LINE);
1457 #ifdef BB_FEATURE_CLEAN_UP
1458 atexit(free_memory);
1461 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1465 return (busy_loop(input));