hush: fix nofork + ctrl-Z clobbering of globals
authorDenis Vlasenko <vda.linux@googlemail.com>
Sat, 28 Apr 2007 16:43:18 +0000 (16:43 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sat, 28 Apr 2007 16:43:18 +0000 (16:43 -0000)
include/libbb.h
libbb/vfork_daemon_rexec.c
shell/hush.c

index fce10f3108bd55e87de312f637c34d90bf0f847c..9950c61f2245ced7e3b23fa9ad4cd6a2bbf8dcac 100644 (file)
@@ -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.
  *
index 78f3c4ad462f0d3400e457f4b3b21edd09d34962..aef74e9949554e68d96e2eacfeabeeffac7aba7d 100644 (file)
@@ -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;
index c87f3b5661ddd1322750bf0e3a30e6a5e014b3b4..b2ff0cb2ec857bbca08eaeff24898cfd7962d0a1 100644 (file)
@@ -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);