14d1abfb2Sjoerg ///////////////////////////////////////////////////////////////////////////////
24d1abfb2Sjoerg //
34d1abfb2Sjoerg /// \file       signals.c
44d1abfb2Sjoerg /// \brief      Handling signals to abort operation
54d1abfb2Sjoerg //
64d1abfb2Sjoerg //  Author:     Lasse Collin
74d1abfb2Sjoerg //
84d1abfb2Sjoerg //  This file has been put into the public domain.
94d1abfb2Sjoerg //  You can do whatever you want with this file.
104d1abfb2Sjoerg //
114d1abfb2Sjoerg ///////////////////////////////////////////////////////////////////////////////
124d1abfb2Sjoerg 
134d1abfb2Sjoerg #include "private.h"
144d1abfb2Sjoerg 
154d1abfb2Sjoerg 
164d1abfb2Sjoerg volatile sig_atomic_t user_abort = false;
174d1abfb2Sjoerg 
184d1abfb2Sjoerg 
194d1abfb2Sjoerg #if !(defined(_WIN32) && !defined(__CYGWIN__))
204d1abfb2Sjoerg 
214d1abfb2Sjoerg /// If we were interrupted by a signal, we store the signal number so that
224d1abfb2Sjoerg /// we can raise that signal to kill the program when all cleanups have
234d1abfb2Sjoerg /// been done.
244d1abfb2Sjoerg static volatile sig_atomic_t exit_signal = 0;
254d1abfb2Sjoerg 
264d1abfb2Sjoerg /// Mask of signals for which have have established a signal handler to set
274d1abfb2Sjoerg /// user_abort to true.
284d1abfb2Sjoerg static sigset_t hooked_signals;
294d1abfb2Sjoerg 
304d1abfb2Sjoerg /// True once signals_init() has finished. This is used to skip blocking
314d1abfb2Sjoerg /// signals (with uninitialized hooked_signals) if signals_block() and
324d1abfb2Sjoerg /// signals_unblock() are called before signals_init() has been called.
334d1abfb2Sjoerg static bool signals_are_initialized = false;
344d1abfb2Sjoerg 
354d1abfb2Sjoerg /// signals_block() and signals_unblock() can be called recursively.
364d1abfb2Sjoerg static size_t signals_block_count = 0;
374d1abfb2Sjoerg 
384d1abfb2Sjoerg 
394d1abfb2Sjoerg static void
signal_handler(int sig)404d1abfb2Sjoerg signal_handler(int sig)
414d1abfb2Sjoerg {
424d1abfb2Sjoerg 	exit_signal = sig;
434d1abfb2Sjoerg 	user_abort = true;
44*7653b22fSchristos 
45*7653b22fSchristos #ifndef TUKLIB_DOSLIKE
46*7653b22fSchristos 	io_write_to_user_abort_pipe();
47*7653b22fSchristos #endif
48*7653b22fSchristos 
494d1abfb2Sjoerg 	return;
504d1abfb2Sjoerg }
514d1abfb2Sjoerg 
524d1abfb2Sjoerg 
534d1abfb2Sjoerg extern void
signals_init(void)544d1abfb2Sjoerg signals_init(void)
554d1abfb2Sjoerg {
564d1abfb2Sjoerg 	// List of signals for which we establish the signal handler.
574d1abfb2Sjoerg 	static const int sigs[] = {
584d1abfb2Sjoerg 		SIGINT,
594d1abfb2Sjoerg 		SIGTERM,
604d1abfb2Sjoerg #ifdef SIGHUP
614d1abfb2Sjoerg 		SIGHUP,
624d1abfb2Sjoerg #endif
634d1abfb2Sjoerg #ifdef SIGPIPE
644d1abfb2Sjoerg 		SIGPIPE,
654d1abfb2Sjoerg #endif
664d1abfb2Sjoerg #ifdef SIGXCPU
674d1abfb2Sjoerg 		SIGXCPU,
684d1abfb2Sjoerg #endif
694d1abfb2Sjoerg #ifdef SIGXFSZ
704d1abfb2Sjoerg 		SIGXFSZ,
714d1abfb2Sjoerg #endif
724d1abfb2Sjoerg 	};
734d1abfb2Sjoerg 
744d1abfb2Sjoerg 	// Mask of the signals for which we have established a signal handler.
754d1abfb2Sjoerg 	sigemptyset(&hooked_signals);
764d1abfb2Sjoerg 	for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i)
774d1abfb2Sjoerg 		sigaddset(&hooked_signals, sigs[i]);
784d1abfb2Sjoerg 
794d1abfb2Sjoerg #ifdef SIGALRM
804d1abfb2Sjoerg 	// Add also the signals from message.c to hooked_signals.
814d1abfb2Sjoerg 	for (size_t i = 0; message_progress_sigs[i] != 0; ++i)
824d1abfb2Sjoerg 		sigaddset(&hooked_signals, message_progress_sigs[i]);
834d1abfb2Sjoerg #endif
844d1abfb2Sjoerg 
85*7653b22fSchristos 	// Using "my_sa" because "sa" may conflict with a sockaddr variable
86*7653b22fSchristos 	// from system headers on Solaris.
87*7653b22fSchristos 	struct sigaction my_sa;
884d1abfb2Sjoerg 
894d1abfb2Sjoerg 	// All the signals that we handle we also blocked while the signal
904d1abfb2Sjoerg 	// handler runs.
91*7653b22fSchristos 	my_sa.sa_mask = hooked_signals;
924d1abfb2Sjoerg 
934d1abfb2Sjoerg 	// Don't set SA_RESTART, because we want EINTR so that we can check
944d1abfb2Sjoerg 	// for user_abort and cleanup before exiting. We block the signals
954d1abfb2Sjoerg 	// for which we have established a handler when we don't want EINTR.
96*7653b22fSchristos 	my_sa.sa_flags = 0;
97*7653b22fSchristos 	my_sa.sa_handler = &signal_handler;
984d1abfb2Sjoerg 
994d1abfb2Sjoerg 	for (size_t i = 0; i < ARRAY_SIZE(sigs); ++i) {
1004d1abfb2Sjoerg 		// If the parent process has left some signals ignored,
1014d1abfb2Sjoerg 		// we don't unignore them.
1024d1abfb2Sjoerg 		struct sigaction old;
1034d1abfb2Sjoerg 		if (sigaction(sigs[i], NULL, &old) == 0
1044d1abfb2Sjoerg 				&& old.sa_handler == SIG_IGN)
1054d1abfb2Sjoerg 			continue;
1064d1abfb2Sjoerg 
1074d1abfb2Sjoerg 		// Establish the signal handler.
108*7653b22fSchristos 		if (sigaction(sigs[i], &my_sa, NULL))
1094d1abfb2Sjoerg 			message_signal_handler();
1104d1abfb2Sjoerg 	}
1114d1abfb2Sjoerg 
1124d1abfb2Sjoerg 	signals_are_initialized = true;
1134d1abfb2Sjoerg 
1144d1abfb2Sjoerg 	return;
1154d1abfb2Sjoerg }
1164d1abfb2Sjoerg 
1174d1abfb2Sjoerg 
1184d1abfb2Sjoerg #ifndef __VMS
1194d1abfb2Sjoerg extern void
signals_block(void)1204d1abfb2Sjoerg signals_block(void)
1214d1abfb2Sjoerg {
1224d1abfb2Sjoerg 	if (signals_are_initialized) {
1234d1abfb2Sjoerg 		if (signals_block_count++ == 0) {
1244d1abfb2Sjoerg 			const int saved_errno = errno;
1254d1abfb2Sjoerg 			mythread_sigmask(SIG_BLOCK, &hooked_signals, NULL);
1264d1abfb2Sjoerg 			errno = saved_errno;
1274d1abfb2Sjoerg 		}
1284d1abfb2Sjoerg 	}
1294d1abfb2Sjoerg 
1304d1abfb2Sjoerg 	return;
1314d1abfb2Sjoerg }
1324d1abfb2Sjoerg 
1334d1abfb2Sjoerg 
1344d1abfb2Sjoerg extern void
signals_unblock(void)1354d1abfb2Sjoerg signals_unblock(void)
1364d1abfb2Sjoerg {
1374d1abfb2Sjoerg 	if (signals_are_initialized) {
1384d1abfb2Sjoerg 		assert(signals_block_count > 0);
1394d1abfb2Sjoerg 
1404d1abfb2Sjoerg 		if (--signals_block_count == 0) {
1414d1abfb2Sjoerg 			const int saved_errno = errno;
1424d1abfb2Sjoerg 			mythread_sigmask(SIG_UNBLOCK, &hooked_signals, NULL);
1434d1abfb2Sjoerg 			errno = saved_errno;
1444d1abfb2Sjoerg 		}
1454d1abfb2Sjoerg 	}
1464d1abfb2Sjoerg 
1474d1abfb2Sjoerg 	return;
1484d1abfb2Sjoerg }
1494d1abfb2Sjoerg #endif
1504d1abfb2Sjoerg 
1514d1abfb2Sjoerg 
1524d1abfb2Sjoerg extern void
signals_exit(void)1534d1abfb2Sjoerg signals_exit(void)
1544d1abfb2Sjoerg {
1554d1abfb2Sjoerg 	const int sig = exit_signal;
1564d1abfb2Sjoerg 
1574d1abfb2Sjoerg 	if (sig != 0) {
1584d1abfb2Sjoerg #if defined(TUKLIB_DOSLIKE) || defined(__VMS)
1594d1abfb2Sjoerg 		// Don't raise(), set only exit status. This avoids
1604d1abfb2Sjoerg 		// printing unwanted message about SIGINT when the user
1614d1abfb2Sjoerg 		// presses C-c.
1624d1abfb2Sjoerg 		set_exit_status(E_ERROR);
1634d1abfb2Sjoerg #else
1644d1abfb2Sjoerg 		struct sigaction sa;
1654d1abfb2Sjoerg 		sa.sa_handler = SIG_DFL;
1664d1abfb2Sjoerg 		sigfillset(&sa.sa_mask);
1674d1abfb2Sjoerg 		sa.sa_flags = 0;
1684d1abfb2Sjoerg 		sigaction(sig, &sa, NULL);
1694d1abfb2Sjoerg 		raise(exit_signal);
1704d1abfb2Sjoerg #endif
1714d1abfb2Sjoerg 	}
1724d1abfb2Sjoerg 
1734d1abfb2Sjoerg 	return;
1744d1abfb2Sjoerg }
1754d1abfb2Sjoerg 
1764d1abfb2Sjoerg #else
1774d1abfb2Sjoerg 
1784d1abfb2Sjoerg // While Windows has some very basic signal handling functions as required
1794d1abfb2Sjoerg // by C89, they are not really used, and e.g. SIGINT doesn't work exactly
1804d1abfb2Sjoerg // the way it does on POSIX (Windows creates a new thread for the signal
1814d1abfb2Sjoerg // handler). Instead, we use SetConsoleCtrlHandler() to catch user
1824d1abfb2Sjoerg // pressing C-c, because that seems to be the recommended way to do it.
1834d1abfb2Sjoerg //
1844d1abfb2Sjoerg // NOTE: This doesn't work under MSYS. Trying with SIGINT doesn't work
1854d1abfb2Sjoerg // either even if it appeared to work at first. So test using Windows
1864d1abfb2Sjoerg // console window.
1874d1abfb2Sjoerg 
1884d1abfb2Sjoerg static BOOL WINAPI
signal_handler(DWORD type lzma_attribute ((__unused__)))1894740c398Sjoerg signal_handler(DWORD type lzma_attribute((__unused__)))
1904d1abfb2Sjoerg {
1914d1abfb2Sjoerg 	// Since we don't get a signal number which we could raise() at
1924d1abfb2Sjoerg 	// signals_exit() like on POSIX, just set the exit status to
1934d1abfb2Sjoerg 	// indicate an error, so that we cannot return with zero exit status.
1944d1abfb2Sjoerg 	set_exit_status(E_ERROR);
1954d1abfb2Sjoerg 	user_abort = true;
1964d1abfb2Sjoerg 	return TRUE;
1974d1abfb2Sjoerg }
1984d1abfb2Sjoerg 
1994d1abfb2Sjoerg 
2004d1abfb2Sjoerg extern void
signals_init(void)2014d1abfb2Sjoerg signals_init(void)
2024d1abfb2Sjoerg {
2034d1abfb2Sjoerg 	if (!SetConsoleCtrlHandler(&signal_handler, TRUE))
2044d1abfb2Sjoerg 		message_signal_handler();
2054d1abfb2Sjoerg 
2064d1abfb2Sjoerg 	return;
2074d1abfb2Sjoerg }
2084d1abfb2Sjoerg 
2094d1abfb2Sjoerg #endif
210