RJK's general signal-handling robustness patch. Should fix the weird
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 2 Nov 2002 14:35:57 +0000 (14:35 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Sat, 2 Nov 2002 14:35:57 +0000 (14:35 +0000)
spin behaviour occasionally seen after pterm's child process dies.

git-svn-id: svn://svn.tartarus.org/sgt/putty@2181 cda61777-01e9-0310-a592-d414129be87e

Recipe
unix/pterm.c
unix/pty.c
unix/signal.c [new file with mode: 0644]
unix/unix.h

diff --git a/Recipe b/Recipe
index 25670e3..99a7294 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -137,6 +137,6 @@ puttygen : [G] puttygen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
          + sshpubk sshaes sshsh512 import winutils puttygen.res LIBS
 
 pterm    : [X] pterm terminal wcwidth uxucs uxmisc tree234 misc ldisc ldiscucs
-         + logging uxprint settings pty be_none uxstore
+         + logging uxprint settings pty be_none uxstore signal
 
 plink    : [U] uxplink uxcons NONSSH UXSSH be_all logging UXMISC
index cc45962..94b732d 100644 (file)
@@ -1927,12 +1927,27 @@ int do_cmdline(int argc, char **argv, int do_everything)
     return err;
 }
 
+static void block_signal(int sig, int block_it) {
+  sigset_t ss;
+
+  sigemptyset(&ss);
+  sigaddset(&ss, sig);
+  if(sigprocmask(block_it ? SIG_BLOCK : SIG_UNBLOCK, &ss, 0) < 0) {
+    perror("sigprocmask");
+    exit(1);
+  }
+}
+
 int main(int argc, char **argv)
 {
     extern int pty_master_fd;         /* declared in pty.c */
     extern void pty_pre_init(void);    /* declared in pty.c */
     struct gui_data *inst;
 
+    /* defer any child exit handling until we're ready to deal with
+     * it */
+    block_signal(SIGCHLD, 1);
+
     pty_pre_init();
 
     gtk_init(&argc, &argv);
@@ -2090,6 +2105,10 @@ int main(int argc, char **argv)
     inst->master_func_id = gdk_input_add(pty_master_fd, GDK_INPUT_READ,
                                         pty_input_func, inst);
 
+    /* now we're reday to deal with the child exit handler being
+     * called */
+    block_signal(SIGCHLD, 0);
+    
     gtk_main();
 
     return 0;
index fd7a93c..8bd20fe 100644 (file)
@@ -29,6 +29,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/ioctl.h>
+#include <errno.h>
 
 #include "putty.h"
 
@@ -74,8 +75,8 @@ static int pty_stamped_utmp = 0;
 static int pty_child_pid;
 static int pty_utmp_helper_pid, pty_utmp_helper_pipe;
 static int pty_term_width, pty_term_height;
-static sig_atomic_t pty_child_dead;
-static int pty_exit_code;
+static volatile sig_atomic_t pty_child_dead;
+static volatile int pty_exit_code;
 #ifndef OMIT_UTMP
 static struct utmp utmp_entry;
 #endif
@@ -163,18 +164,21 @@ static void cleanup_utmp(void)
 
 static void sigchld_handler(int signum)
 {
+    int save_errno = errno;
     pid_t pid;
     int status;
+
     pid = waitpid(-1, &status, WNOHANG);
     if (pid == pty_child_pid && (WIFEXITED(status) || WIFSIGNALED(status))) {
        pty_exit_code = status;
        pty_child_dead = TRUE;
     }
+    errno = save_errno;
 }
 
 static void fatal_sig_handler(int signum)
 {
-    signal(signum, SIG_DFL);
+    putty_signal(signum, SIG_DFL);
     cleanup_utmp();
     setuid(getuid());
     raise(signum);
@@ -254,6 +258,9 @@ void pty_pre_init(void)
     pid_t pid;
     int pipefd[2];
 
+    /* set the child signal handler straight away; it needs to be set
+     * before we ever fork. */
+    putty_signal(SIGCHLD, sigchld_handler);
     pty_master_fd = -1;
 
     if (geteuid() != getuid() || getegid() != getgid()) {
@@ -289,7 +296,7 @@ void pty_pre_init(void)
            ret = read(pipefd[0], buffer, lenof(buffer));
            if (ret <= 0) {
                cleanup_utmp();
-               exit(0);
+               _exit(0);
            } else if (!pty_stamped_utmp) {
                if (dlen < lenof(display))
                    memcpy(display+dlen, buffer,
@@ -307,47 +314,45 @@ void pty_pre_init(void)
                     * unfortunately unprotected against SIGKILL,
                     * but that's life.
                     */
-                   signal(SIGHUP, fatal_sig_handler);
-                   signal(SIGINT, fatal_sig_handler);
-                   signal(SIGQUIT, fatal_sig_handler);
-                   signal(SIGILL, fatal_sig_handler);
-                   signal(SIGABRT, fatal_sig_handler);
-                   signal(SIGFPE, fatal_sig_handler);
-                   signal(SIGPIPE, fatal_sig_handler);
-                   signal(SIGALRM, fatal_sig_handler);
-                   signal(SIGTERM, fatal_sig_handler);
-                   signal(SIGSEGV, fatal_sig_handler);
-                   signal(SIGUSR1, fatal_sig_handler);
-                   signal(SIGUSR2, fatal_sig_handler);
+                   putty_signal(SIGHUP, fatal_sig_handler);
+                   putty_signal(SIGINT, fatal_sig_handler);
+                   putty_signal(SIGQUIT, fatal_sig_handler);
+                   putty_signal(SIGILL, fatal_sig_handler);
+                   putty_signal(SIGABRT, fatal_sig_handler);
+                   putty_signal(SIGFPE, fatal_sig_handler);
+                   putty_signal(SIGPIPE, fatal_sig_handler);
+                   putty_signal(SIGALRM, fatal_sig_handler);
+                   putty_signal(SIGTERM, fatal_sig_handler);
+                   putty_signal(SIGSEGV, fatal_sig_handler);
+                   putty_signal(SIGUSR1, fatal_sig_handler);
+                   putty_signal(SIGUSR2, fatal_sig_handler);
 #ifdef SIGBUS
-                   signal(SIGBUS, fatal_sig_handler);
+                   putty_signal(SIGBUS, fatal_sig_handler);
 #endif
 #ifdef SIGPOLL
-                   signal(SIGPOLL, fatal_sig_handler);
+                   putty_signal(SIGPOLL, fatal_sig_handler);
 #endif
 #ifdef SIGPROF
-                   signal(SIGPROF, fatal_sig_handler);
+                   putty_signal(SIGPROF, fatal_sig_handler);
 #endif
 #ifdef SIGSYS
-                   signal(SIGSYS, fatal_sig_handler);
+                   putty_signal(SIGSYS, fatal_sig_handler);
 #endif
 #ifdef SIGTRAP
-                   signal(SIGTRAP, fatal_sig_handler);
+                   putty_signal(SIGTRAP, fatal_sig_handler);
 #endif
 #ifdef SIGVTALRM
-                   signal(SIGVTALRM, fatal_sig_handler);
+                   putty_signal(SIGVTALRM, fatal_sig_handler);
 #endif
 #ifdef SIGXCPU
-                   signal(SIGXCPU, fatal_sig_handler);
+                   putty_signal(SIGXCPU, fatal_sig_handler);
 #endif
 #ifdef SIGXFSZ
-                   signal(SIGXFSZ, fatal_sig_handler);
+                   putty_signal(SIGXFSZ, fatal_sig_handler);
 #endif
 #ifdef SIGIO
-                   signal(SIGIO, fatal_sig_handler);
+                   putty_signal(SIGIO, fatal_sig_handler);
 #endif
-                   /* Also clean up utmp on normal exit. */
-                   atexit(cleanup_utmp);
                    setup_utmp(pty_name, display);
                }
            }
@@ -356,7 +361,6 @@ void pty_pre_init(void)
        close(pipefd[0]);
        pty_utmp_helper_pid = pid;
        pty_utmp_helper_pipe = pipefd[1];
-       signal(SIGCHLD, sigchld_handler);
     }
 #endif
 
@@ -447,7 +451,7 @@ static char *pty_init(void *frontend, void **backend_handle,
        slavefd = open(pty_name, O_RDWR);
        if (slavefd < 0) {
            perror("slave pty: open");
-           exit(1);
+           _exit(1);
        }
 
        close(pty_master_fd);
@@ -475,8 +479,8 @@ static char *pty_init(void *frontend, void **backend_handle,
         * parent, particularly by things like sh -c 'pterm &' and
         * some window managers. Reverse this for our child process.
         */
-       signal(SIGINT, SIG_DFL);
-       signal(SIGQUIT, SIG_DFL);
+       putty_signal(SIGINT, SIG_DFL);
+       putty_signal(SIGQUIT, SIG_DFL);
        if (pty_argv)
            execvp(pty_argv[0], pty_argv);
        else {
@@ -496,13 +500,11 @@ static char *pty_init(void *frontend, void **backend_handle,
         * If we're here, exec has gone badly foom.
         */
        perror("exec");
-       exit(127);
+       _exit(127);
     } else {
-       close(slavefd);
        pty_child_pid = pid;
        pty_child_dead = FALSE;
-       signal(SIGCHLD, sigchld_handler);
-    }
+    }      
 
     return NULL;
 }
diff --git a/unix/signal.c b/unix/signal.c
new file mode 100644 (file)
index 0000000..4e103b7
--- /dev/null
@@ -0,0 +1,31 @@
+#include <signal.h>
+
+/*
+ * Calling signal() is a non-portable, as it varies in meaning between
+ * platforms and depending on feature macros, and has stupid semantics
+ * at least some of the time.
+ *
+ * This function provides the same interface as the libc function, but
+ * provides consistent semantics.  It assumes POSIX semantics for
+ * sigaction() (so you might need to do some more work if you port to
+ * something ancient like SunOS 4)
+ */
+void (*putty_signal(int sig, void (*func)(int)))(int) {
+    struct sigaction sa;
+    struct sigaction old;
+    
+    sa.sa_handler = func;
+    if(sigemptyset(&sa.sa_mask) < 0)
+       return SIG_ERR;
+    sa.sa_flags = SA_RESTART;
+    if(sigaction(sig, &sa, &old) < 0)
+       return SIG_ERR;
+    return old.sa_handler;
+}
+
+/*
+Local Variables:
+c-basic-offset:4
+comment-column:40
+End:
+*/
index 534bf92..dc2f3c4 100644 (file)
@@ -50,4 +50,7 @@ int next_socket(int *state, int *rwx);
 #define strnicmp strncasecmp
 #define stricmp strcasecmp
 
+/* BSD-semantics version of signal() */
+void (*putty_signal(int sig, void (*func)(int)))(int);
+
 #endif