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 #define MAX_COMMAND_LEN 250 /* max length of a single command
44 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
46 enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND };
49 struct job * head; /* head of list of running jobs */
50 struct job * fg; /* current foreground job */
53 struct redirectionSpecifier {
54 enum redirectionType type; /* type of redirection */
55 int fd; /* file descriptor being redirected */
56 char * filename; /* file to redirect fd to */
60 pid_t pid; /* 0 if exited */
61 char ** argv; /* program name and arguments */
62 int numRedirections; /* elements in redirection array */
63 struct redirectionSpecifier * redirections; /* I/O redirections */
64 glob_t globResult; /* result of parameter globbing */
65 int freeGlob; /* should we globfree(&globResult)? */
66 int isStopped; /* is the program currently running? */
70 int jobId; /* job number */
71 int numProgs; /* total number of programs in job */
72 int runningProgs; /* number of programs running */
73 char * text; /* name of job */
74 char * cmdBuf; /* buffer various argv's point into */
75 pid_t pgrp; /* process group ID for the job */
76 struct childProgram * progs; /* array of programs in job */
77 struct job * next; /* to track background commands */
78 int stoppedProgs; /* number of programs alive, but stopped */
81 struct builtInCommand {
83 char *descr; /* description */
84 char *usage; /* usage */
85 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
88 /* Some function prototypes */
89 static int shell_cd(struct job* cmd, struct jobSet* junk);
90 static int shell_env(struct job* dummy, struct jobSet* junk);
91 static int shell_exit(struct job* cmd, struct jobSet* junk);
92 static int shell_fg_bg(struct job* cmd, struct jobSet* jobList);
93 static int shell_help(struct job* cmd, struct jobSet* junk);
94 static int shell_jobs(struct job* dummy, struct jobSet* jobList);
95 static int shell_pwd(struct job* dummy, struct jobSet* junk);
96 static int shell_set(struct job* cmd, struct jobSet* junk);
97 static int shell_source(struct job* cmd, struct jobSet* jobList);
98 static int shell_unset(struct job* cmd, struct jobSet* junk);
100 static void checkJobs(struct jobSet * jobList);
101 static int getCommand(FILE * source, char * command);
102 static int parseCommand(char ** commandPtr, struct job * job, int * isBg);
103 static int setupRedirections(struct childProgram * prog);
104 static int runCommand(struct job newJob, struct jobSet * jobList, int inBg);
105 static int busy_loop(FILE * input);
108 /* Table of built-in functions */
109 static struct builtInCommand bltins[] = {
110 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
111 {"cd", "Change working directory", "cd [dir]", shell_cd},
112 {"env", "Print all environment variables", "env", shell_env},
113 {"exit", "Exit from shell()", "exit", shell_exit},
114 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
115 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
116 {"pwd", "Print current directory", "pwd", shell_pwd},
117 {"set", "Set environment variable", "set [VAR=value]", shell_set},
118 {"unset", "Unset environment variable", "unset VAR", shell_unset},
119 //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
120 {".", "Source-in and run commands in a file", ". filename", shell_source},
121 {"help", "List shell built-in commands", "help", shell_help},
122 {NULL, NULL, NULL, NULL}
125 static const char shell_usage[] =
127 "The BusyBox command interpreter (shell).\n\n";
130 static char cwd[1024];
131 static char *prompt = "# ";
134 /* built-in 'cd <path>' handler */
135 static int shell_cd(struct job* cmd, struct jobSet* junk)
138 if (!cmd->progs[0].argv[1] == 1)
139 newdir = getenv("HOME");
141 newdir = cmd->progs[0].argv[1];
143 printf("cd: %s: %s\n", newdir, strerror(errno));
146 getcwd(cwd, sizeof(cwd));
151 /* built-in 'env' handler */
152 static int shell_env(struct job* dummy, struct jobSet* junk)
156 for (e = environ ; *e ; e++) {
157 fprintf(stdout, "%s\n", *e);
162 /* built-in 'exit' handler */
163 static int shell_exit(struct job* cmd, struct jobSet* junk)
165 if (!cmd->progs[0].argv[1] == 1)
168 exit(atoi(cmd->progs[0].argv[1]));
171 /* built-in 'fg' and 'bg' handler */
172 static int shell_fg_bg(struct job* cmd, struct jobSet* jobList)
177 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
178 fprintf(stderr, "%s: exactly one argument is expected\n",
179 cmd->progs[0].argv[0]);
183 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
184 fprintf(stderr, "%s: bad argument '%s'\n",
185 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
189 for (job = jobList->head; job; job = job->next)
190 if (job->jobId == jobNum) break;
193 fprintf(stderr, "%s: unknown job %d\n",
194 cmd->progs[0].argv[0], jobNum);
198 if (*cmd->progs[0].argv[0] == 'f') {
199 /* Make this job the foreground job */
201 if (tcsetpgrp(0, job->pgrp))
206 /* Restart the processes in the job */
207 for (i = 0; i < job->numProgs; i++)
208 job->progs[i].isStopped = 0;
210 kill(-job->pgrp, SIGCONT);
212 job->stoppedProgs = 0;
217 /* built-in 'help' handler */
218 static int shell_help(struct job* cmd, struct jobSet* junk)
220 struct builtInCommand *x;
222 fprintf(stdout, "\nBuilt-in commands:\n");
223 fprintf(stdout, "-------------------\n");
224 for ( x=bltins; x->cmd; x++) {
225 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
227 fprintf(stdout, "\n\n");
231 /* built-in 'jobs' handler */
232 static int shell_jobs(struct job* dummy, struct jobSet* jobList)
236 for (job = jobList->head; job; job = job->next) {
237 if (job->runningProgs == job->stoppedProgs)
238 statusString = "Stopped";
240 statusString = "Running";
242 printf(JOB_STATUS_FORMAT, job->jobId, statusString,
249 /* built-in 'pwd' handler */
250 static int shell_pwd(struct job* dummy, struct jobSet* junk)
252 getcwd(cwd, sizeof(cwd));
253 fprintf(stdout, "%s\n", cwd);
257 /* built-in 'set VAR=value' handler */
258 static int shell_set(struct job* cmd, struct jobSet* junk)
262 if (!cmd->progs[0].argv[1] == 1) {
263 return (shell_env(cmd, junk));
265 res = putenv(cmd->progs[0].argv[1]);
267 fprintf(stdout, "set: %s\n", strerror(errno));
271 /* Built-in '.' handler (read-in and execute commands from file) */
272 static int shell_source(struct job* cmd, struct jobSet* junk)
277 if (!cmd->progs[0].argv[1] == 1)
280 input = fopen(cmd->progs[0].argv[1], "r");
282 fprintf(stdout, "Couldn't open file '%s'\n", cmd->progs[0].argv[1]);
286 /* Now run the file */
287 status = busy_loop(input);
291 /* built-in 'unset VAR' handler */
292 static int shell_unset(struct job* cmd, struct jobSet* junk)
294 if (!cmd->progs[0].argv[1] == 1) {
295 fprintf(stdout, "unset: parameter required.\n");
298 unsetenv(cmd->progs[0].argv[1]);
302 /* free up all memory from a job */
303 static void freeJob(struct job * cmd)
307 for (i = 0; i < cmd->numProgs; i++) {
308 free(cmd->progs[i].argv);
309 if (cmd->progs[i].redirections) free(cmd->progs[i].redirections);
310 if (cmd->progs[i].freeGlob) globfree(&cmd->progs[i].globResult);
313 if (cmd->text) free(cmd->text);
317 /* remove a job from the jobList */
318 static void removeJob(struct jobSet * jobList, struct job * job)
320 struct job * prevJob;
323 if (job == jobList->head) {
324 jobList->head = job->next;
326 prevJob = jobList->head;
327 while (prevJob->next != job) prevJob = prevJob->next;
328 prevJob->next = job->next;
334 /* Checks to see if any background processes have exited -- if they
335 have, figure out why and see if a job has completed */
336 static void checkJobs(struct jobSet * jobList)
343 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
344 for (job = jobList->head; job; job = job->next) {
346 while (progNum < job->numProgs &&
347 job->progs[progNum].pid != childpid)
349 if (progNum < job->numProgs) break;
352 if (WIFEXITED(status) || WIFSIGNALED(status)) {
355 job->progs[progNum].pid = 0;
357 if (!job->runningProgs) {
358 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
359 removeJob(jobList, job);
364 job->progs[progNum].isStopped = 1;
366 if (job->stoppedProgs == job->numProgs) {
367 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", job->text);
372 if (childpid == -1 && errno != ECHILD)
376 static int getCommand(FILE * source, char * command)
378 if (source == stdin) {
379 fprintf(stdout, "%s %s", cwd, prompt);
383 if (!fgets(command, MAX_COMMAND_LEN, source)) {
384 if (source == stdin) printf("\n");
388 /* remove trailing newline */
389 command[strlen(command) - 1] = '\0';
394 static void globLastArgument(struct childProgram * prog, int * argcPtr,
395 int * argcAllocedPtr)
398 int argcAlloced = *argcAllocedPtr;
404 if (argc > 1) { /* cmd->globResult is already initialized */
406 i = prog->globResult.gl_pathc;
413 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
414 if (rc == GLOB_NOSPACE) {
415 fprintf(stderr, "out of space during glob operation\n");
417 } else if (rc == GLOB_NOMATCH ||
418 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
419 !strcmp(prog->argv[argc - 1],
420 prog->globResult.gl_pathv[i]))) {
421 /* we need to remove whatever \ quoting is still present */
422 src = dst = prog->argv[argc - 1];
424 if (*src != '\\') *dst++ = *src;
429 argcAlloced += (prog->globResult.gl_pathc - i);
430 prog->argv = realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
431 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
432 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
433 argc += (prog->globResult.gl_pathc - i - 1);
436 *argcAllocedPtr = argcAlloced;
440 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
441 line). If a valid command is found, commandPtr is set to point to
442 the beginning of the next command (if the original command had more
443 then one job associated with it) or NULL if no more commands are
445 static int parseCommand(char ** commandPtr, struct job * job, int * isBg)
448 char * returnCommand = NULL;
449 char * src, * buf, * chptr;
456 struct childProgram * prog;
458 /* skip leading white space */
459 while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++;
461 /* this handles empty lines or leading '#' characters */
462 if (!**commandPtr || (**commandPtr=='#')) {
470 job->progs = malloc(sizeof(*job->progs));
472 /* We set the argv elements to point inside of this string. The
473 memory is freed by freeJob().
475 Getting clean memory relieves us of the task of NULL
476 terminating things and makes the rest of this look a bit
477 cleaner (though it is, admittedly, a tad less efficient) */
478 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
482 prog->numRedirections = 0;
483 prog->redirections = NULL;
488 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
489 prog->argv[0] = job->cmdBuf;
493 while (*src && !done) {
500 fprintf(stderr, "character expected after \\\n");
505 /* in shell, "\'" should yield \' */
506 if (*src != quote) *buf++ = '\\';
507 } else if (*src == '*' || *src == '?' || *src == '[' ||
511 } else if (isspace(*src)) {
512 if (*prog->argv[argc]) {
514 /* +1 here leaves room for the NULL which ends argv */
515 if ((argc + 1) == argvAlloced) {
517 prog->argv = realloc(prog->argv,
518 sizeof(*prog->argv) * argvAlloced);
520 prog->argv[argc] = buf;
522 globLastArgument(prog, &argc, &argvAlloced);
524 } else switch (*src) {
530 case '#': /* comment */
534 case '>': /* redirections */
536 i = prog->numRedirections++;
537 prog->redirections = realloc(prog->redirections,
538 sizeof(*prog->redirections) * (i + 1));
540 prog->redirections[i].fd = -1;
541 if (buf != prog->argv[argc]) {
542 /* the stuff before this character may be the file number
544 prog->redirections[i].fd = strtol(prog->argv[argc], &chptr, 10);
546 if (*chptr && *prog->argv[argc]) {
548 globLastArgument(prog, &argc, &argvAlloced);
552 if (prog->redirections[i].fd == -1) {
554 prog->redirections[i].fd = 1;
556 prog->redirections[i].fd = 0;
561 prog->redirections[i].type = REDIRECT_APPEND, src++;
563 prog->redirections[i].type = REDIRECT_OVERWRITE;
565 prog->redirections[i].type = REDIRECT_INPUT;
568 /* This isn't POSIX sh compliant. Oh well. */
570 while (isspace(*chptr)) chptr++;
573 fprintf(stderr, "file name expected after %c\n", *src);
578 prog->redirections[i].filename = buf;
579 while (*chptr && !isspace(*chptr))
582 src = chptr - 1; /* we src++ later */
583 prog->argv[argc] = ++buf;
587 /* finish this command */
588 if (*prog->argv[argc]) argc++;
590 fprintf(stderr, "empty command in pipe\n");
594 prog->argv[argc] = NULL;
596 /* and start the next */
598 job->progs = realloc(job->progs,
599 sizeof(*job->progs) * job->numProgs);
600 prog = job->progs + (job->numProgs - 1);
601 prog->numRedirections = 0;
602 prog->redirections = NULL;
607 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
608 prog->argv[0] = ++buf;
611 while (*src && isspace(*src)) src++;
614 fprintf(stderr, "empty command in pipe\n");
617 src--; /* we'll ++ it at the end of the loop */
621 case '&': /* background */
623 case ';': /* multiple commands */
625 returnCommand = *commandPtr + (src - *commandPtr) + 1;
632 fprintf(stderr, "character expected after \\\n");
635 if (*src == '*' || *src == '[' || *src == ']' || *src == '?')
645 if (*prog->argv[argc]) {
647 globLastArgument(prog, &argc, &argvAlloced);
653 prog->argv[argc] = NULL;
655 if (!returnCommand) {
656 job->text = malloc(strlen(*commandPtr) + 1);
657 strcpy(job->text, *commandPtr);
659 /* This leaves any trailing spaces, which is a bit sloppy */
661 count = returnCommand - *commandPtr;
662 job->text = malloc(count + 1);
663 strncpy(job->text, *commandPtr, count);
664 job->text[count] = '\0';
667 *commandPtr = returnCommand;
672 static int runCommand(struct job newJob, struct jobSet * jobList,
678 int pipefds[2]; /* pipefd[0] is for reading */
679 struct builtInCommand *x;
681 /* handle built-ins here -- we don't fork() so we can't background
683 for( x=bltins ; x->cmd ; x++) {
684 if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
685 return(x->function(&newJob, jobList));
689 nextin = 0, nextout = 1;
690 for (i = 0; i < newJob.numProgs; i++) {
691 if ((i + 1) < newJob.numProgs) {
693 nextout = pipefds[1];
698 if (!(newJob.progs[i].pid = fork())) {
699 signal(SIGTTOU, SIG_DFL);
711 /* explicit redirections override pipes */
712 setupRedirections(newJob.progs + i);
714 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
715 fatalError( "sh: %s: %s\n", newJob.progs[i].argv[0],
719 /* put our child in the process group whose leader is the
720 first process in this pipe */
721 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
723 if (nextin != 0) close(nextin);
724 if (nextout != 1) close(nextout);
726 /* If there isn't another process, nextin is garbage
727 but it doesn't matter */
731 newJob.pgrp = newJob.progs[0].pid;
733 /* find the ID for the job to use */
735 for (job = jobList->head; job; job = job->next)
736 if (job->jobId >= newJob.jobId)
737 newJob.jobId = job->jobId + 1;
739 /* add the job to the list of running jobs */
740 if (!jobList->head) {
741 job = jobList->head = malloc(sizeof(*job));
743 for (job = jobList->head; job->next; job = job->next);
744 job->next = malloc(sizeof(*job));
750 job->runningProgs = job->numProgs;
751 job->stoppedProgs = 0;
754 /* we don't wait for background jobs to return -- append it
755 to the list of backgrounded jobs and leave it alone */
757 printf("[%d] %d\n", job->jobId,
758 newJob.progs[newJob.numProgs - 1].pid);
762 /* move the new process group into the foreground */
764 if (tcsetpgrp(0, newJob.pgrp))
771 static int setupRedirections(struct childProgram * prog)
776 struct redirectionSpecifier * redir = prog->redirections;
778 for (i = 0; i < prog->numRedirections; i++, redir++) {
779 switch (redir->type) {
783 case REDIRECT_OVERWRITE:
784 mode = O_RDWR | O_CREAT | O_TRUNC;
786 case REDIRECT_APPEND:
787 mode = O_RDWR | O_CREAT | O_APPEND;
791 openfd = open(redir->filename, mode, 0666);
793 /* this could get lost if stderr has been redirected, but
794 bash and ash both lose it as well (though zsh doesn't!) */
795 fprintf(stderr, "error opening %s: %s\n", redir->filename,
800 if (openfd != redir->fd) {
801 dup2(openfd, redir->fd);
810 static int busy_loop(FILE * input)
812 char command[MAX_COMMAND_LEN + 1];
813 char * nextCommand = NULL;
814 struct jobSet jobList = { NULL, NULL };
820 /* don't pay any attention to this signal; it just confuses
821 things and isn't really meant for shells anyway */
822 signal(SIGTTOU, SIG_IGN);
826 /* no job is in the foreground */
828 /* see if any background processes have exited */
832 if (getCommand(input, command)) break;
833 nextCommand = command;
836 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
838 runCommand(newJob, &jobList, inBg);
841 /* a job is running in the foreground; wait for it */
843 while (!jobList.fg->progs[i].pid ||
844 jobList.fg->progs[i].isStopped) i++;
846 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
848 if (WIFEXITED(status) || WIFSIGNALED(status)) {
849 /* the child exited */
850 jobList.fg->runningProgs--;
851 jobList.fg->progs[i].pid = 0;
853 if (!jobList.fg->runningProgs) {
856 removeJob(&jobList, jobList.fg);
859 /* move the shell to the foreground */
860 if (tcsetpgrp(0, getpid()))
864 /* the child was stopped */
865 jobList.fg->stoppedProgs++;
866 jobList.fg->progs[i].isStopped = 1;
868 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
869 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
870 "Stopped", jobList.fg->text);
876 /* move the shell to the foreground */
877 if (tcsetpgrp(0, getpid()))
887 int shell_main(int argc, char ** argv)
889 FILE * input = stdin;
894 /* initialize the cwd */
895 getcwd(cwd, sizeof(cwd));
898 //if (argv[0] && argv[0][0] == '-') {
899 // shell_source("/etc/profile");
903 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
904 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
906 input = fopen(argv[1], "r");
908 fatalError("A: Couldn't open file '%s': %s\n", argv[1], strerror(errno));
910 // fatalError("Got it.\n");
911 //exit(shell_source(argv[1]));
914 return (busy_loop( input));