1 /**
2  * @file
3  * Signal handling
4  *
5  * @authors
6  * Copyright (C) 1996-2000,2012 Michael R. Elkins <me@mutt.org>
7  *
8  * @copyright
9  * This program is free software: you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation, either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /**
24  * @page neo_mutt_signal Signal handling
25  *
26  * Signal handling
27  */
28 
29 #include "config.h"
30 #include <stddef.h>
31 #include <errno.h>
32 #include <signal.h>
33 #include <stdbool.h>
34 #include "mutt/lib.h"
35 #include "config/lib.h"
36 #include "core/lib.h"
37 #include "gui/lib.h"
38 #include "attach/lib.h"
39 #include "mutt_globals.h"
40 #include "protos.h" // IWYU pragma: keep
41 #ifdef USE_DEBUG_GRAPHVIZ
42 #include "debug/lib.h"
43 #endif
44 
45 static int IsEndwin = 0;
46 
47 /**
48  * curses_signal_handler - Catch signals and relay the info to the main program - Implements ::sig_handler_t - @ingroup sig_handler_api
49  * @param sig Signal number, e.g. SIGINT
50  */
curses_signal_handler(int sig)51 static void curses_signal_handler(int sig)
52 {
53   int save_errno = errno;
54 
55   switch (sig)
56   {
57     case SIGTSTP: /* user requested a suspend */
58     {
59       const bool c_suspend = cs_subset_bool(NeoMutt->sub, "suspend");
60       if (!c_suspend)
61         break;
62       IsEndwin = isendwin();
63       mutt_curses_set_cursor(MUTT_CURSOR_VISIBLE);
64       if (!IsEndwin)
65         endwin();
66       kill(0, SIGSTOP);
67     }
68       /* fallthrough */
69 
70     case SIGCONT:
71       if (!IsEndwin)
72         refresh();
73       mutt_curses_set_cursor(MUTT_CURSOR_RESTORE_LAST);
74       /* We don't receive SIGWINCH when suspended; however, no harm is done by
75        * just assuming we received one, and triggering the 'resize' anyway. */
76       SigWinch = true;
77       break;
78 
79     case SIGWINCH:
80       SigWinch = true;
81       break;
82 
83     case SIGINT:
84       SigInt = true;
85       break;
86   }
87   errno = save_errno;
88 }
89 
90 /**
91  * curses_exit_handler - Notify the user and shutdown gracefully - Implements ::sig_handler_t - @ingroup sig_handler_api
92  * @param sig Signal number, e.g. SIGTERM
93  */
curses_exit_handler(int sig)94 static void curses_exit_handler(int sig)
95 {
96   mutt_curses_set_cursor(MUTT_CURSOR_VISIBLE);
97   endwin(); /* just to be safe */
98   mutt_unlink_temp_attachments();
99   mutt_sig_exit_handler(sig); /* DOES NOT RETURN */
100 }
101 
102 /**
103  * curses_segv_handler - Catch a segfault and print a backtrace - Implements ::sig_handler_t - @ingroup sig_handler_api
104  * @param sig Signal number, e.g. SIGSEGV
105  */
curses_segv_handler(int sig)106 static void curses_segv_handler(int sig)
107 {
108   mutt_curses_set_cursor(MUTT_CURSOR_VISIBLE);
109   endwin(); /* just to be safe */
110 #ifdef HAVE_LIBUNWIND
111   show_backtrace();
112 #endif
113 #ifdef USE_DEBUG_GRAPHVIZ
114   dump_graphviz("segfault", NULL);
115 #endif
116 
117   struct sigaction act;
118   sigemptyset(&act.sa_mask);
119   act.sa_flags = 0;
120   act.sa_handler = SIG_DFL;
121   sigaction(sig, &act, NULL);
122   // Re-raise the signal to give outside handlers a chance to deal with it
123   raise(sig);
124 }
125 
126 /**
127  * mutt_signal_init - Initialise the signal handling
128  */
mutt_signal_init(void)129 void mutt_signal_init(void)
130 {
131   mutt_sig_init(curses_signal_handler, curses_exit_handler, curses_segv_handler);
132 }
133