--- /dev/null
+/* 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
--- /dev/null
+/* 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