blob: 9b3435304926289db4301c2ccd7c781a7e63361c [file] [log] [blame]
Erik Andersen3522eb12000-03-12 23:49:18 +00001/* vi: set sw=4 ts=4: */
2/*
Erik Andersen6acaa402000-03-26 14:03:20 +00003 * lash -- the BusyBox Lame-Ass SHell
Erik Andersen3522eb12000-03-12 23:49:18 +00004 *
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>
Erik Andersenf0657d32000-04-12 17:49:52 +000040#ifdef BB_FEATURE_SH_COMMAND_EDITING
41#include "cmdedit.h"
42#endif
Erik Andersen3522eb12000-03-12 23:49:18 +000043
Eric Andersenb54833c2000-07-03 23:56:26 +000044#define MAX_READ 128 /* size of input buffer for `read' builtin */
Erik Andersen3522eb12000-03-12 23:49:18 +000045#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
46
Erik Andersend75af992000-03-16 08:09:09 +000047
48enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000049 REDIRECT_APPEND
50};
Erik Andersen3522eb12000-03-12 23:49:18 +000051
52struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +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 Andersen161220c2000-03-16 08:12:48 +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 Andersen161220c2000-03-16 08:12:48 +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 Andersen161220c2000-03-16 08:12:48 +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 Andersen161220c2000-03-16 08:12:48 +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
Eric Andersen34e19412000-07-10 18:47:24 +000092/* function prototypes for builtins */
93static int builtin_cd(struct job *cmd, struct jobSet *junk);
94static int builtin_env(struct job *dummy, struct jobSet *junk);
95static int builtin_exit(struct job *cmd, struct jobSet *junk);
96static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
97static int builtin_help(struct job *cmd, struct jobSet *junk);
98static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
99static int builtin_pwd(struct job *dummy, struct jobSet *junk);
100static int builtin_export(struct job *cmd, struct jobSet *junk);
101static int builtin_source(struct job *cmd, struct jobSet *jobList);
102static int builtin_unset(struct job *cmd, struct jobSet *junk);
103static int builtin_read(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000104
Eric Andersen34e19412000-07-10 18:47:24 +0000105
106/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000107static void checkJobs(struct jobSet *jobList);
108static int getCommand(FILE * source, char *command);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000109static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
Erik Andersend75af992000-03-16 08:09:09 +0000110static int setupRedirections(struct childProgram *prog);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000111static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg);
Erik Andersen3522eb12000-03-12 23:49:18 +0000112static int busy_loop(FILE * input);
113
Erik Andersend75af992000-03-16 08:09:09 +0000114
Mark Whitley37653aa2000-07-12 23:36:17 +0000115/* Table of built-in functions (these are non-forking builtins, meaning they
116 * can change global variables in the parent shell process but they will not
117 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000118static struct builtInCommand bltins[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000119 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
120 {"cd", "Change working directory", "cd [dir]", builtin_cd},
121 {"exit", "Exit from shell()", "exit", builtin_exit},
122 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
123 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
124 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
125 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
126 {"read", "Input environment variable", "read [VAR]", builtin_read},
Erik Andersen330fd2b2000-05-19 05:35:19 +0000127 {NULL, NULL, NULL, NULL}
128};
129
Mark Whitley37653aa2000-07-12 23:36:17 +0000130/* Table of forking built-in functions (things that fork cannot change global
131 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000132static struct builtInCommand bltins_forking[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000133 {"env", "Print all environment variables", "env", builtin_env},
134 {"pwd", "Print current directory", "pwd", builtin_pwd},
135 {".", "Source-in and run commands in a file", ". filename", builtin_source},
136 {"help", "List shell built-in commands", "help", builtin_help},
Erik Andersen161220c2000-03-16 08:12:48 +0000137 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000138};
139
Erik Andersen3522eb12000-03-12 23:49:18 +0000140static char *prompt = "# ";
Eric Andersenb54833c2000-07-03 23:56:26 +0000141static char *cwd = NULL;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000142static char *local_pending_command = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000143
Erik Andersenf0657d32000-04-12 17:49:52 +0000144#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenfad04fd2000-07-14 06:49:52 +0000145void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000146{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000147 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000148 ioctl(0, TIOCGWINSZ, &win);
149 if (win.ws_col > 0) {
150 cmdedit_setwidth( win.ws_col - 1);
151 }
152}
153#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000154
Erik Andersen3522eb12000-03-12 23:49:18 +0000155
Erik Andersend75af992000-03-16 08:09:09 +0000156/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000157static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000158{
Erik Andersen161220c2000-03-16 08:12:48 +0000159 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000160
Erik Andersen161220c2000-03-16 08:12:48 +0000161 if (!cmd->progs[0].argv[1] == 1)
162 newdir = getenv("HOME");
163 else
164 newdir = cmd->progs[0].argv[1];
165 if (chdir(newdir)) {
166 printf("cd: %s: %s\n", newdir, strerror(errno));
167 return FALSE;
168 }
169 getcwd(cwd, sizeof(cwd));
170
171 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000172}
173
174/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000175static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000176{
Erik Andersen161220c2000-03-16 08:12:48 +0000177 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000178
Erik Andersen161220c2000-03-16 08:12:48 +0000179 for (e = environ; *e; e++) {
180 fprintf(stdout, "%s\n", *e);
181 }
182 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000183}
184
185/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000186static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000187{
Erik Andersen161220c2000-03-16 08:12:48 +0000188 if (!cmd->progs[0].argv[1] == 1)
189 exit TRUE;
190
Eric Andersenb6106152000-06-19 17:25:40 +0000191 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000192}
193
194/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000195static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000196{
Erik Andersen161220c2000-03-16 08:12:48 +0000197 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000198 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000199
Erik Andersen161220c2000-03-16 08:12:48 +0000200 if (!jobList->head) {
201 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000202 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000203 cmd->progs[0].argv[0]);
204 return FALSE;
205 }
206 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000207 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000208 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
209 return FALSE;
210 for (job = jobList->head; job; job = job->next) {
211 if (job->jobId == jobNum) {
212 break;
213 }
214 }
215 }
216 } else {
217 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000218 }
Erik Andersen161220c2000-03-16 08:12:48 +0000219
220 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000221 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000222 cmd->progs[0].argv[0], jobNum);
223 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000224 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000225
Erik Andersen161220c2000-03-16 08:12:48 +0000226 if (*cmd->progs[0].argv[0] == 'f') {
227 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000228 /* suppress messages when run from /linuxrc mag@sysgo.de */
229 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
230 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000231 jobList->fg = job;
232 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000233
Erik Andersen161220c2000-03-16 08:12:48 +0000234 /* Restart the processes in the job */
235 for (i = 0; i < job->numProgs; i++)
236 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000237
Erik Andersen161220c2000-03-16 08:12:48 +0000238 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000239
Erik Andersen161220c2000-03-16 08:12:48 +0000240 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000241
Erik Andersen161220c2000-03-16 08:12:48 +0000242 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000243}
244
245/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000246static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000247{
Erik Andersen161220c2000-03-16 08:12:48 +0000248 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000249
Erik Andersen161220c2000-03-16 08:12:48 +0000250 fprintf(stdout, "\nBuilt-in commands:\n");
251 fprintf(stdout, "-------------------\n");
252 for (x = bltins; x->cmd; x++) {
253 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
254 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000255 for (x = bltins_forking; x->cmd; x++) {
256 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
257 }
Erik Andersen161220c2000-03-16 08:12:48 +0000258 fprintf(stdout, "\n\n");
259 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000260}
261
262/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000263static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000264{
Erik Andersen161220c2000-03-16 08:12:48 +0000265 struct job *job;
266 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000267
Erik Andersen161220c2000-03-16 08:12:48 +0000268 for (job = jobList->head; job; job = job->next) {
269 if (job->runningProgs == job->stoppedProgs)
270 statusString = "Stopped";
271 else
272 statusString = "Running";
273
274 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
275 }
276 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000277}
278
279
280/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000281static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000282{
Erik Andersen161220c2000-03-16 08:12:48 +0000283 getcwd(cwd, sizeof(cwd));
284 fprintf(stdout, "%s\n", cwd);
285 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000286}
287
Erik Andersen6273f652000-03-17 01:12:41 +0000288/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000289static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000290{
Erik Andersen161220c2000-03-16 08:12:48 +0000291 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000292
Erik Andersen161220c2000-03-16 08:12:48 +0000293 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000294 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000295 }
296 res = putenv(cmd->progs[0].argv[1]);
297 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000298 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000299 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000300}
301
Eric Andersenb54833c2000-07-03 23:56:26 +0000302/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000303static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000304{
305 int res = 0, len, newlen;
306 char *s;
307 char string[MAX_READ];
308
309 if (cmd->progs[0].argv[1]) {
310 /* argument (VAR) given: put "VAR=" into buffer */
311 strcpy(string, cmd->progs[0].argv[1]);
312 len = strlen(string);
313 string[len++] = '=';
314 string[len] = '\0';
315 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
316 newlen = strlen(string);
317 if(newlen > len)
318 string[--newlen] = '\0'; /* chomp trailing newline */
319 /*
320 ** string should now contain "VAR=<value>"
321 ** copy it (putenv() won't do that, so we must make sure
322 ** the string resides in a static buffer!)
323 */
324 res = -1;
325 if((s = strdup(string)))
326 res = putenv(s);
327 if (res)
328 fprintf(stdout, "read: %s\n", strerror(errno));
329 }
330 else
331 fgets(string, sizeof(string), stdin);
332
333 return (res);
334}
335
Erik Andersen3522eb12000-03-12 23:49:18 +0000336/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000337static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000338{
Erik Andersen161220c2000-03-16 08:12:48 +0000339 FILE *input;
340 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000341
Erik Andersen161220c2000-03-16 08:12:48 +0000342 if (!cmd->progs[0].argv[1] == 1)
343 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000344
Erik Andersen161220c2000-03-16 08:12:48 +0000345 input = fopen(cmd->progs[0].argv[1], "r");
346 if (!input) {
347 fprintf(stdout, "Couldn't open file '%s'\n",
348 cmd->progs[0].argv[1]);
349 return FALSE;
350 }
Erik Andersend75af992000-03-16 08:09:09 +0000351
Erik Andersen161220c2000-03-16 08:12:48 +0000352 /* Now run the file */
353 status = busy_loop(input);
354 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000355}
356
357/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000358static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000359{
Erik Andersen161220c2000-03-16 08:12:48 +0000360 if (!cmd->progs[0].argv[1] == 1) {
361 fprintf(stdout, "unset: parameter required.\n");
362 return FALSE;
363 }
364 unsetenv(cmd->progs[0].argv[1]);
365 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000366}
367
368/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000369static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000370{
Erik Andersen161220c2000-03-16 08:12:48 +0000371 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000372
Erik Andersen161220c2000-03-16 08:12:48 +0000373 for (i = 0; i < cmd->numProgs; i++) {
374 free(cmd->progs[i].argv);
375 if (cmd->progs[i].redirections)
376 free(cmd->progs[i].redirections);
377 if (cmd->progs[i].freeGlob)
378 globfree(&cmd->progs[i].globResult);
379 }
380 free(cmd->progs);
381 if (cmd->text)
382 free(cmd->text);
383 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000384 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000385}
386
387/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000388static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000389{
Erik Andersen161220c2000-03-16 08:12:48 +0000390 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000391
Erik Andersen161220c2000-03-16 08:12:48 +0000392 freeJob(job);
393 if (job == jobList->head) {
394 jobList->head = job->next;
395 } else {
396 prevJob = jobList->head;
397 while (prevJob->next != job)
398 prevJob = prevJob->next;
399 prevJob->next = job->next;
400 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000401
Erik Andersen161220c2000-03-16 08:12:48 +0000402 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000403}
404
405/* Checks to see if any background processes have exited -- if they
406 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000407static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000408{
Erik Andersen161220c2000-03-16 08:12:48 +0000409 struct job *job;
410 pid_t childpid;
411 int status;
412 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000413
Erik Andersen161220c2000-03-16 08:12:48 +0000414 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
415 for (job = jobList->head; job; job = job->next) {
416 progNum = 0;
417 while (progNum < job->numProgs &&
418 job->progs[progNum].pid != childpid) progNum++;
419 if (progNum < job->numProgs)
420 break;
421 }
422
423 if (WIFEXITED(status) || WIFSIGNALED(status)) {
424 /* child exited */
425 job->runningProgs--;
426 job->progs[progNum].pid = 0;
427
428 if (!job->runningProgs) {
429 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
430 removeJob(jobList, job);
431 }
432 } else {
433 /* child stopped */
434 job->stoppedProgs++;
435 job->progs[progNum].isStopped = 1;
436
437 if (job->stoppedProgs == job->numProgs) {
438 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
439 job->text);
440 }
441 }
Erik Andersend75af992000-03-16 08:09:09 +0000442 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000443
Erik Andersen161220c2000-03-16 08:12:48 +0000444 if (childpid == -1 && errno != ECHILD)
445 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000446}
447
Erik Andersend75af992000-03-16 08:09:09 +0000448static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000449{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000450 if (source == NULL) {
451 if (local_pending_command) {
452 /* a command specified (-c option): return it & mark it done */
453 strcpy(command, local_pending_command);
454 free(local_pending_command);
455 local_pending_command = NULL;
456 return 0;
457 }
458 return 1;
459 }
460
Erik Andersen161220c2000-03-16 08:12:48 +0000461 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000462#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000463 int len;
464 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000465 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000466 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000467 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000468 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000469 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000470 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000471 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000472#else
473 fprintf(stdout, "%s %s", cwd, prompt);
474 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000475#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000476 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000477
Erik Andersen161220c2000-03-16 08:12:48 +0000478 if (!fgets(command, BUFSIZ - 2, source)) {
479 if (source == stdin)
480 printf("\n");
481 return 1;
482 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000483
Erik Andersen161220c2000-03-16 08:12:48 +0000484 /* remove trailing newline */
485 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000486
Erik Andersen161220c2000-03-16 08:12:48 +0000487 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000488}
489
Erik Andersend75af992000-03-16 08:09:09 +0000490static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000491 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000492{
Erik Andersen161220c2000-03-16 08:12:48 +0000493 int argc = *argcPtr;
494 int argcAlloced = *argcAllocedPtr;
495 int rc;
496 int flags;
497 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000498 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000499
Erik Andersen161220c2000-03-16 08:12:48 +0000500 if (argc > 1) { /* cmd->globResult is already initialized */
501 flags = GLOB_APPEND;
502 i = prog->globResult.gl_pathc;
503 } else {
504 prog->freeGlob = 1;
505 flags = 0;
506 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000507 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000508 /* do shell variable substitution */
509 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
510 prog->argv[argc - 1] = var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000511
Erik Andersen161220c2000-03-16 08:12:48 +0000512 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
513 if (rc == GLOB_NOSPACE) {
Matt Kraaid537a952000-07-14 01:51:25 +0000514 errorMsg("out of space during glob operation\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000515 return;
516 } else if (rc == GLOB_NOMATCH ||
517 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
518 !strcmp(prog->argv[argc - 1],
519 prog->globResult.gl_pathv[i]))) {
520 /* we need to remove whatever \ quoting is still present */
521 src = dst = prog->argv[argc - 1];
522 while (*src) {
523 if (*src != '\\')
524 *dst++ = *src;
525 src++;
526 }
527 *dst = '\0';
528 } else if (!rc) {
529 argcAlloced += (prog->globResult.gl_pathc - i);
530 prog->argv =
531 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
532 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
533 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
534 argc += (prog->globResult.gl_pathc - i - 1);
535 }
536
537 *argcAllocedPtr = argcAlloced;
538 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000539}
540
541/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
542 line). If a valid command is found, commandPtr is set to point to
543 the beginning of the next command (if the original command had more
544 then one job associated with it) or NULL if no more commands are
545 present. */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000546static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000547{
Erik Andersen161220c2000-03-16 08:12:48 +0000548 char *command;
549 char *returnCommand = NULL;
550 char *src, *buf, *chptr;
551 int argc = 0;
552 int done = 0;
553 int argvAlloced;
554 int i;
555 char quote = '\0';
556 int count;
557 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000558
Erik Andersen161220c2000-03-16 08:12:48 +0000559 /* skip leading white space */
560 while (**commandPtr && isspace(**commandPtr))
561 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000562
Erik Andersen161220c2000-03-16 08:12:48 +0000563 /* this handles empty lines or leading '#' characters */
564 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000565 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000566 return 0;
567 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000568
Erik Andersen161220c2000-03-16 08:12:48 +0000569 *isBg = 0;
570 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000571 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000572
Erik Andersen161220c2000-03-16 08:12:48 +0000573 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000574 memory is freed by freeJob(). Allocate twice the original
575 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000576
Erik Andersen161220c2000-03-16 08:12:48 +0000577 Getting clean memory relieves us of the task of NULL
578 terminating things and makes the rest of this look a bit
579 cleaner (though it is, admittedly, a tad less efficient) */
Eric Andersenb54833c2000-07-03 23:56:26 +0000580 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000581 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000582
Erik Andersen161220c2000-03-16 08:12:48 +0000583 prog = job->progs;
584 prog->numRedirections = 0;
585 prog->redirections = NULL;
586 prog->freeGlob = 0;
587 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000588
Erik Andersen161220c2000-03-16 08:12:48 +0000589 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000590 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000591 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000592
Erik Andersen161220c2000-03-16 08:12:48 +0000593 buf = command;
594 src = *commandPtr;
595 while (*src && !done) {
596 if (quote == *src) {
597 quote = '\0';
598 } else if (quote) {
599 if (*src == '\\') {
600 src++;
601 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000602 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000603 freeJob(job);
604 return 1;
605 }
606
607 /* in shell, "\'" should yield \' */
608 if (*src != quote)
609 *buf++ = '\\';
610 } else if (*src == '*' || *src == '?' || *src == '[' ||
611 *src == ']') *buf++ = '\\';
612 *buf++ = *src;
613 } else if (isspace(*src)) {
614 if (*prog->argv[argc]) {
615 buf++, argc++;
616 /* +1 here leaves room for the NULL which ends argv */
617 if ((argc + 1) == argvAlloced) {
618 argvAlloced += 5;
619 prog->argv = realloc(prog->argv,
620 sizeof(*prog->argv) *
621 argvAlloced);
622 }
Erik Andersen161220c2000-03-16 08:12:48 +0000623 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000624 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000625 }
626 } else
627 switch (*src) {
628 case '"':
629 case '\'':
630 quote = *src;
631 break;
632
633 case '#': /* comment */
634 done = 1;
635 break;
636
637 case '>': /* redirections */
638 case '<':
639 i = prog->numRedirections++;
640 prog->redirections = realloc(prog->redirections,
641 sizeof(*prog->redirections) *
642 (i + 1));
643
644 prog->redirections[i].fd = -1;
645 if (buf != prog->argv[argc]) {
646 /* the stuff before this character may be the file number
647 being redirected */
648 prog->redirections[i].fd =
649 strtol(prog->argv[argc], &chptr, 10);
650
651 if (*chptr && *prog->argv[argc]) {
652 buf++, argc++;
653 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000654 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000655 }
656 }
657
658 if (prog->redirections[i].fd == -1) {
659 if (*src == '>')
660 prog->redirections[i].fd = 1;
661 else
662 prog->redirections[i].fd = 0;
663 }
664
665 if (*src++ == '>') {
666 if (*src == '>')
667 prog->redirections[i].type =
668 REDIRECT_APPEND, src++;
669 else
670 prog->redirections[i].type = REDIRECT_OVERWRITE;
671 } else {
672 prog->redirections[i].type = REDIRECT_INPUT;
673 }
674
675 /* This isn't POSIX sh compliant. Oh well. */
676 chptr = src;
677 while (isspace(*chptr))
678 chptr++;
679
680 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000681 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000682 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000683 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000684 return 1;
685 }
686
687 prog->redirections[i].filename = buf;
688 while (*chptr && !isspace(*chptr))
689 *buf++ = *chptr++;
690
691 src = chptr - 1; /* we src++ later */
692 prog->argv[argc] = ++buf;
693 break;
694
695 case '|': /* pipe */
696 /* finish this command */
697 if (*prog->argv[argc])
698 argc++;
699 if (!argc) {
Matt Kraaid537a952000-07-14 01:51:25 +0000700 errorMsg("empty command in pipe1\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000701 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000702 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000703 return 1;
704 }
705 prog->argv[argc] = NULL;
706
707 /* and start the next */
708 job->numProgs++;
709 job->progs = realloc(job->progs,
710 sizeof(*job->progs) * job->numProgs);
711 prog = job->progs + (job->numProgs - 1);
712 prog->numRedirections = 0;
713 prog->redirections = NULL;
714 prog->freeGlob = 0;
715 argc = 0;
716
717 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000718 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000719 prog->argv[0] = ++buf;
720
721 src++;
722 while (*src && isspace(*src))
723 src++;
724
725 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000726 errorMsg("empty command in pipe2\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000727 freeJob(job);
728 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000729 return 1;
730 }
731 src--; /* we'll ++ it at the end of the loop */
732
733 break;
734
735 case '&': /* background */
736 *isBg = 1;
737 case ';': /* multiple commands */
738 done = 1;
739 returnCommand = *commandPtr + (src - *commandPtr) + 1;
740 break;
741
742 case '\\':
743 src++;
744 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000745 errorMsg("character expected after \\\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000746 freeJob(job);
Erik Andersen161220c2000-03-16 08:12:48 +0000747 return 1;
748 }
749 if (*src == '*' || *src == '[' || *src == ']'
750 || *src == '?') *buf++ = '\\';
751 /* fallthrough */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000752 case '`':
753 /* Exec a backtick-ed command */
754 {
755 char* newcmd=NULL;
756 char* ptr=NULL;
757 struct job newJob;
758
759 ptr=strchr(++src, '`');
760 if (ptr==NULL) {
761 fprintf(stderr, "Unmatched '`' in command\n");
762 freeJob(job);
763 return 1;
764 }
765
766 newcmd = xmalloc(1+ptr-src);
767 snprintf(newcmd, 1+ptr-src, src);
768
769 if (!parseCommand(&newcmd, &newJob, jobList, isBg) &&
770 newJob.numProgs) {
771 runCommand(&newJob, jobList, *isBg);
772 }
773
774 /* Clip out the the backticked command from the string */
775 memmove(--src, ptr, strlen(ptr)+1);
776 free(newcmd);
777 }
778 break;
Erik Andersen161220c2000-03-16 08:12:48 +0000779 default:
780 *buf++ = *src;
781 }
782
Erik Andersend75af992000-03-16 08:09:09 +0000783 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000784 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000785
Erik Andersen161220c2000-03-16 08:12:48 +0000786 if (*prog->argv[argc]) {
787 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000788 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000789 }
790 if (!argc) {
791 freeJob(job);
792 return 0;
793 }
794 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000795
Erik Andersen161220c2000-03-16 08:12:48 +0000796 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000797 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000798 strcpy(job->text, *commandPtr);
799 } else {
800 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000801 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000802 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000803 strncpy(job->text, *commandPtr, count);
804 job->text[count] = '\0';
805 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000806
Erik Andersen161220c2000-03-16 08:12:48 +0000807 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000808
Erik Andersend75af992000-03-16 08:09:09 +0000809 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000810}
811
Erik Andersenbcd61772000-05-13 06:33:19 +0000812
Eric Andersenec10b9d2000-07-14 01:13:11 +0000813static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000814{
Erik Andersen161220c2000-03-16 08:12:48 +0000815 struct job *job;
816 int i;
817 int nextin, nextout;
818 int pipefds[2]; /* pipefd[0] is for reading */
819 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +0000820#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +0000821 const struct BB_applet *a = applets;
822#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000823
Erik Andersen3522eb12000-03-12 23:49:18 +0000824
Erik Andersen161220c2000-03-16 08:12:48 +0000825 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000826 for (i = 0; i < newJob->numProgs; i++) {
827 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +0000828 pipe(pipefds);
829 nextout = pipefds[1];
830 } else {
831 nextout = 1;
832 }
833
Eric Andersen34e19412000-07-10 18:47:24 +0000834 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000835 for (x = bltins; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000836 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
837 return (x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +0000838 }
839 }
840
Eric Andersenec10b9d2000-07-14 01:13:11 +0000841 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +0000842 signal(SIGTTOU, SIG_DFL);
843
844 if (nextin != 0) {
845 dup2(nextin, 0);
846 close(nextin);
847 }
848
849 if (nextout != 1) {
850 dup2(nextout, 1);
851 close(nextout);
852 }
853
854 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000855 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +0000856
Eric Andersen34e19412000-07-10 18:47:24 +0000857 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000858 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000859 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
860 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +0000861 }
862 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000863#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +0000864 /* Check if the command matches any busybox internal commands here */
865 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
866 * works, but '/bin/cat' doesn't ) */
Erik Andersenbcd61772000-05-13 06:33:19 +0000867 while (a->name != 0) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000868 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000869 int argc;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000870 char** argv=newJob->progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000871 for(argc=0;*argv!=NULL; argv++, argc++);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000872 exit((*(a->main)) (argc, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +0000873 }
874 a++;
875 }
876#endif
877
Eric Andersenec10b9d2000-07-14 01:13:11 +0000878 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
879 fatalError("%s: %s\n", newJob->progs[i].argv[0],
Erik Andersen161220c2000-03-16 08:12:48 +0000880 strerror(errno));
881 }
882
883 /* put our child in the process group whose leader is the
884 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000885 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +0000886
887 if (nextin != 0)
888 close(nextin);
889 if (nextout != 1)
890 close(nextout);
891
892 /* If there isn't another process, nextin is garbage
893 but it doesn't matter */
894 nextin = pipefds[0];
895 }
896
Eric Andersenec10b9d2000-07-14 01:13:11 +0000897 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +0000898
899 /* find the ID for the job to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000900 newJob->jobId = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000901 for (job = jobList->head; job; job = job->next)
Eric Andersenec10b9d2000-07-14 01:13:11 +0000902 if (job->jobId >= newJob->jobId)
903 newJob->jobId = job->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000904
905 /* add the job to the list of running jobs */
906 if (!jobList->head) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000907 job = jobList->head = xmalloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000908 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000909 for (job = jobList->head; job->next; job = job->next);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000910 job->next = xmalloc(sizeof(*job));
Erik Andersen161220c2000-03-16 08:12:48 +0000911 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000912 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000913
Eric Andersenec10b9d2000-07-14 01:13:11 +0000914 *job = *newJob;
Erik Andersen161220c2000-03-16 08:12:48 +0000915 job->next = NULL;
916 job->runningProgs = job->numProgs;
917 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000918
Erik Andersen161220c2000-03-16 08:12:48 +0000919 if (inBg) {
920 /* we don't wait for background jobs to return -- append it
921 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000922 printf("[%d] %d\n", job->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +0000923 newJob->progs[newJob->numProgs - 1].pid);
Erik Andersen161220c2000-03-16 08:12:48 +0000924 } else {
925 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000926
Erik Andersen161220c2000-03-16 08:12:48 +0000927 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000928 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000929 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +0000930 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000931 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000932
Erik Andersen161220c2000-03-16 08:12:48 +0000933 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000934}
935
Erik Andersend75af992000-03-16 08:09:09 +0000936static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000937{
Erik Andersen161220c2000-03-16 08:12:48 +0000938 int i;
939 int openfd;
940 int mode = O_RDONLY;
941 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000942
Erik Andersen161220c2000-03-16 08:12:48 +0000943 for (i = 0; i < prog->numRedirections; i++, redir++) {
944 switch (redir->type) {
945 case REDIRECT_INPUT:
946 mode = O_RDONLY;
947 break;
948 case REDIRECT_OVERWRITE:
949 mode = O_RDWR | O_CREAT | O_TRUNC;
950 break;
951 case REDIRECT_APPEND:
952 mode = O_RDWR | O_CREAT | O_APPEND;
953 break;
954 }
955
956 openfd = open(redir->filename, mode, 0666);
957 if (openfd < 0) {
958 /* this could get lost if stderr has been redirected, but
959 bash and ash both lose it as well (though zsh doesn't!) */
Matt Kraaid537a952000-07-14 01:51:25 +0000960 errorMsg("error opening %s: %s\n", redir->filename,
Erik Andersen161220c2000-03-16 08:12:48 +0000961 strerror(errno));
962 return 1;
963 }
964
965 if (openfd != redir->fd) {
966 dup2(openfd, redir->fd);
967 close(openfd);
968 }
Erik Andersend75af992000-03-16 08:09:09 +0000969 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000970
Erik Andersen161220c2000-03-16 08:12:48 +0000971 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000972}
973
974
Erik Andersend75af992000-03-16 08:09:09 +0000975static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000976{
Erik Andersen161220c2000-03-16 08:12:48 +0000977 char *command;
978 char *nextCommand = NULL;
979 struct jobSet jobList = { NULL, NULL };
980 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000981 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +0000982 int i;
983 int status;
984 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000985
Eric Andersen1c314ad2000-06-28 16:56:25 +0000986 /* save current owner of TTY so we can restore it on exit */
987 parent_pgrp = tcgetpgrp(0);
988
Erik Andersen161220c2000-03-16 08:12:48 +0000989 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000990
Erik Andersen161220c2000-03-16 08:12:48 +0000991 /* don't pay any attention to this signal; it just confuses
992 things and isn't really meant for shells anyway */
993 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +0000994
Erik Andersen161220c2000-03-16 08:12:48 +0000995 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000996 if (!jobList.fg) {
997 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000998
Erik Andersend75af992000-03-16 08:09:09 +0000999 /* see if any background processes have exited */
1000 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001001
Erik Andersend75af992000-03-16 08:09:09 +00001002 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001003 if (getCommand(input, command))
1004 break;
1005 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001006 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001007
Eric Andersenec10b9d2000-07-14 01:13:11 +00001008 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001009 newJob.numProgs) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001010 runCommand(&newJob, &jobList, inBg);
1011 } else {
1012 nextCommand=NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001013 }
1014 } else {
1015 /* a job is running in the foreground; wait for it */
1016 i = 0;
1017 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +00001018 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001019
Erik Andersend75af992000-03-16 08:09:09 +00001020 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001021
Erik Andersend75af992000-03-16 08:09:09 +00001022 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001023 /* the child exited */
1024 jobList.fg->runningProgs--;
1025 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001026
Erik Andersen161220c2000-03-16 08:12:48 +00001027 if (!jobList.fg->runningProgs) {
1028 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001029
Erik Andersen161220c2000-03-16 08:12:48 +00001030 removeJob(&jobList, jobList.fg);
1031 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001032 }
Erik Andersend75af992000-03-16 08:09:09 +00001033 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001034 /* the child was stopped */
1035 jobList.fg->stoppedProgs++;
1036 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001037
Erik Andersen161220c2000-03-16 08:12:48 +00001038 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1039 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1040 "Stopped", jobList.fg->text);
1041 jobList.fg = NULL;
1042 }
Erik Andersend75af992000-03-16 08:09:09 +00001043 }
1044
1045 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001046 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001047 /* suppress messages when run from /linuxrc mag@sysgo.de */
1048 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1049 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001050 }
1051 }
1052 }
Erik Andersen161220c2000-03-16 08:12:48 +00001053 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001054
Eric Andersen1c314ad2000-06-28 16:56:25 +00001055 /* return controlling TTY back to parent process group before exiting */
1056 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001057 perror("tcsetpgrp");
1058
1059 /* return exit status if called with "-c" */
1060 if (input == NULL && WIFEXITED(status))
1061 return WEXITSTATUS(status);
1062
Erik Andersen161220c2000-03-16 08:12:48 +00001063 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001064}
1065
1066
Erik Andersend75af992000-03-16 08:09:09 +00001067int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +00001068{
Erik Andersen161220c2000-03-16 08:12:48 +00001069 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +00001070
Erik Andersen161220c2000-03-16 08:12:48 +00001071 /* initialize the cwd */
Eric Andersenb54833c2000-07-03 23:56:26 +00001072 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1073 if (cwd == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001074 fatalError("out of memory\n");
Eric Andersenb54833c2000-07-03 23:56:26 +00001075 }
1076 getcwd(cwd, sizeof(char)*BUFSIZ);
Erik Andersen3522eb12000-03-12 23:49:18 +00001077
Erik Andersenf0657d32000-04-12 17:49:52 +00001078#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +00001079 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +00001080 signal(SIGWINCH, win_changed);
1081 win_changed(0);
1082#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001083
Erik Andersen161220c2000-03-16 08:12:48 +00001084 //if (argv[0] && argv[0][0] == '-') {
Eric Andersen34e19412000-07-10 18:47:24 +00001085 // builtin_source("/etc/profile");
Erik Andersen161220c2000-03-16 08:12:48 +00001086 //}
Erik Andersen3522eb12000-03-12 23:49:18 +00001087
Erik Andersen161220c2000-03-16 08:12:48 +00001088 if (argc < 2) {
Erik Andersenf0657d32000-04-12 17:49:52 +00001089 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1090 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001091 } else {
Eric Andersen1c314ad2000-06-28 16:56:25 +00001092 if (argv[1][0]=='-' && argv[1][1]=='c') {
1093 int i;
1094 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1095 if (local_pending_command == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001096 fatalError("out of memory\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001097 }
1098 for(i=2; i<argc; i++)
1099 {
1100 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
Eric Andersenb54833c2000-07-03 23:56:26 +00001101 local_pending_command = realloc(local_pending_command,
1102 strlen(local_pending_command) + strlen(argv[i]));
1103 if (local_pending_command==NULL)
Matt Kraaibe84cd42000-07-12 17:02:35 +00001104 fatalError("commands for -c option too long\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001105 }
1106 strcat(local_pending_command, argv[i]);
Eric Andersenb54833c2000-07-03 23:56:26 +00001107 if ( (i + 1) < argc)
Eric Andersen1c314ad2000-06-28 16:56:25 +00001108 strcat(local_pending_command, " ");
1109 }
1110 input = NULL;
1111
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001112 }
Eric Andersen1c314ad2000-06-28 16:56:25 +00001113 else if (argv[1][0]=='-') {
1114 usage(shell_usage);
1115 }
1116 else {
1117 input = fopen(argv[1], "r");
1118 if (!input) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001119 fatalError("Couldn't open file '%s': %s\n", argv[1],
Eric Andersen1c314ad2000-06-28 16:56:25 +00001120 strerror(errno));
1121 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001122 }
Erik Andersen161220c2000-03-16 08:12:48 +00001123 }
Erik Andersend75af992000-03-16 08:09:09 +00001124
Erik Andersen161220c2000-03-16 08:12:48 +00001125 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001126}