blob: 8c1503ff41489bbeb9d2ad86bf9e274bf7f3a228 [file] [log] [blame]
Erik Andersen3522eb12000-03-12 23:49:18 +00001/* vi: set sw=4 ts=4: */
2/*
3 * BusyBox Shell
4 *
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>
40
41
Erik Andersen3522eb12000-03-12 23:49:18 +000042#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
43
Erik Andersend75af992000-03-16 08:09:09 +000044
45enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000046 REDIRECT_APPEND
47};
Erik Andersen3522eb12000-03-12 23:49:18 +000048
49struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000050 struct job *head; /* head of list of running jobs */
51 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000052};
53
54struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000055 enum redirectionType type; /* type of redirection */
56 int fd; /* file descriptor being redirected */
57 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000058};
59
60struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000061 pid_t pid; /* 0 if exited */
62 char **argv; /* program name and arguments */
63 int numRedirections; /* elements in redirection array */
64 struct redirectionSpecifier *redirections; /* I/O redirections */
65 glob_t globResult; /* result of parameter globbing */
66 int freeGlob; /* should we globfree(&globResult)? */
67 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000068};
69
70struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000071 int jobId; /* job number */
72 int numProgs; /* total number of programs in job */
73 int runningProgs; /* number of programs running */
74 char *text; /* name of job */
75 char *cmdBuf; /* buffer various argv's point into */
76 pid_t pgrp; /* process group ID for the job */
77 struct childProgram *progs; /* array of programs in job */
78 struct job *next; /* to track background commands */
79 int stoppedProgs; /* number of programs alive, but stopped */
Erik Andersen3522eb12000-03-12 23:49:18 +000080};
81
82struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +000083 char *cmd; /* name */
84 char *descr; /* description */
85 char *usage; /* usage */
86 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +000087};
88
89/* Some function prototypes */
Erik Andersend75af992000-03-16 08:09:09 +000090static int shell_cd(struct job *cmd, struct jobSet *junk);
91static int shell_env(struct job *dummy, struct jobSet *junk);
92static int shell_exit(struct job *cmd, struct jobSet *junk);
93static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
94static int shell_help(struct job *cmd, struct jobSet *junk);
95static int shell_jobs(struct job *dummy, struct jobSet *jobList);
96static int shell_pwd(struct job *dummy, struct jobSet *junk);
Erik Andersen6273f652000-03-17 01:12:41 +000097static int shell_export(struct job *cmd, struct jobSet *junk);
Erik Andersend75af992000-03-16 08:09:09 +000098static int shell_source(struct job *cmd, struct jobSet *jobList);
99static int shell_unset(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000100
Erik Andersend75af992000-03-16 08:09:09 +0000101static void checkJobs(struct jobSet *jobList);
102static int getCommand(FILE * source, char *command);
103static int parseCommand(char **commandPtr, struct job *job, int *isBg);
104static int setupRedirections(struct childProgram *prog);
105static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
Erik Andersen3522eb12000-03-12 23:49:18 +0000106static int busy_loop(FILE * input);
107
Erik Andersend75af992000-03-16 08:09:09 +0000108
Erik Andersen3522eb12000-03-12 23:49:18 +0000109/* Table of built-in functions */
110static struct builtInCommand bltins[] = {
Erik Andersen161220c2000-03-16 08:12:48 +0000111 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
112 {"cd", "Change working directory", "cd [dir]", shell_cd},
113 //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
114 {"env", "Print all environment variables", "env", shell_env},
115 {"exit", "Exit from shell()", "exit", shell_exit},
116 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
117 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
118 {"pwd", "Print current directory", "pwd", shell_pwd},
Erik Andersen6273f652000-03-17 01:12:41 +0000119 {"export", "Set environment variable", "export [VAR=value]", shell_export},
Erik Andersen161220c2000-03-16 08:12:48 +0000120 {"unset", "Unset environment variable", "unset VAR", shell_unset},
121
Erik Andersenc7c634b2000-03-19 05:28:55 +0000122 {".", "Source-in and run commands in a file", ". filename", shell_source},
Erik Andersen161220c2000-03-16 08:12:48 +0000123 {"help", "List shell built-in commands", "help", shell_help},
124 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000125};
126
127static const char shell_usage[] =
Erik Andersen161220c2000-03-16 08:12:48 +0000128
129 "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
Erik Andersen3522eb12000-03-12 23:49:18 +0000130
131
132static char cwd[1024];
133static char *prompt = "# ";
134
135
Erik Andersen3522eb12000-03-12 23:49:18 +0000136
Erik Andersend75af992000-03-16 08:09:09 +0000137/* built-in 'cd <path>' handler */
138static int shell_cd(struct job *cmd, struct jobSet *junk)
139{
Erik Andersen161220c2000-03-16 08:12:48 +0000140 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000141
Erik Andersen161220c2000-03-16 08:12:48 +0000142 if (!cmd->progs[0].argv[1] == 1)
143 newdir = getenv("HOME");
144 else
145 newdir = cmd->progs[0].argv[1];
146 if (chdir(newdir)) {
147 printf("cd: %s: %s\n", newdir, strerror(errno));
148 return FALSE;
149 }
150 getcwd(cwd, sizeof(cwd));
151
152 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000153}
154
155/* built-in 'env' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000156static int shell_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000157{
Erik Andersen161220c2000-03-16 08:12:48 +0000158 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000159
Erik Andersen161220c2000-03-16 08:12:48 +0000160 for (e = environ; *e; e++) {
161 fprintf(stdout, "%s\n", *e);
162 }
163 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000164}
165
166/* built-in 'exit' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000167static int shell_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000168{
Erik Andersen161220c2000-03-16 08:12:48 +0000169 if (!cmd->progs[0].argv[1] == 1)
170 exit TRUE;
171
172 else
173 exit(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000174}
175
176/* built-in 'fg' and 'bg' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000177static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000178{
Erik Andersen161220c2000-03-16 08:12:48 +0000179 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000180 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000181
Erik Andersen161220c2000-03-16 08:12:48 +0000182 if (!jobList->head) {
183 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
184 fprintf(stderr, "%s: exactly one argument is expected\n",
185 cmd->progs[0].argv[0]);
186 return FALSE;
187 }
188 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
189 fprintf(stderr, "%s: bad argument '%s'\n",
190 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
191 return FALSE;
192 for (job = jobList->head; job; job = job->next) {
193 if (job->jobId == jobNum) {
194 break;
195 }
196 }
197 }
198 } else {
199 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000200 }
Erik Andersen161220c2000-03-16 08:12:48 +0000201
202 if (!job) {
203 fprintf(stderr, "%s: unknown job %d\n",
204 cmd->progs[0].argv[0], jobNum);
205 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000206 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000207
Erik Andersen161220c2000-03-16 08:12:48 +0000208 if (*cmd->progs[0].argv[0] == 'f') {
209 /* Make this job the foreground job */
210 if (tcsetpgrp(0, job->pgrp))
211 perror("tcsetpgrp");
212 jobList->fg = job;
213 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000214
Erik Andersen161220c2000-03-16 08:12:48 +0000215 /* Restart the processes in the job */
216 for (i = 0; i < job->numProgs; i++)
217 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000218
Erik Andersen161220c2000-03-16 08:12:48 +0000219 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000220
Erik Andersen161220c2000-03-16 08:12:48 +0000221 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000222
Erik Andersen161220c2000-03-16 08:12:48 +0000223 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000224}
225
226/* built-in 'help' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000227static int shell_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000228{
Erik Andersen161220c2000-03-16 08:12:48 +0000229 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000230
Erik Andersen161220c2000-03-16 08:12:48 +0000231 fprintf(stdout, "\nBuilt-in commands:\n");
232 fprintf(stdout, "-------------------\n");
233 for (x = bltins; x->cmd; x++) {
234 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
235 }
236 fprintf(stdout, "\n\n");
237 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000238}
239
240/* built-in 'jobs' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000241static int shell_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000242{
Erik Andersen161220c2000-03-16 08:12:48 +0000243 struct job *job;
244 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000245
Erik Andersen161220c2000-03-16 08:12:48 +0000246 for (job = jobList->head; job; job = job->next) {
247 if (job->runningProgs == job->stoppedProgs)
248 statusString = "Stopped";
249 else
250 statusString = "Running";
251
252 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
253 }
254 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000255}
256
257
258/* built-in 'pwd' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000259static int shell_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000260{
Erik Andersen161220c2000-03-16 08:12:48 +0000261 getcwd(cwd, sizeof(cwd));
262 fprintf(stdout, "%s\n", cwd);
263 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000264}
265
Erik Andersen6273f652000-03-17 01:12:41 +0000266/* built-in 'export VAR=value' handler */
267static int shell_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000268{
Erik Andersen161220c2000-03-16 08:12:48 +0000269 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000270
Erik Andersen161220c2000-03-16 08:12:48 +0000271 if (!cmd->progs[0].argv[1] == 1) {
272 return (shell_env(cmd, junk));
273 }
274 res = putenv(cmd->progs[0].argv[1]);
275 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000276 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000277 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000278}
279
280/* Built-in '.' handler (read-in and execute commands from file) */
Erik Andersend75af992000-03-16 08:09:09 +0000281static int shell_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000282{
Erik Andersen161220c2000-03-16 08:12:48 +0000283 FILE *input;
284 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000285
Erik Andersen161220c2000-03-16 08:12:48 +0000286 if (!cmd->progs[0].argv[1] == 1)
287 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000288
Erik Andersen161220c2000-03-16 08:12:48 +0000289 input = fopen(cmd->progs[0].argv[1], "r");
290 if (!input) {
291 fprintf(stdout, "Couldn't open file '%s'\n",
292 cmd->progs[0].argv[1]);
293 return FALSE;
294 }
Erik Andersend75af992000-03-16 08:09:09 +0000295
Erik Andersen161220c2000-03-16 08:12:48 +0000296 /* Now run the file */
297 status = busy_loop(input);
298 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000299}
300
301/* built-in 'unset VAR' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000302static int shell_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000303{
Erik Andersen161220c2000-03-16 08:12:48 +0000304 if (!cmd->progs[0].argv[1] == 1) {
305 fprintf(stdout, "unset: parameter required.\n");
306 return FALSE;
307 }
308 unsetenv(cmd->progs[0].argv[1]);
309 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000310}
311
312/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000313static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000314{
Erik Andersen161220c2000-03-16 08:12:48 +0000315 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000316
Erik Andersen161220c2000-03-16 08:12:48 +0000317 for (i = 0; i < cmd->numProgs; i++) {
318 free(cmd->progs[i].argv);
319 if (cmd->progs[i].redirections)
320 free(cmd->progs[i].redirections);
321 if (cmd->progs[i].freeGlob)
322 globfree(&cmd->progs[i].globResult);
323 }
324 free(cmd->progs);
325 if (cmd->text)
326 free(cmd->text);
327 free(cmd->cmdBuf);
Erik Andersen3522eb12000-03-12 23:49:18 +0000328}
329
330/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000331static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000332{
Erik Andersen161220c2000-03-16 08:12:48 +0000333 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000334
Erik Andersen161220c2000-03-16 08:12:48 +0000335 freeJob(job);
336 if (job == jobList->head) {
337 jobList->head = job->next;
338 } else {
339 prevJob = jobList->head;
340 while (prevJob->next != job)
341 prevJob = prevJob->next;
342 prevJob->next = job->next;
343 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000344
Erik Andersen161220c2000-03-16 08:12:48 +0000345 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000346}
347
348/* Checks to see if any background processes have exited -- if they
349 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000350static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000351{
Erik Andersen161220c2000-03-16 08:12:48 +0000352 struct job *job;
353 pid_t childpid;
354 int status;
355 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000356
Erik Andersen161220c2000-03-16 08:12:48 +0000357 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
358 for (job = jobList->head; job; job = job->next) {
359 progNum = 0;
360 while (progNum < job->numProgs &&
361 job->progs[progNum].pid != childpid) progNum++;
362 if (progNum < job->numProgs)
363 break;
364 }
365
366 if (WIFEXITED(status) || WIFSIGNALED(status)) {
367 /* child exited */
368 job->runningProgs--;
369 job->progs[progNum].pid = 0;
370
371 if (!job->runningProgs) {
372 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
373 removeJob(jobList, job);
374 }
375 } else {
376 /* child stopped */
377 job->stoppedProgs++;
378 job->progs[progNum].isStopped = 1;
379
380 if (job->stoppedProgs == job->numProgs) {
381 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
382 job->text);
383 }
384 }
Erik Andersend75af992000-03-16 08:09:09 +0000385 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000386
Erik Andersen161220c2000-03-16 08:12:48 +0000387 if (childpid == -1 && errno != ECHILD)
388 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000389}
390
Erik Andersend75af992000-03-16 08:09:09 +0000391static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000392{
Erik Andersen161220c2000-03-16 08:12:48 +0000393 if (source == stdin) {
Erik Andersend75af992000-03-16 08:09:09 +0000394#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersenc7c634b2000-03-19 05:28:55 +0000395 int len;
396 char *promptStr;
397 len=fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
398 fflush(stdout);
399 promptStr=(char*)malloc(sizeof(char)*(len+1));
400 sprintf(promptStr, "BBSHELL %s %s", cwd, prompt);
401 cmdedit_read_input(promptStr, fileno(stdin), fileno(stdout), command);
402 free( promptStr);
Erik Andersen161220c2000-03-16 08:12:48 +0000403 return 0;
Erik Andersenc7c634b2000-03-19 05:28:55 +0000404#else
405 fprintf(stdout, "%s %s", cwd, prompt);
406 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000407#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000408 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000409
Erik Andersen161220c2000-03-16 08:12:48 +0000410 if (!fgets(command, BUFSIZ - 2, source)) {
411 if (source == stdin)
412 printf("\n");
413 return 1;
414 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000415
Erik Andersen161220c2000-03-16 08:12:48 +0000416 /* remove trailing newline */
417 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000418
Erik Andersen161220c2000-03-16 08:12:48 +0000419 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000420}
421
Erik Andersend75af992000-03-16 08:09:09 +0000422static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000423 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000424{
Erik Andersen161220c2000-03-16 08:12:48 +0000425 int argc = *argcPtr;
426 int argcAlloced = *argcAllocedPtr;
427 int rc;
428 int flags;
429 int i;
430 char *src, *dst;
Erik Andersen3522eb12000-03-12 23:49:18 +0000431
Erik Andersen161220c2000-03-16 08:12:48 +0000432 if (argc > 1) { /* cmd->globResult is already initialized */
433 flags = GLOB_APPEND;
434 i = prog->globResult.gl_pathc;
435 } else {
436 prog->freeGlob = 1;
437 flags = 0;
438 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000439 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000440
Erik Andersen161220c2000-03-16 08:12:48 +0000441 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
442 if (rc == GLOB_NOSPACE) {
443 fprintf(stderr, "out of space during glob operation\n");
444 return;
445 } else if (rc == GLOB_NOMATCH ||
446 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
447 !strcmp(prog->argv[argc - 1],
448 prog->globResult.gl_pathv[i]))) {
449 /* we need to remove whatever \ quoting is still present */
450 src = dst = prog->argv[argc - 1];
451 while (*src) {
452 if (*src != '\\')
453 *dst++ = *src;
454 src++;
455 }
456 *dst = '\0';
457 } else if (!rc) {
458 argcAlloced += (prog->globResult.gl_pathc - i);
459 prog->argv =
460 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
461 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
462 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
463 argc += (prog->globResult.gl_pathc - i - 1);
464 }
465
466 *argcAllocedPtr = argcAlloced;
467 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000468}
469
470/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
471 line). If a valid command is found, commandPtr is set to point to
472 the beginning of the next command (if the original command had more
473 then one job associated with it) or NULL if no more commands are
474 present. */
Erik Andersend75af992000-03-16 08:09:09 +0000475static int parseCommand(char **commandPtr, struct job *job, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000476{
Erik Andersen161220c2000-03-16 08:12:48 +0000477 char *command;
478 char *returnCommand = NULL;
479 char *src, *buf, *chptr;
480 int argc = 0;
481 int done = 0;
482 int argvAlloced;
483 int i;
484 char quote = '\0';
485 int count;
486 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000487
Erik Andersen161220c2000-03-16 08:12:48 +0000488 /* skip leading white space */
489 while (**commandPtr && isspace(**commandPtr))
490 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000491
Erik Andersen161220c2000-03-16 08:12:48 +0000492 /* this handles empty lines or leading '#' characters */
493 if (!**commandPtr || (**commandPtr == '#')) {
494 job->numProgs = 0;
495 *commandPtr = NULL;
496 return 0;
497 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000498
Erik Andersen161220c2000-03-16 08:12:48 +0000499 *isBg = 0;
500 job->numProgs = 1;
501 job->progs = malloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000502
Erik Andersen161220c2000-03-16 08:12:48 +0000503 /* We set the argv elements to point inside of this string. The
504 memory is freed by freeJob().
Erik Andersen3522eb12000-03-12 23:49:18 +0000505
Erik Andersen161220c2000-03-16 08:12:48 +0000506 Getting clean memory relieves us of the task of NULL
507 terminating things and makes the rest of this look a bit
508 cleaner (though it is, admittedly, a tad less efficient) */
509 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
510 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000511
Erik Andersen161220c2000-03-16 08:12:48 +0000512 prog = job->progs;
513 prog->numRedirections = 0;
514 prog->redirections = NULL;
515 prog->freeGlob = 0;
516 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000517
Erik Andersen161220c2000-03-16 08:12:48 +0000518 argvAlloced = 5;
519 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
520 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000521
Erik Andersen161220c2000-03-16 08:12:48 +0000522 buf = command;
523 src = *commandPtr;
524 while (*src && !done) {
525 if (quote == *src) {
526 quote = '\0';
527 } else if (quote) {
528 if (*src == '\\') {
529 src++;
530 if (!*src) {
531 fprintf(stderr, "character expected after \\\n");
532 freeJob(job);
533 return 1;
534 }
535
536 /* in shell, "\'" should yield \' */
537 if (*src != quote)
538 *buf++ = '\\';
539 } else if (*src == '*' || *src == '?' || *src == '[' ||
540 *src == ']') *buf++ = '\\';
541 *buf++ = *src;
542 } else if (isspace(*src)) {
543 if (*prog->argv[argc]) {
544 buf++, argc++;
545 /* +1 here leaves room for the NULL which ends argv */
546 if ((argc + 1) == argvAlloced) {
547 argvAlloced += 5;
548 prog->argv = realloc(prog->argv,
549 sizeof(*prog->argv) *
550 argvAlloced);
551 }
552 prog->argv[argc] = buf;
553
554 globLastArgument(prog, &argc, &argvAlloced);
555 }
556 } else
557 switch (*src) {
558 case '"':
559 case '\'':
560 quote = *src;
561 break;
562
563 case '#': /* comment */
564 done = 1;
565 break;
566
567 case '>': /* redirections */
568 case '<':
569 i = prog->numRedirections++;
570 prog->redirections = realloc(prog->redirections,
571 sizeof(*prog->redirections) *
572 (i + 1));
573
574 prog->redirections[i].fd = -1;
575 if (buf != prog->argv[argc]) {
576 /* the stuff before this character may be the file number
577 being redirected */
578 prog->redirections[i].fd =
579 strtol(prog->argv[argc], &chptr, 10);
580
581 if (*chptr && *prog->argv[argc]) {
582 buf++, argc++;
583 globLastArgument(prog, &argc, &argvAlloced);
584 }
585 }
586
587 if (prog->redirections[i].fd == -1) {
588 if (*src == '>')
589 prog->redirections[i].fd = 1;
590 else
591 prog->redirections[i].fd = 0;
592 }
593
594 if (*src++ == '>') {
595 if (*src == '>')
596 prog->redirections[i].type =
597 REDIRECT_APPEND, src++;
598 else
599 prog->redirections[i].type = REDIRECT_OVERWRITE;
600 } else {
601 prog->redirections[i].type = REDIRECT_INPUT;
602 }
603
604 /* This isn't POSIX sh compliant. Oh well. */
605 chptr = src;
606 while (isspace(*chptr))
607 chptr++;
608
609 if (!*chptr) {
610 fprintf(stderr, "file name expected after %c\n", *src);
611 freeJob(job);
612 return 1;
613 }
614
615 prog->redirections[i].filename = buf;
616 while (*chptr && !isspace(*chptr))
617 *buf++ = *chptr++;
618
619 src = chptr - 1; /* we src++ later */
620 prog->argv[argc] = ++buf;
621 break;
622
623 case '|': /* pipe */
624 /* finish this command */
625 if (*prog->argv[argc])
626 argc++;
627 if (!argc) {
628 fprintf(stderr, "empty command in pipe\n");
629 freeJob(job);
630 return 1;
631 }
632 prog->argv[argc] = NULL;
633
634 /* and start the next */
635 job->numProgs++;
636 job->progs = realloc(job->progs,
637 sizeof(*job->progs) * job->numProgs);
638 prog = job->progs + (job->numProgs - 1);
639 prog->numRedirections = 0;
640 prog->redirections = NULL;
641 prog->freeGlob = 0;
642 argc = 0;
643
644 argvAlloced = 5;
645 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
646 prog->argv[0] = ++buf;
647
648 src++;
649 while (*src && isspace(*src))
650 src++;
651
652 if (!*src) {
653 fprintf(stderr, "empty command in pipe\n");
654 return 1;
655 }
656 src--; /* we'll ++ it at the end of the loop */
657
658 break;
659
660 case '&': /* background */
661 *isBg = 1;
662 case ';': /* multiple commands */
663 done = 1;
664 returnCommand = *commandPtr + (src - *commandPtr) + 1;
665 break;
666
667 case '\\':
668 src++;
669 if (!*src) {
670 freeJob(job);
671 fprintf(stderr, "character expected after \\\n");
672 return 1;
673 }
674 if (*src == '*' || *src == '[' || *src == ']'
675 || *src == '?') *buf++ = '\\';
676 /* fallthrough */
677 default:
678 *buf++ = *src;
679 }
680
Erik Andersend75af992000-03-16 08:09:09 +0000681 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000682 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000683
Erik Andersen161220c2000-03-16 08:12:48 +0000684 if (*prog->argv[argc]) {
685 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000686 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000687 }
688 if (!argc) {
689 freeJob(job);
690 return 0;
691 }
692 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000693
Erik Andersen161220c2000-03-16 08:12:48 +0000694 if (!returnCommand) {
695 job->text = malloc(strlen(*commandPtr) + 1);
696 strcpy(job->text, *commandPtr);
697 } else {
698 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen3522eb12000-03-12 23:49:18 +0000699
Erik Andersen161220c2000-03-16 08:12:48 +0000700 count = returnCommand - *commandPtr;
701 job->text = malloc(count + 1);
702 strncpy(job->text, *commandPtr, count);
703 job->text[count] = '\0';
704 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000705
Erik Andersen161220c2000-03-16 08:12:48 +0000706 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000707
Erik Andersend75af992000-03-16 08:09:09 +0000708 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000709}
710
Erik Andersend75af992000-03-16 08:09:09 +0000711static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000712{
Erik Andersen161220c2000-03-16 08:12:48 +0000713 struct job *job;
714 int i;
715 int nextin, nextout;
716 int pipefds[2]; /* pipefd[0] is for reading */
717 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000718
Erik Andersen161220c2000-03-16 08:12:48 +0000719 /* handle built-ins here -- we don't fork() so we can't background
720 these very easily */
721 for (x = bltins; x->cmd; x++) {
722 if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
723 return (x->function(&newJob, jobList));
724 }
Erik Andersend75af992000-03-16 08:09:09 +0000725 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000726
Erik Andersen161220c2000-03-16 08:12:48 +0000727 nextin = 0, nextout = 1;
728 for (i = 0; i < newJob.numProgs; i++) {
729 if ((i + 1) < newJob.numProgs) {
730 pipe(pipefds);
731 nextout = pipefds[1];
732 } else {
733 nextout = 1;
734 }
735
736 if (!(newJob.progs[i].pid = fork())) {
737 signal(SIGTTOU, SIG_DFL);
738
739 if (nextin != 0) {
740 dup2(nextin, 0);
741 close(nextin);
742 }
743
744 if (nextout != 1) {
745 dup2(nextout, 1);
746 close(nextout);
747 }
748
749 /* explicit redirections override pipes */
750 setupRedirections(newJob.progs + i);
751
752 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
753 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
754 strerror(errno));
755 }
756
757 /* put our child in the process group whose leader is the
758 first process in this pipe */
759 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
760
761 if (nextin != 0)
762 close(nextin);
763 if (nextout != 1)
764 close(nextout);
765
766 /* If there isn't another process, nextin is garbage
767 but it doesn't matter */
768 nextin = pipefds[0];
769 }
770
771 newJob.pgrp = newJob.progs[0].pid;
772
773 /* find the ID for the job to use */
774 newJob.jobId = 1;
775 for (job = jobList->head; job; job = job->next)
776 if (job->jobId >= newJob.jobId)
777 newJob.jobId = job->jobId + 1;
778
779 /* add the job to the list of running jobs */
780 if (!jobList->head) {
781 job = jobList->head = malloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000782 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000783 for (job = jobList->head; job->next; job = job->next);
784 job->next = malloc(sizeof(*job));
785 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000786 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000787
Erik Andersen161220c2000-03-16 08:12:48 +0000788 *job = newJob;
789 job->next = NULL;
790 job->runningProgs = job->numProgs;
791 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000792
Erik Andersen161220c2000-03-16 08:12:48 +0000793 if (inBg) {
794 /* we don't wait for background jobs to return -- append it
795 to the list of backgrounded jobs and leave it alone */
Erik Andersen3522eb12000-03-12 23:49:18 +0000796
Erik Andersen161220c2000-03-16 08:12:48 +0000797 printf("[%d] %d\n", job->jobId,
798 newJob.progs[newJob.numProgs - 1].pid);
799 } else {
800 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000801
Erik Andersen161220c2000-03-16 08:12:48 +0000802 /* move the new process group into the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000803
Erik Andersen161220c2000-03-16 08:12:48 +0000804 if (tcsetpgrp(0, newJob.pgrp))
805 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000806 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000807
Erik Andersen161220c2000-03-16 08:12:48 +0000808 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000809}
810
Erik Andersend75af992000-03-16 08:09:09 +0000811static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000812{
Erik Andersen161220c2000-03-16 08:12:48 +0000813 int i;
814 int openfd;
815 int mode = O_RDONLY;
816 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000817
Erik Andersen161220c2000-03-16 08:12:48 +0000818 for (i = 0; i < prog->numRedirections; i++, redir++) {
819 switch (redir->type) {
820 case REDIRECT_INPUT:
821 mode = O_RDONLY;
822 break;
823 case REDIRECT_OVERWRITE:
824 mode = O_RDWR | O_CREAT | O_TRUNC;
825 break;
826 case REDIRECT_APPEND:
827 mode = O_RDWR | O_CREAT | O_APPEND;
828 break;
829 }
830
831 openfd = open(redir->filename, mode, 0666);
832 if (openfd < 0) {
833 /* this could get lost if stderr has been redirected, but
834 bash and ash both lose it as well (though zsh doesn't!) */
835 fprintf(stderr, "error opening %s: %s\n", redir->filename,
836 strerror(errno));
837 return 1;
838 }
839
840 if (openfd != redir->fd) {
841 dup2(openfd, redir->fd);
842 close(openfd);
843 }
Erik Andersend75af992000-03-16 08:09:09 +0000844 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000845
Erik Andersen161220c2000-03-16 08:12:48 +0000846 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000847}
848
849
Erik Andersend75af992000-03-16 08:09:09 +0000850static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000851{
Erik Andersen161220c2000-03-16 08:12:48 +0000852 char *command;
853 char *nextCommand = NULL;
854 struct jobSet jobList = { NULL, NULL };
855 struct job newJob;
856 int i;
857 int status;
858 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000859
Erik Andersen161220c2000-03-16 08:12:48 +0000860 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000861
Erik Andersen161220c2000-03-16 08:12:48 +0000862 /* don't pay any attention to this signal; it just confuses
863 things and isn't really meant for shells anyway */
864 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +0000865
Erik Andersen161220c2000-03-16 08:12:48 +0000866 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000867 if (!jobList.fg) {
868 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000869
Erik Andersend75af992000-03-16 08:09:09 +0000870 /* see if any background processes have exited */
871 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +0000872
Erik Andersend75af992000-03-16 08:09:09 +0000873 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +0000874 if (getCommand(input, command))
875 break;
876 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +0000877 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000878
Erik Andersend75af992000-03-16 08:09:09 +0000879 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +0000880 newJob.numProgs) {
881 runCommand(newJob, &jobList, inBg);
Erik Andersend75af992000-03-16 08:09:09 +0000882 }
883 } else {
884 /* a job is running in the foreground; wait for it */
885 i = 0;
886 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +0000887 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000888
Erik Andersend75af992000-03-16 08:09:09 +0000889 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +0000890
Erik Andersend75af992000-03-16 08:09:09 +0000891 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000892 /* the child exited */
893 jobList.fg->runningProgs--;
894 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000895
Erik Andersen161220c2000-03-16 08:12:48 +0000896 if (!jobList.fg->runningProgs) {
897 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +0000898
Erik Andersen161220c2000-03-16 08:12:48 +0000899 removeJob(&jobList, jobList.fg);
900 jobList.fg = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000901
Erik Andersen161220c2000-03-16 08:12:48 +0000902 /* move the shell to the foreground */
903 if (tcsetpgrp(0, getpid()))
904 perror("tcsetpgrp");
905 }
Erik Andersend75af992000-03-16 08:09:09 +0000906 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000907 /* the child was stopped */
908 jobList.fg->stoppedProgs++;
909 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000910
Erik Andersen161220c2000-03-16 08:12:48 +0000911 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
912 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
913 "Stopped", jobList.fg->text);
914 jobList.fg = NULL;
915 }
Erik Andersend75af992000-03-16 08:09:09 +0000916 }
917
918 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +0000919 /* move the shell to the foreground */
920 if (tcsetpgrp(0, getpid()))
921 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000922 }
923 }
924 }
Erik Andersen161220c2000-03-16 08:12:48 +0000925 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +0000926
Erik Andersen161220c2000-03-16 08:12:48 +0000927 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000928}
929
930
Erik Andersend75af992000-03-16 08:09:09 +0000931int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +0000932{
Erik Andersen161220c2000-03-16 08:12:48 +0000933 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +0000934
Erik Andersen161220c2000-03-16 08:12:48 +0000935 if (argc > 2) {
936 usage(shell_usage);
937 }
938 /* initialize the cwd */
939 getcwd(cwd, sizeof(cwd));
Erik Andersen3522eb12000-03-12 23:49:18 +0000940
941
Erik Andersen161220c2000-03-16 08:12:48 +0000942 //if (argv[0] && argv[0][0] == '-') {
943 // shell_source("/etc/profile");
944 //}
Erik Andersen3522eb12000-03-12 23:49:18 +0000945
Erik Andersen161220c2000-03-16 08:12:48 +0000946 if (argc < 2) {
947 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
948 BB_BT);
949 fprintf(stdout,
950 "Enter 'help' for a list of built-in commands.\n\n");
951 } else {
952 input = fopen(argv[1], "r");
953 if (!input)
954 fatalError("A: Couldn't open file '%s': %s\n", argv[1],
955 strerror(errno));
Erik Andersend75af992000-03-16 08:09:09 +0000956// else
957// fatalError("Got it.\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000958 //exit(shell_source(argv[1]));
Erik Andersend75af992000-03-16 08:09:09 +0000959
Erik Andersen161220c2000-03-16 08:12:48 +0000960 /* Set terminal IO to canonical mode, and save old term settings. */
Erik Andersend75af992000-03-16 08:09:09 +0000961#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen161220c2000-03-16 08:12:48 +0000962 cmdedit_init();
Erik Andersend75af992000-03-16 08:09:09 +0000963#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000964 }
Erik Andersend75af992000-03-16 08:09:09 +0000965
Erik Andersen161220c2000-03-16 08:12:48 +0000966 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +0000967}