mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000,2012 Michael R. Elkins <me@mutt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#if HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include "mutt.h"
24#include "mutt_curses.h"
25
26#include <signal.h>
27#include <string.h>
28#include <sys/wait.h>
29#include <errno.h>
30#include <unistd.h>
31
32static sigset_t Sigset;
33static sigset_t SigsetSys;
34static struct sigaction SysOldInt;
35static struct sigaction SysOldQuit;
36static int IsEndwin = 0;
37
38static void exit_print_int_recursive (int n)
39{
40 char digit;
41
42 if (n > 9)
43 exit_print_int_recursive (n / 10);
44
45 digit = '0' + (n % 10);
46 write (1, &digit, 1);
47}
48
49static void exit_print_int (int n)
50{
51 if (n < 0)
52 {
53 write (1, "-", 1);
54 n = -n;
55 }
56 exit_print_int_recursive (n);
57}
58
59static void exit_print_string (const char *str)
60{
61 size_t len = 0;
62
63 if (!str)
64 return;
65
66 while (str[len])
67 len++;
68
69 if (len > 0)
70 write (1, str, len);
71}
72
73/* Attempt to catch "ordinary" signals and shut down gracefully. */
74static void exit_handler (int sig)
75{
76 curs_set (1);
77 endwin (); /* just to be safe */
78
79 exit_print_string ("Caught signal ");
80#if SYS_SIGLIST_DECLARED
81 exit_print_string (sys_siglist[sig]);
82#else
83#if (__sun__ && __svr4__)
84 exit_print_string (_sys_siglist[sig]);
85#else
86#if (__alpha && __osf__)
87 exit_print_string (__sys_siglist[sig]);
88#else
89 exit_print_int (sig);
90#endif
91#endif
92#endif
93 exit_print_string ("... Exiting.\n");
94 exit (0);
95}
96
97static void chld_handler (int sig)
98{
99 /* empty */
100}
101
102static void sighandler (int sig)
103{
104 int save_errno = errno;
105
106 switch (sig)
107 {
108 case SIGTSTP: /* user requested a suspend */
109 if (!option (OPTSUSPEND))
110 break;
111 IsEndwin = isendwin ();
112 curs_set (1);
113 if (!IsEndwin)
114 endwin ();
115 kill (0, SIGSTOP);
116 /* fall through */
117
118 case SIGCONT:
119 if (!IsEndwin)
120 refresh ();
121 mutt_curs_set (-1);
122#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
123 /* We don't receive SIGWINCH when suspended; however, no harm is done by
124 * just assuming we received one, and triggering the 'resize' anyway. */
125 SigWinch = 1;
126#endif
127 break;
128
129#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
130 case SIGWINCH:
131 SigWinch = 1;
132 break;
133#endif
134
135 case SIGINT:
136 SigInt = 1;
137 break;
138
139 }
140 errno = save_errno;
141}
142
143#ifdef USE_SLANG_CURSES
144int mutt_intr_hook (void)
145{
146 return (-1);
147}
148#endif /* USE_SLANG_CURSES */
149
150void mutt_signal_init (void)
151{
152 struct sigaction act;
153
154 sigemptyset (&act.sa_mask);
155 act.sa_flags = 0;
156 act.sa_handler = SIG_IGN;
157 sigaction (SIGPIPE, &act, NULL);
158
159 act.sa_handler = exit_handler;
160 sigaction (SIGTERM, &act, NULL);
161 sigaction (SIGHUP, &act, NULL);
162 sigaction (SIGQUIT, &act, NULL);
163
164 /* we want to avoid race conditions */
165 sigaddset (&act.sa_mask, SIGTSTP);
166
167 act.sa_handler = sighandler;
168
169 /* we want SIGALRM to abort the current syscall, so we do this before
170 * setting the SA_RESTART flag below. currently this is only used to
171 * timeout on a connect() call in a reasonable amount of time.
172 */
173 sigaction (SIGALRM, &act, NULL);
174
175 /* we also don't want to mess with interrupted system calls */
176#ifdef SA_RESTART
177 act.sa_flags = SA_RESTART;
178#endif
179
180 sigaction (SIGCONT, &act, NULL);
181 sigaction (SIGTSTP, &act, NULL);
182 sigaction (SIGINT, &act, NULL);
183#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
184 sigaction (SIGWINCH, &act, NULL);
185#endif
186
187 /* POSIX doesn't allow us to ignore SIGCHLD,
188 * so we just install a dummy handler for it
189 */
190 act.sa_handler = chld_handler;
191 /* don't need to block any other signals here */
192 sigemptyset (&act.sa_mask);
193 /* we don't want to mess with stopped children */
194 act.sa_flags |= SA_NOCLDSTOP;
195 sigaction (SIGCHLD, &act, NULL);
196
197#ifdef USE_SLANG_CURSES
198 /* This bit of code is required because of the implementation of
199 * SLcurses_wgetch(). If a signal is received (like SIGWINCH) when we
200 * are in blocking mode, SLsys_getkey() will not return an error unless
201 * a handler function is defined and it returns -1. This is needed so
202 * that if the user resizes the screen while at a prompt, it will just
203 * abort and go back to the main-menu.
204 */
205 SLang_getkey_intr_hook = mutt_intr_hook;
206#endif
207}
208
209/* signals which are important to block while doing critical ops */
210void mutt_block_signals (void)
211{
212 if (!option (OPTSIGNALSBLOCKED))
213 {
214 sigemptyset (&Sigset);
215 sigaddset (&Sigset, SIGTERM);
216 sigaddset (&Sigset, SIGHUP);
217 sigaddset (&Sigset, SIGTSTP);
218 sigaddset (&Sigset, SIGINT);
219#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
220 sigaddset (&Sigset, SIGWINCH);
221#endif
222 sigprocmask (SIG_BLOCK, &Sigset, 0);
223 set_option (OPTSIGNALSBLOCKED);
224 }
225}
226
227/* restore the previous signal mask */
228void mutt_unblock_signals (void)
229{
230 if (option (OPTSIGNALSBLOCKED))
231 {
232 sigprocmask (SIG_UNBLOCK, &Sigset, 0);
233 unset_option (OPTSIGNALSBLOCKED);
234 }
235}
236
237void mutt_block_signals_system (void)
238{
239 struct sigaction sa;
240
241 if (! option (OPTSYSSIGNALSBLOCKED))
242 {
243 /* POSIX: ignore SIGINT and SIGQUIT & block SIGCHLD before exec */
244 sa.sa_handler = SIG_IGN;
245 sa.sa_flags = 0;
246 sigemptyset (&sa.sa_mask);
247 sigaction (SIGINT, &sa, &SysOldInt);
248 sigaction (SIGQUIT, &sa, &SysOldQuit);
249
250 sigemptyset (&SigsetSys);
251 sigaddset (&SigsetSys, SIGCHLD);
252 sigprocmask (SIG_BLOCK, &SigsetSys, 0);
253 set_option (OPTSYSSIGNALSBLOCKED);
254 }
255}
256
257void mutt_unblock_signals_system (int catch)
258{
259 if (option (OPTSYSSIGNALSBLOCKED))
260 {
261 sigprocmask (SIG_UNBLOCK, &SigsetSys, NULL);
262 if (catch)
263 {
264 sigaction (SIGQUIT, &SysOldQuit, NULL);
265 sigaction (SIGINT, &SysOldInt, NULL);
266 }
267 else
268 {
269 struct sigaction sa;
270
271 sa.sa_handler = SIG_DFL;
272 sigemptyset (&sa.sa_mask);
273 sa.sa_flags = 0;
274 sigaction (SIGQUIT, &sa, NULL);
275 sigaction (SIGINT, &sa, NULL);
276 }
277
278 unset_option (OPTSYSSIGNALSBLOCKED);
279 }
280}
281
282void mutt_allow_interrupt (int disposition)
283{
284 struct sigaction sa;
285
286 memset (&sa, 0, sizeof sa);
287 sa.sa_handler = sighandler;
288#ifdef SA_RESTART
289 if (disposition == 0)
290 sa.sa_flags |= SA_RESTART;
291#endif
292 sigaction (SIGINT, &sa, NULL);
293}