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