forked from neomutt/neomutt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
signal.c
241 lines (210 loc) · 6.08 KB
/
signal.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/**
* @file
* Signal handling
*
* @authors
* Copyright (C) 2017 Richard Russon <[email protected]>
*
* @copyright
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @page signal Signal handling
*
* Signal handling
*/
#include "config.h"
#include <stddef.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "message.h"
#include "signal2.h"
static sigset_t Sigset;
static sigset_t SigsetSys;
static struct sigaction SysOldInt;
static struct sigaction SysOldQuit;
static bool SignalsBlocked;
static bool SysSignalsBlocked;
static sig_handler_t sig_handler = mutt_sig_empty_handler;
static sig_handler_t exit_handler = mutt_sig_exit_handler;
/**
* mutt_sig_empty_handler - Dummy signal handler
* @param sig Signal number, e.g. SIGINT
*
* Useful for signals that we can't ignore,
* or don't want to do anything with.
*/
void mutt_sig_empty_handler(int sig)
{
}
/**
* mutt_sig_exit_handler - Notify the user and shutdown gracefully
* @param sig Signal number, e.g. SIGINT
*/
void mutt_sig_exit_handler(int sig)
{
#if HAVE_DECL_SYS_SIGLIST
printf(_("Caught signal %d (%s) ... Exiting.\n"), sig, sys_siglist[sig]);
#elif (defined(__sun__) && defined(__svr4__))
printf(_("Caught signal %d (%s) ... Exiting.\n"), sig, _sys_siglist[sig]);
#elif (defined(__alpha) && defined(__osf__))
printf(_("Caught signal %d (%s) ... Exiting.\n"), sig, __sys_siglist[sig]);
#else
printf(_("Caught signal %d ... Exiting.\n"), sig);
#endif
exit(0);
}
/**
* mutt_sig_init - Initialise the signal handling
* @param sig_fn Function to handle signals
* @param exit_fn Function to call on uncaught signals
*
* Set up handlers to ignore or catch signals of interest.
* We use three handlers for the signals we want to catch, ignore, or exit.
*/
void mutt_sig_init(sig_handler_t sig_fn, sig_handler_t exit_fn)
{
if (sig_fn)
sig_handler = sig_fn;
if (exit_fn)
exit_handler = exit_fn;
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, NULL);
act.sa_handler = exit_handler;
sigaction(SIGTERM, &act, NULL);
sigaction(SIGHUP, &act, NULL);
sigaction(SIGQUIT, &act, NULL);
/* we want to avoid race conditions */
sigaddset(&act.sa_mask, SIGTSTP);
act.sa_handler = sig_handler;
/* we want SIGALRM to abort the current syscall, so we do this before
* setting the SA_RESTART flag below. currently this is only used to
* timeout on a connect() call in a reasonable amount of time. */
sigaction(SIGALRM, &act, NULL);
/* we also don't want to mess with interrupted system calls */
#ifdef SA_RESTART
act.sa_flags = SA_RESTART;
#endif
sigaction(SIGCONT, &act, NULL);
sigaction(SIGTSTP, &act, NULL);
sigaction(SIGINT, &act, NULL);
sigaction(SIGWINCH, &act, NULL);
/* POSIX doesn't allow us to ignore SIGCHLD,
* so we just install a dummy handler for it */
act.sa_handler = mutt_sig_empty_handler;
/* don't need to block any other signals here */
sigemptyset(&act.sa_mask);
/* we don't want to mess with stopped children */
act.sa_flags |= SA_NOCLDSTOP;
sigaction(SIGCHLD, &act, NULL);
}
/**
* mutt_sig_block - Block signals during critical operations
*
* It's important that certain signals don't interfere with critical operations.
* Call mutt_sig_unblock() to restore the signals' behaviour.
*/
void mutt_sig_block(void)
{
if (SignalsBlocked)
return;
sigemptyset(&Sigset);
sigaddset(&Sigset, SIGTERM);
sigaddset(&Sigset, SIGHUP);
sigaddset(&Sigset, SIGTSTP);
sigaddset(&Sigset, SIGINT);
sigaddset(&Sigset, SIGWINCH);
sigprocmask(SIG_BLOCK, &Sigset, 0);
SignalsBlocked = true;
}
/**
* mutt_sig_unblock - Restore previously blocked signals
*/
void mutt_sig_unblock(void)
{
if (!SignalsBlocked)
return;
sigprocmask(SIG_UNBLOCK, &Sigset, 0);
SignalsBlocked = false;
}
/**
* mutt_sig_block_system - Block signals before calling exec()
*
* It's important that certain signals don't interfere with the child process.
* Call mutt_sig_unblock_system() to restore the signals' behaviour.
*/
void mutt_sig_block_system(void)
{
if (SysSignalsBlocked)
return;
struct sigaction sa;
/* POSIX: ignore SIGINT and SIGQUIT & block SIGCHLD before exec */
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, &SysOldInt);
sigaction(SIGQUIT, &sa, &SysOldQuit);
sigemptyset(&SigsetSys);
sigaddset(&SigsetSys, SIGCHLD);
sigprocmask(SIG_BLOCK, &SigsetSys, 0);
SysSignalsBlocked = true;
}
/**
* mutt_sig_unblock_system - Restore previously blocked signals
* @param catch If true, restore previous SIGINT, SIGQUIT behaviour
*/
void mutt_sig_unblock_system(bool catch)
{
if (!SysSignalsBlocked)
return;
sigprocmask(SIG_UNBLOCK, &SigsetSys, NULL);
if (catch)
{
sigaction(SIGQUIT, &SysOldQuit, NULL);
sigaction(SIGINT, &SysOldInt, NULL);
}
else
{
struct sigaction sa;
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
}
SysSignalsBlocked = false;
}
/**
* mutt_sig_allow_interrupt - Allow/disallow Ctrl-C (SIGINT)
* @param disposition True to allow Ctrl-C to interrupt signals
*
* Allow the user to interrupt some long operations.
*/
void mutt_sig_allow_interrupt(int disposition)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_handler;
#ifdef SA_RESTART
if (disposition == 0)
sa.sa_flags |= SA_RESTART;
#endif
sigaction(SIGINT, &sa, NULL);
}