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