blob: 048394ab67d9fdecd517b1771a785d91633e7cd4 [file] [log] [blame]
Erik Andersen3522eb12000-03-12 23:49:18 +00001/* vi: set sw=4 ts=4: */
2/*
Erik Andersen6acaa402000-03-26 14:03:20 +00003 * lash -- the BusyBox Lame-Ass SHell
Erik Andersen3522eb12000-03-12 23:49:18 +00004 *
5 * Copyright (C) 2000 by Lineo, inc.
6 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
7 *
8 * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
9 * under the following liberal license: "We have placed this source code in the
10 * public domain. Use it in any project, free or commercial."
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 *
26 */
27
28#include "internal.h"
29#include <stdio.h>
30#include <stdlib.h>
31#include <ctype.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <glob.h>
35#include <signal.h>
36#include <string.h>
37#include <sys/ioctl.h>
38#include <sys/wait.h>
39#include <unistd.h>
Erik Andersenf0657d32000-04-12 17:49:52 +000040#ifdef BB_FEATURE_SH_COMMAND_EDITING
41#include "cmdedit.h"
42#endif
Erik Andersen3522eb12000-03-12 23:49:18 +000043
Eric Andersenb54833c2000-07-03 23:56:26 +000044#define MAX_READ 128 /* size of input buffer for `read' builtin */
Erik Andersen3522eb12000-03-12 23:49:18 +000045#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
46
Erik Andersend75af992000-03-16 08:09:09 +000047
48enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000049 REDIRECT_APPEND
50};
Erik Andersen3522eb12000-03-12 23:49:18 +000051
52struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000053 struct job *head; /* head of list of running jobs */
54 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000055};
56
57struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000058 enum redirectionType type; /* type of redirection */
59 int fd; /* file descriptor being redirected */
60 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000061};
62
63struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000064 pid_t pid; /* 0 if exited */
65 char **argv; /* program name and arguments */
66 int numRedirections; /* elements in redirection array */
67 struct redirectionSpecifier *redirections; /* I/O redirections */
68 glob_t globResult; /* result of parameter globbing */
69 int freeGlob; /* should we globfree(&globResult)? */
70 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000071};
72
73struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000074 int jobId; /* job number */
75 int numProgs; /* total number of programs in job */
76 int runningProgs; /* number of programs running */
77 char *text; /* name of job */
78 char *cmdBuf; /* buffer various argv's point into */
79 pid_t pgrp; /* process group ID for the job */
80 struct childProgram *progs; /* array of programs in job */
81 struct job *next; /* to track background commands */
82 int stoppedProgs; /* number of programs alive, but stopped */
Erik Andersen3522eb12000-03-12 23:49:18 +000083};
84
85struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +000086 char *cmd; /* name */
87 char *descr; /* description */
88 char *usage; /* usage */
89 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +000090};
91
Eric Andersen34e19412000-07-10 18:47:24 +000092/* function prototypes for builtins */
93static int builtin_cd(struct job *cmd, struct jobSet *junk);
94static int builtin_env(struct job *dummy, struct jobSet *junk);
95static int builtin_exit(struct job *cmd, struct jobSet *junk);
96static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList);
97static int builtin_help(struct job *cmd, struct jobSet *junk);
98static int builtin_jobs(struct job *dummy, struct jobSet *jobList);
99static int builtin_pwd(struct job *dummy, struct jobSet *junk);
100static int builtin_export(struct job *cmd, struct jobSet *junk);
101static int builtin_source(struct job *cmd, struct jobSet *jobList);
102static int builtin_unset(struct job *cmd, struct jobSet *junk);
103static int builtin_read(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000104
Eric Andersen34e19412000-07-10 18:47:24 +0000105
106/* function prototypes for shell stuff */
Erik Andersend75af992000-03-16 08:09:09 +0000107static void checkJobs(struct jobSet *jobList);
108static int getCommand(FILE * source, char *command);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000109static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg);
Erik Andersend75af992000-03-16 08:09:09 +0000110static int setupRedirections(struct childProgram *prog);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000111static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg);
Erik Andersen3522eb12000-03-12 23:49:18 +0000112static int busy_loop(FILE * input);
113
Erik Andersend75af992000-03-16 08:09:09 +0000114
Mark Whitley37653aa2000-07-12 23:36:17 +0000115/* Table of built-in functions (these are non-forking builtins, meaning they
116 * can change global variables in the parent shell process but they will not
117 * work with pipes and redirects; 'unset foo | whatever' will not work) */
Erik Andersen3522eb12000-03-12 23:49:18 +0000118static struct builtInCommand bltins[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000119 {"bg", "Resume a job in the background", "bg [%%job]", builtin_fg_bg},
120 {"cd", "Change working directory", "cd [dir]", builtin_cd},
121 {"exit", "Exit from shell()", "exit", builtin_exit},
122 {"fg", "Bring job into the foreground", "fg [%%job]", builtin_fg_bg},
123 {"jobs", "Lists the active jobs", "jobs", builtin_jobs},
124 {"export", "Set environment variable", "export [VAR=value]", builtin_export},
125 {"unset", "Unset environment variable", "unset VAR", builtin_unset},
126 {"read", "Input environment variable", "read [VAR]", builtin_read},
Erik Andersen330fd2b2000-05-19 05:35:19 +0000127 {NULL, NULL, NULL, NULL}
128};
129
Mark Whitley37653aa2000-07-12 23:36:17 +0000130/* Table of forking built-in functions (things that fork cannot change global
131 * variables in the parent process, such as the current working directory) */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000132static struct builtInCommand bltins_forking[] = {
Eric Andersen34e19412000-07-10 18:47:24 +0000133 {"env", "Print all environment variables", "env", builtin_env},
134 {"pwd", "Print current directory", "pwd", builtin_pwd},
135 {".", "Source-in and run commands in a file", ". filename", builtin_source},
136 {"help", "List shell built-in commands", "help", builtin_help},
Erik Andersen161220c2000-03-16 08:12:48 +0000137 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000138};
139
140static const char shell_usage[] =
Eric Andersen1c314ad2000-06-28 16:56:25 +0000141 "sh [FILE]...\n"
142 " or: sh -c command [args]...\n"
Erik Andersen7ab9c7e2000-05-12 19:41:47 +0000143#ifndef BB_FEATURE_TRIVIAL_HELP
144 "\nlash: The BusyBox command interpreter (shell).\n\n"
145#endif
146 ;
Erik Andersen3522eb12000-03-12 23:49:18 +0000147
Erik Andersen3522eb12000-03-12 23:49:18 +0000148static char *prompt = "# ";
Eric Andersenb54833c2000-07-03 23:56:26 +0000149static char *cwd = NULL;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000150static char *local_pending_command = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000151
Erik Andersenf0657d32000-04-12 17:49:52 +0000152#ifdef BB_FEATURE_SH_COMMAND_EDITING
153void win_changed(int sig)
154{
155 struct winsize win = { 0, 0 };
156 ioctl(0, TIOCGWINSZ, &win);
157 if (win.ws_col > 0) {
158 cmdedit_setwidth( win.ws_col - 1);
159 }
160}
161#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000162
Erik Andersen3522eb12000-03-12 23:49:18 +0000163
Erik Andersend75af992000-03-16 08:09:09 +0000164/* built-in 'cd <path>' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000165static int builtin_cd(struct job *cmd, struct jobSet *junk)
Erik Andersend75af992000-03-16 08:09:09 +0000166{
Erik Andersen161220c2000-03-16 08:12:48 +0000167 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000168
Erik Andersen161220c2000-03-16 08:12:48 +0000169 if (!cmd->progs[0].argv[1] == 1)
170 newdir = getenv("HOME");
171 else
172 newdir = cmd->progs[0].argv[1];
173 if (chdir(newdir)) {
174 printf("cd: %s: %s\n", newdir, strerror(errno));
175 return FALSE;
176 }
177 getcwd(cwd, sizeof(cwd));
178
179 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000180}
181
182/* built-in 'env' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000183static int builtin_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000184{
Erik Andersen161220c2000-03-16 08:12:48 +0000185 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000186
Erik Andersen161220c2000-03-16 08:12:48 +0000187 for (e = environ; *e; e++) {
188 fprintf(stdout, "%s\n", *e);
189 }
190 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000191}
192
193/* built-in 'exit' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000194static int builtin_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000195{
Erik Andersen161220c2000-03-16 08:12:48 +0000196 if (!cmd->progs[0].argv[1] == 1)
197 exit TRUE;
198
Eric Andersenb6106152000-06-19 17:25:40 +0000199 return(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000200}
201
202/* built-in 'fg' and 'bg' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000203static int builtin_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000204{
Erik Andersen161220c2000-03-16 08:12:48 +0000205 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000206 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000207
Erik Andersen161220c2000-03-16 08:12:48 +0000208 if (!jobList->head) {
209 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
Matt Kraaid537a952000-07-14 01:51:25 +0000210 errorMsg("%s: exactly one argument is expected\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000211 cmd->progs[0].argv[0]);
212 return FALSE;
213 }
214 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
Matt Kraaid537a952000-07-14 01:51:25 +0000215 errorMsg("%s: bad argument '%s'\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000216 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
217 return FALSE;
218 for (job = jobList->head; job; job = job->next) {
219 if (job->jobId == jobNum) {
220 break;
221 }
222 }
223 }
224 } else {
225 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000226 }
Erik Andersen161220c2000-03-16 08:12:48 +0000227
228 if (!job) {
Matt Kraaid537a952000-07-14 01:51:25 +0000229 errorMsg("%s: unknown job %d\n",
Erik Andersen161220c2000-03-16 08:12:48 +0000230 cmd->progs[0].argv[0], jobNum);
231 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000232 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000233
Erik Andersen161220c2000-03-16 08:12:48 +0000234 if (*cmd->progs[0].argv[0] == 'f') {
235 /* Make this job the foreground job */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000236 /* suppress messages when run from /linuxrc mag@sysgo.de */
237 if (tcsetpgrp(0, job->pgrp) && errno != ENOTTY)
238 perror("tcsetpgrp");
Erik Andersen161220c2000-03-16 08:12:48 +0000239 jobList->fg = job;
240 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000241
Erik Andersen161220c2000-03-16 08:12:48 +0000242 /* Restart the processes in the job */
243 for (i = 0; i < job->numProgs; i++)
244 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000245
Erik Andersen161220c2000-03-16 08:12:48 +0000246 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000247
Erik Andersen161220c2000-03-16 08:12:48 +0000248 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000249
Erik Andersen161220c2000-03-16 08:12:48 +0000250 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000251}
252
253/* built-in 'help' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000254static int builtin_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000255{
Erik Andersen161220c2000-03-16 08:12:48 +0000256 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000257
Erik Andersen161220c2000-03-16 08:12:48 +0000258 fprintf(stdout, "\nBuilt-in commands:\n");
259 fprintf(stdout, "-------------------\n");
260 for (x = bltins; x->cmd; x++) {
261 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
262 }
Erik Andersen330fd2b2000-05-19 05:35:19 +0000263 for (x = bltins_forking; x->cmd; x++) {
264 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
265 }
Erik Andersen161220c2000-03-16 08:12:48 +0000266 fprintf(stdout, "\n\n");
267 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000268}
269
270/* built-in 'jobs' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000271static int builtin_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000272{
Erik Andersen161220c2000-03-16 08:12:48 +0000273 struct job *job;
274 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000275
Erik Andersen161220c2000-03-16 08:12:48 +0000276 for (job = jobList->head; job; job = job->next) {
277 if (job->runningProgs == job->stoppedProgs)
278 statusString = "Stopped";
279 else
280 statusString = "Running";
281
282 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
283 }
284 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000285}
286
287
288/* built-in 'pwd' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000289static int builtin_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000290{
Erik Andersen161220c2000-03-16 08:12:48 +0000291 getcwd(cwd, sizeof(cwd));
292 fprintf(stdout, "%s\n", cwd);
293 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000294}
295
Erik Andersen6273f652000-03-17 01:12:41 +0000296/* built-in 'export VAR=value' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000297static int builtin_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000298{
Erik Andersen161220c2000-03-16 08:12:48 +0000299 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000300
Erik Andersen161220c2000-03-16 08:12:48 +0000301 if (!cmd->progs[0].argv[1] == 1) {
Eric Andersen34e19412000-07-10 18:47:24 +0000302 return (builtin_env(cmd, junk));
Erik Andersen161220c2000-03-16 08:12:48 +0000303 }
304 res = putenv(cmd->progs[0].argv[1]);
305 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000306 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000307 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000308}
309
Eric Andersenb54833c2000-07-03 23:56:26 +0000310/* built-in 'read VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000311static int builtin_read(struct job *cmd, struct jobSet *junk)
Eric Andersenb54833c2000-07-03 23:56:26 +0000312{
313 int res = 0, len, newlen;
314 char *s;
315 char string[MAX_READ];
316
317 if (cmd->progs[0].argv[1]) {
318 /* argument (VAR) given: put "VAR=" into buffer */
319 strcpy(string, cmd->progs[0].argv[1]);
320 len = strlen(string);
321 string[len++] = '=';
322 string[len] = '\0';
323 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
324 newlen = strlen(string);
325 if(newlen > len)
326 string[--newlen] = '\0'; /* chomp trailing newline */
327 /*
328 ** string should now contain "VAR=<value>"
329 ** copy it (putenv() won't do that, so we must make sure
330 ** the string resides in a static buffer!)
331 */
332 res = -1;
333 if((s = strdup(string)))
334 res = putenv(s);
335 if (res)
336 fprintf(stdout, "read: %s\n", strerror(errno));
337 }
338 else
339 fgets(string, sizeof(string), stdin);
340
341 return (res);
342}
343
Erik Andersen3522eb12000-03-12 23:49:18 +0000344/* Built-in '.' handler (read-in and execute commands from file) */
Eric Andersen34e19412000-07-10 18:47:24 +0000345static int builtin_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000346{
Erik Andersen161220c2000-03-16 08:12:48 +0000347 FILE *input;
348 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000349
Erik Andersen161220c2000-03-16 08:12:48 +0000350 if (!cmd->progs[0].argv[1] == 1)
351 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000352
Erik Andersen161220c2000-03-16 08:12:48 +0000353 input = fopen(cmd->progs[0].argv[1], "r");
354 if (!input) {
355 fprintf(stdout, "Couldn't open file '%s'\n",
356 cmd->progs[0].argv[1]);
357 return FALSE;
358 }
Erik Andersend75af992000-03-16 08:09:09 +0000359
Erik Andersen161220c2000-03-16 08:12:48 +0000360 /* Now run the file */
361 status = busy_loop(input);
362 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000363}
364
365/* built-in 'unset VAR' handler */
Eric Andersen34e19412000-07-10 18:47:24 +0000366static int builtin_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000367{
Erik Andersen161220c2000-03-16 08:12:48 +0000368 if (!cmd->progs[0].argv[1] == 1) {
369 fprintf(stdout, "unset: parameter required.\n");
370 return FALSE;
371 }
372 unsetenv(cmd->progs[0].argv[1]);
373 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000374}
375
376/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000377static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000378{
Erik Andersen161220c2000-03-16 08:12:48 +0000379 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000380
Erik Andersen161220c2000-03-16 08:12:48 +0000381 for (i = 0; i < cmd->numProgs; i++) {
382 free(cmd->progs[i].argv);
383 if (cmd->progs[i].redirections)
384 free(cmd->progs[i].redirections);
385 if (cmd->progs[i].freeGlob)
386 globfree(&cmd->progs[i].globResult);
387 }
388 free(cmd->progs);
389 if (cmd->text)
390 free(cmd->text);
391 free(cmd->cmdBuf);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000392 memset(cmd, 0, sizeof(struct job));
Erik Andersen3522eb12000-03-12 23:49:18 +0000393}
394
395/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000396static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000397{
Erik Andersen161220c2000-03-16 08:12:48 +0000398 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000399
Erik Andersen161220c2000-03-16 08:12:48 +0000400 freeJob(job);
401 if (job == jobList->head) {
402 jobList->head = job->next;
403 } else {
404 prevJob = jobList->head;
405 while (prevJob->next != job)
406 prevJob = prevJob->next;
407 prevJob->next = job->next;
408 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000409
Erik Andersen161220c2000-03-16 08:12:48 +0000410 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000411}
412
413/* Checks to see if any background processes have exited -- if they
414 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000415static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000416{
Erik Andersen161220c2000-03-16 08:12:48 +0000417 struct job *job;
418 pid_t childpid;
419 int status;
420 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000421
Erik Andersen161220c2000-03-16 08:12:48 +0000422 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
423 for (job = jobList->head; job; job = job->next) {
424 progNum = 0;
425 while (progNum < job->numProgs &&
426 job->progs[progNum].pid != childpid) progNum++;
427 if (progNum < job->numProgs)
428 break;
429 }
430
431 if (WIFEXITED(status) || WIFSIGNALED(status)) {
432 /* child exited */
433 job->runningProgs--;
434 job->progs[progNum].pid = 0;
435
436 if (!job->runningProgs) {
437 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
438 removeJob(jobList, job);
439 }
440 } else {
441 /* child stopped */
442 job->stoppedProgs++;
443 job->progs[progNum].isStopped = 1;
444
445 if (job->stoppedProgs == job->numProgs) {
446 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
447 job->text);
448 }
449 }
Erik Andersend75af992000-03-16 08:09:09 +0000450 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000451
Erik Andersen161220c2000-03-16 08:12:48 +0000452 if (childpid == -1 && errno != ECHILD)
453 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000454}
455
Erik Andersend75af992000-03-16 08:09:09 +0000456static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000457{
Eric Andersen1c314ad2000-06-28 16:56:25 +0000458 if (source == NULL) {
459 if (local_pending_command) {
460 /* a command specified (-c option): return it & mark it done */
461 strcpy(command, local_pending_command);
462 free(local_pending_command);
463 local_pending_command = NULL;
464 return 0;
465 }
466 return 1;
467 }
468
Erik Andersen161220c2000-03-16 08:12:48 +0000469 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000470#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000471 int len;
472 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000473 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000474 fflush(stdout);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000475 promptStr=(char*)xmalloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000476 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000477 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000478 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000479 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000480#else
481 fprintf(stdout, "%s %s", cwd, prompt);
482 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000483#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000484 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000485
Erik Andersen161220c2000-03-16 08:12:48 +0000486 if (!fgets(command, BUFSIZ - 2, source)) {
487 if (source == stdin)
488 printf("\n");
489 return 1;
490 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000491
Erik Andersen161220c2000-03-16 08:12:48 +0000492 /* remove trailing newline */
493 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000494
Erik Andersen161220c2000-03-16 08:12:48 +0000495 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000496}
497
Erik Andersend75af992000-03-16 08:09:09 +0000498static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000499 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000500{
Erik Andersen161220c2000-03-16 08:12:48 +0000501 int argc = *argcPtr;
502 int argcAlloced = *argcAllocedPtr;
503 int rc;
504 int flags;
505 int i;
Eric Andersenb54833c2000-07-03 23:56:26 +0000506 char *src, *dst, *var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000507
Erik Andersen161220c2000-03-16 08:12:48 +0000508 if (argc > 1) { /* cmd->globResult is already initialized */
509 flags = GLOB_APPEND;
510 i = prog->globResult.gl_pathc;
511 } else {
512 prog->freeGlob = 1;
513 flags = 0;
514 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000515 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000516 /* do shell variable substitution */
517 if(*prog->argv[argc - 1] == '$' && (var = getenv(prog->argv[argc - 1] + 1)))
518 prog->argv[argc - 1] = var;
Erik Andersen3522eb12000-03-12 23:49:18 +0000519
Erik Andersen161220c2000-03-16 08:12:48 +0000520 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
521 if (rc == GLOB_NOSPACE) {
Matt Kraaid537a952000-07-14 01:51:25 +0000522 errorMsg("out of space during glob operation\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000523 return;
524 } else if (rc == GLOB_NOMATCH ||
525 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
526 !strcmp(prog->argv[argc - 1],
527 prog->globResult.gl_pathv[i]))) {
528 /* we need to remove whatever \ quoting is still present */
529 src = dst = prog->argv[argc - 1];
530 while (*src) {
531 if (*src != '\\')
532 *dst++ = *src;
533 src++;
534 }
535 *dst = '\0';
536 } else if (!rc) {
537 argcAlloced += (prog->globResult.gl_pathc - i);
538 prog->argv =
539 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
540 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
541 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
542 argc += (prog->globResult.gl_pathc - i - 1);
543 }
544
545 *argcAllocedPtr = argcAlloced;
546 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000547}
548
549/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
550 line). If a valid command is found, commandPtr is set to point to
551 the beginning of the next command (if the original command had more
552 then one job associated with it) or NULL if no more commands are
553 present. */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000554static int parseCommand(char **commandPtr, struct job *job, struct jobSet *jobList, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000555{
Erik Andersen161220c2000-03-16 08:12:48 +0000556 char *command;
557 char *returnCommand = NULL;
558 char *src, *buf, *chptr;
559 int argc = 0;
560 int done = 0;
561 int argvAlloced;
562 int i;
563 char quote = '\0';
564 int count;
565 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000566
Erik Andersen161220c2000-03-16 08:12:48 +0000567 /* skip leading white space */
568 while (**commandPtr && isspace(**commandPtr))
569 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000570
Erik Andersen161220c2000-03-16 08:12:48 +0000571 /* this handles empty lines or leading '#' characters */
572 if (!**commandPtr || (**commandPtr == '#')) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000573 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000574 return 0;
575 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000576
Erik Andersen161220c2000-03-16 08:12:48 +0000577 *isBg = 0;
578 job->numProgs = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000579 job->progs = xmalloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000580
Erik Andersen161220c2000-03-16 08:12:48 +0000581 /* We set the argv elements to point inside of this string. The
Eric Andersenb54833c2000-07-03 23:56:26 +0000582 memory is freed by freeJob(). Allocate twice the original
583 length in case we need to quote every single character.
Erik Andersen3522eb12000-03-12 23:49:18 +0000584
Erik Andersen161220c2000-03-16 08:12:48 +0000585 Getting clean memory relieves us of the task of NULL
586 terminating things and makes the rest of this look a bit
587 cleaner (though it is, admittedly, a tad less efficient) */
Eric Andersenb54833c2000-07-03 23:56:26 +0000588 job->cmdBuf = command = calloc(1, 2*strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000589 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000590
Erik Andersen161220c2000-03-16 08:12:48 +0000591 prog = job->progs;
592 prog->numRedirections = 0;
593 prog->redirections = NULL;
594 prog->freeGlob = 0;
595 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000596
Erik Andersen161220c2000-03-16 08:12:48 +0000597 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000598 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000599 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000600
Erik Andersen161220c2000-03-16 08:12:48 +0000601 buf = command;
602 src = *commandPtr;
603 while (*src && !done) {
604 if (quote == *src) {
605 quote = '\0';
606 } else if (quote) {
607 if (*src == '\\') {
608 src++;
609 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000610 errorMsg("character expected after \\\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000611 freeJob(job);
612 return 1;
613 }
614
615 /* in shell, "\'" should yield \' */
616 if (*src != quote)
617 *buf++ = '\\';
618 } else if (*src == '*' || *src == '?' || *src == '[' ||
619 *src == ']') *buf++ = '\\';
620 *buf++ = *src;
621 } else if (isspace(*src)) {
622 if (*prog->argv[argc]) {
623 buf++, argc++;
624 /* +1 here leaves room for the NULL which ends argv */
625 if ((argc + 1) == argvAlloced) {
626 argvAlloced += 5;
627 prog->argv = realloc(prog->argv,
628 sizeof(*prog->argv) *
629 argvAlloced);
630 }
Erik Andersen161220c2000-03-16 08:12:48 +0000631 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000632 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000633 }
634 } else
635 switch (*src) {
636 case '"':
637 case '\'':
638 quote = *src;
639 break;
640
641 case '#': /* comment */
642 done = 1;
643 break;
644
645 case '>': /* redirections */
646 case '<':
647 i = prog->numRedirections++;
648 prog->redirections = realloc(prog->redirections,
649 sizeof(*prog->redirections) *
650 (i + 1));
651
652 prog->redirections[i].fd = -1;
653 if (buf != prog->argv[argc]) {
654 /* the stuff before this character may be the file number
655 being redirected */
656 prog->redirections[i].fd =
657 strtol(prog->argv[argc], &chptr, 10);
658
659 if (*chptr && *prog->argv[argc]) {
660 buf++, argc++;
661 globLastArgument(prog, &argc, &argvAlloced);
Eric Andersenb54833c2000-07-03 23:56:26 +0000662 prog->argv[argc] = buf;
Erik Andersen161220c2000-03-16 08:12:48 +0000663 }
664 }
665
666 if (prog->redirections[i].fd == -1) {
667 if (*src == '>')
668 prog->redirections[i].fd = 1;
669 else
670 prog->redirections[i].fd = 0;
671 }
672
673 if (*src++ == '>') {
674 if (*src == '>')
675 prog->redirections[i].type =
676 REDIRECT_APPEND, src++;
677 else
678 prog->redirections[i].type = REDIRECT_OVERWRITE;
679 } else {
680 prog->redirections[i].type = REDIRECT_INPUT;
681 }
682
683 /* This isn't POSIX sh compliant. Oh well. */
684 chptr = src;
685 while (isspace(*chptr))
686 chptr++;
687
688 if (!*chptr) {
Matt Kraaid537a952000-07-14 01:51:25 +0000689 errorMsg("file name expected after %c\n", *src);
Erik Andersen161220c2000-03-16 08:12:48 +0000690 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000691 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000692 return 1;
693 }
694
695 prog->redirections[i].filename = buf;
696 while (*chptr && !isspace(*chptr))
697 *buf++ = *chptr++;
698
699 src = chptr - 1; /* we src++ later */
700 prog->argv[argc] = ++buf;
701 break;
702
703 case '|': /* pipe */
704 /* finish this command */
705 if (*prog->argv[argc])
706 argc++;
707 if (!argc) {
Matt Kraaid537a952000-07-14 01:51:25 +0000708 errorMsg("empty command in pipe1\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000709 freeJob(job);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000710 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000711 return 1;
712 }
713 prog->argv[argc] = NULL;
714
715 /* and start the next */
716 job->numProgs++;
717 job->progs = realloc(job->progs,
718 sizeof(*job->progs) * job->numProgs);
719 prog = job->progs + (job->numProgs - 1);
720 prog->numRedirections = 0;
721 prog->redirections = NULL;
722 prog->freeGlob = 0;
723 argc = 0;
724
725 argvAlloced = 5;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000726 prog->argv = xmalloc(sizeof(*prog->argv) * argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000727 prog->argv[0] = ++buf;
728
729 src++;
730 while (*src && isspace(*src))
731 src++;
732
733 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000734 errorMsg("empty command in pipe2\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000735 freeJob(job);
736 job->numProgs=0;
Erik Andersen161220c2000-03-16 08:12:48 +0000737 return 1;
738 }
739 src--; /* we'll ++ it at the end of the loop */
740
741 break;
742
743 case '&': /* background */
744 *isBg = 1;
745 case ';': /* multiple commands */
746 done = 1;
747 returnCommand = *commandPtr + (src - *commandPtr) + 1;
748 break;
749
750 case '\\':
751 src++;
752 if (!*src) {
Matt Kraaid537a952000-07-14 01:51:25 +0000753 errorMsg("character expected after \\\n");
Eric Andersenec10b9d2000-07-14 01:13:11 +0000754 freeJob(job);
Erik Andersen161220c2000-03-16 08:12:48 +0000755 return 1;
756 }
757 if (*src == '*' || *src == '[' || *src == ']'
758 || *src == '?') *buf++ = '\\';
759 /* fallthrough */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000760 case '`':
761 /* Exec a backtick-ed command */
762 {
763 char* newcmd=NULL;
764 char* ptr=NULL;
765 struct job newJob;
766
767 ptr=strchr(++src, '`');
768 if (ptr==NULL) {
769 fprintf(stderr, "Unmatched '`' in command\n");
770 freeJob(job);
771 return 1;
772 }
773
774 newcmd = xmalloc(1+ptr-src);
775 snprintf(newcmd, 1+ptr-src, src);
776
777 if (!parseCommand(&newcmd, &newJob, jobList, isBg) &&
778 newJob.numProgs) {
779 runCommand(&newJob, jobList, *isBg);
780 }
781
782 /* Clip out the the backticked command from the string */
783 memmove(--src, ptr, strlen(ptr)+1);
784 free(newcmd);
785 }
786 break;
Erik Andersen161220c2000-03-16 08:12:48 +0000787 default:
788 *buf++ = *src;
789 }
790
Erik Andersend75af992000-03-16 08:09:09 +0000791 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000792 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000793
Erik Andersen161220c2000-03-16 08:12:48 +0000794 if (*prog->argv[argc]) {
795 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000796 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000797 }
798 if (!argc) {
799 freeJob(job);
800 return 0;
801 }
802 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000803
Erik Andersen161220c2000-03-16 08:12:48 +0000804 if (!returnCommand) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000805 job->text = xmalloc(strlen(*commandPtr) + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000806 strcpy(job->text, *commandPtr);
807 } else {
808 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000809 count = returnCommand - *commandPtr;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000810 job->text = xmalloc(count + 1);
Erik Andersen161220c2000-03-16 08:12:48 +0000811 strncpy(job->text, *commandPtr, count);
812 job->text[count] = '\0';
813 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000814
Erik Andersen161220c2000-03-16 08:12:48 +0000815 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000816
Erik Andersend75af992000-03-16 08:09:09 +0000817 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000818}
819
Erik Andersenbcd61772000-05-13 06:33:19 +0000820
Eric Andersenec10b9d2000-07-14 01:13:11 +0000821static int runCommand(struct job *newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000822{
Erik Andersen161220c2000-03-16 08:12:48 +0000823 struct job *job;
824 int i;
825 int nextin, nextout;
826 int pipefds[2]; /* pipefd[0] is for reading */
827 struct builtInCommand *x;
Eric Andersenb54833c2000-07-03 23:56:26 +0000828#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Erik Andersenbcd61772000-05-13 06:33:19 +0000829 const struct BB_applet *a = applets;
830#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000831
Erik Andersen3522eb12000-03-12 23:49:18 +0000832
Erik Andersen161220c2000-03-16 08:12:48 +0000833 nextin = 0, nextout = 1;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000834 for (i = 0; i < newJob->numProgs; i++) {
835 if ((i + 1) < newJob->numProgs) {
Erik Andersen161220c2000-03-16 08:12:48 +0000836 pipe(pipefds);
837 nextout = pipefds[1];
838 } else {
839 nextout = 1;
840 }
841
Eric Andersen34e19412000-07-10 18:47:24 +0000842 /* Check if the command matches any non-forking builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000843 for (x = bltins; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000844 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
845 return (x->function(newJob, jobList));
Erik Andersen330fd2b2000-05-19 05:35:19 +0000846 }
847 }
848
Eric Andersenec10b9d2000-07-14 01:13:11 +0000849 if (!(newJob->progs[i].pid = fork())) {
Erik Andersen161220c2000-03-16 08:12:48 +0000850 signal(SIGTTOU, SIG_DFL);
851
852 if (nextin != 0) {
853 dup2(nextin, 0);
854 close(nextin);
855 }
856
857 if (nextout != 1) {
858 dup2(nextout, 1);
859 close(nextout);
860 }
861
862 /* explicit redirections override pipes */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000863 setupRedirections(newJob->progs + i);
Erik Andersen161220c2000-03-16 08:12:48 +0000864
Eric Andersen34e19412000-07-10 18:47:24 +0000865 /* Check if the command matches any of the other builtins */
Erik Andersen330fd2b2000-05-19 05:35:19 +0000866 for (x = bltins_forking; x->cmd; x++) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000867 if (!strcmp(newJob->progs[i].argv[0], x->cmd)) {
868 exit (x->function(newJob, jobList));
Erik Andersenbcd61772000-05-13 06:33:19 +0000869 }
870 }
Eric Andersenb54833c2000-07-03 23:56:26 +0000871#ifdef BB_FEATURE_SH_STANDALONE_SHELL
Eric Andersen34e19412000-07-10 18:47:24 +0000872 /* Check if the command matches any busybox internal commands here */
873 /* TODO: Add matching when paths are appended (i.e. 'cat' currently
874 * works, but '/bin/cat' doesn't ) */
Erik Andersenbcd61772000-05-13 06:33:19 +0000875 while (a->name != 0) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000876 if (strcmp(newJob->progs[i].argv[0], a->name) == 0) {
Erik Andersenbcd61772000-05-13 06:33:19 +0000877 int argc;
Eric Andersenec10b9d2000-07-14 01:13:11 +0000878 char** argv=newJob->progs[i].argv;
Erik Andersenc3f5c9c2000-05-13 19:00:07 +0000879 for(argc=0;*argv!=NULL; argv++, argc++);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000880 exit((*(a->main)) (argc, newJob->progs[i].argv));
Erik Andersenbcd61772000-05-13 06:33:19 +0000881 }
882 a++;
883 }
884#endif
885
Eric Andersenec10b9d2000-07-14 01:13:11 +0000886 execvp(newJob->progs[i].argv[0], newJob->progs[i].argv);
887 fatalError("%s: %s\n", newJob->progs[i].argv[0],
Erik Andersen161220c2000-03-16 08:12:48 +0000888 strerror(errno));
889 }
890
891 /* put our child in the process group whose leader is the
892 first process in this pipe */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000893 setpgid(newJob->progs[i].pid, newJob->progs[0].pid);
Erik Andersen161220c2000-03-16 08:12:48 +0000894
895 if (nextin != 0)
896 close(nextin);
897 if (nextout != 1)
898 close(nextout);
899
900 /* If there isn't another process, nextin is garbage
901 but it doesn't matter */
902 nextin = pipefds[0];
903 }
904
Eric Andersenec10b9d2000-07-14 01:13:11 +0000905 newJob->pgrp = newJob->progs[0].pid;
Erik Andersen161220c2000-03-16 08:12:48 +0000906
907 /* find the ID for the job to use */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000908 newJob->jobId = 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000909 for (job = jobList->head; job; job = job->next)
Eric Andersenec10b9d2000-07-14 01:13:11 +0000910 if (job->jobId >= newJob->jobId)
911 newJob->jobId = job->jobId + 1;
Erik Andersen161220c2000-03-16 08:12:48 +0000912
913 /* add the job to the list of running jobs */
914 if (!jobList->head) {
Eric Andersenec10b9d2000-07-14 01:13:11 +0000915 job = jobList->head = xmalloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000916 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000917 for (job = jobList->head; job->next; job = job->next);
Eric Andersenec10b9d2000-07-14 01:13:11 +0000918 job->next = xmalloc(sizeof(*job));
Erik Andersen161220c2000-03-16 08:12:48 +0000919 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000920 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000921
Eric Andersenec10b9d2000-07-14 01:13:11 +0000922 *job = *newJob;
Erik Andersen161220c2000-03-16 08:12:48 +0000923 job->next = NULL;
924 job->runningProgs = job->numProgs;
925 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000926
Erik Andersen161220c2000-03-16 08:12:48 +0000927 if (inBg) {
928 /* we don't wait for background jobs to return -- append it
929 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000930 printf("[%d] %d\n", job->jobId,
Eric Andersenec10b9d2000-07-14 01:13:11 +0000931 newJob->progs[newJob->numProgs - 1].pid);
Erik Andersen161220c2000-03-16 08:12:48 +0000932 } else {
933 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000934
Erik Andersen161220c2000-03-16 08:12:48 +0000935 /* move the new process group into the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +0000936 /* suppress messages when run from /linuxrc mag@sysgo.de */
Eric Andersenec10b9d2000-07-14 01:13:11 +0000937 if (tcsetpgrp(0, newJob->pgrp) && errno != ENOTTY)
Erik Andersen161220c2000-03-16 08:12:48 +0000938 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000939 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000940
Erik Andersen161220c2000-03-16 08:12:48 +0000941 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000942}
943
Erik Andersend75af992000-03-16 08:09:09 +0000944static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000945{
Erik Andersen161220c2000-03-16 08:12:48 +0000946 int i;
947 int openfd;
948 int mode = O_RDONLY;
949 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000950
Erik Andersen161220c2000-03-16 08:12:48 +0000951 for (i = 0; i < prog->numRedirections; i++, redir++) {
952 switch (redir->type) {
953 case REDIRECT_INPUT:
954 mode = O_RDONLY;
955 break;
956 case REDIRECT_OVERWRITE:
957 mode = O_RDWR | O_CREAT | O_TRUNC;
958 break;
959 case REDIRECT_APPEND:
960 mode = O_RDWR | O_CREAT | O_APPEND;
961 break;
962 }
963
964 openfd = open(redir->filename, mode, 0666);
965 if (openfd < 0) {
966 /* this could get lost if stderr has been redirected, but
967 bash and ash both lose it as well (though zsh doesn't!) */
Matt Kraaid537a952000-07-14 01:51:25 +0000968 errorMsg("error opening %s: %s\n", redir->filename,
Erik Andersen161220c2000-03-16 08:12:48 +0000969 strerror(errno));
970 return 1;
971 }
972
973 if (openfd != redir->fd) {
974 dup2(openfd, redir->fd);
975 close(openfd);
976 }
Erik Andersend75af992000-03-16 08:09:09 +0000977 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000978
Erik Andersen161220c2000-03-16 08:12:48 +0000979 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000980}
981
982
Erik Andersend75af992000-03-16 08:09:09 +0000983static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000984{
Erik Andersen161220c2000-03-16 08:12:48 +0000985 char *command;
986 char *nextCommand = NULL;
987 struct jobSet jobList = { NULL, NULL };
988 struct job newJob;
Eric Andersen1c314ad2000-06-28 16:56:25 +0000989 pid_t parent_pgrp;
Erik Andersen161220c2000-03-16 08:12:48 +0000990 int i;
991 int status;
992 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000993
Eric Andersen1c314ad2000-06-28 16:56:25 +0000994 /* save current owner of TTY so we can restore it on exit */
995 parent_pgrp = tcgetpgrp(0);
996
Erik Andersen161220c2000-03-16 08:12:48 +0000997 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000998
Erik Andersen161220c2000-03-16 08:12:48 +0000999 /* don't pay any attention to this signal; it just confuses
1000 things and isn't really meant for shells anyway */
1001 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +00001002
Erik Andersen161220c2000-03-16 08:12:48 +00001003 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +00001004 if (!jobList.fg) {
1005 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +00001006
Erik Andersend75af992000-03-16 08:09:09 +00001007 /* see if any background processes have exited */
1008 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +00001009
Erik Andersend75af992000-03-16 08:09:09 +00001010 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +00001011 if (getCommand(input, command))
1012 break;
1013 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +00001014 }
Erik Andersen3522eb12000-03-12 23:49:18 +00001015
Eric Andersenec10b9d2000-07-14 01:13:11 +00001016 if (!parseCommand(&nextCommand, &newJob, &jobList, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +00001017 newJob.numProgs) {
Eric Andersenec10b9d2000-07-14 01:13:11 +00001018 runCommand(&newJob, &jobList, inBg);
1019 } else {
1020 nextCommand=NULL;
Erik Andersend75af992000-03-16 08:09:09 +00001021 }
1022 } else {
1023 /* a job is running in the foreground; wait for it */
1024 i = 0;
1025 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +00001026 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +00001027
Erik Andersend75af992000-03-16 08:09:09 +00001028 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +00001029
Erik Andersend75af992000-03-16 08:09:09 +00001030 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +00001031 /* the child exited */
1032 jobList.fg->runningProgs--;
1033 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001034
Erik Andersen161220c2000-03-16 08:12:48 +00001035 if (!jobList.fg->runningProgs) {
1036 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +00001037
Erik Andersen161220c2000-03-16 08:12:48 +00001038 removeJob(&jobList, jobList.fg);
1039 jobList.fg = NULL;
Erik Andersen161220c2000-03-16 08:12:48 +00001040 }
Erik Andersend75af992000-03-16 08:09:09 +00001041 } else {
Erik Andersen161220c2000-03-16 08:12:48 +00001042 /* the child was stopped */
1043 jobList.fg->stoppedProgs++;
1044 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +00001045
Erik Andersen161220c2000-03-16 08:12:48 +00001046 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
1047 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
1048 "Stopped", jobList.fg->text);
1049 jobList.fg = NULL;
1050 }
Erik Andersend75af992000-03-16 08:09:09 +00001051 }
1052
1053 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +00001054 /* move the shell to the foreground */
Eric Andersen1c314ad2000-06-28 16:56:25 +00001055 /* suppress messages when run from /linuxrc mag@sysgo.de */
1056 if (tcsetpgrp(0, getpid()) && errno != ENOTTY)
1057 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +00001058 }
1059 }
1060 }
Erik Andersen161220c2000-03-16 08:12:48 +00001061 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +00001062
Eric Andersen1c314ad2000-06-28 16:56:25 +00001063 /* return controlling TTY back to parent process group before exiting */
1064 if (tcsetpgrp(0, parent_pgrp))
Eric Andersenb54833c2000-07-03 23:56:26 +00001065 perror("tcsetpgrp");
1066
1067 /* return exit status if called with "-c" */
1068 if (input == NULL && WIFEXITED(status))
1069 return WEXITSTATUS(status);
1070
Erik Andersen161220c2000-03-16 08:12:48 +00001071 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +00001072}
1073
1074
Erik Andersend75af992000-03-16 08:09:09 +00001075int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +00001076{
Erik Andersen161220c2000-03-16 08:12:48 +00001077 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +00001078
Erik Andersen161220c2000-03-16 08:12:48 +00001079 /* initialize the cwd */
Eric Andersenb54833c2000-07-03 23:56:26 +00001080 cwd = (char *) calloc(BUFSIZ, sizeof(char));
1081 if (cwd == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001082 fatalError("out of memory\n");
Eric Andersenb54833c2000-07-03 23:56:26 +00001083 }
1084 getcwd(cwd, sizeof(char)*BUFSIZ);
Erik Andersen3522eb12000-03-12 23:49:18 +00001085
Erik Andersenf0657d32000-04-12 17:49:52 +00001086#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +00001087 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +00001088 signal(SIGWINCH, win_changed);
1089 win_changed(0);
1090#endif
Erik Andersen3522eb12000-03-12 23:49:18 +00001091
Erik Andersen161220c2000-03-16 08:12:48 +00001092 //if (argv[0] && argv[0][0] == '-') {
Eric Andersen34e19412000-07-10 18:47:24 +00001093 // builtin_source("/etc/profile");
Erik Andersen161220c2000-03-16 08:12:48 +00001094 //}
Erik Andersen3522eb12000-03-12 23:49:18 +00001095
Erik Andersen161220c2000-03-16 08:12:48 +00001096 if (argc < 2) {
Erik Andersenf0657d32000-04-12 17:49:52 +00001097 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
1098 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +00001099 } else {
Eric Andersen1c314ad2000-06-28 16:56:25 +00001100 if (argv[1][0]=='-' && argv[1][1]=='c') {
1101 int i;
1102 local_pending_command = (char *) calloc(BUFSIZ, sizeof(char));
1103 if (local_pending_command == 0) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001104 fatalError("out of memory\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001105 }
1106 for(i=2; i<argc; i++)
1107 {
1108 if (strlen(local_pending_command) + strlen(argv[i]) >= BUFSIZ) {
Eric Andersenb54833c2000-07-03 23:56:26 +00001109 local_pending_command = realloc(local_pending_command,
1110 strlen(local_pending_command) + strlen(argv[i]));
1111 if (local_pending_command==NULL)
Matt Kraaibe84cd42000-07-12 17:02:35 +00001112 fatalError("commands for -c option too long\n");
Eric Andersen1c314ad2000-06-28 16:56:25 +00001113 }
1114 strcat(local_pending_command, argv[i]);
Eric Andersenb54833c2000-07-03 23:56:26 +00001115 if ( (i + 1) < argc)
Eric Andersen1c314ad2000-06-28 16:56:25 +00001116 strcat(local_pending_command, " ");
1117 }
1118 input = NULL;
1119
Erik Andersene5b6c7d2000-04-17 16:16:10 +00001120 }
Eric Andersen1c314ad2000-06-28 16:56:25 +00001121 else if (argv[1][0]=='-') {
1122 usage(shell_usage);
1123 }
1124 else {
1125 input = fopen(argv[1], "r");
1126 if (!input) {
Matt Kraaibe84cd42000-07-12 17:02:35 +00001127 fatalError("Couldn't open file '%s': %s\n", argv[1],
Eric Andersen1c314ad2000-06-28 16:56:25 +00001128 strerror(errno));
1129 }
Erik Andersenf0657d32000-04-12 17:49:52 +00001130 }
Erik Andersen161220c2000-03-16 08:12:48 +00001131 }
Erik Andersend75af992000-03-16 08:09:09 +00001132
Erik Andersen161220c2000-03-16 08:12:48 +00001133 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +00001134}