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