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
37 #include <sys/ioctl.h>
40 #ifdef BB_FEATURE_SH_COMMAND_EDITING
45 #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
48 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_export(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 {"exit", "Exit from shell()", "exit", shell_exit},
117 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
118 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
119 {"export", "Set environment variable", "export [VAR=value]", shell_export},
120 {"unset", "Unset environment variable", "unset VAR", shell_unset},
121 {NULL, NULL, NULL, NULL}
124 /* Table of built-in functions */
125 static struct builtInCommand bltins_forking[] = {
126 {"env", "Print all environment variables", "env", shell_env},
127 {"pwd", "Print current directory", "pwd", shell_pwd},
128 {".", "Source-in and run commands in a file", ". filename", shell_source},
129 {"help", "List shell built-in commands", "help", shell_help},
130 {NULL, NULL, NULL, NULL}
133 static const char shell_usage[] =
135 " or: sh -c command [args]...\n"
136 #ifndef BB_FEATURE_TRIVIAL_HELP
137 "\nlash: The BusyBox command interpreter (shell).\n\n"
141 static char cwd[1024];
142 static char *prompt = "# ";
143 static char *local_pending_command = NULL;
145 #ifdef BB_FEATURE_SH_COMMAND_EDITING
146 void win_changed(int sig)
148 struct winsize win = { 0, 0 };
149 ioctl(0, TIOCGWINSZ, &win);
150 if (win.ws_col > 0) {
151 cmdedit_setwidth( win.ws_col - 1);
157 /* built-in 'cd <path>' handler */
158 static int shell_cd(struct job *cmd, struct jobSet *junk)
162 if (!cmd->progs[0].argv[1] == 1)
163 newdir = getenv("HOME");
165 newdir = cmd->progs[0].argv[1];
167 printf("cd: %s: %s\n", newdir, strerror(errno));
170 getcwd(cwd, sizeof(cwd));
175 /* built-in 'env' handler */
176 static int shell_env(struct job *dummy, struct jobSet *junk)
180 for (e = environ; *e; e++) {
181 fprintf(stdout, "%s\n", *e);
186 /* built-in 'exit' handler */
187 static int shell_exit(struct job *cmd, struct jobSet *junk)
189 if (!cmd->progs[0].argv[1] == 1)
192 return(atoi(cmd->progs[0].argv[1]));
195 /* built-in 'fg' and 'bg' handler */
196 static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
199 struct job *job=NULL;
201 if (!jobList->head) {
202 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
203 fprintf(stderr, "%s: exactly one argument is expected\n",
204 cmd->progs[0].argv[0]);
207 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
208 fprintf(stderr, "%s: bad argument '%s'\n",
209 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
211 for (job = jobList->head; job; job = job->next) {
212 if (job->jobId == jobNum) {
222 fprintf(stderr, "%s: unknown job %d\n",
223 cmd->progs[0].argv[0], jobNum);
227 if (*cmd->progs[0].argv[0] == 'f') {
228 /* Make this job the foreground job */
229 /* suppress messages when run from /linuxrc mag@sysgo.de */
230 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
235 /* Restart the processes in the job */
236 for (i = 0; i < job->numProgs; i++)
237 job->progs[i].isStopped = 0;
239 kill(-job->pgrp, SIGCONT);
241 job->stoppedProgs = 0;
246 /* built-in 'help' handler */
247 static int shell_help(struct job *cmd, struct jobSet *junk)
249 struct builtInCommand *x;
251 fprintf(stdout, "\nBuilt-in commands:\n");
252 fprintf(stdout, "-------------------\n");
253 for (x = bltins; x->cmd; x++) {
254 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
256 for (x = bltins_forking; x->cmd; x++) {
257 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
259 fprintf(stdout, "\n\n");
263 /* built-in 'jobs' handler */
264 static int shell_jobs(struct job *dummy, struct jobSet *jobList)
269 for (job = jobList->head; job; job = job->next) {
270 if (job->runningProgs == job->stoppedProgs)
271 statusString = "Stopped";
273 statusString = "Running";
275 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
281 /* built-in 'pwd' handler */
282 static int shell_pwd(struct job *dummy, struct jobSet *junk)
284 getcwd(cwd, sizeof(cwd));
285 fprintf(stdout, "%s\n", cwd);
289 /* built-in 'export VAR=value' handler */
290 static int shell_export(struct job *cmd, struct jobSet *junk)
294 if (!cmd->progs[0].argv[1] == 1) {
295 return (shell_env(cmd, junk));
297 res = putenv(cmd->progs[0].argv[1]);
299 fprintf(stdout, "export: %s\n", strerror(errno));
303 /* Built-in '.' handler (read-in and execute commands from file) */
304 static int shell_source(struct job *cmd, struct jobSet *junk)
309 if (!cmd->progs[0].argv[1] == 1)
312 input = fopen(cmd->progs[0].argv[1], "r");
314 fprintf(stdout, "Couldn't open file '%s'\n",
315 cmd->progs[0].argv[1]);
319 /* Now run the file */
320 status = busy_loop(input);
324 /* built-in 'unset VAR' handler */
325 static int shell_unset(struct job *cmd, struct jobSet *junk)
327 if (!cmd->progs[0].argv[1] == 1) {
328 fprintf(stdout, "unset: parameter required.\n");
331 unsetenv(cmd->progs[0].argv[1]);
335 /* free up all memory from a job */
336 static void freeJob(struct job *cmd)
340 for (i = 0; i < cmd->numProgs; i++) {
341 free(cmd->progs[i].argv);
342 if (cmd->progs[i].redirections)
343 free(cmd->progs[i].redirections);
344 if (cmd->progs[i].freeGlob)
345 globfree(&cmd->progs[i].globResult);
353 /* remove a job from the jobList */
354 static void removeJob(struct jobSet *jobList, struct job *job)
359 if (job == jobList->head) {
360 jobList->head = job->next;
362 prevJob = jobList->head;
363 while (prevJob->next != job)
364 prevJob = prevJob->next;
365 prevJob->next = job->next;
371 /* Checks to see if any background processes have exited -- if they
372 have, figure out why and see if a job has completed */
373 static void checkJobs(struct jobSet *jobList)
380 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
381 for (job = jobList->head; job; job = job->next) {
383 while (progNum < job->numProgs &&
384 job->progs[progNum].pid != childpid) progNum++;
385 if (progNum < job->numProgs)
389 if (WIFEXITED(status) || WIFSIGNALED(status)) {
392 job->progs[progNum].pid = 0;
394 if (!job->runningProgs) {
395 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
396 removeJob(jobList, job);
401 job->progs[progNum].isStopped = 1;
403 if (job->stoppedProgs == job->numProgs) {
404 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
410 if (childpid == -1 && errno != ECHILD)
414 static int getCommand(FILE * source, char *command)
416 if (source == NULL) {
417 if (local_pending_command) {
418 /* a command specified (-c option): return it & mark it done */
419 strcpy(command, local_pending_command);
420 free(local_pending_command);
421 local_pending_command = NULL;
427 if (source == stdin) {
428 #ifdef BB_FEATURE_SH_COMMAND_EDITING
431 len=fprintf(stdout, "%s %s", cwd, prompt);
433 promptStr=(char*)malloc(sizeof(char)*(len+1));
434 sprintf(promptStr, "%s %s", cwd, prompt);
435 cmdedit_read_input(promptStr, command);
439 fprintf(stdout, "%s %s", cwd, prompt);
444 if (!fgets(command, BUFSIZ - 2, source)) {
450 /* remove trailing newline */
451 command[strlen(command) - 1] = '\0';
456 static void globLastArgument(struct childProgram *prog, int *argcPtr,
460 int argcAlloced = *argcAllocedPtr;
466 if (argc > 1) { /* cmd->globResult is already initialized */
468 i = prog->globResult.gl_pathc;
475 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
476 if (rc == GLOB_NOSPACE) {
477 fprintf(stderr, "out of space during glob operation\n");
479 } else if (rc == GLOB_NOMATCH ||
480 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
481 !strcmp(prog->argv[argc - 1],
482 prog->globResult.gl_pathv[i]))) {
483 /* we need to remove whatever \ quoting is still present */
484 src = dst = prog->argv[argc - 1];
492 argcAlloced += (prog->globResult.gl_pathc - i);
494 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
495 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
496 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
497 argc += (prog->globResult.gl_pathc - i - 1);
500 *argcAllocedPtr = argcAlloced;
504 /* Return cmd->numProgs as 0 if no command is present (e.g. an empty
505 line). If a valid command is found, commandPtr is set to point to
506 the beginning of the next command (if the original command had more
507 then one job associated with it) or NULL if no more commands are
509 static int parseCommand(char **commandPtr, struct job *job, int *isBg)
512 char *returnCommand = NULL;
513 char *src, *buf, *chptr;
520 struct childProgram *prog;
522 /* skip leading white space */
523 while (**commandPtr && isspace(**commandPtr))
526 /* this handles empty lines or leading '#' characters */
527 if (!**commandPtr || (**commandPtr == '#')) {
535 job->progs = malloc(sizeof(*job->progs));
537 /* We set the argv elements to point inside of this string. The
538 memory is freed by freeJob().
540 Getting clean memory relieves us of the task of NULL
541 terminating things and makes the rest of this look a bit
542 cleaner (though it is, admittedly, a tad less efficient) */
543 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
547 prog->numRedirections = 0;
548 prog->redirections = NULL;
553 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
554 prog->argv[0] = job->cmdBuf;
558 while (*src && !done) {
565 fprintf(stderr, "character expected after \\\n");
570 /* in shell, "\'" should yield \' */
573 } else if (*src == '*' || *src == '?' || *src == '[' ||
574 *src == ']') *buf++ = '\\';
576 } else if (isspace(*src)) {
577 if (*prog->argv[argc]) {
579 /* +1 here leaves room for the NULL which ends argv */
580 if ((argc + 1) == argvAlloced) {
582 prog->argv = realloc(prog->argv,
583 sizeof(*prog->argv) *
586 prog->argv[argc] = buf;
588 globLastArgument(prog, &argc, &argvAlloced);
597 case '#': /* comment */
601 case '>': /* redirections */
603 i = prog->numRedirections++;
604 prog->redirections = realloc(prog->redirections,
605 sizeof(*prog->redirections) *
608 prog->redirections[i].fd = -1;
609 if (buf != prog->argv[argc]) {
610 /* the stuff before this character may be the file number
612 prog->redirections[i].fd =
613 strtol(prog->argv[argc], &chptr, 10);
615 if (*chptr && *prog->argv[argc]) {
617 globLastArgument(prog, &argc, &argvAlloced);
621 if (prog->redirections[i].fd == -1) {
623 prog->redirections[i].fd = 1;
625 prog->redirections[i].fd = 0;
630 prog->redirections[i].type =
631 REDIRECT_APPEND, src++;
633 prog->redirections[i].type = REDIRECT_OVERWRITE;
635 prog->redirections[i].type = REDIRECT_INPUT;
638 /* This isn't POSIX sh compliant. Oh well. */
640 while (isspace(*chptr))
644 fprintf(stderr, "file name expected after %c\n", *src);
649 prog->redirections[i].filename = buf;
650 while (*chptr && !isspace(*chptr))
653 src = chptr - 1; /* we src++ later */
654 prog->argv[argc] = ++buf;
658 /* finish this command */
659 if (*prog->argv[argc])
662 fprintf(stderr, "empty command in pipe\n");
666 prog->argv[argc] = NULL;
668 /* and start the next */
670 job->progs = realloc(job->progs,
671 sizeof(*job->progs) * job->numProgs);
672 prog = job->progs + (job->numProgs - 1);
673 prog->numRedirections = 0;
674 prog->redirections = NULL;
679 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
680 prog->argv[0] = ++buf;
683 while (*src && isspace(*src))
687 fprintf(stderr, "empty command in pipe\n");
690 src--; /* we'll ++ it at the end of the loop */
694 case '&': /* background */
696 case ';': /* multiple commands */
698 returnCommand = *commandPtr + (src - *commandPtr) + 1;
705 fprintf(stderr, "character expected after \\\n");
708 if (*src == '*' || *src == '[' || *src == ']'
709 || *src == '?') *buf++ = '\\';
718 if (*prog->argv[argc]) {
720 globLastArgument(prog, &argc, &argvAlloced);
726 prog->argv[argc] = NULL;
728 if (!returnCommand) {
729 job->text = malloc(strlen(*commandPtr) + 1);
730 strcpy(job->text, *commandPtr);
732 /* This leaves any trailing spaces, which is a bit sloppy */
733 count = returnCommand - *commandPtr;
734 job->text = malloc(count + 1);
735 strncpy(job->text, *commandPtr, count);
736 job->text[count] = '\0';
739 *commandPtr = returnCommand;
745 static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
750 int pipefds[2]; /* pipefd[0] is for reading */
751 struct builtInCommand *x;
752 #ifdef BB_FEATURE_STANDALONE_SHELL
753 const struct BB_applet *a = applets;
757 nextin = 0, nextout = 1;
758 for (i = 0; i < newJob.numProgs; i++) {
759 if ((i + 1) < newJob.numProgs) {
761 nextout = pipefds[1];
766 /* Match any built-ins here */
767 for (x = bltins; x->cmd; x++) {
768 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
769 return (x->function(&newJob, jobList));
773 if (!(newJob.progs[i].pid = fork())) {
774 signal(SIGTTOU, SIG_DFL);
786 /* explicit redirections override pipes */
787 setupRedirections(newJob.progs + i);
789 /* Match any built-ins here */
790 for (x = bltins_forking; x->cmd; x++) {
791 if (!strcmp(newJob.progs[i].argv[0], x->cmd)) {
792 exit (x->function(&newJob, jobList));
795 #ifdef BB_FEATURE_STANDALONE_SHELL
796 /* Handle busybox internals here */
797 while (a->name != 0) {
798 if (strcmp(newJob.progs[i].argv[0], a->name) == 0) {
800 char** argv=newJob.progs[i].argv;
801 for(argc=0;*argv!=NULL; argv++, argc++);
802 exit((*(a->main)) (argc, newJob.progs[i].argv));
808 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
809 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
813 /* put our child in the process group whose leader is the
814 first process in this pipe */
815 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
822 /* If there isn't another process, nextin is garbage
823 but it doesn't matter */
827 newJob.pgrp = newJob.progs[0].pid;
829 /* find the ID for the job to use */
831 for (job = jobList->head; job; job = job->next)
832 if (job->jobId >= newJob.jobId)
833 newJob.jobId = job->jobId + 1;
835 /* add the job to the list of running jobs */
836 if (!jobList->head) {
837 job = jobList->head = malloc(sizeof(*job));
839 for (job = jobList->head; job->next; job = job->next);
840 job->next = malloc(sizeof(*job));
846 job->runningProgs = job->numProgs;
847 job->stoppedProgs = 0;
850 /* we don't wait for background jobs to return -- append it
851 to the list of backgrounded jobs and leave it alone */
852 printf("[%d] %d\n", job->jobId,
853 newJob.progs[newJob.numProgs - 1].pid);
857 /* move the new process group into the foreground */
858 /* suppress messages when run from /linuxrc mag@sysgo.de */
859 if (tcsetpgrp(0, newJob.pgrp) && errno != ENOTTY)
866 static int setupRedirections(struct childProgram *prog)
871 struct redirectionSpecifier *redir = prog->redirections;
873 for (i = 0; i < prog->numRedirections; i++, redir++) {
874 switch (redir->type) {
878 case REDIRECT_OVERWRITE:
879 mode = O_RDWR | O_CREAT | O_TRUNC;
881 case REDIRECT_APPEND:
882 mode = O_RDWR | O_CREAT | O_APPEND;
886 openfd = open(redir->filename, mode, 0666);
888 /* this could get lost if stderr has been redirected, but
889 bash and ash both lose it as well (though zsh doesn't!) */
890 fprintf(stderr, "error opening %s: %s\n", redir->filename,
895 if (openfd != redir->fd) {
896 dup2(openfd, redir->fd);
905 static int busy_loop(FILE * input)
908 char *nextCommand = NULL;
909 struct jobSet jobList = { NULL, NULL };
916 /* save current owner of TTY so we can restore it on exit */
917 parent_pgrp = tcgetpgrp(0);
919 command = (char *) calloc(BUFSIZ, sizeof(char));
921 /* don't pay any attention to this signal; it just confuses
922 things and isn't really meant for shells anyway */
923 signal(SIGTTOU, SIG_IGN);
927 /* no job is in the foreground */
929 /* see if any background processes have exited */
933 if (getCommand(input, command))
935 nextCommand = command;
938 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
940 runCommand(newJob, &jobList, inBg);
943 /* a job is running in the foreground; wait for it */
945 while (!jobList.fg->progs[i].pid ||
946 jobList.fg->progs[i].isStopped) i++;
948 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
950 if (WIFEXITED(status) || WIFSIGNALED(status)) {
951 /* the child exited */
952 jobList.fg->runningProgs--;
953 jobList.fg->progs[i].pid = 0;
955 if (!jobList.fg->runningProgs) {
958 removeJob(&jobList, jobList.fg);
962 /* the child was stopped */
963 jobList.fg->stoppedProgs++;
964 jobList.fg->progs[i].isStopped = 1;
966 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
967 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
968 "Stopped", jobList.fg->text);
974 /* move the shell to the foreground */
975 /* suppress messages when run from /linuxrc mag@sysgo.de */
976 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
983 /* return controlling TTY back to parent process group before exiting */
984 if (tcsetpgrp(0, parent_pgrp))
991 int shell_main(int argc, char **argv)
995 /* initialize the cwd */
996 getcwd(cwd, sizeof(cwd));
998 #ifdef BB_FEATURE_SH_COMMAND_EDITING
1000 signal(SIGWINCH, win_changed);
1004 //if (argv[0] && argv[0][0] == '-') {
1005 // shell_source("/etc/profile");
1009 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1010 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
1012 if (argv[1][0]=='-' && argv[1][1]=='c') {
1014 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1015 if (local_pending_command == 0) {
1016 fatalError("sh: out of memory\n");
1018 for(i=2; i<argc; i++)
1020 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
1021 fatalError("sh: commands for -c option too long\n");
1023 strcat(local_pending_command, argv[i]);
1025 strcat(local_pending_command, " ");
1030 else if (argv[1][0]=='-') {
1034 input = fopen(argv[1], "r");
1036 fatalError("sh: Couldn't open file '%s': %s\n", argv[1],
1042 return (busy_loop(input));