mirror of
https://git.tukaani.org/xz.git
synced 2024-04-04 12:36:23 +02:00
181 lines
4 KiB
C
181 lines
4 KiB
C
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
/// \file signals.c
|
||
|
/// \brief Handling signals to abort operation
|
||
|
//
|
||
|
// Copyright (C) 2007-2009 Lasse Collin
|
||
|
//
|
||
|
// This program is free software; you can redistribute it and/or
|
||
|
// modify it under the terms of the GNU Lesser General Public
|
||
|
// License as published by the Free Software Foundation; either
|
||
|
// version 2.1 of the License, 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
|
||
|
// Lesser General Public License for more details.
|
||
|
//
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "private.h"
|
||
|
|
||
|
|
||
|
volatile sig_atomic_t user_abort = false;
|
||
|
|
||
|
|
||
|
#ifndef _WIN32
|
||
|
|
||
|
/// 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 have have established a signal handler to set
|
||
|
/// user_abort to true.
|
||
|
static sigset_t hooked_signals;
|
||
|
|
||
|
/// 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;
|
||
|
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]);
|
||
|
|
||
|
struct sigaction sa;
|
||
|
|
||
|
// All the signals that we handle we also blocked while the signal
|
||
|
// handler runs.
|
||
|
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.
|
||
|
sa.sa_flags = 0;
|
||
|
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], &sa, NULL))
|
||
|
message_signal_handler();
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
extern void
|
||
|
signals_block(void)
|
||
|
{
|
||
|
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)
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
extern void
|
||
|
signals_exit(void)
|
||
|
{
|
||
|
const int sig = exit_signal;
|
||
|
|
||
|
if (sig != 0) {
|
||
|
struct sigaction sa;
|
||
|
sa.sa_handler = SIG_DFL;
|
||
|
sigfillset(&sa.sa_mask);
|
||
|
sa.sa_flags = 0;
|
||
|
sigaction(sig, &sa, NULL);
|
||
|
raise(exit_signal);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
// While Windows has some very basic signal handling functions as required
|
||
|
// by C89, they are not really used, or so I understood. Instead, we use
|
||
|
// SetConsoleCtrlHandler() to catch user pressing C-c.
|
||
|
|
||
|
#include <windows.h>
|
||
|
|
||
|
|
||
|
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
|