]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
Facility for registered fatal signal cleanups.
authorBruno Haible <bruno@clisp.org>
Tue, 14 Oct 2003 10:27:04 +0000 (10:27 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:11:03 +0000 (12:11 +0200)
gettext-tools/lib/ChangeLog
gettext-tools/lib/Makefile.am
gettext-tools/lib/Makefile.msvc
gettext-tools/lib/Makefile.vms
gettext-tools/lib/fatal-signal.c [new file with mode: 0644]
gettext-tools/lib/fatal-signal.h [new file with mode: 0644]

index 22de4667d97bc12e981a93ddec7ddec2053f67cb..43ebb155daa0c3aadf2c8151f6b6e4b47b0eb9da 100644 (file)
@@ -1,3 +1,13 @@
+2003-10-08  Bruno Haible  <bruno@clisp.org>
+
+       * fatal-signal.h: New file.
+       * fatal-signal.c: New file.
+       * Makefile.am (libgettextlib_la_SOURCES): Add them.
+       * Makefile.msvc (OBJECTS): Add fatal-signal.obj.
+       (fatal-signal.obj): New rule.
+       * Makefile.vms (OBJECTS): Add fatal-signal.obj.
+       (fatal-signal.obj): New rule.
+
 2003-09-21  Bruno Haible  <bruno@clisp.org>
 
        * vasprintf.c (int_vasprintf): Assume ANSI C when copying a structure.
index bbdd05935b81998caf93f4f1a3a8b4f907c2329c..ce4cf6ccf82f92cffcdf3e8cd2fcef361d23fad3 100644 (file)
@@ -43,6 +43,7 @@ libgettextlib_la_SOURCES = \
   error-progname.h error-progname.c \
   execute.h execute.c w32spawn.h \
   exit.h \
+  fatal-signal.h fatal-signal.c \
   findprog.h findprog.c \
   fstrcmp.h fstrcmp.c \
   full-write.h full-write.c \
index b099e9702729ebfd8503a55fdb85758b3a3bb046..a219e557fe293aff7486d73a724ff10a49544b6c 100644 (file)
@@ -90,6 +90,7 @@ OBJECTS = \
   error.obj \
   error-progname.obj \
   execute.obj \
+  fatal-signal.obj \
   findprog.obj \
   fstrcmp.obj \
   full-write.obj \
@@ -163,6 +164,9 @@ error-progname.obj : error-progname.c
 execute.obj : execute.c
        $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c execute.c
 
+fatal-signal.obj : fatal-signal.c
+       $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c fatal-signal.c
+
 findprog.obj : findprog.c
        $(CC) $(INCLUDES) $(CFLAGS) $(PICFLAGS) -c findprog.c
 
index e199087bb6e594ebe526f9b48788a596422695e7..68d4923e7cb34caca5b5df4af09cae63db4fc500 100644 (file)
@@ -48,6 +48,7 @@ OBJECTS = \
   error.obj, \
   error-progname.obj, \
   execute.obj, \
+  fatal-signal.obj, \
   findprog.obj, \
   fstrcmp.obj, \
   full-write.obj, \
@@ -124,6 +125,9 @@ error-progname.obj : error-progname.c
 execute.obj : execute.c
        $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) execute.c
 
+fatal-signal.obj : fatal-signal.c
+       $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) fatal-signal.c
+
 findprog.obj : findprog.c
        $(CC) $(INCLUDES) $(CFLAGS) /define=($(DEFS)) findprog.c
 
diff --git a/gettext-tools/lib/fatal-signal.c b/gettext-tools/lib/fatal-signal.c
new file mode 100644 (file)
index 0000000..107da66
--- /dev/null
@@ -0,0 +1,253 @@
+/* Emergency actions in case of a fatal signal.
+   Copyright (C) 2003 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification.  */
+#include "fatal-signal.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include "xmalloc.h"
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* ========================================================================= */
+
+
+/* The list of fatal signals.
+   These are those signals whose default action is to terminate the process
+   without a core dump, except
+     SIGKILL - because it cannot be caught,
+     SIGALRM SIGUSR1 SIGUSR2 SIGPOLL SIGIO SIGLOST - because applications
+       often use them for their own purpose,
+     SIGPROF SIGVTALRM - because they are used for profiling,
+     SIGSTKFLT - because it is more similar to SIGFPE, SIGSEGV, SIGBUS,
+     SIGSYS - because it is more similar to SIGABRT, SIGSEGV,
+     SIGPWR - because it of too special use,
+     SIGRTMIN...SIGRTMAX - because they are reserved for application use.
+   plus
+     SIGXCPU, SIGXFSZ - because they are quite similar to SIGTERM.  */
+
+static const int fatal_signals[] =
+  {
+    /* ISO C 99 signals.  */
+#ifdef SIGINT
+    SIGINT,
+#endif
+#ifdef SIGTERM
+    SIGTERM,
+#endif
+    /* POSIX:2001 signals.  */
+#ifdef SIGHUP
+    SIGHUP,
+#endif
+#ifdef SIGPIPE
+    SIGPIPE,
+#endif
+    /* BSD signals.  */
+#ifdef SIGXCPU
+    SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+    SIGXFSZ,
+#endif
+    0
+  };
+
+#define num_fatal_signals (SIZEOF (fatal_signals) - 1)
+
+
+/* ========================================================================= */
+
+
+typedef void (*action_t) (void);
+
+/* Type of an entry in the actions array.
+   The 'action' field is accessed from within the fatal_signal_handler(),
+   therefore we mark it as 'volatile'.  */
+typedef struct
+{
+  volatile action_t action;
+}
+actions_entry_t;
+
+/* The registered cleanup actions.  */
+static actions_entry_t static_actions[32];
+static actions_entry_t * volatile actions = static_actions;
+static sig_atomic_t volatile actions_count = 0;
+static size_t actions_allocated = SIZEOF (static_actions);
+
+
+/* Uninstall the handlers.  */
+static inline void
+uninstall_handlers ()
+{
+  size_t i;
+
+  for (i = 0; i < num_fatal_signals; i++)
+    signal (fatal_signals[i], SIG_DFL);
+}
+
+
+/* The signal handler.  It gets called asynchronously.  */
+static void
+fatal_signal_handler (int sig)
+{
+  for (;;)
+    {
+      /* Get the last registered cleanup action, in a reentrant way.  */
+      action_t action;
+      size_t n = actions_count;
+      if (n == 0)
+       break;
+      n--;
+      actions_count = n;
+      action = actions[n].action;
+      /* Execute the action.  */
+      action ();
+    }
+
+  /* Now execute the signal's default action.
+     If signal() blocks the signal being delivered for the duration of the
+     signal handler's execution, the re-raised signal is delivered when this
+     handler returns; otherwise it is delivered already during raise().  */
+  uninstall_handlers ();
+#if HAVE_RAISE
+  raise (sig);
+#else
+  kill (getpid (), sig);
+#endif
+}
+
+
+/* Install the handlers.  */
+static inline void
+install_handlers ()
+{
+  size_t i;
+
+  for (i = 0; i < num_fatal_signals; i++)
+    signal (fatal_signals[i], &fatal_signal_handler);
+}
+
+
+/* Register a cleanup function to be executed when a catchable fatal signal
+   occurs.  */
+void
+at_fatal_signal (action_t action)
+{
+  static bool cleanup_initialized = false;
+  if (!cleanup_initialized)
+    {
+      install_handlers ();
+      cleanup_initialized = true;
+    }
+
+  if (actions_count == actions_allocated)
+    {
+      /* Extend the actions array.  Note that we cannot use xrealloc(),
+        because then the cleanup() function could access an already
+        deallocated array.  */
+      actions_entry_t *old_actions = actions;
+      size_t new_actions_allocated = 2 * actions_allocated;
+      actions_entry_t *new_actions =
+       xmalloc (new_actions_allocated * sizeof (actions_entry_t));
+
+      memcpy (new_actions, old_actions,
+             actions_allocated * sizeof (actions_entry_t));
+      actions = new_actions;
+      actions_allocated = new_actions_allocated;
+      /* Now we can free the old actions array.  */
+      if (old_actions != static_actions)
+       free (old_actions);
+    }
+  /* The two uses of 'volatile' in the types above (and ISO C 99 section
+     5.1.2.3.(5)) ensure that we increment the actions_count only after
+     the new action has been written to the memory location
+     actions[actions_count].  */
+  actions[actions_count].action = action;
+  actions_count++;
+}
+
+
+/* ========================================================================= */
+
+
+#if HAVE_POSIX_SIGNALBLOCKING
+
+static sigset_t fatal_signal_set;
+
+static void
+init_fatal_signal_set ()
+{
+  static bool fatal_signal_set_initialized = false;
+  if (!fatal_signal_set_initialized)
+    {
+      size_t i;
+
+      sigemptyset (&fatal_signal_set);
+      for (i = 0; i < num_fatal_signals; i++)
+       sigaddset (&fatal_signal_set, fatal_signals[i]);
+
+      fatal_signal_set_initialized = true;
+    }
+}
+
+/* Temporarily delay the catchable fatal signals.  */
+void
+block_fatal_signals ()
+{
+  init_fatal_signal_set ();
+  sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
+}
+
+/* Stop delaying the catchable fatal signals.  */
+void
+unblock_fatal_signals ()
+{
+  init_fatal_signal_set ();
+  sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
+}
+
+#else
+
+/* Don't bother caring about the old systems which don't have POSIX signal
+   blocking.  */
+
+void
+block_fatal_signals ()
+{
+}
+
+void
+unblock_fatal_signals ()
+{
+}
+
+#endif
diff --git a/gettext-tools/lib/fatal-signal.h b/gettext-tools/lib/fatal-signal.h
new file mode 100644 (file)
index 0000000..16e4f72
--- /dev/null
@@ -0,0 +1,72 @@
+/* Emergency actions in case of a fatal signal.
+   Copyright (C) 2003 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* It is often useful to do some cleanup action when a usually fatal signal
+   terminates the process, like removing a temporary file or killing a
+   subprocess that may be stuck waiting for a device, pipe or network input.
+   Such signals are SIGHUP, SIGINT, SIGPIPE, SIGTERM, and possibly others.
+   The limitation of this facility is that it cannot work for SIGKILL.  */
+
+/* Register a cleanup function to be executed when a catchable fatal signal
+   occurs.
+
+   Restrictions for the cleanup function:
+     - The cleanup function can do all kinds of system calls.
+     - It can also access application dependent memory locations and data
+       structures provided they are in a consistent state. One way to ensure
+       this is through block_fatal_signals()/unblock_fatal_signals(), see
+       below.  Another - more tricky - way to ensure this is the careful use
+       of 'volatile'.
+   However,
+     - malloc() and similarly complex facilities are not safe to be called
+       because they are not guaranteed to be in a consistent state.
+     - Also, the cleanup function must not block the catchable fatal signals
+       and leave them blocked upon return.
+
+   The cleanup function is executed asynchronously.  It is unspecified
+   whether during its execution the catchable fatal signals are blocked
+   or not.  */
+extern void at_fatal_signal (void (*function) (void));
+
+
+/* Sometimes it is necessary to block the usually fatal signals while the
+   data structures being accessed by the cleanup action are being built or
+   reorganized.  This is the case, for example, when a temporary file or
+   directory is created through mkstemp() or mkdtemp(), because these
+   functions create the temporary file or directory _before_ returning its
+   name to the application.  */
+
+/* Temporarily delay the catchable fatal signals.
+   The signals will be blocked (= delayed) until the next call to
+   unblock_fatal_signals().  If the signals are already blocked, a further
+   call to block_fatal_signals() has no effect.  */
+extern void block_fatal_signals (void);
+
+/* Stop delaying the catchable fatal signals.  */
+extern void unblock_fatal_signals (void);
+
+
+#ifdef __cplusplus
+}
+#endif