blob: 6029eaa5c38846fa788640a0bf84f3d8fcba1f04 [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 Andersen6efc48c2000-07-18 08:16:39 +000049#define MAX_LINE 256 /* size of input buffer for `read' builtin */
Eric Andersenb54833c2000-07-03 23:56:26 +000050#define MAX_READ 128 /* size of input buffer for `read' builtin */
Erik Andersen3522eb12000-03-12 23:49:18 +000051#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
52
Erik Andersend75af992000-03-16 08:09:09 +000053
54enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000055 REDIRECT_APPEND
56};
Erik Andersen3522eb12000-03-12 23:49:18 +000057
58struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000059 struct job *head; /* head of list of running jobs */
60 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000061};
62
63struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000064 enum redirectionType type; /* type of redirection */
65 int fd; /* file descriptor being redirected */
66 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000067};
68
69struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000070 pid_t pid; /* 0 if exited */
71 char **argv; /* program name and arguments */
72 int numRedirections; /* elements in redirection array */
73 struct redirectionSpecifier *redirections; /* I/O redirections */
74 glob_t globResult; /* result of parameter globbing */
75 int freeGlob; /* should we globfree(&globResult)? */
76 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000077};
78
79struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000080 int jobId; /* job number */
81 int numProgs; /* total number of programs in job */
82 int runningProgs; /* number of programs running */
83 char *text; /* name of job */
84 char *cmdBuf; /* buffer various argv's point into */
85 pid_t pgrp; /* process group ID for the job */
86 struct childProgram *progs; /* array of programs in job */
87 struct job *next; /* to track background commands */
88 int stoppedProgs; /* number of programs alive, but stopped */
Erik Andersen3522eb12000-03-12 23:49:18 +000089};
90
91struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +000092 char *cmd; /* name */
93 char *descr; /* description */
94 char *usage; /* usage */
95 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +000096};
97
Eric Andersen34e19412000-07-10 18:47:24 +000098/* function prototypes for builtins */
99static int builtin_cd(struct job *cmd, struct jobSet *junk);
100static int builtin_env(struct job *dummy, struct jobSet *junk);
101static int builtin_exit(struct job *cmd, struct jobSet *junk);
102static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
103static int builtin_help(struct job *cmd, struct jobSet *junk);
104static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
105static int builtin_pwd(struct job *dummy, struct jobSet *junk);
106static int builtin_export(struct job *cmd, struct jobSet *junk);
107static int builtin_source(struct job *cmd, struct jobSet *jobList);
108static int builtin_unset(struct job *cmd, struct jobSet *junk);
109static int builtin_read(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000110
Eric Andersen34e19412000-07-10 18:47:24 +0000111
112/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000113static void checkJobs(struct jobSet *jobList);
114static int getCommand(FILE * source, char *command);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000115static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
Erik Andersend75af992000-03-16 08:09:09 +0000116static int setupRedirections(struct childProgram *prog);
Eric Andersena1d187a2000-07-17 19:14:41 +0000117static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2]);
Erik Andersen3522eb12000-03-12 23:49:18 +0000118static int busy_loop(FILE * input);
119
Erik Andersend75af992000-03-16 08:09:09 +0000120
Mark Whitley37653aa2000-07-12 23:36:17 +0000121/* Table of built-in functions (these are non-forking builtins, meaning they
122 * can change global variables in the parent shell process but they will not
123 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000124static struct builtInCommand bltins[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000125 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
126 {"cd", "Change working directory", "cd [dir]", builtin_cd},
127 {"exit", "Exit from shell()", "exit", builtin_exit},
128 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
129 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
130 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
131 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
132 {"read", "Input environment variable", "read [VAR]", builtin_read},
Erik Andersen330fd2b2000-05-19 05:35:19 +0000133 {NULL, NULL, NULL, NULL}
134};
135
Mark Whitley37653aa2000-07-12 23:36:17 +0000136/* Table of forking built-in functions (things that fork cannot change global
137 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000138static struct builtInCommand bltins_forking[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000139 {"env", "Print all environment variables", "env", builtin_env},
140 {"pwd", "Print current directory", "pwd", builtin_pwd},
141 {".", "Source-in and run commands in a file", ". filename", builtin_source},
142 {"help", "List shell built-in commands", "help", builtin_help},
Erik Andersen161220c2000-03-16 08:12:48 +0000143 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000144};
145
Erik Andersen3522eb12000-03-12 23:49:18 +0000146static char *prompt = "# ";
Eric Andersen6efc48c2000-07-18 08:16:39 +0000147static char *cwd;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000148static char *local_pending_command = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000149
Erik Andersenf0657d32000-04-12 17:49:52 +0000150#ifdef BB_FEATURE_SH_COMMAND_EDITING
Eric Andersenfad04fd2000-07-14 06:49:52 +0000151void win_changed(int junk)
Erik Andersenf0657d32000-04-12 17:49:52 +0000152{
Eric Andersenfad04fd2000-07-14 06:49:52 +0000153 struct winsize win = { 0, 0, 0, 0 };
Erik Andersenf0657d32000-04-12 17:49:52 +0000154 ioctl(0, TIOCGWINSZ, &win);
155 if (win.ws_col > 0) {
156 cmdedit_setwidth( win.ws_col - 1);
157 }
158}
159#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000160
Erik Andersen3522eb12000-03-12 23:49:18 +0000161
Erik Andersend75af992000-03-16 08:09:09 +0000162/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000163static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000164{
Erik Andersen161220c2000-03-16 08:12:48 +0000165 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000166
Erik Andersen161220c2000-03-16 08:12:48 +0000167 if (!cmd->progs[0].argv[1] == 1)
168 newdir = getenv("HOME");
169 else
170 newdir = cmd->progs[0].argv[1];
171 if (chdir(newdir)) {
172 printf("cd: %s: %s\n", newdir, strerror(errno));
173 return FALSE;
174 }
Eric Andersen6efc48c2000-07-18 08:16:39 +0000175 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000176
177 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000178}
179
180/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000181static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000182{
Erik Andersen161220c2000-03-16 08:12:48 +0000183 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000184
Erik Andersen161220c2000-03-16 08:12:48 +0000185 for (e = environ; *e; e++) {
186 fprintf(stdout, "%s\n", *e);
187 }
188 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000189}
190
191/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000192static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000193{
Erik Andersen161220c2000-03-16 08:12:48 +0000194 if (!cmd->progs[0].argv[1] == 1)
195 exit TRUE;
196
Eric Andersenb6106152000-06-19 17:25:40 +0000197 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000198}
199
200/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000201static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000202{
Erik Andersen161220c2000-03-16 08:12:48 +0000203 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000204 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000205
Erik Andersen161220c2000-03-16 08:12:48 +0000206 if (!jobList->head) {
207 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000208 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000209 cmd->progs[0].argv[0]);
210 return FALSE;
211 }
212 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000213 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000214 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
215 return FALSE;
216 for (job = jobList->head; job; job = job->next) {
217 if (job->jobId == jobNum) {
218 break;
219 }
220 }
221 }
222 } else {
223 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000224 }
Erik Andersen161220c2000-03-16 08:12:48 +0000225
226 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000227 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000228 cmd->progs[0].argv[0], jobNum);
229 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000230 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000231
Erik Andersen161220c2000-03-16 08:12:48 +0000232 if (*cmd->progs[0].argv[0] == 'f') {
233 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000234 /* suppress messages when run from /linuxrc mag@sysgo.de */
235 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
236 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000237 jobList->fg = job;
238 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000239
Erik Andersen161220c2000-03-16 08:12:48 +0000240 /* Restart the processes in the job */
241 for (i = 0; i < job->numProgs; i++)
242 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000243
Erik Andersen161220c2000-03-16 08:12:48 +0000244 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000245
Erik Andersen161220c2000-03-16 08:12:48 +0000246 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000247
Erik Andersen161220c2000-03-16 08:12:48 +0000248 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000249}
250
251/* built-in 'help' handler */
Eric Andersenfad04fd2000-07-14 06:49:52 +0000252static int builtin_help(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000253{
Erik Andersen161220c2000-03-16 08:12:48 +0000254 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000255
Erik Andersen161220c2000-03-16 08:12:48 +0000256 fprintf(stdout, "\nBuilt-in commands:\n");
257 fprintf(stdout, "-------------------\n");
258 for (x = bltins; x->cmd; x++) {
259 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
260 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000261 for (x = bltins_forking; x->cmd; x++) {
262 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
263 }
Erik Andersen161220c2000-03-16 08:12:48 +0000264 fprintf(stdout, "\n\n");
265 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000266}
267
268/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000269static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000270{
Erik Andersen161220c2000-03-16 08:12:48 +0000271 struct job *job;
272 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000273
Erik Andersen161220c2000-03-16 08:12:48 +0000274 for (job = jobList->head; job; job = job->next) {
275 if (job->runningProgs == job->stoppedProgs)
276 statusString = "Stopped";
277 else
278 statusString = "Running";
279
280 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
281 }
282 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000283}
284
285
286/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000287static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000288{
Eric Andersen6efc48c2000-07-18 08:16:39 +0000289 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen161220c2000-03-16 08:12:48 +0000290 fprintf(stdout, "%s\n", cwd);
291 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000292}
293
Erik Andersen6273f652000-03-17 01:12:41 +0000294/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000295static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000296{
Erik Andersen161220c2000-03-16 08:12:48 +0000297 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000298
Erik Andersen161220c2000-03-16 08:12:48 +0000299 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000300 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000301 }
302 res = putenv(cmd->progs[0].argv[1]);
303 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000304 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000305 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000306}
307
Eric Andersenb54833c2000-07-03 23:56:26 +0000308/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000309static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000310{
311 int res = 0, len, newlen;
312 char *s;
313 char string[MAX_READ];
314
315 if (cmd->progs[0].argv[1]) {
316 /* argument (VAR) given: put "VAR=" into buffer */
317 strcpy(string, cmd->progs[0].argv[1]);
318 len = strlen(string);
319 string[len++] = '=';
320 string[len] = '\0';
321 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
322 newlen = strlen(string);
323 if(newlen > len)
324 string[--newlen] = '\0'; /* chomp trailing newline */
325 /*
326 ** string should now contain "VAR=<value>"
327 ** copy it (putenv() won't do that, so we must make sure
328 ** the string resides in a static buffer!)
329 */
330 res = -1;
331 if((s = strdup(string)))
332 res = putenv(s);
333 if (res)
334 fprintf(stdout, "read: %s\n", strerror(errno));
335 }
336 else
337 fgets(string, sizeof(string), stdin);
338
339 return (res);
340}
341
Erik Andersen3522eb12000-03-12 23:49:18 +0000342/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000343static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000344{
Erik Andersen161220c2000-03-16 08:12:48 +0000345 FILE *input;
346 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000347
Erik Andersen161220c2000-03-16 08:12:48 +0000348 if (!cmd->progs[0].argv[1] == 1)
349 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000350
Erik Andersen161220c2000-03-16 08:12:48 +0000351 input = fopen(cmd->progs[0].argv[1], "r");
352 if (!input) {
353 fprintf(stdout, "Couldn't open file '%s'\n",
354 cmd->progs[0].argv[1]);
355 return FALSE;
356 }
Erik Andersend75af992000-03-16 08:09:09 +0000357
Erik Andersen161220c2000-03-16 08:12:48 +0000358 /* Now run the file */
359 status = busy_loop(input);
360 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000361}
362
363/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000364static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000365{
Erik Andersen161220c2000-03-16 08:12:48 +0000366 if (!cmd->progs[0].argv[1] == 1) {
367 fprintf(stdout, "unset: parameter required.\n");
368 return FALSE;
369 }
370 unsetenv(cmd->progs[0].argv[1]);
371 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000372}
373
374/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000375static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000376{
Erik Andersen161220c2000-03-16 08:12:48 +0000377 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000378
Erik Andersen161220c2000-03-16 08:12:48 +0000379 for (i = 0; i < cmd->numProgs; i++) {
380 free(cmd->progs[i].argv);
381 if (cmd->progs[i].redirections)
382 free(cmd->progs[i].redirections);
383 if (cmd->progs[i].freeGlob)
384 globfree(&cmd->progs[i].globResult);
385 }
386 free(cmd->progs);
387 if (cmd->text)
388 free(cmd->text);
389 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000390 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000391}
392
393/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000394static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000395{
Erik Andersen161220c2000-03-16 08:12:48 +0000396 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000397
Erik Andersen161220c2000-03-16 08:12:48 +0000398 freeJob(job);
399 if (job == jobList->head) {
400 jobList->head = job->next;
401 } else {
402 prevJob = jobList->head;
403 while (prevJob->next != job)
404 prevJob = prevJob->next;
405 prevJob->next = job->next;
406 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000407
Erik Andersen161220c2000-03-16 08:12:48 +0000408 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000409}
410
411/* Checks to see if any background processes have exited -- if they
412 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000413static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000414{
Erik Andersen161220c2000-03-16 08:12:48 +0000415 struct job *job;
416 pid_t childpid;
417 int status;
418 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000419
Erik Andersen161220c2000-03-16 08:12:48 +0000420 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
421 for (job = jobList->head; job; job = job->next) {
422 progNum = 0;
423 while (progNum < job->numProgs &&
424 job->progs[progNum].pid != childpid) progNum++;
425 if (progNum < job->numProgs)
426 break;
427 }
428
Eric Andersena1d187a2000-07-17 19:14:41 +0000429 /* This happens on backticked commands */
430 if(job==NULL)
431 return;
432
Erik Andersen161220c2000-03-16 08:12:48 +0000433 if (WIFEXITED(status) || WIFSIGNALED(status)) {
434 /* child exited */
435 job->runningProgs--;
436 job->progs[progNum].pid = 0;
437
438 if (!job->runningProgs) {
439 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
440 removeJob(jobList, job);
441 }
442 } else {
443 /* child stopped */
444 job->stoppedProgs++;
445 job->progs[progNum].isStopped = 1;
446
447 if (job->stoppedProgs == job->numProgs) {
448 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
449 job->text);
450 }
451 }
Erik Andersend75af992000-03-16 08:09:09 +0000452 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000453
Erik Andersen161220c2000-03-16 08:12:48 +0000454 if (childpid == -1 && errno != ECHILD)
455 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000456}
457
Erik Andersend75af992000-03-16 08:09:09 +0000458static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000459{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000460 if (source == NULL) {
461 if (local_pending_command) {
462 /* a command specified (-c option): return it & mark it done */
463 strcpy(command, local_pending_command);
464 free(local_pending_command);
465 local_pending_command = NULL;
466 return 0;
467 }
468 return 1;
469 }
470
Erik Andersen161220c2000-03-16 08:12:48 +0000471 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000472#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000473 int len;
474 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000475 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000476 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000477 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000478 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000479 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000480 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000481 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000482#else
483 fprintf(stdout, "%s %s", cwd, prompt);
484 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000485#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000486 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000487
Erik Andersen161220c2000-03-16 08:12:48 +0000488 if (!fgets(command, BUFSIZ - 2, source)) {
489 if (source == stdin)
490 printf("\n");
491 return 1;
492 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000493
Erik Andersen161220c2000-03-16 08:12:48 +0000494 /* remove trailing newline */
495 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000496
Erik Andersen161220c2000-03-16 08:12:48 +0000497 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000498}
499
Erik Andersend75af992000-03-16 08:09:09 +0000500static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000501 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000502{
Erik Andersen161220c2000-03-16 08:12:48 +0000503 int argc = *argcPtr;
504 int argcAlloced = *argcAllocedPtr;
505 int rc;
506 int flags;
507 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000508 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000509
Erik Andersen161220c2000-03-16 08:12:48 +0000510 if (argc > 1) { /* cmd->globResult is already initialized */
511 flags = GLOB_APPEND;
512 i = prog->globResult.gl_pathc;
513 } else {
514 prog->freeGlob = 1;
515 flags = 0;
516 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000517 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000518 /* do shell variable substitution */
519 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
520 prog->argv[argc - 1] = var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000521
Erik Andersen161220c2000-03-16 08:12:48 +0000522 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
523 if (rc == GLOB_NOSPACE) {
Matt Kraaid537a952000-07-14 01:51:25 +0000524 errorMsg("out of space during glob operation\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000525 return;
526 } else if (rc == GLOB_NOMATCH ||
527 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
528 !strcmp(prog->argv[argc - 1],
529 prog->globResult.gl_pathv[i]))) {
530 /* we need to remove whatever \ quoting is still present */
531 src = dst = prog->argv[argc - 1];
532 while (*src) {
533 if (*src != '\\')
534 *dst++ = *src;
535 src++;
536 }
537 *dst = '\0';
538 } else if (!rc) {
539 argcAlloced += (prog->globResult.gl_pathc - i);
540 prog->argv =
541 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
542 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
543 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
544 argc += (prog->globResult.gl_pathc - i - 1);
545 }
546
547 *argcAllocedPtr = argcAlloced;
548 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000549}
550
551/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
552 line). If a valid command is found, commandPtr is set to point to
553 the beginning of the next command (if the original command had more
554 then one job associated with it) or NULL if no more commands are
555 present. */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000556static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000557{
Erik Andersen161220c2000-03-16 08:12:48 +0000558 char *command;
559 char *returnCommand = NULL;
560 char *src, *buf, *chptr;
561 int argc = 0;
562 int done = 0;
563 int argvAlloced;
564 int i;
565 char quote = '\0';
566 int count;
567 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000568
Erik Andersen161220c2000-03-16 08:12:48 +0000569 /* skip leading white space */
570 while (**commandPtr && isspace(**commandPtr))
571 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000572
Erik Andersen161220c2000-03-16 08:12:48 +0000573 /* this handles empty lines or leading '#' characters */
574 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000575 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000576 return 0;
577 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000578
Erik Andersen161220c2000-03-16 08:12:48 +0000579 *isBg = 0;
580 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000581 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000582
Erik Andersen161220c2000-03-16 08:12:48 +0000583 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000584 memory is freed by freeJob(). Allocate twice the original
585 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000586
Erik Andersen161220c2000-03-16 08:12:48 +0000587 Getting clean memory relieves us of the task of NULL
588 terminating things and makes the rest of this look a bit
589 cleaner (though it is, admittedly, a tad less efficient) */
Eric Andersenb54833c2000-07-03 23:56:26 +0000590 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000591 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000592
Erik Andersen161220c2000-03-16 08:12:48 +0000593 prog = job->progs;
594 prog->numRedirections = 0;
595 prog->redirections = NULL;
596 prog->freeGlob = 0;
597 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000598
Erik Andersen161220c2000-03-16 08:12:48 +0000599 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000600 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000601 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000602
Erik Andersen161220c2000-03-16 08:12:48 +0000603 buf = command;
604 src = *commandPtr;
605 while (*src && !done) {
606 if (quote == *src) {
607 quote = '\0';
608 } else if (quote) {
609 if (*src == '\\') {
610 src++;
611 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000612 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000613 freeJob(job);
614 return 1;
615 }
616
617 /* in shell, "\'" should yield \' */
618 if (*src != quote)
619 *buf++ = '\\';
620 } else if (*src == '*' || *src == '?' || *src == '[' ||
621 *src == ']') *buf++ = '\\';
622 *buf++ = *src;
623 } else if (isspace(*src)) {
624 if (*prog->argv[argc]) {
625 buf++, argc++;
626 /* +1 here leaves room for the NULL which ends argv */
627 if ((argc + 1) == argvAlloced) {
628 argvAlloced += 5;
629 prog->argv = realloc(prog->argv,
630 sizeof(*prog->argv) *
631 argvAlloced);
632 }
Erik Andersen161220c2000-03-16 08:12:48 +0000633 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000634 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000635 }
636 } else
637 switch (*src) {
638 case '"':
639 case '\'':
640 quote = *src;
641 break;
642
643 case '#': /* comment */
644 done = 1;
645 break;
646
647 case '>': /* redirections */
648 case '<':
649 i = prog->numRedirections++;
650 prog->redirections = realloc(prog->redirections,
651 sizeof(*prog->redirections) *
652 (i + 1));
653
654 prog->redirections[i].fd = -1;
655 if (buf != prog->argv[argc]) {
656 /* the stuff before this character may be the file number
657 being redirected */
658 prog->redirections[i].fd =
659 strtol(prog->argv[argc], &chptr, 10);
660
661 if (*chptr && *prog->argv[argc]) {
662 buf++, argc++;
663 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000664 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000665 }
666 }
667
668 if (prog->redirections[i].fd == -1) {
669 if (*src == '>')
670 prog->redirections[i].fd = 1;
671 else
672 prog->redirections[i].fd = 0;
673 }
674
675 if (*src++ == '>') {
676 if (*src == '>')
677 prog->redirections[i].type =
678 REDIRECT_APPEND, src++;
679 else
680 prog->redirections[i].type = REDIRECT_OVERWRITE;
681 } else {
682 prog->redirections[i].type = REDIRECT_INPUT;
683 }
684
685 /* This isn't POSIX sh compliant. Oh well. */
686 chptr = src;
687 while (isspace(*chptr))
688 chptr++;
689
690 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000691 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000692 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000693 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000694 return 1;
695 }
696
697 prog->redirections[i].filename = buf;
698 while (*chptr && !isspace(*chptr))
699 *buf++ = *chptr++;
700
701 src = chptr - 1; /* we src++ later */
702 prog->argv[argc] = ++buf;
703 break;
704
705 case '|': /* pipe */
706 /* finish this command */
707 if (*prog->argv[argc])
708 argc++;
709 if (!argc) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000710 errorMsg("empty command in pipe\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000711 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000712 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000713 return 1;
714 }
715 prog->argv[argc] = NULL;
716
717 /* and start the next */
718 job->numProgs++;
719 job->progs = realloc(job->progs,
720 sizeof(*job->progs) * job->numProgs);
721 prog = job->progs + (job->numProgs - 1);
722 prog->numRedirections = 0;
723 prog->redirections = NULL;
724 prog->freeGlob = 0;
725 argc = 0;
726
727 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000728 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000729 prog->argv[0] = ++buf;
730
731 src++;
732 while (*src && isspace(*src))
733 src++;
734
735 if (!*src) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000736 errorMsg("empty command in pipe\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000737 freeJob(job);
738 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000739 return 1;
740 }
741 src--; /* we'll ++ it at the end of the loop */
742
743 break;
744
745 case '&': /* background */
746 *isBg = 1;
747 case ';': /* multiple commands */
748 done = 1;
749 returnCommand = *commandPtr + (src - *commandPtr) + 1;
750 break;
751
752 case '\\':
753 src++;
754 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000755 errorMsg("character expected after \\\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000756 freeJob(job);
Erik Andersen161220c2000-03-16 08:12:48 +0000757 return 1;
758 }
759 if (*src == '*' || *src == '[' || *src == ']'
760 || *src == '?') *buf++ = '\\';
761 /* fallthrough */
Eric Andersena1d187a2000-07-17 19:14:41 +0000762#ifdef BB_FEATURE_SH_BACKTICKS
Eric Andersenec10b9d2000-07-14 01:13:11 +0000763 case '`':
764 /* Exec a backtick-ed command */
765 {
Eric Andersena1d187a2000-07-17 19:14:41 +0000766 char* charptr1=NULL, *charptr2;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000767 char* ptr=NULL;
Eric Andersena1d187a2000-07-17 19:14:41 +0000768 struct job *newJob;
769 struct jobSet njobList = { NULL, NULL };
770 int pipefd[2];
771 int size;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000772
773 ptr=strchr(++src, '`');
774 if (ptr==NULL) {
775 fprintf(stderr, "Unmatched '`' in command\n");
776 freeJob(job);
777 return 1;
778 }
779
Eric Andersena1d187a2000-07-17 19:14:41 +0000780 /* Make some space to hold just the backticked command */
Eric Andersen6efc48c2000-07-18 08:16:39 +0000781 charptr1 = charptr2 = xmalloc(1+ptr-src);
Eric Andersena1d187a2000-07-17 19:14:41 +0000782 snprintf(charptr1, 1+ptr-src, src);
783 newJob = xmalloc(sizeof(struct job));
784 /* Now parse and run the backticked command */
785 if (!parseCommand(&charptr1, newJob, &njobList, isBg)
786 && newJob->numProgs) {
787 pipe(pipefd);
788 runCommand(newJob, &njobList, 0, pipefd);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000789 }
Eric Andersena1d187a2000-07-17 19:14:41 +0000790 checkJobs(jobList);
Eric Andersen6efc48c2000-07-18 08:16:39 +0000791 freeJob(newJob);
792 free(charptr2);
793
794 /* Make a copy of any stuff left over in the command
795 * line after the second backtick */
796 charptr2 = xmalloc(strlen(ptr)+1);
797 memcpy(charptr2, ptr+1, strlen(ptr));
798
Eric Andersenec10b9d2000-07-14 01:13:11 +0000799
Eric Andersena1d187a2000-07-17 19:14:41 +0000800 /* Copy the output from the backtick-ed command into the
801 * command line, making extra room as needed */
802 --src;
803 charptr1 = xmalloc(BUFSIZ);
804 while ( (size=fullRead(pipefd[0], charptr1, BUFSIZ-1)) >0) {
805 int newSize=src - *commandPtr + size + 1 + strlen(charptr2);
806 if (newSize > BUFSIZ) {
807 *commandPtr=realloc(*commandPtr, src - *commandPtr +
808 size + 1 + strlen(charptr2));
809 }
810 memcpy(src, charptr1, size);
811 src+=size;
812 }
813 free(charptr1);
814 close(pipefd[0]);
815 if (*(src-1)=='\n')
816 --src;
817
818 /* Now paste into the *commandPtr all the stuff
819 * leftover after the second backtick */
820 memcpy(src, charptr2, strlen(charptr2));
Eric Andersena1d187a2000-07-17 19:14:41 +0000821 free(charptr2);
822
Eric Andersena1d187a2000-07-17 19:14:41 +0000823 /* Now recursively call parseCommand to deal with the new
824 * and improved version of the command line with the backtick
825 * results expanded in place... */
Eric Andersen6efc48c2000-07-18 08:16:39 +0000826 freeJob(job);
Eric Andersena1d187a2000-07-17 19:14:41 +0000827 return(parseCommand(commandPtr, job, jobList, isBg));
Eric Andersenec10b9d2000-07-14 01:13:11 +0000828 }
829 break;
Eric Andersena1d187a2000-07-17 19:14:41 +0000830#endif // BB_FEATURE_SH_BACKTICKS
Erik Andersen161220c2000-03-16 08:12:48 +0000831 default:
832 *buf++ = *src;
833 }
834
Erik Andersend75af992000-03-16 08:09:09 +0000835 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000836 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000837
Erik Andersen161220c2000-03-16 08:12:48 +0000838 if (*prog->argv[argc]) {
839 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000840 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000841 }
842 if (!argc) {
843 freeJob(job);
844 return 0;
845 }
846 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000847
Erik Andersen161220c2000-03-16 08:12:48 +0000848 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000849 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000850 strcpy(job->text, *commandPtr);
851 } else {
852 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000853 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000854 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000855 strncpy(job->text, *commandPtr, count);
856 job->text[count] = '\0';
857 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000858
Erik Andersen161220c2000-03-16 08:12:48 +0000859 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000860
Erik Andersend75af992000-03-16 08:09:09 +0000861 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000862}
863
Eric Andersena1d187a2000-07-17 19:14:41 +0000864static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg, int outPipe[2])
Erik Andersen3522eb12000-03-12 23:49:18 +0000865{
Erik Andersen161220c2000-03-16 08:12:48 +0000866 struct job *job;
867 int i;
Eric Andersen6efc48c2000-07-18 08:16:39 +0000868 int nextin, nextout;
Erik Andersen161220c2000-03-16 08:12:48 +0000869 int pipefds[2]; /* pipefd[0] is for reading */
870 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +0000871#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +0000872 const struct BB_applet *a = applets;
873#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000874
Eric Andersen6efc48c2000-07-18 08:16:39 +0000875
876 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000877 for (i = 0; i < newJob->numProgs; i++) {
878 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +0000879 pipe(pipefds);
880 nextout = pipefds[1];
881 } else {
Eric Andersen6efc48c2000-07-18 08:16:39 +0000882 if (outPipe[1]!=-1) {
883 nextout = outPipe[1];
884 } else {
885 nextout = 1;
886 }
Erik Andersen161220c2000-03-16 08:12:48 +0000887 }
888
Eric Andersen34e19412000-07-10 18:47:24 +0000889 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000890 for (x = bltins; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000891 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
892 return (x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +0000893 }
894 }
895
Eric Andersenec10b9d2000-07-14 01:13:11 +0000896 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +0000897 signal(SIGTTOU, SIG_DFL);
898
Eric Andersena1d187a2000-07-17 19:14:41 +0000899 if (outPipe[1]!=-1) {
900 close(outPipe[0]);
Eric Andersen6efc48c2000-07-18 08:16:39 +0000901 }
902 if (nextin != 0) {
903 dup2(nextin, 0);
904 close(nextin);
905 }
906
907 if (nextout != 1) {
Erik Andersen161220c2000-03-16 08:12:48 +0000908 dup2(nextout, 1);
Eric Andersena1d187a2000-07-17 19:14:41 +0000909 dup2(nextout, 2);
Erik Andersen161220c2000-03-16 08:12:48 +0000910 close(nextout);
911 }
912
913 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000914 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +0000915
Eric Andersen34e19412000-07-10 18:47:24 +0000916 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000917 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000918 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
919 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +0000920 }
921 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000922#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +0000923 /* Check if the command matches any busybox internal commands here */
Eric Andersen6efc48c2000-07-18 08:16:39 +0000924 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
925 * works, but '/bin/cat' doesn't ) */
Erik Andersenbcd61772000-05-13 06:33:19 +0000926 while (a->name != 0) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000927 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000928 int argc;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000929 char** argv=newJob->progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000930 for(argc=0;*argv!=NULL; argv++, argc++);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000931 exit((*(a->main)) (argc, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +0000932 }
933 a++;
934 }
935#endif
936
Eric Andersenec10b9d2000-07-14 01:13:11 +0000937 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
Eric Andersen6efc48c2000-07-18 08:16:39 +0000938 fatalError("sh: %s: %s\n", newJob->progs[i].argv[0],
Erik Andersen161220c2000-03-16 08:12:48 +0000939 strerror(errno));
940 }
Eric Andersen6efc48c2000-07-18 08:16:39 +0000941 if (outPipe[1]!=-1) {
Eric Andersena1d187a2000-07-17 19:14:41 +0000942 close(outPipe[1]);
943 }
Erik Andersen161220c2000-03-16 08:12:48 +0000944
945 /* put our child in the process group whose leader is the
946 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000947 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +0000948 if (nextin != 0)
949 close(nextin);
Eric Andersen6efc48c2000-07-18 08:16:39 +0000950 if (nextout != 1)
Erik Andersen161220c2000-03-16 08:12:48 +0000951 close(nextout);
952
953 /* If there isn't another process, nextin is garbage
954 but it doesn't matter */
955 nextin = pipefds[0];
956 }
957
Eric Andersenec10b9d2000-07-14 01:13:11 +0000958 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +0000959
960 /* find the ID for the job to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000961 newJob->jobId = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000962 for (job = jobList->head; job; job = job->next)
Eric Andersenec10b9d2000-07-14 01:13:11 +0000963 if (job->jobId >= newJob->jobId)
964 newJob->jobId = job->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000965
966 /* add the job to the list of running jobs */
967 if (!jobList->head) {
Eric Andersen6efc48c2000-07-18 08:16:39 +0000968 job = jobList->head = malloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000969 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000970 for (job = jobList->head; job->next; job = job->next);
Eric Andersen6efc48c2000-07-18 08:16:39 +0000971 job->next = malloc(sizeof(*job));
Erik Andersen161220c2000-03-16 08:12:48 +0000972 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000973 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000974
Eric Andersenec10b9d2000-07-14 01:13:11 +0000975 *job = *newJob;
Erik Andersen161220c2000-03-16 08:12:48 +0000976 job->next = NULL;
977 job->runningProgs = job->numProgs;
978 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000979
Erik Andersen161220c2000-03-16 08:12:48 +0000980 if (inBg) {
981 /* we don't wait for background jobs to return -- append it
982 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000983 printf("[%d] %d\n", job->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +0000984 newJob->progs[newJob->numProgs - 1].pid);
Erik Andersen161220c2000-03-16 08:12:48 +0000985 } else {
986 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000987
Erik Andersen161220c2000-03-16 08:12:48 +0000988 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000989 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000990 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +0000991 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000992 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000993
Erik Andersen161220c2000-03-16 08:12:48 +0000994 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000995}
996
Erik Andersend75af992000-03-16 08:09:09 +0000997static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000998{
Erik Andersen161220c2000-03-16 08:12:48 +0000999 int i;
1000 int openfd;
1001 int mode = O_RDONLY;
1002 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +00001003
Erik Andersen161220c2000-03-16 08:12:48 +00001004 for (i = 0; i < prog->numRedirections; i++, redir++) {
1005 switch (redir->type) {
1006 case REDIRECT_INPUT:
1007 mode = O_RDONLY;
1008 break;
1009 case REDIRECT_OVERWRITE:
1010 mode = O_RDWR | O_CREAT | O_TRUNC;
1011 break;
1012 case REDIRECT_APPEND:
1013 mode = O_RDWR | O_CREAT | O_APPEND;
1014 break;
1015 }
1016
1017 openfd = open(redir->filename, mode, 0666);
1018 if (openfd < 0) {
1019 /* this could get lost if stderr has been redirected, but
1020 bash and ash both lose it as well (though zsh doesn't!) */
Matt Kraaid537a952000-07-14 01:51:25 +00001021 errorMsg("error opening %s: %s\n", redir->filename,
Erik Andersen161220c2000-03-16 08:12:48 +00001022 strerror(errno));
1023 return 1;
1024 }
1025
1026 if (openfd != redir->fd) {
1027 dup2(openfd, redir->fd);
1028 close(openfd);
1029 }
Erik Andersend75af992000-03-16 08:09:09 +00001030 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001031
Erik Andersen161220c2000-03-16 08:12:48 +00001032 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001033}
1034
1035
Erik Andersend75af992000-03-16 08:09:09 +00001036static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +00001037{
Erik Andersen161220c2000-03-16 08:12:48 +00001038 char *command;
1039 char *nextCommand = NULL;
1040 struct jobSet jobList = { NULL, NULL };
1041 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +00001042 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +00001043 int i;
1044 int status;
1045 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +00001046
Eric Andersen1c314ad2000-06-28 16:56:25 +00001047 /* save current owner of TTY so we can restore it on exit */
1048 parent_pgrp = tcgetpgrp(0);
1049
Erik Andersen161220c2000-03-16 08:12:48 +00001050 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +00001051
Erik Andersen161220c2000-03-16 08:12:48 +00001052 /* don't pay any attention to this signal; it just confuses
1053 things and isn't really meant for shells anyway */
1054 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001055
Erik Andersen161220c2000-03-16 08:12:48 +00001056 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001057 if (!jobList.fg) {
1058 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001059
Erik Andersend75af992000-03-16 08:09:09 +00001060 /* see if any background processes have exited */
1061 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001062
Erik Andersend75af992000-03-16 08:09:09 +00001063 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001064 if (getCommand(input, command))
1065 break;
1066 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001067 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001068
Eric Andersenec10b9d2000-07-14 01:13:11 +00001069 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001070 newJob.numProgs) {
Eric Andersena1d187a2000-07-17 19:14:41 +00001071 int pipefds[2] = {-1,-1};
1072 runCommand(&newJob, &jobList, inBg, pipefds);
Eric Andersenec10b9d2000-07-14 01:13:11 +00001073 } else {
Eric Andersena1d187a2000-07-17 19:14:41 +00001074 free(command);
1075 command = (char *) calloc(BUFSIZ, sizeof(char));
Eric Andersen6efc48c2000-07-18 08:16:39 +00001076 nextCommand = NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001077 }
1078 } else {
1079 /* a job is running in the foreground; wait for it */
1080 i = 0;
1081 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +00001082 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001083
Erik Andersend75af992000-03-16 08:09:09 +00001084 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001085
Erik Andersend75af992000-03-16 08:09:09 +00001086 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001087 /* the child exited */
1088 jobList.fg->runningProgs--;
1089 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001090
Erik Andersen161220c2000-03-16 08:12:48 +00001091 if (!jobList.fg->runningProgs) {
1092 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001093
Erik Andersen161220c2000-03-16 08:12:48 +00001094 removeJob(&jobList, jobList.fg);
1095 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001096 }
Erik Andersend75af992000-03-16 08:09:09 +00001097 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001098 /* the child was stopped */
1099 jobList.fg->stoppedProgs++;
1100 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001101
Erik Andersen161220c2000-03-16 08:12:48 +00001102 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1103 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1104 "Stopped", jobList.fg->text);
1105 jobList.fg = NULL;
1106 }
Erik Andersend75af992000-03-16 08:09:09 +00001107 }
1108
1109 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001110 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001111 /* suppress messages when run from /linuxrc mag@sysgo.de */
1112 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1113 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001114 }
1115 }
1116 }
Erik Andersen161220c2000-03-16 08:12:48 +00001117 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001118
Eric Andersen1c314ad2000-06-28 16:56:25 +00001119 /* return controlling TTY back to parent process group before exiting */
1120 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001121 perror("tcsetpgrp");
1122
1123 /* return exit status if called with "-c" */
1124 if (input == NULL && WIFEXITED(status))
1125 return WEXITSTATUS(status);
1126
Erik Andersen161220c2000-03-16 08:12:48 +00001127 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001128}
1129
1130
Eric Andersen6efc48c2000-07-18 08:16:39 +00001131
Erik Andersend75af992000-03-16 08:09:09 +00001132int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +00001133{
Erik Andersen161220c2000-03-16 08:12:48 +00001134 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +00001135
Eric Andersen6efc48c2000-07-18 08:16:39 +00001136 /* initialize the cwd -- this is never freed...*/
1137 cwd=(char*)xmalloc(sizeof(char)*MAX_LINE+1);
1138 getcwd(cwd, sizeof(char)*MAX_LINE);
Erik Andersen3522eb12000-03-12 23:49:18 +00001139
Erik Andersenf0657d32000-04-12 17:49:52 +00001140#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +00001141 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +00001142 signal(SIGWINCH, win_changed);
1143 win_changed(0);
1144#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001145
Erik Andersen161220c2000-03-16 08:12:48 +00001146 //if (argv[0] && argv[0][0] == '-') {
Eric Andersen34e19412000-07-10 18:47:24 +00001147 // builtin_source("/etc/profile");
Erik Andersen161220c2000-03-16 08:12:48 +00001148 //}
Erik Andersen3522eb12000-03-12 23:49:18 +00001149
Eric Andersen6efc48c2000-07-18 08:16:39 +00001150
Erik Andersen161220c2000-03-16 08:12:48 +00001151 if (argc < 2) {
Eric Andersen6efc48c2000-07-18 08:16:39 +00001152 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
Erik Andersenf0657d32000-04-12 17:49:52 +00001153 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001154 } else {
Eric Andersen1c314ad2000-06-28 16:56:25 +00001155 if (argv[1][0]=='-' && argv[1][1]=='c') {
1156 int i;
1157 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1158 if (local_pending_command == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001159 fatalError("out of memory\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001160 }
1161 for(i=2; i<argc; i++)
1162 {
1163 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
Eric Andersenb54833c2000-07-03 23:56:26 +00001164 local_pending_command = realloc(local_pending_command,
1165 strlen(local_pending_command) + strlen(argv[i]));
1166 if (local_pending_command==NULL)
Matt Kraaibe84cd42000-07-12 17:02:35 +00001167 fatalError("commands for -c option too long\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001168 }
1169 strcat(local_pending_command, argv[i]);
Eric Andersenb54833c2000-07-03 23:56:26 +00001170 if ( (i + 1) < argc)
Eric Andersen1c314ad2000-06-28 16:56:25 +00001171 strcat(local_pending_command, " ");
1172 }
1173 input = NULL;
1174
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001175 }
Eric Andersen1c314ad2000-06-28 16:56:25 +00001176 else if (argv[1][0]=='-') {
1177 usage(shell_usage);
1178 }
1179 else {
1180 input = fopen(argv[1], "r");
1181 if (!input) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001182 fatalError("Couldn't open file '%s': %s\n", argv[1],
Eric Andersen1c314ad2000-06-28 16:56:25 +00001183 strerror(errno));
1184 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001185 }
Erik Andersen161220c2000-03-16 08:12:48 +00001186 }
Erik Andersend75af992000-03-16 08:09:09 +00001187
Erik Andersen161220c2000-03-16 08:12:48 +00001188 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001189}