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