From: Bruno Haible Date: Tue, 14 Oct 2003 10:27:04 +0000 (+0000) Subject: Facility for registered fatal signal cleanups. X-Git-Tag: v0.13~214 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ac21bf3bbcd11e015c2c771b8fa38ddb9ad8d2e5;p=thirdparty%2Fgettext.git Facility for registered fatal signal cleanups. --- diff --git a/gettext-tools/lib/ChangeLog b/gettext-tools/lib/ChangeLog index 22de4667d..43ebb155d 100644 --- a/gettext-tools/lib/ChangeLog +++ b/gettext-tools/lib/ChangeLog @@ -1,3 +1,13 @@ +2003-10-08 Bruno Haible + + * 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 * vasprintf.c (int_vasprintf): Assume ANSI C when copying a structure. diff --git a/gettext-tools/lib/Makefile.am b/gettext-tools/lib/Makefile.am index bbdd05935..ce4cf6ccf 100644 --- a/gettext-tools/lib/Makefile.am +++ b/gettext-tools/lib/Makefile.am @@ -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 \ diff --git a/gettext-tools/lib/Makefile.msvc b/gettext-tools/lib/Makefile.msvc index b099e9702..a219e557f 100644 --- a/gettext-tools/lib/Makefile.msvc +++ b/gettext-tools/lib/Makefile.msvc @@ -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 diff --git a/gettext-tools/lib/Makefile.vms b/gettext-tools/lib/Makefile.vms index e199087bb..68d4923e7 100644 --- a/gettext-tools/lib/Makefile.vms +++ b/gettext-tools/lib/Makefile.vms @@ -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 index 000000000..107da661a --- /dev/null +++ b/gettext-tools/lib/fatal-signal.c @@ -0,0 +1,253 @@ +/* Emergency actions in case of a fatal signal. + Copyright (C) 2003 Free Software Foundation, Inc. + Written by Bruno Haible , 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 +#include +#include +#include +#if HAVE_UNISTD_H +# include +#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 index 000000000..16e4f7243 --- /dev/null +++ b/gettext-tools/lib/fatal-signal.h @@ -0,0 +1,72 @@ +/* Emergency actions in case of a fatal signal. + Copyright (C) 2003 Free Software Foundation, Inc. + Written by Bruno Haible , 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