mirror of
https://git.tukaani.org/xz.git
synced 2024-04-04 12:36:23 +02:00
ff592c616e
This way, if xz is stopped the elapsed time and estimated time remaining won't get confused by the amount of time spent in the stopped state. This raises SIGSTOP. It's not clear to me if this is the correct way. POSIX and glibc docs say that SIGTSTP shouldn't stop the process if it is orphaned but this commit doesn't attempt to handle that. Search for SIGTSTP in section 2.4.3: https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html
224 lines
5.2 KiB
C
224 lines
5.2 KiB
C
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
/// \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
|
|
|
|
#ifdef USE_SIGTSTP_HANDLER
|
|
// Add the SIGTSTP handler from mytime.c to hooked_signals.
|
|
sigaddset(&hooked_signals, SIGTSTP);
|
|
#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;
|
|
|
|
struct sigaction old;
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) {
|
|
// If the parent process has left some signals ignored,
|
|
// we don't unignore them.
|
|
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();
|
|
}
|
|
|
|
#ifdef USE_SIGTSTP_HANDLER
|
|
if (!(sigaction(SIGTSTP, NULL, &old) == 0
|
|
&& old.sa_handler == SIG_IGN)) {
|
|
my_sa.sa_handler = &mytime_sigtstp_handler;
|
|
if (sigaction(SIGTSTP, &my_sa, NULL))
|
|
message_signal_handler();
|
|
}
|
|
#endif
|
|
|
|
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
|