(root)/
man-db-2.12.0/
lib/
cleanup.c
       1  /*
       2   * cleanup.c -- simple dynamic cleanup function management
       3   * Copyright (C) 1995 Markus Armbruster.
       4   * Copyright (C) 2007 Colin Watson.
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Library General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2 of the License, or (at your option) any later version.
      10   *
      11   * This library is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14   * Library General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU Library General Public
      17   * License along with this library; see the file docs/COPYING.LIB.  If not,
      18   * write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
      19   * Floor, Boston, MA  02110-1301  USA.
      20  */
      21  
      22  #ifdef HAVE_CONFIG_H
      23  #  include "config.h"
      24  #endif /* HAVE_CONFIG_H */
      25  
      26  #include <stdbool.h>
      27  #include <stdlib.h>
      28  #include <stdio.h>		/* SunOS's losing assert.h needs it */
      29  #include <assert.h>
      30  #include <signal.h>
      31  #include <unistd.h>
      32  #include <string.h>
      33  
      34  #include "xalloc.h"
      35  
      36  #include "manconfig.h"		/* for FATAL */
      37  #include "cleanup.h"
      38  
      39  
      40  
      41  /* Dealing with signals */
      42  
      43  
      44  /* saved signal actions */
      45  #ifdef SIGHUP
      46  static struct sigaction saved_hup_action;
      47  #endif /* SIGHUP */
      48  static struct sigaction saved_int_action;
      49  static struct sigaction saved_term_action;
      50  
      51  
      52  /* Run cleanups, then reraise signal with default handler. */
      53  static _Noreturn void
      54  sighandler (int signo)
      55  {
      56    struct sigaction act;
      57    sigset_t set;
      58  
      59    do_cleanups_sigsafe (true);
      60  
      61    /* set default signal action */
      62    memset (&act, 0, sizeof act);
      63    act.sa_handler = SIG_DFL;
      64    sigemptyset (&act.sa_mask);
      65    act.sa_flags = 0;
      66    if (sigaction (signo, &act, NULL)) {
      67      /* should not happen */
      68      _exit (FATAL);		/* exit() is taboo from signal handlers! */
      69    }
      70  
      71    /* unmask signo */
      72    if (   sigemptyset (&set)
      73        || sigaddset (&set, signo)
      74        || sigprocmask (SIG_UNBLOCK, &set, NULL)) {
      75      /* shouldn't happen */
      76      _exit (FATAL);		/* exit() is taboo from signal handlers! */
      77    }
      78  
      79    /* signal has now default action and is unmasked,
      80       reraise it to terminate program abnormally */
      81    raise (signo);
      82    abort();
      83  }
      84  
      85  
      86  /* Save signo's current action to oldact, if its handler is SIG_DFL
      87     install sighandler, return 0 on success, -1 on failure. */
      88  static int
      89  trap_signal (int signo, struct sigaction *oldact)
      90  {
      91    if (sigaction (signo, NULL, oldact)) {
      92      return -1;
      93    }
      94  
      95    if (oldact->sa_handler == SIG_DFL) {
      96      struct sigaction act;
      97  
      98      memset (&act, 0, sizeof act);
      99      act.sa_handler = sighandler;
     100      sigemptyset (&act.sa_mask);
     101      act.sa_flags = 0;
     102      return sigaction (signo, &act, oldact);
     103    }
     104  
     105    return 0;
     106  }
     107  
     108  
     109  /* Trap some abnormal exits to call do_cleanups(). */
     110  static int
     111  trap_abnormal_exits (void)
     112  {
     113  #ifdef SIGHUP
     114    if (trap_signal (SIGHUP, &saved_hup_action))
     115      return -1;
     116  #endif /* SIGHUP */
     117    if (trap_signal (SIGINT, &saved_int_action))
     118      return -1;
     119    if (trap_signal (SIGTERM, &saved_term_action))
     120      return -1;
     121    return 0;
     122  }
     123  
     124  
     125  /* Restore signo's action from oldact if its current handler is
     126     sighandler, return 0 on success, -1 on failure. */
     127  static int
     128  untrap_signal (int signo, struct sigaction *oldact)
     129  {
     130    struct sigaction act;
     131    if (sigaction (signo, NULL, &act)) {
     132      return -1;
     133    }
     134  
     135    if (act.sa_handler == sighandler) {
     136      return sigaction (signo, oldact, NULL);
     137    }
     138  
     139    return 0;
     140  }
     141  
     142  
     143  /* Undo a previous trap_abnormal_exits(). */
     144  static int
     145  untrap_abnormal_exits (void)
     146  {
     147  #ifdef SIGHUP
     148    if (untrap_signal (SIGHUP, &saved_hup_action))
     149      return -1;
     150  #endif /* SIGHUP */
     151    if (untrap_signal (SIGINT, &saved_int_action))
     152      return -1;
     153    if (untrap_signal (SIGTERM, &saved_term_action))
     154      return -1;
     155    return 0;
     156  }
     157  
     158  
     159  
     160  typedef struct {
     161    cleanup_fun fun;
     162    void *arg;
     163    int sigsafe;
     164  } slot;
     165  
     166  static slot *stack = NULL;	/* stack of cleanup functions */
     167  static unsigned nslots = 0;	/* #slots in stack */
     168  static unsigned tos = 0;	/* top of stack, 0 <= tos <= nslots */
     169  
     170  /* Call cleanup functions in stack from from top to bottom,
     171   * Automatically called on program termination via exit(3) or default
     172   * action for SIGHUP, SIGINT or SIGTERM.
     173   * Since this may be called from a signal handler, do not use free().
     174   * If in_sighandler is true, cleanup functions with sigsafe=0 will not be
     175   * called.
     176   */
     177  void
     178  do_cleanups_sigsafe (bool in_sighandler)
     179  {
     180    unsigned i;
     181  
     182    assert (tos <= nslots);
     183    for (i = tos; i > 0; --i)
     184      if (!in_sighandler || stack[i-1].sigsafe)
     185        stack[i-1].fun (stack[i-1].arg);
     186  }
     187  
     188  /* Call cleanup functions in stack from from top to bottom,
     189   * Automatically called on program termination via exit(3).
     190   */
     191  void
     192  do_cleanups (void)
     193  {
     194    do_cleanups_sigsafe (false);
     195    tos = 0;
     196    nslots = 0;
     197    free (stack);
     198    stack = NULL;
     199  }
     200  
     201  
     202  /* Push a cleanup function on the cleanup stack,
     203   * return 0 on success, -1 on failure.
     204   * Caution: the cleanup function may be called from signal handlers if
     205   * sigsafe=1. If you just want a convenient atexit() wrapper, pass
     206   * sigsafe=0.
     207   */
     208  int
     209  push_cleanup (cleanup_fun fun, void *arg, int sigsafe)
     210  {
     211    static bool handler_installed = false;
     212  
     213    assert (tos <= nslots);
     214  
     215    if (!handler_installed) {
     216      if (atexit (do_cleanups))
     217        return -1;
     218      handler_installed = true;
     219    }
     220  
     221    if (tos == nslots) {
     222      /* stack is full, allocate another slot */
     223      /* stack is not expected to grow much, otherwise we would double it */
     224      slot *new_stack;
     225  
     226      if (stack) {
     227        new_stack = xnrealloc (stack, nslots+1, sizeof (slot));
     228      } else {
     229        new_stack = xnmalloc (nslots+1, sizeof (slot));
     230      }
     231  
     232      if (!new_stack) return -1;
     233      stack = new_stack;
     234      ++nslots;
     235    }
     236  
     237    assert (tos < nslots);
     238    stack[tos].fun = fun;
     239    stack[tos].arg = arg;
     240    stack[tos].sigsafe = sigsafe;
     241    ++tos;
     242  
     243  
     244    trap_abnormal_exits();
     245  
     246    return 0;
     247  }
     248  
     249  
     250  /* Remove topmost cleanup function from the cleanup stack that matches the
     251   * given values.
     252   */
     253  void
     254  pop_cleanup (cleanup_fun fun, void *arg)
     255  {
     256    unsigned i, j;
     257  
     258    assert (tos > 0);
     259  
     260    for (i = tos; i > 0; --i) {
     261      if (stack[i-1].fun == fun && stack[i-1].arg == arg) {
     262        for (j = i; j < tos; ++j)
     263          stack[j-1] = stack[j];
     264        --tos;
     265        break;
     266      }
     267    }
     268  
     269    if (tos == 0) untrap_abnormal_exits();
     270  }
     271  
     272  
     273  /* Pop all cleanup functions from the cleanup stack. */
     274  void
     275  pop_all_cleanups (void)
     276  {
     277    tos = 0;
     278    untrap_abnormal_exits();
     279  }