source: MondoRescue/branches/stable/mindi-busybox/shell/lash.c@ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File size: 39.0 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * lash -- the BusyBox Lame-Ass SHell
4 *
5 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6 *
7 * Based in part on ladsh.c by Michael K. Johnson and Erik W. Troan, which is
8 * under the following liberal license: "We have placed this source code in the
9 * public domain. Use it in any project, free or commercial."
10 *
11 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
12 */
13
14/* This shell's parsing engine is officially at a dead-end. Future
15 * work shell work should be done using hush, msh, or ash. This is
16 * still a very useful, small shell -- it just don't need any more
17 * features beyond what it already has...
18 */
19
20//For debugging/development on the shell only...
21//#define DEBUG_SHELL
22
23#include <getopt.h>
24#include <glob.h>
25
26#include "busybox.h" /* for struct bb_applet */
27
28#define expand_t glob_t
29
30/* Always enable for the moment... */
31#define CONFIG_LASH_PIPE_N_REDIRECTS
32#define CONFIG_LASH_JOB_CONTROL
33#define ENABLE_LASH_PIPE_N_REDIRECTS 1
34#define ENABLE_LASH_JOB_CONTROL 1
35
36
37enum { MAX_READ = 128 }; /* size of input buffer for 'read' builtin */
38#define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n"
39
40
41#if ENABLE_LASH_PIPE_N_REDIRECTS
42enum redir_type { REDIRECT_INPUT, REDIRECT_OVERWRITE,
43 REDIRECT_APPEND
44};
45#endif
46
47enum {
48 DEFAULT_CONTEXT = 0x1,
49 IF_TRUE_CONTEXT = 0x2,
50 IF_FALSE_CONTEXT = 0x4,
51 THEN_EXP_CONTEXT = 0x8,
52 ELSE_EXP_CONTEXT = 0x10
53};
54
55#define LASH_OPT_DONE (1)
56#define LASH_OPT_SAW_QUOTE (2)
57
58#if ENABLE_LASH_PIPE_N_REDIRECTS
59struct redir_struct {
60 enum redir_type type; /* type of redirection */
61 int fd; /* file descriptor being redirected */
62 char *filename; /* file to redirect fd to */
63};
64#endif
65
66struct child_prog {
67 pid_t pid; /* 0 if exited */
68 char **argv; /* program name and arguments */
69 int num_redirects; /* elements in redirection array */
70 int is_stopped; /* is the program currently running? */
71 struct job *family; /* pointer back to the child's parent job */
72#if ENABLE_LASH_PIPE_N_REDIRECTS
73 struct redir_struct *redirects; /* I/O redirects */
74#endif
75};
76
77struct jobset {
78 struct job *head; /* head of list of running jobs */
79 struct job *fg; /* current foreground job */
80};
81
82struct job {
83 int jobid; /* job number */
84 int num_progs; /* total number of programs in job */
85 int running_progs; /* number of programs running */
86 char *text; /* name of job */
87 char *cmdbuf; /* buffer various argv's point into */
88 pid_t pgrp; /* process group ID for the job */
89 struct child_prog *progs; /* array of programs in job */
90 struct job *next; /* to track background commands */
91 int stopped_progs; /* number of programs alive, but stopped */
92 unsigned int job_context; /* bitmask defining current context */
93 struct jobset *job_list;
94};
95
96struct built_in_command {
97 const char *cmd; /* name */
98 const char *descr; /* description */
99 int (*function) (struct child_prog *); /* function ptr */
100};
101
102/* function prototypes for builtins */
103static int builtin_cd(struct child_prog *cmd);
104static int builtin_exec(struct child_prog *cmd);
105static int builtin_exit(struct child_prog *cmd);
106static int builtin_fg_bg(struct child_prog *cmd);
107static int builtin_help(struct child_prog *cmd);
108static int builtin_jobs(struct child_prog *dummy);
109static int builtin_pwd(struct child_prog *dummy);
110static int builtin_export(struct child_prog *cmd);
111static int builtin_source(struct child_prog *cmd);
112static int builtin_unset(struct child_prog *cmd);
113static int builtin_read(struct child_prog *cmd);
114
115
116/* function prototypes for shell stuff */
117static void checkjobs(struct jobset *job_list);
118static void remove_job(struct jobset *j_list, struct job *job);
119static int get_command_bufsiz(FILE * source, char *command);
120static int parse_command(char **command_ptr, struct job *job, int *inbg);
121static int run_command(struct job *newjob, int inbg, int outpipe[2]);
122static int pseudo_exec(struct child_prog *cmd) ATTRIBUTE_NORETURN;
123static int busy_loop(FILE * input);
124
125
126/* Table of built-in functions (these are non-forking builtins, meaning they
127 * can change global variables in the parent shell process but they will not
128 * work with pipes and redirects; 'unset foo | whatever' will not work) */
129static const struct built_in_command bltins[] = {
130 {"bg" , "Resume a job in the background", builtin_fg_bg},
131 {"cd" , "Change working directory", builtin_cd},
132 {"exec" , "Exec command, replacing this shell with the exec'd process", builtin_exec},
133 {"exit" , "Exit from shell()", builtin_exit},
134 {"fg" , "Bring job into the foreground", builtin_fg_bg},
135 {"jobs" , "Lists the active jobs", builtin_jobs},
136 {"export", "Set environment variable", builtin_export},
137 {"unset" , "Unset environment variable", builtin_unset},
138 {"read" , "Input environment variable", builtin_read},
139 {"." , "Source-in and run commands in a file", builtin_source},
140 /* These were "forked applets", but distinction was nuked */
141 /* Original comment retained: */
142 /* Table of forking built-in functions (things that fork cannot change global
143 * variables in the parent process, such as the current working directory) */
144 {"pwd" , "Print current directory", builtin_pwd},
145 {"help" , "List shell built-in commands", builtin_help},
146 /* to do: add ulimit */
147};
148
149
150#define VEC_LAST(v) v[ARRAY_SIZE(v)-1]
151
152
153static int shell_context; /* Type prompt trigger (PS1 or PS2) */
154
155
156/* Globals that are static to this file */
157static char *cwd;
158static char *local_pending_command;
159static struct jobset job_list = { NULL, NULL };
160static int argc;
161static char **argv;
162static llist_t *close_me_list;
163static int last_return_code;
164static int last_bg_pid;
165static unsigned int last_jobid;
166static int shell_terminal;
167static const char *PS1;
168static const char *PS2 = "> ";
169
170
171#ifdef DEBUG_SHELL
172static inline void debug_printf(const char *format, ...)
173{
174 va_list args;
175 va_start(args, format);
176 vfprintf(stderr, format, args);
177 va_end(args);
178}
179#else
180static inline void debug_printf(const char ATTRIBUTE_UNUSED *format, ...) { }
181#endif
182
183/*
184 Most builtins need access to the struct child_prog that has
185 their arguments, previously coded as cmd->progs[0]. That coding
186 can exhibit a bug, if the builtin is not the first command in
187 a pipeline: "echo foo | exec sort" will attempt to exec foo.
188
189builtin previous use notes
190------ ----------------- ---------
191cd cmd->progs[0]
192exec cmd->progs[0] squashed bug: didn't look for applets or forking builtins
193exit cmd->progs[0]
194fg_bg cmd->progs[0], job_list->head, job_list->fg
195help 0
196jobs job_list->head
197pwd 0
198export cmd->progs[0]
199source cmd->progs[0]
200unset cmd->progs[0]
201read cmd->progs[0]
202
203I added "struct job *family;" to struct child_prog,
204and switched API to builtin_foo(struct child_prog *child);
205So cmd->text becomes child->family->text
206 cmd->job_context becomes child->family->job_context
207 cmd->progs[0] becomes *child
208 job_list becomes child->family->job_list
209 */
210
211
212static void update_cwd(void)
213{
214 cwd = xrealloc_getcwd_or_warn(cwd);
215 if (!cwd)
216 cwd = xstrdup(bb_msg_unknown);
217}
218
219/* built-in 'cd <path>' handler */
220static int builtin_cd(struct child_prog *child)
221{
222 char *newdir;
223
224 if (child->argv[1] == NULL)
225 newdir = getenv("HOME");
226 else
227 newdir = child->argv[1];
228 if (chdir(newdir)) {
229 bb_perror_msg("cd: %s", newdir);
230 return EXIT_FAILURE;
231 }
232 update_cwd();
233 return EXIT_SUCCESS;
234}
235
236/* built-in 'exec' handler */
237static int builtin_exec(struct child_prog *child)
238{
239 if (child->argv[1] == NULL)
240 return EXIT_SUCCESS; /* Really? */
241 child->argv++;
242 while (close_me_list)
243 close((long)llist_pop(&close_me_list));
244 pseudo_exec(child);
245 /* never returns */
246}
247
248/* built-in 'exit' handler */
249static int builtin_exit(struct child_prog *child)
250{
251 if (child->argv[1] == NULL)
252 exit(EXIT_SUCCESS);
253
254 exit(atoi(child->argv[1]));
255}
256
257/* built-in 'fg' and 'bg' handler */
258static int builtin_fg_bg(struct child_prog *child)
259{
260 int i, jobnum;
261 struct job *job;
262
263 /* If they gave us no args, assume they want the last backgrounded task */
264 if (!child->argv[1]) {
265 for (job = child->family->job_list->head; job; job = job->next) {
266 if (job->jobid == last_jobid) {
267 goto found;
268 }
269 }
270 bb_error_msg("%s: no current job", child->argv[0]);
271 return EXIT_FAILURE;
272 }
273 if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) {
274 bb_error_msg(bb_msg_invalid_arg, child->argv[1], child->argv[0]);
275 return EXIT_FAILURE;
276 }
277 for (job = child->family->job_list->head; job; job = job->next) {
278 if (job->jobid == jobnum) {
279 goto found;
280 }
281 }
282 bb_error_msg("%s: %d: no such job", child->argv[0], jobnum);
283 return EXIT_FAILURE;
284 found:
285 if (*child->argv[0] == 'f') {
286 /* Put the job into the foreground. */
287 tcsetpgrp(shell_terminal, job->pgrp);
288
289 child->family->job_list->fg = job;
290 }
291
292 /* Restart the processes in the job */
293 for (i = 0; i < job->num_progs; i++)
294 job->progs[i].is_stopped = 0;
295
296 job->stopped_progs = 0;
297
298 i = kill(- job->pgrp, SIGCONT);
299 if (i < 0) {
300 if (errno == ESRCH) {
301 remove_job(&job_list, job);
302 } else {
303 bb_perror_msg("kill (SIGCONT)");
304 }
305 }
306
307 return EXIT_SUCCESS;
308}
309
310/* built-in 'help' handler */
311static int builtin_help(struct child_prog ATTRIBUTE_UNUSED *dummy)
312{
313 const struct built_in_command *x;
314
315 printf("\nBuilt-in commands:\n"
316 "-------------------\n");
317 for (x = bltins; x <= &VEC_LAST(bltins); x++) {
318 if (x->descr == NULL)
319 continue;
320 printf("%s\t%s\n", x->cmd, x->descr);
321 }
322 putchar('\n');
323 return EXIT_SUCCESS;
324}
325
326/* built-in 'jobs' handler */
327static int builtin_jobs(struct child_prog *child)
328{
329 struct job *job;
330 const char *status_string;
331
332 for (job = child->family->job_list->head; job; job = job->next) {
333 if (job->running_progs == job->stopped_progs)
334 status_string = "Stopped";
335 else
336 status_string = "Running";
337
338 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->text);
339 }
340 return EXIT_SUCCESS;
341}
342
343
344/* built-in 'pwd' handler */
345static int builtin_pwd(struct child_prog ATTRIBUTE_UNUSED *dummy)
346{
347 update_cwd();
348 puts(cwd);
349 return EXIT_SUCCESS;
350}
351
352/* built-in 'export VAR=value' handler */
353static int builtin_export(struct child_prog *child)
354{
355 int res;
356 char *v = child->argv[1];
357
358 if (v == NULL) {
359 char **e;
360 for (e = environ; *e; e++) {
361 puts(*e);
362 }
363 return 0;
364 }
365 res = putenv(v);
366 if (res)
367 bb_perror_msg("export");
368#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
369 if (strncmp(v, "PS1=", 4) == 0)
370 PS1 = getenv("PS1");
371#endif
372
373#if ENABLE_LOCALE_SUPPORT
374 // TODO: why getenv? "" would be just as good...
375 if (strncmp(v, "LC_ALL=", 7) == 0)
376 setlocale(LC_ALL, getenv("LC_ALL"));
377 if (strncmp(v, "LC_CTYPE=", 9) == 0)
378 setlocale(LC_CTYPE, getenv("LC_CTYPE"));
379#endif
380
381 return res;
382}
383
384/* built-in 'read VAR' handler */
385static int builtin_read(struct child_prog *child)
386{
387 int res = 0, len;
388 char *s;
389 char string[MAX_READ];
390
391 if (child->argv[1]) {
392 /* argument (VAR) given: put "VAR=" into buffer */
393 safe_strncpy(string, child->argv[1], MAX_READ-1);
394 len = strlen(string);
395 string[len++] = '=';
396 string[len] = '\0';
397 fgets(&string[len], sizeof(string) - len, stdin); /* read string */
398 res = strlen(string);
399 if (res > len)
400 string[--res] = '\0'; /* chomp trailing newline */
401 /*
402 ** string should now contain "VAR=<value>"
403 ** copy it (putenv() won't do that, so we must make sure
404 ** the string resides in a static buffer!)
405 */
406 res = -1;
407 s = strdup(string);
408 if (s)
409 res = putenv(s);
410 if (res)
411 bb_perror_msg("read");
412 } else
413 fgets(string, sizeof(string), stdin);
414
415 return res;
416}
417
418/* Built-in '.' handler (read-in and execute commands from file) */
419static int builtin_source(struct child_prog *child)
420{
421 FILE *input;
422 int status;
423
424 input = fopen_or_warn(child->argv[1], "r");
425 if (!input) {
426 return EXIT_FAILURE;
427 }
428
429 llist_add_to(&close_me_list, (void *)(long)fileno(input));
430 /* Now run the file */
431 status = busy_loop(input);
432 fclose(input);
433 llist_pop(&close_me_list);
434 return status;
435}
436
437/* built-in 'unset VAR' handler */
438static int builtin_unset(struct child_prog *child)
439{
440 if (child->argv[1] == NULL) {
441 printf(bb_msg_requires_arg, "unset");
442 return EXIT_FAILURE;
443 }
444 unsetenv(child->argv[1]);
445 return EXIT_SUCCESS;
446}
447
448#if ENABLE_LASH_JOB_CONTROL
449/* free up all memory from a job */
450static void free_job(struct job *cmd)
451{
452 int i;
453 struct jobset *keep;
454
455 for (i = 0; i < cmd->num_progs; i++) {
456 free(cmd->progs[i].argv);
457#if ENABLE_LASH_PIPE_N_REDIRECTS
458 if (cmd->progs[i].redirects)
459 free(cmd->progs[i].redirects);
460#endif
461 }
462 free(cmd->progs);
463 free(cmd->text);
464 free(cmd->cmdbuf);
465 keep = cmd->job_list;
466 memset(cmd, 0, sizeof(struct job));
467 cmd->job_list = keep;
468}
469
470/* remove a job from a jobset */
471static void remove_job(struct jobset *j_list, struct job *job)
472{
473 struct job *prevjob;
474
475 free_job(job);
476 if (job == j_list->head) {
477 j_list->head = job->next;
478 } else {
479 prevjob = j_list->head;
480 while (prevjob->next != job)
481 prevjob = prevjob->next;
482 prevjob->next = job->next;
483 }
484
485 if (j_list->head)
486 last_jobid = j_list->head->jobid;
487 else
488 last_jobid = 0;
489
490 free(job);
491}
492
493/* Checks to see if any background processes have exited -- if they
494 have, figure out why and see if a job has completed */
495static void checkjobs(struct jobset *j_list)
496{
497 struct job *job;
498 pid_t childpid;
499 int status;
500 int prognum = 0;
501
502 while ((childpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
503 for (job = j_list->head; job; job = job->next) {
504 prognum = 0;
505 while (prognum < job->num_progs &&
506 job->progs[prognum].pid != childpid) prognum++;
507 if (prognum < job->num_progs)
508 break;
509 }
510
511 /* This happens on backticked commands */
512 if (job == NULL)
513 return;
514
515 if (WIFEXITED(status) || WIFSIGNALED(status)) {
516 /* child exited */
517 job->running_progs--;
518 job->progs[prognum].pid = 0;
519
520 if (!job->running_progs) {
521 printf(JOB_STATUS_FORMAT, job->jobid, "Done", job->text);
522 last_jobid = 0;
523 remove_job(j_list, job);
524 }
525 } else {
526 /* child stopped */
527 job->stopped_progs++;
528 job->progs[prognum].is_stopped = 1;
529 }
530 }
531
532 if (childpid == -1 && errno != ECHILD)
533 bb_perror_msg("waitpid");
534}
535#else
536static void checkjobs(struct jobset *j_list)
537{
538}
539static void free_job(struct job *cmd)
540{
541}
542static void remove_job(struct jobset *j_list, struct job *job)
543{
544}
545#endif
546
547#if ENABLE_LASH_PIPE_N_REDIRECTS
548/* squirrel != NULL means we squirrel away copies of stdin, stdout,
549 * and stderr if they are redirected. */
550static int setup_redirects(struct child_prog *prog, int squirrel[])
551{
552 int i;
553 int openfd;
554 int mode = O_RDONLY;
555 struct redir_struct *redir = prog->redirects;
556
557 for (i = 0; i < prog->num_redirects; i++, redir++) {
558 switch (redir->type) {
559 case REDIRECT_INPUT:
560 mode = O_RDONLY;
561 break;
562 case REDIRECT_OVERWRITE:
563 mode = O_WRONLY | O_CREAT | O_TRUNC;
564 break;
565 case REDIRECT_APPEND:
566 mode = O_WRONLY | O_CREAT | O_APPEND;
567 break;
568 }
569
570 openfd = open3_or_warn(redir->filename, mode, 0666);
571 if (openfd < 0) {
572 /* this could get lost if stderr has been redirected, but
573 bash and ash both lose it as well (though zsh doesn't!) */
574 return 1;
575 }
576
577 if (openfd != redir->fd) {
578 if (squirrel && redir->fd < 3) {
579 squirrel[redir->fd] = dup(redir->fd);
580 fcntl(squirrel[redir->fd], F_SETFD, FD_CLOEXEC);
581 }
582 dup2(openfd, redir->fd);
583 close(openfd);
584 }
585 }
586
587 return 0;
588}
589
590static void restore_redirects(int squirrel[])
591{
592 int i, fd;
593 for (i = 0; i < 3; i++) {
594 fd = squirrel[i];
595 if (fd != -1) {
596 /* No error checking. I sure wouldn't know what
597 * to do with an error if I found one! */
598 dup2(fd, i);
599 close(fd);
600 }
601 }
602}
603#else
604static inline int setup_redirects(struct child_prog *prog, int squirrel[])
605{
606 return 0;
607}
608static inline void restore_redirects(int squirrel[])
609{
610}
611#endif
612
613static inline void cmdedit_set_initial_prompt(void)
614{
615#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
616 PS1 = NULL;
617#else
618 PS1 = getenv("PS1");
619 if (PS1 == 0)
620 PS1 = "\\w \\$ ";
621#endif
622}
623
624static inline const char* setup_prompt_string(void)
625{
626#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
627 /* Set up the prompt */
628 if (shell_context == 0) {
629 char *ns;
630 free((char*)PS1);
631 ns = xmalloc(strlen(cwd)+4);
632 sprintf(ns, "%s %c ", cwd, (geteuid() != 0) ? '$': '#');
633 PS1 = ns;
634 return ns;
635 } else {
636 return PS2;
637 }
638#else
639 return (shell_context == 0)? PS1 : PS2;
640#endif
641}
642
643#if ENABLE_FEATURE_EDITING
644static line_input_t *line_input_state;
645#endif
646
647static int get_command_bufsiz(FILE * source, char *command)
648{
649 const char *prompt_str;
650
651 if (source == NULL) {
652 if (local_pending_command) {
653 /* a command specified (-c option): return it & mark it done */
654 strncpy(command, local_pending_command, BUFSIZ);
655 local_pending_command = NULL;
656 return 0;
657 }
658 return 1;
659 }
660
661 if (source == stdin) {
662 prompt_str = setup_prompt_string();
663
664#if ENABLE_FEATURE_EDITING
665 /*
666 ** enable command line editing only while a command line
667 ** is actually being read; otherwise, we'll end up bequeathing
668 ** atexit() handlers and other unwanted stuff to our
669 ** child processes (rob@sysgo.de)
670 */
671 read_line_input(prompt_str, command, BUFSIZ, line_input_state);
672 return 0;
673#else
674 fputs(prompt_str, stdout);
675#endif
676 }
677
678 if (!fgets(command, BUFSIZ - 2, source)) {
679 if (source == stdin)
680 puts("");
681 return 1;
682 }
683
684 return 0;
685}
686
687static char * strsep_space(char *string, int * ix)
688{
689 /* Short circuit the trivial case */
690 if (!string || ! string[*ix])
691 return NULL;
692
693 /* Find the end of the token. */
694 while (string[*ix] && !isspace(string[*ix]) ) {
695 (*ix)++;
696 }
697
698 /* Find the end of any whitespace trailing behind
699 * the token and let that be part of the token */
700 while (string[*ix] && (isspace)(string[*ix]) ) {
701 (*ix)++;
702 }
703
704 if (!*ix) {
705 /* Nothing useful was found */
706 return NULL;
707 }
708
709 return xstrndup(string, *ix);
710}
711
712static int expand_arguments(char *command)
713{
714 static const char out_of_space[] ALIGN1 = "out of space during expansion";
715
716 int total_length = 0, length, i, retval, ix = 0;
717 expand_t expand_result;
718 char *tmpcmd, *cmd, *cmd_copy;
719 char *src, *dst, *var;
720 int flags = GLOB_NOCHECK
721#ifdef GLOB_BRACE
722 | GLOB_BRACE
723#endif
724#ifdef GLOB_TILDE
725 | GLOB_TILDE
726#endif
727 ;
728
729 /* get rid of the terminating \n */
730 chomp(command);
731
732 /* Fix up escape sequences to be the Real Thing(tm) */
733 while (command && command[ix]) {
734 if (command[ix] == '\\') {
735 const char *tmp = command+ix+1;
736 command[ix] = bb_process_escape_sequence( &tmp );
737 memmove(command+ix + 1, tmp, strlen(tmp)+1);
738 }
739 ix++;
740 }
741 /* Use glob and then fixup environment variables and such */
742
743 /* It turns out that glob is very stupid. We have to feed it one word at a
744 * time since it can't cope with a full string. Here we convert command
745 * (char*) into cmd (char**, one word per string) */
746
747 /* We need a clean copy, so strsep can mess up the copy while
748 * we write stuff into the original (in a minute) */
749 cmd = cmd_copy = xstrdup(command);
750 *command = '\0';
751 for (ix = 0, tmpcmd = cmd;
752 (tmpcmd = strsep_space(cmd, &ix)) != NULL; cmd += ix, ix = 0) {
753 if (*tmpcmd == '\0')
754 break;
755 /* we need to trim() the result for glob! */
756 trim(tmpcmd);
757 retval = glob(tmpcmd, flags, NULL, &expand_result);
758 free(tmpcmd); /* Free mem allocated by strsep_space */
759 if (retval == GLOB_NOSPACE) {
760 /* Mem may have been allocated... */
761 globfree(&expand_result);
762 bb_error_msg(out_of_space);
763 return FALSE;
764 } else if (retval != 0) {
765 /* Some other error. GLOB_NOMATCH shouldn't
766 * happen because of the GLOB_NOCHECK flag in
767 * the glob call. */
768 bb_error_msg("syntax error");
769 return FALSE;
770 } else {
771 /* Convert from char** (one word per string) to a simple char*,
772 * but don't overflow command which is BUFSIZ in length */
773 for (i = 0; i < expand_result.gl_pathc; i++) {
774 length = strlen(expand_result.gl_pathv[i]);
775 if (total_length+length+1 >= BUFSIZ) {
776 bb_error_msg(out_of_space);
777 return FALSE;
778 }
779 strcat(command+total_length, " ");
780 total_length += 1;
781 strcat(command+total_length, expand_result.gl_pathv[i]);
782 total_length += length;
783 }
784 globfree(&expand_result);
785 }
786 }
787 free(cmd_copy);
788 trim(command);
789
790 /* Now do the shell variable substitutions which
791 * wordexp can't do for us, namely $? and $! */
792 src = command;
793 while ((dst = strchr(src,'$')) != NULL) {
794 var = NULL;
795 switch (*(dst+1)) {
796 case '?':
797 var = itoa(last_return_code);
798 break;
799 case '!':
800 if (last_bg_pid == -1)
801 *var = '\0';
802 else
803 var = itoa(last_bg_pid);
804 break;
805 /* Everything else like $$, $#, $[0-9], etc. should all be
806 * expanded by wordexp(), so we can in theory skip that stuff
807 * here, but just to be on the safe side (i.e., since uClibc
808 * wordexp doesn't do this stuff yet), lets leave it in for
809 * now. */
810 case '$':
811 var = itoa(getpid());
812 break;
813 case '#':
814 var = itoa(argc-1);
815 break;
816 case '0':case '1':case '2':case '3':case '4':
817 case '5':case '6':case '7':case '8':case '9':
818 {
819 int ixx = *(dst+1)-48+1;
820 if (ixx >= argc) {
821 var = '\0';
822 } else {
823 var = argv[ixx];
824 }
825 }
826 break;
827
828 }
829 if (var) {
830 /* a single character construction was found, and
831 * already handled in the case statement */
832 src = dst + 2;
833 } else {
834 /* Looks like an environment variable */
835 char delim_hold;
836 int num_skip_chars = 0;
837 int dstlen = strlen(dst);
838 /* Is this a ${foo} type variable? */
839 if (dstlen >= 2 && *(dst+1) == '{') {
840 src = strchr(dst+1, '}');
841 num_skip_chars = 1;
842 } else {
843 src = dst + 1;
844 while ((isalnum)(*src) || *src == '_') src++;
845 }
846 if (src == NULL) {
847 src = dst+dstlen;
848 }
849 delim_hold = *src;
850 *src = '\0'; /* temporary */
851 var = getenv(dst + 1 + num_skip_chars);
852 *src = delim_hold;
853 src += num_skip_chars;
854 }
855 if (var == NULL) {
856 /* Seems we got an un-expandable variable. So delete it. */
857 var = (char*)"";
858 }
859 {
860 int subst_len = strlen(var);
861 int trail_len = strlen(src);
862 if (dst+subst_len+trail_len >= command+BUFSIZ) {
863 bb_error_msg(out_of_space);
864 return FALSE;
865 }
866 /* Move stuff to the end of the string to accommodate
867 * filling the created gap with the new stuff */
868 memmove(dst+subst_len, src, trail_len+1);
869 /* Now copy in the new stuff */
870 memcpy(dst, var, subst_len);
871 src = dst+subst_len;
872 }
873 }
874
875 return TRUE;
876}
877
878/* Return cmd->num_progs as 0 if no command is present (e.g. an empty
879 line). If a valid command is found, command_ptr is set to point to
880 the beginning of the next command (if the original command had more
881 then one job associated with it) or NULL if no more commands are
882 present. */
883static int parse_command(char **command_ptr, struct job *job, int *inbg)
884{
885 char *command;
886 char *return_command = NULL;
887 char *src, *buf;
888 int argc_l;
889 int flag;
890 int argv_alloced;
891 char quote = '\0';
892 struct child_prog *prog;
893#if ENABLE_LASH_PIPE_N_REDIRECTS
894 int i;
895 char *chptr;
896#endif
897
898 /* skip leading white space */
899 *command_ptr = skip_whitespace(*command_ptr);
900
901 /* this handles empty lines or leading '#' characters */
902 if (!**command_ptr || (**command_ptr == '#')) {
903 job->num_progs = 0;
904 return 0;
905 }
906
907 *inbg = 0;
908 job->num_progs = 1;
909 job->progs = xmalloc(sizeof(*job->progs));
910
911 /* We set the argv elements to point inside of this string. The
912 memory is freed by free_job(). Allocate twice the original
913 length in case we need to quote every single character.
914
915 Getting clean memory relieves us of the task of NULL
916 terminating things and makes the rest of this look a bit
917 cleaner (though it is, admittedly, a tad less efficient) */
918 job->cmdbuf = command = xzalloc(2*strlen(*command_ptr) + 1);
919 job->text = NULL;
920
921 prog = job->progs;
922 prog->num_redirects = 0;
923 prog->is_stopped = 0;
924 prog->family = job;
925#if ENABLE_LASH_PIPE_N_REDIRECTS
926 prog->redirects = NULL;
927#endif
928
929 argv_alloced = 5;
930 prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
931 prog->argv[0] = job->cmdbuf;
932
933 flag = argc_l = 0;
934 buf = command;
935 src = *command_ptr;
936 while (*src && !(flag & LASH_OPT_DONE)) {
937 if (quote == *src) {
938 quote = '\0';
939 } else if (quote) {
940 if (*src == '\\') {
941 src++;
942 if (!*src) {
943 bb_error_msg("character expected after \\");
944 free_job(job);
945 return 1;
946 }
947
948 /* in shell, "\'" should yield \' */
949 if (*src != quote) {
950 *buf++ = '\\';
951 *buf++ = '\\';
952 }
953 } else if (*src == '*' || *src == '?' || *src == '[' ||
954 *src == ']') *buf++ = '\\';
955 *buf++ = *src;
956 } else if (isspace(*src)) {
957 if (*prog->argv[argc_l] || (flag & LASH_OPT_SAW_QUOTE)) {
958 buf++, argc_l++;
959 /* +1 here leaves room for the NULL which ends argv */
960 if ((argc_l + 1) == argv_alloced) {
961 argv_alloced += 5;
962 prog->argv = xrealloc(prog->argv,
963 sizeof(*prog->argv) * argv_alloced);
964 }
965 prog->argv[argc_l] = buf;
966 flag ^= LASH_OPT_SAW_QUOTE;
967 }
968 } else
969 switch (*src) {
970 case '"':
971 case '\'':
972 quote = *src;
973 flag |= LASH_OPT_SAW_QUOTE;
974 break;
975
976 case '#': /* comment */
977 if (*(src-1)== '$')
978 *buf++ = *src;
979 else
980 flag |= LASH_OPT_DONE;
981 break;
982
983#if ENABLE_LASH_PIPE_N_REDIRECTS
984 case '>': /* redirects */
985 case '<':
986 i = prog->num_redirects++;
987 prog->redirects = xrealloc(prog->redirects,
988 sizeof(*prog->redirects) * (i + 1));
989
990 prog->redirects[i].fd = -1;
991 if (buf != prog->argv[argc_l]) {
992 /* the stuff before this character may be the file number
993 being redirected */
994 prog->redirects[i].fd =
995 strtol(prog->argv[argc_l], &chptr, 10);
996
997 if (*chptr && *prog->argv[argc_l]) {
998 buf++, argc_l++;
999 prog->argv[argc_l] = buf;
1000 }
1001 }
1002
1003 if (prog->redirects[i].fd == -1) {
1004 if (*src == '>')
1005 prog->redirects[i].fd = 1;
1006 else
1007 prog->redirects[i].fd = 0;
1008 }
1009
1010 if (*src++ == '>') {
1011 if (*src == '>')
1012 prog->redirects[i].type =
1013 REDIRECT_APPEND, src++;
1014 else
1015 prog->redirects[i].type = REDIRECT_OVERWRITE;
1016 } else {
1017 prog->redirects[i].type = REDIRECT_INPUT;
1018 }
1019
1020 /* This isn't POSIX sh compliant. Oh well. */
1021 chptr = src;
1022 chptr = skip_whitespace(chptr);
1023
1024 if (!*chptr) {
1025 bb_error_msg("file name expected after %c", *(src-1));
1026 free_job(job);
1027 job->num_progs = 0;
1028 return 1;
1029 }
1030
1031 prog->redirects[i].filename = buf;
1032 while (*chptr && !isspace(*chptr))
1033 *buf++ = *chptr++;
1034
1035 src = chptr - 1; /* we src++ later */
1036 prog->argv[argc_l] = ++buf;
1037 break;
1038
1039 case '|': /* pipe */
1040 /* finish this command */
1041 if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE)
1042 argc_l++;
1043 if (!argc_l) {
1044 goto empty_command_in_pipe;
1045 }
1046 prog->argv[argc_l] = NULL;
1047
1048 /* and start the next */
1049 job->num_progs++;
1050 job->progs = xrealloc(job->progs,
1051 sizeof(*job->progs) * job->num_progs);
1052 prog = job->progs + (job->num_progs - 1);
1053 prog->num_redirects = 0;
1054 prog->redirects = NULL;
1055 prog->is_stopped = 0;
1056 prog->family = job;
1057 argc_l = 0;
1058
1059 argv_alloced = 5;
1060 prog->argv = xmalloc(sizeof(*prog->argv) * argv_alloced);
1061 prog->argv[0] = ++buf;
1062
1063 src++;
1064 src = skip_whitespace(src);
1065
1066 if (!*src) {
1067empty_command_in_pipe:
1068 bb_error_msg("empty command in pipe");
1069 free_job(job);
1070 job->num_progs = 0;
1071 return 1;
1072 }
1073 src--; /* we'll ++ it at the end of the loop */
1074
1075 break;
1076#endif
1077
1078#if ENABLE_LASH_JOB_CONTROL
1079 case '&': /* background */
1080 *inbg = 1;
1081 /* fallthrough */
1082#endif
1083 case ';': /* multiple commands */
1084 flag |= LASH_OPT_DONE;
1085 return_command = *command_ptr + (src - *command_ptr) + 1;
1086 break;
1087
1088 case '\\':
1089 src++;
1090 if (!*src) {
1091 bb_error_msg("character expected after \\");
1092 free_job(job);
1093 return 1;
1094 }
1095 if (*src == '*' || *src == '[' || *src == ']'
1096 || *src == '?') *buf++ = '\\';
1097 /* fallthrough */
1098 default:
1099 *buf++ = *src;
1100 }
1101
1102 src++;
1103 }
1104
1105 if (*prog->argv[argc_l] || flag & LASH_OPT_SAW_QUOTE) {
1106 argc_l++;
1107 }
1108 if (!argc_l) {
1109 free_job(job);
1110 return 0;
1111 }
1112 prog->argv[argc_l] = NULL;
1113
1114 if (!return_command) {
1115 job->text = xstrdup(*command_ptr);
1116 } else {
1117 /* This leaves any trailing spaces, which is a bit sloppy */
1118 job->text = xstrndup(*command_ptr, return_command - *command_ptr);
1119 }
1120
1121 *command_ptr = return_command;
1122
1123 return 0;
1124}
1125
1126/* Run the child_prog, no matter what kind of command it uses.
1127 */
1128static int pseudo_exec(struct child_prog *child)
1129{
1130 const struct built_in_command *x;
1131
1132 /* Check if the command matches any of the non-forking builtins.
1133 * Depending on context, this might be redundant. But it's
1134 * easier to waste a few CPU cycles than it is to figure out
1135 * if this is one of those cases.
1136 */
1137 /* Check if the command matches any of the forking builtins. */
1138 for (x = bltins; x <= &VEC_LAST(bltins); x++) {
1139 if (strcmp(child->argv[0], x->cmd) == 0) {
1140 _exit(x->function(child));
1141 }
1142 }
1143
1144
1145 /* Check if the command matches any busybox internal
1146 * commands ("applets") here. Following discussions from
1147 * November 2000 on busybox@busybox.net, don't use
1148 * bb_get_last_path_component(). This way explicit (with
1149 * slashes) filenames will never be interpreted as an
1150 * applet, just like with builtins. This way the user can
1151 * override an applet with an explicit filename reference.
1152 * The only downside to this change is that an explicit
1153 * /bin/foo invocation will fork and exec /bin/foo, even if
1154 * /bin/foo is a symlink to busybox.
1155 */
1156 if (ENABLE_FEATURE_SH_STANDALONE) {
1157 run_applet_and_exit(child->argv[0], child->argv);
1158 }
1159
1160 execvp(child->argv[0], child->argv);
1161
1162 /* Do not use bb_perror_msg_and_die() here, since we must not
1163 * call exit() but should call _exit() instead */
1164 bb_perror_msg("%s", child->argv[0]);
1165 _exit(EXIT_FAILURE);
1166}
1167
1168static void insert_job(struct job *newjob, int inbg)
1169{
1170 struct job *thejob;
1171 struct jobset *j_list = newjob->job_list;
1172
1173 /* find the ID for thejob to use */
1174 newjob->jobid = 1;
1175 for (thejob = j_list->head; thejob; thejob = thejob->next)
1176 if (thejob->jobid >= newjob->jobid)
1177 newjob->jobid = thejob->jobid + 1;
1178
1179 /* add thejob to the list of running jobs */
1180 if (!j_list->head) {
1181 thejob = j_list->head = xmalloc(sizeof(*thejob));
1182 } else {
1183 for (thejob = j_list->head; thejob->next; thejob = thejob->next) /* nothing */;
1184 thejob->next = xmalloc(sizeof(*thejob));
1185 thejob = thejob->next;
1186 }
1187
1188 *thejob = *newjob; /* physically copy the struct job */
1189 thejob->next = NULL;
1190 thejob->running_progs = thejob->num_progs;
1191 thejob->stopped_progs = 0;
1192
1193#if ENABLE_LASH_JOB_CONTROL
1194 if (inbg) {
1195 /* we don't wait for background thejobs to return -- append it
1196 to the list of backgrounded thejobs and leave it alone */
1197 printf("[%d] %d\n", thejob->jobid,
1198 newjob->progs[newjob->num_progs - 1].pid);
1199 last_jobid = newjob->jobid;
1200 last_bg_pid = newjob->progs[newjob->num_progs - 1].pid;
1201 } else {
1202 newjob->job_list->fg = thejob;
1203
1204 /* move the new process group into the foreground */
1205 /* Ignore errors since child could have already exited */
1206 tcsetpgrp(shell_terminal, newjob->pgrp);
1207 }
1208#endif
1209}
1210
1211static int run_command(struct job *newjob, int inbg, int outpipe[2])
1212{
1213 /* struct job *thejob; */
1214 int i;
1215 int nextin, nextout;
1216 int pipefds[2]; /* pipefd[0] is for reading */
1217 const struct built_in_command *x;
1218 struct child_prog *child;
1219
1220 nextin = 0;
1221 for (i = 0; i < newjob->num_progs; i++) {
1222 child = &(newjob->progs[i]);
1223
1224 nextout = 1;
1225 if ((i + 1) < newjob->num_progs) {
1226 xpipe(pipefds);
1227 nextout = pipefds[1];
1228 } else if (outpipe[1] != -1) {
1229 nextout = outpipe[1];
1230 }
1231
1232 /* Check if the command matches any non-forking builtins,
1233 * but only if this is a simple command.
1234 * Non-forking builtins within pipes have to fork anyway,
1235 * and are handled in pseudo_exec. "echo foo | read bar"
1236 * is doomed to failure, and doesn't work on bash, either.
1237 */
1238 if (newjob->num_progs == 1) {
1239 int rcode;
1240 int squirrel[] = {-1, -1, -1};
1241
1242 /* Check if the command sets an environment variable. */
1243 if (strchr(child->argv[0], '=') != NULL) {
1244 child->argv[1] = child->argv[0];
1245 return builtin_export(child);
1246 }
1247
1248 for (x = bltins; x <= &VEC_LAST(bltins); x++) {
1249 if (strcmp(child->argv[0], x->cmd) == 0) {
1250 setup_redirects(child, squirrel);
1251 rcode = x->function(child);
1252 restore_redirects(squirrel);
1253 return rcode;
1254 }
1255 }
1256#if ENABLE_FEATURE_SH_STANDALONE
1257 {
1258 const struct bb_applet *a = find_applet_by_name(child->argv[i]);
1259 if (a && a->nofork) {
1260 setup_redirects(child, squirrel);
1261 rcode = run_nofork_applet(a, child->argv + i);
1262 restore_redirects(squirrel);
1263 return rcode;
1264 }
1265 }
1266#endif
1267 }
1268
1269#if BB_MMU
1270 child->pid = fork();
1271#else
1272 child->pid = vfork();
1273#endif
1274 if (!child->pid) {
1275 /* Set the handling for job control signals back to the default. */
1276 signal(SIGINT, SIG_DFL);
1277 signal(SIGQUIT, SIG_DFL);
1278 signal(SIGTSTP, SIG_DFL);
1279 signal(SIGTTIN, SIG_DFL);
1280 signal(SIGTTOU, SIG_DFL);
1281 signal(SIGCHLD, SIG_DFL);
1282
1283 /* Close all open filehandles. */
1284 while (close_me_list)
1285 close((long)llist_pop(&close_me_list));
1286
1287 if (outpipe[1] != -1) {
1288 close(outpipe[0]);
1289 }
1290 if (nextin != 0) {
1291 dup2(nextin, 0);
1292 close(nextin);
1293 }
1294
1295 if (nextout != 1) {
1296 dup2(nextout, 1);
1297 dup2(nextout, 2); /* Really? */
1298 close(nextout);
1299 close(pipefds[0]);
1300 }
1301
1302 /* explicit redirects override pipes */
1303 setup_redirects(child,NULL);
1304
1305 pseudo_exec(child);
1306 }
1307 if (outpipe[1] != -1) {
1308 close(outpipe[1]);
1309 }
1310
1311 /* put our child in the process group whose leader is the
1312 first process in this pipe */
1313 setpgid(child->pid, newjob->progs[0].pid);
1314 if (nextin != 0)
1315 close(nextin);
1316 if (nextout != 1)
1317 close(nextout);
1318
1319 /* If there isn't another process, nextin is garbage
1320 but it doesn't matter */
1321 nextin = pipefds[0];
1322 }
1323
1324 newjob->pgrp = newjob->progs[0].pid;
1325
1326 insert_job(newjob, inbg);
1327
1328 return 0;
1329}
1330
1331static int busy_loop(FILE * input)
1332{
1333 char *command;
1334 char *next_command = NULL;
1335 struct job newjob;
1336 int i;
1337 int inbg = 0;
1338 int status;
1339#if ENABLE_LASH_JOB_CONTROL
1340 pid_t parent_pgrp;
1341 /* save current owner of TTY so we can restore it on exit */
1342 parent_pgrp = tcgetpgrp(shell_terminal);
1343#endif
1344 newjob.job_list = &job_list;
1345 newjob.job_context = DEFAULT_CONTEXT;
1346
1347 command = xzalloc(BUFSIZ);
1348
1349 while (1) {
1350 if (!job_list.fg) {
1351 /* no job is in the foreground */
1352
1353 /* see if any background processes have exited */
1354 checkjobs(&job_list);
1355
1356 if (!next_command) {
1357 if (get_command_bufsiz(input, command))
1358 break;
1359 next_command = command;
1360 }
1361
1362 if (!expand_arguments(next_command)) {
1363 free(command);
1364 command = xzalloc(BUFSIZ);
1365 next_command = NULL;
1366 continue;
1367 }
1368
1369 if (!parse_command(&next_command, &newjob, &inbg) &&
1370 newjob.num_progs) {
1371 int pipefds[2] = { -1, -1 };
1372 debug_printf("job=%p fed to run_command by busy_loop()'\n",
1373 &newjob);
1374 run_command(&newjob, inbg, pipefds);
1375 }
1376 else {
1377 free(command);
1378 command = xzalloc(BUFSIZ);
1379 next_command = NULL;
1380 }
1381 } else {
1382 /* a job is running in the foreground; wait for it */
1383 i = 0;
1384 while (!job_list.fg->progs[i].pid ||
1385 job_list.fg->progs[i].is_stopped == 1) i++;
1386
1387 if (waitpid(job_list.fg->progs[i].pid, &status, WUNTRACED) < 0) {
1388 if (errno != ECHILD) {
1389 bb_perror_msg_and_die("waitpid(%d)", job_list.fg->progs[i].pid);
1390 }
1391 }
1392
1393 if (WIFEXITED(status) || WIFSIGNALED(status)) {
1394 /* the child exited */
1395 job_list.fg->running_progs--;
1396 job_list.fg->progs[i].pid = 0;
1397
1398 last_return_code = WEXITSTATUS(status);
1399
1400 if (!job_list.fg->running_progs) {
1401 /* child exited */
1402 remove_job(&job_list, job_list.fg);
1403 job_list.fg = NULL;
1404 }
1405 }
1406#if ENABLE_LASH_JOB_CONTROL
1407 else {
1408 /* the child was stopped */
1409 job_list.fg->stopped_progs++;
1410 job_list.fg->progs[i].is_stopped = 1;
1411
1412 if (job_list.fg->stopped_progs == job_list.fg->running_progs) {
1413 printf("\n" JOB_STATUS_FORMAT, job_list.fg->jobid,
1414 "Stopped", job_list.fg->text);
1415 job_list.fg = NULL;
1416 }
1417 }
1418
1419 if (!job_list.fg) {
1420 /* move the shell to the foreground */
1421 /* suppress messages when run from /linuxrc mag@sysgo.de */
1422 if (tcsetpgrp(shell_terminal, getpgrp()) && errno != ENOTTY)
1423 bb_perror_msg("tcsetpgrp");
1424 }
1425#endif
1426 }
1427 }
1428 free(command);
1429
1430#if ENABLE_LASH_JOB_CONTROL
1431 /* return controlling TTY back to parent process group before exiting */
1432 if (tcsetpgrp(shell_terminal, parent_pgrp) && errno != ENOTTY)
1433 bb_perror_msg("tcsetpgrp");
1434#endif
1435
1436 /* return exit status if called with "-c" */
1437 if (input == NULL && WIFEXITED(status))
1438 return WEXITSTATUS(status);
1439
1440 return 0;
1441}
1442
1443#if ENABLE_FEATURE_CLEAN_UP
1444static void free_memory(void)
1445{
1446 free(cwd);
1447
1448 if (job_list.fg && !job_list.fg->running_progs) {
1449 remove_job(&job_list, job_list.fg);
1450 }
1451}
1452#else
1453void free_memory(void);
1454#endif
1455
1456#if ENABLE_LASH_JOB_CONTROL
1457/* Make sure we have a controlling tty. If we get started under a job
1458 * aware app (like bash for example), make sure we are now in charge so
1459 * we don't fight over who gets the foreground */
1460static void setup_job_control(void)
1461{
1462 int status;
1463 pid_t shell_pgrp;
1464
1465 /* Loop until we are in the foreground. */
1466 while ((status = tcgetpgrp(shell_terminal)) >= 0) {
1467 shell_pgrp = getpgrp();
1468 if (status == shell_pgrp) {
1469 break;
1470 }
1471 kill(- shell_pgrp, SIGTTIN);
1472 }
1473
1474 /* Ignore interactive and job-control signals. */
1475 signal(SIGINT, SIG_IGN);
1476 signal(SIGQUIT, SIG_IGN);
1477 signal(SIGTSTP, SIG_IGN);
1478 signal(SIGTTIN, SIG_IGN);
1479 signal(SIGTTOU, SIG_IGN);
1480 signal(SIGCHLD, SIG_IGN);
1481
1482 /* Put ourselves in our own process group. */
1483 setsid();
1484 shell_pgrp = getpid();
1485 setpgid(shell_pgrp, shell_pgrp);
1486
1487 /* Grab control of the terminal. */
1488 tcsetpgrp(shell_terminal, shell_pgrp);
1489}
1490#else
1491static inline void setup_job_control(void)
1492{
1493}
1494#endif
1495
1496int lash_main(int argc_l, char **argv_l);
1497int lash_main(int argc_l, char **argv_l)
1498{
1499 unsigned opt;
1500 FILE *input = stdin;
1501 argc = argc_l;
1502 argv = argv_l;
1503
1504#if ENABLE_FEATURE_EDITING
1505 line_input_state = new_line_input_t(FOR_SHELL);
1506#endif
1507
1508 /* These variables need re-initializing when recursing */
1509 last_jobid = 0;
1510 close_me_list = NULL;
1511 job_list.head = NULL;
1512 job_list.fg = NULL;
1513 last_return_code = 1;
1514
1515 if (argv[0] && argv[0][0] == '-') {
1516 FILE *prof_input;
1517 prof_input = fopen("/etc/profile", "r");
1518 if (prof_input) {
1519 llist_add_to(&close_me_list, (void *)(long)fileno(prof_input));
1520 /* Now run the file */
1521 busy_loop(prof_input);
1522 fclose_if_not_stdin(prof_input);
1523 llist_pop(&close_me_list);
1524 }
1525 }
1526
1527 opt = getopt32(argv_l, "+ic:", &local_pending_command);
1528#define LASH_OPT_i (1<<0)
1529#define LASH_OPT_c (1<<1)
1530 if (opt & LASH_OPT_c) {
1531 input = NULL;
1532 optind++;
1533 argv += optind;
1534 }
1535 /* A shell is interactive if the `-i' flag was given, or if all of
1536 * the following conditions are met:
1537 * no -c command
1538 * no arguments remaining or the -s flag given
1539 * standard input is a terminal
1540 * standard output is a terminal
1541 * Refer to Posix.2, the description of the `sh' utility. */
1542 if (argv[optind] == NULL && input == stdin
1543 && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
1544 ) {
1545 opt |= LASH_OPT_i;
1546 }
1547 setup_job_control();
1548 if (opt & LASH_OPT_i) {
1549 /* Looks like they want an interactive shell */
1550 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
1551 printf("\n\n%s built-in shell (lash)\n"
1552 "Enter 'help' for a list of built-in commands.\n\n",
1553 bb_banner);
1554 }
1555 } else if (!local_pending_command && argv[optind]) {
1556 //printf( "optind=%d argv[optind]='%s'\n", optind, argv[optind]);
1557 input = xfopen(argv[optind], "r");
1558 /* be lazy, never mark this closed */
1559 llist_add_to(&close_me_list, (void *)(long)fileno(input));
1560 }
1561
1562 /* initialize the cwd -- this is never freed...*/
1563 update_cwd();
1564
1565 if (ENABLE_FEATURE_CLEAN_UP) atexit(free_memory);
1566
1567 if (ENABLE_FEATURE_EDITING) cmdedit_set_initial_prompt();
1568 else PS1 = NULL;
1569
1570 return busy_loop(input);
1571}
Note: See TracBrowser for help on using the repository browser.