diff options
Diffstat (limited to '')
-rw-r--r-- | src/xz/signals.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/src/xz/signals.c b/src/xz/signals.c new file mode 100644 index 0000000..7aef463 --- /dev/null +++ b/src/xz/signals.c @@ -0,0 +1,209 @@ +/////////////////////////////////////////////////////////////////////////////// +// +/// \file signals.c +/// \brief Handling signals to abort operation +// +// Author: Lasse Collin +// +// This file has been put into the public domain. +// You can do whatever you want with this file. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "private.h" + + +volatile sig_atomic_t user_abort = false; + + +#if !(defined(_WIN32) && !defined(__CYGWIN__)) + +/// If we were interrupted by a signal, we store the signal number so that +/// we can raise that signal to kill the program when all cleanups have +/// been done. +static volatile sig_atomic_t exit_signal = 0; + +/// Mask of signals for which we have established a signal handler to set +/// user_abort to true. +static sigset_t hooked_signals; + +/// True once signals_init() has finished. This is used to skip blocking +/// signals (with uninitialized hooked_signals) if signals_block() and +/// signals_unblock() are called before signals_init() has been called. +static bool signals_are_initialized = false; + +/// signals_block() and signals_unblock() can be called recursively. +static size_t signals_block_count = 0; + + +static void +signal_handler(int sig) +{ + exit_signal = sig; + user_abort = true; + +#ifndef TUKLIB_DOSLIKE + io_write_to_user_abort_pipe(); +#endif + + return; +} + + +extern void +signals_init(void) +{ + // List of signals for which we establish the signal handler. + static const int sigs[] = { + SIGINT, + SIGTERM, +#ifdef SIGHUP + SIGHUP, +#endif +#ifdef SIGPIPE + SIGPIPE, +#endif +#ifdef SIGXCPU + SIGXCPU, +#endif +#ifdef SIGXFSZ + SIGXFSZ, +#endif + }; + + // Mask of the signals for which we have established a signal handler. + sigemptyset(&hooked_signals); + for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) + sigaddset(&hooked_signals, sigs[i]); + +#ifdef SIGALRM + // Add also the signals from message.c to hooked_signals. + for (size_t i = 0; message_progress_sigs[i] != 0; ++i) + sigaddset(&hooked_signals, message_progress_sigs[i]); +#endif + + // Using "my_sa" because "sa" may conflict with a sockaddr variable + // from system headers on Solaris. + struct sigaction my_sa; + + // All the signals that we handle we also blocked while the signal + // handler runs. + my_sa.sa_mask = hooked_signals; + + // Don't set SA_RESTART, because we want EINTR so that we can check + // for user_abort and cleanup before exiting. We block the signals + // for which we have established a handler when we don't want EINTR. + my_sa.sa_flags = 0; + my_sa.sa_handler = &signal_handler; + + for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) { + // If the parent process has left some signals ignored, + // we don't unignore them. + struct sigaction old; + if (sigaction(sigs[i], NULL, &old) == 0 + && old.sa_handler == SIG_IGN) + continue; + + // Establish the signal handler. + if (sigaction(sigs[i], &my_sa, NULL)) + message_signal_handler(); + } + + signals_are_initialized = true; + + return; +} + + +#ifndef __VMS +extern void +signals_block(void) +{ + if (signals_are_initialized) { + if (signals_block_count++ == 0) { + const int saved_errno = errno; + mythread_sigmask(SIG_BLOCK, &hooked_signals, NULL); + errno = saved_errno; + } + } + + return; +} + + +extern void +signals_unblock(void) +{ + if (signals_are_initialized) { + assert(signals_block_count > 0); + + if (--signals_block_count == 0) { + const int saved_errno = errno; + mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL); + errno = saved_errno; + } + } + + return; +} +#endif + + +extern void +signals_exit(void) +{ + const int sig = (int)exit_signal; + + if (sig != 0) { +#if defined(TUKLIB_DOSLIKE) || defined(__VMS) + // Don't raise(), set only exit status. This avoids + // printing unwanted message about SIGINT when the user + // presses C-c. + set_exit_status(E_ERROR); +#else + struct sigaction sa; + sa.sa_handler = SIG_DFL; + sigfillset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(sig, &sa, NULL); + raise(sig); +#endif + } + + return; +} + +#else + +// While Windows has some very basic signal handling functions as required +// by C89, they are not really used, and e.g. SIGINT doesn't work exactly +// the way it does on POSIX (Windows creates a new thread for the signal +// handler). Instead, we use SetConsoleCtrlHandler() to catch user +// pressing C-c, because that seems to be the recommended way to do it. +// +// NOTE: This doesn't work under MSYS. Trying with SIGINT doesn't work +// either even if it appeared to work at first. So test using Windows +// console window. + +static BOOL WINAPI +signal_handler(DWORD type lzma_attribute((__unused__))) +{ + // Since we don't get a signal number which we could raise() at + // signals_exit() like on POSIX, just set the exit status to + // indicate an error, so that we cannot return with zero exit status. + set_exit_status(E_ERROR); + user_abort = true; + return TRUE; +} + + +extern void +signals_init(void) +{ + if (!SetConsoleCtrlHandler(&signal_handler, TRUE)) + message_signal_handler(); + + return; +} + +#endif |