blob: 068159697f7d028288ada542e36df83c689c14b3 [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 Andersend75af992000-03-16 08:09:09 +000042#ifdef BB_FEATURE_SH_COMMAND_EDITING
43#include "cmdedit.h"
44#endif
45
Erik Andersen3522eb12000-03-12 23:49:18 +000046#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
47
Erik Andersend75af992000-03-16 08:09:09 +000048
49enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE,
Erik Andersen161220c2000-03-16 08:12:48 +000050 REDIRECT_APPEND
51};
Erik Andersen3522eb12000-03-12 23:49:18 +000052
53struct jobSet {
Erik Andersen161220c2000-03-16 08:12:48 +000054 struct job *head; /* head of list of running jobs */
55 struct job *fg; /* current foreground job */
Erik Andersen3522eb12000-03-12 23:49:18 +000056};
57
58struct redirectionSpecifier {
Erik Andersen161220c2000-03-16 08:12:48 +000059 enum redirectionType type; /* type of redirection */
60 int fd; /* file descriptor being redirected */
61 char *filename; /* file to redirect fd to */
Erik Andersen3522eb12000-03-12 23:49:18 +000062};
63
64struct childProgram {
Erik Andersen161220c2000-03-16 08:12:48 +000065 pid_t pid; /* 0 if exited */
66 char **argv; /* program name and arguments */
67 int numRedirections; /* elements in redirection array */
68 struct redirectionSpecifier *redirections; /* I/O redirections */
69 glob_t globResult; /* result of parameter globbing */
70 int freeGlob; /* should we globfree(&globResult)? */
71 int isStopped; /* is the program currently running? */
Erik Andersen3522eb12000-03-12 23:49:18 +000072};
73
74struct job {
Erik Andersen161220c2000-03-16 08:12:48 +000075 int jobId; /* job number */
76 int numProgs; /* total number of programs in job */
77 int runningProgs; /* number of programs running */
78 char *text; /* name of job */
79 char *cmdBuf; /* buffer various argv's point into */
80 pid_t pgrp; /* process group ID for the job */
81 struct childProgram *progs; /* array of programs in job */
82 struct job *next; /* to track background commands */
83 int stoppedProgs; /* number of programs alive, but stopped */
Erik Andersen3522eb12000-03-12 23:49:18 +000084};
85
86struct builtInCommand {
Erik Andersen161220c2000-03-16 08:12:48 +000087 char *cmd; /* name */
88 char *descr; /* description */
89 char *usage; /* usage */
90 int (*function) (struct job *, struct jobSet * jobList); /* function ptr */
Erik Andersen3522eb12000-03-12 23:49:18 +000091};
92
93/* Some function prototypes */
Erik Andersend75af992000-03-16 08:09:09 +000094static int shell_cd(struct job *cmd, struct jobSet *junk);
95static int shell_env(struct job *dummy, struct jobSet *junk);
96static int shell_exit(struct job *cmd, struct jobSet *junk);
97static int shell_fg_bg(struct job *cmd, struct jobSet *jobList);
98static int shell_help(struct job *cmd, struct jobSet *junk);
99static int shell_jobs(struct job *dummy, struct jobSet *jobList);
100static int shell_pwd(struct job *dummy, struct jobSet *junk);
Erik Andersen6273f652000-03-17 01:12:41 +0000101static int shell_export(struct job *cmd, struct jobSet *junk);
Erik Andersend75af992000-03-16 08:09:09 +0000102static int shell_source(struct job *cmd, struct jobSet *jobList);
103static int shell_unset(struct job *cmd, struct jobSet *junk);
Erik Andersen3522eb12000-03-12 23:49:18 +0000104
Erik Andersend75af992000-03-16 08:09:09 +0000105static void checkJobs(struct jobSet *jobList);
106static int getCommand(FILE * source, char *command);
107static int parseCommand(char **commandPtr, struct job *job, int *isBg);
108static int setupRedirections(struct childProgram *prog);
109static int runCommand(struct job newJob, struct jobSet *jobList, int inBg);
Erik Andersen3522eb12000-03-12 23:49:18 +0000110static int busy_loop(FILE * input);
111
Erik Andersend75af992000-03-16 08:09:09 +0000112
Erik Andersen3522eb12000-03-12 23:49:18 +0000113/* Table of built-in functions */
114static struct builtInCommand bltins[] = {
Erik Andersen161220c2000-03-16 08:12:48 +0000115 {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg},
116 {"cd", "Change working directory", "cd [dir]", shell_cd},
117 //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo},
118 {"env", "Print all environment variables", "env", shell_env},
119 {"exit", "Exit from shell()", "exit", shell_exit},
120 {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg},
121 {"jobs", "Lists the active jobs", "jobs", shell_jobs},
122 {"pwd", "Print current directory", "pwd", shell_pwd},
Erik Andersen6273f652000-03-17 01:12:41 +0000123 {"export", "Set environment variable", "export [VAR=value]", shell_export},
Erik Andersen161220c2000-03-16 08:12:48 +0000124 {"unset", "Unset environment variable", "unset VAR", shell_unset},
125
126 {".", "Source-in and run commands in a file", ". filename",
127 shell_source},
128 {"help", "List shell built-in commands", "help", shell_help},
129 {NULL, NULL, NULL, NULL}
Erik Andersen3522eb12000-03-12 23:49:18 +0000130};
131
132static const char shell_usage[] =
Erik Andersen161220c2000-03-16 08:12:48 +0000133
134 "sh [FILE]...\n\n" "The BusyBox command interpreter (shell).\n\n";
Erik Andersen3522eb12000-03-12 23:49:18 +0000135
136
137static char cwd[1024];
138static char *prompt = "# ";
139
140
Erik Andersen3522eb12000-03-12 23:49:18 +0000141
Erik Andersend75af992000-03-16 08:09:09 +0000142/* built-in 'cd <path>' handler */
143static int shell_cd(struct job *cmd, struct jobSet *junk)
144{
Erik Andersen161220c2000-03-16 08:12:48 +0000145 char *newdir;
Erik Andersend75af992000-03-16 08:09:09 +0000146
Erik Andersen161220c2000-03-16 08:12:48 +0000147 if (!cmd->progs[0].argv[1] == 1)
148 newdir = getenv("HOME");
149 else
150 newdir = cmd->progs[0].argv[1];
151 if (chdir(newdir)) {
152 printf("cd: %s: %s\n", newdir, strerror(errno));
153 return FALSE;
154 }
155 getcwd(cwd, sizeof(cwd));
156
157 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000158}
159
160/* built-in 'env' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000161static int shell_env(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000162{
Erik Andersen161220c2000-03-16 08:12:48 +0000163 char **e;
Erik Andersen3522eb12000-03-12 23:49:18 +0000164
Erik Andersen161220c2000-03-16 08:12:48 +0000165 for (e = environ; *e; e++) {
166 fprintf(stdout, "%s\n", *e);
167 }
168 return (0);
Erik Andersen3522eb12000-03-12 23:49:18 +0000169}
170
171/* built-in 'exit' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000172static int shell_exit(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000173{
Erik Andersen161220c2000-03-16 08:12:48 +0000174 if (!cmd->progs[0].argv[1] == 1)
175 exit TRUE;
176
177 else
178 exit(atoi(cmd->progs[0].argv[1]));
Erik Andersen3522eb12000-03-12 23:49:18 +0000179}
180
181/* built-in 'fg' and 'bg' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000182static int shell_fg_bg(struct job *cmd, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000183{
Erik Andersen161220c2000-03-16 08:12:48 +0000184 int i, jobNum;
Erik Andersen6273f652000-03-17 01:12:41 +0000185 struct job *job=NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000186
Erik Andersen161220c2000-03-16 08:12:48 +0000187 if (!jobList->head) {
188 if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) {
189 fprintf(stderr, "%s: exactly one argument is expected\n",
190 cmd->progs[0].argv[0]);
191 return FALSE;
192 }
193 if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) {
194 fprintf(stderr, "%s: bad argument '%s'\n",
195 cmd->progs[0].argv[0], cmd->progs[0].argv[1]);
196 return FALSE;
197 for (job = jobList->head; job; job = job->next) {
198 if (job->jobId == jobNum) {
199 break;
200 }
201 }
202 }
203 } else {
204 job = jobList->head;
Erik Andersend75af992000-03-16 08:09:09 +0000205 }
Erik Andersen161220c2000-03-16 08:12:48 +0000206
207 if (!job) {
208 fprintf(stderr, "%s: unknown job %d\n",
209 cmd->progs[0].argv[0], jobNum);
210 return FALSE;
Erik Andersend75af992000-03-16 08:09:09 +0000211 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000212
Erik Andersen161220c2000-03-16 08:12:48 +0000213 if (*cmd->progs[0].argv[0] == 'f') {
214 /* Make this job the foreground job */
215 if (tcsetpgrp(0, job->pgrp))
216 perror("tcsetpgrp");
217 jobList->fg = job;
218 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000219
Erik Andersen161220c2000-03-16 08:12:48 +0000220 /* Restart the processes in the job */
221 for (i = 0; i < job->numProgs; i++)
222 job->progs[i].isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000223
Erik Andersen161220c2000-03-16 08:12:48 +0000224 kill(-job->pgrp, SIGCONT);
Erik Andersen3522eb12000-03-12 23:49:18 +0000225
Erik Andersen161220c2000-03-16 08:12:48 +0000226 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000227
Erik Andersen161220c2000-03-16 08:12:48 +0000228 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000229}
230
231/* built-in 'help' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000232static int shell_help(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000233{
Erik Andersen161220c2000-03-16 08:12:48 +0000234 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000235
Erik Andersen161220c2000-03-16 08:12:48 +0000236 fprintf(stdout, "\nBuilt-in commands:\n");
237 fprintf(stdout, "-------------------\n");
238 for (x = bltins; x->cmd; x++) {
239 fprintf(stdout, "%s\t%s\n", x->cmd, x->descr);
240 }
241 fprintf(stdout, "\n\n");
242 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000243}
244
245/* built-in 'jobs' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000246static int shell_jobs(struct job *dummy, struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000247{
Erik Andersen161220c2000-03-16 08:12:48 +0000248 struct job *job;
249 char *statusString;
Erik Andersen3522eb12000-03-12 23:49:18 +0000250
Erik Andersen161220c2000-03-16 08:12:48 +0000251 for (job = jobList->head; job; job = job->next) {
252 if (job->runningProgs == job->stoppedProgs)
253 statusString = "Stopped";
254 else
255 statusString = "Running";
256
257 printf(JOB_STATUS_FORMAT, job->jobId, statusString, job->text);
258 }
259 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000260}
261
262
263/* built-in 'pwd' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000264static int shell_pwd(struct job *dummy, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000265{
Erik Andersen161220c2000-03-16 08:12:48 +0000266 getcwd(cwd, sizeof(cwd));
267 fprintf(stdout, "%s\n", cwd);
268 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000269}
270
Erik Andersen6273f652000-03-17 01:12:41 +0000271/* built-in 'export VAR=value' handler */
272static int shell_export(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000273{
Erik Andersen161220c2000-03-16 08:12:48 +0000274 int res;
Erik Andersen3522eb12000-03-12 23:49:18 +0000275
Erik Andersen161220c2000-03-16 08:12:48 +0000276 if (!cmd->progs[0].argv[1] == 1) {
277 return (shell_env(cmd, junk));
278 }
279 res = putenv(cmd->progs[0].argv[1]);
280 if (res)
Erik Andersen6273f652000-03-17 01:12:41 +0000281 fprintf(stdout, "export: %s\n", strerror(errno));
Erik Andersen161220c2000-03-16 08:12:48 +0000282 return (res);
Erik Andersen3522eb12000-03-12 23:49:18 +0000283}
284
285/* Built-in '.' handler (read-in and execute commands from file) */
Erik Andersend75af992000-03-16 08:09:09 +0000286static int shell_source(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000287{
Erik Andersen161220c2000-03-16 08:12:48 +0000288 FILE *input;
289 int status;
Erik Andersen3522eb12000-03-12 23:49:18 +0000290
Erik Andersen161220c2000-03-16 08:12:48 +0000291 if (!cmd->progs[0].argv[1] == 1)
292 return FALSE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000293
Erik Andersen161220c2000-03-16 08:12:48 +0000294 input = fopen(cmd->progs[0].argv[1], "r");
295 if (!input) {
296 fprintf(stdout, "Couldn't open file '%s'\n",
297 cmd->progs[0].argv[1]);
298 return FALSE;
299 }
Erik Andersend75af992000-03-16 08:09:09 +0000300
Erik Andersen161220c2000-03-16 08:12:48 +0000301 /* Now run the file */
302 status = busy_loop(input);
303 return (status);
Erik Andersen3522eb12000-03-12 23:49:18 +0000304}
305
306/* built-in 'unset VAR' handler */
Erik Andersend75af992000-03-16 08:09:09 +0000307static int shell_unset(struct job *cmd, struct jobSet *junk)
Erik Andersen3522eb12000-03-12 23:49:18 +0000308{
Erik Andersen161220c2000-03-16 08:12:48 +0000309 if (!cmd->progs[0].argv[1] == 1) {
310 fprintf(stdout, "unset: parameter required.\n");
311 return FALSE;
312 }
313 unsetenv(cmd->progs[0].argv[1]);
314 return TRUE;
Erik Andersen3522eb12000-03-12 23:49:18 +0000315}
316
317/* free up all memory from a job */
Erik Andersend75af992000-03-16 08:09:09 +0000318static void freeJob(struct job *cmd)
Erik Andersen3522eb12000-03-12 23:49:18 +0000319{
Erik Andersen161220c2000-03-16 08:12:48 +0000320 int i;
Erik Andersen3522eb12000-03-12 23:49:18 +0000321
Erik Andersen161220c2000-03-16 08:12:48 +0000322 for (i = 0; i < cmd->numProgs; i++) {
323 free(cmd->progs[i].argv);
324 if (cmd->progs[i].redirections)
325 free(cmd->progs[i].redirections);
326 if (cmd->progs[i].freeGlob)
327 globfree(&cmd->progs[i].globResult);
328 }
329 free(cmd->progs);
330 if (cmd->text)
331 free(cmd->text);
332 free(cmd->cmdBuf);
Erik Andersen3522eb12000-03-12 23:49:18 +0000333}
334
335/* remove a job from the jobList */
Erik Andersend75af992000-03-16 08:09:09 +0000336static void removeJob(struct jobSet *jobList, struct job *job)
Erik Andersen3522eb12000-03-12 23:49:18 +0000337{
Erik Andersen161220c2000-03-16 08:12:48 +0000338 struct job *prevJob;
Erik Andersen3522eb12000-03-12 23:49:18 +0000339
Erik Andersen161220c2000-03-16 08:12:48 +0000340 freeJob(job);
341 if (job == jobList->head) {
342 jobList->head = job->next;
343 } else {
344 prevJob = jobList->head;
345 while (prevJob->next != job)
346 prevJob = prevJob->next;
347 prevJob->next = job->next;
348 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000349
Erik Andersen161220c2000-03-16 08:12:48 +0000350 free(job);
Erik Andersen3522eb12000-03-12 23:49:18 +0000351}
352
353/* Checks to see if any background processes have exited -- if they
354 have, figure out why and see if a job has completed */
Erik Andersend75af992000-03-16 08:09:09 +0000355static void checkJobs(struct jobSet *jobList)
Erik Andersen3522eb12000-03-12 23:49:18 +0000356{
Erik Andersen161220c2000-03-16 08:12:48 +0000357 struct job *job;
358 pid_t childpid;
359 int status;
360 int progNum = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000361
Erik Andersen161220c2000-03-16 08:12:48 +0000362 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
363 for (job = jobList->head; job; job = job->next) {
364 progNum = 0;
365 while (progNum < job->numProgs &&
366 job->progs[progNum].pid != childpid) progNum++;
367 if (progNum < job->numProgs)
368 break;
369 }
370
371 if (WIFEXITED(status) || WIFSIGNALED(status)) {
372 /* child exited */
373 job->runningProgs--;
374 job->progs[progNum].pid = 0;
375
376 if (!job->runningProgs) {
377 printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text);
378 removeJob(jobList, job);
379 }
380 } else {
381 /* child stopped */
382 job->stoppedProgs++;
383 job->progs[progNum].isStopped = 1;
384
385 if (job->stoppedProgs == job->numProgs) {
386 printf(JOB_STATUS_FORMAT, job->jobId, "Stopped",
387 job->text);
388 }
389 }
Erik Andersend75af992000-03-16 08:09:09 +0000390 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000391
Erik Andersen161220c2000-03-16 08:12:48 +0000392 if (childpid == -1 && errno != ECHILD)
393 perror("waitpid");
Erik Andersen3522eb12000-03-12 23:49:18 +0000394}
395
Erik Andersend75af992000-03-16 08:09:09 +0000396static int getCommand(FILE * source, char *command)
Erik Andersen3522eb12000-03-12 23:49:18 +0000397{
Erik Andersen161220c2000-03-16 08:12:48 +0000398 if (source == stdin) {
399 fprintf(stdout, "BBSHELL %s %s", cwd, prompt);
400 fflush(stdout);
Erik Andersend75af992000-03-16 08:09:09 +0000401#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen161220c2000-03-16 08:12:48 +0000402 cmdedit_read_input(fileno(stdin), fileno(stdout), command);
403 return 0;
Erik Andersend75af992000-03-16 08:09:09 +0000404#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000405 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000406
Erik Andersen161220c2000-03-16 08:12:48 +0000407 if (!fgets(command, BUFSIZ - 2, source)) {
408 if (source == stdin)
409 printf("\n");
410 return 1;
411 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000412
Erik Andersen161220c2000-03-16 08:12:48 +0000413 /* remove trailing newline */
414 command[strlen(command) - 1] = '\0';
Erik Andersen3522eb12000-03-12 23:49:18 +0000415
Erik Andersen161220c2000-03-16 08:12:48 +0000416 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000417}
418
Erik Andersend75af992000-03-16 08:09:09 +0000419static void globLastArgument(struct childProgram *prog, int *argcPtr,
Erik Andersen161220c2000-03-16 08:12:48 +0000420 int *argcAllocedPtr)
Erik Andersen3522eb12000-03-12 23:49:18 +0000421{
Erik Andersen161220c2000-03-16 08:12:48 +0000422 int argc = *argcPtr;
423 int argcAlloced = *argcAllocedPtr;
424 int rc;
425 int flags;
426 int i;
427 char *src, *dst;
Erik Andersen3522eb12000-03-12 23:49:18 +0000428
Erik Andersen161220c2000-03-16 08:12:48 +0000429 if (argc > 1) { /* cmd->globResult is already initialized */
430 flags = GLOB_APPEND;
431 i = prog->globResult.gl_pathc;
432 } else {
433 prog->freeGlob = 1;
434 flags = 0;
435 i = 0;
Erik Andersend75af992000-03-16 08:09:09 +0000436 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000437
Erik Andersen161220c2000-03-16 08:12:48 +0000438 rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult);
439 if (rc == GLOB_NOSPACE) {
440 fprintf(stderr, "out of space during glob operation\n");
441 return;
442 } else if (rc == GLOB_NOMATCH ||
443 (!rc && (prog->globResult.gl_pathc - i) == 1 &&
444 !strcmp(prog->argv[argc - 1],
445 prog->globResult.gl_pathv[i]))) {
446 /* we need to remove whatever \ quoting is still present */
447 src = dst = prog->argv[argc - 1];
448 while (*src) {
449 if (*src != '\\')
450 *dst++ = *src;
451 src++;
452 }
453 *dst = '\0';
454 } else if (!rc) {
455 argcAlloced += (prog->globResult.gl_pathc - i);
456 prog->argv =
457 realloc(prog->argv, argcAlloced * sizeof(*prog->argv));
458 memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i,
459 sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i));
460 argc += (prog->globResult.gl_pathc - i - 1);
461 }
462
463 *argcAllocedPtr = argcAlloced;
464 *argcPtr = argc;
Erik Andersen3522eb12000-03-12 23:49:18 +0000465}
466
467/* Return cmd->numProgs as 0 if no command is present (e.g. an empty
468 line). If a valid command is found, commandPtr is set to point to
469 the beginning of the next command (if the original command had more
470 then one job associated with it) or NULL if no more commands are
471 present. */
Erik Andersend75af992000-03-16 08:09:09 +0000472static int parseCommand(char **commandPtr, struct job *job, int *isBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000473{
Erik Andersen161220c2000-03-16 08:12:48 +0000474 char *command;
475 char *returnCommand = NULL;
476 char *src, *buf, *chptr;
477 int argc = 0;
478 int done = 0;
479 int argvAlloced;
480 int i;
481 char quote = '\0';
482 int count;
483 struct childProgram *prog;
Erik Andersen3522eb12000-03-12 23:49:18 +0000484
Erik Andersen161220c2000-03-16 08:12:48 +0000485 /* skip leading white space */
486 while (**commandPtr && isspace(**commandPtr))
487 (*commandPtr)++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000488
Erik Andersen161220c2000-03-16 08:12:48 +0000489 /* this handles empty lines or leading '#' characters */
490 if (!**commandPtr || (**commandPtr == '#')) {
491 job->numProgs = 0;
492 *commandPtr = NULL;
493 return 0;
494 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000495
Erik Andersen161220c2000-03-16 08:12:48 +0000496 *isBg = 0;
497 job->numProgs = 1;
498 job->progs = malloc(sizeof(*job->progs));
Erik Andersen3522eb12000-03-12 23:49:18 +0000499
Erik Andersen161220c2000-03-16 08:12:48 +0000500 /* We set the argv elements to point inside of this string. The
501 memory is freed by freeJob().
Erik Andersen3522eb12000-03-12 23:49:18 +0000502
Erik Andersen161220c2000-03-16 08:12:48 +0000503 Getting clean memory relieves us of the task of NULL
504 terminating things and makes the rest of this look a bit
505 cleaner (though it is, admittedly, a tad less efficient) */
506 job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1);
507 job->text = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000508
Erik Andersen161220c2000-03-16 08:12:48 +0000509 prog = job->progs;
510 prog->numRedirections = 0;
511 prog->redirections = NULL;
512 prog->freeGlob = 0;
513 prog->isStopped = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000514
Erik Andersen161220c2000-03-16 08:12:48 +0000515 argvAlloced = 5;
516 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
517 prog->argv[0] = job->cmdBuf;
Erik Andersen3522eb12000-03-12 23:49:18 +0000518
Erik Andersen161220c2000-03-16 08:12:48 +0000519 buf = command;
520 src = *commandPtr;
521 while (*src && !done) {
522 if (quote == *src) {
523 quote = '\0';
524 } else if (quote) {
525 if (*src == '\\') {
526 src++;
527 if (!*src) {
528 fprintf(stderr, "character expected after \\\n");
529 freeJob(job);
530 return 1;
531 }
532
533 /* in shell, "\'" should yield \' */
534 if (*src != quote)
535 *buf++ = '\\';
536 } else if (*src == '*' || *src == '?' || *src == '[' ||
537 *src == ']') *buf++ = '\\';
538 *buf++ = *src;
539 } else if (isspace(*src)) {
540 if (*prog->argv[argc]) {
541 buf++, argc++;
542 /* +1 here leaves room for the NULL which ends argv */
543 if ((argc + 1) == argvAlloced) {
544 argvAlloced += 5;
545 prog->argv = realloc(prog->argv,
546 sizeof(*prog->argv) *
547 argvAlloced);
548 }
549 prog->argv[argc] = buf;
550
551 globLastArgument(prog, &argc, &argvAlloced);
552 }
553 } else
554 switch (*src) {
555 case '"':
556 case '\'':
557 quote = *src;
558 break;
559
560 case '#': /* comment */
561 done = 1;
562 break;
563
564 case '>': /* redirections */
565 case '<':
566 i = prog->numRedirections++;
567 prog->redirections = realloc(prog->redirections,
568 sizeof(*prog->redirections) *
569 (i + 1));
570
571 prog->redirections[i].fd = -1;
572 if (buf != prog->argv[argc]) {
573 /* the stuff before this character may be the file number
574 being redirected */
575 prog->redirections[i].fd =
576 strtol(prog->argv[argc], &chptr, 10);
577
578 if (*chptr && *prog->argv[argc]) {
579 buf++, argc++;
580 globLastArgument(prog, &argc, &argvAlloced);
581 }
582 }
583
584 if (prog->redirections[i].fd == -1) {
585 if (*src == '>')
586 prog->redirections[i].fd = 1;
587 else
588 prog->redirections[i].fd = 0;
589 }
590
591 if (*src++ == '>') {
592 if (*src == '>')
593 prog->redirections[i].type =
594 REDIRECT_APPEND, src++;
595 else
596 prog->redirections[i].type = REDIRECT_OVERWRITE;
597 } else {
598 prog->redirections[i].type = REDIRECT_INPUT;
599 }
600
601 /* This isn't POSIX sh compliant. Oh well. */
602 chptr = src;
603 while (isspace(*chptr))
604 chptr++;
605
606 if (!*chptr) {
607 fprintf(stderr, "file name expected after %c\n", *src);
608 freeJob(job);
609 return 1;
610 }
611
612 prog->redirections[i].filename = buf;
613 while (*chptr && !isspace(*chptr))
614 *buf++ = *chptr++;
615
616 src = chptr - 1; /* we src++ later */
617 prog->argv[argc] = ++buf;
618 break;
619
620 case '|': /* pipe */
621 /* finish this command */
622 if (*prog->argv[argc])
623 argc++;
624 if (!argc) {
625 fprintf(stderr, "empty command in pipe\n");
626 freeJob(job);
627 return 1;
628 }
629 prog->argv[argc] = NULL;
630
631 /* and start the next */
632 job->numProgs++;
633 job->progs = realloc(job->progs,
634 sizeof(*job->progs) * job->numProgs);
635 prog = job->progs + (job->numProgs - 1);
636 prog->numRedirections = 0;
637 prog->redirections = NULL;
638 prog->freeGlob = 0;
639 argc = 0;
640
641 argvAlloced = 5;
642 prog->argv = malloc(sizeof(*prog->argv) * argvAlloced);
643 prog->argv[0] = ++buf;
644
645 src++;
646 while (*src && isspace(*src))
647 src++;
648
649 if (!*src) {
650 fprintf(stderr, "empty command in pipe\n");
651 return 1;
652 }
653 src--; /* we'll ++ it at the end of the loop */
654
655 break;
656
657 case '&': /* background */
658 *isBg = 1;
659 case ';': /* multiple commands */
660 done = 1;
661 returnCommand = *commandPtr + (src - *commandPtr) + 1;
662 break;
663
664 case '\\':
665 src++;
666 if (!*src) {
667 freeJob(job);
668 fprintf(stderr, "character expected after \\\n");
669 return 1;
670 }
671 if (*src == '*' || *src == '[' || *src == ']'
672 || *src == '?') *buf++ = '\\';
673 /* fallthrough */
674 default:
675 *buf++ = *src;
676 }
677
Erik Andersend75af992000-03-16 08:09:09 +0000678 src++;
Erik Andersen161220c2000-03-16 08:12:48 +0000679 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000680
Erik Andersen161220c2000-03-16 08:12:48 +0000681 if (*prog->argv[argc]) {
682 argc++;
Erik Andersend75af992000-03-16 08:09:09 +0000683 globLastArgument(prog, &argc, &argvAlloced);
Erik Andersen161220c2000-03-16 08:12:48 +0000684 }
685 if (!argc) {
686 freeJob(job);
687 return 0;
688 }
689 prog->argv[argc] = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000690
Erik Andersen161220c2000-03-16 08:12:48 +0000691 if (!returnCommand) {
692 job->text = malloc(strlen(*commandPtr) + 1);
693 strcpy(job->text, *commandPtr);
694 } else {
695 /* This leaves any trailing spaces, which is a bit sloppy */
Erik Andersen3522eb12000-03-12 23:49:18 +0000696
Erik Andersen161220c2000-03-16 08:12:48 +0000697 count = returnCommand - *commandPtr;
698 job->text = malloc(count + 1);
699 strncpy(job->text, *commandPtr, count);
700 job->text[count] = '\0';
701 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000702
Erik Andersen161220c2000-03-16 08:12:48 +0000703 *commandPtr = returnCommand;
Erik Andersen3522eb12000-03-12 23:49:18 +0000704
Erik Andersend75af992000-03-16 08:09:09 +0000705 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000706}
707
Erik Andersend75af992000-03-16 08:09:09 +0000708static int runCommand(struct job newJob, struct jobSet *jobList, int inBg)
Erik Andersen3522eb12000-03-12 23:49:18 +0000709{
Erik Andersen161220c2000-03-16 08:12:48 +0000710 struct job *job;
711 int i;
712 int nextin, nextout;
713 int pipefds[2]; /* pipefd[0] is for reading */
714 struct builtInCommand *x;
Erik Andersen3522eb12000-03-12 23:49:18 +0000715
Erik Andersen161220c2000-03-16 08:12:48 +0000716 /* handle built-ins here -- we don't fork() so we can't background
717 these very easily */
718 for (x = bltins; x->cmd; x++) {
719 if (!strcmp(newJob.progs[0].argv[0], x->cmd)) {
720 return (x->function(&newJob, jobList));
721 }
Erik Andersend75af992000-03-16 08:09:09 +0000722 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000723
Erik Andersen161220c2000-03-16 08:12:48 +0000724 nextin = 0, nextout = 1;
725 for (i = 0; i < newJob.numProgs; i++) {
726 if ((i + 1) < newJob.numProgs) {
727 pipe(pipefds);
728 nextout = pipefds[1];
729 } else {
730 nextout = 1;
731 }
732
733 if (!(newJob.progs[i].pid = fork())) {
734 signal(SIGTTOU, SIG_DFL);
735
736 if (nextin != 0) {
737 dup2(nextin, 0);
738 close(nextin);
739 }
740
741 if (nextout != 1) {
742 dup2(nextout, 1);
743 close(nextout);
744 }
745
746 /* explicit redirections override pipes */
747 setupRedirections(newJob.progs + i);
748
749 execvp(newJob.progs[i].argv[0], newJob.progs[i].argv);
750 fatalError("sh: %s: %s\n", newJob.progs[i].argv[0],
751 strerror(errno));
752 }
753
754 /* put our child in the process group whose leader is the
755 first process in this pipe */
756 setpgid(newJob.progs[i].pid, newJob.progs[0].pid);
757
758 if (nextin != 0)
759 close(nextin);
760 if (nextout != 1)
761 close(nextout);
762
763 /* If there isn't another process, nextin is garbage
764 but it doesn't matter */
765 nextin = pipefds[0];
766 }
767
768 newJob.pgrp = newJob.progs[0].pid;
769
770 /* find the ID for the job to use */
771 newJob.jobId = 1;
772 for (job = jobList->head; job; job = job->next)
773 if (job->jobId >= newJob.jobId)
774 newJob.jobId = job->jobId + 1;
775
776 /* add the job to the list of running jobs */
777 if (!jobList->head) {
778 job = jobList->head = malloc(sizeof(*job));
Erik Andersend75af992000-03-16 08:09:09 +0000779 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000780 for (job = jobList->head; job->next; job = job->next);
781 job->next = malloc(sizeof(*job));
782 job = job->next;
Erik Andersend75af992000-03-16 08:09:09 +0000783 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000784
Erik Andersen161220c2000-03-16 08:12:48 +0000785 *job = newJob;
786 job->next = NULL;
787 job->runningProgs = job->numProgs;
788 job->stoppedProgs = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000789
Erik Andersen161220c2000-03-16 08:12:48 +0000790 if (inBg) {
791 /* we don't wait for background jobs to return -- append it
792 to the list of backgrounded jobs and leave it alone */
Erik Andersen3522eb12000-03-12 23:49:18 +0000793
Erik Andersen161220c2000-03-16 08:12:48 +0000794 printf("[%d] %d\n", job->jobId,
795 newJob.progs[newJob.numProgs - 1].pid);
796 } else {
797 jobList->fg = job;
Erik Andersen3522eb12000-03-12 23:49:18 +0000798
Erik Andersen161220c2000-03-16 08:12:48 +0000799 /* move the new process group into the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000800
Erik Andersen161220c2000-03-16 08:12:48 +0000801 if (tcsetpgrp(0, newJob.pgrp))
802 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000803 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000804
Erik Andersen161220c2000-03-16 08:12:48 +0000805 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000806}
807
Erik Andersend75af992000-03-16 08:09:09 +0000808static int setupRedirections(struct childProgram *prog)
Erik Andersen3522eb12000-03-12 23:49:18 +0000809{
Erik Andersen161220c2000-03-16 08:12:48 +0000810 int i;
811 int openfd;
812 int mode = O_RDONLY;
813 struct redirectionSpecifier *redir = prog->redirections;
Erik Andersen3522eb12000-03-12 23:49:18 +0000814
Erik Andersen161220c2000-03-16 08:12:48 +0000815 for (i = 0; i < prog->numRedirections; i++, redir++) {
816 switch (redir->type) {
817 case REDIRECT_INPUT:
818 mode = O_RDONLY;
819 break;
820 case REDIRECT_OVERWRITE:
821 mode = O_RDWR | O_CREAT | O_TRUNC;
822 break;
823 case REDIRECT_APPEND:
824 mode = O_RDWR | O_CREAT | O_APPEND;
825 break;
826 }
827
828 openfd = open(redir->filename, mode, 0666);
829 if (openfd < 0) {
830 /* this could get lost if stderr has been redirected, but
831 bash and ash both lose it as well (though zsh doesn't!) */
832 fprintf(stderr, "error opening %s: %s\n", redir->filename,
833 strerror(errno));
834 return 1;
835 }
836
837 if (openfd != redir->fd) {
838 dup2(openfd, redir->fd);
839 close(openfd);
840 }
Erik Andersend75af992000-03-16 08:09:09 +0000841 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000842
Erik Andersen161220c2000-03-16 08:12:48 +0000843 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000844}
845
846
Erik Andersend75af992000-03-16 08:09:09 +0000847static int busy_loop(FILE * input)
Erik Andersen3522eb12000-03-12 23:49:18 +0000848{
Erik Andersen161220c2000-03-16 08:12:48 +0000849 char *command;
850 char *nextCommand = NULL;
851 struct jobSet jobList = { NULL, NULL };
852 struct job newJob;
853 int i;
854 int status;
855 int inBg;
Erik Andersen3522eb12000-03-12 23:49:18 +0000856
Erik Andersen161220c2000-03-16 08:12:48 +0000857 command = (char *) calloc(BUFSIZ, sizeof(char));
Erik Andersend75af992000-03-16 08:09:09 +0000858
Erik Andersen161220c2000-03-16 08:12:48 +0000859 /* don't pay any attention to this signal; it just confuses
860 things and isn't really meant for shells anyway */
861 signal(SIGTTOU, SIG_IGN);
Erik Andersend75af992000-03-16 08:09:09 +0000862
Erik Andersen161220c2000-03-16 08:12:48 +0000863 while (1) {
Erik Andersend75af992000-03-16 08:09:09 +0000864 if (!jobList.fg) {
865 /* no job is in the foreground */
Erik Andersen3522eb12000-03-12 23:49:18 +0000866
Erik Andersend75af992000-03-16 08:09:09 +0000867 /* see if any background processes have exited */
868 checkJobs(&jobList);
Erik Andersen3522eb12000-03-12 23:49:18 +0000869
Erik Andersend75af992000-03-16 08:09:09 +0000870 if (!nextCommand) {
Erik Andersen161220c2000-03-16 08:12:48 +0000871 if (getCommand(input, command))
872 break;
873 nextCommand = command;
Erik Andersend75af992000-03-16 08:09:09 +0000874 }
Erik Andersen3522eb12000-03-12 23:49:18 +0000875
Erik Andersend75af992000-03-16 08:09:09 +0000876 if (!parseCommand(&nextCommand, &newJob, &inBg) &&
Erik Andersen161220c2000-03-16 08:12:48 +0000877 newJob.numProgs) {
878 runCommand(newJob, &jobList, inBg);
Erik Andersend75af992000-03-16 08:09:09 +0000879 }
880 } else {
881 /* a job is running in the foreground; wait for it */
882 i = 0;
883 while (!jobList.fg->progs[i].pid ||
Erik Andersen161220c2000-03-16 08:12:48 +0000884 jobList.fg->progs[i].isStopped) i++;
Erik Andersen3522eb12000-03-12 23:49:18 +0000885
Erik Andersend75af992000-03-16 08:09:09 +0000886 waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED);
Erik Andersen3522eb12000-03-12 23:49:18 +0000887
Erik Andersend75af992000-03-16 08:09:09 +0000888 if (WIFEXITED(status) || WIFSIGNALED(status)) {
Erik Andersen161220c2000-03-16 08:12:48 +0000889 /* the child exited */
890 jobList.fg->runningProgs--;
891 jobList.fg->progs[i].pid = 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000892
Erik Andersen161220c2000-03-16 08:12:48 +0000893 if (!jobList.fg->runningProgs) {
894 /* child exited */
Erik Andersen3522eb12000-03-12 23:49:18 +0000895
Erik Andersen161220c2000-03-16 08:12:48 +0000896 removeJob(&jobList, jobList.fg);
897 jobList.fg = NULL;
Erik Andersen3522eb12000-03-12 23:49:18 +0000898
Erik Andersen161220c2000-03-16 08:12:48 +0000899 /* move the shell to the foreground */
900 if (tcsetpgrp(0, getpid()))
901 perror("tcsetpgrp");
902 }
Erik Andersend75af992000-03-16 08:09:09 +0000903 } else {
Erik Andersen161220c2000-03-16 08:12:48 +0000904 /* the child was stopped */
905 jobList.fg->stoppedProgs++;
906 jobList.fg->progs[i].isStopped = 1;
Erik Andersen3522eb12000-03-12 23:49:18 +0000907
Erik Andersen161220c2000-03-16 08:12:48 +0000908 if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) {
909 printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId,
910 "Stopped", jobList.fg->text);
911 jobList.fg = NULL;
912 }
Erik Andersend75af992000-03-16 08:09:09 +0000913 }
914
915 if (!jobList.fg) {
Erik Andersen161220c2000-03-16 08:12:48 +0000916 /* move the shell to the foreground */
917 if (tcsetpgrp(0, getpid()))
918 perror("tcsetpgrp");
Erik Andersend75af992000-03-16 08:09:09 +0000919 }
920 }
921 }
Erik Andersen161220c2000-03-16 08:12:48 +0000922 free(command);
Erik Andersen3522eb12000-03-12 23:49:18 +0000923
Erik Andersen161220c2000-03-16 08:12:48 +0000924 return 0;
Erik Andersen3522eb12000-03-12 23:49:18 +0000925}
926
927
Erik Andersend75af992000-03-16 08:09:09 +0000928int shell_main(int argc, char **argv)
Erik Andersen3522eb12000-03-12 23:49:18 +0000929{
Erik Andersen161220c2000-03-16 08:12:48 +0000930 FILE *input = stdin;
Erik Andersen3522eb12000-03-12 23:49:18 +0000931
Erik Andersen161220c2000-03-16 08:12:48 +0000932 if (argc > 2) {
933 usage(shell_usage);
934 }
935 /* initialize the cwd */
936 getcwd(cwd, sizeof(cwd));
Erik Andersen3522eb12000-03-12 23:49:18 +0000937
938
Erik Andersen161220c2000-03-16 08:12:48 +0000939 //if (argv[0] && argv[0][0] == '-') {
940 // shell_source("/etc/profile");
941 //}
Erik Andersen3522eb12000-03-12 23:49:18 +0000942
Erik Andersen161220c2000-03-16 08:12:48 +0000943 if (argc < 2) {
944 fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER,
945 BB_BT);
946 fprintf(stdout,
947 "Enter 'help' for a list of built-in commands.\n\n");
948 } else {
949 input = fopen(argv[1], "r");
950 if (!input)
951 fatalError("A: Couldn't open file '%s': %s\n", argv[1],
952 strerror(errno));
Erik Andersend75af992000-03-16 08:09:09 +0000953// else
954// fatalError("Got it.\n");
Erik Andersen161220c2000-03-16 08:12:48 +0000955 //exit(shell_source(argv[1]));
Erik Andersend75af992000-03-16 08:09:09 +0000956
Erik Andersen161220c2000-03-16 08:12:48 +0000957 /* Set terminal IO to canonical mode, and save old term settings. */
Erik Andersend75af992000-03-16 08:09:09 +0000958#ifdef BB_FEATURE_SH_COMMAND_EDITING
Erik Andersen161220c2000-03-16 08:12:48 +0000959 cmdedit_init();
Erik Andersend75af992000-03-16 08:09:09 +0000960#endif
Erik Andersen161220c2000-03-16 08:12:48 +0000961 }
Erik Andersend75af992000-03-16 08:09:09 +0000962
Erik Andersen161220c2000-03-16 08:12:48 +0000963 return (busy_loop(input));
Erik Andersen3522eb12000-03-12 23:49:18 +0000964}