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