blob: b96b46456814a4e36e6d5d9458c2b39fbf06f7d0 [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},
Erik Andersen161220c2000-03-16 08:12:48 +0000116 {"env", "Print all environment variables", "env", shell_env},
117 {"exit", "Exit from shell()", "exit", shell_exit},
118 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
119 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
120 {"pwd", "Print current directory", "pwd", shell_pwd},
Erik Andersen6273f652000-03-17 01:12:41 +0000121 {"export", "Set environment variable", "export [VAR=value]", shell_export},
Erik Andersen161220c2000-03-16 08:12:48 +0000122 {"unset", "Unset environment variable", "unset VAR", shell_unset},
Erik Andersenc7c634b2000-03-19 05:28:55 +0000123 {".", "Source-in and run commands in a file", ". filename", shell_source},
Erik Andersen161220c2000-03-16 08:12:48 +0000124 {"help", "List shell built-in commands", "help", shell_help},
125 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000126};
127
128static const char shell_usage[] =
Erik Andersen161220c2000-03-16 08:12:48 +0000129
130 "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
Erik Andersen3522eb12000-03-12 23:49:18 +0000131
132
133static char cwd[1024];
134static char *prompt = "# ";
135
Erik Andersenf0657d32000-04-12 17:49:52 +0000136#ifdef BB_FEATURE_SH_COMMAND_EDITING
137void win_changed(int sig)
138{
139 struct winsize win = { 0, 0 };
140 ioctl(0, TIOCGWINSZ, &win);
141 if (win.ws_col > 0) {
142 cmdedit_setwidth( win.ws_col - 1);
143 }
144}
145#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000146
Erik Andersen3522eb12000-03-12 23:49:18 +0000147
Erik Andersend75af992000-03-16 08:09:09 +0000148/* built-in 'cd <path>' handler */
149static int shell_cd(struct job *cmd, struct jobSet *junk)
150{
Erik Andersen161220c2000-03-16 08:12:48 +0000151 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000152
Erik Andersen161220c2000-03-16 08:12:48 +0000153 if (!cmd->progs[0].argv[1] == 1)
154 newdir = getenv("HOME");
155 else
156 newdir = cmd->progs[0].argv[1];
157 if (chdir(newdir)) {
158 printf("cd: %s: %s\n", newdir, strerror(errno));
159 return FALSE;
160 }
161 getcwd(cwd, sizeof(cwd));
162
163 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000164}
165
166/* built-in 'env' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000167static int shell_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000168{
Erik Andersen161220c2000-03-16 08:12:48 +0000169 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000170
Erik Andersen161220c2000-03-16 08:12:48 +0000171 for (e = environ; *e; e++) {
172 fprintf(stdout, "%s\n", *e);
173 }
174 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000175}
176
177/* built-in 'exit' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000178static int shell_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000179{
Erik Andersen161220c2000-03-16 08:12:48 +0000180 if (!cmd->progs[0].argv[1] == 1)
181 exit TRUE;
182
183 else
184 exit(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000185}
186
187/* built-in 'fg' and 'bg' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000188static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000189{
Erik Andersen161220c2000-03-16 08:12:48 +0000190 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000191 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000192
Erik Andersen161220c2000-03-16 08:12:48 +0000193 if (!jobList->head) {
194 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
195 fprintf(stderr, "%s: exactly one argument is expected\n",
196 cmd->progs[0].argv[0]);
197 return FALSE;
198 }
199 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
200 fprintf(stderr, "%s: bad argument '%s'\n",
201 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
202 return FALSE;
203 for (job = jobList->head; job; job = job->next) {
204 if (job->jobId == jobNum) {
205 break;
206 }
207 }
208 }
209 } else {
210 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000211 }
Erik Andersen161220c2000-03-16 08:12:48 +0000212
213 if (!job) {
214 fprintf(stderr, "%s: unknown job %d\n",
215 cmd->progs[0].argv[0], jobNum);
216 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000217 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000218
Erik Andersen161220c2000-03-16 08:12:48 +0000219 if (*cmd->progs[0].argv[0] == 'f') {
220 /* Make this job the foreground job */
221 if (tcsetpgrp(0, job->pgrp))
222 perror("tcsetpgrp");
223 jobList->fg = job;
224 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000225
Erik Andersen161220c2000-03-16 08:12:48 +0000226 /* Restart the processes in the job */
227 for (i = 0; i < job->numProgs; i++)
228 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000229
Erik Andersen161220c2000-03-16 08:12:48 +0000230 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000231
Erik Andersen161220c2000-03-16 08:12:48 +0000232 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000233
Erik Andersen161220c2000-03-16 08:12:48 +0000234 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000235}
236
237/* built-in 'help' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000238static int shell_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000239{
Erik Andersen161220c2000-03-16 08:12:48 +0000240 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000241
Erik Andersen161220c2000-03-16 08:12:48 +0000242 fprintf(stdout, "\nBuilt-in commands:\n");
243 fprintf(stdout, "-------------------\n");
244 for (x = bltins; x->cmd; x++) {
245 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
246 }
247 fprintf(stdout, "\n\n");
248 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000249}
250
251/* built-in 'jobs' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000252static int shell_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000253{
Erik Andersen161220c2000-03-16 08:12:48 +0000254 struct job *job;
255 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000256
Erik Andersen161220c2000-03-16 08:12:48 +0000257 for (job = jobList->head; job; job = job->next) {
258 if (job->runningProgs == job->stoppedProgs)
259 statusString = "Stopped";
260 else
261 statusString = "Running";
262
263 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
264 }
265 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000266}
267
268
269/* built-in 'pwd' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000270static int shell_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000271{
Erik Andersen161220c2000-03-16 08:12:48 +0000272 getcwd(cwd, sizeof(cwd));
273 fprintf(stdout, "%s\n", cwd);
274 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000275}
276
Erik Andersen6273f652000-03-17 01:12:41 +0000277/* built-in 'export VAR=value' handler */
278static int shell_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000279{
Erik Andersen161220c2000-03-16 08:12:48 +0000280 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000281
Erik Andersen161220c2000-03-16 08:12:48 +0000282 if (!cmd->progs[0].argv[1] == 1) {
283 return (shell_env(cmd, junk));
284 }
285 res = putenv(cmd->progs[0].argv[1]);
286 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000287 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000288 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000289}
290
291/* Built-in '.' handler (read-in and execute commands from file) */
Erik Andersend75af992000-03-16 08:09:09 +0000292static int shell_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000293{
Erik Andersen161220c2000-03-16 08:12:48 +0000294 FILE *input;
295 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000296
Erik Andersen161220c2000-03-16 08:12:48 +0000297 if (!cmd->progs[0].argv[1] == 1)
298 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000299
Erik Andersen161220c2000-03-16 08:12:48 +0000300 input = fopen(cmd->progs[0].argv[1], "r");
301 if (!input) {
302 fprintf(stdout, "Couldn't open file '%s'\n",
303 cmd->progs[0].argv[1]);
304 return FALSE;
305 }
Erik Andersend75af992000-03-16 08:09:09 +0000306
Erik Andersen161220c2000-03-16 08:12:48 +0000307 /* Now run the file */
308 status = busy_loop(input);
309 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000310}
311
312/* built-in 'unset VAR' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000313static int shell_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000314{
Erik Andersen161220c2000-03-16 08:12:48 +0000315 if (!cmd->progs[0].argv[1] == 1) {
316 fprintf(stdout, "unset: parameter required.\n");
317 return FALSE;
318 }
319 unsetenv(cmd->progs[0].argv[1]);
320 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000321}
322
323/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000324static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000325{
Erik Andersen161220c2000-03-16 08:12:48 +0000326 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000327
Erik Andersen161220c2000-03-16 08:12:48 +0000328 for (i = 0; i < cmd->numProgs; i++) {
329 free(cmd->progs[i].argv);
330 if (cmd->progs[i].redirections)
331 free(cmd->progs[i].redirections);
332 if (cmd->progs[i].freeGlob)
333 globfree(&cmd->progs[i].globResult);
334 }
335 free(cmd->progs);
336 if (cmd->text)
337 free(cmd->text);
338 free(cmd->cmdBuf);
Erik Andersen3522eb12000-03-12 23:49:18 +0000339}
340
341/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000342static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000343{
Erik Andersen161220c2000-03-16 08:12:48 +0000344 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000345
Erik Andersen161220c2000-03-16 08:12:48 +0000346 freeJob(job);
347 if (job == jobList->head) {
348 jobList->head = job->next;
349 } else {
350 prevJob = jobList->head;
351 while (prevJob->next != job)
352 prevJob = prevJob->next;
353 prevJob->next = job->next;
354 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000355
Erik Andersen161220c2000-03-16 08:12:48 +0000356 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000357}
358
359/* Checks to see if any background processes have exited -- if they
360 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000361static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000362{
Erik Andersen161220c2000-03-16 08:12:48 +0000363 struct job *job;
364 pid_t childpid;
365 int status;
366 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000367
Erik Andersen161220c2000-03-16 08:12:48 +0000368 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
369 for (job = jobList->head; job; job = job->next) {
370 progNum = 0;
371 while (progNum < job->numProgs &&
372 job->progs[progNum].pid != childpid) progNum++;
373 if (progNum < job->numProgs)
374 break;
375 }
376
377 if (WIFEXITED(status) || WIFSIGNALED(status)) {
378 /* child exited */
379 job->runningProgs--;
380 job->progs[progNum].pid = 0;
381
382 if (!job->runningProgs) {
383 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
384 removeJob(jobList, job);
385 }
386 } else {
387 /* child stopped */
388 job->stoppedProgs++;
389 job->progs[progNum].isStopped = 1;
390
391 if (job->stoppedProgs == job->numProgs) {
392 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
393 job->text);
394 }
395 }
Erik Andersend75af992000-03-16 08:09:09 +0000396 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000397
Erik Andersen161220c2000-03-16 08:12:48 +0000398 if (childpid == -1 && errno != ECHILD)
399 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000400}
401
Erik Andersend75af992000-03-16 08:09:09 +0000402static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000403{
Erik Andersen161220c2000-03-16 08:12:48 +0000404 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000405#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000406 int len;
407 char *promptStr;
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000408 len=fprintf(stdout, "%s %s", cwd, prompt);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000409 fflush(stdout);
410 promptStr=(char*)malloc(sizeof(char)*(len+1));
Erik Andersend4bc1fc2000-04-05 05:19:03 +0000411 sprintf(promptStr, "%s %s", cwd, prompt);
Erik Andersenf0657d32000-04-12 17:49:52 +0000412 cmdedit_read_input(promptStr, command);
Erik Andersenc7c634b2000-03-19 05:28:55 +0000413 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000414 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000415#else
416 fprintf(stdout, "%s %s", cwd, prompt);
417 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000418#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000419 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000420
Erik Andersen161220c2000-03-16 08:12:48 +0000421 if (!fgets(command, BUFSIZ - 2, source)) {
422 if (source == stdin)
423 printf("\n");
424 return 1;
425 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000426
Erik Andersen161220c2000-03-16 08:12:48 +0000427 /* remove trailing newline */
428 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000429
Erik Andersen161220c2000-03-16 08:12:48 +0000430 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000431}
432
Erik Andersend75af992000-03-16 08:09:09 +0000433static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000434 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000435{
Erik Andersen161220c2000-03-16 08:12:48 +0000436 int argc = *argcPtr;
437 int argcAlloced = *argcAllocedPtr;
438 int rc;
439 int flags;
440 int i;
441 char *src, *dst;
Erik Andersen3522eb12000-03-12 23:49:18 +0000442
Erik Andersen161220c2000-03-16 08:12:48 +0000443 if (argc > 1) { /* cmd->globResult is already initialized */
444 flags = GLOB_APPEND;
445 i = prog->globResult.gl_pathc;
446 } else {
447 prog->freeGlob = 1;
448 flags = 0;
449 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000450 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000451
Erik Andersen161220c2000-03-16 08:12:48 +0000452 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
453 if (rc == GLOB_NOSPACE) {
454 fprintf(stderr, "out of space during glob operation\n");
455 return;
456 } else if (rc == GLOB_NOMATCH ||
457 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
458 !strcmp(prog->argv[argc - 1],
459 prog->globResult.gl_pathv[i]))) {
460 /* we need to remove whatever \ quoting is still present */
461 src = dst = prog->argv[argc - 1];
462 while (*src) {
463 if (*src != '\\')
464 *dst++ = *src;
465 src++;
466 }
467 *dst = '\0';
468 } else if (!rc) {
469 argcAlloced += (prog->globResult.gl_pathc - i);
470 prog->argv =
471 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
472 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
473 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
474 argc += (prog->globResult.gl_pathc - i - 1);
475 }
476
477 *argcAllocedPtr = argcAlloced;
478 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000479}
480
481/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
482 line). If a valid command is found, commandPtr is set to point to
483 the beginning of the next command (if the original command had more
484 then one job associated with it) or NULL if no more commands are
485 present. */
Erik Andersend75af992000-03-16 08:09:09 +0000486static int parseCommand(char **commandPtr, struct job *job, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000487{
Erik Andersen161220c2000-03-16 08:12:48 +0000488 char *command;
489 char *returnCommand = NULL;
490 char *src, *buf, *chptr;
491 int argc = 0;
492 int done = 0;
493 int argvAlloced;
494 int i;
495 char quote = '\0';
496 int count;
497 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000498
Erik Andersen161220c2000-03-16 08:12:48 +0000499 /* skip leading white space */
500 while (**commandPtr && isspace(**commandPtr))
501 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000502
Erik Andersen161220c2000-03-16 08:12:48 +0000503 /* this handles empty lines or leading '#' characters */
504 if (!**commandPtr || (**commandPtr == '#')) {
505 job->numProgs = 0;
506 *commandPtr = NULL;
507 return 0;
508 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000509
Erik Andersen161220c2000-03-16 08:12:48 +0000510 *isBg = 0;
511 job->numProgs = 1;
512 job->progs = malloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000513
Erik Andersen161220c2000-03-16 08:12:48 +0000514 /* We set the argv elements to point inside of this string. The
515 memory is freed by freeJob().
Erik Andersen3522eb12000-03-12 23:49:18 +0000516
Erik Andersen161220c2000-03-16 08:12:48 +0000517 Getting clean memory relieves us of the task of NULL
518 terminating things and makes the rest of this look a bit
519 cleaner (though it is, admittedly, a tad less efficient) */
520 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
521 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000522
Erik Andersen161220c2000-03-16 08:12:48 +0000523 prog = job->progs;
524 prog->numRedirections = 0;
525 prog->redirections = NULL;
526 prog->freeGlob = 0;
527 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000528
Erik Andersen161220c2000-03-16 08:12:48 +0000529 argvAlloced = 5;
530 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
531 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000532
Erik Andersen161220c2000-03-16 08:12:48 +0000533 buf = command;
534 src = *commandPtr;
535 while (*src && !done) {
536 if (quote == *src) {
537 quote = '\0';
538 } else if (quote) {
539 if (*src == '\\') {
540 src++;
541 if (!*src) {
542 fprintf(stderr, "character expected after \\\n");
543 freeJob(job);
544 return 1;
545 }
546
547 /* in shell, "\'" should yield \' */
548 if (*src != quote)
549 *buf++ = '\\';
550 } else if (*src == '*' || *src == '?' || *src == '[' ||
551 *src == ']') *buf++ = '\\';
552 *buf++ = *src;
553 } else if (isspace(*src)) {
554 if (*prog->argv[argc]) {
555 buf++, argc++;
556 /* +1 here leaves room for the NULL which ends argv */
557 if ((argc + 1) == argvAlloced) {
558 argvAlloced += 5;
559 prog->argv = realloc(prog->argv,
560 sizeof(*prog->argv) *
561 argvAlloced);
562 }
563 prog->argv[argc] = buf;
564
565 globLastArgument(prog, &argc, &argvAlloced);
566 }
567 } else
568 switch (*src) {
569 case '"':
570 case '\'':
571 quote = *src;
572 break;
573
574 case '#': /* comment */
575 done = 1;
576 break;
577
578 case '>': /* redirections */
579 case '<':
580 i = prog->numRedirections++;
581 prog->redirections = realloc(prog->redirections,
582 sizeof(*prog->redirections) *
583 (i + 1));
584
585 prog->redirections[i].fd = -1;
586 if (buf != prog->argv[argc]) {
587 /* the stuff before this character may be the file number
588 being redirected */
589 prog->redirections[i].fd =
590 strtol(prog->argv[argc], &chptr, 10);
591
592 if (*chptr && *prog->argv[argc]) {
593 buf++, argc++;
594 globLastArgument(prog, &argc, &argvAlloced);
595 }
596 }
597
598 if (prog->redirections[i].fd == -1) {
599 if (*src == '>')
600 prog->redirections[i].fd = 1;
601 else
602 prog->redirections[i].fd = 0;
603 }
604
605 if (*src++ == '>') {
606 if (*src == '>')
607 prog->redirections[i].type =
608 REDIRECT_APPEND, src++;
609 else
610 prog->redirections[i].type = REDIRECT_OVERWRITE;
611 } else {
612 prog->redirections[i].type = REDIRECT_INPUT;
613 }
614
615 /* This isn't POSIX sh compliant. Oh well. */
616 chptr = src;
617 while (isspace(*chptr))
618 chptr++;
619
620 if (!*chptr) {
621 fprintf(stderr, "file name expected after %c\n", *src);
622 freeJob(job);
623 return 1;
624 }
625
626 prog->redirections[i].filename = buf;
627 while (*chptr && !isspace(*chptr))
628 *buf++ = *chptr++;
629
630 src = chptr - 1; /* we src++ later */
631 prog->argv[argc] = ++buf;
632 break;
633
634 case '|': /* pipe */
635 /* finish this command */
636 if (*prog->argv[argc])
637 argc++;
638 if (!argc) {
639 fprintf(stderr, "empty command in pipe\n");
640 freeJob(job);
641 return 1;
642 }
643 prog->argv[argc] = NULL;
644
645 /* and start the next */
646 job->numProgs++;
647 job->progs = realloc(job->progs,
648 sizeof(*job->progs) * job->numProgs);
649 prog = job->progs + (job->numProgs - 1);
650 prog->numRedirections = 0;
651 prog->redirections = NULL;
652 prog->freeGlob = 0;
653 argc = 0;
654
655 argvAlloced = 5;
656 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
657 prog->argv[0] = ++buf;
658
659 src++;
660 while (*src && isspace(*src))
661 src++;
662
663 if (!*src) {
664 fprintf(stderr, "empty command in pipe\n");
665 return 1;
666 }
667 src--; /* we'll ++ it at the end of the loop */
668
669 break;
670
671 case '&': /* background */
672 *isBg = 1;
673 case ';': /* multiple commands */
674 done = 1;
675 returnCommand = *commandPtr + (src - *commandPtr) + 1;
676 break;
677
678 case '\\':
679 src++;
680 if (!*src) {
681 freeJob(job);
682 fprintf(stderr, "character expected after \\\n");
683 return 1;
684 }
685 if (*src == '*' || *src == '[' || *src == ']'
686 || *src == '?') *buf++ = '\\';
687 /* fallthrough */
688 default:
689 *buf++ = *src;
690 }
691
Erik Andersend75af992000-03-16 08:09:09 +0000692 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000693 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000694
Erik Andersen161220c2000-03-16 08:12:48 +0000695 if (*prog->argv[argc]) {
696 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000697 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000698 }
699 if (!argc) {
700 freeJob(job);
701 return 0;
702 }
703 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000704
Erik Andersen161220c2000-03-16 08:12:48 +0000705 if (!returnCommand) {
706 job->text = malloc(strlen(*commandPtr) + 1);
707 strcpy(job->text, *commandPtr);
708 } else {
709 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen161220c2000-03-16 08:12:48 +0000710 count = returnCommand - *commandPtr;
711 job->text = malloc(count + 1);
712 strncpy(job->text, *commandPtr, count);
713 job->text[count] = '\0';
714 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000715
Erik Andersen161220c2000-03-16 08:12:48 +0000716 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000717
Erik Andersend75af992000-03-16 08:09:09 +0000718 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000719}
720
Erik Andersend75af992000-03-16 08:09:09 +0000721static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000722{
Erik Andersen161220c2000-03-16 08:12:48 +0000723 struct job *job;
724 int i;
725 int nextin, nextout;
726 int pipefds[2]; /* pipefd[0] is for reading */
727 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000728
Erik Andersen161220c2000-03-16 08:12:48 +0000729 /* handle built-ins here -- we don't fork() so we can't background
730 these very easily */
731 for (x = bltins; x->cmd; x++) {
732 if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
733 return (x->function(&newJob, jobList));
734 }
Erik Andersend75af992000-03-16 08:09:09 +0000735 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000736
Erik Andersen161220c2000-03-16 08:12:48 +0000737 nextin = 0, nextout = 1;
738 for (i = 0; i < newJob.numProgs; i++) {
739 if ((i + 1) < newJob.numProgs) {
740 pipe(pipefds);
741 nextout = pipefds[1];
742 } else {
743 nextout = 1;
744 }
745
746 if (!(newJob.progs[i].pid = fork())) {
747 signal(SIGTTOU, SIG_DFL);
748
749 if (nextin != 0) {
750 dup2(nextin, 0);
751 close(nextin);
752 }
753
754 if (nextout != 1) {
755 dup2(nextout, 1);
756 close(nextout);
757 }
758
759 /* explicit redirections override pipes */
760 setupRedirections(newJob.progs + i);
761
762 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
763 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
764 strerror(errno));
765 }
766
767 /* put our child in the process group whose leader is the
768 first process in this pipe */
769 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
770
771 if (nextin != 0)
772 close(nextin);
773 if (nextout != 1)
774 close(nextout);
775
776 /* If there isn't another process, nextin is garbage
777 but it doesn't matter */
778 nextin = pipefds[0];
779 }
780
781 newJob.pgrp = newJob.progs[0].pid;
782
783 /* find the ID for the job to use */
784 newJob.jobId = 1;
785 for (job = jobList->head; job; job = job->next)
786 if (job->jobId >= newJob.jobId)
787 newJob.jobId = job->jobId + 1;
788
789 /* add the job to the list of running jobs */
790 if (!jobList->head) {
791 job = jobList->head = malloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000792 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000793 for (job = jobList->head; job->next; job = job->next);
794 job->next = malloc(sizeof(*job));
795 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000796 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000797
Erik Andersen161220c2000-03-16 08:12:48 +0000798 *job = newJob;
799 job->next = NULL;
800 job->runningProgs = job->numProgs;
801 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000802
Erik Andersen161220c2000-03-16 08:12:48 +0000803 if (inBg) {
804 /* we don't wait for background jobs to return -- append it
805 to the list of backgrounded jobs and leave it alone */
Erik Andersen161220c2000-03-16 08:12:48 +0000806 printf("[%d] %d\n", job->jobId,
807 newJob.progs[newJob.numProgs - 1].pid);
808 } else {
809 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000810
Erik Andersen161220c2000-03-16 08:12:48 +0000811 /* move the new process group into the foreground */
Erik Andersen161220c2000-03-16 08:12:48 +0000812 if (tcsetpgrp(0, newJob.pgrp))
813 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000814 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000815
Erik Andersen161220c2000-03-16 08:12:48 +0000816 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000817}
818
Erik Andersend75af992000-03-16 08:09:09 +0000819static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000820{
Erik Andersen161220c2000-03-16 08:12:48 +0000821 int i;
822 int openfd;
823 int mode = O_RDONLY;
824 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000825
Erik Andersen161220c2000-03-16 08:12:48 +0000826 for (i = 0; i < prog->numRedirections; i++, redir++) {
827 switch (redir->type) {
828 case REDIRECT_INPUT:
829 mode = O_RDONLY;
830 break;
831 case REDIRECT_OVERWRITE:
832 mode = O_RDWR | O_CREAT | O_TRUNC;
833 break;
834 case REDIRECT_APPEND:
835 mode = O_RDWR | O_CREAT | O_APPEND;
836 break;
837 }
838
839 openfd = open(redir->filename, mode, 0666);
840 if (openfd < 0) {
841 /* this could get lost if stderr has been redirected, but
842 bash and ash both lose it as well (though zsh doesn't!) */
843 fprintf(stderr, "error opening %s: %s\n", redir->filename,
844 strerror(errno));
845 return 1;
846 }
847
848 if (openfd != redir->fd) {
849 dup2(openfd, redir->fd);
850 close(openfd);
851 }
Erik Andersend75af992000-03-16 08:09:09 +0000852 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000853
Erik Andersen161220c2000-03-16 08:12:48 +0000854 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000855}
856
857
Erik Andersend75af992000-03-16 08:09:09 +0000858static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000859{
Erik Andersen161220c2000-03-16 08:12:48 +0000860 char *command;
861 char *nextCommand = NULL;
862 struct jobSet jobList = { NULL, NULL };
863 struct job newJob;
864 int i;
865 int status;
866 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000867
Erik Andersen161220c2000-03-16 08:12:48 +0000868 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000869
Erik Andersen161220c2000-03-16 08:12:48 +0000870 /* don't pay any attention to this signal; it just confuses
871 things and isn't really meant for shells anyway */
872 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +0000873
Erik Andersen161220c2000-03-16 08:12:48 +0000874 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000875 if (!jobList.fg) {
876 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000877
Erik Andersend75af992000-03-16 08:09:09 +0000878 /* see if any background processes have exited */
879 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +0000880
Erik Andersend75af992000-03-16 08:09:09 +0000881 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +0000882 if (getCommand(input, command))
883 break;
884 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +0000885 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000886
Erik Andersend75af992000-03-16 08:09:09 +0000887 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +0000888 newJob.numProgs) {
889 runCommand(newJob, &jobList, inBg);
Erik Andersend75af992000-03-16 08:09:09 +0000890 }
891 } else {
892 /* a job is running in the foreground; wait for it */
893 i = 0;
894 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +0000895 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000896
Erik Andersend75af992000-03-16 08:09:09 +0000897 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +0000898
Erik Andersend75af992000-03-16 08:09:09 +0000899 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000900 /* the child exited */
901 jobList.fg->runningProgs--;
902 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000903
Erik Andersen161220c2000-03-16 08:12:48 +0000904 if (!jobList.fg->runningProgs) {
905 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +0000906
Erik Andersen161220c2000-03-16 08:12:48 +0000907 removeJob(&jobList, jobList.fg);
908 jobList.fg = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000909
Erik Andersen161220c2000-03-16 08:12:48 +0000910 /* move the shell to the foreground */
911 if (tcsetpgrp(0, getpid()))
912 perror("tcsetpgrp");
913 }
Erik Andersend75af992000-03-16 08:09:09 +0000914 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000915 /* the child was stopped */
916 jobList.fg->stoppedProgs++;
917 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000918
Erik Andersen161220c2000-03-16 08:12:48 +0000919 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
920 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
921 "Stopped", jobList.fg->text);
922 jobList.fg = NULL;
923 }
Erik Andersend75af992000-03-16 08:09:09 +0000924 }
925
926 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +0000927 /* move the shell to the foreground */
928 if (tcsetpgrp(0, getpid()))
929 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000930 }
931 }
932 }
Erik Andersen161220c2000-03-16 08:12:48 +0000933 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +0000934
Erik Andersen161220c2000-03-16 08:12:48 +0000935 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000936}
937
938
Erik Andersend75af992000-03-16 08:09:09 +0000939int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +0000940{
Erik Andersen161220c2000-03-16 08:12:48 +0000941 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +0000942
Erik Andersen161220c2000-03-16 08:12:48 +0000943 if (argc > 2) {
944 usage(shell_usage);
945 }
946 /* initialize the cwd */
947 getcwd(cwd, sizeof(cwd));
Erik Andersen3522eb12000-03-12 23:49:18 +0000948
Erik Andersenf0657d32000-04-12 17:49:52 +0000949#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen1d1d9502000-04-21 01:26:49 +0000950 cmdedit_init();
Erik Andersenf0657d32000-04-12 17:49:52 +0000951 signal(SIGWINCH, win_changed);
952 win_changed(0);
953#endif
Erik Andersen3522eb12000-03-12 23:49:18 +0000954
Erik Andersen161220c2000-03-16 08:12:48 +0000955 //if (argv[0] && argv[0][0] == '-') {
956 // shell_source("/etc/profile");
957 //}
Erik Andersen3522eb12000-03-12 23:49:18 +0000958
Erik Andersen161220c2000-03-16 08:12:48 +0000959 if (argc < 2) {
Erik Andersenf0657d32000-04-12 17:49:52 +0000960 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT);
961 fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000962 } else {
Erik Andersene5b6c7d2000-04-17 16:16:10 +0000963 if (*argv[1]=='-') {
964 usage("sh\n\nlash -- the BusyBox LAme SHell (command interpreter)\n");
965 }
Erik Andersen161220c2000-03-16 08:12:48 +0000966 input = fopen(argv[1], "r");
Erik Andersenf0657d32000-04-12 17:49:52 +0000967 if (!input) {
Erik Andersene5b6c7d2000-04-17 16:16:10 +0000968 fatalError("sh: Couldn't open file '%s': %s\n", argv[1],
Erik Andersen161220c2000-03-16 08:12:48 +0000969 strerror(errno));
Erik Andersenf0657d32000-04-12 17:49:52 +0000970 }
Erik Andersen161220c2000-03-16 08:12:48 +0000971 }
Erik Andersend75af992000-03-16 08:09:09 +0000972
Erik Andersen161220c2000-03-16 08:12:48 +0000973 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +0000974}