blob: 1dfce9e776ddfd997f7750ec005014bd8e294164 [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
Eric Andersenb54833c2000-07-03 23:56:26 +000049#define MAX_READ 128 /* size of input buffer for `read' builtin */
Erik Andersen3522eb12000-03-12 23:49:18 +000050#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
51
Erik Andersend75af992000-03-16 08:09:09 +000052
53enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000054 REDIRECT_APPEND
55};
Erik Andersen3522eb12000-03-12 23:49:18 +000056
57struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000058 struct job *head; /* head of list of running jobs */
59 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000060};
61
62struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000063 enum redirectionType type; /* type of redirection */
64 int fd; /* file descriptor being redirected */
65 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000066};
67
68struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000069 pid_t pid; /* 0 if exited */
70 char **argv; /* program name and arguments */
71 int numRedirections; /* elements in redirection array */
72 struct redirectionSpecifier *redirections; /* I/O redirections */
73 glob_t globResult; /* result of parameter globbing */
74 int freeGlob; /* should we globfree(&globResult)? */
75 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000076};
77
78struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000079 int jobId; /* job number */
80 int numProgs; /* total number of programs in job */
81 int runningProgs; /* number of programs running */
82 char *text; /* name of job */
83 char *cmdBuf; /* buffer various argv's point into */
84 pid_t pgrp; /* process group ID for the job */
85 struct childProgram *progs; /* array of programs in job */
86 struct job *next; /* to track background commands */
87 int stoppedProgs; /* number of programs alive, but stopped */
Erik Andersen3522eb12000-03-12 23:49:18 +000088};
89
90struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +000091 char *cmd; /* name */
92 char *descr; /* description */
93 char *usage; /* usage */
94 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +000095};
96
Eric Andersen34e19412000-07-10 18:47:24 +000097/* function prototypes for builtins */
98static int builtin_cd(struct job *cmd, struct jobSet *junk);
99static int builtin_env(struct job *dummy, struct jobSet *junk);
100static int builtin_exit(struct job *cmd, struct jobSet *junk);
101static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
102static int builtin_help(struct job *cmd, struct jobSet *junk);
103static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
104static int builtin_pwd(struct job *dummy, struct jobSet *junk);
105static int builtin_export(struct job *cmd, struct jobSet *junk);
106static int builtin_source(struct job *cmd, struct jobSet *jobList);
107static int builtin_unset(struct job *cmd, struct jobSet *junk);
108static int builtin_read(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000109
Eric Andersen34e19412000-07-10 18:47:24 +0000110
111/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000112static void checkJobs(struct jobSet *jobList);
113static int getCommand(FILE * source, char *command);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000114static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
Erik Andersend75af992000-03-16 08:09:09 +0000115static int setupRedirections(struct childProgram *prog);
Eric Andersena1d187a2000-07-17 19:14:41 +0000116static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
Erik Andersen3522eb12000-03-12 23:49:18 +0000117static int busy_loop(FILE * input);
118
Erik Andersend75af992000-03-16 08:09:09 +0000119
Mark Whitley37653aa2000-07-12 23:36:17 +0000120/* Table of built-in functions (these are non-forking builtins, meaning they
121 * can change global variables in the parent shell process but they will not
122 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000123static struct builtInCommand bltins[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000124 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
125 {"cd", "Change working directory", "cd [dir]", builtin_cd},
126 {"exit", "Exit from shell()", "exit", builtin_exit},
127 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
128 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
129 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
130 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
131 {"read", "Input environment variable", "read [VAR]", builtin_read},
Erik Andersen330fd2b2000-05-19 05:35:19 +0000132 {NULL, NULL, NULL, NULL}
133};
134
Mark Whitley37653aa2000-07-12 23:36:17 +0000135/* Table of forking built-in functions (things that fork cannot change global
136 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000137static struct builtInCommand bltins_forking[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000138 {"env", "Print all environment variables", "env", builtin_env},
139 {"pwd", "Print current directory", "pwd", builtin_pwd},
140 {".", "Source-in and run commands in a file", ". filename", builtin_source},
141 {"help", "List shell built-in commands", "help", builtin_help},
Erik Andersen161220c2000-03-16 08:12:48 +0000142 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000143};
144
Erik Andersen3522eb12000-03-12 23:49:18 +0000145static char *prompt = "# ";
Eric Andersenb54833c2000-07-03 23:56:26 +0000146static char *cwd = NULL;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000147static char *local_pending_command = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000148
Erik Andersenf0657d32000-04-12 17:49:52 +0000149#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenfad04fd2000-07-14 06:49:52 +0000150void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000151{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000152 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000153 ioctl(0, TIOCGWINSZ, &win);
154 if (win.ws_col > 0) {
155 cmdedit_setwidth( win.ws_col - 1);
156 }
157}
158#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000159
Erik Andersen3522eb12000-03-12 23:49:18 +0000160
Erik Andersend75af992000-03-16 08:09:09 +0000161/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000162static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000163{
Erik Andersen161220c2000-03-16 08:12:48 +0000164 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000165
Erik Andersen161220c2000-03-16 08:12:48 +0000166 if (!cmd->progs[0].argv[1] == 1)
167 newdir = getenv("HOME");
168 else
169 newdir = cmd->progs[0].argv[1];
170 if (chdir(newdir)) {
171 printf("cd: %s: %s\n", newdir, strerror(errno));
172 return FALSE;
173 }
174 getcwd(cwd, sizeof(cwd));
175
176 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000177}
178
179/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000180static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000181{
Erik Andersen161220c2000-03-16 08:12:48 +0000182 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000183
Erik Andersen161220c2000-03-16 08:12:48 +0000184 for (e = environ; *e; e++) {
185 fprintf(stdout, "%s\n", *e);
186 }
187 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000188}
189
190/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000191static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000192{
Erik Andersen161220c2000-03-16 08:12:48 +0000193 if (!cmd->progs[0].argv[1] == 1)
194 exit TRUE;
195
Eric Andersenb6106152000-06-19 17:25:40 +0000196 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000197}
198
199/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000200static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000201{
Erik Andersen161220c2000-03-16 08:12:48 +0000202 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000203 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000204
Erik Andersen161220c2000-03-16 08:12:48 +0000205 if (!jobList->head) {
206 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000207 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000208 cmd->progs[0].argv[0]);
209 return FALSE;
210 }
211 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000212 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000213 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
214 return FALSE;
215 for (job = jobList->head; job; job = job->next) {
216 if (job->jobId == jobNum) {
217 break;
218 }
219 }
220 }
221 } else {
222 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000223 }
Erik Andersen161220c2000-03-16 08:12:48 +0000224
225 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000226 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000227 cmd->progs[0].argv[0], jobNum);
228 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000229 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000230
Erik Andersen161220c2000-03-16 08:12:48 +0000231 if (*cmd->progs[0].argv[0] == 'f') {
232 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000233 /* suppress messages when run from /linuxrc mag@sysgo.de */
234 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
235 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000236 jobList->fg = job;
237 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000238
Erik Andersen161220c2000-03-16 08:12:48 +0000239 /* Restart the processes in the job */
240 for (i = 0; i < job->numProgs; i++)
241 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000242
Erik Andersen161220c2000-03-16 08:12:48 +0000243 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000244
Erik Andersen161220c2000-03-16 08:12:48 +0000245 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000246
Erik Andersen161220c2000-03-16 08:12:48 +0000247 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000248}
249
250/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000251static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000252{
Erik Andersen161220c2000-03-16 08:12:48 +0000253 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000254
Erik Andersen161220c2000-03-16 08:12:48 +0000255 fprintf(stdout, "\nBuilt-in commands:\n");
256 fprintf(stdout, "-------------------\n");
257 for (x = bltins; x->cmd; x++) {
258 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
259 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000260 for (x = bltins_forking; x->cmd; x++) {
261 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
262 }
Erik Andersen161220c2000-03-16 08:12:48 +0000263 fprintf(stdout, "\n\n");
264 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000265}
266
267/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000268static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000269{
Erik Andersen161220c2000-03-16 08:12:48 +0000270 struct job *job;
271 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000272
Erik Andersen161220c2000-03-16 08:12:48 +0000273 for (job = jobList->head; job; job = job->next) {
274 if (job->runningProgs == job->stoppedProgs)
275 statusString = "Stopped";
276 else
277 statusString = "Running";
278
279 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
280 }
281 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000282}
283
284
285/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000286static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000287{
Erik Andersen161220c2000-03-16 08:12:48 +0000288 getcwd(cwd, sizeof(cwd));
289 fprintf(stdout, "%s\n", cwd);
290 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000291}
292
Erik Andersen6273f652000-03-17 01:12:41 +0000293/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000294static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000295{
Erik Andersen161220c2000-03-16 08:12:48 +0000296 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000297
Erik Andersen161220c2000-03-16 08:12:48 +0000298 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000299 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000300 }
301 res = putenv(cmd->progs[0].argv[1]);
302 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000303 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000304 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000305}
306
Eric Andersenb54833c2000-07-03 23:56:26 +0000307/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000308static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000309{
310 int res = 0, len, newlen;
311 char *s;
312 char string[MAX_READ];
313
314 if (cmd->progs[0].argv[1]) {
315 /* argument (VAR) given: put "VAR=" into buffer */
316 strcpy(string, cmd->progs[0].argv[1]);
317 len = strlen(string);
318 string[len++] = '=';
319 string[len] = '\0';
320 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
321 newlen = strlen(string);
322 if(newlen > len)
323 string[--newlen] = '\0'; /* chomp trailing newline */
324 /*
325 ** string should now contain "VAR=<value>"
326 ** copy it (putenv() won't do that, so we must make sure
327 ** the string resides in a static buffer!)
328 */
329 res = -1;
330 if((s = strdup(string)))
331 res = putenv(s);
332 if (res)
333 fprintf(stdout, "read: %s\n", strerror(errno));
334 }
335 else
336 fgets(string, sizeof(string), stdin);
337
338 return (res);
339}
340
Erik Andersen3522eb12000-03-12 23:49:18 +0000341/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000342static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000343{
Erik Andersen161220c2000-03-16 08:12:48 +0000344 FILE *input;
345 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000346
Erik Andersen161220c2000-03-16 08:12:48 +0000347 if (!cmd->progs[0].argv[1] == 1)
348 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000349
Erik Andersen161220c2000-03-16 08:12:48 +0000350 input = fopen(cmd->progs[0].argv[1], "r");
351 if (!input) {
352 fprintf(stdout, "Couldn't open file '%s'\n",
353 cmd->progs[0].argv[1]);
354 return FALSE;
355 }
Erik Andersend75af992000-03-16 08:09:09 +0000356
Erik Andersen161220c2000-03-16 08:12:48 +0000357 /* Now run the file */
358 status = busy_loop(input);
359 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000360}
361
362/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000363static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000364{
Erik Andersen161220c2000-03-16 08:12:48 +0000365 if (!cmd->progs[0].argv[1] == 1) {
366 fprintf(stdout, "unset: parameter required.\n");
367 return FALSE;
368 }
369 unsetenv(cmd->progs[0].argv[1]);
370 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000371}
372
373/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000374static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000375{
Erik Andersen161220c2000-03-16 08:12:48 +0000376 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000377
Erik Andersen161220c2000-03-16 08:12:48 +0000378 for (i = 0; i < cmd->numProgs; i++) {
379 free(cmd->progs[i].argv);
380 if (cmd->progs[i].redirections)
381 free(cmd->progs[i].redirections);
382 if (cmd->progs[i].freeGlob)
383 globfree(&cmd->progs[i].globResult);
384 }
385 free(cmd->progs);
386 if (cmd->text)
387 free(cmd->text);
388 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000389 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000390}
391
392/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000393static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000394{
Erik Andersen161220c2000-03-16 08:12:48 +0000395 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000396
Erik Andersen161220c2000-03-16 08:12:48 +0000397 freeJob(job);
398 if (job == jobList->head) {
399 jobList->head = job->next;
400 } else {
401 prevJob = jobList->head;
402 while (prevJob->next != job)
403 prevJob = prevJob->next;
404 prevJob->next = job->next;
405 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000406
Erik Andersen161220c2000-03-16 08:12:48 +0000407 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000408}
409
410/* Checks to see if any background processes have exited -- if they
411 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000412static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000413{
Erik Andersen161220c2000-03-16 08:12:48 +0000414 struct job *job;
415 pid_t childpid;
416 int status;
417 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000418
Erik Andersen161220c2000-03-16 08:12:48 +0000419 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
420 for (job = jobList->head; job; job = job->next) {
421 progNum = 0;
422 while (progNum < job->numProgs &&
423 job->progs[progNum].pid != childpid) progNum++;
424 if (progNum < job->numProgs)
425 break;
426 }
427
Eric Andersena1d187a2000-07-17 19:14:41 +0000428 /* This happens on backticked commands */
429 if(job==NULL)
430 return;
431
Erik Andersen161220c2000-03-16 08:12:48 +0000432 if (WIFEXITED(status) || WIFSIGNALED(status)) {
433 /* child exited */
434 job->runningProgs--;
435 job->progs[progNum].pid = 0;
436
437 if (!job->runningProgs) {
438 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
439 removeJob(jobList, job);
440 }
441 } else {
442 /* child stopped */
443 job->stoppedProgs++;
444 job->progs[progNum].isStopped = 1;
445
446 if (job->stoppedProgs == job->numProgs) {
447 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
448 job->text);
449 }
450 }
Erik Andersend75af992000-03-16 08:09:09 +0000451 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000452
Erik Andersen161220c2000-03-16 08:12:48 +0000453 if (childpid == -1 && errno != ECHILD)
454 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000455}
456
Erik Andersend75af992000-03-16 08:09:09 +0000457static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000458{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000459 if (source == NULL) {
460 if (local_pending_command) {
461 /* a command specified (-c option): return it & mark it done */
462 strcpy(command, local_pending_command);
463 free(local_pending_command);
464 local_pending_command = NULL;
465 return 0;
466 }
467 return 1;
468 }
469
Erik Andersen161220c2000-03-16 08:12:48 +0000470 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000471#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000472 int len;
473 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000474 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000475 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000476 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000477 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000478 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000479 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000480 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000481#else
482 fprintf(stdout, "%s %s", cwd, prompt);
483 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000484#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000485 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000486
Erik Andersen161220c2000-03-16 08:12:48 +0000487 if (!fgets(command, BUFSIZ - 2, source)) {
488 if (source == stdin)
489 printf("\n");
490 return 1;
491 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000492
Erik Andersen161220c2000-03-16 08:12:48 +0000493 /* remove trailing newline */
494 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000495
Erik Andersen161220c2000-03-16 08:12:48 +0000496 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000497}
498
Erik Andersend75af992000-03-16 08:09:09 +0000499static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000500 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000501{
Erik Andersen161220c2000-03-16 08:12:48 +0000502 int argc = *argcPtr;
503 int argcAlloced = *argcAllocedPtr;
504 int rc;
505 int flags;
506 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000507 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000508
Erik Andersen161220c2000-03-16 08:12:48 +0000509 if (argc > 1) { /* cmd->globResult is already initialized */
510 flags = GLOB_APPEND;
511 i = prog->globResult.gl_pathc;
512 } else {
513 prog->freeGlob = 1;
514 flags = 0;
515 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000516 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000517 /* do shell variable substitution */
518 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
519 prog->argv[argc - 1] = var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000520
Erik Andersen161220c2000-03-16 08:12:48 +0000521 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
522 if (rc == GLOB_NOSPACE) {
Matt Kraaid537a952000-07-14 01:51:25 +0000523 errorMsg("out of space during glob operation\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000524 return;
525 } else if (rc == GLOB_NOMATCH ||
526 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
527 !strcmp(prog->argv[argc - 1],
528 prog->globResult.gl_pathv[i]))) {
529 /* we need to remove whatever \ quoting is still present */
530 src = dst = prog->argv[argc - 1];
531 while (*src) {
532 if (*src != '\\')
533 *dst++ = *src;
534 src++;
535 }
536 *dst = '\0';
537 } else if (!rc) {
538 argcAlloced += (prog->globResult.gl_pathc - i);
539 prog->argv =
540 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
541 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
542 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
543 argc += (prog->globResult.gl_pathc - i - 1);
544 }
545
546 *argcAllocedPtr = argcAlloced;
547 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000548}
549
550/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
551 line). If a valid command is found, commandPtr is set to point to
552 the beginning of the next command (if the original command had more
553 then one job associated with it) or NULL if no more commands are
554 present. */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000555static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000556{
Erik Andersen161220c2000-03-16 08:12:48 +0000557 char *command;
558 char *returnCommand = NULL;
559 char *src, *buf, *chptr;
560 int argc = 0;
561 int done = 0;
562 int argvAlloced;
563 int i;
564 char quote = '\0';
565 int count;
566 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000567
Erik Andersen161220c2000-03-16 08:12:48 +0000568 /* skip leading white space */
569 while (**commandPtr && isspace(**commandPtr))
570 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000571
Erik Andersen161220c2000-03-16 08:12:48 +0000572 /* this handles empty lines or leading '#' characters */
573 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000574 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000575 return 0;
576 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000577
Erik Andersen161220c2000-03-16 08:12:48 +0000578 *isBg = 0;
579 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000580 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000581
Erik Andersen161220c2000-03-16 08:12:48 +0000582 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000583 memory is freed by freeJob(). Allocate twice the original
584 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000585
Erik Andersen161220c2000-03-16 08:12:48 +0000586 Getting clean memory relieves us of the task of NULL
587 terminating things and makes the rest of this look a bit
588 cleaner (though it is, admittedly, a tad less efficient) */
Eric Andersenb54833c2000-07-03 23:56:26 +0000589 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000590 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000591
Erik Andersen161220c2000-03-16 08:12:48 +0000592 prog = job->progs;
593 prog->numRedirections = 0;
594 prog->redirections = NULL;
595 prog->freeGlob = 0;
596 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000597
Erik Andersen161220c2000-03-16 08:12:48 +0000598 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000599 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000600 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000601
Erik Andersen161220c2000-03-16 08:12:48 +0000602 buf = command;
603 src = *commandPtr;
604 while (*src && !done) {
605 if (quote == *src) {
606 quote = '\0';
607 } else if (quote) {
608 if (*src == '\\') {
609 src++;
610 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000611 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000612 freeJob(job);
613 return 1;
614 }
615
616 /* in shell, "\'" should yield \' */
617 if (*src != quote)
618 *buf++ = '\\';
619 } else if (*src == '*' || *src == '?' || *src == '[' ||
620 *src == ']') *buf++ = '\\';
621 *buf++ = *src;
622 } else if (isspace(*src)) {
623 if (*prog->argv[argc]) {
624 buf++, argc++;
625 /* +1 here leaves room for the NULL which ends argv */
626 if ((argc + 1) == argvAlloced) {
627 argvAlloced += 5;
628 prog->argv = realloc(prog->argv,
629 sizeof(*prog->argv) *
630 argvAlloced);
631 }
Erik Andersen161220c2000-03-16 08:12:48 +0000632 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000633 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000634 }
635 } else
636 switch (*src) {
637 case '"':
638 case '\'':
639 quote = *src;
640 break;
641
642 case '#': /* comment */
643 done = 1;
644 break;
645
646 case '>': /* redirections */
647 case '<':
648 i = prog->numRedirections++;
649 prog->redirections = realloc(prog->redirections,
650 sizeof(*prog->redirections) *
651 (i + 1));
652
653 prog->redirections[i].fd = -1;
654 if (buf != prog->argv[argc]) {
655 /* the stuff before this character may be the file number
656 being redirected */
657 prog->redirections[i].fd =
658 strtol(prog->argv[argc], &chptr, 10);
659
660 if (*chptr && *prog->argv[argc]) {
661 buf++, argc++;
662 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000663 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000664 }
665 }
666
667 if (prog->redirections[i].fd == -1) {
668 if (*src == '>')
669 prog->redirections[i].fd = 1;
670 else
671 prog->redirections[i].fd = 0;
672 }
673
674 if (*src++ == '>') {
675 if (*src == '>')
676 prog->redirections[i].type =
677 REDIRECT_APPEND, src++;
678 else
679 prog->redirections[i].type = REDIRECT_OVERWRITE;
680 } else {
681 prog->redirections[i].type = REDIRECT_INPUT;
682 }
683
684 /* This isn't POSIX sh compliant. Oh well. */
685 chptr = src;
686 while (isspace(*chptr))
687 chptr++;
688
689 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000690 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000691 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000692 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000693 return 1;
694 }
695
696 prog->redirections[i].filename = buf;
697 while (*chptr && !isspace(*chptr))
698 *buf++ = *chptr++;
699
700 src = chptr - 1; /* we src++ later */
701 prog->argv[argc] = ++buf;
702 break;
703
704 case '|': /* pipe */
705 /* finish this command */
706 if (*prog->argv[argc])
707 argc++;
708 if (!argc) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000709 errorMsg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000710 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000711 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000712 return 1;
713 }
714 prog->argv[argc] = NULL;
715
716 /* and start the next */
717 job->numProgs++;
718 job->progs = realloc(job->progs,
719 sizeof(*job->progs) * job->numProgs);
720 prog = job->progs + (job->numProgs - 1);
721 prog->numRedirections = 0;
722 prog->redirections = NULL;
723 prog->freeGlob = 0;
724 argc = 0;
725
726 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000727 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000728 prog->argv[0] = ++buf;
729
730 src++;
731 while (*src && isspace(*src))
732 src++;
733
734 if (!*src) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000735 errorMsg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000736 freeJob(job);
737 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000738 return 1;
739 }
740 src--; /* we'll ++ it at the end of the loop */
741
742 break;
743
744 case '&': /* background */
745 *isBg = 1;
746 case ';': /* multiple commands */
747 done = 1;
748 returnCommand = *commandPtr + (src - *commandPtr) + 1;
749 break;
750
751 case '\\':
752 src++;
753 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000754 errorMsg("character expected after \\\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000755 freeJob(job);
Erik Andersen161220c2000-03-16 08:12:48 +0000756 return 1;
757 }
758 if (*src == '*' || *src == '[' || *src == ']'
759 || *src == '?') *buf++ = '\\';
760 /* fallthrough */
Eric Andersena1d187a2000-07-17 19:14:41 +0000761#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +0000762 case '`':
763 /* Exec a backtick-ed command */
764 {
Eric Andersena1d187a2000-07-17 19:14:41 +0000765 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000766 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +0000767 struct job *newJob;
768 struct jobSet njobList = { NULL, NULL };
769 int pipefd[2];
770 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000771
772 ptr=strchr(++src, '`');
773 if (ptr==NULL) {
774 fprintf(stderr, "Unmatched '`' in command\n");
775 freeJob(job);
776 return 1;
777 }
778
Eric Andersena1d187a2000-07-17 19:14:41 +0000779 /* Make a copy of any stuff left over in the command
780 * line after the second backtick */
781 charptr2 = xmalloc(strlen(ptr)+1);
782 memcpy(charptr2, ptr+1, strlen(ptr));
Eric Andersenec10b9d2000-07-14 01:13:11 +0000783
Eric Andersena1d187a2000-07-17 19:14:41 +0000784 /* Make some space to hold just the backticked command */
785 charptr1 = xmalloc(1+ptr-src);
786 snprintf(charptr1, 1+ptr-src, src);
787 newJob = xmalloc(sizeof(struct job));
788 /* Now parse and run the backticked command */
789 if (!parseCommand(&charptr1, newJob, &njobList, isBg)
790 && newJob->numProgs) {
791 pipe(pipefd);
792 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000793 }
Eric Andersena1d187a2000-07-17 19:14:41 +0000794 checkJobs(jobList);
795 free(charptr1);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000796
Eric Andersena1d187a2000-07-17 19:14:41 +0000797 /* Copy the output from the backtick-ed command into the
798 * command line, making extra room as needed */
799 --src;
800 charptr1 = xmalloc(BUFSIZ);
801 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
802 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
803 if (newSize > BUFSIZ) {
804 *commandPtr=realloc(*commandPtr, src - *commandPtr +
805 size + 1 + strlen(charptr2));
806 }
807 memcpy(src, charptr1, size);
808 src+=size;
809 }
810 free(charptr1);
811 close(pipefd[0]);
812 if (*(src-1)=='\n')
813 --src;
814
815 /* Now paste into the *commandPtr all the stuff
816 * leftover after the second backtick */
817 memcpy(src, charptr2, strlen(charptr2));
818 fprintf(stderr,"*commandPtr='%s'\n", *commandPtr);
819 free(charptr2);
820
821
822 /* Now recursively call parseCommand to deal with the new
823 * and improved version of the command line with the backtick
824 * results expanded in place... */
825 return(parseCommand(commandPtr, job, jobList, isBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +0000826 }
827 break;
Eric Andersena1d187a2000-07-17 19:14:41 +0000828#endif // BB_FEATURE_SH_BACKTICKS
Erik Andersen161220c2000-03-16 08:12:48 +0000829 default:
830 *buf++ = *src;
831 }
832
Erik Andersend75af992000-03-16 08:09:09 +0000833 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000834 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000835
Erik Andersen161220c2000-03-16 08:12:48 +0000836 if (*prog->argv[argc]) {
837 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000838 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000839 }
840 if (!argc) {
841 freeJob(job);
842 return 0;
843 }
844 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000845
Erik Andersen161220c2000-03-16 08:12:48 +0000846 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000847 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000848 strcpy(job->text, *commandPtr);
849 } else {
850 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000851 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000852 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000853 strncpy(job->text, *commandPtr, count);
854 job->text[count] = '\0';
855 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000856
Erik Andersen161220c2000-03-16 08:12:48 +0000857 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000858
Erik Andersend75af992000-03-16 08:09:09 +0000859 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000860}
861
Erik Andersenbcd61772000-05-13 06:33:19 +0000862
Eric Andersena1d187a2000-07-17 19:14:41 +0000863static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +0000864{
Erik Andersen161220c2000-03-16 08:12:48 +0000865 struct job *job;
Eric Andersena1d187a2000-07-17 19:14:41 +0000866 int nextin=0, nextout, stdoutfd=fileno(stdout);
Erik Andersen161220c2000-03-16 08:12:48 +0000867 int i;
Erik Andersen161220c2000-03-16 08:12:48 +0000868 int pipefds[2]; /* pipefd[0] is for reading */
869 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +0000870#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +0000871 const struct BB_applet *a = applets;
872#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000873
Eric Andersenec10b9d2000-07-14 01:13:11 +0000874 for (i = 0; i < newJob->numProgs; i++) {
875 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +0000876 pipe(pipefds);
877 nextout = pipefds[1];
878 } else {
Eric Andersena1d187a2000-07-17 19:14:41 +0000879 nextout = stdoutfd;
Erik Andersen161220c2000-03-16 08:12:48 +0000880 }
881
Eric Andersen34e19412000-07-10 18:47:24 +0000882 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000883 for (x = bltins; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000884 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
885 return (x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +0000886 }
887 }
888
Eric Andersenec10b9d2000-07-14 01:13:11 +0000889 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +0000890 signal(SIGTTOU, SIG_DFL);
891
Eric Andersena1d187a2000-07-17 19:14:41 +0000892 if (outPipe[1]!=-1) {
893 close(outPipe[0]);
894 nextout = stdoutfd = outPipe[1];
Erik Andersen161220c2000-03-16 08:12:48 +0000895 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +0000896 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +0000897 close(nextout);
898 }
899
Eric Andersena1d187a2000-07-17 19:14:41 +0000900 //dup2(nextin, 0);
901 //close(nextin);
902
Erik Andersen161220c2000-03-16 08:12:48 +0000903 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000904 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +0000905
Eric Andersen34e19412000-07-10 18:47:24 +0000906 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000907 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000908 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
909 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +0000910 }
911 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000912#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +0000913 /* Check if the command matches any busybox internal commands here */
Eric Andersena1d187a2000-07-17 19:14:41 +0000914 /* TODO: Add matching on commands with paths appended (i.e. 'cat'
915 * currently works, but '/bin/cat' doesn't ) */
Erik Andersenbcd61772000-05-13 06:33:19 +0000916 while (a->name != 0) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000917 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000918 int argc;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000919 char** argv=newJob->progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000920 for(argc=0;*argv!=NULL; argv++, argc++);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000921 exit((*(a->main)) (argc, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +0000922 }
923 a++;
924 }
925#endif
926
Eric Andersenec10b9d2000-07-14 01:13:11 +0000927 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
928 fatalError("%s: %s\n", newJob->progs[i].argv[0],
Erik Andersen161220c2000-03-16 08:12:48 +0000929 strerror(errno));
930 }
Eric Andersena1d187a2000-07-17 19:14:41 +0000931 if (outPipe[1]!=-1) {
932 close(outPipe[1]);
933 }
Erik Andersen161220c2000-03-16 08:12:48 +0000934
935 /* put our child in the process group whose leader is the
936 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000937 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +0000938
939 if (nextin != 0)
940 close(nextin);
Eric Andersena1d187a2000-07-17 19:14:41 +0000941 if (nextout != stdoutfd)
Erik Andersen161220c2000-03-16 08:12:48 +0000942 close(nextout);
943
944 /* If there isn't another process, nextin is garbage
945 but it doesn't matter */
946 nextin = pipefds[0];
947 }
948
Eric Andersenec10b9d2000-07-14 01:13:11 +0000949 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +0000950
951 /* find the ID for the job to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000952 newJob->jobId = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000953 for (job = jobList->head; job; job = job->next)
Eric Andersenec10b9d2000-07-14 01:13:11 +0000954 if (job->jobId >= newJob->jobId)
955 newJob->jobId = job->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000956
957 /* add the job to the list of running jobs */
958 if (!jobList->head) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000959 job = jobList->head = xmalloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000960 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000961 for (job = jobList->head; job->next; job = job->next);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000962 job->next = xmalloc(sizeof(*job));
Erik Andersen161220c2000-03-16 08:12:48 +0000963 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000964 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000965
Eric Andersenec10b9d2000-07-14 01:13:11 +0000966 *job = *newJob;
Erik Andersen161220c2000-03-16 08:12:48 +0000967 job->next = NULL;
968 job->runningProgs = job->numProgs;
969 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000970
Erik Andersen161220c2000-03-16 08:12:48 +0000971 if (inBg) {
972 /* we don't wait for background jobs to return -- append it
973 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000974 printf("[%d] %d\n", job->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +0000975 newJob->progs[newJob->numProgs - 1].pid);
Erik Andersen161220c2000-03-16 08:12:48 +0000976 } else {
977 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000978
Erik Andersen161220c2000-03-16 08:12:48 +0000979 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000980 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000981 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +0000982 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000983 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000984
Erik Andersen161220c2000-03-16 08:12:48 +0000985 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000986}
987
Erik Andersend75af992000-03-16 08:09:09 +0000988static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000989{
Erik Andersen161220c2000-03-16 08:12:48 +0000990 int i;
991 int openfd;
992 int mode = O_RDONLY;
993 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000994
Erik Andersen161220c2000-03-16 08:12:48 +0000995 for (i = 0; i < prog->numRedirections; i++, redir++) {
996 switch (redir->type) {
997 case REDIRECT_INPUT:
998 mode = O_RDONLY;
999 break;
1000 case REDIRECT_OVERWRITE:
1001 mode = O_RDWR | O_CREAT | O_TRUNC;
1002 break;
1003 case REDIRECT_APPEND:
1004 mode = O_RDWR | O_CREAT | O_APPEND;
1005 break;
1006 }
1007
1008 openfd = open(redir->filename, mode, 0666);
1009 if (openfd < 0) {
1010 /* this could get lost if stderr has been redirected, but
1011 bash and ash both lose it as well (though zsh doesn't!) */
Matt Kraaid537a952000-07-14 01:51:25 +00001012 errorMsg("error opening %s: %s\n", redir->filename,
Erik Andersen161220c2000-03-16 08:12:48 +00001013 strerror(errno));
1014 return 1;
1015 }
1016
1017 if (openfd != redir->fd) {
1018 dup2(openfd, redir->fd);
1019 close(openfd);
1020 }
Erik Andersend75af992000-03-16 08:09:09 +00001021 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001022
Erik Andersen161220c2000-03-16 08:12:48 +00001023 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001024}
1025
1026
Erik Andersend75af992000-03-16 08:09:09 +00001027static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001028{
Erik Andersen161220c2000-03-16 08:12:48 +00001029 char *command;
1030 char *nextCommand = NULL;
1031 struct jobSet jobList = { NULL, NULL };
1032 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001033 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001034 int i;
1035 int status;
1036 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +00001037
Eric Andersen1c314ad2000-06-28 16:56:25 +00001038 /* save current owner of TTY so we can restore it on exit */
1039 parent_pgrp = tcgetpgrp(0);
1040
Erik Andersen161220c2000-03-16 08:12:48 +00001041 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001042
Erik Andersen161220c2000-03-16 08:12:48 +00001043 /* don't pay any attention to this signal; it just confuses
1044 things and isn't really meant for shells anyway */
1045 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001046
Erik Andersen161220c2000-03-16 08:12:48 +00001047 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001048 if (!jobList.fg) {
1049 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001050
Erik Andersend75af992000-03-16 08:09:09 +00001051 /* see if any background processes have exited */
1052 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001053
Erik Andersend75af992000-03-16 08:09:09 +00001054 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001055 if (getCommand(input, command))
1056 break;
1057 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001058 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001059
Eric Andersenec10b9d2000-07-14 01:13:11 +00001060 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001061 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001062 int pipefds[2] = {-1,-1};
1063 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001064 } else {
1065 nextCommand=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +00001066 free(command);
1067 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001068 }
1069 } else {
1070 /* a job is running in the foreground; wait for it */
1071 i = 0;
1072 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +00001073 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001074
Erik Andersend75af992000-03-16 08:09:09 +00001075 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001076
Erik Andersend75af992000-03-16 08:09:09 +00001077 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001078 /* the child exited */
1079 jobList.fg->runningProgs--;
1080 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001081
Erik Andersen161220c2000-03-16 08:12:48 +00001082 if (!jobList.fg->runningProgs) {
1083 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001084
Erik Andersen161220c2000-03-16 08:12:48 +00001085 removeJob(&jobList, jobList.fg);
1086 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001087 }
Erik Andersend75af992000-03-16 08:09:09 +00001088 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001089 /* the child was stopped */
1090 jobList.fg->stoppedProgs++;
1091 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001092
Erik Andersen161220c2000-03-16 08:12:48 +00001093 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1094 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1095 "Stopped", jobList.fg->text);
1096 jobList.fg = NULL;
1097 }
Erik Andersend75af992000-03-16 08:09:09 +00001098 }
1099
1100 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001101 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001102 /* suppress messages when run from /linuxrc mag@sysgo.de */
1103 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1104 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001105 }
1106 }
1107 }
Erik Andersen161220c2000-03-16 08:12:48 +00001108 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001109
Eric Andersen1c314ad2000-06-28 16:56:25 +00001110 /* return controlling TTY back to parent process group before exiting */
1111 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001112 perror("tcsetpgrp");
1113
1114 /* return exit status if called with "-c" */
1115 if (input == NULL && WIFEXITED(status))
1116 return WEXITSTATUS(status);
1117
Erik Andersen161220c2000-03-16 08:12:48 +00001118 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001119}
1120
1121
Erik Andersend75af992000-03-16 08:09:09 +00001122int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +00001123{
Erik Andersen161220c2000-03-16 08:12:48 +00001124 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +00001125
Erik Andersen161220c2000-03-16 08:12:48 +00001126 /* initialize the cwd */
Eric Andersenb54833c2000-07-03 23:56:26 +00001127 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1128 if (cwd == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001129 fatalError("out of memory\n");
Eric Andersenb54833c2000-07-03 23:56:26 +00001130 }
1131 getcwd(cwd, sizeof(char)*BUFSIZ);
Erik Andersen3522eb12000-03-12 23:49:18 +00001132
Erik Andersenf0657d32000-04-12 17:49:52 +00001133#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +00001134 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +00001135 signal(SIGWINCH, win_changed);
1136 win_changed(0);
1137#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001138
Erik Andersen161220c2000-03-16 08:12:48 +00001139 //if (argv[0] && argv[0][0] == '-') {
Eric Andersen34e19412000-07-10 18:47:24 +00001140 // builtin_source("/etc/profile");
Erik Andersen161220c2000-03-16 08:12:48 +00001141 //}
Erik Andersen3522eb12000-03-12 23:49:18 +00001142
Erik Andersen161220c2000-03-16 08:12:48 +00001143 if (argc < 2) {
Erik Andersenf0657d32000-04-12 17:49:52 +00001144 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1145 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001146 } else {
Eric Andersen1c314ad2000-06-28 16:56:25 +00001147 if (argv[1][0]=='-' && argv[1][1]=='c') {
1148 int i;
1149 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1150 if (local_pending_command == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001151 fatalError("out of memory\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001152 }
1153 for(i=2; i<argc; i++)
1154 {
1155 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
Eric Andersenb54833c2000-07-03 23:56:26 +00001156 local_pending_command = realloc(local_pending_command,
1157 strlen(local_pending_command) + strlen(argv[i]));
1158 if (local_pending_command==NULL)
Matt Kraaibe84cd42000-07-12 17:02:35 +00001159 fatalError("commands for -c option too long\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001160 }
1161 strcat(local_pending_command, argv[i]);
Eric Andersenb54833c2000-07-03 23:56:26 +00001162 if ( (i + 1) < argc)
Eric Andersen1c314ad2000-06-28 16:56:25 +00001163 strcat(local_pending_command, " ");
1164 }
1165 input = NULL;
1166
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001167 }
Eric Andersen1c314ad2000-06-28 16:56:25 +00001168 else if (argv[1][0]=='-') {
1169 usage(shell_usage);
1170 }
1171 else {
1172 input = fopen(argv[1], "r");
1173 if (!input) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001174 fatalError("Couldn't open file '%s': %s\n", argv[1],
Eric Andersen1c314ad2000-06-28 16:56:25 +00001175 strerror(errno));
1176 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001177 }
Erik Andersen161220c2000-03-16 08:12:48 +00001178 }
Erik Andersend75af992000-03-16 08:09:09 +00001179
Erik Andersen161220c2000-03-16 08:12:48 +00001180 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001181}