blob: 498f43779cd29516e13e098ae5f45d105015a4fb [file] [log] [blame]
Erik Andersen3522eb12000-03-12 23:49:18 +00001/* vi: set sw=4 ts=4: */
2/*
3 * BusyBox Shell
4 *
5 * Copyright (C) 2000 by Lineo, inc.
6 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
7 *
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."
11 *
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.
16 *
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.
21 *
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
25 *
26 */
27
28#include "internal.h"
29#include <stdio.h>
30#include <stdlib.h>
31#include <ctype.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <glob.h>
35#include <signal.h>
36#include <string.h>
37#include <sys/ioctl.h>
38#include <sys/wait.h>
39#include <unistd.h>
40
41
Erik Andersend75af992000-03-16 08:09:09 +000042#ifdef BB_FEATURE_SH_COMMAND_EDITING
43#include "cmdedit.h"
44#endif
45
Erik Andersen3522eb12000-03-12 23:49:18 +000046#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
47
Erik Andersend75af992000-03-16 08:09:09 +000048
49enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
50 REDIRECT_APPEND };
Erik Andersen3522eb12000-03-12 23:49:18 +000051
52struct jobSet {
Erik Andersend75af992000-03-16 08:09:09 +000053 struct job *head; /* head of list of running jobs */
54 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000055};
56
57struct redirectionSpecifier {
Erik Andersend75af992000-03-16 08:09:09 +000058 enum redirectionType type; /* type of redirection */
59 int fd; /* file descriptor being redirected */
60 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000061};
62
63struct childProgram {
Erik Andersend75af992000-03-16 08:09:09 +000064 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? */
Erik Andersen3522eb12000-03-12 23:49:18 +000071};
72
73struct job {
Erik Andersend75af992000-03-16 08:09:09 +000074 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 */
Erik Andersen3522eb12000-03-12 23:49:18 +000083};
84
85struct builtInCommand {
Erik Andersend75af992000-03-16 08:09:09 +000086 char *cmd; /* name */
87 char *descr; /* description */
88 char *usage; /* usage */
89 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +000090};
91
92/* Some function prototypes */
Erik Andersend75af992000-03-16 08:09:09 +000093static int shell_cd(struct job *cmd, struct jobSet *junk);
94static int shell_env(struct job *dummy, struct jobSet *junk);
95static int shell_exit(struct job *cmd, struct jobSet *junk);
96static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
97static int shell_help(struct job *cmd, struct jobSet *junk);
98static int shell_jobs(struct job *dummy, struct jobSet *jobList);
99static int shell_pwd(struct job *dummy, struct jobSet *junk);
100static int shell_set(struct job *cmd, struct jobSet *junk);
101static int shell_source(struct job *cmd, struct jobSet *jobList);
102static int shell_unset(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000103
Erik Andersend75af992000-03-16 08:09:09 +0000104static void checkJobs(struct jobSet *jobList);
105static int getCommand(FILE * source, char *command);
106static int parseCommand(char **commandPtr, struct job *job, int *isBg);
107static int setupRedirections(struct childProgram *prog);
108static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
Erik Andersen3522eb12000-03-12 23:49:18 +0000109static int busy_loop(FILE * input);
110
Erik Andersend75af992000-03-16 08:09:09 +0000111
Erik Andersen3522eb12000-03-12 23:49:18 +0000112/* Table of built-in functions */
113static struct builtInCommand bltins[] = {
Erik Andersend75af992000-03-16 08:09:09 +0000114 {"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",
125 shell_source},
126 {"help", "List shell built-in commands", "help", shell_help},
127 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000128};
129
130static const char shell_usage[] =
Erik Andersend75af992000-03-16 08:09:09 +0000131 "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
Erik Andersen3522eb12000-03-12 23:49:18 +0000132
133
134static char cwd[1024];
135static char *prompt = "# ";
136
137
Erik Andersen3522eb12000-03-12 23:49:18 +0000138
Erik Andersend75af992000-03-16 08:09:09 +0000139/* built-in 'cd <path>' handler */
140static int shell_cd(struct job *cmd, struct jobSet *junk)
141{
142 char *newdir;
143 if (!cmd->progs[0].argv[1] == 1)
144 newdir = getenv("HOME");
145 else
146 newdir = cmd->progs[0].argv[1];
147 if (chdir(newdir)) {
148 printf("cd: %s: %s\n", newdir, strerror(errno));
149 return FALSE;
150 }
151 getcwd(cwd, sizeof(cwd));
152
153 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000154}
155
156/* built-in 'env' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000157static int shell_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000158{
Erik Andersend75af992000-03-16 08:09:09 +0000159 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000160
Erik Andersend75af992000-03-16 08:09:09 +0000161 for (e = environ; *e; e++) {
162 fprintf(stdout, "%s\n", *e);
163 }
164 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000165}
166
167/* built-in 'exit' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000168static int shell_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000169{
Erik Andersend75af992000-03-16 08:09:09 +0000170 if (!cmd->progs[0].argv[1] == 1)
171 exit TRUE;
172 else
173 exit(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000174}
175
176/* built-in 'fg' and 'bg' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000177static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000178{
Erik Andersend75af992000-03-16 08:09:09 +0000179 int i, jobNum;
180 struct job *job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000181
Erik Andersend75af992000-03-16 08:09:09 +0000182 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]);
186 return FALSE;
187 }
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]);
191 return FALSE;
192 }
193 } else {
194 job = jobList->head;
195 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000196
Erik Andersend75af992000-03-16 08:09:09 +0000197 for (job = jobList->head; job; job = job->next)
198 if (job->jobId == jobNum)
199 break;
Erik Andersen3522eb12000-03-12 23:49:18 +0000200
Erik Andersend75af992000-03-16 08:09:09 +0000201 if (!job) {
202 fprintf(stderr, "%s: unknown job %d\n",
203 cmd->progs[0].argv[0], jobNum);
204 return FALSE;
205 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000206
Erik Andersend75af992000-03-16 08:09:09 +0000207 if (*cmd->progs[0].argv[0] == 'f') {
208 /* Make this job the foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +0000209
Erik Andersend75af992000-03-16 08:09:09 +0000210 if (tcsetpgrp(0, job->pgrp))
211 perror("tcsetpgrp");
212 jobList->fg = job;
213 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000214
Erik Andersend75af992000-03-16 08:09:09 +0000215 /* Restart the processes in the job */
216 for (i = 0; i < job->numProgs; i++)
217 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000218
Erik Andersend75af992000-03-16 08:09:09 +0000219 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000220
Erik Andersend75af992000-03-16 08:09:09 +0000221 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000222
Erik Andersend75af992000-03-16 08:09:09 +0000223 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000224}
225
226/* built-in 'help' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000227static int shell_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000228{
Erik Andersend75af992000-03-16 08:09:09 +0000229 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000230
Erik Andersend75af992000-03-16 08:09:09 +0000231 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);
235 }
236 fprintf(stdout, "\n\n");
237 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000238}
239
240/* built-in 'jobs' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000241static int shell_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000242{
Erik Andersend75af992000-03-16 08:09:09 +0000243 struct job *job;
244 char *statusString;
245 for (job = jobList->head; job; job = job->next) {
246 if (job->runningProgs == job->stoppedProgs)
247 statusString = "Stopped";
248 else
249 statusString = "Running";
Erik Andersen3522eb12000-03-12 23:49:18 +0000250
Erik Andersend75af992000-03-16 08:09:09 +0000251 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
252 }
253 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000254}
255
256
257/* built-in 'pwd' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000258static int shell_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000259{
Erik Andersend75af992000-03-16 08:09:09 +0000260 getcwd(cwd, sizeof(cwd));
261 fprintf(stdout, "%s\n", cwd);
262 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000263}
264
265/* built-in 'set VAR=value' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000266static int shell_set(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000267{
Erik Andersend75af992000-03-16 08:09:09 +0000268 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000269
Erik Andersend75af992000-03-16 08:09:09 +0000270 if (!cmd->progs[0].argv[1] == 1) {
271 return (shell_env(cmd, junk));
272 }
273 res = putenv(cmd->progs[0].argv[1]);
274 if (res)
275 fprintf(stdout, "set: %s\n", strerror(errno));
276 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000277}
278
279/* Built-in '.' handler (read-in and execute commands from file) */
Erik Andersend75af992000-03-16 08:09:09 +0000280static int shell_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000281{
Erik Andersend75af992000-03-16 08:09:09 +0000282 FILE *input;
283 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000284
Erik Andersend75af992000-03-16 08:09:09 +0000285 if (!cmd->progs[0].argv[1] == 1)
286 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000287
Erik Andersend75af992000-03-16 08:09:09 +0000288 input = fopen(cmd->progs[0].argv[1], "r");
289 if (!input) {
290 fprintf(stdout, "Couldn't open file '%s'\n",
291 cmd->progs[0].argv[1]);
292 return FALSE;
293 }
294
295 /* Now run the file */
296 status = busy_loop(input);
297 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000298}
299
300/* built-in 'unset VAR' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000301static int shell_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000302{
Erik Andersend75af992000-03-16 08:09:09 +0000303 if (!cmd->progs[0].argv[1] == 1) {
304 fprintf(stdout, "unset: parameter required.\n");
305 return FALSE;
306 }
307 unsetenv(cmd->progs[0].argv[1]);
308 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000309}
310
311/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000312static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000313{
314 int i;
315
316 for (i = 0; i < cmd->numProgs; i++) {
Erik Andersend75af992000-03-16 08:09:09 +0000317 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);
Erik Andersen3522eb12000-03-12 23:49:18 +0000322 }
323 free(cmd->progs);
Erik Andersend75af992000-03-16 08:09:09 +0000324 if (cmd->text)
325 free(cmd->text);
Erik Andersen3522eb12000-03-12 23:49:18 +0000326 free(cmd->cmdBuf);
327}
328
329/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000330static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000331{
Erik Andersend75af992000-03-16 08:09:09 +0000332 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000333
Erik Andersend75af992000-03-16 08:09:09 +0000334 freeJob(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000335 if (job == jobList->head) {
Erik Andersend75af992000-03-16 08:09:09 +0000336 jobList->head = job->next;
Erik Andersen3522eb12000-03-12 23:49:18 +0000337 } else {
Erik Andersend75af992000-03-16 08:09:09 +0000338 prevJob = jobList->head;
339 while (prevJob->next != job)
340 prevJob = prevJob->next;
341 prevJob->next = job->next;
Erik Andersen3522eb12000-03-12 23:49:18 +0000342 }
343
344 free(job);
345}
346
347/* Checks to see if any background processes have exited -- if they
348 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000349static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000350{
Erik Andersend75af992000-03-16 08:09:09 +0000351 struct job *job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000352 pid_t childpid;
353 int status;
Erik Andersend75af992000-03-16 08:09:09 +0000354 int progNum = 0;
355
Erik Andersen3522eb12000-03-12 23:49:18 +0000356 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
Erik Andersend75af992000-03-16 08:09:09 +0000357 for (job = jobList->head; job; job = job->next) {
358 progNum = 0;
359 while (progNum < job->numProgs &&
360 job->progs[progNum].pid != childpid) progNum++;
361 if (progNum < job->numProgs)
362 break;
363 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000364
Erik Andersend75af992000-03-16 08:09:09 +0000365 if (WIFEXITED(status) || WIFSIGNALED(status)) {
366 /* child exited */
367 job->runningProgs--;
368 job->progs[progNum].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000369
Erik Andersend75af992000-03-16 08:09:09 +0000370 if (!job->runningProgs) {
371 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
372 removeJob(jobList, job);
373 }
374 } else {
375 /* child stopped */
376 job->stoppedProgs++;
377 job->progs[progNum].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000378
Erik Andersend75af992000-03-16 08:09:09 +0000379 if (job->stoppedProgs == job->numProgs) {
380 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
381 job->text);
382 }
383 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000384 }
385
386 if (childpid == -1 && errno != ECHILD)
Erik Andersend75af992000-03-16 08:09:09 +0000387 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000388}
389
Erik Andersend75af992000-03-16 08:09:09 +0000390static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000391{
392 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000393 fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
394 fflush(stdout);
395#ifdef BB_FEATURE_SH_COMMAND_EDITING
396 cmdedit_read_input(fileno(stdin), fileno(stdout), command);
397 return 0;
398#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000399 }
400
Erik Andersend75af992000-03-16 08:09:09 +0000401 if (!fgets(command, BUFSIZ - 2, source)) {
402 if (source == stdin)
403 printf("\n");
404 return 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000405 }
406
407 /* remove trailing newline */
408 command[strlen(command) - 1] = '\0';
409
410 return 0;
411}
412
Erik Andersend75af992000-03-16 08:09:09 +0000413static void globLastArgument(struct childProgram *prog, int *argcPtr,
414 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000415{
416 int argc = *argcPtr;
417 int argcAlloced = *argcAllocedPtr;
418 int rc;
419 int flags;
420 int i;
Erik Andersend75af992000-03-16 08:09:09 +0000421 char *src, *dst;
Erik Andersen3522eb12000-03-12 23:49:18 +0000422
Erik Andersend75af992000-03-16 08:09:09 +0000423 if (argc > 1) { /* cmd->globResult is already initialized */
424 flags = GLOB_APPEND;
425 i = prog->globResult.gl_pathc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000426 } else {
Erik Andersend75af992000-03-16 08:09:09 +0000427 prog->freeGlob = 1;
428 flags = 0;
429 i = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000430 }
431
432 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
433 if (rc == GLOB_NOSPACE) {
Erik Andersend75af992000-03-16 08:09:09 +0000434 fprintf(stderr, "out of space during glob operation\n");
435 return;
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];
442 while (*src) {
443 if (*src != '\\')
444 *dst++ = *src;
445 src++;
446 }
447 *dst = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000448 } else if (!rc) {
Erik Andersend75af992000-03-16 08:09:09 +0000449 argcAlloced += (prog->globResult.gl_pathc - i);
450 prog->argv =
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);
Erik Andersen3522eb12000-03-12 23:49:18 +0000455 }
456
457 *argcAllocedPtr = argcAlloced;
458 *argcPtr = argc;
459}
460
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
465 present. */
Erik Andersend75af992000-03-16 08:09:09 +0000466static int parseCommand(char **commandPtr, struct job *job, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000467{
Erik Andersend75af992000-03-16 08:09:09 +0000468 char *command;
469 char *returnCommand = NULL;
470 char *src, *buf, *chptr;
Erik Andersen3522eb12000-03-12 23:49:18 +0000471 int argc = 0;
472 int done = 0;
473 int argvAlloced;
474 int i;
Erik Andersend75af992000-03-16 08:09:09 +0000475 char quote = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000476 int count;
Erik Andersend75af992000-03-16 08:09:09 +0000477 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000478
479 /* skip leading white space */
Erik Andersend75af992000-03-16 08:09:09 +0000480 while (**commandPtr && isspace(**commandPtr))
481 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000482
Erik Andersend75af992000-03-16 08:09:09 +0000483 /* this handles empty lines or leading '#' characters */
484 if (!**commandPtr || (**commandPtr == '#')) {
485 job->numProgs = 0;
486 *commandPtr = NULL;
487 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000488 }
489
490 *isBg = 0;
491 job->numProgs = 1;
492 job->progs = malloc(sizeof(*job->progs));
493
494 /* We set the argv elements to point inside of this string. The
495 memory is freed by freeJob().
496
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);
501 job->text = NULL;
502
503 prog = job->progs;
504 prog->numRedirections = 0;
505 prog->redirections = NULL;
506 prog->freeGlob = 0;
507 prog->isStopped = 0;
508
509 argvAlloced = 5;
510 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
511 prog->argv[0] = job->cmdBuf;
512
513 buf = command;
514 src = *commandPtr;
515 while (*src && !done) {
Erik Andersend75af992000-03-16 08:09:09 +0000516 if (quote == *src) {
517 quote = '\0';
518 } else if (quote) {
519 if (*src == '\\') {
520 src++;
521 if (!*src) {
522 fprintf(stderr, "character expected after \\\n");
523 freeJob(job);
524 return 1;
525 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000526
Erik Andersend75af992000-03-16 08:09:09 +0000527 /* in shell, "\'" should yield \' */
528 if (*src != quote)
529 *buf++ = '\\';
530 } else if (*src == '*' || *src == '?' || *src == '[' ||
531 *src == ']') *buf++ = '\\';
532 *buf++ = *src;
533 } else if (isspace(*src)) {
534 if (*prog->argv[argc]) {
535 buf++, argc++;
536 /* +1 here leaves room for the NULL which ends argv */
537 if ((argc + 1) == argvAlloced) {
538 argvAlloced += 5;
539 prog->argv = realloc(prog->argv,
540 sizeof(*prog->argv) *
541 argvAlloced);
542 }
543 prog->argv[argc] = buf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000544
Erik Andersend75af992000-03-16 08:09:09 +0000545 globLastArgument(prog, &argc, &argvAlloced);
546 }
547 } else
548 switch (*src) {
549 case '"':
550 case '\'':
551 quote = *src;
552 break;
Erik Andersen3522eb12000-03-12 23:49:18 +0000553
Erik Andersend75af992000-03-16 08:09:09 +0000554 case '#': /* comment */
555 done = 1;
556 break;
Erik Andersen3522eb12000-03-12 23:49:18 +0000557
Erik Andersend75af992000-03-16 08:09:09 +0000558 case '>': /* redirections */
559 case '<':
560 i = prog->numRedirections++;
561 prog->redirections = realloc(prog->redirections,
562 sizeof(*prog->redirections) *
563 (i + 1));
Erik Andersen3522eb12000-03-12 23:49:18 +0000564
Erik Andersend75af992000-03-16 08:09:09 +0000565 prog->redirections[i].fd = -1;
566 if (buf != prog->argv[argc]) {
567 /* the stuff before this character may be the file number
568 being redirected */
569 prog->redirections[i].fd =
570 strtol(prog->argv[argc], &chptr, 10);
Erik Andersen3522eb12000-03-12 23:49:18 +0000571
Erik Andersend75af992000-03-16 08:09:09 +0000572 if (*chptr && *prog->argv[argc]) {
573 buf++, argc++;
574 globLastArgument(prog, &argc, &argvAlloced);
575 }
576 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000577
Erik Andersend75af992000-03-16 08:09:09 +0000578 if (prog->redirections[i].fd == -1) {
579 if (*src == '>')
580 prog->redirections[i].fd = 1;
581 else
582 prog->redirections[i].fd = 0;
583 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000584
Erik Andersend75af992000-03-16 08:09:09 +0000585 if (*src++ == '>') {
586 if (*src == '>')
587 prog->redirections[i].type =
588 REDIRECT_APPEND, src++;
589 else
590 prog->redirections[i].type = REDIRECT_OVERWRITE;
591 } else {
592 prog->redirections[i].type = REDIRECT_INPUT;
593 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000594
Erik Andersend75af992000-03-16 08:09:09 +0000595 /* This isn't POSIX sh compliant. Oh well. */
596 chptr = src;
597 while (isspace(*chptr))
598 chptr++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000599
Erik Andersend75af992000-03-16 08:09:09 +0000600 if (!*chptr) {
601 fprintf(stderr, "file name expected after %c\n", *src);
602 freeJob(job);
603 return 1;
604 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000605
Erik Andersend75af992000-03-16 08:09:09 +0000606 prog->redirections[i].filename = buf;
607 while (*chptr && !isspace(*chptr))
608 *buf++ = *chptr++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000609
Erik Andersend75af992000-03-16 08:09:09 +0000610 src = chptr - 1; /* we src++ later */
611 prog->argv[argc] = ++buf;
612 break;
Erik Andersen3522eb12000-03-12 23:49:18 +0000613
Erik Andersend75af992000-03-16 08:09:09 +0000614 case '|': /* pipe */
615 /* finish this command */
616 if (*prog->argv[argc])
617 argc++;
618 if (!argc) {
619 fprintf(stderr, "empty command in pipe\n");
620 freeJob(job);
621 return 1;
622 }
623 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000624
Erik Andersend75af992000-03-16 08:09:09 +0000625 /* and start the next */
626 job->numProgs++;
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;
632 prog->freeGlob = 0;
633 argc = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000634
Erik Andersend75af992000-03-16 08:09:09 +0000635 argvAlloced = 5;
636 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
637 prog->argv[0] = ++buf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000638
Erik Andersend75af992000-03-16 08:09:09 +0000639 src++;
640 while (*src && isspace(*src))
641 src++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000642
Erik Andersend75af992000-03-16 08:09:09 +0000643 if (!*src) {
644 fprintf(stderr, "empty command in pipe\n");
645 return 1;
646 }
647 src--; /* we'll ++ it at the end of the loop */
Erik Andersen3522eb12000-03-12 23:49:18 +0000648
Erik Andersend75af992000-03-16 08:09:09 +0000649 break;
Erik Andersen3522eb12000-03-12 23:49:18 +0000650
Erik Andersend75af992000-03-16 08:09:09 +0000651 case '&': /* background */
652 *isBg = 1;
653 case ';': /* multiple commands */
654 done = 1;
655 returnCommand = *commandPtr + (src - *commandPtr) + 1;
656 break;
Erik Andersen3522eb12000-03-12 23:49:18 +0000657
Erik Andersend75af992000-03-16 08:09:09 +0000658 case '\\':
659 src++;
660 if (!*src) {
661 freeJob(job);
662 fprintf(stderr, "character expected after \\\n");
663 return 1;
664 }
665 if (*src == '*' || *src == '[' || *src == ']'
666 || *src == '?') *buf++ = '\\';
667 /* fallthrough */
668 default:
669 *buf++ = *src;
670 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000671
Erik Andersend75af992000-03-16 08:09:09 +0000672 src++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000673 }
674
675 if (*prog->argv[argc]) {
Erik Andersend75af992000-03-16 08:09:09 +0000676 argc++;
677 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen3522eb12000-03-12 23:49:18 +0000678 }
679 if (!argc) {
Erik Andersend75af992000-03-16 08:09:09 +0000680 freeJob(job);
681 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000682 }
683 prog->argv[argc] = NULL;
684
685 if (!returnCommand) {
Erik Andersend75af992000-03-16 08:09:09 +0000686 job->text = malloc(strlen(*commandPtr) + 1);
687 strcpy(job->text, *commandPtr);
Erik Andersen3522eb12000-03-12 23:49:18 +0000688 } else {
Erik Andersend75af992000-03-16 08:09:09 +0000689 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen3522eb12000-03-12 23:49:18 +0000690
Erik Andersend75af992000-03-16 08:09:09 +0000691 count = returnCommand - *commandPtr;
692 job->text = malloc(count + 1);
693 strncpy(job->text, *commandPtr, count);
694 job->text[count] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000695 }
696
697 *commandPtr = returnCommand;
698
699 return 0;
700}
701
Erik Andersend75af992000-03-16 08:09:09 +0000702static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000703{
Erik Andersend75af992000-03-16 08:09:09 +0000704 struct job *job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000705 int i;
706 int nextin, nextout;
Erik Andersend75af992000-03-16 08:09:09 +0000707 int pipefds[2]; /* pipefd[0] is for reading */
Erik Andersen3522eb12000-03-12 23:49:18 +0000708 struct builtInCommand *x;
709
710 /* handle built-ins here -- we don't fork() so we can't background
711 these very easily */
Erik Andersend75af992000-03-16 08:09:09 +0000712 for (x = bltins; x->cmd; x++) {
713 if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
714 return (x->function(&newJob, jobList));
715 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000716 }
717
718 nextin = 0, nextout = 1;
719 for (i = 0; i < newJob.numProgs; i++) {
Erik Andersend75af992000-03-16 08:09:09 +0000720 if ((i + 1) < newJob.numProgs) {
721 pipe(pipefds);
722 nextout = pipefds[1];
723 } else {
724 nextout = 1;
725 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000726
Erik Andersend75af992000-03-16 08:09:09 +0000727 if (!(newJob.progs[i].pid = fork())) {
728 signal(SIGTTOU, SIG_DFL);
Erik Andersen3522eb12000-03-12 23:49:18 +0000729
Erik Andersend75af992000-03-16 08:09:09 +0000730 if (nextin != 0) {
731 dup2(nextin, 0);
732 close(nextin);
733 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000734
Erik Andersend75af992000-03-16 08:09:09 +0000735 if (nextout != 1) {
736 dup2(nextout, 1);
737 close(nextout);
738 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000739
Erik Andersend75af992000-03-16 08:09:09 +0000740 /* explicit redirections override pipes */
741 setupRedirections(newJob.progs + i);
Erik Andersen3522eb12000-03-12 23:49:18 +0000742
Erik Andersend75af992000-03-16 08:09:09 +0000743 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
744 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
745 strerror(errno));
746 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000747
Erik Andersend75af992000-03-16 08:09:09 +0000748 /* 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);
Erik Andersen3522eb12000-03-12 23:49:18 +0000751
Erik Andersend75af992000-03-16 08:09:09 +0000752 if (nextin != 0)
753 close(nextin);
754 if (nextout != 1)
755 close(nextout);
Erik Andersen3522eb12000-03-12 23:49:18 +0000756
Erik Andersend75af992000-03-16 08:09:09 +0000757 /* If there isn't another process, nextin is garbage
758 but it doesn't matter */
759 nextin = pipefds[0];
Erik Andersen3522eb12000-03-12 23:49:18 +0000760 }
761
762 newJob.pgrp = newJob.progs[0].pid;
763
764 /* find the ID for the job to use */
765 newJob.jobId = 1;
766 for (job = jobList->head; job; job = job->next)
Erik Andersend75af992000-03-16 08:09:09 +0000767 if (job->jobId >= newJob.jobId)
768 newJob.jobId = job->jobId + 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000769
770 /* add the job to the list of running jobs */
771 if (!jobList->head) {
Erik Andersend75af992000-03-16 08:09:09 +0000772 job = jobList->head = malloc(sizeof(*job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000773 } else {
Erik Andersend75af992000-03-16 08:09:09 +0000774 for (job = jobList->head; job->next; job = job->next);
775 job->next = malloc(sizeof(*job));
776 job = job->next;
Erik Andersen3522eb12000-03-12 23:49:18 +0000777 }
778
779 *job = newJob;
780 job->next = NULL;
781 job->runningProgs = job->numProgs;
782 job->stoppedProgs = 0;
783
784 if (inBg) {
Erik Andersend75af992000-03-16 08:09:09 +0000785 /* we don't wait for background jobs to return -- append it
786 to the list of backgrounded jobs and leave it alone */
Erik Andersen3522eb12000-03-12 23:49:18 +0000787
Erik Andersend75af992000-03-16 08:09:09 +0000788 printf("[%d] %d\n", job->jobId,
789 newJob.progs[newJob.numProgs - 1].pid);
Erik Andersen3522eb12000-03-12 23:49:18 +0000790 } else {
Erik Andersend75af992000-03-16 08:09:09 +0000791 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000792
Erik Andersend75af992000-03-16 08:09:09 +0000793 /* move the new process group into the foreground */
794
795 if (tcsetpgrp(0, newJob.pgrp))
796 perror("tcsetpgrp");
Erik Andersen3522eb12000-03-12 23:49:18 +0000797 }
798
799 return 0;
800}
801
Erik Andersend75af992000-03-16 08:09:09 +0000802static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000803{
804 int i;
805 int openfd;
Erik Andersend75af992000-03-16 08:09:09 +0000806 int mode = O_RDONLY;
807 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000808
809 for (i = 0; i < prog->numRedirections; i++, redir++) {
Erik Andersend75af992000-03-16 08:09:09 +0000810 switch (redir->type) {
811 case REDIRECT_INPUT:
812 mode = O_RDONLY;
813 break;
814 case REDIRECT_OVERWRITE:
815 mode = O_RDWR | O_CREAT | O_TRUNC;
816 break;
817 case REDIRECT_APPEND:
818 mode = O_RDWR | O_CREAT | O_APPEND;
819 break;
820 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000821
Erik Andersend75af992000-03-16 08:09:09 +0000822 openfd = open(redir->filename, mode, 0666);
823 if (openfd < 0) {
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,
827 strerror(errno));
828 return 1;
829 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000830
Erik Andersend75af992000-03-16 08:09:09 +0000831 if (openfd != redir->fd) {
832 dup2(openfd, redir->fd);
833 close(openfd);
834 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000835 }
836
837 return 0;
838}
839
840
Erik Andersend75af992000-03-16 08:09:09 +0000841static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000842{
Erik Andersend75af992000-03-16 08:09:09 +0000843 char *command;
844 char *nextCommand = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000845 struct jobSet jobList = { NULL, NULL };
846 struct job newJob;
847 int i;
848 int status;
849 int inBg;
850
Erik Andersend75af992000-03-16 08:09:09 +0000851 command = (char*) calloc(BUFSIZ, sizeof(char));
852
Erik Andersen3522eb12000-03-12 23:49:18 +0000853 /* 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);
Erik Andersend75af992000-03-16 08:09:09 +0000856
Erik Andersen3522eb12000-03-12 23:49:18 +0000857 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000858 if (!jobList.fg) {
859 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000860
Erik Andersend75af992000-03-16 08:09:09 +0000861 /* see if any background processes have exited */
862 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +0000863
Erik Andersend75af992000-03-16 08:09:09 +0000864 if (!nextCommand) {
865 if (getCommand(input, command))
866 break;
867 nextCommand = command;
868 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000869
Erik Andersend75af992000-03-16 08:09:09 +0000870 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
871 newJob.numProgs) {
872 runCommand(newJob, &jobList, inBg);
873 }
874 } else {
875 /* a job is running in the foreground; wait for it */
876 i = 0;
877 while (!jobList.fg->progs[i].pid ||
878 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000879
Erik Andersend75af992000-03-16 08:09:09 +0000880 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +0000881
Erik Andersend75af992000-03-16 08:09:09 +0000882 if (WIFEXITED(status) || WIFSIGNALED(status)) {
883 /* the child exited */
884 jobList.fg->runningProgs--;
885 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000886
Erik Andersend75af992000-03-16 08:09:09 +0000887 if (!jobList.fg->runningProgs) {
888 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +0000889
Erik Andersend75af992000-03-16 08:09:09 +0000890 removeJob(&jobList, jobList.fg);
891 jobList.fg = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000892
Erik Andersend75af992000-03-16 08:09:09 +0000893 /* move the shell to the foreground */
894 if (tcsetpgrp(0, getpid()))
895 perror("tcsetpgrp");
896 }
897 } else {
898 /* the child was stopped */
899 jobList.fg->stoppedProgs++;
900 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000901
Erik Andersend75af992000-03-16 08:09:09 +0000902 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
903 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
904 "Stopped", jobList.fg->text);
905 jobList.fg = NULL;
906 }
907 }
908
909 if (!jobList.fg) {
910 /* move the shell to the foreground */
911 if (tcsetpgrp(0, getpid()))
912 perror("tcsetpgrp");
913 }
914 }
915 }
916 free( command);
Erik Andersen3522eb12000-03-12 23:49:18 +0000917
918 return 0;
919}
920
921
Erik Andersend75af992000-03-16 08:09:09 +0000922int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +0000923{
Erik Andersend75af992000-03-16 08:09:09 +0000924 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +0000925
926 if (argc > 2) {
Erik Andersend75af992000-03-16 08:09:09 +0000927 usage(shell_usage);
928 }
929 /* initialize the cwd */
930 getcwd(cwd, sizeof(cwd));
Erik Andersen3522eb12000-03-12 23:49:18 +0000931
932
Erik Andersend75af992000-03-16 08:09:09 +0000933 //if (argv[0] && argv[0][0] == '-') {
934 // shell_source("/etc/profile");
935 //}
Erik Andersen3522eb12000-03-12 23:49:18 +0000936
Erik Andersend75af992000-03-16 08:09:09 +0000937 if (argc < 2) {
938 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
939 BB_BT);
940 fprintf(stdout,
941 "Enter 'help' for a list of built-in commands.\n\n");
942 } else {
943 input = fopen(argv[1], "r");
944 if (!input)
945 fatalError("A: Couldn't open file '%s': %s\n", argv[1],
946 strerror(errno));
947// else
948// fatalError("Got it.\n");
949 //exit(shell_source(argv[1]));
950
951 /* Set terminal IO to canonical mode, and save old term settings. */
952#ifdef BB_FEATURE_SH_COMMAND_EDITING
953 cmdedit_init();
954#endif
955 }
956
957 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +0000958}