(root)/
xz-5.4.5/
src/
xz/
signals.c
       1  ///////////////////////////////////////////////////////////////////////////////
       2  //
       3  /// \file       signals.c
       4  /// \brief      Handling signals to abort operation
       5  //
       6  //  Author:     Lasse Collin
       7  //
       8  //  This file has been put into the public domain.
       9  //  You can do whatever you want with this file.
      10  //
      11  ///////////////////////////////////////////////////////////////////////////////
      12  
      13  #include "private.h"
      14  
      15  
      16  volatile sig_atomic_t user_abort = false;
      17  
      18  
      19  #if !(defined(_WIN32) && !defined(__CYGWIN__))
      20  
      21  /// If we were interrupted by a signal, we store the signal number so that
      22  /// we can raise that signal to kill the program when all cleanups have
      23  /// been done.
      24  static volatile sig_atomic_t exit_signal = 0;
      25  
      26  /// Mask of signals for which we have established a signal handler to set
      27  /// user_abort to true.
      28  static sigset_t hooked_signals;
      29  
      30  /// True once signals_init() has finished. This is used to skip blocking
      31  /// signals (with uninitialized hooked_signals) if signals_block() and
      32  /// signals_unblock() are called before signals_init() has been called.
      33  static bool signals_are_initialized = false;
      34  
      35  /// signals_block() and signals_unblock() can be called recursively.
      36  static size_t signals_block_count = 0;
      37  
      38  
      39  static void
      40  signal_handler(int sig)
      41  {
      42  	exit_signal = sig;
      43  	user_abort = true;
      44  
      45  #ifndef TUKLIB_DOSLIKE
      46  	io_write_to_user_abort_pipe();
      47  #endif
      48  
      49  	return;
      50  }
      51  
      52  
      53  extern void
      54  signals_init(void)
      55  {
      56  	// List of signals for which we establish the signal handler.
      57  	static const int sigs[] = {
      58  		SIGINT,
      59  		SIGTERM,
      60  #ifdef SIGHUP
      61  		SIGHUP,
      62  #endif
      63  #ifdef SIGPIPE
      64  		SIGPIPE,
      65  #endif
      66  #ifdef SIGXCPU
      67  		SIGXCPU,
      68  #endif
      69  #ifdef SIGXFSZ
      70  		SIGXFSZ,
      71  #endif
      72  	};
      73  
      74  	// Mask of the signals for which we have established a signal handler.
      75  	sigemptyset(&hooked_signals);
      76  	for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i)
      77  		sigaddset(&hooked_signals, sigs[i]);
      78  
      79  #ifdef SIGALRM
      80  	// Add also the signals from message.c to hooked_signals.
      81  	for (size_t i = 0; message_progress_sigs[i] != 0; ++i)
      82  		sigaddset(&hooked_signals, message_progress_sigs[i]);
      83  #endif
      84  
      85  	// Using "my_sa" because "sa" may conflict with a sockaddr variable
      86  	// from system headers on Solaris.
      87  	struct sigaction my_sa;
      88  
      89  	// All the signals that we handle we also blocked while the signal
      90  	// handler runs.
      91  	my_sa.sa_mask = hooked_signals;
      92  
      93  	// Don't set SA_RESTART, because we want EINTR so that we can check
      94  	// for user_abort and cleanup before exiting. We block the signals
      95  	// for which we have established a handler when we don't want EINTR.
      96  	my_sa.sa_flags = 0;
      97  	my_sa.sa_handler = &signal_handler;
      98  
      99  	for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) {
     100  		// If the parent process has left some signals ignored,
     101  		// we don't unignore them.
     102  		struct sigaction old;
     103  		if (sigaction(sigs[i], NULL, &old) == 0
     104  				&& old.sa_handler == SIG_IGN)
     105  			continue;
     106  
     107  		// Establish the signal handler.
     108  		if (sigaction(sigs[i], &my_sa, NULL))
     109  			message_signal_handler();
     110  	}
     111  
     112  	signals_are_initialized = true;
     113  
     114  	return;
     115  }
     116  
     117  
     118  #ifndef __VMS
     119  extern void
     120  signals_block(void)
     121  {
     122  	if (signals_are_initialized) {
     123  		if (signals_block_count++ == 0) {
     124  			const int saved_errno = errno;
     125  			mythread_sigmask(SIG_BLOCK, &hooked_signals, NULL);
     126  			errno = saved_errno;
     127  		}
     128  	}
     129  
     130  	return;
     131  }
     132  
     133  
     134  extern void
     135  signals_unblock(void)
     136  {
     137  	if (signals_are_initialized) {
     138  		assert(signals_block_count > 0);
     139  
     140  		if (--signals_block_count == 0) {
     141  			const int saved_errno = errno;
     142  			mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL);
     143  			errno = saved_errno;
     144  		}
     145  	}
     146  
     147  	return;
     148  }
     149  #endif
     150  
     151  
     152  extern void
     153  signals_exit(void)
     154  {
     155  	const int sig = (int)exit_signal;
     156  
     157  	if (sig != 0) {
     158  #if defined(TUKLIB_DOSLIKE) || defined(__VMS)
     159  		// Don't raise(), set only exit status. This avoids
     160  		// printing unwanted message about SIGINT when the user
     161  		// presses C-c.
     162  		set_exit_status(E_ERROR);
     163  #else
     164  		struct sigaction sa;
     165  		sa.sa_handler = SIG_DFL;
     166  		sigfillset(&sa.sa_mask);
     167  		sa.sa_flags = 0;
     168  		sigaction(sig, &sa, NULL);
     169  		raise(sig);
     170  #endif
     171  	}
     172  
     173  	return;
     174  }
     175  
     176  #else
     177  
     178  // While Windows has some very basic signal handling functions as required
     179  // by C89, they are not really used, and e.g. SIGINT doesn't work exactly
     180  // the way it does on POSIX (Windows creates a new thread for the signal
     181  // handler). Instead, we use SetConsoleCtrlHandler() to catch user
     182  // pressing C-c, because that seems to be the recommended way to do it.
     183  //
     184  // NOTE: This doesn't work under MSYS. Trying with SIGINT doesn't work
     185  // either even if it appeared to work at first. So test using Windows
     186  // console window.
     187  
     188  static BOOL WINAPI
     189  signal_handler(DWORD type lzma_attribute((__unused__)))
     190  {
     191  	// Since we don't get a signal number which we could raise() at
     192  	// signals_exit() like on POSIX, just set the exit status to
     193  	// indicate an error, so that we cannot return with zero exit status.
     194  	set_exit_status(E_ERROR);
     195  	user_abort = true;
     196  	return TRUE;
     197  }
     198  
     199  
     200  extern void
     201  signals_init(void)
     202  {
     203  	if (!SetConsoleCtrlHandler(&signal_handler, TRUE))
     204  		message_signal_handler();
     205  
     206  	return;
     207  }
     208  
     209  #endif