Blame SOURCES/xsane-0.999-signal-handling.patch

8ceb32
From 3b5d3b7e1f320b0bfbe48024a586c0a22375aa2d Mon Sep 17 00:00:00 2001
8ceb32
From: Nils Philippsen <nils@redhat.com>
8ceb32
Date: Thu, 3 Jul 2014 10:38:03 +0200
8ceb32
Subject: [PATCH] patch: signal-handling
8ceb32
8ceb32
Squashed commit of the following:
8ceb32
8ceb32
commit 1e9e8cf5edc469114c8eadf46817cd5c1261b35c
8ceb32
Author: Nils Philippsen <nils@redhat.com>
8ceb32
Date:   Thu Jul 3 10:14:52 2014 +0200
8ceb32
8ceb32
    don't use g_unix_open_pipe(), g_unix_fd_add()
8ceb32
8ceb32
    These functions have only recently been added to glib. Use pipe()/
8ceb32
    fcntl() and g_io_channel_unix_new()/g_io_add_watch() instead which are
8ceb32
    available in the minimum glib version needed for gtk+-2.x.
8ceb32
8ceb32
commit acbdf3f693d3d2a78ee7490ca1bf76957daf00cf
8ceb32
Author: Nils Philippsen <nils@redhat.com>
8ceb32
Date:   Thu Mar 13 13:38:12 2014 +0100
8ceb32
8ceb32
    separate signal handlers in top and bottom half
8ceb32
8ceb32
    This is to avoid race-conditions occurring when a signal is received
8ceb32
    while the signal handler is not yet finished. It also avoids calling
8ceb32
    non-reentrant functions from a signal handler. The top half (the real
8ceb32
    signal handler) just writes a character into a pipe which gets picked up
8ceb32
    and serviced by the bottom half from the normal event loop, this
8ceb32
    serializes things and makes using non-reentrant functions safe.
8ceb32
---
8ceb32
 src/xsane.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------
8ceb32
 1 file changed, 136 insertions(+), 15 deletions(-)
8ceb32
8ceb32
diff --git a/src/xsane.c b/src/xsane.c
8ceb32
index 2b9211b..fc2ebbe 100644
8ceb32
--- a/src/xsane.c
8ceb32
+++ b/src/xsane.c
8ceb32
@@ -47,6 +47,7 @@
8ceb32
 #endif
8ceb32
 
8ceb32
 #include <sys/wait.h>
8ceb32
+#include <glib-unix.h>
8ceb32
 
8ceb32
 #include <stdarg.h>
8ceb32
 
8ceb32
@@ -121,6 +122,7 @@ static const Preferences_medium_t pref_default_medium[]=
8ceb32
 
8ceb32
 int DBG_LEVEL = 0;
8ceb32
 static guint xsane_resolution_timer = 0;
8ceb32
+static int xsane_signal_pipe[2];
8ceb32
 
8ceb32
 /* ---------------------------------------------------------------------------------------------------------------------- */
8ceb32
 
8ceb32
@@ -161,8 +163,11 @@ void xsane_pref_save(void);
8ceb32
 static int xsane_pref_restore(void);
8ceb32
 static void xsane_pref_save_media(void);
8ceb32
 static void xsane_pref_restore_media(void);
8ceb32
-static RETSIGTYPE xsane_quit_handler(int signal);
8ceb32
-static RETSIGTYPE xsane_sigchld_handler(int signal);
8ceb32
+static RETSIGTYPE xsane_signal_handler_top_half(int signal);
8ceb32
+static gboolean xsane_signal_handler_bottom_half(GIOChannel *source,
8ceb32
+                                                 GIOCondition condition,
8ceb32
+                                                 gpointer user_data);
8ceb32
+static void xsane_sigchld_handler(void);
8ceb32
 static void xsane_quit(void);
8ceb32
 static void xsane_exit(void);
8ceb32
 static gint xsane_standard_option_win_delete(GtkWidget *widget, gpointer data);
8ceb32
@@ -2296,16 +2301,119 @@ static void xsane_pref_restore_media(void)
8ceb32
 
8ceb32
 /* ---------------------------------------------------------------------------------------------------------------------- */
8ceb32
 
8ceb32
-static RETSIGTYPE xsane_quit_handler(int signal)
8ceb32
+static RETSIGTYPE xsane_signal_handler_top_half(int signal)
8ceb32
 {
8ceb32
-  DBG(DBG_proc, "xsane_quit_handler\n");
8ceb32
+  const char *msg_func = "xsane_signal_handler_top_half(): ";
8ceb32
+  const char *msg_short_write = "Short write() while processing signal.\n";
8ceb32
+  const char *msg_err = "Error during write().\n";
8ceb32
+  char sig_char;
8ceb32
+  ssize_t written;
8ceb32
+  int errno_saved = errno;
8ceb32
 
8ceb32
-  xsane_quit();
8ceb32
+  switch (signal)
8ceb32
+  {
8ceb32
+    case SIGTERM:
8ceb32
+      sig_char = 't';
8ceb32
+      break;
8ceb32
+    case SIGINT:
8ceb32
+      sig_char = 'i';
8ceb32
+      break;
8ceb32
+    case SIGHUP:
8ceb32
+      sig_char = 'h';
8ceb32
+      break;
8ceb32
+    case SIGCHLD:
8ceb32
+      sig_char = 'c';
8ceb32
+      break;
8ceb32
+    default:
8ceb32
+      sig_char = '?';
8ceb32
+      break;
8ceb32
+  }
8ceb32
+
8ceb32
+  if ((written = write(xsane_signal_pipe[1], &sig_char, 1)) <= 0)
8ceb32
+  {
8ceb32
+    /* At this point, all bets are off. Salvage what we can. */
8ceb32
+
8ceb32
+    const char *msg = (written == 0) ? msg_short_write : msg_err;
8ceb32
+
8ceb32
+    if ((write(STDERR_FILENO, msg_func, strlen(msg_func)) < 0) ||
8ceb32
+        (write(STDERR_FILENO, msg, strlen(msg)) < 0))
8ceb32
+    {
8ceb32
+      /* This is really a no-op, but at this point it doesn't really matter
8ceb32
+       * anymore if the writes succeeded or not. */
8ceb32
+      goto bail_out;
8ceb32
+    }
8ceb32
+
8ceb32
+bail_out:
8ceb32
+    /* Ignore SIGCHLD errors, zombie processes don't hurt that much. */
8ceb32
+    if (signal != SIGCHLD)
8ceb32
+    {
8ceb32
+      struct SIGACTION act;
8ceb32
+      memset(&act, 0, sizeof(act));
8ceb32
+      act.sa_handler = SIG_DFL;
8ceb32
+      sigaction(signal, &act, NULL);
8ceb32
+      raise(signal);
8ceb32
+    }
8ceb32
+  }
8ceb32
+
8ceb32
+  errno = errno_saved;
8ceb32
+}
8ceb32
+
8ceb32
+static gboolean xsane_signal_handler_bottom_half(GIOChannel *source,
8ceb32
+                                                 GIOCondition condition,
8ceb32
+                                                 gpointer user_data)
8ceb32
+{
8ceb32
+  char sig_char;
8ceb32
+  ssize_t readlen;
8ceb32
+
8ceb32
+  DBG(DBG_proc, "xsane_signal_handler_bottom_half\n");
8ceb32
+
8ceb32
+  while ((readlen = read(xsane_signal_pipe[0], &sig_char, 1)) != 0)
8ceb32
+  {
8ceb32
+    if (readlen < 0)
8ceb32
+    {
8ceb32
+      if (errno == EINTR)
8ceb32
+      {
8ceb32
+        /* if interrupted by signal, just repeat reading */
8ceb32
+        continue;
8ceb32
+      }
8ceb32
+      else
8ceb32
+      {
8ceb32
+        break;
8ceb32
+      }
8ceb32
+    }
8ceb32
+
8ceb32
+    switch (sig_char)
8ceb32
+    {
8ceb32
+      case 't':
8ceb32
+      case 'i':
8ceb32
+      case 'h':
8ceb32
+        xsane_quit();
8ceb32
+        break;
8ceb32
+      case 'c':
8ceb32
+        xsane_sigchld_handler();
8ceb32
+        break;
8ceb32
+      default:
8ceb32
+        DBG(DBG_error,
8ceb32
+            "Don't know how to cope with character-encoded signal: '%c'\n",
8ceb32
+            sig_char);
8ceb32
+        break;
8ceb32
+    }
8ceb32
+  }
8ceb32
+
8ceb32
+  /* previous invocation might have read more than it should, so ignore
8ceb32
+   * EAGAIN/EWOULDBLOCK */
8ceb32
+  if (readlen < 0 && errno != EAGAIN && errno != EWOULDBLOCK)
8ceb32
+  {
8ceb32
+    DBG(DBG_error, "Error while reading from pipe: %d '%s'\n", errno,
8ceb32
+        strerror(errno));
8ceb32
+  }
8ceb32
+
8ceb32
+  return TRUE;
8ceb32
 }
8ceb32
 
8ceb32
 /* ---------------------------------------------------------------------------------------------------------------------- */
8ceb32
 
8ceb32
-static RETSIGTYPE xsane_sigchld_handler(int signal)
8ceb32
+static void xsane_sigchld_handler(void)
8ceb32
 {
8ceb32
  int status;
8ceb32
  XsaneChildprocess **childprocess_listptr = &xsane.childprocess_list;
8ceb32
@@ -6026,6 +6134,8 @@ void xsane_interface(int argc, char **argv)
8ceb32
 {
8ceb32
  struct SIGACTION act;
8ceb32
 
8ceb32
+  GIOChannel *gio_pipe_read;
8ceb32
+
8ceb32
   DBG(DBG_proc, "xsane_interface\n");
8ceb32
 
8ceb32
   xsane.info_label = NULL;
8ceb32
@@ -6069,18 +6179,29 @@ void xsane_interface(int argc, char **argv)
8ceb32
     }
8ceb32
   }
8ceb32
 
8ceb32
+  if ((pipe(xsane_signal_pipe) == -1) ||
8ceb32
+      (fcntl(xsane_signal_pipe[0], F_SETFD, FD_CLOEXEC) == -1) ||
8ceb32
+      (fcntl(xsane_signal_pipe[0], F_SETFL, O_NONBLOCK) == -1) ||
8ceb32
+      (fcntl(xsane_signal_pipe[1], F_SETFD, FD_CLOEXEC) == -1) ||
8ceb32
+      (fcntl(xsane_signal_pipe[1], F_SETFL, O_NONBLOCK) == -1) ||
8ceb32
+      !(gio_pipe_read = g_io_channel_unix_new(xsane_signal_pipe[0])) ||
8ceb32
+      !g_io_add_watch(gio_pipe_read, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
8ceb32
+          xsane_signal_handler_bottom_half, NULL))
8ceb32
+  {
8ceb32
+    DBG(DBG_error,
8ceb32
+        "Couldn't create signal handling pipe, set flags on it or install\n"
8ceb32
+        "bottom half of handler.\n");
8ceb32
+    exit(1);
8ceb32
+  }
8ceb32
+
8ceb32
   /* define SIGTERM, SIGINT, SIGHUP-handler to make sure that e.g. all temporary files are deleted */
8ceb32
   /* when xsane gets such a signal */
8ceb32
   memset(&act, 0, sizeof(act));
8ceb32
-  act.sa_handler = xsane_quit_handler;
8ceb32
-  sigaction(SIGTERM, &act, 0);
8ceb32
-  sigaction(SIGINT,  &act, 0);
8ceb32
-  sigaction(SIGHUP,  &act, 0);
8ceb32
-
8ceb32
-  /* add a signal handler that cleans up zombie child processes */
8ceb32
-  memset(&act, 0, sizeof(act));
8ceb32
-  act.sa_handler = xsane_sigchld_handler;
8ceb32
-  sigaction(SIGCHLD, &act, 0);
8ceb32
+  act.sa_handler = xsane_signal_handler_top_half;
8ceb32
+  sigaction(SIGTERM, &act, NULL);
8ceb32
+  sigaction(SIGINT,  &act, NULL);
8ceb32
+  sigaction(SIGHUP,  &act, NULL);
8ceb32
+  sigaction(SIGCHLD, &act, NULL);
8ceb32
 
8ceb32
   gtk_main();
8ceb32
   sane_exit();
8ceb32
-- 
8ceb32
1.9.3
8ceb32