blob: bd95ba761fabb1b23f5005c6228962874ce71a5f [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
44
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
92/* Some function prototypes */
Erik Andersend75af992000-03-16 08:09:09 +000093static int shell_cd(struct job *cmd, struct jobSet *junk);
94static int shell_env(struct job *dummy, struct jobSet *junk);
95static int shell_exit(struct job *cmd, struct jobSet *junk);
96static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
97static int shell_help(struct job *cmd, struct jobSet *junk);
98static int shell_jobs(struct job *dummy, struct jobSet *jobList);
99static int shell_pwd(struct job *dummy, struct jobSet *junk);
Erik Andersen6273f652000-03-17 01:12:41 +0000100static int shell_export(struct job *cmd, struct jobSet *junk);
Erik Andersend75af992000-03-16 08:09:09 +0000101static int shell_source(struct job *cmd, struct jobSet *jobList);
102static int shell_unset(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000103
Erik Andersend75af992000-03-16 08:09:09 +0000104static void checkJobs(struct jobSet *jobList);
105static int getCommand(FILE * source, char *command);
106static int parseCommand(char **commandPtr, struct job *job, int *isBg);
107static int setupRedirections(struct childProgram *prog);
108static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
Erik Andersen3522eb12000-03-12 23:49:18 +0000109static int busy_loop(FILE * input);
110
Erik Andersend75af992000-03-16 08:09:09 +0000111
Erik Andersen3522eb12000-03-12 23:49:18 +0000112/* Table of built-in functions */
113static struct builtInCommand bltins[] = {
Erik Andersen161220c2000-03-16 08:12:48 +0000114 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
115 {"cd", "Change working directory", "cd [dir]", shell_cd},
116 //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
117 {"env", "Print all environment variables", "env", shell_env},
118 {"exit", "Exit from shell()", "exit", shell_exit},
119 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
120 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
121 {"pwd", "Print current directory", "pwd", shell_pwd},
Erik Andersen6273f652000-03-17 01:12:41 +0000122 {"export", "Set environment variable", "export [VAR=value]", shell_export},
Erik Andersen161220c2000-03-16 08:12:48 +0000123 {"unset", "Unset environment variable", "unset VAR", shell_unset},
124
Erik Andersenc7c634b2000-03-19 05:28:55 +0000125 {".", "Source-in and run commands in a file", ". filename", shell_source},
Erik Andersen161220c2000-03-16 08:12:48 +0000126 {"help", "List shell built-in commands", "help", shell_help},
127 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000128};
129
130static const char shell_usage[] =
Erik Andersen161220c2000-03-16 08:12:48 +0000131
132 "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
Erik Andersen3522eb12000-03-12 23:49:18 +0000133
134
135static char cwd[1024];
136static char *prompt = "# ";
137
Erik Andersenf0657d32000-04-12 17:49:52 +0000138#ifdef BB_FEATURE_SH_COMMAND_EDITING
139void win_changed(int sig)
140{
141 struct winsize win = { 0, 0 };
142 ioctl(0, TIOCGWINSZ, &win);
143 if (win.ws_col > 0) {
144 cmdedit_setwidth( win.ws_col - 1);
145 }
146}
147#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000148
Erik Andersen3522eb12000-03-12 23:49:18 +0000149
Erik Andersend75af992000-03-16 08:09:09 +0000150/* built-in 'cd <path>' handler */
151static int shell_cd(struct job *cmd, struct jobSet *junk)
152{
Erik Andersen161220c2000-03-16 08:12:48 +0000153 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000154
Erik Andersen161220c2000-03-16 08:12:48 +0000155 if (!cmd->progs[0].argv[1] == 1)
156 newdir = getenv("HOME");
157 else
158 newdir = cmd->progs[0].argv[1];
159 if (chdir(newdir)) {
160 printf("cd: %s: %s\n", newdir, strerror(errno));
161 return FALSE;
162 }
163 getcwd(cwd, sizeof(cwd));
164
165 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000166}
167
168/* built-in 'env' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000169static int shell_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000170{
Erik Andersen161220c2000-03-16 08:12:48 +0000171 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000172
Erik Andersen161220c2000-03-16 08:12:48 +0000173 for (e = environ; *e; e++) {
174 fprintf(stdout, "%s\n", *e);
175 }
176 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000177}
178
179/* built-in 'exit' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000180static int shell_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000181{
Erik Andersen161220c2000-03-16 08:12:48 +0000182 if (!cmd->progs[0].argv[1] == 1)
183 exit TRUE;
184
185 else
186 exit(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000187}
188
189/* built-in 'fg' and 'bg' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000190static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000191{
Erik Andersen161220c2000-03-16 08:12:48 +0000192 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000193 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000194
Erik Andersen161220c2000-03-16 08:12:48 +0000195 if (!jobList->head) {
196 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
197 fprintf(stderr, "%s: exactly one argument is expected\n",
198 cmd->progs[0].argv[0]);
199 return FALSE;
200 }
201 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
202 fprintf(stderr, "%s: bad argument '%s'\n",
203 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
204 return FALSE;
205 for (job = jobList->head; job; job = job->next) {
206 if (job->jobId == jobNum) {
207 break;
208 }
209 }
210 }
211 } else {
212 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000213 }
Erik Andersen161220c2000-03-16 08:12:48 +0000214
215 if (!job) {
216 fprintf(stderr, "%s: unknown job %d\n",
217 cmd->progs[0].argv[0], jobNum);
218 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000219 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000220
Erik Andersen161220c2000-03-16 08:12:48 +0000221 if (*cmd->progs[0].argv[0] == 'f') {
222 /* Make this job the foreground job */
223 if (tcsetpgrp(0, job->pgrp))
224 perror("tcsetpgrp");
225 jobList->fg = job;
226 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000227
Erik Andersen161220c2000-03-16 08:12:48 +0000228 /* Restart the processes in the job */
229 for (i = 0; i < job->numProgs; i++)
230 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000231
Erik Andersen161220c2000-03-16 08:12:48 +0000232 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000233
Erik Andersen161220c2000-03-16 08:12:48 +0000234 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000235
Erik Andersen161220c2000-03-16 08:12:48 +0000236 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000237}
238
239/* built-in 'help' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000240static int shell_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000241{
Erik Andersen161220c2000-03-16 08:12:48 +0000242 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000243
Erik Andersen161220c2000-03-16 08:12:48 +0000244 fprintf(stdout, "\nBuilt-in commands:\n");
245 fprintf(stdout, "-------------------\n");
246 for (x = bltins; x->cmd; x++) {
247 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
248 }
249 fprintf(stdout, "\n\n");
250 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000251}
252
253/* built-in 'jobs' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000254static int shell_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000255{
Erik Andersen161220c2000-03-16 08:12:48 +0000256 struct job *job;
257 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000258
Erik Andersen161220c2000-03-16 08:12:48 +0000259 for (job = jobList->head; job; job = job->next) {
260 if (job->runningProgs == job->stoppedProgs)
261 statusString = "Stopped";
262 else
263 statusString = "Running";
264
265 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
266 }
267 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000268}
269
270
271/* built-in 'pwd' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000272static int shell_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000273{
Erik Andersen161220c2000-03-16 08:12:48 +0000274 getcwd(cwd, sizeof(cwd));
275 fprintf(stdout, "%s\n", cwd);
276 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000277}
278
Erik Andersen6273f652000-03-17 01:12:41 +0000279/* built-in 'export VAR=value' handler */
280static int shell_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000281{
Erik Andersen161220c2000-03-16 08:12:48 +0000282 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000283
Erik Andersen161220c2000-03-16 08:12:48 +0000284 if (!cmd->progs[0].argv[1] == 1) {
285 return (shell_env(cmd, junk));
286 }
287 res = putenv(cmd->progs[0].argv[1]);
288 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000289 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000290 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000291}
292
293/* Built-in '.' handler (read-in and execute commands from file) */
Erik Andersend75af992000-03-16 08:09:09 +0000294static int shell_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000295{
Erik Andersen161220c2000-03-16 08:12:48 +0000296 FILE *input;
297 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000298
Erik Andersen161220c2000-03-16 08:12:48 +0000299 if (!cmd->progs[0].argv[1] == 1)
300 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000301
Erik Andersen161220c2000-03-16 08:12:48 +0000302 input = fopen(cmd->progs[0].argv[1], "r");
303 if (!input) {
304 fprintf(stdout, "Couldn't open file '%s'\n",
305 cmd->progs[0].argv[1]);
306 return FALSE;
307 }
Erik Andersend75af992000-03-16 08:09:09 +0000308
Erik Andersen161220c2000-03-16 08:12:48 +0000309 /* Now run the file */
310 status = busy_loop(input);
311 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000312}
313
314/* built-in 'unset VAR' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000315static int shell_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000316{
Erik Andersen161220c2000-03-16 08:12:48 +0000317 if (!cmd->progs[0].argv[1] == 1) {
318 fprintf(stdout, "unset: parameter required.\n");
319 return FALSE;
320 }
321 unsetenv(cmd->progs[0].argv[1]);
322 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000323}
324
325/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000326static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000327{
Erik Andersen161220c2000-03-16 08:12:48 +0000328 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000329
Erik Andersen161220c2000-03-16 08:12:48 +0000330 for (i = 0; i < cmd->numProgs; i++) {
331 free(cmd->progs[i].argv);
332 if (cmd->progs[i].redirections)
333 free(cmd->progs[i].redirections);
334 if (cmd->progs[i].freeGlob)
335 globfree(&cmd->progs[i].globResult);
336 }
337 free(cmd->progs);
338 if (cmd->text)
339 free(cmd->text);
340 free(cmd->cmdBuf);
Erik Andersen3522eb12000-03-12 23:49:18 +0000341}
342
343/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000344static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000345{
Erik Andersen161220c2000-03-16 08:12:48 +0000346 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000347
Erik Andersen161220c2000-03-16 08:12:48 +0000348 freeJob(job);
349 if (job == jobList->head) {
350 jobList->head = job->next;
351 } else {
352 prevJob = jobList->head;
353 while (prevJob->next != job)
354 prevJob = prevJob->next;
355 prevJob->next = job->next;
356 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000357
Erik Andersen161220c2000-03-16 08:12:48 +0000358 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000359}
360
361/* Checks to see if any background processes have exited -- if they
362 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000363static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000364{
Erik Andersen161220c2000-03-16 08:12:48 +0000365 struct job *job;
366 pid_t childpid;
367 int status;
368 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000369
Erik Andersen161220c2000-03-16 08:12:48 +0000370 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
371 for (job = jobList->head; job; job = job->next) {
372 progNum = 0;
373 while (progNum < job->numProgs &&
374 job->progs[progNum].pid != childpid) progNum++;
375 if (progNum < job->numProgs)
376 break;
377 }
378
379 if (WIFEXITED(status) || WIFSIGNALED(status)) {
380 /* child exited */
381 job->runningProgs--;
382 job->progs[progNum].pid = 0;
383
384 if (!job->runningProgs) {
385 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
386 removeJob(jobList, job);
387 }
388 } else {
389 /* child stopped */
390 job->stoppedProgs++;
391 job->progs[progNum].isStopped = 1;
392
393 if (job->stoppedProgs == job->numProgs) {
394 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
395 job->text);
396 }
397 }
Erik Andersend75af992000-03-16 08:09:09 +0000398 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000399
Erik Andersen161220c2000-03-16 08:12:48 +0000400 if (childpid == -1 && errno != ECHILD)
401 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000402}
403
Erik Andersend75af992000-03-16 08:09:09 +0000404static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000405{
Erik Andersen161220c2000-03-16 08:12:48 +0000406 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000407#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000408 int len;
409 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000410 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000411 fflush(stdout);
412 promptStr=(char*)malloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000413 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000414 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000415 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000416 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000417#else
418 fprintf(stdout, "%s %s", cwd, prompt);
419 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000420#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000421 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000422
Erik Andersen161220c2000-03-16 08:12:48 +0000423 if (!fgets(command, BUFSIZ - 2, source)) {
424 if (source == stdin)
425 printf("\n");
426 return 1;
427 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000428
Erik Andersen161220c2000-03-16 08:12:48 +0000429 /* remove trailing newline */
430 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000431
Erik Andersen161220c2000-03-16 08:12:48 +0000432 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000433}
434
Erik Andersend75af992000-03-16 08:09:09 +0000435static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000436 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000437{
Erik Andersen161220c2000-03-16 08:12:48 +0000438 int argc = *argcPtr;
439 int argcAlloced = *argcAllocedPtr;
440 int rc;
441 int flags;
442 int i;
443 char *src, *dst;
Erik Andersen3522eb12000-03-12 23:49:18 +0000444
Erik Andersen161220c2000-03-16 08:12:48 +0000445 if (argc > 1) { /* cmd->globResult is already initialized */
446 flags = GLOB_APPEND;
447 i = prog->globResult.gl_pathc;
448 } else {
449 prog->freeGlob = 1;
450 flags = 0;
451 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000452 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000453
Erik Andersen161220c2000-03-16 08:12:48 +0000454 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
455 if (rc == GLOB_NOSPACE) {
456 fprintf(stderr, "out of space during glob operation\n");
457 return;
458 } else if (rc == GLOB_NOMATCH ||
459 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
460 !strcmp(prog->argv[argc - 1],
461 prog->globResult.gl_pathv[i]))) {
462 /* we need to remove whatever \ quoting is still present */
463 src = dst = prog->argv[argc - 1];
464 while (*src) {
465 if (*src != '\\')
466 *dst++ = *src;
467 src++;
468 }
469 *dst = '\0';
470 } else if (!rc) {
471 argcAlloced += (prog->globResult.gl_pathc - i);
472 prog->argv =
473 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
474 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
475 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
476 argc += (prog->globResult.gl_pathc - i - 1);
477 }
478
479 *argcAllocedPtr = argcAlloced;
480 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000481}
482
483/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
484 line). If a valid command is found, commandPtr is set to point to
485 the beginning of the next command (if the original command had more
486 then one job associated with it) or NULL if no more commands are
487 present. */
Erik Andersend75af992000-03-16 08:09:09 +0000488static int parseCommand(char **commandPtr, struct job *job, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000489{
Erik Andersen161220c2000-03-16 08:12:48 +0000490 char *command;
491 char *returnCommand = NULL;
492 char *src, *buf, *chptr;
493 int argc = 0;
494 int done = 0;
495 int argvAlloced;
496 int i;
497 char quote = '\0';
498 int count;
499 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000500
Erik Andersen161220c2000-03-16 08:12:48 +0000501 /* skip leading white space */
502 while (**commandPtr && isspace(**commandPtr))
503 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000504
Erik Andersen161220c2000-03-16 08:12:48 +0000505 /* this handles empty lines or leading '#' characters */
506 if (!**commandPtr || (**commandPtr == '#')) {
507 job->numProgs = 0;
508 *commandPtr = NULL;
509 return 0;
510 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000511
Erik Andersen161220c2000-03-16 08:12:48 +0000512 *isBg = 0;
513 job->numProgs = 1;
514 job->progs = malloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000515
Erik Andersen161220c2000-03-16 08:12:48 +0000516 /* We set the argv elements to point inside of this string. The
517 memory is freed by freeJob().
Erik Andersen3522eb12000-03-12 23:49:18 +0000518
Erik Andersen161220c2000-03-16 08:12:48 +0000519 Getting clean memory relieves us of the task of NULL
520 terminating things and makes the rest of this look a bit
521 cleaner (though it is, admittedly, a tad less efficient) */
522 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
523 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000524
Erik Andersen161220c2000-03-16 08:12:48 +0000525 prog = job->progs;
526 prog->numRedirections = 0;
527 prog->redirections = NULL;
528 prog->freeGlob = 0;
529 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000530
Erik Andersen161220c2000-03-16 08:12:48 +0000531 argvAlloced = 5;
532 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
533 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000534
Erik Andersen161220c2000-03-16 08:12:48 +0000535 buf = command;
536 src = *commandPtr;
537 while (*src && !done) {
538 if (quote == *src) {
539 quote = '\0';
540 } else if (quote) {
541 if (*src == '\\') {
542 src++;
543 if (!*src) {
544 fprintf(stderr, "character expected after \\\n");
545 freeJob(job);
546 return 1;
547 }
548
549 /* in shell, "\'" should yield \' */
550 if (*src != quote)
551 *buf++ = '\\';
552 } else if (*src == '*' || *src == '?' || *src == '[' ||
553 *src == ']') *buf++ = '\\';
554 *buf++ = *src;
555 } else if (isspace(*src)) {
556 if (*prog->argv[argc]) {
557 buf++, argc++;
558 /* +1 here leaves room for the NULL which ends argv */
559 if ((argc + 1) == argvAlloced) {
560 argvAlloced += 5;
561 prog->argv = realloc(prog->argv,
562 sizeof(*prog->argv) *
563 argvAlloced);
564 }
565 prog->argv[argc] = buf;
566
567 globLastArgument(prog, &argc, &argvAlloced);
568 }
569 } else
570 switch (*src) {
571 case '"':
572 case '\'':
573 quote = *src;
574 break;
575
576 case '#': /* comment */
577 done = 1;
578 break;
579
580 case '>': /* redirections */
581 case '<':
582 i = prog->numRedirections++;
583 prog->redirections = realloc(prog->redirections,
584 sizeof(*prog->redirections) *
585 (i + 1));
586
587 prog->redirections[i].fd = -1;
588 if (buf != prog->argv[argc]) {
589 /* the stuff before this character may be the file number
590 being redirected */
591 prog->redirections[i].fd =
592 strtol(prog->argv[argc], &chptr, 10);
593
594 if (*chptr && *prog->argv[argc]) {
595 buf++, argc++;
596 globLastArgument(prog, &argc, &argvAlloced);
597 }
598 }
599
600 if (prog->redirections[i].fd == -1) {
601 if (*src == '>')
602 prog->redirections[i].fd = 1;
603 else
604 prog->redirections[i].fd = 0;
605 }
606
607 if (*src++ == '>') {
608 if (*src == '>')
609 prog->redirections[i].type =
610 REDIRECT_APPEND, src++;
611 else
612 prog->redirections[i].type = REDIRECT_OVERWRITE;
613 } else {
614 prog->redirections[i].type = REDIRECT_INPUT;
615 }
616
617 /* This isn't POSIX sh compliant. Oh well. */
618 chptr = src;
619 while (isspace(*chptr))
620 chptr++;
621
622 if (!*chptr) {
623 fprintf(stderr, "file name expected after %c\n", *src);
624 freeJob(job);
625 return 1;
626 }
627
628 prog->redirections[i].filename = buf;
629 while (*chptr && !isspace(*chptr))
630 *buf++ = *chptr++;
631
632 src = chptr - 1; /* we src++ later */
633 prog->argv[argc] = ++buf;
634 break;
635
636 case '|': /* pipe */
637 /* finish this command */
638 if (*prog->argv[argc])
639 argc++;
640 if (!argc) {
641 fprintf(stderr, "empty command in pipe\n");
642 freeJob(job);
643 return 1;
644 }
645 prog->argv[argc] = NULL;
646
647 /* and start the next */
648 job->numProgs++;
649 job->progs = realloc(job->progs,
650 sizeof(*job->progs) * job->numProgs);
651 prog = job->progs + (job->numProgs - 1);
652 prog->numRedirections = 0;
653 prog->redirections = NULL;
654 prog->freeGlob = 0;
655 argc = 0;
656
657 argvAlloced = 5;
658 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
659 prog->argv[0] = ++buf;
660
661 src++;
662 while (*src && isspace(*src))
663 src++;
664
665 if (!*src) {
666 fprintf(stderr, "empty command in pipe\n");
667 return 1;
668 }
669 src--; /* we'll ++ it at the end of the loop */
670
671 break;
672
673 case '&': /* background */
674 *isBg = 1;
675 case ';': /* multiple commands */
676 done = 1;
677 returnCommand = *commandPtr + (src - *commandPtr) + 1;
678 break;
679
680 case '\\':
681 src++;
682 if (!*src) {
683 freeJob(job);
684 fprintf(stderr, "character expected after \\\n");
685 return 1;
686 }
687 if (*src == '*' || *src == '[' || *src == ']'
688 || *src == '?') *buf++ = '\\';
689 /* fallthrough */
690 default:
691 *buf++ = *src;
692 }
693
Erik Andersend75af992000-03-16 08:09:09 +0000694 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000695 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000696
Erik Andersen161220c2000-03-16 08:12:48 +0000697 if (*prog->argv[argc]) {
698 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000699 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000700 }
701 if (!argc) {
702 freeJob(job);
703 return 0;
704 }
705 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000706
Erik Andersen161220c2000-03-16 08:12:48 +0000707 if (!returnCommand) {
708 job->text = malloc(strlen(*commandPtr) + 1);
709 strcpy(job->text, *commandPtr);
710 } else {
711 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000712 count = returnCommand - *commandPtr;
713 job->text = malloc(count + 1);
714 strncpy(job->text, *commandPtr, count);
715 job->text[count] = '\0';
716 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000717
Erik Andersen161220c2000-03-16 08:12:48 +0000718 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000719
Erik Andersend75af992000-03-16 08:09:09 +0000720 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000721}
722
Erik Andersend75af992000-03-16 08:09:09 +0000723static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000724{
Erik Andersen161220c2000-03-16 08:12:48 +0000725 struct job *job;
726 int i;
727 int nextin, nextout;
728 int pipefds[2]; /* pipefd[0] is for reading */
729 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000730
Erik Andersen161220c2000-03-16 08:12:48 +0000731 /* handle built-ins here -- we don't fork() so we can't background
732 these very easily */
733 for (x = bltins; x->cmd; x++) {
734 if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
735 return (x->function(&newJob, jobList));
736 }
Erik Andersend75af992000-03-16 08:09:09 +0000737 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000738
Erik Andersen161220c2000-03-16 08:12:48 +0000739 nextin = 0, nextout = 1;
740 for (i = 0; i < newJob.numProgs; i++) {
741 if ((i + 1) < newJob.numProgs) {
742 pipe(pipefds);
743 nextout = pipefds[1];
744 } else {
745 nextout = 1;
746 }
747
748 if (!(newJob.progs[i].pid = fork())) {
749 signal(SIGTTOU, SIG_DFL);
750
751 if (nextin != 0) {
752 dup2(nextin, 0);
753 close(nextin);
754 }
755
756 if (nextout != 1) {
757 dup2(nextout, 1);
758 close(nextout);
759 }
760
761 /* explicit redirections override pipes */
762 setupRedirections(newJob.progs + i);
763
764 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
765 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
766 strerror(errno));
767 }
768
769 /* put our child in the process group whose leader is the
770 first process in this pipe */
771 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
772
773 if (nextin != 0)
774 close(nextin);
775 if (nextout != 1)
776 close(nextout);
777
778 /* If there isn't another process, nextin is garbage
779 but it doesn't matter */
780 nextin = pipefds[0];
781 }
782
783 newJob.pgrp = newJob.progs[0].pid;
784
785 /* find the ID for the job to use */
786 newJob.jobId = 1;
787 for (job = jobList->head; job; job = job->next)
788 if (job->jobId >= newJob.jobId)
789 newJob.jobId = job->jobId + 1;
790
791 /* add the job to the list of running jobs */
792 if (!jobList->head) {
793 job = jobList->head = malloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000794 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000795 for (job = jobList->head; job->next; job = job->next);
796 job->next = malloc(sizeof(*job));
797 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000798 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000799
Erik Andersen161220c2000-03-16 08:12:48 +0000800 *job = newJob;
801 job->next = NULL;
802 job->runningProgs = job->numProgs;
803 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000804
Erik Andersen161220c2000-03-16 08:12:48 +0000805 if (inBg) {
806 /* we don't wait for background jobs to return -- append it
807 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000808 printf("[%d] %d\n", job->jobId,
809 newJob.progs[newJob.numProgs - 1].pid);
810 } else {
811 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000812
Erik Andersen161220c2000-03-16 08:12:48 +0000813 /* move the new process group into the foreground */
Erik Andersen161220c2000-03-16 08:12:48 +0000814 if (tcsetpgrp(0, newJob.pgrp))
815 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000816 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000817
Erik Andersen161220c2000-03-16 08:12:48 +0000818 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000819}
820
Erik Andersend75af992000-03-16 08:09:09 +0000821static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000822{
Erik Andersen161220c2000-03-16 08:12:48 +0000823 int i;
824 int openfd;
825 int mode = O_RDONLY;
826 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000827
Erik Andersen161220c2000-03-16 08:12:48 +0000828 for (i = 0; i < prog->numRedirections; i++, redir++) {
829 switch (redir->type) {
830 case REDIRECT_INPUT:
831 mode = O_RDONLY;
832 break;
833 case REDIRECT_OVERWRITE:
834 mode = O_RDWR | O_CREAT | O_TRUNC;
835 break;
836 case REDIRECT_APPEND:
837 mode = O_RDWR | O_CREAT | O_APPEND;
838 break;
839 }
840
841 openfd = open(redir->filename, mode, 0666);
842 if (openfd < 0) {
843 /* this could get lost if stderr has been redirected, but
844 bash and ash both lose it as well (though zsh doesn't!) */
845 fprintf(stderr, "error opening %s: %s\n", redir->filename,
846 strerror(errno));
847 return 1;
848 }
849
850 if (openfd != redir->fd) {
851 dup2(openfd, redir->fd);
852 close(openfd);
853 }
Erik Andersend75af992000-03-16 08:09:09 +0000854 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000855
Erik Andersen161220c2000-03-16 08:12:48 +0000856 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000857}
858
859
Erik Andersend75af992000-03-16 08:09:09 +0000860static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000861{
Erik Andersen161220c2000-03-16 08:12:48 +0000862 char *command;
863 char *nextCommand = NULL;
864 struct jobSet jobList = { NULL, NULL };
865 struct job newJob;
866 int i;
867 int status;
868 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000869
Erik Andersen161220c2000-03-16 08:12:48 +0000870 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000871
Erik Andersen161220c2000-03-16 08:12:48 +0000872 /* don't pay any attention to this signal; it just confuses
873 things and isn't really meant for shells anyway */
874 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +0000875
Erik Andersen161220c2000-03-16 08:12:48 +0000876 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000877 if (!jobList.fg) {
878 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000879
Erik Andersend75af992000-03-16 08:09:09 +0000880 /* see if any background processes have exited */
881 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +0000882
Erik Andersend75af992000-03-16 08:09:09 +0000883 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +0000884 if (getCommand(input, command))
885 break;
886 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +0000887 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000888
Erik Andersend75af992000-03-16 08:09:09 +0000889 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +0000890 newJob.numProgs) {
891 runCommand(newJob, &jobList, inBg);
Erik Andersend75af992000-03-16 08:09:09 +0000892 }
893 } else {
894 /* a job is running in the foreground; wait for it */
895 i = 0;
896 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +0000897 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000898
Erik Andersend75af992000-03-16 08:09:09 +0000899 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +0000900
Erik Andersend75af992000-03-16 08:09:09 +0000901 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000902 /* the child exited */
903 jobList.fg->runningProgs--;
904 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000905
Erik Andersen161220c2000-03-16 08:12:48 +0000906 if (!jobList.fg->runningProgs) {
907 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +0000908
Erik Andersen161220c2000-03-16 08:12:48 +0000909 removeJob(&jobList, jobList.fg);
910 jobList.fg = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000911
Erik Andersen161220c2000-03-16 08:12:48 +0000912 /* move the shell to the foreground */
913 if (tcsetpgrp(0, getpid()))
914 perror("tcsetpgrp");
915 }
Erik Andersend75af992000-03-16 08:09:09 +0000916 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000917 /* the child was stopped */
918 jobList.fg->stoppedProgs++;
919 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000920
Erik Andersen161220c2000-03-16 08:12:48 +0000921 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
922 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
923 "Stopped", jobList.fg->text);
924 jobList.fg = NULL;
925 }
Erik Andersend75af992000-03-16 08:09:09 +0000926 }
927
928 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +0000929 /* move the shell to the foreground */
930 if (tcsetpgrp(0, getpid()))
931 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000932 }
933 }
934 }
Erik Andersen161220c2000-03-16 08:12:48 +0000935 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +0000936
Erik Andersen161220c2000-03-16 08:12:48 +0000937 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000938}
939
940
Erik Andersend75af992000-03-16 08:09:09 +0000941int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +0000942{
Erik Andersen161220c2000-03-16 08:12:48 +0000943 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +0000944
Erik Andersen161220c2000-03-16 08:12:48 +0000945 if (argc > 2) {
946 usage(shell_usage);
947 }
948 /* initialize the cwd */
949 getcwd(cwd, sizeof(cwd));
Erik Andersen3522eb12000-03-12 23:49:18 +0000950
Erik Andersenf0657d32000-04-12 17:49:52 +0000951#ifdef BB_FEATURE_SH_COMMAND_EDITING
952 signal(SIGWINCH, win_changed);
953 win_changed(0);
954#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000955
Erik Andersen161220c2000-03-16 08:12:48 +0000956 //if (argv[0] && argv[0][0] == '-') {
957 // shell_source("/etc/profile");
958 //}
Erik Andersen3522eb12000-03-12 23:49:18 +0000959
Erik Andersen161220c2000-03-16 08:12:48 +0000960 if (argc < 2) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000961 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
962 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000963 } else {
Erik Andersene5b6c7d2000-04-17 16:16:10 +0000964 if (*argv[1]=='-') {
965 usage("sh\n\nlash -- the BusyBox LAme SHell (command interpreter)\n");
966 }
Erik Andersen161220c2000-03-16 08:12:48 +0000967 input = fopen(argv[1], "r");
Erik Andersenf0657d32000-04-12 17:49:52 +0000968 if (!input) {
Erik Andersene5b6c7d2000-04-17 16:16:10 +0000969 fatalError("sh: Couldn't open file '%s': %s\n", argv[1],
Erik Andersen161220c2000-03-16 08:12:48 +0000970 strerror(errno));
Erik Andersenf0657d32000-04-12 17:49:52 +0000971 }
Erik Andersen161220c2000-03-16 08:12:48 +0000972 }
Erik Andersend75af992000-03-16 08:09:09 +0000973
Erik Andersen161220c2000-03-16 08:12:48 +0000974 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +0000975}