From 1359da6ac7f7f44f82d224e61cc2c6ecb58baeef Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sat, 21 Apr 2007 23:27:30 +0000 Subject: hush: make Ctrl-Z work (at least sometimes) --- shell/README | 5 + shell/hush.c | 400 +++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 229 insertions(+), 176 deletions(-) diff --git a/shell/README b/shell/README index b78d651a5..2b1d05ceb 100644 --- a/shell/README +++ b/shell/README @@ -1,5 +1,10 @@ Various bits of what is known about busybox shells, in no particular order. +2007-04-21 +hush: "sleep 5 | sleep 6" + Ctrl-Z + fg seems to work. +"rm -i" + Ctrl-C, "sleep 5" + Ctrl-Z still doesn't work +for SH_STANDALONE case :( + 2007-04-21 hush: fixed non-backgrounding of "sleep 1 &" and totally broken "sleep 1 | sleep 2 &". Noticed a bug where successive jobs diff --git a/shell/hush.c b/shell/hush.c index b367a08b2..e2ce3676d 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -82,7 +82,9 @@ #include /* should be pretty obvious */ /* #include */ -/* #define DEBUG_SHELL */ +//#define DEBUG_SHELL +/* Finer-grained debug switch */ +//#define DEBUG_SHELL_JOBS #define SPECIAL_VAR_SYMBOL 03 @@ -274,9 +276,9 @@ struct in_str { #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" struct built_in_command { - const char *cmd; /* name */ - const char *descr; /* description */ - int (*function) (struct child_prog *); /* function ptr */ + const char *cmd; /* name */ + const char *descr; /* description */ + int (*function) (char **argv); /* function ptr */ }; /* belongs in busybox.h */ @@ -285,24 +287,24 @@ static int max(int a, int b) return (a > b) ? a : b; } -/* This should be in utility.c */ #ifdef DEBUG_SHELL -static void debug_printf(const char *format, ...) -{ - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); -} +#define debug_printf(...) fprintf(stderr, __VA_ARGS__) /* broken, of course, but OK for testing */ static char *indenter(int i) { static char blanks[] = " "; - return &blanks[sizeof(blanks)-i-1]; + return &blanks[sizeof(blanks) - i - 1]; } #else #define debug_printf(...) do {} while (0) #endif + +#ifdef DEBUG_SHELL_JOBS +#define debug_jobs_printf(...) fprintf(stderr, __VA_ARGS__) +#else +#define debug_jobs_printf(...) do {} while (0) +#endif + #define final_printf debug_printf static void __syntax(const char *file, int line) @@ -314,23 +316,23 @@ static void __syntax(const char *file, int line) /* Index of subroutines: */ /* function prototypes for builtins */ -static int builtin_cd(struct child_prog *child); -static int builtin_env(struct child_prog *child); -static int builtin_eval(struct child_prog *child); -static int builtin_exec(struct child_prog *child); -static int builtin_exit(struct child_prog *child); -static int builtin_export(struct child_prog *child); -static int builtin_fg_bg(struct child_prog *child); -static int builtin_help(struct child_prog *child); -static int builtin_jobs(struct child_prog *child); -static int builtin_pwd(struct child_prog *child); -static int builtin_read(struct child_prog *child); -static int builtin_set(struct child_prog *child); -static int builtin_shift(struct child_prog *child); -static int builtin_source(struct child_prog *child); -static int builtin_umask(struct child_prog *child); -static int builtin_unset(struct child_prog *child); -static int builtin_not_written(struct child_prog *child); +static int builtin_cd(char **argv); +static int builtin_env(char **argv); +static int builtin_eval(char **argv); +static int builtin_exec(char **argv); +static int builtin_exit(char **argv); +static int builtin_export(char **argv); +static int builtin_fg_bg(char **argv); +static int builtin_help(char **argv); +static int builtin_jobs(char **argv); +static int builtin_pwd(char **argv); +static int builtin_read(char **argv); +static int builtin_set(char **argv); +static int builtin_shift(char **argv); +static int builtin_source(char **argv); +static int builtin_umask(char **argv); +static int builtin_unset(char **argv); +static int builtin_not_written(char **argv); /* o_string manipulation: */ static int b_check_space(o_string *o, int len); static int b_addchr(o_string *o, int ch); @@ -354,6 +356,7 @@ static int free_pipe(struct pipe *pi, int indent); /* really run the final data structures: */ static int setup_redirects(struct child_prog *prog, int squirrel[]); static int run_list_real(struct pipe *pi); +static void pseudo_exec_argv(char **argv) ATTRIBUTE_NORETURN; static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN; static int run_pipe_real(struct pipe *pi); /* extended glob support: */ @@ -387,6 +390,7 @@ static int checkjobs(struct pipe* fg_pipe); static int checkjobs_and_fg_shell(struct pipe* fg_pipe); static void insert_bg_job(struct pipe *pi); static void remove_bg_job(struct pipe *pi); +static void delete_finished_bg_job(struct pipe *pi); /* local variable support */ static char **make_list_in(char **inp, char *name); static char *insert_var_value(char *inp); @@ -503,18 +507,14 @@ static const char *set_cwd(void) return cwd; } -// It seems ALL built-ins ever use *only* child->argv in child param. -// Passing argv directly may make 'child->argv += n' modifications -// unneeded on vfork codepaths. - /* built-in 'eval' handler */ -static int builtin_eval(struct child_prog *child) +static int builtin_eval(char **argv) { char *str = NULL; int rcode = EXIT_SUCCESS; - if (child->argv[1]) { - str = make_string(child->argv + 1); + if (argv[1]) { + str = make_string(argv + 1); parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_PARSE_SEMICOLON); free(str); @@ -524,13 +524,13 @@ static int builtin_eval(struct child_prog *child) } /* built-in 'cd ' handler */ -static int builtin_cd(struct child_prog *child) +static int builtin_cd(char **argv) { char *newdir; - if (child->argv[1] == NULL) + if (argv[1] == NULL) newdir = getenv("HOME"); else - newdir = child->argv[1]; + newdir = argv[1]; if (chdir(newdir)) { printf("cd: %s: %s\n", newdir, strerror(errno)); return EXIT_FAILURE; @@ -540,7 +540,7 @@ static int builtin_cd(struct child_prog *child) } /* built-in 'env' handler */ -static int builtin_env(struct child_prog *dummy ATTRIBUTE_UNUSED) +static int builtin_env(char **argv ATTRIBUTE_UNUSED) { /* TODO: call env applet's code instead */ char **e = environ; @@ -553,37 +553,36 @@ static int builtin_env(struct child_prog *dummy ATTRIBUTE_UNUSED) } /* built-in 'exec' handler */ -static int builtin_exec(struct child_prog *child) +static int builtin_exec(char **argv) { - if (child->argv[1] == NULL) + if (argv[1] == NULL) return EXIT_SUCCESS; /* Really? */ - child->argv++; - pseudo_exec(child); + pseudo_exec_argv(argv + 1); /* never returns */ } /* built-in 'exit' handler */ -static int builtin_exit(struct child_prog *child) +static int builtin_exit(char **argv) { // TODO: bash does it ONLY on top-level sh exit (+interacive only?) //puts("exit"); /* bash does it */ - if (child->argv[1] == NULL) + if (argv[1] == NULL) hush_exit(last_return_code); /* mimic bash: exit 123abc == exit 255 + error msg */ xfunc_error_retval = 255; /* bash: exit -2 == exit 254, no error msg */ - hush_exit(xatoi(child->argv[1])); + hush_exit(xatoi(argv[1])); } /* built-in 'export VAR=value' handler */ -static int builtin_export(struct child_prog *child) +static int builtin_export(char **argv) { int res = 0; - char *name = child->argv[1]; + char *name = argv[1]; if (name == NULL) { - return builtin_env(child); + return builtin_env(argv); } name = strdup(name); @@ -624,7 +623,7 @@ static int builtin_export(struct child_prog *child) } /* built-in 'fg' and 'bg' handler */ -static int builtin_fg_bg(struct child_prog *child) +static int builtin_fg_bg(char **argv) { int i, jobnum; struct pipe *pi; @@ -632,17 +631,17 @@ static int builtin_fg_bg(struct child_prog *child) if (!interactive_fd) return EXIT_FAILURE; /* If they gave us no args, assume they want the last backgrounded task */ - if (!child->argv[1]) { + if (!argv[1]) { for (pi = job_list; pi; pi = pi->next) { if (pi->jobid == last_jobid) { goto found; } } - bb_error_msg("%s: no current job", child->argv[0]); + bb_error_msg("%s: no current job", argv[0]); return EXIT_FAILURE; } - if (sscanf(child->argv[1], "%%%d", &jobnum) != 1) { - bb_error_msg("%s: bad argument '%s'", child->argv[0], child->argv[1]); + if (sscanf(argv[1], "%%%d", &jobnum) != 1) { + bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]); return EXIT_FAILURE; } for (pi = job_list; pi; pi = pi->next) { @@ -650,37 +649,42 @@ static int builtin_fg_bg(struct child_prog *child) goto found; } } - bb_error_msg("%s: %d: no such job", child->argv[0], jobnum); + bb_error_msg("%s: %d: no such job", argv[0], jobnum); return EXIT_FAILURE; found: // TODO: bash prints a string representation // of job being foregrounded (like "sleep 1 | cat") - if (*child->argv[0] == 'f') { + if (*argv[0] == 'f') { /* Put the job into the foreground. */ tcsetpgrp(interactive_fd, pi->pgrp); } /* Restart the processes in the job */ - for (i = 0; i < pi->num_progs; i++) + debug_jobs_printf("reviving %d procs, pgrp %d\n", pi->num_progs, pi->pgrp); + for (i = 0; i < pi->num_progs; i++) { + debug_jobs_printf("reviving pid %d\n", pi->progs[i].pid); pi->progs[i].is_stopped = 0; + } + pi->stopped_progs = 0; i = kill(- pi->pgrp, SIGCONT); - pi->stopped_progs = 0; if (i < 0) { if (errno == ESRCH) { - remove_bg_job(pi); + delete_finished_bg_job(pi); } else { bb_perror_msg("kill (SIGCONT)"); } } - if (*child->argv[0] == 'f') + if (*argv[0] == 'f') { + remove_bg_job(pi); return checkjobs_and_fg_shell(pi); + } return EXIT_SUCCESS; } /* built-in 'help' handler */ -static int builtin_help(struct child_prog *dummy ATTRIBUTE_UNUSED) +static int builtin_help(char **argv ATTRIBUTE_UNUSED) { const struct built_in_command *x; @@ -696,7 +700,7 @@ static int builtin_help(struct child_prog *dummy ATTRIBUTE_UNUSED) } /* built-in 'jobs' handler */ -static int builtin_jobs(struct child_prog *child ATTRIBUTE_UNUSED) +static int builtin_jobs(char **argv ATTRIBUTE_UNUSED) { struct pipe *job; const char *status_string; @@ -713,18 +717,18 @@ static int builtin_jobs(struct child_prog *child ATTRIBUTE_UNUSED) } /* built-in 'pwd' handler */ -static int builtin_pwd(struct child_prog *dummy ATTRIBUTE_UNUSED) +static int builtin_pwd(char **argv ATTRIBUTE_UNUSED) { puts(set_cwd()); return EXIT_SUCCESS; } /* built-in 'read VAR' handler */ -static int builtin_read(struct child_prog *child) +static int builtin_read(char **argv) { int res; - if (child->argv[1]) { + if (argv[1]) { char string[BUFSIZ]; char *var = NULL; @@ -732,9 +736,9 @@ static int builtin_read(struct child_prog *child) /* read string */ fgets(string, sizeof(string), stdin); chomp(string); - var = malloc(strlen(child->argv[1]) + strlen(string) + 2); + var = malloc(strlen(argv[1]) + strlen(string) + 2); if (var) { - sprintf(var, "%s=%s", child->argv[1], string); + sprintf(var, "%s=%s", argv[1], string); res = set_local_var(var, 0); } else res = -1; @@ -748,9 +752,9 @@ static int builtin_read(struct child_prog *child) } /* built-in 'set VAR=value' handler */ -static int builtin_set(struct child_prog *child) +static int builtin_set(char **argv) { - char *temp = child->argv[1]; + char *temp = argv[1]; struct variables *e; if (temp == NULL) @@ -764,11 +768,11 @@ static int builtin_set(struct child_prog *child) /* Built-in 'shift' handler */ -static int builtin_shift(struct child_prog *child) +static int builtin_shift(char **argv) { int n = 1; - if (child->argv[1]) { - n = atoi(child->argv[1]); + if (argv[1]) { + n = atoi(argv[1]); } if (n >= 0 && n < global_argc) { /* XXX This probably breaks $0 */ @@ -780,25 +784,25 @@ static int builtin_shift(struct child_prog *child) } /* Built-in '.' handler (read-in and execute commands from file) */ -static int builtin_source(struct child_prog *child) +static int builtin_source(char **argv) { FILE *input; int status; - if (child->argv[1] == NULL) + if (argv[1] == NULL) return EXIT_FAILURE; /* XXX search through $PATH is missing */ - input = fopen(child->argv[1], "r"); + input = fopen(argv[1], "r"); if (!input) { - bb_error_msg("cannot open '%s'", child->argv[1]); + bb_error_msg("cannot open '%s'", argv[1]); return EXIT_FAILURE; } /* Now run the file */ /* XXX argv and argc are broken; need to save old global_argv * (pointer only is OK!) on this stack frame, - * set global_argv=child->argv+1, recurse, and restore. */ + * set global_argv=argv+1, recurse, and restore. */ mark_open(fileno(input)); status = parse_file_outer(input); mark_closed(fileno(input)); @@ -806,10 +810,10 @@ static int builtin_source(struct child_prog *child) return status; } -static int builtin_umask(struct child_prog *child) +static int builtin_umask(char **argv) { mode_t new_umask; - const char *arg = child->argv[1]; + const char *arg = argv[1]; char *end; if (arg) { new_umask = strtoul(arg, &end, 8); @@ -825,16 +829,16 @@ static int builtin_umask(struct child_prog *child) } /* built-in 'unset VAR' handler */ -static int builtin_unset(struct child_prog *child) +static int builtin_unset(char **argv) { /* bash returned already true */ - unset_local_var(child->argv[1]); + unset_local_var(argv[1]); return EXIT_SUCCESS; } -static int builtin_not_written(struct child_prog *child) +static int builtin_not_written(char **argv) { - printf("builtin_%s not written\n", child->argv[0]); + printf("builtin_%s not written\n", argv[0]); return EXIT_FAILURE; } @@ -1132,68 +1136,71 @@ static void restore_redirects(int squirrel[]) /* XXX no exit() here. If you don't exec, use _exit instead. * The at_exit handlers apparently confuse the calling process, * in particular stdin handling. Not sure why? -- because of vfork! (vda) */ -static void pseudo_exec(struct child_prog *child) +static void pseudo_exec_argv(char **argv) { int i, rcode; char *p; const struct built_in_command *x; - if (child->argv) { - for (i = 0; is_assignment(child->argv[i]); i++) { - debug_printf("pid %d environment modification: %s\n", - getpid(), child->argv[i]); - // FIXME: vfork case?? - p = insert_var_value(child->argv[i]); - putenv(strdup(p)); - if (p != child->argv[i]) - free(p); - } - child->argv += i; /* XXX this hack isn't so horrible, since we are about - to exit, and therefore don't need to keep data - structures consistent for free() use. */ - // FIXME: ...unless we have _vforked_! Think NOMMU! - - /* If a variable is assigned in a forest, and nobody listens, - * was it ever really set? - */ - if (child->argv[0] == NULL) { - _exit(EXIT_SUCCESS); - } - - /* - * Check if the command matches any of the builtins. - * Depending on context, this might be redundant. But it's - * easier to waste a few CPU cycles than it is to figure out - * if this is one of those cases. - */ - for (x = bltins; x->cmd; x++) { - if (strcmp(child->argv[0], x->cmd) == 0) { - debug_printf("builtin exec %s\n", child->argv[0]); - rcode = x->function(child); - fflush(stdout); - _exit(rcode); - } - } + for (i = 0; is_assignment(argv[i]); i++) { + debug_printf("pid %d environment modification: %s\n", + getpid(), argv[i]); + // FIXME: vfork case?? + p = insert_var_value(argv[i]); + putenv(strdup(p)); + if (p != argv[i]) + free(p); + } + argv += i; + /* If a variable is assigned in a forest, and nobody listens, + * was it ever really set? + */ + if (argv[0] == NULL) { + _exit(EXIT_SUCCESS); + } - /* Check if the command matches any busybox internal commands - * ("applets") here. - * FIXME: This feature is not 100% safe, since - * BusyBox is not fully reentrant, so we have no guarantee the things - * from the .bss are still zeroed, or that things from .data are still - * at their defaults. We could exec ourself from /proc/self/exe, but I - * really dislike relying on /proc for things. We could exec ourself - * from global_argv[0], but if we are in a chroot, we may not be able - * to find ourself... */ + /* + * Check if the command matches any of the builtins. + * Depending on context, this might be redundant. But it's + * easier to waste a few CPU cycles than it is to figure out + * if this is one of those cases. + */ + for (x = bltins; x->cmd; x++) { + if (strcmp(argv[0], x->cmd) == 0) { + debug_printf("builtin exec %s\n", argv[0]); + rcode = x->function(argv); + fflush(stdout); + _exit(rcode); + } + } + + /* Check if the command matches any busybox internal commands + * ("applets") here. + * FIXME: This feature is not 100% safe, since + * BusyBox is not fully reentrant, so we have no guarantee the things + * from the .bss are still zeroed, or that things from .data are still + * at their defaults. We could exec ourself from /proc/self/exe, but I + * really dislike relying on /proc for things. We could exec ourself + * from global_argv[0], but if we are in a chroot, we may not be able + * to find ourself... */ #if ENABLE_FEATURE_SH_STANDALONE - debug_printf("running applet %s\n", child->argv[0]); - run_applet_and_exit(child->argv[0], child->argv); + debug_printf("running applet %s\n", argv[0]); + run_applet_and_exit(argv[0], argv); // is it ok that run_applet_and_exit() does exit(), not _exit()? // NB: IIRC on NOMMU we are after _vfork_, not fork! #endif - debug_printf("exec of %s\n", child->argv[0]); - execvp(child->argv[0], child->argv); - bb_perror_msg("cannot exec '%s'", child->argv[0]); - _exit(1); + debug_printf("exec of %s\n", argv[0]); + execvp(argv[0], argv); + bb_perror_msg("cannot exec '%s'", argv[0]); + _exit(1); +} + +static void pseudo_exec(struct child_prog *child) +{ + int rcode; + + if (child->argv) { + pseudo_exec_argv(child->argv); } if (child->group) { @@ -1233,13 +1240,17 @@ static void insert_bg_job(struct pipe *pi) /* physically copy the struct job */ memcpy(thejob, pi, sizeof(struct pipe)); +// (pi->num_progs+1) is one-too-many I think? + thejob->progs = xmalloc(sizeof(pi->progs[0]) * (pi->num_progs+1)); + memcpy(thejob->progs, pi->progs, sizeof(pi->progs[0]) * (pi->num_progs+1)); thejob->next = NULL; - thejob->running_progs = thejob->num_progs; - thejob->stopped_progs = 0; + /*seems to be wrong:*/ + /*thejob->running_progs = thejob->num_progs;*/ + /*thejob->stopped_progs = 0;*/ thejob->text = xmalloc(BUFSIZ); /* cmdedit buffer size */ - //if (pi->progs[0] && pi->progs[0].argv && pi->progs[0].argv[0]) { + // FIXME: overflow check? and also trim the size, BUFSIZ can be 4K! char *bar = thejob->text; char **foo = pi->progs[0].argv; if (foo) @@ -1254,7 +1265,6 @@ static void insert_bg_job(struct pipe *pi) last_jobid = thejob->jobid; } -/* remove a backgrounded job */ static void remove_bg_job(struct pipe *pi) { struct pipe *prev_pipe; @@ -1271,7 +1281,12 @@ static void remove_bg_job(struct pipe *pi) last_jobid = job_list->jobid; else last_jobid = 0; +} +/* remove a backgrounded job */ +static void delete_finished_bg_job(struct pipe *pi) +{ + remove_bg_job(pi); pi->stopped_progs = 0; free_pipe(pi, 0); free(pi); @@ -1289,36 +1304,67 @@ static int checkjobs(struct pipe* fg_pipe) int rcode = 0; attributes = WUNTRACED; -//WUNTRACED?? huh, what will happed on Ctrl-Z? fg waiting code -//doesn't seem to be ready for stopped children! (only exiting ones)... if (fg_pipe == NULL) { attributes |= WNOHANG; } +/* Do we do this right? + * bash-3.00# sleep 20 | false + * + * [3]+ Stopped sleep 20 | false + * bash-3.00# echo $? + * 1 <========== bg pipe is not fully done, but exitcode is already known! + */ + wait_more: while ((childpid = waitpid(-1, &status, attributes)) > 0) { + const int dead = WIFEXITED(status) || WIFSIGNALED(status); + +#ifdef DEBUG_SHELL_JOBS + if (WIFSTOPPED(status)) + debug_jobs_printf("pid %d stopped by sig %d (exitcode %d)\n", + childpid, WSTOPSIG(status), WEXITSTATUS(status)); + if (WIFSIGNALED(status)) + debug_jobs_printf("pid %d killed by sig %d (exitcode %d)\n", + childpid, WTERMSIG(status), WEXITSTATUS(status)); + if (WIFEXITED(status)) + debug_jobs_printf("pid %d exited, exitcode %d\n", + childpid, WEXITSTATUS(status)); +#endif /* Were we asked to wait for fg pipe? */ if (fg_pipe) { int i; for (i = 0; i < fg_pipe->num_progs; i++) { + debug_jobs_printf("check pid %d\n", fg_pipe->progs[i].pid); if (fg_pipe->progs[i].pid == childpid) { /* printf("process %d exit %d\n", i, WEXITSTATUS(status)); */ - fg_pipe->progs[i].pid = 0; - if (i == fg_pipe->num_progs-1) - /* last process gives overall exitstatus */ - rcode = WEXITSTATUS(status); - if (--fg_pipe->running_progs <= 0) - /* All processes in fg pipe have exited */ + if (dead) { + fg_pipe->progs[i].pid = 0; + fg_pipe->running_progs--; + if (i == fg_pipe->num_progs-1) + /* last process gives overall exitstatus */ + rcode = WEXITSTATUS(status); + } else { + fg_pipe->progs[i].is_stopped = 1; + fg_pipe->stopped_progs++; + } + debug_jobs_printf("fg_pipe: running_progs %d stopped_progs %d\n", + fg_pipe->running_progs, fg_pipe->stopped_progs); + if (fg_pipe->running_progs - fg_pipe->stopped_progs <= 0) { + /* All processes in fg pipe have exited/stopped */ + if (fg_pipe->running_progs) + insert_bg_job(fg_pipe); return rcode; + } /* There are still running processes in the fg pipe */ goto wait_more; } } + /* fall through to searching process in bg pipes */ } /* We asked to wait for bg or orphaned children */ /* No need to remember exitcode in this case */ - for (pi = job_list; pi; pi = pi->next) { prognum = 0; while (prognum < pi->num_progs) { @@ -1330,18 +1376,17 @@ static int checkjobs(struct pipe* fg_pipe) /* Happens when shell is used as init process (init=/bin/sh) */ debug_printf("checkjobs: pid %d was not in our list!\n", childpid); - continue; + goto wait_more; found_pi_and_prognum: - if (WIFEXITED(status) || WIFSIGNALED(status)) { + if (dead) { /* child exited */ - pi->running_progs--; pi->progs[prognum].pid = 0; - + pi->running_progs--; if (!pi->running_progs) { printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->text); - remove_bg_job(pi); + delete_finished_bg_job(pi); } } else { /* child stopped */ @@ -1350,7 +1395,9 @@ static int checkjobs(struct pipe* fg_pipe) } } - if (childpid == -1 && errno != ECHILD) + /* wait found no children or failed */ + + if (childpid && errno != ECHILD) bb_perror_msg("waitpid"); /* move the shell to the foreground */ @@ -1402,6 +1449,8 @@ static int run_pipe_real(struct pipe *pi) nextin = 0; pi->pgrp = -1; + pi->running_progs = 0; + pi->stopped_progs = 0; /* Check if this is a simple builtin (not part of a pipe). * Builtins within pipes have to fork anyway, and are handled in @@ -1418,12 +1467,14 @@ static int run_pipe_real(struct pipe *pi) return rcode; } - if (single_fg && pi->progs[0].argv != NULL) { - for (i = 0; is_assignment(child->argv[i]); i++) + if (single_fg && child->argv != NULL) { + char **argv = child->argv; + + for (i = 0; is_assignment(argv[i]); i++) continue; - if (i != 0 && child->argv[i] == NULL) { + if (i != 0 && argv[i] == NULL) { /* assignments, but no command: set the local environment */ - for (i = 0; child->argv[i] != NULL; i++) { + for (i = 0; argv[i] != NULL; i++) { /* Ok, this case is tricky. We have to decide if this is a * local variable, or an already exported variable. If it is * already exported, we have to export the new value. If it is @@ -1432,7 +1483,7 @@ static int run_pipe_real(struct pipe *pi) * variable. */ int export_me = 0; char *name, *value; - name = xstrdup(child->argv[i]); + name = xstrdup(argv[i]); debug_printf("Local environment set: %s\n", name); value = strchr(name, '='); if (value) @@ -1441,17 +1492,17 @@ static int run_pipe_real(struct pipe *pi) export_me = 1; } free(name); - p = insert_var_value(child->argv[i]); + p = insert_var_value(argv[i]); set_local_var(p, export_me); - if (p != child->argv[i]) + if (p != argv[i]) free(p); } return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ } - for (i = 0; is_assignment(child->argv[i]); i++) { - p = insert_var_value(child->argv[i]); + for (i = 0; is_assignment(argv[i]); i++) { + p = insert_var_value(argv[i]); putenv(strdup(p)); - if (p != child->argv[i]) { + if (p != argv[i]) { child->sp--; free(p); } @@ -1459,27 +1510,25 @@ static int run_pipe_real(struct pipe *pi) if (child->sp) { char *str; - str = make_string(child->argv + i); + str = make_string(argv + i); parse_string_outer(str, FLAG_EXIT_FROM_LOOP | FLAG_REPARSING); free(str); return last_return_code; } for (x = bltins; x->cmd; x++) { - if (strcmp(child->argv[i], x->cmd) == 0) { - if (x->function == builtin_exec && child->argv[i+1] == NULL) { + if (strcmp(argv[i], x->cmd) == 0) { + if (x->function == builtin_exec && argv[i+1] == NULL) { debug_printf("magic exec\n"); setup_redirects(child, NULL); return EXIT_SUCCESS; } - debug_printf("builtin inline %s\n", child->argv[0]); + debug_printf("builtin inline %s\n", argv[0]); /* XXX setup_redirects acts on file descriptors, not FILEs. * This is perfect for work that comes after exec(). * Is it really safe for inline use? Experimentally, * things seem to work with glibc. */ setup_redirects(child, squirrel); - child->argv += i; /* XXX horrible hack */ - rcode = x->function(child); - child->argv -= i; /* XXX restore hack so free() can work right */ + rcode = x->function(argv + i); restore_redirects(squirrel); return rcode; } @@ -1488,10 +1537,10 @@ static int run_pipe_real(struct pipe *pi) { // FIXME: applet runs like part of shell - for example, it ignores // SIGINT! Try to Ctrl-C out of "rm -i"... doesn't work - const struct bb_applet *a = find_applet_by_name(child->argv[i]); + const struct bb_applet *a = find_applet_by_name(argv[i]); if (a && a->nofork) { setup_redirects(child, squirrel); - rcode = run_nofork_applet(a, child->argv + i); + rcode = run_nofork_applet(a, argv + i); restore_redirects(squirrel); return rcode; } @@ -1503,7 +1552,6 @@ static int run_pipe_real(struct pipe *pi) * for initial child code after fork */ set_jobctrl_sighandler(SIG_IGN); - pi->running_progs = 0; for (i = 0; i < pi->num_progs; i++) { child = &(pi->progs[i]); -- cgit v1.2.3