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