blob: c9ef39f491d75f07d0da0d60a03e959c69b3ac56 [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
Eric Andersena1d187a2000-07-17 19:14:41 +000028
29#define BB_FEATURE_SH_BACKTICKS
30
31
32
Erik Andersen3522eb12000-03-12 23:49:18 +000033#include "internal.h"
34#include <stdio.h>
35#include <stdlib.h>
36#include <ctype.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <glob.h>
40#include <signal.h>
41#include <string.h>
42#include <sys/ioctl.h>
43#include <sys/wait.h>
44#include <unistd.h>
Erik Andersenf0657d32000-04-12 17:49:52 +000045#ifdef BB_FEATURE_SH_COMMAND_EDITING
46#include "cmdedit.h"
47#endif
Erik Andersen3522eb12000-03-12 23:49:18 +000048
Pavel Roskin9c5fcc32000-07-17 23:45:12 +000049#define bb_need_full_version
50#define BB_DECLARE_EXTERN
51#include "messages.c"
52
Eric Andersenb54833c2000-07-03 23:56:26 +000053#define MAX_READ 128 /* size of input buffer for `read' builtin */
Erik Andersen3522eb12000-03-12 23:49:18 +000054#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
55
Erik Andersend75af992000-03-16 08:09:09 +000056
57enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000058 REDIRECT_APPEND
59};
Erik Andersen3522eb12000-03-12 23:49:18 +000060
61struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000062 struct job *head; /* head of list of running jobs */
63 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000064};
65
66struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000067 enum redirectionType type; /* type of redirection */
68 int fd; /* file descriptor being redirected */
69 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000070};
71
72struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000073 pid_t pid; /* 0 if exited */
74 char **argv; /* program name and arguments */
75 int numRedirections; /* elements in redirection array */
76 struct redirectionSpecifier *redirections; /* I/O redirections */
77 glob_t globResult; /* result of parameter globbing */
78 int freeGlob; /* should we globfree(&globResult)? */
79 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000080};
81
82struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000083 int jobId; /* job number */
84 int numProgs; /* total number of programs in job */
85 int runningProgs; /* number of programs running */
86 char *text; /* name of job */
87 char *cmdBuf; /* buffer various argv's point into */
88 pid_t pgrp; /* process group ID for the job */
89 struct childProgram *progs; /* array of programs in job */
90 struct job *next; /* to track background commands */
91 int stoppedProgs; /* number of programs alive, but stopped */
Erik Andersen3522eb12000-03-12 23:49:18 +000092};
93
94struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +000095 char *cmd; /* name */
96 char *descr; /* description */
97 char *usage; /* usage */
98 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +000099};
100
Eric Andersen34e19412000-07-10 18:47:24 +0000101/* function prototypes for builtins */
102static int builtin_cd(struct job *cmd, struct jobSet *junk);
103static int builtin_env(struct job *dummy, struct jobSet *junk);
104static int builtin_exit(struct job *cmd, struct jobSet *junk);
105static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
106static int builtin_help(struct job *cmd, struct jobSet *junk);
107static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
108static int builtin_pwd(struct job *dummy, struct jobSet *junk);
109static int builtin_export(struct job *cmd, struct jobSet *junk);
110static int builtin_source(struct job *cmd, struct jobSet *jobList);
111static int builtin_unset(struct job *cmd, struct jobSet *junk);
112static int builtin_read(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000113
Eric Andersen34e19412000-07-10 18:47:24 +0000114
115/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000116static void checkJobs(struct jobSet *jobList);
117static int getCommand(FILE * source, char *command);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000118static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
Erik Andersend75af992000-03-16 08:09:09 +0000119static int setupRedirections(struct childProgram *prog);
Eric Andersena1d187a2000-07-17 19:14:41 +0000120static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
Erik Andersen3522eb12000-03-12 23:49:18 +0000121static int busy_loop(FILE * input);
122
Erik Andersend75af992000-03-16 08:09:09 +0000123
Mark Whitley37653aa2000-07-12 23:36:17 +0000124/* Table of built-in functions (these are non-forking builtins, meaning they
125 * can change global variables in the parent shell process but they will not
126 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000127static struct builtInCommand bltins[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000128 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
129 {"cd", "Change working directory", "cd [dir]", builtin_cd},
130 {"exit", "Exit from shell()", "exit", builtin_exit},
131 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
132 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
133 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
134 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
135 {"read", "Input environment variable", "read [VAR]", builtin_read},
Erik Andersen330fd2b2000-05-19 05:35:19 +0000136 {NULL, NULL, NULL, NULL}
137};
138
Mark Whitley37653aa2000-07-12 23:36:17 +0000139/* Table of forking built-in functions (things that fork cannot change global
140 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000141static struct builtInCommand bltins_forking[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000142 {"env", "Print all environment variables", "env", builtin_env},
143 {"pwd", "Print current directory", "pwd", builtin_pwd},
144 {".", "Source-in and run commands in a file", ". filename", builtin_source},
145 {"help", "List shell built-in commands", "help", builtin_help},
Erik Andersen161220c2000-03-16 08:12:48 +0000146 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000147};
148
Erik Andersen3522eb12000-03-12 23:49:18 +0000149static char *prompt = "# ";
Eric Andersenb54833c2000-07-03 23:56:26 +0000150static char *cwd = NULL;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000151static char *local_pending_command = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000152
Erik Andersenf0657d32000-04-12 17:49:52 +0000153#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenfad04fd2000-07-14 06:49:52 +0000154void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000155{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000156 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000157 ioctl(0, TIOCGWINSZ, &win);
158 if (win.ws_col > 0) {
159 cmdedit_setwidth( win.ws_col - 1);
160 }
161}
162#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000163
Erik Andersen3522eb12000-03-12 23:49:18 +0000164
Erik Andersend75af992000-03-16 08:09:09 +0000165/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000166static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000167{
Erik Andersen161220c2000-03-16 08:12:48 +0000168 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000169
Erik Andersen161220c2000-03-16 08:12:48 +0000170 if (!cmd->progs[0].argv[1] == 1)
171 newdir = getenv("HOME");
172 else
173 newdir = cmd->progs[0].argv[1];
174 if (chdir(newdir)) {
175 printf("cd: %s: %s\n", newdir, strerror(errno));
176 return FALSE;
177 }
178 getcwd(cwd, sizeof(cwd));
179
180 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000181}
182
183/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000184static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000185{
Erik Andersen161220c2000-03-16 08:12:48 +0000186 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000187
Erik Andersen161220c2000-03-16 08:12:48 +0000188 for (e = environ; *e; e++) {
189 fprintf(stdout, "%s\n", *e);
190 }
191 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000192}
193
194/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000195static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000196{
Erik Andersen161220c2000-03-16 08:12:48 +0000197 if (!cmd->progs[0].argv[1] == 1)
198 exit TRUE;
199
Eric Andersenb6106152000-06-19 17:25:40 +0000200 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000201}
202
203/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000204static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000205{
Erik Andersen161220c2000-03-16 08:12:48 +0000206 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000207 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000208
Erik Andersen161220c2000-03-16 08:12:48 +0000209 if (!jobList->head) {
210 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000211 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000212 cmd->progs[0].argv[0]);
213 return FALSE;
214 }
215 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000216 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000217 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
218 return FALSE;
219 for (job = jobList->head; job; job = job->next) {
220 if (job->jobId == jobNum) {
221 break;
222 }
223 }
224 }
225 } else {
226 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000227 }
Erik Andersen161220c2000-03-16 08:12:48 +0000228
229 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000230 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000231 cmd->progs[0].argv[0], jobNum);
232 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000233 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000234
Erik Andersen161220c2000-03-16 08:12:48 +0000235 if (*cmd->progs[0].argv[0] == 'f') {
236 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000237 /* suppress messages when run from /linuxrc mag@sysgo.de */
238 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
239 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000240 jobList->fg = job;
241 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000242
Erik Andersen161220c2000-03-16 08:12:48 +0000243 /* Restart the processes in the job */
244 for (i = 0; i < job->numProgs; i++)
245 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000246
Erik Andersen161220c2000-03-16 08:12:48 +0000247 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000248
Erik Andersen161220c2000-03-16 08:12:48 +0000249 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000250
Erik Andersen161220c2000-03-16 08:12:48 +0000251 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000252}
253
254/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000255static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000256{
Erik Andersen161220c2000-03-16 08:12:48 +0000257 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000258
Erik Andersen161220c2000-03-16 08:12:48 +0000259 fprintf(stdout, "\nBuilt-in commands:\n");
260 fprintf(stdout, "-------------------\n");
261 for (x = bltins; x->cmd; x++) {
262 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
263 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000264 for (x = bltins_forking; x->cmd; x++) {
265 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
266 }
Erik Andersen161220c2000-03-16 08:12:48 +0000267 fprintf(stdout, "\n\n");
268 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000269}
270
271/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000272static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000273{
Erik Andersen161220c2000-03-16 08:12:48 +0000274 struct job *job;
275 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000276
Erik Andersen161220c2000-03-16 08:12:48 +0000277 for (job = jobList->head; job; job = job->next) {
278 if (job->runningProgs == job->stoppedProgs)
279 statusString = "Stopped";
280 else
281 statusString = "Running";
282
283 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
284 }
285 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000286}
287
288
289/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000290static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000291{
Erik Andersen161220c2000-03-16 08:12:48 +0000292 getcwd(cwd, sizeof(cwd));
293 fprintf(stdout, "%s\n", cwd);
294 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000295}
296
Erik Andersen6273f652000-03-17 01:12:41 +0000297/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000298static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000299{
Erik Andersen161220c2000-03-16 08:12:48 +0000300 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000301
Erik Andersen161220c2000-03-16 08:12:48 +0000302 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000303 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000304 }
305 res = putenv(cmd->progs[0].argv[1]);
306 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000307 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000308 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000309}
310
Eric Andersenb54833c2000-07-03 23:56:26 +0000311/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000312static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000313{
314 int res = 0, len, newlen;
315 char *s;
316 char string[MAX_READ];
317
318 if (cmd->progs[0].argv[1]) {
319 /* argument (VAR) given: put "VAR=" into buffer */
320 strcpy(string, cmd->progs[0].argv[1]);
321 len = strlen(string);
322 string[len++] = '=';
323 string[len] = '\0';
324 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
325 newlen = strlen(string);
326 if(newlen > len)
327 string[--newlen] = '\0'; /* chomp trailing newline */
328 /*
329 ** string should now contain "VAR=<value>"
330 ** copy it (putenv() won't do that, so we must make sure
331 ** the string resides in a static buffer!)
332 */
333 res = -1;
334 if((s = strdup(string)))
335 res = putenv(s);
336 if (res)
337 fprintf(stdout, "read: %s\n", strerror(errno));
338 }
339 else
340 fgets(string, sizeof(string), stdin);
341
342 return (res);
343}
344
Erik Andersen3522eb12000-03-12 23:49:18 +0000345/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000346static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000347{
Erik Andersen161220c2000-03-16 08:12:48 +0000348 FILE *input;
349 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000350
Erik Andersen161220c2000-03-16 08:12:48 +0000351 if (!cmd->progs[0].argv[1] == 1)
352 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000353
Erik Andersen161220c2000-03-16 08:12:48 +0000354 input = fopen(cmd->progs[0].argv[1], "r");
355 if (!input) {
356 fprintf(stdout, "Couldn't open file '%s'\n",
357 cmd->progs[0].argv[1]);
358 return FALSE;
359 }
Erik Andersend75af992000-03-16 08:09:09 +0000360
Erik Andersen161220c2000-03-16 08:12:48 +0000361 /* Now run the file */
362 status = busy_loop(input);
363 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000364}
365
366/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000367static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000368{
Erik Andersen161220c2000-03-16 08:12:48 +0000369 if (!cmd->progs[0].argv[1] == 1) {
370 fprintf(stdout, "unset: parameter required.\n");
371 return FALSE;
372 }
373 unsetenv(cmd->progs[0].argv[1]);
374 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000375}
376
377/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000378static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000379{
Erik Andersen161220c2000-03-16 08:12:48 +0000380 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000381
Erik Andersen161220c2000-03-16 08:12:48 +0000382 for (i = 0; i < cmd->numProgs; i++) {
383 free(cmd->progs[i].argv);
384 if (cmd->progs[i].redirections)
385 free(cmd->progs[i].redirections);
386 if (cmd->progs[i].freeGlob)
387 globfree(&cmd->progs[i].globResult);
388 }
389 free(cmd->progs);
390 if (cmd->text)
391 free(cmd->text);
392 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000393 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000394}
395
396/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000397static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000398{
Erik Andersen161220c2000-03-16 08:12:48 +0000399 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000400
Erik Andersen161220c2000-03-16 08:12:48 +0000401 freeJob(job);
402 if (job == jobList->head) {
403 jobList->head = job->next;
404 } else {
405 prevJob = jobList->head;
406 while (prevJob->next != job)
407 prevJob = prevJob->next;
408 prevJob->next = job->next;
409 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000410
Erik Andersen161220c2000-03-16 08:12:48 +0000411 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000412}
413
414/* Checks to see if any background processes have exited -- if they
415 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000416static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000417{
Erik Andersen161220c2000-03-16 08:12:48 +0000418 struct job *job;
419 pid_t childpid;
420 int status;
421 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000422
Erik Andersen161220c2000-03-16 08:12:48 +0000423 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
424 for (job = jobList->head; job; job = job->next) {
425 progNum = 0;
426 while (progNum < job->numProgs &&
427 job->progs[progNum].pid != childpid) progNum++;
428 if (progNum < job->numProgs)
429 break;
430 }
431
Eric Andersena1d187a2000-07-17 19:14:41 +0000432 /* This happens on backticked commands */
433 if(job==NULL)
434 return;
435
Erik Andersen161220c2000-03-16 08:12:48 +0000436 if (WIFEXITED(status) || WIFSIGNALED(status)) {
437 /* child exited */
438 job->runningProgs--;
439 job->progs[progNum].pid = 0;
440
441 if (!job->runningProgs) {
442 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
443 removeJob(jobList, job);
444 }
445 } else {
446 /* child stopped */
447 job->stoppedProgs++;
448 job->progs[progNum].isStopped = 1;
449
450 if (job->stoppedProgs == job->numProgs) {
451 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
452 job->text);
453 }
454 }
Erik Andersend75af992000-03-16 08:09:09 +0000455 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000456
Erik Andersen161220c2000-03-16 08:12:48 +0000457 if (childpid == -1 && errno != ECHILD)
458 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000459}
460
Erik Andersend75af992000-03-16 08:09:09 +0000461static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000462{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000463 if (source == NULL) {
464 if (local_pending_command) {
465 /* a command specified (-c option): return it & mark it done */
466 strcpy(command, local_pending_command);
467 free(local_pending_command);
468 local_pending_command = NULL;
469 return 0;
470 }
471 return 1;
472 }
473
Erik Andersen161220c2000-03-16 08:12:48 +0000474 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000475#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000476 int len;
477 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000478 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000479 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000480 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000481 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000482 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000483 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000484 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000485#else
486 fprintf(stdout, "%s %s", cwd, prompt);
487 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000488#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000489 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000490
Erik Andersen161220c2000-03-16 08:12:48 +0000491 if (!fgets(command, BUFSIZ - 2, source)) {
492 if (source == stdin)
493 printf("\n");
494 return 1;
495 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000496
Erik Andersen161220c2000-03-16 08:12:48 +0000497 /* remove trailing newline */
498 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000499
Erik Andersen161220c2000-03-16 08:12:48 +0000500 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000501}
502
Erik Andersend75af992000-03-16 08:09:09 +0000503static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000504 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000505{
Erik Andersen161220c2000-03-16 08:12:48 +0000506 int argc = *argcPtr;
507 int argcAlloced = *argcAllocedPtr;
508 int rc;
509 int flags;
510 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000511 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000512
Erik Andersen161220c2000-03-16 08:12:48 +0000513 if (argc > 1) { /* cmd->globResult is already initialized */
514 flags = GLOB_APPEND;
515 i = prog->globResult.gl_pathc;
516 } else {
517 prog->freeGlob = 1;
518 flags = 0;
519 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000520 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000521 /* do shell variable substitution */
522 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
523 prog->argv[argc - 1] = var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000524
Erik Andersen161220c2000-03-16 08:12:48 +0000525 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
526 if (rc == GLOB_NOSPACE) {
Matt Kraaid537a952000-07-14 01:51:25 +0000527 errorMsg("out of space during glob operation\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000528 return;
529 } else if (rc == GLOB_NOMATCH ||
530 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
531 !strcmp(prog->argv[argc - 1],
532 prog->globResult.gl_pathv[i]))) {
533 /* we need to remove whatever \ quoting is still present */
534 src = dst = prog->argv[argc - 1];
535 while (*src) {
536 if (*src != '\\')
537 *dst++ = *src;
538 src++;
539 }
540 *dst = '\0';
541 } else if (!rc) {
542 argcAlloced += (prog->globResult.gl_pathc - i);
543 prog->argv =
544 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
545 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
546 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
547 argc += (prog->globResult.gl_pathc - i - 1);
548 }
549
550 *argcAllocedPtr = argcAlloced;
551 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000552}
553
554/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
555 line). If a valid command is found, commandPtr is set to point to
556 the beginning of the next command (if the original command had more
557 then one job associated with it) or NULL if no more commands are
558 present. */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000559static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000560{
Erik Andersen161220c2000-03-16 08:12:48 +0000561 char *command;
562 char *returnCommand = NULL;
563 char *src, *buf, *chptr;
564 int argc = 0;
565 int done = 0;
566 int argvAlloced;
567 int i;
568 char quote = '\0';
569 int count;
570 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000571
Erik Andersen161220c2000-03-16 08:12:48 +0000572 /* skip leading white space */
573 while (**commandPtr && isspace(**commandPtr))
574 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000575
Erik Andersen161220c2000-03-16 08:12:48 +0000576 /* this handles empty lines or leading '#' characters */
577 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000578 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000579 return 0;
580 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000581
Erik Andersen161220c2000-03-16 08:12:48 +0000582 *isBg = 0;
583 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000584 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000585
Erik Andersen161220c2000-03-16 08:12:48 +0000586 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000587 memory is freed by freeJob(). Allocate twice the original
588 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000589
Erik Andersen161220c2000-03-16 08:12:48 +0000590 Getting clean memory relieves us of the task of NULL
591 terminating things and makes the rest of this look a bit
592 cleaner (though it is, admittedly, a tad less efficient) */
Eric Andersenb54833c2000-07-03 23:56:26 +0000593 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000594 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000595
Erik Andersen161220c2000-03-16 08:12:48 +0000596 prog = job->progs;
597 prog->numRedirections = 0;
598 prog->redirections = NULL;
599 prog->freeGlob = 0;
600 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000601
Erik Andersen161220c2000-03-16 08:12:48 +0000602 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000603 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000604 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000605
Erik Andersen161220c2000-03-16 08:12:48 +0000606 buf = command;
607 src = *commandPtr;
608 while (*src && !done) {
609 if (quote == *src) {
610 quote = '\0';
611 } else if (quote) {
612 if (*src == '\\') {
613 src++;
614 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000615 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000616 freeJob(job);
617 return 1;
618 }
619
620 /* in shell, "\'" should yield \' */
621 if (*src != quote)
622 *buf++ = '\\';
623 } else if (*src == '*' || *src == '?' || *src == '[' ||
624 *src == ']') *buf++ = '\\';
625 *buf++ = *src;
626 } else if (isspace(*src)) {
627 if (*prog->argv[argc]) {
628 buf++, argc++;
629 /* +1 here leaves room for the NULL which ends argv */
630 if ((argc + 1) == argvAlloced) {
631 argvAlloced += 5;
632 prog->argv = realloc(prog->argv,
633 sizeof(*prog->argv) *
634 argvAlloced);
635 }
Erik Andersen161220c2000-03-16 08:12:48 +0000636 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000637 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000638 }
639 } else
640 switch (*src) {
641 case '"':
642 case '\'':
643 quote = *src;
644 break;
645
646 case '#': /* comment */
647 done = 1;
648 break;
649
650 case '>': /* redirections */
651 case '<':
652 i = prog->numRedirections++;
653 prog->redirections = realloc(prog->redirections,
654 sizeof(*prog->redirections) *
655 (i + 1));
656
657 prog->redirections[i].fd = -1;
658 if (buf != prog->argv[argc]) {
659 /* the stuff before this character may be the file number
660 being redirected */
661 prog->redirections[i].fd =
662 strtol(prog->argv[argc], &chptr, 10);
663
664 if (*chptr && *prog->argv[argc]) {
665 buf++, argc++;
666 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000667 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000668 }
669 }
670
671 if (prog->redirections[i].fd == -1) {
672 if (*src == '>')
673 prog->redirections[i].fd = 1;
674 else
675 prog->redirections[i].fd = 0;
676 }
677
678 if (*src++ == '>') {
679 if (*src == '>')
680 prog->redirections[i].type =
681 REDIRECT_APPEND, src++;
682 else
683 prog->redirections[i].type = REDIRECT_OVERWRITE;
684 } else {
685 prog->redirections[i].type = REDIRECT_INPUT;
686 }
687
688 /* This isn't POSIX sh compliant. Oh well. */
689 chptr = src;
690 while (isspace(*chptr))
691 chptr++;
692
693 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000694 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000695 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000696 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000697 return 1;
698 }
699
700 prog->redirections[i].filename = buf;
701 while (*chptr && !isspace(*chptr))
702 *buf++ = *chptr++;
703
704 src = chptr - 1; /* we src++ later */
705 prog->argv[argc] = ++buf;
706 break;
707
708 case '|': /* pipe */
709 /* finish this command */
710 if (*prog->argv[argc])
711 argc++;
712 if (!argc) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000713 errorMsg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000714 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000715 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000716 return 1;
717 }
718 prog->argv[argc] = NULL;
719
720 /* and start the next */
721 job->numProgs++;
722 job->progs = realloc(job->progs,
723 sizeof(*job->progs) * job->numProgs);
724 prog = job->progs + (job->numProgs - 1);
725 prog->numRedirections = 0;
726 prog->redirections = NULL;
727 prog->freeGlob = 0;
728 argc = 0;
729
730 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000731 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000732 prog->argv[0] = ++buf;
733
734 src++;
735 while (*src && isspace(*src))
736 src++;
737
738 if (!*src) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000739 errorMsg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000740 freeJob(job);
741 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000742 return 1;
743 }
744 src--; /* we'll ++ it at the end of the loop */
745
746 break;
747
748 case '&': /* background */
749 *isBg = 1;
750 case ';': /* multiple commands */
751 done = 1;
752 returnCommand = *commandPtr + (src - *commandPtr) + 1;
753 break;
754
755 case '\\':
756 src++;
757 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000758 errorMsg("character expected after \\\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000759 freeJob(job);
Erik Andersen161220c2000-03-16 08:12:48 +0000760 return 1;
761 }
762 if (*src == '*' || *src == '[' || *src == ']'
763 || *src == '?') *buf++ = '\\';
764 /* fallthrough */
Eric Andersena1d187a2000-07-17 19:14:41 +0000765#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +0000766 case '`':
767 /* Exec a backtick-ed command */
768 {
Eric Andersena1d187a2000-07-17 19:14:41 +0000769 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000770 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +0000771 struct job *newJob;
772 struct jobSet njobList = { NULL, NULL };
773 int pipefd[2];
774 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000775
776 ptr=strchr(++src, '`');
777 if (ptr==NULL) {
778 fprintf(stderr, "Unmatched '`' in command\n");
779 freeJob(job);
780 return 1;
781 }
782
Eric Andersena1d187a2000-07-17 19:14:41 +0000783 /* Make a copy of any stuff left over in the command
784 * line after the second backtick */
785 charptr2 = xmalloc(strlen(ptr)+1);
786 memcpy(charptr2, ptr+1, strlen(ptr));
Eric Andersenec10b9d2000-07-14 01:13:11 +0000787
Eric Andersena1d187a2000-07-17 19:14:41 +0000788 /* Make some space to hold just the backticked command */
789 charptr1 = xmalloc(1+ptr-src);
790 snprintf(charptr1, 1+ptr-src, src);
791 newJob = xmalloc(sizeof(struct job));
792 /* Now parse and run the backticked command */
793 if (!parseCommand(&charptr1, newJob, &njobList, isBg)
794 && newJob->numProgs) {
795 pipe(pipefd);
796 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000797 }
Eric Andersena1d187a2000-07-17 19:14:41 +0000798 checkJobs(jobList);
799 free(charptr1);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000800
Eric Andersena1d187a2000-07-17 19:14:41 +0000801 /* Copy the output from the backtick-ed command into the
802 * command line, making extra room as needed */
803 --src;
804 charptr1 = xmalloc(BUFSIZ);
805 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
806 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
807 if (newSize > BUFSIZ) {
808 *commandPtr=realloc(*commandPtr, src - *commandPtr +
809 size + 1 + strlen(charptr2));
810 }
811 memcpy(src, charptr1, size);
812 src+=size;
813 }
814 free(charptr1);
815 close(pipefd[0]);
816 if (*(src-1)=='\n')
817 --src;
818
819 /* Now paste into the *commandPtr all the stuff
820 * leftover after the second backtick */
821 memcpy(src, charptr2, strlen(charptr2));
822 fprintf(stderr,"*commandPtr='%s'\n", *commandPtr);
823 free(charptr2);
824
825
826 /* Now recursively call parseCommand to deal with the new
827 * and improved version of the command line with the backtick
828 * results expanded in place... */
829 return(parseCommand(commandPtr, job, jobList, isBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +0000830 }
831 break;
Eric Andersena1d187a2000-07-17 19:14:41 +0000832#endif // BB_FEATURE_SH_BACKTICKS
Erik Andersen161220c2000-03-16 08:12:48 +0000833 default:
834 *buf++ = *src;
835 }
836
Erik Andersend75af992000-03-16 08:09:09 +0000837 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000838 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000839
Erik Andersen161220c2000-03-16 08:12:48 +0000840 if (*prog->argv[argc]) {
841 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000842 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000843 }
844 if (!argc) {
845 freeJob(job);
846 return 0;
847 }
848 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000849
Erik Andersen161220c2000-03-16 08:12:48 +0000850 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000851 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000852 strcpy(job->text, *commandPtr);
853 } else {
854 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000855 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000856 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000857 strncpy(job->text, *commandPtr, count);
858 job->text[count] = '\0';
859 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000860
Erik Andersen161220c2000-03-16 08:12:48 +0000861 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000862
Erik Andersend75af992000-03-16 08:09:09 +0000863 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000864}
865
Erik Andersenbcd61772000-05-13 06:33:19 +0000866
Eric Andersena1d187a2000-07-17 19:14:41 +0000867static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +0000868{
Erik Andersen161220c2000-03-16 08:12:48 +0000869 struct job *job;
Eric Andersena1d187a2000-07-17 19:14:41 +0000870 int nextin=0, nextout, stdoutfd=fileno(stdout);
Erik Andersen161220c2000-03-16 08:12:48 +0000871 int i;
Erik Andersen161220c2000-03-16 08:12:48 +0000872 int pipefds[2]; /* pipefd[0] is for reading */
873 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +0000874#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +0000875 const struct BB_applet *a = applets;
876#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000877
Eric Andersenec10b9d2000-07-14 01:13:11 +0000878 for (i = 0; i < newJob->numProgs; i++) {
879 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +0000880 pipe(pipefds);
881 nextout = pipefds[1];
882 } else {
Eric Andersena1d187a2000-07-17 19:14:41 +0000883 nextout = stdoutfd;
Erik Andersen161220c2000-03-16 08:12:48 +0000884 }
885
Eric Andersen34e19412000-07-10 18:47:24 +0000886 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000887 for (x = bltins; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000888 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
889 return (x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +0000890 }
891 }
892
Eric Andersenec10b9d2000-07-14 01:13:11 +0000893 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +0000894 signal(SIGTTOU, SIG_DFL);
895
Eric Andersena1d187a2000-07-17 19:14:41 +0000896 if (outPipe[1]!=-1) {
897 close(outPipe[0]);
898 nextout = stdoutfd = outPipe[1];
Erik Andersen161220c2000-03-16 08:12:48 +0000899 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +0000900 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +0000901 close(nextout);
902 }
903
Eric Andersena1d187a2000-07-17 19:14:41 +0000904 //dup2(nextin, 0);
905 //close(nextin);
906
Erik Andersen161220c2000-03-16 08:12:48 +0000907 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000908 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +0000909
Eric Andersen34e19412000-07-10 18:47:24 +0000910 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000911 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000912 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
913 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +0000914 }
915 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000916#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +0000917 /* Check if the command matches any busybox internal commands here */
Eric Andersena1d187a2000-07-17 19:14:41 +0000918 /* TODO: Add matching on commands with paths appended (i.e. 'cat'
919 * currently works, but '/bin/cat' doesn't ) */
Erik Andersenbcd61772000-05-13 06:33:19 +0000920 while (a->name != 0) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000921 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000922 int argc;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000923 char** argv=newJob->progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000924 for(argc=0;*argv!=NULL; argv++, argc++);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000925 exit((*(a->main)) (argc, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +0000926 }
927 a++;
928 }
929#endif
930
Eric Andersenec10b9d2000-07-14 01:13:11 +0000931 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
932 fatalError("%s: %s\n", newJob->progs[i].argv[0],
Erik Andersen161220c2000-03-16 08:12:48 +0000933 strerror(errno));
934 }
Eric Andersena1d187a2000-07-17 19:14:41 +0000935 if (outPipe[1]!=-1) {
936 close(outPipe[1]);
937 }
Erik Andersen161220c2000-03-16 08:12:48 +0000938
939 /* put our child in the process group whose leader is the
940 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000941 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +0000942
943 if (nextin != 0)
944 close(nextin);
Eric Andersena1d187a2000-07-17 19:14:41 +0000945 if (nextout != stdoutfd)
Erik Andersen161220c2000-03-16 08:12:48 +0000946 close(nextout);
947
948 /* If there isn't another process, nextin is garbage
949 but it doesn't matter */
950 nextin = pipefds[0];
951 }
952
Eric Andersenec10b9d2000-07-14 01:13:11 +0000953 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +0000954
955 /* find the ID for the job to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000956 newJob->jobId = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000957 for (job = jobList->head; job; job = job->next)
Eric Andersenec10b9d2000-07-14 01:13:11 +0000958 if (job->jobId >= newJob->jobId)
959 newJob->jobId = job->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000960
961 /* add the job to the list of running jobs */
962 if (!jobList->head) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000963 job = jobList->head = xmalloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000964 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000965 for (job = jobList->head; job->next; job = job->next);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000966 job->next = xmalloc(sizeof(*job));
Erik Andersen161220c2000-03-16 08:12:48 +0000967 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000968 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000969
Eric Andersenec10b9d2000-07-14 01:13:11 +0000970 *job = *newJob;
Erik Andersen161220c2000-03-16 08:12:48 +0000971 job->next = NULL;
972 job->runningProgs = job->numProgs;
973 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000974
Erik Andersen161220c2000-03-16 08:12:48 +0000975 if (inBg) {
976 /* we don't wait for background jobs to return -- append it
977 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000978 printf("[%d] %d\n", job->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +0000979 newJob->progs[newJob->numProgs - 1].pid);
Erik Andersen161220c2000-03-16 08:12:48 +0000980 } else {
981 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000982
Erik Andersen161220c2000-03-16 08:12:48 +0000983 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000984 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000985 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +0000986 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000987 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000988
Erik Andersen161220c2000-03-16 08:12:48 +0000989 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000990}
991
Erik Andersend75af992000-03-16 08:09:09 +0000992static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000993{
Erik Andersen161220c2000-03-16 08:12:48 +0000994 int i;
995 int openfd;
996 int mode = O_RDONLY;
997 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000998
Erik Andersen161220c2000-03-16 08:12:48 +0000999 for (i = 0; i < prog->numRedirections; i++, redir++) {
1000 switch (redir->type) {
1001 case REDIRECT_INPUT:
1002 mode = O_RDONLY;
1003 break;
1004 case REDIRECT_OVERWRITE:
1005 mode = O_RDWR | O_CREAT | O_TRUNC;
1006 break;
1007 case REDIRECT_APPEND:
1008 mode = O_RDWR | O_CREAT | O_APPEND;
1009 break;
1010 }
1011
1012 openfd = open(redir->filename, mode, 0666);
1013 if (openfd < 0) {
1014 /* this could get lost if stderr has been redirected, but
1015 bash and ash both lose it as well (though zsh doesn't!) */
Matt Kraaid537a952000-07-14 01:51:25 +00001016 errorMsg("error opening %s: %s\n", redir->filename,
Erik Andersen161220c2000-03-16 08:12:48 +00001017 strerror(errno));
1018 return 1;
1019 }
1020
1021 if (openfd != redir->fd) {
1022 dup2(openfd, redir->fd);
1023 close(openfd);
1024 }
Erik Andersend75af992000-03-16 08:09:09 +00001025 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001026
Erik Andersen161220c2000-03-16 08:12:48 +00001027 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001028}
1029
1030
Erik Andersend75af992000-03-16 08:09:09 +00001031static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001032{
Erik Andersen161220c2000-03-16 08:12:48 +00001033 char *command;
1034 char *nextCommand = NULL;
1035 struct jobSet jobList = { NULL, NULL };
1036 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001037 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001038 int i;
1039 int status;
1040 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +00001041
Eric Andersen1c314ad2000-06-28 16:56:25 +00001042 /* save current owner of TTY so we can restore it on exit */
1043 parent_pgrp = tcgetpgrp(0);
1044
Erik Andersen161220c2000-03-16 08:12:48 +00001045 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001046
Erik Andersen161220c2000-03-16 08:12:48 +00001047 /* don't pay any attention to this signal; it just confuses
1048 things and isn't really meant for shells anyway */
1049 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001050
Erik Andersen161220c2000-03-16 08:12:48 +00001051 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001052 if (!jobList.fg) {
1053 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001054
Erik Andersend75af992000-03-16 08:09:09 +00001055 /* see if any background processes have exited */
1056 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001057
Erik Andersend75af992000-03-16 08:09:09 +00001058 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001059 if (getCommand(input, command))
1060 break;
1061 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001062 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001063
Eric Andersenec10b9d2000-07-14 01:13:11 +00001064 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001065 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001066 int pipefds[2] = {-1,-1};
1067 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001068 } else {
1069 nextCommand=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +00001070 free(command);
1071 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001072 }
1073 } else {
1074 /* a job is running in the foreground; wait for it */
1075 i = 0;
1076 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +00001077 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001078
Erik Andersend75af992000-03-16 08:09:09 +00001079 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001080
Erik Andersend75af992000-03-16 08:09:09 +00001081 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001082 /* the child exited */
1083 jobList.fg->runningProgs--;
1084 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001085
Erik Andersen161220c2000-03-16 08:12:48 +00001086 if (!jobList.fg->runningProgs) {
1087 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001088
Erik Andersen161220c2000-03-16 08:12:48 +00001089 removeJob(&jobList, jobList.fg);
1090 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001091 }
Erik Andersend75af992000-03-16 08:09:09 +00001092 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001093 /* the child was stopped */
1094 jobList.fg->stoppedProgs++;
1095 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001096
Erik Andersen161220c2000-03-16 08:12:48 +00001097 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1098 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1099 "Stopped", jobList.fg->text);
1100 jobList.fg = NULL;
1101 }
Erik Andersend75af992000-03-16 08:09:09 +00001102 }
1103
1104 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001105 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001106 /* suppress messages when run from /linuxrc mag@sysgo.de */
1107 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1108 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001109 }
1110 }
1111 }
Erik Andersen161220c2000-03-16 08:12:48 +00001112 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001113
Eric Andersen1c314ad2000-06-28 16:56:25 +00001114 /* return controlling TTY back to parent process group before exiting */
1115 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001116 perror("tcsetpgrp");
1117
1118 /* return exit status if called with "-c" */
1119 if (input == NULL && WIFEXITED(status))
1120 return WEXITSTATUS(status);
1121
Erik Andersen161220c2000-03-16 08:12:48 +00001122 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001123}
1124
1125
Erik Andersend75af992000-03-16 08:09:09 +00001126int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +00001127{
Erik Andersen161220c2000-03-16 08:12:48 +00001128 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +00001129
Erik Andersen161220c2000-03-16 08:12:48 +00001130 /* initialize the cwd */
Eric Andersenb54833c2000-07-03 23:56:26 +00001131 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1132 if (cwd == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001133 fatalError("out of memory\n");
Eric Andersenb54833c2000-07-03 23:56:26 +00001134 }
1135 getcwd(cwd, sizeof(char)*BUFSIZ);
Erik Andersen3522eb12000-03-12 23:49:18 +00001136
Erik Andersenf0657d32000-04-12 17:49:52 +00001137#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +00001138 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +00001139 signal(SIGWINCH, win_changed);
1140 win_changed(0);
1141#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001142
Erik Andersen161220c2000-03-16 08:12:48 +00001143 //if (argv[0] && argv[0][0] == '-') {
Eric Andersen34e19412000-07-10 18:47:24 +00001144 // builtin_source("/etc/profile");
Erik Andersen161220c2000-03-16 08:12:48 +00001145 //}
Erik Andersen3522eb12000-03-12 23:49:18 +00001146
Erik Andersen161220c2000-03-16 08:12:48 +00001147 if (argc < 2) {
Pavel Roskin9c5fcc32000-07-17 23:45:12 +00001148 fprintf(stdout, "\n\n%s Built-in shell\n", full_version);
Erik Andersenf0657d32000-04-12 17:49:52 +00001149 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001150 } else {
Eric Andersen1c314ad2000-06-28 16:56:25 +00001151 if (argv[1][0]=='-' && argv[1][1]=='c') {
1152 int i;
1153 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1154 if (local_pending_command == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001155 fatalError("out of memory\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001156 }
1157 for(i=2; i<argc; i++)
1158 {
1159 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
Eric Andersenb54833c2000-07-03 23:56:26 +00001160 local_pending_command = realloc(local_pending_command,
1161 strlen(local_pending_command) + strlen(argv[i]));
1162 if (local_pending_command==NULL)
Matt Kraaibe84cd42000-07-12 17:02:35 +00001163 fatalError("commands for -c option too long\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001164 }
1165 strcat(local_pending_command, argv[i]);
Eric Andersenb54833c2000-07-03 23:56:26 +00001166 if ( (i + 1) < argc)
Eric Andersen1c314ad2000-06-28 16:56:25 +00001167 strcat(local_pending_command, " ");
1168 }
1169 input = NULL;
1170
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001171 }
Eric Andersen1c314ad2000-06-28 16:56:25 +00001172 else if (argv[1][0]=='-') {
1173 usage(shell_usage);
1174 }
1175 else {
1176 input = fopen(argv[1], "r");
1177 if (!input) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001178 fatalError("Couldn't open file '%s': %s\n", argv[1],
Eric Andersen1c314ad2000-06-28 16:56:25 +00001179 strerror(errno));
1180 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001181 }
Erik Andersen161220c2000-03-16 08:12:48 +00001182 }
Erik Andersend75af992000-03-16 08:09:09 +00001183
Erik Andersen161220c2000-03-16 08:12:48 +00001184 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001185}