1 /* vi: set sw=4 ts=4: */
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
37 #include <sys/ioctl.h>
42 #ifdef BB_FEATURE_SH_COMMAND_EDITING
46 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
49 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
53 struct job *head; /* head of list of running jobs */
54 struct job *fg; /* current foreground job */
57 struct redirectionSpecifier {
58 enum redirectionType type; /* type of redirection */
59 int fd; /* file descriptor being redirected */
60 char *filename; /* file to redirect fd to */
64 pid_t pid; /* 0 if exited */
65 char **argv; /* program name and arguments */
66 int numRedirections; /* elements in redirection array */
67 struct redirectionSpecifier *redirections; /* I/O redirections */
68 glob_t globResult; /* result of parameter globbing */
69 int freeGlob; /* should we globfree(&globResult)? */
70 int isStopped; /* is the program currently running? */
74 int jobId; /* job number */
75 int numProgs; /* total number of programs in job */
76 int runningProgs; /* number of programs running */
77 char *text; /* name of job */
78 char *cmdBuf; /* buffer various argv's point into */
79 pid_t pgrp; /* process group ID for the job */
80 struct childProgram *progs; /* array of programs in job */
81 struct job *next; /* to track background commands */
82 int stoppedProgs; /* number of programs alive, but stopped */
85 struct builtInCommand {
87 char *descr; /* description */
88 char *usage; /* usage */
89 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
92 /* Some function prototypes */
93 static int shell_cd(struct job *cmd, struct jobSet *junk);
94 static int shell_env(struct job *dummy, struct jobSet *junk);
95 static int shell_exit(struct job *cmd, struct jobSet *junk);
96 static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
97 static int shell_help(struct job *cmd, struct jobSet *junk);
98 static int shell_jobs(struct job *dummy, struct jobSet *jobList);
99 static int shell_pwd(struct job *dummy, struct jobSet *junk);
100 static int shell_set(struct job *cmd, struct jobSet *junk);
101 static int shell_source(struct job *cmd, struct jobSet *jobList);
102 static int shell_unset(struct job *cmd, struct jobSet *junk);
104 static void checkJobs(struct jobSet *jobList);
105 static int getCommand(FILE * source, char *command);
106 static int parseCommand(char **commandPtr, struct job *job, int *isBg);
107 static int setupRedirections(struct childProgram *prog);
108 static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
109 static int busy_loop(FILE * input);
112 /* Table of built-in functions */
113 static struct builtInCommand bltins[] = {
114 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
115 {"cd", "Change working directory", "cd [dir]", shell_cd},
116 //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
117 {"env", "Print all environment variables", "env", shell_env},
118 {"exit", "Exit from shell()", "exit", shell_exit},
119 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
120 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
121 {"pwd", "Print current directory", "pwd", shell_pwd},
122 {"set", "Set environment variable", "set [VAR=value]", shell_set},
123 {"unset", "Unset environment variable", "unset VAR", shell_unset},
124 {".", "Source-in and run commands in a file", ". filename",
126 {"help", "List shell built-in commands", "help", shell_help},
127 {NULL, NULL, NULL, NULL}
130 static const char shell_usage[] =
131 "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
134 static char cwd[1024];
135 static char *prompt = "# ";
139 /* built-in 'cd <path>' handler */
140 static int shell_cd(struct job *cmd, struct jobSet *junk)
143 if (!cmd->progs[0].argv[1] == 1)
144 newdir = getenv("HOME");
146 newdir = cmd->progs[0].argv[1];
148 printf("cd: %s: %s\n", newdir, strerror(errno));
151 getcwd(cwd, sizeof(cwd));
156 /* built-in 'env' handler */
157 static int shell_env(struct job *dummy, struct jobSet *junk)
161 for (e = environ; *e; e++) {
162 fprintf(stdout, "%s\n", *e);
167 /* built-in 'exit' handler */
168 static int shell_exit(struct job *cmd, struct jobSet *junk)
170 if (!cmd->progs[0].argv[1] == 1)
173 exit(atoi(cmd->progs[0].argv[1]));
176 /* built-in 'fg' and 'bg' handler */
177 static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
182 if (!jobList->head) {
183 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
184 fprintf(stderr, "%s: exactly one argument is expected\n",
185 cmd->progs[0].argv[0]);
188 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
189 fprintf(stderr, "%s: bad argument '%s'\n",
190 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
197 for (job = jobList->head; job; job = job->next)
198 if (job->jobId == jobNum)
202 fprintf(stderr, "%s: unknown job %d\n",
203 cmd->progs[0].argv[0], jobNum);
207 if (*cmd->progs[0].argv[0] == 'f') {
208 /* Make this job the foreground job */
210 if (tcsetpgrp(0, job->pgrp))
215 /* Restart the processes in the job */
216 for (i = 0; i < job->numProgs; i++)
217 job->progs[i].isStopped = 0;
219 kill(-job->pgrp, SIGCONT);
221 job->stoppedProgs = 0;
226 /* built-in 'help' handler */
227 static int shell_help(struct job *cmd, struct jobSet *junk)
229 struct builtInCommand *x;
231 fprintf(stdout, "\nBuilt-in commands:\n");
232 fprintf(stdout, "-------------------\n");
233 for (x = bltins; x->cmd; x++) {
234 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
236 fprintf(stdout, "\n\n");
240 /* built-in 'jobs' handler */
241 static int shell_jobs(struct job *dummy, struct jobSet *jobList)
245 for (job = jobList->head; job; job = job->next) {
246 if (job->runningProgs == job->stoppedProgs)
247 statusString = "Stopped";
249 statusString = "Running";
251 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
257 /* built-in 'pwd' handler */
258 static int shell_pwd(struct job *dummy, struct jobSet *junk)
260 getcwd(cwd, sizeof(cwd));
261 fprintf(stdout, "%s\n", cwd);
265 /* built-in 'set VAR=value' handler */
266 static int shell_set(struct job *cmd, struct jobSet *junk)
270 if (!cmd->progs[0].argv[1] == 1) {
271 return (shell_env(cmd, junk));
273 res = putenv(cmd->progs[0].argv[1]);
275 fprintf(stdout, "set: %s\n", strerror(errno));
279 /* Built-in '.' handler (read-in and execute commands from file) */
280 static int shell_source(struct job *cmd, struct jobSet *junk)
285 if (!cmd->progs[0].argv[1] == 1)
288 input = fopen(cmd->progs[0].argv[1], "r");
290 fprintf(stdout, "Couldn't open file '%s'\n",
291 cmd->progs[0].argv[1]);
295 /* Now run the file */
296 status = busy_loop(input);
300 /* built-in 'unset VAR' handler */
301 static int shell_unset(struct job *cmd, struct jobSet *junk)
303 if (!cmd->progs[0].argv[1] == 1) {
304 fprintf(stdout, "unset: parameter required.\n");
307 unsetenv(cmd->progs[0].argv[1]);
311 /* free up all memory from a job */
312 static void freeJob(struct job *cmd)
316 for (i = 0; i < cmd->numProgs; i++) {
317 free(cmd->progs[i].argv);
318 if (cmd->progs[i].redirections)
319 free(cmd->progs[i].redirections);
320 if (cmd->progs[i].freeGlob)
321 globfree(&cmd->progs[i].globResult);
329 /* remove a job from the jobList */
330 static void removeJob(struct jobSet *jobList, struct job *job)
335 if (job == jobList->head) {
336 jobList->head = job->next;
338 prevJob = jobList->head;
339 while (prevJob->next != job)
340 prevJob = prevJob->next;
341 prevJob->next = job->next;
347 /* Checks to see if any background processes have exited -- if they
348 have, figure out why and see if a job has completed */
349 static void checkJobs(struct jobSet *jobList)
356 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
357 for (job = jobList->head; job; job = job->next) {
359 while (progNum < job->numProgs &&
360 job->progs[progNum].pid != childpid) progNum++;
361 if (progNum < job->numProgs)
365 if (WIFEXITED(status) || WIFSIGNALED(status)) {
368 job->progs[progNum].pid = 0;
370 if (!job->runningProgs) {
371 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
372 removeJob(jobList, job);
377 job->progs[progNum].isStopped = 1;
379 if (job->stoppedProgs == job->numProgs) {
380 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
386 if (childpid == -1 && errno != ECHILD)
390 static int getCommand(FILE * source, char *command)
392 if (source == stdin) {
393 fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
395 #ifdef BB_FEATURE_SH_COMMAND_EDITING
396 cmdedit_read_input(fileno(stdin), fileno(stdout), command);
401 if (!fgets(command, BUFSIZ - 2, source)) {
407 /* remove trailing newline */
408 command[strlen(command) - 1] = '\0';
413 static void globLastArgument(struct childProgram *prog, int *argcPtr,
417 int argcAlloced = *argcAllocedPtr;
423 if (argc > 1) { /* cmd->globResult is already initialized */
425 i = prog->globResult.gl_pathc;
432 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
433 if (rc == GLOB_NOSPACE) {
434 fprintf(stderr, "out of space during glob operation\n");
436 } else if (rc == GLOB_NOMATCH ||
437 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
438 !strcmp(prog->argv[argc - 1],
439 prog->globResult.gl_pathv[i]))) {
440 /* we need to remove whatever \ quoting is still present */
441 src = dst = prog->argv[argc - 1];
449 argcAlloced += (prog->globResult.gl_pathc - i);
451 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
452 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
453 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
454 argc += (prog->globResult.gl_pathc - i - 1);
457 *argcAllocedPtr = argcAlloced;
461 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
462 line). If a valid command is found, commandPtr is set to point to
463 the beginning of the next command (if the original command had more
464 then one job associated with it) or NULL if no more commands are
466 static int parseCommand(char **commandPtr, struct job *job, int *isBg)
469 char *returnCommand = NULL;
470 char *src, *buf, *chptr;
477 struct childProgram *prog;
479 /* skip leading white space */
480 while (**commandPtr && isspace(**commandPtr))
483 /* this handles empty lines or leading '#' characters */
484 if (!**commandPtr || (**commandPtr == '#')) {
492 job->progs = malloc(sizeof(*job->progs));
494 /* We set the argv elements to point inside of this string. The
495 memory is freed by freeJob().
497 Getting clean memory relieves us of the task of NULL
498 terminating things and makes the rest of this look a bit
499 cleaner (though it is, admittedly, a tad less efficient) */
500 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
504 prog->numRedirections = 0;
505 prog->redirections = NULL;
510 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
511 prog->argv[0] = job->cmdBuf;
515 while (*src && !done) {
522 fprintf(stderr, "character expected after \\\n");
527 /* in shell, "\'" should yield \' */
530 } else if (*src == '*' || *src == '?' || *src == '[' ||
531 *src == ']') *buf++ = '\\';
533 } else if (isspace(*src)) {
534 if (*prog->argv[argc]) {
536 /* +1 here leaves room for the NULL which ends argv */
537 if ((argc + 1) == argvAlloced) {
539 prog->argv = realloc(prog->argv,
540 sizeof(*prog->argv) *
543 prog->argv[argc] = buf;
545 globLastArgument(prog, &argc, &argvAlloced);
554 case '#': /* comment */
558 case '>': /* redirections */
560 i = prog->numRedirections++;
561 prog->redirections = realloc(prog->redirections,
562 sizeof(*prog->redirections) *
565 prog->redirections[i].fd = -1;
566 if (buf != prog->argv[argc]) {
567 /* the stuff before this character may be the file number
569 prog->redirections[i].fd =
570 strtol(prog->argv[argc], &chptr, 10);
572 if (*chptr && *prog->argv[argc]) {
574 globLastArgument(prog, &argc, &argvAlloced);
578 if (prog->redirections[i].fd == -1) {
580 prog->redirections[i].fd = 1;
582 prog->redirections[i].fd = 0;
587 prog->redirections[i].type =
588 REDIRECT_APPEND, src++;
590 prog->redirections[i].type = REDIRECT_OVERWRITE;
592 prog->redirections[i].type = REDIRECT_INPUT;
595 /* This isn't POSIX sh compliant. Oh well. */
597 while (isspace(*chptr))
601 fprintf(stderr, "file name expected after %c\n", *src);
606 prog->redirections[i].filename = buf;
607 while (*chptr && !isspace(*chptr))
610 src = chptr - 1; /* we src++ later */
611 prog->argv[argc] = ++buf;
615 /* finish this command */
616 if (*prog->argv[argc])
619 fprintf(stderr, "empty command in pipe\n");
623 prog->argv[argc] = NULL;
625 /* and start the next */
627 job->progs = realloc(job->progs,
628 sizeof(*job->progs) * job->numProgs);
629 prog = job->progs + (job->numProgs - 1);
630 prog->numRedirections = 0;
631 prog->redirections = NULL;
636 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
637 prog->argv[0] = ++buf;
640 while (*src && isspace(*src))
644 fprintf(stderr, "empty command in pipe\n");
647 src--; /* we'll ++ it at the end of the loop */
651 case '&': /* background */
653 case ';': /* multiple commands */
655 returnCommand = *commandPtr + (src - *commandPtr) + 1;
662 fprintf(stderr, "character expected after \\\n");
665 if (*src == '*' || *src == '[' || *src == ']'
666 || *src == '?') *buf++ = '\\';
675 if (*prog->argv[argc]) {
677 globLastArgument(prog, &argc, &argvAlloced);
683 prog->argv[argc] = NULL;
685 if (!returnCommand) {
686 job->text = malloc(strlen(*commandPtr) + 1);
687 strcpy(job->text, *commandPtr);
689 /* This leaves any trailing spaces, which is a bit sloppy */
691 count = returnCommand - *commandPtr;
692 job->text = malloc(count + 1);
693 strncpy(job->text, *commandPtr, count);
694 job->text[count] = '\0';
697 *commandPtr = returnCommand;
702 static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
707 int pipefds[2]; /* pipefd[0] is for reading */
708 struct builtInCommand *x;
710 /* handle built-ins here -- we don't fork() so we can't background
712 for (x = bltins; x->cmd; x++) {
713 if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
714 return (x->function(&newJob, jobList));
718 nextin = 0, nextout = 1;
719 for (i = 0; i < newJob.numProgs; i++) {
720 if ((i + 1) < newJob.numProgs) {
722 nextout = pipefds[1];
727 if (!(newJob.progs[i].pid = fork())) {
728 signal(SIGTTOU, SIG_DFL);
740 /* explicit redirections override pipes */
741 setupRedirections(newJob.progs + i);
743 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
744 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
748 /* put our child in the process group whose leader is the
749 first process in this pipe */
750 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
757 /* If there isn't another process, nextin is garbage
758 but it doesn't matter */
762 newJob.pgrp = newJob.progs[0].pid;
764 /* find the ID for the job to use */
766 for (job = jobList->head; job; job = job->next)
767 if (job->jobId >= newJob.jobId)
768 newJob.jobId = job->jobId + 1;
770 /* add the job to the list of running jobs */
771 if (!jobList->head) {
772 job = jobList->head = malloc(sizeof(*job));
774 for (job = jobList->head; job->next; job = job->next);
775 job->next = malloc(sizeof(*job));
781 job->runningProgs = job->numProgs;
782 job->stoppedProgs = 0;
785 /* we don't wait for background jobs to return -- append it
786 to the list of backgrounded jobs and leave it alone */
788 printf("[%d] %d\n", job->jobId,
789 newJob.progs[newJob.numProgs - 1].pid);
793 /* move the new process group into the foreground */
795 if (tcsetpgrp(0, newJob.pgrp))
802 static int setupRedirections(struct childProgram *prog)
807 struct redirectionSpecifier *redir = prog->redirections;
809 for (i = 0; i < prog->numRedirections; i++, redir++) {
810 switch (redir->type) {
814 case REDIRECT_OVERWRITE:
815 mode = O_RDWR | O_CREAT | O_TRUNC;
817 case REDIRECT_APPEND:
818 mode = O_RDWR | O_CREAT | O_APPEND;
822 openfd = open(redir->filename, mode, 0666);
824 /* this could get lost if stderr has been redirected, but
825 bash and ash both lose it as well (though zsh doesn't!) */
826 fprintf(stderr, "error opening %s: %s\n", redir->filename,
831 if (openfd != redir->fd) {
832 dup2(openfd, redir->fd);
841 static int busy_loop(FILE * input)
844 char *nextCommand = NULL;
845 struct jobSet jobList = { NULL, NULL };
851 command = (char*) calloc(BUFSIZ, sizeof(char));
853 /* don't pay any attention to this signal; it just confuses
854 things and isn't really meant for shells anyway */
855 signal(SIGTTOU, SIG_IGN);
859 /* no job is in the foreground */
861 /* see if any background processes have exited */
865 if (getCommand(input, command))
867 nextCommand = command;
870 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
872 runCommand(newJob, &jobList, inBg);
875 /* a job is running in the foreground; wait for it */
877 while (!jobList.fg->progs[i].pid ||
878 jobList.fg->progs[i].isStopped) i++;
880 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
882 if (WIFEXITED(status) || WIFSIGNALED(status)) {
883 /* the child exited */
884 jobList.fg->runningProgs--;
885 jobList.fg->progs[i].pid = 0;
887 if (!jobList.fg->runningProgs) {
890 removeJob(&jobList, jobList.fg);
893 /* move the shell to the foreground */
894 if (tcsetpgrp(0, getpid()))
898 /* the child was stopped */
899 jobList.fg->stoppedProgs++;
900 jobList.fg->progs[i].isStopped = 1;
902 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
903 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
904 "Stopped", jobList.fg->text);
910 /* move the shell to the foreground */
911 if (tcsetpgrp(0, getpid()))
922 int shell_main(int argc, char **argv)
929 /* initialize the cwd */
930 getcwd(cwd, sizeof(cwd));
933 //if (argv[0] && argv[0][0] == '-') {
934 // shell_source("/etc/profile");
938 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
941 "Enter 'help' for a list of built-in commands.\n\n");
943 input = fopen(argv[1], "r");
945 fatalError("A: Couldn't open file '%s': %s\n", argv[1],
948 // fatalError("Got it.\n");
949 //exit(shell_source(argv[1]));
951 /* Set terminal IO to canonical mode, and save old term settings. */
952 #ifdef BB_FEATURE_SH_COMMAND_EDITING
957 return (busy_loop(input));