diff options
author | Erik Andersen <andersen@codepoet.org> | 2000-03-12 23:49:18 +0000 |
---|---|---|
committer | Erik Andersen <andersen@codepoet.org> | 2000-03-12 23:49:18 +0000 |
commit | 3522eb1ab3a4f1bcbf2c0f0eed387733b29c9e95 (patch) | |
tree | 5670f3711de435e2e159ff9ceebda1bebe33d38c | |
parent | 269bba25149289201055c9448e8c64fc05dbf89d (diff) | |
download | busybox-w32-3522eb1ab3a4f1bcbf2c0f0eed387733b29c9e95.tar.gz busybox-w32-3522eb1ab3a4f1bcbf2c0f0eed387733b29c9e95.tar.bz2 busybox-w32-3522eb1ab3a4f1bcbf2c0f0eed387733b29c9e95.zip |
First pass at writing a shell for busybox.
Works fine, full job control support, etc.
No syntax yet (if, then, while, etc). Handles
pipes and & processes fine.
TODO: add command editing, add syntax suport.
-Erik
-rw-r--r-- | applets/busybox.c | 9 | ||||
-rw-r--r-- | busybox.c | 9 | ||||
-rw-r--r-- | busybox.def.h | 1 | ||||
-rw-r--r-- | internal.h | 1 | ||||
-rw-r--r-- | lash.c | 915 | ||||
-rw-r--r-- | sh.c | 915 | ||||
-rw-r--r-- | shell/lash.c | 915 | ||||
-rw-r--r-- | utility.c | 4 |
8 files changed, 2761 insertions, 8 deletions
diff --git a/applets/busybox.c b/applets/busybox.c index cea191cb3..9aa46eaae 100644 --- a/applets/busybox.c +++ b/applets/busybox.c | |||
@@ -223,13 +223,16 @@ static const struct Applet applets[] = { | |||
223 | #ifdef BB_RMMOD //sbin | 223 | #ifdef BB_RMMOD //sbin |
224 | {"rmmod", rmmod_main}, | 224 | {"rmmod", rmmod_main}, |
225 | #endif | 225 | #endif |
226 | #ifdef BB_SED //bin | ||
227 | {"sed", sed_main}, | ||
228 | #endif | ||
229 | #ifdef BB_SH //bin | ||
230 | {"sh", shell_main}, | ||
231 | #endif | ||
226 | #ifdef BB_SFDISK //sbin | 232 | #ifdef BB_SFDISK //sbin |
227 | {"fdisk", sfdisk_main}, | 233 | {"fdisk", sfdisk_main}, |
228 | {"sfdisk", sfdisk_main}, | 234 | {"sfdisk", sfdisk_main}, |
229 | #endif | 235 | #endif |
230 | #ifdef BB_SED //bin | ||
231 | {"sed", sed_main}, | ||
232 | #endif | ||
233 | #ifdef BB_SLEEP //bin | 236 | #ifdef BB_SLEEP //bin |
234 | {"sleep", sleep_main}, | 237 | {"sleep", sleep_main}, |
235 | #endif | 238 | #endif |
@@ -223,13 +223,16 @@ static const struct Applet applets[] = { | |||
223 | #ifdef BB_RMMOD //sbin | 223 | #ifdef BB_RMMOD //sbin |
224 | {"rmmod", rmmod_main}, | 224 | {"rmmod", rmmod_main}, |
225 | #endif | 225 | #endif |
226 | #ifdef BB_SED //bin | ||
227 | {"sed", sed_main}, | ||
228 | #endif | ||
229 | #ifdef BB_SH //bin | ||
230 | {"sh", shell_main}, | ||
231 | #endif | ||
226 | #ifdef BB_SFDISK //sbin | 232 | #ifdef BB_SFDISK //sbin |
227 | {"fdisk", sfdisk_main}, | 233 | {"fdisk", sfdisk_main}, |
228 | {"sfdisk", sfdisk_main}, | 234 | {"sfdisk", sfdisk_main}, |
229 | #endif | 235 | #endif |
230 | #ifdef BB_SED //bin | ||
231 | {"sed", sed_main}, | ||
232 | #endif | ||
233 | #ifdef BB_SLEEP //bin | 236 | #ifdef BB_SLEEP //bin |
234 | {"sleep", sleep_main}, | 237 | {"sleep", sleep_main}, |
235 | #endif | 238 | #endif |
diff --git a/busybox.def.h b/busybox.def.h index fdbb6fed6..0caa57380 100644 --- a/busybox.def.h +++ b/busybox.def.h | |||
@@ -77,6 +77,7 @@ | |||
77 | //#define BB_RMMOD | 77 | //#define BB_RMMOD |
78 | #define BB_SED | 78 | #define BB_SED |
79 | //#define BB_SFDISK | 79 | //#define BB_SFDISK |
80 | #define BB_SH | ||
80 | #define BB_SLEEP | 81 | #define BB_SLEEP |
81 | #define BB_SORT | 82 | #define BB_SORT |
82 | #define BB_SWAPONOFF | 83 | #define BB_SWAPONOFF |
diff --git a/internal.h b/internal.h index 2e2556d81..e96eebba9 100644 --- a/internal.h +++ b/internal.h | |||
@@ -125,6 +125,7 @@ extern int rmdir_main(int argc, char **argv); | |||
125 | extern int rmmod_main(int argc, char** argv); | 125 | extern int rmmod_main(int argc, char** argv); |
126 | extern int sed_main(int argc, char** argv); | 126 | extern int sed_main(int argc, char** argv); |
127 | extern int sfdisk_main(int argc, char** argv); | 127 | extern int sfdisk_main(int argc, char** argv); |
128 | extern int shell_main(int argc, char** argv); | ||
128 | extern int sleep_main(int argc, char** argv); | 129 | extern int sleep_main(int argc, char** argv); |
129 | extern int sort_main(int argc, char** argv); | 130 | extern int sort_main(int argc, char** argv); |
130 | extern int swap_on_off_main(int argc, char** argv); | 131 | extern int swap_on_off_main(int argc, char** argv); |
@@ -0,0 +1,915 @@ | |||
1 | /* 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 | |||
42 | #define MAX_COMMAND_LEN 250 /* max length of a single command | ||
43 | string */ | ||
44 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | ||
45 | |||
46 | enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND }; | ||
47 | |||
48 | struct jobSet { | ||
49 | struct job * head; /* head of list of running jobs */ | ||
50 | struct job * fg; /* current foreground job */ | ||
51 | }; | ||
52 | |||
53 | struct redirectionSpecifier { | ||
54 | enum redirectionType type; /* type of redirection */ | ||
55 | int fd; /* file descriptor being redirected */ | ||
56 | char * filename; /* file to redirect fd to */ | ||
57 | }; | ||
58 | |||
59 | struct childProgram { | ||
60 | pid_t pid; /* 0 if exited */ | ||
61 | char ** argv; /* program name and arguments */ | ||
62 | int numRedirections; /* elements in redirection array */ | ||
63 | struct redirectionSpecifier * redirections; /* I/O redirections */ | ||
64 | glob_t globResult; /* result of parameter globbing */ | ||
65 | int freeGlob; /* should we globfree(&globResult)? */ | ||
66 | int isStopped; /* is the program currently running? */ | ||
67 | }; | ||
68 | |||
69 | struct job { | ||
70 | int jobId; /* job number */ | ||
71 | int numProgs; /* total number of programs in job */ | ||
72 | int runningProgs; /* number of programs running */ | ||
73 | char * text; /* name of job */ | ||
74 | char * cmdBuf; /* buffer various argv's point into */ | ||
75 | pid_t pgrp; /* process group ID for the job */ | ||
76 | struct childProgram * progs; /* array of programs in job */ | ||
77 | struct job * next; /* to track background commands */ | ||
78 | int stoppedProgs; /* number of programs alive, but stopped */ | ||
79 | }; | ||
80 | |||
81 | struct builtInCommand { | ||
82 | char *cmd; /* name */ | ||
83 | char *descr; /* description */ | ||
84 | char *usage; /* usage */ | ||
85 | int (*function) (struct job *, struct jobSet * jobList); /* function ptr */ | ||
86 | }; | ||
87 | |||
88 | /* Some function prototypes */ | ||
89 | static int shell_cd(struct job* cmd, struct jobSet* junk); | ||
90 | static int shell_env(struct job* dummy, struct jobSet* junk); | ||
91 | static int shell_exit(struct job* cmd, struct jobSet* junk); | ||
92 | static int shell_fg_bg(struct job* cmd, struct jobSet* jobList); | ||
93 | static int shell_help(struct job* cmd, struct jobSet* junk); | ||
94 | static int shell_jobs(struct job* dummy, struct jobSet* jobList); | ||
95 | static int shell_pwd(struct job* dummy, struct jobSet* junk); | ||
96 | static int shell_set(struct job* cmd, struct jobSet* junk); | ||
97 | static int shell_source(struct job* cmd, struct jobSet* jobList); | ||
98 | static int shell_unset(struct job* cmd, struct jobSet* junk); | ||
99 | |||
100 | static void checkJobs(struct jobSet * jobList); | ||
101 | static int getCommand(FILE * source, char * command); | ||
102 | static int parseCommand(char ** commandPtr, struct job * job, int * isBg); | ||
103 | static int setupRedirections(struct childProgram * prog); | ||
104 | static int runCommand(struct job newJob, struct jobSet * jobList, int inBg); | ||
105 | static int busy_loop(FILE * input); | ||
106 | |||
107 | |||
108 | /* Table of built-in functions */ | ||
109 | static struct builtInCommand bltins[] = { | ||
110 | {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg}, | ||
111 | {"cd", "Change working directory", "cd [dir]", shell_cd}, | ||
112 | {"env", "Print all environment variables", "env", shell_env}, | ||
113 | {"exit", "Exit from shell()", "exit", shell_exit}, | ||
114 | {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, | ||
115 | {"jobs", "Lists the active jobs", "jobs", shell_jobs}, | ||
116 | {"pwd", "Print current directory", "pwd", shell_pwd}, | ||
117 | {"set", "Set environment variable", "set [VAR=value]", shell_set}, | ||
118 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, | ||
119 | //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo}, | ||
120 | {".", "Source-in and run commands in a file", ". filename", shell_source}, | ||
121 | {"help", "List shell built-in commands", "help", shell_help}, | ||
122 | {NULL, NULL, NULL, NULL} | ||
123 | }; | ||
124 | |||
125 | static const char shell_usage[] = | ||
126 | "sh [FILE]...\n\n" | ||
127 | "The BusyBox command interpreter (shell).\n\n"; | ||
128 | |||
129 | |||
130 | static char cwd[1024]; | ||
131 | static char *prompt = "# "; | ||
132 | |||
133 | |||
134 | /* built-in 'cd <path>' handler */ | ||
135 | static int shell_cd(struct job* cmd, struct jobSet* junk) | ||
136 | { | ||
137 | char *newdir; | ||
138 | if (!cmd->progs[0].argv[1] == 1) | ||
139 | newdir = getenv("HOME"); | ||
140 | else | ||
141 | newdir = cmd->progs[0].argv[1]; | ||
142 | if (chdir(newdir)) { | ||
143 | printf("cd: %s: %s\n", newdir, strerror(errno)); | ||
144 | return FALSE; | ||
145 | } | ||
146 | getcwd(cwd, sizeof(cwd)); | ||
147 | |||
148 | return TRUE; | ||
149 | } | ||
150 | |||
151 | /* built-in 'env' handler */ | ||
152 | static int shell_env(struct job* dummy, struct jobSet* junk) | ||
153 | { | ||
154 | char **e; | ||
155 | |||
156 | for (e = environ ; *e ; e++) { | ||
157 | fprintf(stdout, "%s\n", *e); | ||
158 | } | ||
159 | return (0); | ||
160 | } | ||
161 | |||
162 | /* built-in 'exit' handler */ | ||
163 | static int shell_exit(struct job* cmd, struct jobSet* junk) | ||
164 | { | ||
165 | if (!cmd->progs[0].argv[1] == 1) | ||
166 | exit TRUE; | ||
167 | else | ||
168 | exit(atoi(cmd->progs[0].argv[1])); | ||
169 | } | ||
170 | |||
171 | /* built-in 'fg' and 'bg' handler */ | ||
172 | static int shell_fg_bg(struct job* cmd, struct jobSet* jobList) | ||
173 | { | ||
174 | int i, jobNum; | ||
175 | struct job* job; | ||
176 | |||
177 | if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { | ||
178 | fprintf(stderr, "%s: exactly one argument is expected\n", | ||
179 | cmd->progs[0].argv[0]); | ||
180 | return FALSE; | ||
181 | } | ||
182 | |||
183 | if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) { | ||
184 | fprintf(stderr, "%s: bad argument '%s'\n", | ||
185 | cmd->progs[0].argv[0], cmd->progs[0].argv[1]); | ||
186 | return FALSE; | ||
187 | } | ||
188 | |||
189 | for (job = jobList->head; job; job = job->next) | ||
190 | if (job->jobId == jobNum) break; | ||
191 | |||
192 | if (!job) { | ||
193 | fprintf(stderr, "%s: unknown job %d\n", | ||
194 | cmd->progs[0].argv[0], jobNum); | ||
195 | return FALSE; | ||
196 | } | ||
197 | |||
198 | if (*cmd->progs[0].argv[0] == 'f') { | ||
199 | /* Make this job the foreground job */ | ||
200 | |||
201 | if (tcsetpgrp(0, job->pgrp)) | ||
202 | perror("tcsetpgrp"); | ||
203 | jobList->fg = job; | ||
204 | } | ||
205 | |||
206 | /* Restart the processes in the job */ | ||
207 | for (i = 0; i < job->numProgs; i++) | ||
208 | job->progs[i].isStopped = 0; | ||
209 | |||
210 | kill(-job->pgrp, SIGCONT); | ||
211 | |||
212 | job->stoppedProgs = 0; | ||
213 | |||
214 | return TRUE; | ||
215 | } | ||
216 | |||
217 | /* built-in 'help' handler */ | ||
218 | static int shell_help(struct job* cmd, struct jobSet* junk) | ||
219 | { | ||
220 | struct builtInCommand *x; | ||
221 | |||
222 | fprintf(stdout, "\nBuilt-in commands:\n"); | ||
223 | fprintf(stdout, "-------------------\n"); | ||
224 | for ( x=bltins; x->cmd; x++) { | ||
225 | fprintf(stdout, "%s\t%s\n", x->cmd, x->descr); | ||
226 | } | ||
227 | fprintf(stdout, "\n\n"); | ||
228 | return TRUE; | ||
229 | } | ||
230 | |||
231 | /* built-in 'jobs' handler */ | ||
232 | static int shell_jobs(struct job* dummy, struct jobSet* jobList) | ||
233 | { | ||
234 | struct job * job; | ||
235 | char * statusString; | ||
236 | for (job = jobList->head; job; job = job->next) { | ||
237 | if (job->runningProgs == job->stoppedProgs) | ||
238 | statusString = "Stopped"; | ||
239 | else | ||
240 | statusString = "Running"; | ||
241 | |||
242 | printf(JOB_STATUS_FORMAT, job->jobId, statusString, | ||
243 | job->text); | ||
244 | } | ||
245 | return TRUE; | ||
246 | } | ||
247 | |||
248 | |||
249 | /* built-in 'pwd' handler */ | ||
250 | static int shell_pwd(struct job* dummy, struct jobSet* junk) | ||
251 | { | ||
252 | getcwd(cwd, sizeof(cwd)); | ||
253 | fprintf(stdout, "%s\n", cwd); | ||
254 | return TRUE; | ||
255 | } | ||
256 | |||
257 | /* built-in 'set VAR=value' handler */ | ||
258 | static int shell_set(struct job* cmd, struct jobSet* junk) | ||
259 | { | ||
260 | int res; | ||
261 | |||
262 | if (!cmd->progs[0].argv[1] == 1) { | ||
263 | return (shell_env(cmd, junk)); | ||
264 | } | ||
265 | res = putenv(cmd->progs[0].argv[1]); | ||
266 | if (res) | ||
267 | fprintf(stdout, "set: %s\n", strerror(errno)); | ||
268 | return (res); | ||
269 | } | ||
270 | |||
271 | /* Built-in '.' handler (read-in and execute commands from file) */ | ||
272 | static int shell_source(struct job* cmd, struct jobSet* junk) | ||
273 | { | ||
274 | FILE *input; | ||
275 | int status; | ||
276 | |||
277 | if (!cmd->progs[0].argv[1] == 1) | ||
278 | return FALSE; | ||
279 | |||
280 | input = fopen(cmd->progs[0].argv[1], "r"); | ||
281 | if (!input) { | ||
282 | fprintf(stdout, "Couldn't open file '%s'\n", cmd->progs[0].argv[1]); | ||
283 | return FALSE; | ||
284 | } | ||
285 | |||
286 | /* Now run the file */ | ||
287 | status = busy_loop(input); | ||
288 | return (status); | ||
289 | } | ||
290 | |||
291 | /* built-in 'unset VAR' handler */ | ||
292 | static int shell_unset(struct job* cmd, struct jobSet* junk) | ||
293 | { | ||
294 | if (!cmd->progs[0].argv[1] == 1) { | ||
295 | fprintf(stdout, "unset: parameter required.\n"); | ||
296 | return FALSE; | ||
297 | } | ||
298 | unsetenv(cmd->progs[0].argv[1]); | ||
299 | return TRUE; | ||
300 | } | ||
301 | |||
302 | /* free up all memory from a job */ | ||
303 | static void freeJob(struct job * cmd) | ||
304 | { | ||
305 | int i; | ||
306 | |||
307 | for (i = 0; i < cmd->numProgs; i++) { | ||
308 | free(cmd->progs[i].argv); | ||
309 | if (cmd->progs[i].redirections) free(cmd->progs[i].redirections); | ||
310 | if (cmd->progs[i].freeGlob) globfree(&cmd->progs[i].globResult); | ||
311 | } | ||
312 | free(cmd->progs); | ||
313 | if (cmd->text) free(cmd->text); | ||
314 | free(cmd->cmdBuf); | ||
315 | } | ||
316 | |||
317 | /* remove a job from the jobList */ | ||
318 | static void removeJob(struct jobSet * jobList, struct job * job) | ||
319 | { | ||
320 | struct job * prevJob; | ||
321 | |||
322 | freeJob(job); | ||
323 | if (job == jobList->head) { | ||
324 | jobList->head = job->next; | ||
325 | } else { | ||
326 | prevJob = jobList->head; | ||
327 | while (prevJob->next != job) prevJob = prevJob->next; | ||
328 | prevJob->next = job->next; | ||
329 | } | ||
330 | |||
331 | free(job); | ||
332 | } | ||
333 | |||
334 | /* Checks to see if any background processes have exited -- if they | ||
335 | have, figure out why and see if a job has completed */ | ||
336 | static void checkJobs(struct jobSet * jobList) | ||
337 | { | ||
338 | struct job * job; | ||
339 | pid_t childpid; | ||
340 | int status; | ||
341 | int progNum=0; | ||
342 | |||
343 | while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { | ||
344 | for (job = jobList->head; job; job = job->next) { | ||
345 | progNum = 0; | ||
346 | while (progNum < job->numProgs && | ||
347 | job->progs[progNum].pid != childpid) | ||
348 | progNum++; | ||
349 | if (progNum < job->numProgs) break; | ||
350 | } | ||
351 | |||
352 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | ||
353 | /* child exited */ | ||
354 | job->runningProgs--; | ||
355 | job->progs[progNum].pid = 0; | ||
356 | |||
357 | if (!job->runningProgs) { | ||
358 | printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text); | ||
359 | removeJob(jobList, job); | ||
360 | } | ||
361 | } else { | ||
362 | /* child stopped */ | ||
363 | job->stoppedProgs++; | ||
364 | job->progs[progNum].isStopped = 1; | ||
365 | |||
366 | if (job->stoppedProgs == job->numProgs) { | ||
367 | printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", job->text); | ||
368 | } | ||
369 | } | ||
370 | } | ||
371 | |||
372 | if (childpid == -1 && errno != ECHILD) | ||
373 | perror("waitpid"); | ||
374 | } | ||
375 | |||
376 | static int getCommand(FILE * source, char * command) | ||
377 | { | ||
378 | if (source == stdin) { | ||
379 | fprintf(stdout, "%s %s", cwd, prompt); | ||
380 | fflush(stdout); | ||
381 | } | ||
382 | |||
383 | if (!fgets(command, MAX_COMMAND_LEN, source)) { | ||
384 | if (source == stdin) printf("\n"); | ||
385 | return 1; | ||
386 | } | ||
387 | |||
388 | /* remove trailing newline */ | ||
389 | command[strlen(command) - 1] = '\0'; | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static void globLastArgument(struct childProgram * prog, int * argcPtr, | ||
395 | int * argcAllocedPtr) | ||
396 | { | ||
397 | int argc = *argcPtr; | ||
398 | int argcAlloced = *argcAllocedPtr; | ||
399 | int rc; | ||
400 | int flags; | ||
401 | int i; | ||
402 | char * src, * dst; | ||
403 | |||
404 | if (argc > 1) { /* cmd->globResult is already initialized */ | ||
405 | flags = GLOB_APPEND; | ||
406 | i = prog->globResult.gl_pathc; | ||
407 | } else { | ||
408 | prog->freeGlob = 1; | ||
409 | flags = 0; | ||
410 | i = 0; | ||
411 | } | ||
412 | |||
413 | rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); | ||
414 | if (rc == GLOB_NOSPACE) { | ||
415 | fprintf(stderr, "out of space during glob operation\n"); | ||
416 | return; | ||
417 | } else if (rc == GLOB_NOMATCH || | ||
418 | (!rc && (prog->globResult.gl_pathc - i) == 1 && | ||
419 | !strcmp(prog->argv[argc - 1], | ||
420 | prog->globResult.gl_pathv[i]))) { | ||
421 | /* we need to remove whatever \ quoting is still present */ | ||
422 | src = dst = prog->argv[argc - 1]; | ||
423 | while (*src) { | ||
424 | if (*src != '\\') *dst++ = *src; | ||
425 | src++; | ||
426 | } | ||
427 | *dst = '\0'; | ||
428 | } else if (!rc) { | ||
429 | argcAlloced += (prog->globResult.gl_pathc - i); | ||
430 | prog->argv = realloc(prog->argv, argcAlloced * sizeof(*prog->argv)); | ||
431 | memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i, | ||
432 | sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i)); | ||
433 | argc += (prog->globResult.gl_pathc - i - 1); | ||
434 | } | ||
435 | |||
436 | *argcAllocedPtr = argcAlloced; | ||
437 | *argcPtr = argc; | ||
438 | } | ||
439 | |||
440 | /* Return cmd->numProgs as 0 if no command is present (e.g. an empty | ||
441 | line). If a valid command is found, commandPtr is set to point to | ||
442 | the beginning of the next command (if the original command had more | ||
443 | then one job associated with it) or NULL if no more commands are | ||
444 | present. */ | ||
445 | static int parseCommand(char ** commandPtr, struct job * job, int * isBg) | ||
446 | { | ||
447 | char * command; | ||
448 | char * returnCommand = NULL; | ||
449 | char * src, * buf, * chptr; | ||
450 | int argc = 0; | ||
451 | int done = 0; | ||
452 | int argvAlloced; | ||
453 | int i; | ||
454 | char quote = '\0'; | ||
455 | int count; | ||
456 | struct childProgram * prog; | ||
457 | |||
458 | /* skip leading white space */ | ||
459 | while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++; | ||
460 | |||
461 | /* this handles empty lines or leading '#' characters */ | ||
462 | if (!**commandPtr || (**commandPtr=='#')) { | ||
463 | job->numProgs = 0; | ||
464 | *commandPtr = NULL; | ||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | *isBg = 0; | ||
469 | job->numProgs = 1; | ||
470 | job->progs = malloc(sizeof(*job->progs)); | ||
471 | |||
472 | /* We set the argv elements to point inside of this string. The | ||
473 | memory is freed by freeJob(). | ||
474 | |||
475 | Getting clean memory relieves us of the task of NULL | ||
476 | terminating things and makes the rest of this look a bit | ||
477 | cleaner (though it is, admittedly, a tad less efficient) */ | ||
478 | job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1); | ||
479 | job->text = NULL; | ||
480 | |||
481 | prog = job->progs; | ||
482 | prog->numRedirections = 0; | ||
483 | prog->redirections = NULL; | ||
484 | prog->freeGlob = 0; | ||
485 | prog->isStopped = 0; | ||
486 | |||
487 | argvAlloced = 5; | ||
488 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | ||
489 | prog->argv[0] = job->cmdBuf; | ||
490 | |||
491 | buf = command; | ||
492 | src = *commandPtr; | ||
493 | while (*src && !done) { | ||
494 | if (quote == *src) { | ||
495 | quote = '\0'; | ||
496 | } else if (quote) { | ||
497 | if (*src == '\\') { | ||
498 | src++; | ||
499 | if (!*src) { | ||
500 | fprintf(stderr, "character expected after \\\n"); | ||
501 | freeJob(job); | ||
502 | return 1; | ||
503 | } | ||
504 | |||
505 | /* in shell, "\'" should yield \' */ | ||
506 | if (*src != quote) *buf++ = '\\'; | ||
507 | } else if (*src == '*' || *src == '?' || *src == '[' || | ||
508 | *src == ']') | ||
509 | *buf++ = '\\'; | ||
510 | *buf++ = *src; | ||
511 | } else if (isspace(*src)) { | ||
512 | if (*prog->argv[argc]) { | ||
513 | buf++, argc++; | ||
514 | /* +1 here leaves room for the NULL which ends argv */ | ||
515 | if ((argc + 1) == argvAlloced) { | ||
516 | argvAlloced += 5; | ||
517 | prog->argv = realloc(prog->argv, | ||
518 | sizeof(*prog->argv) * argvAlloced); | ||
519 | } | ||
520 | prog->argv[argc] = buf; | ||
521 | |||
522 | globLastArgument(prog, &argc, &argvAlloced); | ||
523 | } | ||
524 | } else switch (*src) { | ||
525 | case '"': | ||
526 | case '\'': | ||
527 | quote = *src; | ||
528 | break; | ||
529 | |||
530 | case '#': /* comment */ | ||
531 | done = 1; | ||
532 | break; | ||
533 | |||
534 | case '>': /* redirections */ | ||
535 | case '<': | ||
536 | i = prog->numRedirections++; | ||
537 | prog->redirections = realloc(prog->redirections, | ||
538 | sizeof(*prog->redirections) * (i + 1)); | ||
539 | |||
540 | prog->redirections[i].fd = -1; | ||
541 | if (buf != prog->argv[argc]) { | ||
542 | /* the stuff before this character may be the file number | ||
543 | being redirected */ | ||
544 | prog->redirections[i].fd = strtol(prog->argv[argc], &chptr, 10); | ||
545 | |||
546 | if (*chptr && *prog->argv[argc]) { | ||
547 | buf++, argc++; | ||
548 | globLastArgument(prog, &argc, &argvAlloced); | ||
549 | } | ||
550 | } | ||
551 | |||
552 | if (prog->redirections[i].fd == -1) { | ||
553 | if (*src == '>') | ||
554 | prog->redirections[i].fd = 1; | ||
555 | else | ||
556 | prog->redirections[i].fd = 0; | ||
557 | } | ||
558 | |||
559 | if (*src++ == '>') { | ||
560 | if (*src == '>') | ||
561 | prog->redirections[i].type = REDIRECT_APPEND, src++; | ||
562 | else | ||
563 | prog->redirections[i].type = REDIRECT_OVERWRITE; | ||
564 | } else { | ||
565 | prog->redirections[i].type = REDIRECT_INPUT; | ||
566 | } | ||
567 | |||
568 | /* This isn't POSIX sh compliant. Oh well. */ | ||
569 | chptr = src; | ||
570 | while (isspace(*chptr)) chptr++; | ||
571 | |||
572 | if (!*chptr) { | ||
573 | fprintf(stderr, "file name expected after %c\n", *src); | ||
574 | freeJob(job); | ||
575 | return 1; | ||
576 | } | ||
577 | |||
578 | prog->redirections[i].filename = buf; | ||
579 | while (*chptr && !isspace(*chptr)) | ||
580 | *buf++ = *chptr++; | ||
581 | |||
582 | src = chptr - 1; /* we src++ later */ | ||
583 | prog->argv[argc] = ++buf; | ||
584 | break; | ||
585 | |||
586 | case '|': /* pipe */ | ||
587 | /* finish this command */ | ||
588 | if (*prog->argv[argc]) argc++; | ||
589 | if (!argc) { | ||
590 | fprintf(stderr, "empty command in pipe\n"); | ||
591 | freeJob(job); | ||
592 | return 1; | ||
593 | } | ||
594 | prog->argv[argc] = NULL; | ||
595 | |||
596 | /* and start the next */ | ||
597 | job->numProgs++; | ||
598 | job->progs = realloc(job->progs, | ||
599 | sizeof(*job->progs) * job->numProgs); | ||
600 | prog = job->progs + (job->numProgs - 1); | ||
601 | prog->numRedirections = 0; | ||
602 | prog->redirections = NULL; | ||
603 | prog->freeGlob = 0; | ||
604 | argc = 0; | ||
605 | |||
606 | argvAlloced = 5; | ||
607 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | ||
608 | prog->argv[0] = ++buf; | ||
609 | |||
610 | src++; | ||
611 | while (*src && isspace(*src)) src++; | ||
612 | |||
613 | if (!*src) { | ||
614 | fprintf(stderr, "empty command in pipe\n"); | ||
615 | return 1; | ||
616 | } | ||
617 | src--; /* we'll ++ it at the end of the loop */ | ||
618 | |||
619 | break; | ||
620 | |||
621 | case '&': /* background */ | ||
622 | *isBg = 1; | ||
623 | case ';': /* multiple commands */ | ||
624 | done = 1; | ||
625 | returnCommand = *commandPtr + (src - *commandPtr) + 1; | ||
626 | break; | ||
627 | |||
628 | case '\\': | ||
629 | src++; | ||
630 | if (!*src) { | ||
631 | freeJob(job); | ||
632 | fprintf(stderr, "character expected after \\\n"); | ||
633 | return 1; | ||
634 | } | ||
635 | if (*src == '*' || *src == '[' || *src == ']' || *src == '?') | ||
636 | *buf++ = '\\'; | ||
637 | /* fallthrough */ | ||
638 | default: | ||
639 | *buf++ = *src; | ||
640 | } | ||
641 | |||
642 | src++; | ||
643 | } | ||
644 | |||
645 | if (*prog->argv[argc]) { | ||
646 | argc++; | ||
647 | globLastArgument(prog, &argc, &argvAlloced); | ||
648 | } | ||
649 | if (!argc) { | ||
650 | freeJob(job); | ||
651 | return 0; | ||
652 | } | ||
653 | prog->argv[argc] = NULL; | ||
654 | |||
655 | if (!returnCommand) { | ||
656 | job->text = malloc(strlen(*commandPtr) + 1); | ||
657 | strcpy(job->text, *commandPtr); | ||
658 | } else { | ||
659 | /* This leaves any trailing spaces, which is a bit sloppy */ | ||
660 | |||
661 | count = returnCommand - *commandPtr; | ||
662 | job->text = malloc(count + 1); | ||
663 | strncpy(job->text, *commandPtr, count); | ||
664 | job->text[count] = '\0'; | ||
665 | } | ||
666 | |||
667 | *commandPtr = returnCommand; | ||
668 | |||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | static int runCommand(struct job newJob, struct jobSet * jobList, | ||
673 | int inBg) | ||
674 | { | ||
675 | struct job * job; | ||
676 | int i; | ||
677 | int nextin, nextout; | ||
678 | int pipefds[2]; /* pipefd[0] is for reading */ | ||
679 | struct builtInCommand *x; | ||
680 | |||
681 | /* handle built-ins here -- we don't fork() so we can't background | ||
682 | these very easily */ | ||
683 | for( x=bltins ; x->cmd ; x++) { | ||
684 | if (!strcmp(newJob.progs[0].argv[0], x->cmd)) { | ||
685 | return(x->function(&newJob, jobList)); | ||
686 | } | ||
687 | } | ||
688 | |||
689 | nextin = 0, nextout = 1; | ||
690 | for (i = 0; i < newJob.numProgs; i++) { | ||
691 | if ((i + 1) < newJob.numProgs) { | ||
692 | pipe(pipefds); | ||
693 | nextout = pipefds[1]; | ||
694 | } else { | ||
695 | nextout = 1; | ||
696 | } | ||
697 | |||
698 | if (!(newJob.progs[i].pid = fork())) { | ||
699 | signal(SIGTTOU, SIG_DFL); | ||
700 | |||
701 | if (nextin != 0) { | ||
702 | dup2(nextin, 0); | ||
703 | close(nextin); | ||
704 | } | ||
705 | |||
706 | if (nextout != 1) { | ||
707 | dup2(nextout, 1); | ||
708 | close(nextout); | ||
709 | } | ||
710 | |||
711 | /* explicit redirections override pipes */ | ||
712 | setupRedirections(newJob.progs + i); | ||
713 | |||
714 | execvp(newJob.progs[i].argv[0], newJob.progs[i].argv); | ||
715 | fatalError( "sh: %s: %s\n", newJob.progs[i].argv[0], | ||
716 | strerror(errno)); | ||
717 | } | ||
718 | |||
719 | /* put our child in the process group whose leader is the | ||
720 | first process in this pipe */ | ||
721 | setpgid(newJob.progs[i].pid, newJob.progs[0].pid); | ||
722 | |||
723 | if (nextin != 0) close(nextin); | ||
724 | if (nextout != 1) close(nextout); | ||
725 | |||
726 | /* If there isn't another process, nextin is garbage | ||
727 | but it doesn't matter */ | ||
728 | nextin = pipefds[0]; | ||
729 | } | ||
730 | |||
731 | newJob.pgrp = newJob.progs[0].pid; | ||
732 | |||
733 | /* find the ID for the job to use */ | ||
734 | newJob.jobId = 1; | ||
735 | for (job = jobList->head; job; job = job->next) | ||
736 | if (job->jobId >= newJob.jobId) | ||
737 | newJob.jobId = job->jobId + 1; | ||
738 | |||
739 | /* add the job to the list of running jobs */ | ||
740 | if (!jobList->head) { | ||
741 | job = jobList->head = malloc(sizeof(*job)); | ||
742 | } else { | ||
743 | for (job = jobList->head; job->next; job = job->next); | ||
744 | job->next = malloc(sizeof(*job)); | ||
745 | job = job->next; | ||
746 | } | ||
747 | |||
748 | *job = newJob; | ||
749 | job->next = NULL; | ||
750 | job->runningProgs = job->numProgs; | ||
751 | job->stoppedProgs = 0; | ||
752 | |||
753 | if (inBg) { | ||
754 | /* we don't wait for background jobs to return -- append it | ||
755 | to the list of backgrounded jobs and leave it alone */ | ||
756 | |||
757 | printf("[%d] %d\n", job->jobId, | ||
758 | newJob.progs[newJob.numProgs - 1].pid); | ||
759 | } else { | ||
760 | jobList->fg = job; | ||
761 | |||
762 | /* move the new process group into the foreground */ | ||
763 | |||
764 | if (tcsetpgrp(0, newJob.pgrp)) | ||
765 | perror("tcsetpgrp"); | ||
766 | } | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static int setupRedirections(struct childProgram * prog) | ||
772 | { | ||
773 | int i; | ||
774 | int openfd; | ||
775 | int mode=O_RDONLY; | ||
776 | struct redirectionSpecifier * redir = prog->redirections; | ||
777 | |||
778 | for (i = 0; i < prog->numRedirections; i++, redir++) { | ||
779 | switch (redir->type) { | ||
780 | case REDIRECT_INPUT: | ||
781 | mode = O_RDONLY; | ||
782 | break; | ||
783 | case REDIRECT_OVERWRITE: | ||
784 | mode = O_RDWR | O_CREAT | O_TRUNC; | ||
785 | break; | ||
786 | case REDIRECT_APPEND: | ||
787 | mode = O_RDWR | O_CREAT | O_APPEND; | ||
788 | break; | ||
789 | } | ||
790 | |||
791 | openfd = open(redir->filename, mode, 0666); | ||
792 | if (openfd < 0) { | ||
793 | /* this could get lost if stderr has been redirected, but | ||
794 | bash and ash both lose it as well (though zsh doesn't!) */ | ||
795 | fprintf(stderr, "error opening %s: %s\n", redir->filename, | ||
796 | strerror(errno)); | ||
797 | return 1; | ||
798 | } | ||
799 | |||
800 | if (openfd != redir->fd) { | ||
801 | dup2(openfd, redir->fd); | ||
802 | close(openfd); | ||
803 | } | ||
804 | } | ||
805 | |||
806 | return 0; | ||
807 | } | ||
808 | |||
809 | |||
810 | static int busy_loop(FILE * input) | ||
811 | { | ||
812 | char command[MAX_COMMAND_LEN + 1]; | ||
813 | char * nextCommand = NULL; | ||
814 | struct jobSet jobList = { NULL, NULL }; | ||
815 | struct job newJob; | ||
816 | int i; | ||
817 | int status; | ||
818 | int inBg; | ||
819 | |||
820 | /* don't pay any attention to this signal; it just confuses | ||
821 | things and isn't really meant for shells anyway */ | ||
822 | signal(SIGTTOU, SIG_IGN); | ||
823 | |||
824 | while (1) { | ||
825 | if (!jobList.fg) { | ||
826 | /* no job is in the foreground */ | ||
827 | |||
828 | /* see if any background processes have exited */ | ||
829 | checkJobs(&jobList); | ||
830 | |||
831 | if (!nextCommand) { | ||
832 | if (getCommand(input, command)) break; | ||
833 | nextCommand = command; | ||
834 | } | ||
835 | |||
836 | if (!parseCommand(&nextCommand, &newJob, &inBg) && | ||
837 | newJob.numProgs) { | ||
838 | runCommand(newJob, &jobList, inBg); | ||
839 | } | ||
840 | } else { | ||
841 | /* a job is running in the foreground; wait for it */ | ||
842 | i = 0; | ||
843 | while (!jobList.fg->progs[i].pid || | ||
844 | jobList.fg->progs[i].isStopped) i++; | ||
845 | |||
846 | waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED); | ||
847 | |||
848 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | ||
849 | /* the child exited */ | ||
850 | jobList.fg->runningProgs--; | ||
851 | jobList.fg->progs[i].pid = 0; | ||
852 | |||
853 | if (!jobList.fg->runningProgs) { | ||
854 | /* child exited */ | ||
855 | |||
856 | removeJob(&jobList, jobList.fg); | ||
857 | jobList.fg = NULL; | ||
858 | |||
859 | /* move the shell to the foreground */ | ||
860 | if (tcsetpgrp(0, getpid())) | ||
861 | perror("tcsetpgrp"); | ||
862 | } | ||
863 | } else { | ||
864 | /* the child was stopped */ | ||
865 | jobList.fg->stoppedProgs++; | ||
866 | jobList.fg->progs[i].isStopped = 1; | ||
867 | |||
868 | if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) { | ||
869 | printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId, | ||
870 | "Stopped", jobList.fg->text); | ||
871 | jobList.fg = NULL; | ||
872 | } | ||
873 | } | ||
874 | |||
875 | if (!jobList.fg) { | ||
876 | /* move the shell to the foreground */ | ||
877 | if (tcsetpgrp(0, getpid())) | ||
878 | perror("tcsetpgrp"); | ||
879 | } | ||
880 | } | ||
881 | } | ||
882 | |||
883 | return 0; | ||
884 | } | ||
885 | |||
886 | |||
887 | int shell_main(int argc, char ** argv) | ||
888 | { | ||
889 | FILE * input = stdin; | ||
890 | |||
891 | if (argc > 2) { | ||
892 | usage( shell_usage); | ||
893 | } | ||
894 | /* initialize the cwd */ | ||
895 | getcwd(cwd, sizeof(cwd)); | ||
896 | |||
897 | |||
898 | //if (argv[0] && argv[0][0] == '-') { | ||
899 | // shell_source("/etc/profile"); | ||
900 | //} | ||
901 | |||
902 | if (argc < 2) { | ||
903 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); | ||
904 | fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); | ||
905 | } else { | ||
906 | input = fopen(argv[1], "r"); | ||
907 | if (!input) | ||
908 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], strerror(errno)); | ||
909 | // else | ||
910 | // fatalError("Got it.\n"); | ||
911 | //exit(shell_source(argv[1])); | ||
912 | } | ||
913 | |||
914 | return (busy_loop( input)); | ||
915 | } | ||
@@ -0,0 +1,915 @@ | |||
1 | /* 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 | |||
42 | #define MAX_COMMAND_LEN 250 /* max length of a single command | ||
43 | string */ | ||
44 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | ||
45 | |||
46 | enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND }; | ||
47 | |||
48 | struct jobSet { | ||
49 | struct job * head; /* head of list of running jobs */ | ||
50 | struct job * fg; /* current foreground job */ | ||
51 | }; | ||
52 | |||
53 | struct redirectionSpecifier { | ||
54 | enum redirectionType type; /* type of redirection */ | ||
55 | int fd; /* file descriptor being redirected */ | ||
56 | char * filename; /* file to redirect fd to */ | ||
57 | }; | ||
58 | |||
59 | struct childProgram { | ||
60 | pid_t pid; /* 0 if exited */ | ||
61 | char ** argv; /* program name and arguments */ | ||
62 | int numRedirections; /* elements in redirection array */ | ||
63 | struct redirectionSpecifier * redirections; /* I/O redirections */ | ||
64 | glob_t globResult; /* result of parameter globbing */ | ||
65 | int freeGlob; /* should we globfree(&globResult)? */ | ||
66 | int isStopped; /* is the program currently running? */ | ||
67 | }; | ||
68 | |||
69 | struct job { | ||
70 | int jobId; /* job number */ | ||
71 | int numProgs; /* total number of programs in job */ | ||
72 | int runningProgs; /* number of programs running */ | ||
73 | char * text; /* name of job */ | ||
74 | char * cmdBuf; /* buffer various argv's point into */ | ||
75 | pid_t pgrp; /* process group ID for the job */ | ||
76 | struct childProgram * progs; /* array of programs in job */ | ||
77 | struct job * next; /* to track background commands */ | ||
78 | int stoppedProgs; /* number of programs alive, but stopped */ | ||
79 | }; | ||
80 | |||
81 | struct builtInCommand { | ||
82 | char *cmd; /* name */ | ||
83 | char *descr; /* description */ | ||
84 | char *usage; /* usage */ | ||
85 | int (*function) (struct job *, struct jobSet * jobList); /* function ptr */ | ||
86 | }; | ||
87 | |||
88 | /* Some function prototypes */ | ||
89 | static int shell_cd(struct job* cmd, struct jobSet* junk); | ||
90 | static int shell_env(struct job* dummy, struct jobSet* junk); | ||
91 | static int shell_exit(struct job* cmd, struct jobSet* junk); | ||
92 | static int shell_fg_bg(struct job* cmd, struct jobSet* jobList); | ||
93 | static int shell_help(struct job* cmd, struct jobSet* junk); | ||
94 | static int shell_jobs(struct job* dummy, struct jobSet* jobList); | ||
95 | static int shell_pwd(struct job* dummy, struct jobSet* junk); | ||
96 | static int shell_set(struct job* cmd, struct jobSet* junk); | ||
97 | static int shell_source(struct job* cmd, struct jobSet* jobList); | ||
98 | static int shell_unset(struct job* cmd, struct jobSet* junk); | ||
99 | |||
100 | static void checkJobs(struct jobSet * jobList); | ||
101 | static int getCommand(FILE * source, char * command); | ||
102 | static int parseCommand(char ** commandPtr, struct job * job, int * isBg); | ||
103 | static int setupRedirections(struct childProgram * prog); | ||
104 | static int runCommand(struct job newJob, struct jobSet * jobList, int inBg); | ||
105 | static int busy_loop(FILE * input); | ||
106 | |||
107 | |||
108 | /* Table of built-in functions */ | ||
109 | static struct builtInCommand bltins[] = { | ||
110 | {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg}, | ||
111 | {"cd", "Change working directory", "cd [dir]", shell_cd}, | ||
112 | {"env", "Print all environment variables", "env", shell_env}, | ||
113 | {"exit", "Exit from shell()", "exit", shell_exit}, | ||
114 | {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, | ||
115 | {"jobs", "Lists the active jobs", "jobs", shell_jobs}, | ||
116 | {"pwd", "Print current directory", "pwd", shell_pwd}, | ||
117 | {"set", "Set environment variable", "set [VAR=value]", shell_set}, | ||
118 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, | ||
119 | //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo}, | ||
120 | {".", "Source-in and run commands in a file", ". filename", shell_source}, | ||
121 | {"help", "List shell built-in commands", "help", shell_help}, | ||
122 | {NULL, NULL, NULL, NULL} | ||
123 | }; | ||
124 | |||
125 | static const char shell_usage[] = | ||
126 | "sh [FILE]...\n\n" | ||
127 | "The BusyBox command interpreter (shell).\n\n"; | ||
128 | |||
129 | |||
130 | static char cwd[1024]; | ||
131 | static char *prompt = "# "; | ||
132 | |||
133 | |||
134 | /* built-in 'cd <path>' handler */ | ||
135 | static int shell_cd(struct job* cmd, struct jobSet* junk) | ||
136 | { | ||
137 | char *newdir; | ||
138 | if (!cmd->progs[0].argv[1] == 1) | ||
139 | newdir = getenv("HOME"); | ||
140 | else | ||
141 | newdir = cmd->progs[0].argv[1]; | ||
142 | if (chdir(newdir)) { | ||
143 | printf("cd: %s: %s\n", newdir, strerror(errno)); | ||
144 | return FALSE; | ||
145 | } | ||
146 | getcwd(cwd, sizeof(cwd)); | ||
147 | |||
148 | return TRUE; | ||
149 | } | ||
150 | |||
151 | /* built-in 'env' handler */ | ||
152 | static int shell_env(struct job* dummy, struct jobSet* junk) | ||
153 | { | ||
154 | char **e; | ||
155 | |||
156 | for (e = environ ; *e ; e++) { | ||
157 | fprintf(stdout, "%s\n", *e); | ||
158 | } | ||
159 | return (0); | ||
160 | } | ||
161 | |||
162 | /* built-in 'exit' handler */ | ||
163 | static int shell_exit(struct job* cmd, struct jobSet* junk) | ||
164 | { | ||
165 | if (!cmd->progs[0].argv[1] == 1) | ||
166 | exit TRUE; | ||
167 | else | ||
168 | exit(atoi(cmd->progs[0].argv[1])); | ||
169 | } | ||
170 | |||
171 | /* built-in 'fg' and 'bg' handler */ | ||
172 | static int shell_fg_bg(struct job* cmd, struct jobSet* jobList) | ||
173 | { | ||
174 | int i, jobNum; | ||
175 | struct job* job; | ||
176 | |||
177 | if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { | ||
178 | fprintf(stderr, "%s: exactly one argument is expected\n", | ||
179 | cmd->progs[0].argv[0]); | ||
180 | return FALSE; | ||
181 | } | ||
182 | |||
183 | if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) { | ||
184 | fprintf(stderr, "%s: bad argument '%s'\n", | ||
185 | cmd->progs[0].argv[0], cmd->progs[0].argv[1]); | ||
186 | return FALSE; | ||
187 | } | ||
188 | |||
189 | for (job = jobList->head; job; job = job->next) | ||
190 | if (job->jobId == jobNum) break; | ||
191 | |||
192 | if (!job) { | ||
193 | fprintf(stderr, "%s: unknown job %d\n", | ||
194 | cmd->progs[0].argv[0], jobNum); | ||
195 | return FALSE; | ||
196 | } | ||
197 | |||
198 | if (*cmd->progs[0].argv[0] == 'f') { | ||
199 | /* Make this job the foreground job */ | ||
200 | |||
201 | if (tcsetpgrp(0, job->pgrp)) | ||
202 | perror("tcsetpgrp"); | ||
203 | jobList->fg = job; | ||
204 | } | ||
205 | |||
206 | /* Restart the processes in the job */ | ||
207 | for (i = 0; i < job->numProgs; i++) | ||
208 | job->progs[i].isStopped = 0; | ||
209 | |||
210 | kill(-job->pgrp, SIGCONT); | ||
211 | |||
212 | job->stoppedProgs = 0; | ||
213 | |||
214 | return TRUE; | ||
215 | } | ||
216 | |||
217 | /* built-in 'help' handler */ | ||
218 | static int shell_help(struct job* cmd, struct jobSet* junk) | ||
219 | { | ||
220 | struct builtInCommand *x; | ||
221 | |||
222 | fprintf(stdout, "\nBuilt-in commands:\n"); | ||
223 | fprintf(stdout, "-------------------\n"); | ||
224 | for ( x=bltins; x->cmd; x++) { | ||
225 | fprintf(stdout, "%s\t%s\n", x->cmd, x->descr); | ||
226 | } | ||
227 | fprintf(stdout, "\n\n"); | ||
228 | return TRUE; | ||
229 | } | ||
230 | |||
231 | /* built-in 'jobs' handler */ | ||
232 | static int shell_jobs(struct job* dummy, struct jobSet* jobList) | ||
233 | { | ||
234 | struct job * job; | ||
235 | char * statusString; | ||
236 | for (job = jobList->head; job; job = job->next) { | ||
237 | if (job->runningProgs == job->stoppedProgs) | ||
238 | statusString = "Stopped"; | ||
239 | else | ||
240 | statusString = "Running"; | ||
241 | |||
242 | printf(JOB_STATUS_FORMAT, job->jobId, statusString, | ||
243 | job->text); | ||
244 | } | ||
245 | return TRUE; | ||
246 | } | ||
247 | |||
248 | |||
249 | /* built-in 'pwd' handler */ | ||
250 | static int shell_pwd(struct job* dummy, struct jobSet* junk) | ||
251 | { | ||
252 | getcwd(cwd, sizeof(cwd)); | ||
253 | fprintf(stdout, "%s\n", cwd); | ||
254 | return TRUE; | ||
255 | } | ||
256 | |||
257 | /* built-in 'set VAR=value' handler */ | ||
258 | static int shell_set(struct job* cmd, struct jobSet* junk) | ||
259 | { | ||
260 | int res; | ||
261 | |||
262 | if (!cmd->progs[0].argv[1] == 1) { | ||
263 | return (shell_env(cmd, junk)); | ||
264 | } | ||
265 | res = putenv(cmd->progs[0].argv[1]); | ||
266 | if (res) | ||
267 | fprintf(stdout, "set: %s\n", strerror(errno)); | ||
268 | return (res); | ||
269 | } | ||
270 | |||
271 | /* Built-in '.' handler (read-in and execute commands from file) */ | ||
272 | static int shell_source(struct job* cmd, struct jobSet* junk) | ||
273 | { | ||
274 | FILE *input; | ||
275 | int status; | ||
276 | |||
277 | if (!cmd->progs[0].argv[1] == 1) | ||
278 | return FALSE; | ||
279 | |||
280 | input = fopen(cmd->progs[0].argv[1], "r"); | ||
281 | if (!input) { | ||
282 | fprintf(stdout, "Couldn't open file '%s'\n", cmd->progs[0].argv[1]); | ||
283 | return FALSE; | ||
284 | } | ||
285 | |||
286 | /* Now run the file */ | ||
287 | status = busy_loop(input); | ||
288 | return (status); | ||
289 | } | ||
290 | |||
291 | /* built-in 'unset VAR' handler */ | ||
292 | static int shell_unset(struct job* cmd, struct jobSet* junk) | ||
293 | { | ||
294 | if (!cmd->progs[0].argv[1] == 1) { | ||
295 | fprintf(stdout, "unset: parameter required.\n"); | ||
296 | return FALSE; | ||
297 | } | ||
298 | unsetenv(cmd->progs[0].argv[1]); | ||
299 | return TRUE; | ||
300 | } | ||
301 | |||
302 | /* free up all memory from a job */ | ||
303 | static void freeJob(struct job * cmd) | ||
304 | { | ||
305 | int i; | ||
306 | |||
307 | for (i = 0; i < cmd->numProgs; i++) { | ||
308 | free(cmd->progs[i].argv); | ||
309 | if (cmd->progs[i].redirections) free(cmd->progs[i].redirections); | ||
310 | if (cmd->progs[i].freeGlob) globfree(&cmd->progs[i].globResult); | ||
311 | } | ||
312 | free(cmd->progs); | ||
313 | if (cmd->text) free(cmd->text); | ||
314 | free(cmd->cmdBuf); | ||
315 | } | ||
316 | |||
317 | /* remove a job from the jobList */ | ||
318 | static void removeJob(struct jobSet * jobList, struct job * job) | ||
319 | { | ||
320 | struct job * prevJob; | ||
321 | |||
322 | freeJob(job); | ||
323 | if (job == jobList->head) { | ||
324 | jobList->head = job->next; | ||
325 | } else { | ||
326 | prevJob = jobList->head; | ||
327 | while (prevJob->next != job) prevJob = prevJob->next; | ||
328 | prevJob->next = job->next; | ||
329 | } | ||
330 | |||
331 | free(job); | ||
332 | } | ||
333 | |||
334 | /* Checks to see if any background processes have exited -- if they | ||
335 | have, figure out why and see if a job has completed */ | ||
336 | static void checkJobs(struct jobSet * jobList) | ||
337 | { | ||
338 | struct job * job; | ||
339 | pid_t childpid; | ||
340 | int status; | ||
341 | int progNum=0; | ||
342 | |||
343 | while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { | ||
344 | for (job = jobList->head; job; job = job->next) { | ||
345 | progNum = 0; | ||
346 | while (progNum < job->numProgs && | ||
347 | job->progs[progNum].pid != childpid) | ||
348 | progNum++; | ||
349 | if (progNum < job->numProgs) break; | ||
350 | } | ||
351 | |||
352 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | ||
353 | /* child exited */ | ||
354 | job->runningProgs--; | ||
355 | job->progs[progNum].pid = 0; | ||
356 | |||
357 | if (!job->runningProgs) { | ||
358 | printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text); | ||
359 | removeJob(jobList, job); | ||
360 | } | ||
361 | } else { | ||
362 | /* child stopped */ | ||
363 | job->stoppedProgs++; | ||
364 | job->progs[progNum].isStopped = 1; | ||
365 | |||
366 | if (job->stoppedProgs == job->numProgs) { | ||
367 | printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", job->text); | ||
368 | } | ||
369 | } | ||
370 | } | ||
371 | |||
372 | if (childpid == -1 && errno != ECHILD) | ||
373 | perror("waitpid"); | ||
374 | } | ||
375 | |||
376 | static int getCommand(FILE * source, char * command) | ||
377 | { | ||
378 | if (source == stdin) { | ||
379 | fprintf(stdout, "%s %s", cwd, prompt); | ||
380 | fflush(stdout); | ||
381 | } | ||
382 | |||
383 | if (!fgets(command, MAX_COMMAND_LEN, source)) { | ||
384 | if (source == stdin) printf("\n"); | ||
385 | return 1; | ||
386 | } | ||
387 | |||
388 | /* remove trailing newline */ | ||
389 | command[strlen(command) - 1] = '\0'; | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static void globLastArgument(struct childProgram * prog, int * argcPtr, | ||
395 | int * argcAllocedPtr) | ||
396 | { | ||
397 | int argc = *argcPtr; | ||
398 | int argcAlloced = *argcAllocedPtr; | ||
399 | int rc; | ||
400 | int flags; | ||
401 | int i; | ||
402 | char * src, * dst; | ||
403 | |||
404 | if (argc > 1) { /* cmd->globResult is already initialized */ | ||
405 | flags = GLOB_APPEND; | ||
406 | i = prog->globResult.gl_pathc; | ||
407 | } else { | ||
408 | prog->freeGlob = 1; | ||
409 | flags = 0; | ||
410 | i = 0; | ||
411 | } | ||
412 | |||
413 | rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); | ||
414 | if (rc == GLOB_NOSPACE) { | ||
415 | fprintf(stderr, "out of space during glob operation\n"); | ||
416 | return; | ||
417 | } else if (rc == GLOB_NOMATCH || | ||
418 | (!rc && (prog->globResult.gl_pathc - i) == 1 && | ||
419 | !strcmp(prog->argv[argc - 1], | ||
420 | prog->globResult.gl_pathv[i]))) { | ||
421 | /* we need to remove whatever \ quoting is still present */ | ||
422 | src = dst = prog->argv[argc - 1]; | ||
423 | while (*src) { | ||
424 | if (*src != '\\') *dst++ = *src; | ||
425 | src++; | ||
426 | } | ||
427 | *dst = '\0'; | ||
428 | } else if (!rc) { | ||
429 | argcAlloced += (prog->globResult.gl_pathc - i); | ||
430 | prog->argv = realloc(prog->argv, argcAlloced * sizeof(*prog->argv)); | ||
431 | memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i, | ||
432 | sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i)); | ||
433 | argc += (prog->globResult.gl_pathc - i - 1); | ||
434 | } | ||
435 | |||
436 | *argcAllocedPtr = argcAlloced; | ||
437 | *argcPtr = argc; | ||
438 | } | ||
439 | |||
440 | /* Return cmd->numProgs as 0 if no command is present (e.g. an empty | ||
441 | line). If a valid command is found, commandPtr is set to point to | ||
442 | the beginning of the next command (if the original command had more | ||
443 | then one job associated with it) or NULL if no more commands are | ||
444 | present. */ | ||
445 | static int parseCommand(char ** commandPtr, struct job * job, int * isBg) | ||
446 | { | ||
447 | char * command; | ||
448 | char * returnCommand = NULL; | ||
449 | char * src, * buf, * chptr; | ||
450 | int argc = 0; | ||
451 | int done = 0; | ||
452 | int argvAlloced; | ||
453 | int i; | ||
454 | char quote = '\0'; | ||
455 | int count; | ||
456 | struct childProgram * prog; | ||
457 | |||
458 | /* skip leading white space */ | ||
459 | while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++; | ||
460 | |||
461 | /* this handles empty lines or leading '#' characters */ | ||
462 | if (!**commandPtr || (**commandPtr=='#')) { | ||
463 | job->numProgs = 0; | ||
464 | *commandPtr = NULL; | ||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | *isBg = 0; | ||
469 | job->numProgs = 1; | ||
470 | job->progs = malloc(sizeof(*job->progs)); | ||
471 | |||
472 | /* We set the argv elements to point inside of this string. The | ||
473 | memory is freed by freeJob(). | ||
474 | |||
475 | Getting clean memory relieves us of the task of NULL | ||
476 | terminating things and makes the rest of this look a bit | ||
477 | cleaner (though it is, admittedly, a tad less efficient) */ | ||
478 | job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1); | ||
479 | job->text = NULL; | ||
480 | |||
481 | prog = job->progs; | ||
482 | prog->numRedirections = 0; | ||
483 | prog->redirections = NULL; | ||
484 | prog->freeGlob = 0; | ||
485 | prog->isStopped = 0; | ||
486 | |||
487 | argvAlloced = 5; | ||
488 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | ||
489 | prog->argv[0] = job->cmdBuf; | ||
490 | |||
491 | buf = command; | ||
492 | src = *commandPtr; | ||
493 | while (*src && !done) { | ||
494 | if (quote == *src) { | ||
495 | quote = '\0'; | ||
496 | } else if (quote) { | ||
497 | if (*src == '\\') { | ||
498 | src++; | ||
499 | if (!*src) { | ||
500 | fprintf(stderr, "character expected after \\\n"); | ||
501 | freeJob(job); | ||
502 | return 1; | ||
503 | } | ||
504 | |||
505 | /* in shell, "\'" should yield \' */ | ||
506 | if (*src != quote) *buf++ = '\\'; | ||
507 | } else if (*src == '*' || *src == '?' || *src == '[' || | ||
508 | *src == ']') | ||
509 | *buf++ = '\\'; | ||
510 | *buf++ = *src; | ||
511 | } else if (isspace(*src)) { | ||
512 | if (*prog->argv[argc]) { | ||
513 | buf++, argc++; | ||
514 | /* +1 here leaves room for the NULL which ends argv */ | ||
515 | if ((argc + 1) == argvAlloced) { | ||
516 | argvAlloced += 5; | ||
517 | prog->argv = realloc(prog->argv, | ||
518 | sizeof(*prog->argv) * argvAlloced); | ||
519 | } | ||
520 | prog->argv[argc] = buf; | ||
521 | |||
522 | globLastArgument(prog, &argc, &argvAlloced); | ||
523 | } | ||
524 | } else switch (*src) { | ||
525 | case '"': | ||
526 | case '\'': | ||
527 | quote = *src; | ||
528 | break; | ||
529 | |||
530 | case '#': /* comment */ | ||
531 | done = 1; | ||
532 | break; | ||
533 | |||
534 | case '>': /* redirections */ | ||
535 | case '<': | ||
536 | i = prog->numRedirections++; | ||
537 | prog->redirections = realloc(prog->redirections, | ||
538 | sizeof(*prog->redirections) * (i + 1)); | ||
539 | |||
540 | prog->redirections[i].fd = -1; | ||
541 | if (buf != prog->argv[argc]) { | ||
542 | /* the stuff before this character may be the file number | ||
543 | being redirected */ | ||
544 | prog->redirections[i].fd = strtol(prog->argv[argc], &chptr, 10); | ||
545 | |||
546 | if (*chptr && *prog->argv[argc]) { | ||
547 | buf++, argc++; | ||
548 | globLastArgument(prog, &argc, &argvAlloced); | ||
549 | } | ||
550 | } | ||
551 | |||
552 | if (prog->redirections[i].fd == -1) { | ||
553 | if (*src == '>') | ||
554 | prog->redirections[i].fd = 1; | ||
555 | else | ||
556 | prog->redirections[i].fd = 0; | ||
557 | } | ||
558 | |||
559 | if (*src++ == '>') { | ||
560 | if (*src == '>') | ||
561 | prog->redirections[i].type = REDIRECT_APPEND, src++; | ||
562 | else | ||
563 | prog->redirections[i].type = REDIRECT_OVERWRITE; | ||
564 | } else { | ||
565 | prog->redirections[i].type = REDIRECT_INPUT; | ||
566 | } | ||
567 | |||
568 | /* This isn't POSIX sh compliant. Oh well. */ | ||
569 | chptr = src; | ||
570 | while (isspace(*chptr)) chptr++; | ||
571 | |||
572 | if (!*chptr) { | ||
573 | fprintf(stderr, "file name expected after %c\n", *src); | ||
574 | freeJob(job); | ||
575 | return 1; | ||
576 | } | ||
577 | |||
578 | prog->redirections[i].filename = buf; | ||
579 | while (*chptr && !isspace(*chptr)) | ||
580 | *buf++ = *chptr++; | ||
581 | |||
582 | src = chptr - 1; /* we src++ later */ | ||
583 | prog->argv[argc] = ++buf; | ||
584 | break; | ||
585 | |||
586 | case '|': /* pipe */ | ||
587 | /* finish this command */ | ||
588 | if (*prog->argv[argc]) argc++; | ||
589 | if (!argc) { | ||
590 | fprintf(stderr, "empty command in pipe\n"); | ||
591 | freeJob(job); | ||
592 | return 1; | ||
593 | } | ||
594 | prog->argv[argc] = NULL; | ||
595 | |||
596 | /* and start the next */ | ||
597 | job->numProgs++; | ||
598 | job->progs = realloc(job->progs, | ||
599 | sizeof(*job->progs) * job->numProgs); | ||
600 | prog = job->progs + (job->numProgs - 1); | ||
601 | prog->numRedirections = 0; | ||
602 | prog->redirections = NULL; | ||
603 | prog->freeGlob = 0; | ||
604 | argc = 0; | ||
605 | |||
606 | argvAlloced = 5; | ||
607 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | ||
608 | prog->argv[0] = ++buf; | ||
609 | |||
610 | src++; | ||
611 | while (*src && isspace(*src)) src++; | ||
612 | |||
613 | if (!*src) { | ||
614 | fprintf(stderr, "empty command in pipe\n"); | ||
615 | return 1; | ||
616 | } | ||
617 | src--; /* we'll ++ it at the end of the loop */ | ||
618 | |||
619 | break; | ||
620 | |||
621 | case '&': /* background */ | ||
622 | *isBg = 1; | ||
623 | case ';': /* multiple commands */ | ||
624 | done = 1; | ||
625 | returnCommand = *commandPtr + (src - *commandPtr) + 1; | ||
626 | break; | ||
627 | |||
628 | case '\\': | ||
629 | src++; | ||
630 | if (!*src) { | ||
631 | freeJob(job); | ||
632 | fprintf(stderr, "character expected after \\\n"); | ||
633 | return 1; | ||
634 | } | ||
635 | if (*src == '*' || *src == '[' || *src == ']' || *src == '?') | ||
636 | *buf++ = '\\'; | ||
637 | /* fallthrough */ | ||
638 | default: | ||
639 | *buf++ = *src; | ||
640 | } | ||
641 | |||
642 | src++; | ||
643 | } | ||
644 | |||
645 | if (*prog->argv[argc]) { | ||
646 | argc++; | ||
647 | globLastArgument(prog, &argc, &argvAlloced); | ||
648 | } | ||
649 | if (!argc) { | ||
650 | freeJob(job); | ||
651 | return 0; | ||
652 | } | ||
653 | prog->argv[argc] = NULL; | ||
654 | |||
655 | if (!returnCommand) { | ||
656 | job->text = malloc(strlen(*commandPtr) + 1); | ||
657 | strcpy(job->text, *commandPtr); | ||
658 | } else { | ||
659 | /* This leaves any trailing spaces, which is a bit sloppy */ | ||
660 | |||
661 | count = returnCommand - *commandPtr; | ||
662 | job->text = malloc(count + 1); | ||
663 | strncpy(job->text, *commandPtr, count); | ||
664 | job->text[count] = '\0'; | ||
665 | } | ||
666 | |||
667 | *commandPtr = returnCommand; | ||
668 | |||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | static int runCommand(struct job newJob, struct jobSet * jobList, | ||
673 | int inBg) | ||
674 | { | ||
675 | struct job * job; | ||
676 | int i; | ||
677 | int nextin, nextout; | ||
678 | int pipefds[2]; /* pipefd[0] is for reading */ | ||
679 | struct builtInCommand *x; | ||
680 | |||
681 | /* handle built-ins here -- we don't fork() so we can't background | ||
682 | these very easily */ | ||
683 | for( x=bltins ; x->cmd ; x++) { | ||
684 | if (!strcmp(newJob.progs[0].argv[0], x->cmd)) { | ||
685 | return(x->function(&newJob, jobList)); | ||
686 | } | ||
687 | } | ||
688 | |||
689 | nextin = 0, nextout = 1; | ||
690 | for (i = 0; i < newJob.numProgs; i++) { | ||
691 | if ((i + 1) < newJob.numProgs) { | ||
692 | pipe(pipefds); | ||
693 | nextout = pipefds[1]; | ||
694 | } else { | ||
695 | nextout = 1; | ||
696 | } | ||
697 | |||
698 | if (!(newJob.progs[i].pid = fork())) { | ||
699 | signal(SIGTTOU, SIG_DFL); | ||
700 | |||
701 | if (nextin != 0) { | ||
702 | dup2(nextin, 0); | ||
703 | close(nextin); | ||
704 | } | ||
705 | |||
706 | if (nextout != 1) { | ||
707 | dup2(nextout, 1); | ||
708 | close(nextout); | ||
709 | } | ||
710 | |||
711 | /* explicit redirections override pipes */ | ||
712 | setupRedirections(newJob.progs + i); | ||
713 | |||
714 | execvp(newJob.progs[i].argv[0], newJob.progs[i].argv); | ||
715 | fatalError( "sh: %s: %s\n", newJob.progs[i].argv[0], | ||
716 | strerror(errno)); | ||
717 | } | ||
718 | |||
719 | /* put our child in the process group whose leader is the | ||
720 | first process in this pipe */ | ||
721 | setpgid(newJob.progs[i].pid, newJob.progs[0].pid); | ||
722 | |||
723 | if (nextin != 0) close(nextin); | ||
724 | if (nextout != 1) close(nextout); | ||
725 | |||
726 | /* If there isn't another process, nextin is garbage | ||
727 | but it doesn't matter */ | ||
728 | nextin = pipefds[0]; | ||
729 | } | ||
730 | |||
731 | newJob.pgrp = newJob.progs[0].pid; | ||
732 | |||
733 | /* find the ID for the job to use */ | ||
734 | newJob.jobId = 1; | ||
735 | for (job = jobList->head; job; job = job->next) | ||
736 | if (job->jobId >= newJob.jobId) | ||
737 | newJob.jobId = job->jobId + 1; | ||
738 | |||
739 | /* add the job to the list of running jobs */ | ||
740 | if (!jobList->head) { | ||
741 | job = jobList->head = malloc(sizeof(*job)); | ||
742 | } else { | ||
743 | for (job = jobList->head; job->next; job = job->next); | ||
744 | job->next = malloc(sizeof(*job)); | ||
745 | job = job->next; | ||
746 | } | ||
747 | |||
748 | *job = newJob; | ||
749 | job->next = NULL; | ||
750 | job->runningProgs = job->numProgs; | ||
751 | job->stoppedProgs = 0; | ||
752 | |||
753 | if (inBg) { | ||
754 | /* we don't wait for background jobs to return -- append it | ||
755 | to the list of backgrounded jobs and leave it alone */ | ||
756 | |||
757 | printf("[%d] %d\n", job->jobId, | ||
758 | newJob.progs[newJob.numProgs - 1].pid); | ||
759 | } else { | ||
760 | jobList->fg = job; | ||
761 | |||
762 | /* move the new process group into the foreground */ | ||
763 | |||
764 | if (tcsetpgrp(0, newJob.pgrp)) | ||
765 | perror("tcsetpgrp"); | ||
766 | } | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static int setupRedirections(struct childProgram * prog) | ||
772 | { | ||
773 | int i; | ||
774 | int openfd; | ||
775 | int mode=O_RDONLY; | ||
776 | struct redirectionSpecifier * redir = prog->redirections; | ||
777 | |||
778 | for (i = 0; i < prog->numRedirections; i++, redir++) { | ||
779 | switch (redir->type) { | ||
780 | case REDIRECT_INPUT: | ||
781 | mode = O_RDONLY; | ||
782 | break; | ||
783 | case REDIRECT_OVERWRITE: | ||
784 | mode = O_RDWR | O_CREAT | O_TRUNC; | ||
785 | break; | ||
786 | case REDIRECT_APPEND: | ||
787 | mode = O_RDWR | O_CREAT | O_APPEND; | ||
788 | break; | ||
789 | } | ||
790 | |||
791 | openfd = open(redir->filename, mode, 0666); | ||
792 | if (openfd < 0) { | ||
793 | /* this could get lost if stderr has been redirected, but | ||
794 | bash and ash both lose it as well (though zsh doesn't!) */ | ||
795 | fprintf(stderr, "error opening %s: %s\n", redir->filename, | ||
796 | strerror(errno)); | ||
797 | return 1; | ||
798 | } | ||
799 | |||
800 | if (openfd != redir->fd) { | ||
801 | dup2(openfd, redir->fd); | ||
802 | close(openfd); | ||
803 | } | ||
804 | } | ||
805 | |||
806 | return 0; | ||
807 | } | ||
808 | |||
809 | |||
810 | static int busy_loop(FILE * input) | ||
811 | { | ||
812 | char command[MAX_COMMAND_LEN + 1]; | ||
813 | char * nextCommand = NULL; | ||
814 | struct jobSet jobList = { NULL, NULL }; | ||
815 | struct job newJob; | ||
816 | int i; | ||
817 | int status; | ||
818 | int inBg; | ||
819 | |||
820 | /* don't pay any attention to this signal; it just confuses | ||
821 | things and isn't really meant for shells anyway */ | ||
822 | signal(SIGTTOU, SIG_IGN); | ||
823 | |||
824 | while (1) { | ||
825 | if (!jobList.fg) { | ||
826 | /* no job is in the foreground */ | ||
827 | |||
828 | /* see if any background processes have exited */ | ||
829 | checkJobs(&jobList); | ||
830 | |||
831 | if (!nextCommand) { | ||
832 | if (getCommand(input, command)) break; | ||
833 | nextCommand = command; | ||
834 | } | ||
835 | |||
836 | if (!parseCommand(&nextCommand, &newJob, &inBg) && | ||
837 | newJob.numProgs) { | ||
838 | runCommand(newJob, &jobList, inBg); | ||
839 | } | ||
840 | } else { | ||
841 | /* a job is running in the foreground; wait for it */ | ||
842 | i = 0; | ||
843 | while (!jobList.fg->progs[i].pid || | ||
844 | jobList.fg->progs[i].isStopped) i++; | ||
845 | |||
846 | waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED); | ||
847 | |||
848 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | ||
849 | /* the child exited */ | ||
850 | jobList.fg->runningProgs--; | ||
851 | jobList.fg->progs[i].pid = 0; | ||
852 | |||
853 | if (!jobList.fg->runningProgs) { | ||
854 | /* child exited */ | ||
855 | |||
856 | removeJob(&jobList, jobList.fg); | ||
857 | jobList.fg = NULL; | ||
858 | |||
859 | /* move the shell to the foreground */ | ||
860 | if (tcsetpgrp(0, getpid())) | ||
861 | perror("tcsetpgrp"); | ||
862 | } | ||
863 | } else { | ||
864 | /* the child was stopped */ | ||
865 | jobList.fg->stoppedProgs++; | ||
866 | jobList.fg->progs[i].isStopped = 1; | ||
867 | |||
868 | if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) { | ||
869 | printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId, | ||
870 | "Stopped", jobList.fg->text); | ||
871 | jobList.fg = NULL; | ||
872 | } | ||
873 | } | ||
874 | |||
875 | if (!jobList.fg) { | ||
876 | /* move the shell to the foreground */ | ||
877 | if (tcsetpgrp(0, getpid())) | ||
878 | perror("tcsetpgrp"); | ||
879 | } | ||
880 | } | ||
881 | } | ||
882 | |||
883 | return 0; | ||
884 | } | ||
885 | |||
886 | |||
887 | int shell_main(int argc, char ** argv) | ||
888 | { | ||
889 | FILE * input = stdin; | ||
890 | |||
891 | if (argc > 2) { | ||
892 | usage( shell_usage); | ||
893 | } | ||
894 | /* initialize the cwd */ | ||
895 | getcwd(cwd, sizeof(cwd)); | ||
896 | |||
897 | |||
898 | //if (argv[0] && argv[0][0] == '-') { | ||
899 | // shell_source("/etc/profile"); | ||
900 | //} | ||
901 | |||
902 | if (argc < 2) { | ||
903 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); | ||
904 | fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); | ||
905 | } else { | ||
906 | input = fopen(argv[1], "r"); | ||
907 | if (!input) | ||
908 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], strerror(errno)); | ||
909 | // else | ||
910 | // fatalError("Got it.\n"); | ||
911 | //exit(shell_source(argv[1])); | ||
912 | } | ||
913 | |||
914 | return (busy_loop( input)); | ||
915 | } | ||
diff --git a/shell/lash.c b/shell/lash.c new file mode 100644 index 000000000..9e467dc54 --- /dev/null +++ b/shell/lash.c | |||
@@ -0,0 +1,915 @@ | |||
1 | /* 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 | |||
42 | #define MAX_COMMAND_LEN 250 /* max length of a single command | ||
43 | string */ | ||
44 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | ||
45 | |||
46 | enum redirectionType { REDIRECT_INPUT, REDIRECT_OVERWRITE, REDIRECT_APPEND }; | ||
47 | |||
48 | struct jobSet { | ||
49 | struct job * head; /* head of list of running jobs */ | ||
50 | struct job * fg; /* current foreground job */ | ||
51 | }; | ||
52 | |||
53 | struct redirectionSpecifier { | ||
54 | enum redirectionType type; /* type of redirection */ | ||
55 | int fd; /* file descriptor being redirected */ | ||
56 | char * filename; /* file to redirect fd to */ | ||
57 | }; | ||
58 | |||
59 | struct childProgram { | ||
60 | pid_t pid; /* 0 if exited */ | ||
61 | char ** argv; /* program name and arguments */ | ||
62 | int numRedirections; /* elements in redirection array */ | ||
63 | struct redirectionSpecifier * redirections; /* I/O redirections */ | ||
64 | glob_t globResult; /* result of parameter globbing */ | ||
65 | int freeGlob; /* should we globfree(&globResult)? */ | ||
66 | int isStopped; /* is the program currently running? */ | ||
67 | }; | ||
68 | |||
69 | struct job { | ||
70 | int jobId; /* job number */ | ||
71 | int numProgs; /* total number of programs in job */ | ||
72 | int runningProgs; /* number of programs running */ | ||
73 | char * text; /* name of job */ | ||
74 | char * cmdBuf; /* buffer various argv's point into */ | ||
75 | pid_t pgrp; /* process group ID for the job */ | ||
76 | struct childProgram * progs; /* array of programs in job */ | ||
77 | struct job * next; /* to track background commands */ | ||
78 | int stoppedProgs; /* number of programs alive, but stopped */ | ||
79 | }; | ||
80 | |||
81 | struct builtInCommand { | ||
82 | char *cmd; /* name */ | ||
83 | char *descr; /* description */ | ||
84 | char *usage; /* usage */ | ||
85 | int (*function) (struct job *, struct jobSet * jobList); /* function ptr */ | ||
86 | }; | ||
87 | |||
88 | /* Some function prototypes */ | ||
89 | static int shell_cd(struct job* cmd, struct jobSet* junk); | ||
90 | static int shell_env(struct job* dummy, struct jobSet* junk); | ||
91 | static int shell_exit(struct job* cmd, struct jobSet* junk); | ||
92 | static int shell_fg_bg(struct job* cmd, struct jobSet* jobList); | ||
93 | static int shell_help(struct job* cmd, struct jobSet* junk); | ||
94 | static int shell_jobs(struct job* dummy, struct jobSet* jobList); | ||
95 | static int shell_pwd(struct job* dummy, struct jobSet* junk); | ||
96 | static int shell_set(struct job* cmd, struct jobSet* junk); | ||
97 | static int shell_source(struct job* cmd, struct jobSet* jobList); | ||
98 | static int shell_unset(struct job* cmd, struct jobSet* junk); | ||
99 | |||
100 | static void checkJobs(struct jobSet * jobList); | ||
101 | static int getCommand(FILE * source, char * command); | ||
102 | static int parseCommand(char ** commandPtr, struct job * job, int * isBg); | ||
103 | static int setupRedirections(struct childProgram * prog); | ||
104 | static int runCommand(struct job newJob, struct jobSet * jobList, int inBg); | ||
105 | static int busy_loop(FILE * input); | ||
106 | |||
107 | |||
108 | /* Table of built-in functions */ | ||
109 | static struct builtInCommand bltins[] = { | ||
110 | {"bg", "Resume a job in the background", "bg [%%job]", shell_fg_bg}, | ||
111 | {"cd", "Change working directory", "cd [dir]", shell_cd}, | ||
112 | {"env", "Print all environment variables", "env", shell_env}, | ||
113 | {"exit", "Exit from shell()", "exit", shell_exit}, | ||
114 | {"fg", "Bring job into the foreground", "fg [%%job]", shell_fg_bg}, | ||
115 | {"jobs", "Lists the active jobs", "jobs", shell_jobs}, | ||
116 | {"pwd", "Print current directory", "pwd", shell_pwd}, | ||
117 | {"set", "Set environment variable", "set [VAR=value]", shell_set}, | ||
118 | {"unset", "Unset environment variable", "unset VAR", shell_unset}, | ||
119 | //{"echo", "Echo arguments on stdout", "echo arg1 [...]", shell_echo}, | ||
120 | {".", "Source-in and run commands in a file", ". filename", shell_source}, | ||
121 | {"help", "List shell built-in commands", "help", shell_help}, | ||
122 | {NULL, NULL, NULL, NULL} | ||
123 | }; | ||
124 | |||
125 | static const char shell_usage[] = | ||
126 | "sh [FILE]...\n\n" | ||
127 | "The BusyBox command interpreter (shell).\n\n"; | ||
128 | |||
129 | |||
130 | static char cwd[1024]; | ||
131 | static char *prompt = "# "; | ||
132 | |||
133 | |||
134 | /* built-in 'cd <path>' handler */ | ||
135 | static int shell_cd(struct job* cmd, struct jobSet* junk) | ||
136 | { | ||
137 | char *newdir; | ||
138 | if (!cmd->progs[0].argv[1] == 1) | ||
139 | newdir = getenv("HOME"); | ||
140 | else | ||
141 | newdir = cmd->progs[0].argv[1]; | ||
142 | if (chdir(newdir)) { | ||
143 | printf("cd: %s: %s\n", newdir, strerror(errno)); | ||
144 | return FALSE; | ||
145 | } | ||
146 | getcwd(cwd, sizeof(cwd)); | ||
147 | |||
148 | return TRUE; | ||
149 | } | ||
150 | |||
151 | /* built-in 'env' handler */ | ||
152 | static int shell_env(struct job* dummy, struct jobSet* junk) | ||
153 | { | ||
154 | char **e; | ||
155 | |||
156 | for (e = environ ; *e ; e++) { | ||
157 | fprintf(stdout, "%s\n", *e); | ||
158 | } | ||
159 | return (0); | ||
160 | } | ||
161 | |||
162 | /* built-in 'exit' handler */ | ||
163 | static int shell_exit(struct job* cmd, struct jobSet* junk) | ||
164 | { | ||
165 | if (!cmd->progs[0].argv[1] == 1) | ||
166 | exit TRUE; | ||
167 | else | ||
168 | exit(atoi(cmd->progs[0].argv[1])); | ||
169 | } | ||
170 | |||
171 | /* built-in 'fg' and 'bg' handler */ | ||
172 | static int shell_fg_bg(struct job* cmd, struct jobSet* jobList) | ||
173 | { | ||
174 | int i, jobNum; | ||
175 | struct job* job; | ||
176 | |||
177 | if (!cmd->progs[0].argv[1] || cmd->progs[0].argv[2]) { | ||
178 | fprintf(stderr, "%s: exactly one argument is expected\n", | ||
179 | cmd->progs[0].argv[0]); | ||
180 | return FALSE; | ||
181 | } | ||
182 | |||
183 | if (sscanf(cmd->progs[0].argv[1], "%%%d", &jobNum) != 1) { | ||
184 | fprintf(stderr, "%s: bad argument '%s'\n", | ||
185 | cmd->progs[0].argv[0], cmd->progs[0].argv[1]); | ||
186 | return FALSE; | ||
187 | } | ||
188 | |||
189 | for (job = jobList->head; job; job = job->next) | ||
190 | if (job->jobId == jobNum) break; | ||
191 | |||
192 | if (!job) { | ||
193 | fprintf(stderr, "%s: unknown job %d\n", | ||
194 | cmd->progs[0].argv[0], jobNum); | ||
195 | return FALSE; | ||
196 | } | ||
197 | |||
198 | if (*cmd->progs[0].argv[0] == 'f') { | ||
199 | /* Make this job the foreground job */ | ||
200 | |||
201 | if (tcsetpgrp(0, job->pgrp)) | ||
202 | perror("tcsetpgrp"); | ||
203 | jobList->fg = job; | ||
204 | } | ||
205 | |||
206 | /* Restart the processes in the job */ | ||
207 | for (i = 0; i < job->numProgs; i++) | ||
208 | job->progs[i].isStopped = 0; | ||
209 | |||
210 | kill(-job->pgrp, SIGCONT); | ||
211 | |||
212 | job->stoppedProgs = 0; | ||
213 | |||
214 | return TRUE; | ||
215 | } | ||
216 | |||
217 | /* built-in 'help' handler */ | ||
218 | static int shell_help(struct job* cmd, struct jobSet* junk) | ||
219 | { | ||
220 | struct builtInCommand *x; | ||
221 | |||
222 | fprintf(stdout, "\nBuilt-in commands:\n"); | ||
223 | fprintf(stdout, "-------------------\n"); | ||
224 | for ( x=bltins; x->cmd; x++) { | ||
225 | fprintf(stdout, "%s\t%s\n", x->cmd, x->descr); | ||
226 | } | ||
227 | fprintf(stdout, "\n\n"); | ||
228 | return TRUE; | ||
229 | } | ||
230 | |||
231 | /* built-in 'jobs' handler */ | ||
232 | static int shell_jobs(struct job* dummy, struct jobSet* jobList) | ||
233 | { | ||
234 | struct job * job; | ||
235 | char * statusString; | ||
236 | for (job = jobList->head; job; job = job->next) { | ||
237 | if (job->runningProgs == job->stoppedProgs) | ||
238 | statusString = "Stopped"; | ||
239 | else | ||
240 | statusString = "Running"; | ||
241 | |||
242 | printf(JOB_STATUS_FORMAT, job->jobId, statusString, | ||
243 | job->text); | ||
244 | } | ||
245 | return TRUE; | ||
246 | } | ||
247 | |||
248 | |||
249 | /* built-in 'pwd' handler */ | ||
250 | static int shell_pwd(struct job* dummy, struct jobSet* junk) | ||
251 | { | ||
252 | getcwd(cwd, sizeof(cwd)); | ||
253 | fprintf(stdout, "%s\n", cwd); | ||
254 | return TRUE; | ||
255 | } | ||
256 | |||
257 | /* built-in 'set VAR=value' handler */ | ||
258 | static int shell_set(struct job* cmd, struct jobSet* junk) | ||
259 | { | ||
260 | int res; | ||
261 | |||
262 | if (!cmd->progs[0].argv[1] == 1) { | ||
263 | return (shell_env(cmd, junk)); | ||
264 | } | ||
265 | res = putenv(cmd->progs[0].argv[1]); | ||
266 | if (res) | ||
267 | fprintf(stdout, "set: %s\n", strerror(errno)); | ||
268 | return (res); | ||
269 | } | ||
270 | |||
271 | /* Built-in '.' handler (read-in and execute commands from file) */ | ||
272 | static int shell_source(struct job* cmd, struct jobSet* junk) | ||
273 | { | ||
274 | FILE *input; | ||
275 | int status; | ||
276 | |||
277 | if (!cmd->progs[0].argv[1] == 1) | ||
278 | return FALSE; | ||
279 | |||
280 | input = fopen(cmd->progs[0].argv[1], "r"); | ||
281 | if (!input) { | ||
282 | fprintf(stdout, "Couldn't open file '%s'\n", cmd->progs[0].argv[1]); | ||
283 | return FALSE; | ||
284 | } | ||
285 | |||
286 | /* Now run the file */ | ||
287 | status = busy_loop(input); | ||
288 | return (status); | ||
289 | } | ||
290 | |||
291 | /* built-in 'unset VAR' handler */ | ||
292 | static int shell_unset(struct job* cmd, struct jobSet* junk) | ||
293 | { | ||
294 | if (!cmd->progs[0].argv[1] == 1) { | ||
295 | fprintf(stdout, "unset: parameter required.\n"); | ||
296 | return FALSE; | ||
297 | } | ||
298 | unsetenv(cmd->progs[0].argv[1]); | ||
299 | return TRUE; | ||
300 | } | ||
301 | |||
302 | /* free up all memory from a job */ | ||
303 | static void freeJob(struct job * cmd) | ||
304 | { | ||
305 | int i; | ||
306 | |||
307 | for (i = 0; i < cmd->numProgs; i++) { | ||
308 | free(cmd->progs[i].argv); | ||
309 | if (cmd->progs[i].redirections) free(cmd->progs[i].redirections); | ||
310 | if (cmd->progs[i].freeGlob) globfree(&cmd->progs[i].globResult); | ||
311 | } | ||
312 | free(cmd->progs); | ||
313 | if (cmd->text) free(cmd->text); | ||
314 | free(cmd->cmdBuf); | ||
315 | } | ||
316 | |||
317 | /* remove a job from the jobList */ | ||
318 | static void removeJob(struct jobSet * jobList, struct job * job) | ||
319 | { | ||
320 | struct job * prevJob; | ||
321 | |||
322 | freeJob(job); | ||
323 | if (job == jobList->head) { | ||
324 | jobList->head = job->next; | ||
325 | } else { | ||
326 | prevJob = jobList->head; | ||
327 | while (prevJob->next != job) prevJob = prevJob->next; | ||
328 | prevJob->next = job->next; | ||
329 | } | ||
330 | |||
331 | free(job); | ||
332 | } | ||
333 | |||
334 | /* Checks to see if any background processes have exited -- if they | ||
335 | have, figure out why and see if a job has completed */ | ||
336 | static void checkJobs(struct jobSet * jobList) | ||
337 | { | ||
338 | struct job * job; | ||
339 | pid_t childpid; | ||
340 | int status; | ||
341 | int progNum=0; | ||
342 | |||
343 | while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { | ||
344 | for (job = jobList->head; job; job = job->next) { | ||
345 | progNum = 0; | ||
346 | while (progNum < job->numProgs && | ||
347 | job->progs[progNum].pid != childpid) | ||
348 | progNum++; | ||
349 | if (progNum < job->numProgs) break; | ||
350 | } | ||
351 | |||
352 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | ||
353 | /* child exited */ | ||
354 | job->runningProgs--; | ||
355 | job->progs[progNum].pid = 0; | ||
356 | |||
357 | if (!job->runningProgs) { | ||
358 | printf(JOB_STATUS_FORMAT, job->jobId, "Done", job->text); | ||
359 | removeJob(jobList, job); | ||
360 | } | ||
361 | } else { | ||
362 | /* child stopped */ | ||
363 | job->stoppedProgs++; | ||
364 | job->progs[progNum].isStopped = 1; | ||
365 | |||
366 | if (job->stoppedProgs == job->numProgs) { | ||
367 | printf(JOB_STATUS_FORMAT, job->jobId, "Stopped", job->text); | ||
368 | } | ||
369 | } | ||
370 | } | ||
371 | |||
372 | if (childpid == -1 && errno != ECHILD) | ||
373 | perror("waitpid"); | ||
374 | } | ||
375 | |||
376 | static int getCommand(FILE * source, char * command) | ||
377 | { | ||
378 | if (source == stdin) { | ||
379 | fprintf(stdout, "%s %s", cwd, prompt); | ||
380 | fflush(stdout); | ||
381 | } | ||
382 | |||
383 | if (!fgets(command, MAX_COMMAND_LEN, source)) { | ||
384 | if (source == stdin) printf("\n"); | ||
385 | return 1; | ||
386 | } | ||
387 | |||
388 | /* remove trailing newline */ | ||
389 | command[strlen(command) - 1] = '\0'; | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static void globLastArgument(struct childProgram * prog, int * argcPtr, | ||
395 | int * argcAllocedPtr) | ||
396 | { | ||
397 | int argc = *argcPtr; | ||
398 | int argcAlloced = *argcAllocedPtr; | ||
399 | int rc; | ||
400 | int flags; | ||
401 | int i; | ||
402 | char * src, * dst; | ||
403 | |||
404 | if (argc > 1) { /* cmd->globResult is already initialized */ | ||
405 | flags = GLOB_APPEND; | ||
406 | i = prog->globResult.gl_pathc; | ||
407 | } else { | ||
408 | prog->freeGlob = 1; | ||
409 | flags = 0; | ||
410 | i = 0; | ||
411 | } | ||
412 | |||
413 | rc = glob(prog->argv[argc - 1], flags, NULL, &prog->globResult); | ||
414 | if (rc == GLOB_NOSPACE) { | ||
415 | fprintf(stderr, "out of space during glob operation\n"); | ||
416 | return; | ||
417 | } else if (rc == GLOB_NOMATCH || | ||
418 | (!rc && (prog->globResult.gl_pathc - i) == 1 && | ||
419 | !strcmp(prog->argv[argc - 1], | ||
420 | prog->globResult.gl_pathv[i]))) { | ||
421 | /* we need to remove whatever \ quoting is still present */ | ||
422 | src = dst = prog->argv[argc - 1]; | ||
423 | while (*src) { | ||
424 | if (*src != '\\') *dst++ = *src; | ||
425 | src++; | ||
426 | } | ||
427 | *dst = '\0'; | ||
428 | } else if (!rc) { | ||
429 | argcAlloced += (prog->globResult.gl_pathc - i); | ||
430 | prog->argv = realloc(prog->argv, argcAlloced * sizeof(*prog->argv)); | ||
431 | memcpy(prog->argv + (argc - 1), prog->globResult.gl_pathv + i, | ||
432 | sizeof(*(prog->argv)) * (prog->globResult.gl_pathc - i)); | ||
433 | argc += (prog->globResult.gl_pathc - i - 1); | ||
434 | } | ||
435 | |||
436 | *argcAllocedPtr = argcAlloced; | ||
437 | *argcPtr = argc; | ||
438 | } | ||
439 | |||
440 | /* Return cmd->numProgs as 0 if no command is present (e.g. an empty | ||
441 | line). If a valid command is found, commandPtr is set to point to | ||
442 | the beginning of the next command (if the original command had more | ||
443 | then one job associated with it) or NULL if no more commands are | ||
444 | present. */ | ||
445 | static int parseCommand(char ** commandPtr, struct job * job, int * isBg) | ||
446 | { | ||
447 | char * command; | ||
448 | char * returnCommand = NULL; | ||
449 | char * src, * buf, * chptr; | ||
450 | int argc = 0; | ||
451 | int done = 0; | ||
452 | int argvAlloced; | ||
453 | int i; | ||
454 | char quote = '\0'; | ||
455 | int count; | ||
456 | struct childProgram * prog; | ||
457 | |||
458 | /* skip leading white space */ | ||
459 | while (**commandPtr && isspace(**commandPtr)) (*commandPtr)++; | ||
460 | |||
461 | /* this handles empty lines or leading '#' characters */ | ||
462 | if (!**commandPtr || (**commandPtr=='#')) { | ||
463 | job->numProgs = 0; | ||
464 | *commandPtr = NULL; | ||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | *isBg = 0; | ||
469 | job->numProgs = 1; | ||
470 | job->progs = malloc(sizeof(*job->progs)); | ||
471 | |||
472 | /* We set the argv elements to point inside of this string. The | ||
473 | memory is freed by freeJob(). | ||
474 | |||
475 | Getting clean memory relieves us of the task of NULL | ||
476 | terminating things and makes the rest of this look a bit | ||
477 | cleaner (though it is, admittedly, a tad less efficient) */ | ||
478 | job->cmdBuf = command = calloc(1, strlen(*commandPtr) + 1); | ||
479 | job->text = NULL; | ||
480 | |||
481 | prog = job->progs; | ||
482 | prog->numRedirections = 0; | ||
483 | prog->redirections = NULL; | ||
484 | prog->freeGlob = 0; | ||
485 | prog->isStopped = 0; | ||
486 | |||
487 | argvAlloced = 5; | ||
488 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | ||
489 | prog->argv[0] = job->cmdBuf; | ||
490 | |||
491 | buf = command; | ||
492 | src = *commandPtr; | ||
493 | while (*src && !done) { | ||
494 | if (quote == *src) { | ||
495 | quote = '\0'; | ||
496 | } else if (quote) { | ||
497 | if (*src == '\\') { | ||
498 | src++; | ||
499 | if (!*src) { | ||
500 | fprintf(stderr, "character expected after \\\n"); | ||
501 | freeJob(job); | ||
502 | return 1; | ||
503 | } | ||
504 | |||
505 | /* in shell, "\'" should yield \' */ | ||
506 | if (*src != quote) *buf++ = '\\'; | ||
507 | } else if (*src == '*' || *src == '?' || *src == '[' || | ||
508 | *src == ']') | ||
509 | *buf++ = '\\'; | ||
510 | *buf++ = *src; | ||
511 | } else if (isspace(*src)) { | ||
512 | if (*prog->argv[argc]) { | ||
513 | buf++, argc++; | ||
514 | /* +1 here leaves room for the NULL which ends argv */ | ||
515 | if ((argc + 1) == argvAlloced) { | ||
516 | argvAlloced += 5; | ||
517 | prog->argv = realloc(prog->argv, | ||
518 | sizeof(*prog->argv) * argvAlloced); | ||
519 | } | ||
520 | prog->argv[argc] = buf; | ||
521 | |||
522 | globLastArgument(prog, &argc, &argvAlloced); | ||
523 | } | ||
524 | } else switch (*src) { | ||
525 | case '"': | ||
526 | case '\'': | ||
527 | quote = *src; | ||
528 | break; | ||
529 | |||
530 | case '#': /* comment */ | ||
531 | done = 1; | ||
532 | break; | ||
533 | |||
534 | case '>': /* redirections */ | ||
535 | case '<': | ||
536 | i = prog->numRedirections++; | ||
537 | prog->redirections = realloc(prog->redirections, | ||
538 | sizeof(*prog->redirections) * (i + 1)); | ||
539 | |||
540 | prog->redirections[i].fd = -1; | ||
541 | if (buf != prog->argv[argc]) { | ||
542 | /* the stuff before this character may be the file number | ||
543 | being redirected */ | ||
544 | prog->redirections[i].fd = strtol(prog->argv[argc], &chptr, 10); | ||
545 | |||
546 | if (*chptr && *prog->argv[argc]) { | ||
547 | buf++, argc++; | ||
548 | globLastArgument(prog, &argc, &argvAlloced); | ||
549 | } | ||
550 | } | ||
551 | |||
552 | if (prog->redirections[i].fd == -1) { | ||
553 | if (*src == '>') | ||
554 | prog->redirections[i].fd = 1; | ||
555 | else | ||
556 | prog->redirections[i].fd = 0; | ||
557 | } | ||
558 | |||
559 | if (*src++ == '>') { | ||
560 | if (*src == '>') | ||
561 | prog->redirections[i].type = REDIRECT_APPEND, src++; | ||
562 | else | ||
563 | prog->redirections[i].type = REDIRECT_OVERWRITE; | ||
564 | } else { | ||
565 | prog->redirections[i].type = REDIRECT_INPUT; | ||
566 | } | ||
567 | |||
568 | /* This isn't POSIX sh compliant. Oh well. */ | ||
569 | chptr = src; | ||
570 | while (isspace(*chptr)) chptr++; | ||
571 | |||
572 | if (!*chptr) { | ||
573 | fprintf(stderr, "file name expected after %c\n", *src); | ||
574 | freeJob(job); | ||
575 | return 1; | ||
576 | } | ||
577 | |||
578 | prog->redirections[i].filename = buf; | ||
579 | while (*chptr && !isspace(*chptr)) | ||
580 | *buf++ = *chptr++; | ||
581 | |||
582 | src = chptr - 1; /* we src++ later */ | ||
583 | prog->argv[argc] = ++buf; | ||
584 | break; | ||
585 | |||
586 | case '|': /* pipe */ | ||
587 | /* finish this command */ | ||
588 | if (*prog->argv[argc]) argc++; | ||
589 | if (!argc) { | ||
590 | fprintf(stderr, "empty command in pipe\n"); | ||
591 | freeJob(job); | ||
592 | return 1; | ||
593 | } | ||
594 | prog->argv[argc] = NULL; | ||
595 | |||
596 | /* and start the next */ | ||
597 | job->numProgs++; | ||
598 | job->progs = realloc(job->progs, | ||
599 | sizeof(*job->progs) * job->numProgs); | ||
600 | prog = job->progs + (job->numProgs - 1); | ||
601 | prog->numRedirections = 0; | ||
602 | prog->redirections = NULL; | ||
603 | prog->freeGlob = 0; | ||
604 | argc = 0; | ||
605 | |||
606 | argvAlloced = 5; | ||
607 | prog->argv = malloc(sizeof(*prog->argv) * argvAlloced); | ||
608 | prog->argv[0] = ++buf; | ||
609 | |||
610 | src++; | ||
611 | while (*src && isspace(*src)) src++; | ||
612 | |||
613 | if (!*src) { | ||
614 | fprintf(stderr, "empty command in pipe\n"); | ||
615 | return 1; | ||
616 | } | ||
617 | src--; /* we'll ++ it at the end of the loop */ | ||
618 | |||
619 | break; | ||
620 | |||
621 | case '&': /* background */ | ||
622 | *isBg = 1; | ||
623 | case ';': /* multiple commands */ | ||
624 | done = 1; | ||
625 | returnCommand = *commandPtr + (src - *commandPtr) + 1; | ||
626 | break; | ||
627 | |||
628 | case '\\': | ||
629 | src++; | ||
630 | if (!*src) { | ||
631 | freeJob(job); | ||
632 | fprintf(stderr, "character expected after \\\n"); | ||
633 | return 1; | ||
634 | } | ||
635 | if (*src == '*' || *src == '[' || *src == ']' || *src == '?') | ||
636 | *buf++ = '\\'; | ||
637 | /* fallthrough */ | ||
638 | default: | ||
639 | *buf++ = *src; | ||
640 | } | ||
641 | |||
642 | src++; | ||
643 | } | ||
644 | |||
645 | if (*prog->argv[argc]) { | ||
646 | argc++; | ||
647 | globLastArgument(prog, &argc, &argvAlloced); | ||
648 | } | ||
649 | if (!argc) { | ||
650 | freeJob(job); | ||
651 | return 0; | ||
652 | } | ||
653 | prog->argv[argc] = NULL; | ||
654 | |||
655 | if (!returnCommand) { | ||
656 | job->text = malloc(strlen(*commandPtr) + 1); | ||
657 | strcpy(job->text, *commandPtr); | ||
658 | } else { | ||
659 | /* This leaves any trailing spaces, which is a bit sloppy */ | ||
660 | |||
661 | count = returnCommand - *commandPtr; | ||
662 | job->text = malloc(count + 1); | ||
663 | strncpy(job->text, *commandPtr, count); | ||
664 | job->text[count] = '\0'; | ||
665 | } | ||
666 | |||
667 | *commandPtr = returnCommand; | ||
668 | |||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | static int runCommand(struct job newJob, struct jobSet * jobList, | ||
673 | int inBg) | ||
674 | { | ||
675 | struct job * job; | ||
676 | int i; | ||
677 | int nextin, nextout; | ||
678 | int pipefds[2]; /* pipefd[0] is for reading */ | ||
679 | struct builtInCommand *x; | ||
680 | |||
681 | /* handle built-ins here -- we don't fork() so we can't background | ||
682 | these very easily */ | ||
683 | for( x=bltins ; x->cmd ; x++) { | ||
684 | if (!strcmp(newJob.progs[0].argv[0], x->cmd)) { | ||
685 | return(x->function(&newJob, jobList)); | ||
686 | } | ||
687 | } | ||
688 | |||
689 | nextin = 0, nextout = 1; | ||
690 | for (i = 0; i < newJob.numProgs; i++) { | ||
691 | if ((i + 1) < newJob.numProgs) { | ||
692 | pipe(pipefds); | ||
693 | nextout = pipefds[1]; | ||
694 | } else { | ||
695 | nextout = 1; | ||
696 | } | ||
697 | |||
698 | if (!(newJob.progs[i].pid = fork())) { | ||
699 | signal(SIGTTOU, SIG_DFL); | ||
700 | |||
701 | if (nextin != 0) { | ||
702 | dup2(nextin, 0); | ||
703 | close(nextin); | ||
704 | } | ||
705 | |||
706 | if (nextout != 1) { | ||
707 | dup2(nextout, 1); | ||
708 | close(nextout); | ||
709 | } | ||
710 | |||
711 | /* explicit redirections override pipes */ | ||
712 | setupRedirections(newJob.progs + i); | ||
713 | |||
714 | execvp(newJob.progs[i].argv[0], newJob.progs[i].argv); | ||
715 | fatalError( "sh: %s: %s\n", newJob.progs[i].argv[0], | ||
716 | strerror(errno)); | ||
717 | } | ||
718 | |||
719 | /* put our child in the process group whose leader is the | ||
720 | first process in this pipe */ | ||
721 | setpgid(newJob.progs[i].pid, newJob.progs[0].pid); | ||
722 | |||
723 | if (nextin != 0) close(nextin); | ||
724 | if (nextout != 1) close(nextout); | ||
725 | |||
726 | /* If there isn't another process, nextin is garbage | ||
727 | but it doesn't matter */ | ||
728 | nextin = pipefds[0]; | ||
729 | } | ||
730 | |||
731 | newJob.pgrp = newJob.progs[0].pid; | ||
732 | |||
733 | /* find the ID for the job to use */ | ||
734 | newJob.jobId = 1; | ||
735 | for (job = jobList->head; job; job = job->next) | ||
736 | if (job->jobId >= newJob.jobId) | ||
737 | newJob.jobId = job->jobId + 1; | ||
738 | |||
739 | /* add the job to the list of running jobs */ | ||
740 | if (!jobList->head) { | ||
741 | job = jobList->head = malloc(sizeof(*job)); | ||
742 | } else { | ||
743 | for (job = jobList->head; job->next; job = job->next); | ||
744 | job->next = malloc(sizeof(*job)); | ||
745 | job = job->next; | ||
746 | } | ||
747 | |||
748 | *job = newJob; | ||
749 | job->next = NULL; | ||
750 | job->runningProgs = job->numProgs; | ||
751 | job->stoppedProgs = 0; | ||
752 | |||
753 | if (inBg) { | ||
754 | /* we don't wait for background jobs to return -- append it | ||
755 | to the list of backgrounded jobs and leave it alone */ | ||
756 | |||
757 | printf("[%d] %d\n", job->jobId, | ||
758 | newJob.progs[newJob.numProgs - 1].pid); | ||
759 | } else { | ||
760 | jobList->fg = job; | ||
761 | |||
762 | /* move the new process group into the foreground */ | ||
763 | |||
764 | if (tcsetpgrp(0, newJob.pgrp)) | ||
765 | perror("tcsetpgrp"); | ||
766 | } | ||
767 | |||
768 | return 0; | ||
769 | } | ||
770 | |||
771 | static int setupRedirections(struct childProgram * prog) | ||
772 | { | ||
773 | int i; | ||
774 | int openfd; | ||
775 | int mode=O_RDONLY; | ||
776 | struct redirectionSpecifier * redir = prog->redirections; | ||
777 | |||
778 | for (i = 0; i < prog->numRedirections; i++, redir++) { | ||
779 | switch (redir->type) { | ||
780 | case REDIRECT_INPUT: | ||
781 | mode = O_RDONLY; | ||
782 | break; | ||
783 | case REDIRECT_OVERWRITE: | ||
784 | mode = O_RDWR | O_CREAT | O_TRUNC; | ||
785 | break; | ||
786 | case REDIRECT_APPEND: | ||
787 | mode = O_RDWR | O_CREAT | O_APPEND; | ||
788 | break; | ||
789 | } | ||
790 | |||
791 | openfd = open(redir->filename, mode, 0666); | ||
792 | if (openfd < 0) { | ||
793 | /* this could get lost if stderr has been redirected, but | ||
794 | bash and ash both lose it as well (though zsh doesn't!) */ | ||
795 | fprintf(stderr, "error opening %s: %s\n", redir->filename, | ||
796 | strerror(errno)); | ||
797 | return 1; | ||
798 | } | ||
799 | |||
800 | if (openfd != redir->fd) { | ||
801 | dup2(openfd, redir->fd); | ||
802 | close(openfd); | ||
803 | } | ||
804 | } | ||
805 | |||
806 | return 0; | ||
807 | } | ||
808 | |||
809 | |||
810 | static int busy_loop(FILE * input) | ||
811 | { | ||
812 | char command[MAX_COMMAND_LEN + 1]; | ||
813 | char * nextCommand = NULL; | ||
814 | struct jobSet jobList = { NULL, NULL }; | ||
815 | struct job newJob; | ||
816 | int i; | ||
817 | int status; | ||
818 | int inBg; | ||
819 | |||
820 | /* don't pay any attention to this signal; it just confuses | ||
821 | things and isn't really meant for shells anyway */ | ||
822 | signal(SIGTTOU, SIG_IGN); | ||
823 | |||
824 | while (1) { | ||
825 | if (!jobList.fg) { | ||
826 | /* no job is in the foreground */ | ||
827 | |||
828 | /* see if any background processes have exited */ | ||
829 | checkJobs(&jobList); | ||
830 | |||
831 | if (!nextCommand) { | ||
832 | if (getCommand(input, command)) break; | ||
833 | nextCommand = command; | ||
834 | } | ||
835 | |||
836 | if (!parseCommand(&nextCommand, &newJob, &inBg) && | ||
837 | newJob.numProgs) { | ||
838 | runCommand(newJob, &jobList, inBg); | ||
839 | } | ||
840 | } else { | ||
841 | /* a job is running in the foreground; wait for it */ | ||
842 | i = 0; | ||
843 | while (!jobList.fg->progs[i].pid || | ||
844 | jobList.fg->progs[i].isStopped) i++; | ||
845 | |||
846 | waitpid(jobList.fg->progs[i].pid, &status, WUNTRACED); | ||
847 | |||
848 | if (WIFEXITED(status) || WIFSIGNALED(status)) { | ||
849 | /* the child exited */ | ||
850 | jobList.fg->runningProgs--; | ||
851 | jobList.fg->progs[i].pid = 0; | ||
852 | |||
853 | if (!jobList.fg->runningProgs) { | ||
854 | /* child exited */ | ||
855 | |||
856 | removeJob(&jobList, jobList.fg); | ||
857 | jobList.fg = NULL; | ||
858 | |||
859 | /* move the shell to the foreground */ | ||
860 | if (tcsetpgrp(0, getpid())) | ||
861 | perror("tcsetpgrp"); | ||
862 | } | ||
863 | } else { | ||
864 | /* the child was stopped */ | ||
865 | jobList.fg->stoppedProgs++; | ||
866 | jobList.fg->progs[i].isStopped = 1; | ||
867 | |||
868 | if (jobList.fg->stoppedProgs == jobList.fg->runningProgs) { | ||
869 | printf("\n" JOB_STATUS_FORMAT, jobList.fg->jobId, | ||
870 | "Stopped", jobList.fg->text); | ||
871 | jobList.fg = NULL; | ||
872 | } | ||
873 | } | ||
874 | |||
875 | if (!jobList.fg) { | ||
876 | /* move the shell to the foreground */ | ||
877 | if (tcsetpgrp(0, getpid())) | ||
878 | perror("tcsetpgrp"); | ||
879 | } | ||
880 | } | ||
881 | } | ||
882 | |||
883 | return 0; | ||
884 | } | ||
885 | |||
886 | |||
887 | int shell_main(int argc, char ** argv) | ||
888 | { | ||
889 | FILE * input = stdin; | ||
890 | |||
891 | if (argc > 2) { | ||
892 | usage( shell_usage); | ||
893 | } | ||
894 | /* initialize the cwd */ | ||
895 | getcwd(cwd, sizeof(cwd)); | ||
896 | |||
897 | |||
898 | //if (argv[0] && argv[0][0] == '-') { | ||
899 | // shell_source("/etc/profile"); | ||
900 | //} | ||
901 | |||
902 | if (argc < 2) { | ||
903 | fprintf(stdout, "\n\nBusyBox v%s (%s) Built-in shell\n", BB_VER, BB_BT); | ||
904 | fprintf(stdout, "Enter 'help' for a list of built-in commands.\n\n"); | ||
905 | } else { | ||
906 | input = fopen(argv[1], "r"); | ||
907 | if (!input) | ||
908 | fatalError("A: Couldn't open file '%s': %s\n", argv[1], strerror(errno)); | ||
909 | // else | ||
910 | // fatalError("Got it.\n"); | ||
911 | //exit(shell_source(argv[1])); | ||
912 | } | ||
913 | |||
914 | return (busy_loop( input)); | ||
915 | } | ||
@@ -96,8 +96,8 @@ extern void errorMsg(char *s, ...) | |||
96 | va_start(p, s); | 96 | va_start(p, s); |
97 | fflush(stdout); | 97 | fflush(stdout); |
98 | vfprintf(stderr, s, p); | 98 | vfprintf(stderr, s, p); |
99 | fprintf(stderr, "\n"); | ||
100 | va_end(p); | 99 | va_end(p); |
100 | fflush(stderr); | ||
101 | } | 101 | } |
102 | 102 | ||
103 | extern void fatalError(char *s, ...) | 103 | extern void fatalError(char *s, ...) |
@@ -107,8 +107,8 @@ extern void fatalError(char *s, ...) | |||
107 | va_start(p, s); | 107 | va_start(p, s); |
108 | fflush(stdout); | 108 | fflush(stdout); |
109 | vfprintf(stderr, s, p); | 109 | vfprintf(stderr, s, p); |
110 | fprintf(stderr, "\n"); | ||
111 | va_end(p); | 110 | va_end(p); |
111 | fflush(stderr); | ||
112 | exit( FALSE); | 112 | exit( FALSE); |
113 | } | 113 | } |
114 | 114 | ||