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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 12 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.