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
43 #include <sys/ioctl.h>
46 #ifdef BB_FEATURE_SH_COMMAND_EDITING
50 #define MAX_LINE 256 /* size of input buffer for `read' builtin */
51 #define MAX_READ 128 /* size of input buffer for `read' builtin */
52 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
55 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
59 #define REGULAR_JOB_CONTEXT 0x1
60 #define IF_EXP_CONTEXT 0x2
61 #define THEN_EXP_CONTEXT 0x4
62 #define ELSE_EXP_CONTEXT 0x8
64 enum jobContext { REGULAR_APP, IF_CONTEXT, THEN_CONTEXT
68 struct job *head; /* head of list of running jobs */
69 struct job *fg; /* current foreground job */
72 struct redirectionSpecifier {
73 enum redirectionType type; /* type of redirection */
74 int fd; /* file descriptor being redirected */
75 char *filename; /* file to redirect fd to */
79 pid_t pid; /* 0 if exited */
80 char **argv; /* program name and arguments */
81 int numRedirections; /* elements in redirection array */
82 struct redirectionSpecifier *redirections; /* I/O redirections */
83 glob_t globResult; /* result of parameter globbing */
84 int freeGlob; /* should we globfree(&globResult)? */
85 int isStopped; /* is the program currently running? */
89 int jobId; /* job number */
90 int numProgs; /* total number of programs in job */
91 int runningProgs; /* number of programs running */
92 char *text; /* name of job */
93 char *cmdBuf; /* buffer various argv's point into */
94 pid_t pgrp; /* process group ID for the job */
95 struct childProgram *progs; /* array of programs in job */
96 struct job *next; /* to track background commands */
97 int stoppedProgs; /* number of programs alive, but stopped */
98 int jobContext; /* bitmask defining current context */
101 struct builtInCommand {
102 char *cmd; /* name */
103 char *descr; /* description */
104 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
107 /* function prototypes for builtins */
108 static int builtin_cd(struct job *cmd, struct jobSet *junk);
109 static int builtin_env(struct job *dummy, struct jobSet *junk);
110 static int builtin_exit(struct job *cmd, struct jobSet *junk);
111 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
112 static int builtin_help(struct job *cmd, struct jobSet *junk);
113 static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
114 static int builtin_pwd(struct job *dummy, struct jobSet *junk);
115 static int builtin_export(struct job *cmd, struct jobSet *junk);
116 static int builtin_source(struct job *cmd, struct jobSet *jobList);
117 static int builtin_unset(struct job *cmd, struct jobSet *junk);
118 static int builtin_read(struct job *cmd, struct jobSet *junk);
119 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
120 static int builtin_if(struct job *cmd, struct jobSet *junk);
121 static int builtin_then(struct job *cmd, struct jobSet *junk);
122 static int builtin_else(struct job *cmd, struct jobSet *junk);
123 static int builtin_fi(struct job *cmd, struct jobSet *junk);
127 /* function prototypes for shell stuff */
128 static void checkJobs(struct jobSet *jobList);
129 static int getCommand(FILE * source, char *command);
130 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
131 static int setupRedirections(struct childProgram *prog);
132 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
133 static int busy_loop(FILE * input);
136 /* Table of built-in functions (these are non-forking builtins, meaning they
137 * can change global variables in the parent shell process but they will not
138 * work with pipes and redirects; 'unset foo | whatever' will not work) */
139 static struct builtInCommand bltins[] = {
140 {"bg", "Resume a job in the background", builtin_fg_bg},
141 {"cd", "Change working directory", builtin_cd},
142 {"exit", "Exit from shell()", builtin_exit},
143 {"fg", "Bring job into the foreground", builtin_fg_bg},
144 {"jobs", "Lists the active jobs", builtin_jobs},
145 {"export", "Set environment variable", builtin_export},
146 {"unset", "Unset environment variable", builtin_unset},
147 {"read", "Input environment variable", builtin_read},
151 /* Table of forking built-in functions (things that fork cannot change global
152 * variables in the parent process, such as the current working directory) */
153 static struct builtInCommand bltins_forking[] = {
154 {"env", "Print all environment variables", builtin_env},
155 {"pwd", "Print current directory", builtin_pwd},
156 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
157 {"if", NULL, builtin_if},
158 {"then", NULL, builtin_then},
159 {"else", NULL, builtin_else},
160 {"fi", NULL, builtin_fi},
162 {".", "Source-in and run commands in a file", builtin_source},
163 {"help", "List shell built-in commands", builtin_help},
167 static char *prompt = "# ";
169 static char *local_pending_command = NULL;
170 static char *promptStr = NULL;
171 static struct jobSet jobList = { NULL, NULL };
173 #ifdef BB_FEATURE_SH_COMMAND_EDITING
174 void win_changed(int junk)
176 struct winsize win = { 0, 0, 0, 0 };
177 ioctl(0, TIOCGWINSZ, &win);
178 if (win.ws_col > 0) {
179 cmdedit_setwidth( win.ws_col - 1);
185 /* built-in 'cd <path>' handler */
186 static int builtin_cd(struct job *cmd, struct jobSet *junk)
190 if (!cmd->progs[0].argv[1] == 1)
191 newdir = getenv("HOME");
193 newdir = cmd->progs[0].argv[1];
195 printf("cd: %s: %s\n", newdir, strerror(errno));
198 getcwd(cwd, sizeof(char)*MAX_LINE);
203 /* built-in 'env' handler */
204 static int builtin_env(struct job *dummy, struct jobSet *junk)
208 for (e = environ; *e; e++) {
209 fprintf(stdout, "%s\n", *e);
214 /* built-in 'exit' handler */
215 static int builtin_exit(struct job *cmd, struct jobSet *junk)
217 if (!cmd->progs[0].argv[1] == 1)
220 return(atoi(cmd->progs[0].argv[1]));
223 /* built-in 'fg' and 'bg' handler */
224 static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
227 struct job *job=NULL;
229 if (!jobList->head) {
230 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
231 errorMsg("%s: exactly one argument is expected\n",
232 cmd->progs[0].argv[0]);
235 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
236 errorMsg("%s: bad argument '%s'\n",
237 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
239 for (job = jobList->head; job; job = job->next) {
240 if (job->jobId == jobNum) {
250 errorMsg("%s: unknown job %d\n",
251 cmd->progs[0].argv[0], jobNum);
255 if (*cmd->progs[0].argv[0] == 'f') {
256 /* Make this job the foreground job */
257 /* suppress messages when run from /linuxrc mag@sysgo.de */
258 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
263 /* Restart the processes in the job */
264 for (i = 0; i < job->numProgs; i++)
265 job->progs[i].isStopped = 0;
267 kill(-job->pgrp, SIGCONT);
269 job->stoppedProgs = 0;
274 /* built-in 'help' handler */
275 static int builtin_help(struct job *dummy, struct jobSet *junk)
277 struct builtInCommand *x;
279 fprintf(stdout, "\nBuilt-in commands:\n");
280 fprintf(stdout, "-------------------\n");
281 for (x = bltins; x->cmd; x++) {
284 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
286 for (x = bltins_forking; x->cmd; x++) {
289 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
291 fprintf(stdout, "\n\n");
295 /* built-in 'jobs' handler */
296 static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
301 for (job = jobList->head; job; job = job->next) {
302 if (job->runningProgs == job->stoppedProgs)
303 statusString = "Stopped";
305 statusString = "Running";
307 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
313 /* built-in 'pwd' handler */
314 static int builtin_pwd(struct job *dummy, struct jobSet *junk)
316 getcwd(cwd, sizeof(char)*MAX_LINE);
317 fprintf(stdout, "%s\n", cwd);
321 /* built-in 'export VAR=value' handler */
322 static int builtin_export(struct job *cmd, struct jobSet *junk)
326 if (!cmd->progs[0].argv[1] == 1) {
327 return (builtin_env(cmd, junk));
329 res = putenv(cmd->progs[0].argv[1]);
331 fprintf(stdout, "export: %s\n", strerror(errno));
335 /* built-in 'read VAR' handler */
336 static int builtin_read(struct job *cmd, struct jobSet *junk)
338 int res = 0, len, newlen;
340 char string[MAX_READ];
342 if (cmd->progs[0].argv[1]) {
343 /* argument (VAR) given: put "VAR=" into buffer */
344 strcpy(string, cmd->progs[0].argv[1]);
345 len = strlen(string);
348 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
349 newlen = strlen(string);
351 string[--newlen] = '\0'; /* chomp trailing newline */
353 ** string should now contain "VAR=<value>"
354 ** copy it (putenv() won't do that, so we must make sure
355 ** the string resides in a static buffer!)
358 if((s = strdup(string)))
361 fprintf(stdout, "read: %s\n", strerror(errno));
364 fgets(string, sizeof(string), stdin);
369 #ifdef BB_FEATURE_SH_IF_EXPRESSIONS
370 /* Built-in handler for 'if' commands */
371 static int builtin_if(struct job *cmd, struct jobSet *junk)
373 cmd->jobContext |= IF_EXP_CONTEXT;
374 printf("Hit an if -- jobContext=%d\n", cmd->jobContext);
378 /* Built-in handler for 'then' (part of the 'if' command) */
379 static int builtin_then(struct job *cmd, struct jobSet *junk)
381 if (cmd->jobContext & IF_EXP_CONTEXT) {
382 fprintf(stderr, "unexpected token `then'\n");
386 cmd->jobContext |= THEN_EXP_CONTEXT;
387 printf("Hit an then -- jobContext=%d\n", cmd->jobContext);
391 /* Built-in handler for 'else' (part of the 'if' command) */
392 static int builtin_else(struct job *cmd, struct jobSet *junk)
394 printf("Hit an else\n");
395 cmd->jobContext |= ELSE_EXP_CONTEXT;
399 /* Built-in handler for 'fi' (part of the 'if' command) */
400 static int builtin_fi(struct job *cmd, struct jobSet *junk)
402 printf("Hit an fi\n");
407 /* Built-in '.' handler (read-in and execute commands from file) */
408 static int builtin_source(struct job *cmd, struct jobSet *junk)
413 if (!cmd->progs[0].argv[1] == 1)
416 input = fopen(cmd->progs[0].argv[1], "r");
418 fprintf(stdout, "Couldn't open file '%s'\n",
419 cmd->progs[0].argv[1]);
423 /* Now run the file */
424 status = busy_loop(input);
428 /* built-in 'unset VAR' handler */
429 static int builtin_unset(struct job *cmd, struct jobSet *junk)
431 if (!cmd->progs[0].argv[1] == 1) {
432 fprintf(stdout, "unset: parameter required.\n");
435 unsetenv(cmd->progs[0].argv[1]);
439 /* free up all memory from a job */
440 static void freeJob(struct job *cmd)
444 for (i = 0; i < cmd->numProgs; i++) {
445 free(cmd->progs[i].argv);
446 if (cmd->progs[i].redirections)
447 free(cmd->progs[i].redirections);
448 if (cmd->progs[i].freeGlob)
449 globfree(&cmd->progs[i].globResult);
455 memset(cmd, 0, sizeof(struct job));
458 /* remove a job from the jobList */
459 static void removeJob(struct jobSet *jobList, struct job *job)
464 if (job == jobList->head) {
465 jobList->head = job->next;
467 prevJob = jobList->head;
468 while (prevJob->next != job)
469 prevJob = prevJob->next;
470 prevJob->next = job->next;
476 /* Checks to see if any background processes have exited -- if they
477 have, figure out why and see if a job has completed */
478 static void checkJobs(struct jobSet *jobList)
485 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
486 for (job = jobList->head; job; job = job->next) {
488 while (progNum < job->numProgs &&
489 job->progs[progNum].pid != childpid) progNum++;
490 if (progNum < job->numProgs)
494 /* This happens on backticked commands */
498 if (WIFEXITED(status) || WIFSIGNALED(status)) {
501 job->progs[progNum].pid = 0;
503 if (!job->runningProgs) {
504 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
505 removeJob(jobList, job);
510 job->progs[progNum].isStopped = 1;
512 if (job->stoppedProgs == job->numProgs) {
513 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
519 if (childpid == -1 && errno != ECHILD)
523 static int getCommand(FILE * source, char *command)
525 if (source == NULL) {
526 if (local_pending_command) {
527 /* a command specified (-c option): return it & mark it done */
528 strcpy(command, local_pending_command);
529 free(local_pending_command);
530 local_pending_command = NULL;
536 if (source == stdin) {
537 #ifdef BB_FEATURE_SH_COMMAND_EDITING
539 len=fprintf(stdout, "%s %s", cwd, prompt);
541 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
542 sprintf(promptStr, "%s %s", cwd, prompt);
543 cmdedit_read_input(promptStr, command);
547 fprintf(stdout, "%s %s", cwd, prompt);
552 if (!fgets(command, BUFSIZ - 2, source)) {
558 /* remove trailing newline */
559 command[strlen(command) - 1] = '\0';
564 static void globLastArgument(struct childProgram *prog, int *argcPtr,
568 int argcAlloced = *argcAllocedPtr;
572 char *src, *dst, *var;
574 if (argc > 1) { /* cmd->globResult is already initialized */
576 i = prog->globResult.gl_pathc;
582 /* do shell variable substitution */
583 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
584 prog->argv[argc - 1] = var;
586 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
587 if (rc == GLOB_NOSPACE) {
588 errorMsg("out of space during glob operation\n");
590 } else if (rc == GLOB_NOMATCH ||
591 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
592 !strcmp(prog->argv[argc - 1],
593 prog->globResult.gl_pathv[i]))) {
594 /* we need to remove whatever \ quoting is still present */
595 src = dst = prog->argv[argc - 1];
603 argcAlloced += (prog->globResult.gl_pathc - i);
605 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
606 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
607 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
608 argc += (prog->globResult.gl_pathc - i - 1);
611 *argcAllocedPtr = argcAlloced;
615 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
616 line). If a valid command is found, commandPtr is set to point to
617 the beginning of the next command (if the original command had more
618 then one job associated with it) or NULL if no more commands are
620 static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
623 char *returnCommand = NULL;
624 char *src, *buf, *chptr;
631 struct childProgram *prog;
633 /* skip leading white space */
634 while (**commandPtr && isspace(**commandPtr))
637 /* this handles empty lines or leading '#' characters */
638 if (!**commandPtr || (**commandPtr == '#')) {
645 job->progs = xmalloc(sizeof(*job->progs));
647 /* We set the argv elements to point inside of this string. The
648 memory is freed by freeJob(). Allocate twice the original
649 length in case we need to quote every single character.
651 Getting clean memory relieves us of the task of NULL
652 terminating things and makes the rest of this look a bit
653 cleaner (though it is, admittedly, a tad less efficient) */
654 job->cmdBuf = command = calloc(2*strlen(*commandPtr) + 1, sizeof(char));
656 job->jobContext = REGULAR_JOB_CONTEXT;
659 prog->numRedirections = 0;
660 prog->redirections = NULL;
665 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
666 prog->argv[0] = job->cmdBuf;
670 while (*src && !done) {
677 errorMsg("character expected after \\\n");
682 /* in shell, "\'" should yield \' */
685 } else if (*src == '*' || *src == '?' || *src == '[' ||
686 *src == ']') *buf++ = '\\';
688 } else if (isspace(*src)) {
689 if (*prog->argv[argc]) {
691 /* +1 here leaves room for the NULL which ends argv */
692 if ((argc + 1) == argvAlloced) {
694 prog->argv = realloc(prog->argv,
695 sizeof(*prog->argv) *
698 globLastArgument(prog, &argc, &argvAlloced);
699 prog->argv[argc] = buf;
708 case '#': /* comment */
712 case '>': /* redirections */
714 i = prog->numRedirections++;
715 prog->redirections = realloc(prog->redirections,
716 sizeof(*prog->redirections) *
719 prog->redirections[i].fd = -1;
720 if (buf != prog->argv[argc]) {
721 /* the stuff before this character may be the file number
723 prog->redirections[i].fd =
724 strtol(prog->argv[argc], &chptr, 10);
726 if (*chptr && *prog->argv[argc]) {
728 globLastArgument(prog, &argc, &argvAlloced);
729 prog->argv[argc] = buf;
733 if (prog->redirections[i].fd == -1) {
735 prog->redirections[i].fd = 1;
737 prog->redirections[i].fd = 0;
742 prog->redirections[i].type =
743 REDIRECT_APPEND, src++;
745 prog->redirections[i].type = REDIRECT_OVERWRITE;
747 prog->redirections[i].type = REDIRECT_INPUT;
750 /* This isn't POSIX sh compliant. Oh well. */
752 while (isspace(*chptr))
756 errorMsg("file name expected after %c\n", *src);
762 prog->redirections[i].filename = buf;
763 while (*chptr && !isspace(*chptr))
766 src = chptr - 1; /* we src++ later */
767 prog->argv[argc] = ++buf;
771 /* finish this command */
772 if (*prog->argv[argc])
775 errorMsg("empty command in pipe\n");
780 prog->argv[argc] = NULL;
782 /* and start the next */
784 job->progs = realloc(job->progs,
785 sizeof(*job->progs) * job->numProgs);
786 prog = job->progs + (job->numProgs - 1);
787 prog->numRedirections = 0;
788 prog->redirections = NULL;
793 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
794 prog->argv[0] = ++buf;
797 while (*src && isspace(*src))
801 errorMsg("empty command in pipe\n");
806 src--; /* we'll ++ it at the end of the loop */
810 case '&': /* background */
812 case ';': /* multiple commands */
814 returnCommand = *commandPtr + (src - *commandPtr) + 1;
820 errorMsg("character expected after \\\n");
824 if (*src == '*' || *src == '[' || *src == ']'
825 || *src == '?') *buf++ = '\\';
827 #ifdef BB_FEATURE_SH_BACKTICKS
829 /* Exec a backtick-ed command */
831 char* charptr1=NULL, *charptr2;
834 struct jobSet njobList = { NULL, NULL };
838 ptr=strchr(++src, '`');
840 fprintf(stderr, "Unmatched '`' in command\n");
845 /* Make some space to hold just the backticked command */
846 charptr1 = charptr2 = xmalloc(1+ptr-src);
847 snprintf(charptr1, 1+ptr-src, src);
848 newJob = xmalloc(sizeof(struct job));
849 /* Now parse and run the backticked command */
850 if (!parseCommand(&charptr1, newJob, &njobList, isBg)
851 && newJob->numProgs) {
853 runCommand(newJob, &njobList, 0, pipefd);
859 /* Make a copy of any stuff left over in the command
860 * line after the second backtick */
861 charptr2 = xmalloc(strlen(ptr)+1);
862 memcpy(charptr2, ptr+1, strlen(ptr));
865 /* Copy the output from the backtick-ed command into the
866 * command line, making extra room as needed */
868 charptr1 = xmalloc(BUFSIZ);
869 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
870 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
871 if (newSize > BUFSIZ) {
872 *commandPtr=realloc(*commandPtr, src - *commandPtr +
873 size + 1 + strlen(charptr2));
875 memcpy(src, charptr1, size);
883 /* Now paste into the *commandPtr all the stuff
884 * leftover after the second backtick */
885 memcpy(src, charptr2, strlen(charptr2));
888 /* Now recursively call parseCommand to deal with the new
889 * and improved version of the command line with the backtick
890 * results expanded in place... */
892 return(parseCommand(commandPtr, job, jobList, isBg));
895 #endif // BB_FEATURE_SH_BACKTICKS
903 if (*prog->argv[argc]) {
905 globLastArgument(prog, &argc, &argvAlloced);
911 prog->argv[argc] = NULL;
913 if (!returnCommand) {
914 job->text = xmalloc(strlen(*commandPtr) + 1);
915 strcpy(job->text, *commandPtr);
917 /* This leaves any trailing spaces, which is a bit sloppy */
918 count = returnCommand - *commandPtr;
919 job->text = xmalloc(count + 1);
920 strncpy(job->text, *commandPtr, count);
921 job->text[count] = '\0';
924 *commandPtr = returnCommand;
929 static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
934 int pipefds[2]; /* pipefd[0] is for reading */
935 struct builtInCommand *x;
936 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
937 const struct BB_applet *a = applets;
941 nextin = 0, nextout = 1;
942 for (i = 0; i < newJob->numProgs; i++) {
943 if ((i + 1) < newJob->numProgs) {
945 nextout = pipefds[1];
947 if (outPipe[1]!=-1) {
948 nextout = outPipe[1];
954 /* Check if the command matches any non-forking builtins */
955 for (x = bltins; x->cmd; x++) {
956 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
957 return (x->function(newJob, jobList));
961 if (!(newJob->progs[i].pid = fork())) {
962 signal(SIGTTOU, SIG_DFL);
964 if (outPipe[1]!=-1) {
978 /* explicit redirections override pipes */
979 setupRedirections(newJob->progs + i);
981 /* Check if the command matches any of the other builtins */
982 for (x = bltins_forking; x->cmd; x++) {
983 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
984 exit (x->function(newJob, jobList));
987 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
988 /* Check if the command matches any busybox internal commands here */
989 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
990 * works, but '/bin/cat' doesn't ) */
991 while (a->name != 0) {
992 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
994 char** argv=newJob->progs[i].argv;
995 for(argc=0;*argv!=NULL; argv++, argc++);
996 exit((*(a->main)) (argc, newJob->progs[i].argv));
1002 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
1003 fatalError("sh: %s: %s\n", newJob->progs[i].argv[0],
1006 if (outPipe[1]!=-1) {
1010 /* put our child in the process group whose leader is the
1011 first process in this pipe */
1012 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
1018 /* If there isn't another process, nextin is garbage
1019 but it doesn't matter */
1020 nextin = pipefds[0];
1023 newJob->pgrp = newJob->progs[0].pid;
1025 /* find the ID for the theJob to use */
1027 for (theJob = jobList->head; theJob; theJob = theJob->next)
1028 if (theJob->jobId >= newJob->jobId)
1029 newJob->jobId = theJob->jobId + 1;
1031 /* add the theJob to the list of running jobs */
1032 if (!jobList->head) {
1033 theJob = jobList->head = malloc(sizeof(*theJob));
1035 for (theJob = jobList->head; theJob->next; theJob = theJob->next);
1036 theJob->next = malloc(sizeof(*theJob));
1037 theJob = theJob->next;
1041 theJob->next = NULL;
1042 theJob->runningProgs = theJob->numProgs;
1043 theJob->stoppedProgs = 0;
1046 /* we don't wait for background theJobs to return -- append it
1047 to the list of backgrounded theJobs and leave it alone */
1048 printf("[%d] %d\n", theJob->jobId,
1049 newJob->progs[newJob->numProgs - 1].pid);
1051 jobList->fg = theJob;
1053 /* move the new process group into the foreground */
1054 /* suppress messages when run from /linuxrc mag@sysgo.de */
1055 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
1056 perror("tcsetpgrp");
1062 static int setupRedirections(struct childProgram *prog)
1066 int mode = O_RDONLY;
1067 struct redirectionSpecifier *redir = prog->redirections;
1069 for (i = 0; i < prog->numRedirections; i++, redir++) {
1070 switch (redir->type) {
1071 case REDIRECT_INPUT:
1074 case REDIRECT_OVERWRITE:
1075 mode = O_RDWR | O_CREAT | O_TRUNC;
1077 case REDIRECT_APPEND:
1078 mode = O_RDWR | O_CREAT | O_APPEND;
1082 openfd = open(redir->filename, mode, 0666);
1084 /* this could get lost if stderr has been redirected, but
1085 bash and ash both lose it as well (though zsh doesn't!) */
1086 errorMsg("error opening %s: %s\n", redir->filename,
1091 if (openfd != redir->fd) {
1092 dup2(openfd, redir->fd);
1101 static int busy_loop(FILE * input)
1104 char *nextCommand = NULL;
1111 /* save current owner of TTY so we can restore it on exit */
1112 parent_pgrp = tcgetpgrp(0);
1114 command = (char *) calloc(BUFSIZ, sizeof(char));
1116 /* don't pay any attention to this signal; it just confuses
1117 things and isn't really meant for shells anyway */
1118 signal(SIGTTOU, SIG_IGN);
1122 /* no job is in the foreground */
1124 /* see if any background processes have exited */
1125 checkJobs(&jobList);
1128 if (getCommand(input, command))
1130 nextCommand = command;
1133 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
1135 int pipefds[2] = {-1,-1};
1136 runCommand(&newJob, &jobList, inBg, pipefds);
1140 command = (char *) calloc(BUFSIZ, sizeof(char));
1144 /* a job is running in the foreground; wait for it */
1146 while (!jobList.fg->progs[i].pid ||
1147 jobList.fg->progs[i].isStopped == 1) i++;
1149 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
1151 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1152 /* the child exited */
1153 jobList.fg->runningProgs--;
1154 jobList.fg->progs[i].pid = 0;
1156 if (!jobList.fg->runningProgs) {
1159 removeJob(&jobList, jobList.fg);
1163 /* the child was stopped */
1164 jobList.fg->stoppedProgs++;
1165 jobList.fg->progs[i].isStopped = 1;
1167 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1168 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1169 "Stopped", jobList.fg->text);
1175 /* move the shell to the foreground */
1176 /* suppress messages when run from /linuxrc mag@sysgo.de */
1177 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1178 perror("tcsetpgrp");
1184 /* return controlling TTY back to parent process group before exiting */
1185 if (tcsetpgrp(0, parent_pgrp))
1186 perror("tcsetpgrp");
1188 /* return exit status if called with "-c" */
1189 if (input == NULL && WIFEXITED(status))
1190 return WEXITSTATUS(status);
1196 #ifdef BB_FEATURE_CLEAN_UP
1197 void free_memory(void)
1203 if (local_pending_command)
1204 free(local_pending_command);
1206 if (jobList.fg && !jobList.fg->runningProgs) {
1207 removeJob(&jobList, jobList.fg);
1213 int shell_main(int argc, char **argv)
1215 FILE *input = stdin;
1217 /* initialize the cwd -- this is never freed...*/
1218 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1219 getcwd(cwd, sizeof(char)*MAX_LINE);
1221 #ifdef BB_FEATURE_CLEAN_UP
1222 atexit(free_memory);
1225 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1227 signal(SIGWINCH, win_changed);
1231 //if (argv[0] && argv[0][0] == '-') {
1232 // builtin_source("/etc/profile");
1237 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1238 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1240 if (argv[1][0]=='-' && argv[1][1]=='c') {
1242 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1243 if (local_pending_command == 0) {
1244 fatalError("out of memory\n");
1246 for(i=2; i<argc; i++)
1248 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1249 local_pending_command = realloc(local_pending_command,
1250 strlen(local_pending_command) + strlen(argv[i]));
1251 if (local_pending_command==NULL)
1252 fatalError("commands for -c option too long\n");
1254 strcat(local_pending_command, argv[i]);
1255 if ( (i + 1) < argc)
1256 strcat(local_pending_command, " ");
1261 else if (argv[1][0]=='-') {
1265 input = fopen(argv[1], "r");
1267 fatalError("Couldn't open file '%s': %s\n", argv[1],
1273 return (busy_loop(input));