hush: fix nofork + ctrl-Z clobbering of globals
This commit is contained in:
@ -509,10 +509,20 @@ int wait_nohang(int *wstat);
|
|||||||
#define wait_exitcode(w) ((w) >> 8)
|
#define wait_exitcode(w) ((w) >> 8)
|
||||||
#define wait_stopsig(w) ((w) >> 8)
|
#define wait_stopsig(w) ((w) >> 8)
|
||||||
#define wait_stopped(w) (((w) & 127) == 127)
|
#define wait_stopped(w) (((w) & 127) == 127)
|
||||||
/* Does NOT check that applet is NOFORK, just blindly runs it */
|
|
||||||
int run_nofork_applet(const struct bb_applet *a, char **argv);
|
|
||||||
/* wait4pid(spawn(argv)) + NOFORK/NOEXEC (if configured) */
|
/* wait4pid(spawn(argv)) + NOFORK/NOEXEC (if configured) */
|
||||||
int spawn_and_wait(char **argv);
|
int spawn_and_wait(char **argv);
|
||||||
|
struct nofork_save_area {
|
||||||
|
const struct bb_applet *current_applet;
|
||||||
|
int xfunc_error_retval;
|
||||||
|
uint32_t option_mask32;
|
||||||
|
int die_sleep;
|
||||||
|
smallint saved;
|
||||||
|
};
|
||||||
|
void save_nofork_data(struct nofork_save_area *save);
|
||||||
|
void restore_nofork_data(struct nofork_save_area *save);
|
||||||
|
/* Does NOT check that applet is NOFORK, just blindly runs it */
|
||||||
|
int run_nofork_applet(const struct bb_applet *a, char **argv);
|
||||||
|
int run_nofork_applet_prime(struct nofork_save_area *old, const struct bb_applet *a, char **argv);
|
||||||
|
|
||||||
/* Helpers for daemonization.
|
/* Helpers for daemonization.
|
||||||
*
|
*
|
||||||
|
@ -100,16 +100,29 @@ int wait_pid(int *wstat, int pid)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
int run_nofork_applet(const struct bb_applet *a, char **argv)
|
void save_nofork_data(struct nofork_save_area *save)
|
||||||
|
{
|
||||||
|
save->current_applet = current_applet;
|
||||||
|
save->xfunc_error_retval = xfunc_error_retval;
|
||||||
|
save->option_mask32 = option_mask32;
|
||||||
|
save->die_sleep = die_sleep;
|
||||||
|
save->saved = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void restore_nofork_data(struct nofork_save_area *save)
|
||||||
|
{
|
||||||
|
current_applet = save->current_applet;
|
||||||
|
xfunc_error_retval = save->xfunc_error_retval;
|
||||||
|
option_mask32 = save->option_mask32;
|
||||||
|
die_sleep = save->die_sleep;
|
||||||
|
|
||||||
|
applet_name = current_applet->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_nofork_applet_prime(struct nofork_save_area *old, const struct bb_applet *a, char **argv)
|
||||||
{
|
{
|
||||||
int rc, argc;
|
int rc, argc;
|
||||||
|
|
||||||
/* Save some shared globals */
|
|
||||||
const struct bb_applet *old_a = current_applet;
|
|
||||||
int old_x = xfunc_error_retval;
|
|
||||||
uint32_t old_m = option_mask32;
|
|
||||||
int old_sleep = die_sleep;
|
|
||||||
|
|
||||||
current_applet = a;
|
current_applet = a;
|
||||||
applet_name = a->name;
|
applet_name = a->name;
|
||||||
xfunc_error_retval = EXIT_FAILURE;
|
xfunc_error_retval = EXIT_FAILURE;
|
||||||
@ -138,14 +151,18 @@ int run_nofork_applet(const struct bb_applet *a, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Restoring globals */
|
/* Restoring globals */
|
||||||
current_applet = old_a;
|
restore_nofork_data(old);
|
||||||
applet_name = old_a->name;
|
|
||||||
xfunc_error_retval = old_x;
|
|
||||||
option_mask32 = old_m;
|
|
||||||
die_sleep = old_sleep;
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int run_nofork_applet(const struct bb_applet *a, char **argv)
|
||||||
|
{
|
||||||
|
struct nofork_save_area old;
|
||||||
|
/* Saving globals */
|
||||||
|
save_nofork_data(&old);
|
||||||
|
return run_nofork_applet_prime(&old, a, argv);
|
||||||
|
}
|
||||||
|
|
||||||
int spawn_and_wait(char **argv)
|
int spawn_and_wait(char **argv)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
40
shell/hush.c
40
shell/hush.c
@ -441,30 +441,28 @@ static void signal_SA_RESTART(int sig, void (*handler)(int))
|
|||||||
sigaction(sig, &sa, NULL);
|
sigaction(sig, &sa, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct nofork_save_area nofork_save;
|
||||||
static sigjmp_buf nofork_jb;
|
static sigjmp_buf nofork_jb;
|
||||||
static smallint nofork_flag;
|
|
||||||
static struct pipe *nofork_pipe;
|
static struct pipe *nofork_pipe;
|
||||||
|
|
||||||
static void handler_ctrl_z(int sig)
|
static void handler_ctrl_z(int sig)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
fprintf(stderr, "got tty sig %d\n", sig);
|
debug_jobs_printf("got tty sig %d\n", sig);
|
||||||
if (!nofork_flag)
|
|
||||||
return;
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid < 0) /* can't fork. Pretend there were no Ctrl-Z */
|
if (pid < 0) /* can't fork. Pretend there were no Ctrl-Z */
|
||||||
return;
|
return;
|
||||||
fprintf(stderr, "bg'ing nofork\n");
|
debug_jobs_printf("bg'ing nofork\n");
|
||||||
nofork_flag = 0;
|
nofork_save.saved = 0; /* flag the fact that Ctrl-Z was handled */
|
||||||
nofork_pipe->running_progs = 1;
|
nofork_pipe->running_progs = 1;
|
||||||
nofork_pipe->stopped_progs = 0;
|
nofork_pipe->stopped_progs = 0;
|
||||||
if (!pid) { /* child */
|
if (!pid) { /* child */
|
||||||
fprintf(stderr, "setting pgrp for child\n");
|
debug_jobs_printf("setting pgrp for child\n");
|
||||||
setpgrp();
|
setpgrp();
|
||||||
signal(sig, SIG_DFL); /* make child do default action (stop) */
|
signal(SIGTSTP, SIG_DFL); /* make child do default action (stop) */
|
||||||
raise(sig); /* resend TSTP so that child will be stopped */
|
raise(SIGTSTP); /* resend TSTP so that child will be stopped */
|
||||||
fprintf(stderr, "returning to child\n");
|
debug_jobs_printf("returning to child\n");
|
||||||
/* return to nofork, it will eventually exit now,
|
/* return to nofork, it will eventually exit now,
|
||||||
* not return back to shell */
|
* not return back to shell */
|
||||||
return;
|
return;
|
||||||
@ -1588,25 +1586,23 @@ static int run_pipe_real(struct pipe *pi)
|
|||||||
const struct bb_applet *a = find_applet_by_name(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);
|
||||||
if (sigsetjmp(nofork_jb, 1) == 0) {
|
/* TSTP handler will store pid etc in pi */
|
||||||
// enable ctrl_z here, not globally?
|
|
||||||
nofork_flag = 1;
|
|
||||||
/* TSTP handler will store pid there etc */
|
|
||||||
nofork_pipe = pi;
|
nofork_pipe = pi;
|
||||||
rcode = run_nofork_applet(a, argv + i);
|
save_nofork_data(&nofork_save);
|
||||||
if (--nofork_flag != 0)
|
if (sigsetjmp(nofork_jb, 1) == 0) {
|
||||||
|
signal_SA_RESTART(SIGTSTP, handler_ctrl_z);
|
||||||
|
rcode = run_nofork_applet_prime(&nofork_save, a, argv + i);
|
||||||
|
if (--nofork_save.saved != 0)
|
||||||
/* Ctrl-Z! forked, we are child */
|
/* Ctrl-Z! forked, we are child */
|
||||||
exit(rcode);
|
exit(rcode);
|
||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
return rcode;
|
return rcode;
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Exiting nofork early\n");
|
|
||||||
/* Ctrl-Z, forked, we are parent.
|
/* Ctrl-Z, forked, we are parent.
|
||||||
* Sighandler has longjmped us here */
|
* Sighandler has longjmped us here */
|
||||||
//problem: run_nofork_applet did not do the
|
signal(SIGTSTP, SIG_IGN);
|
||||||
// "restore" trick and globals are trashed:
|
debug_jobs_printf("Exiting nofork early\n");
|
||||||
// for one, applet_name is not "hush" :)
|
restore_nofork_data(&nofork_save);
|
||||||
// need to split run_nofork_applet into setup/run/restore...
|
|
||||||
restore_redirects(squirrel);
|
restore_redirects(squirrel);
|
||||||
insert_bg_job(pi);
|
insert_bg_job(pi);
|
||||||
return 0;
|
return 0;
|
||||||
@ -3021,8 +3017,6 @@ static void setup_job_control(void)
|
|||||||
|
|
||||||
/* Grab control of the terminal. */
|
/* Grab control of the terminal. */
|
||||||
tcsetpgrp(interactive_fd, shell_pgrp);
|
tcsetpgrp(interactive_fd, shell_pgrp);
|
||||||
|
|
||||||
signal_SA_RESTART(SIGTSTP, handler_ctrl_z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int hush_main(int argc, char **argv);
|
int hush_main(int argc, char **argv);
|
||||||
|
Reference in New Issue
Block a user