1 /* signals.c -- install and maintain signal handlers.
2
3 Copyright 1993-2023 Free Software Foundation, Inc.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 Originally written by Brian Fox. */
19
20 #include "info.h"
21 #include "display.h"
22 #include "footnotes.h"
23 #include "window.h"
24 #include "signals.h"
25
26 void initialize_info_signal_handler (void);
27
28 /* **************************************************************** */
29 /* */
30 /* Signal Handling for Info */
31 /* */
32 /* **************************************************************** */
33
34 #if defined (HAVE_SIGACTION) || defined (HAVE_SIGPROCMASK)
35 static void
36 mask_termsig (sigset_t *set)
37 {
38 # if defined (SIGTSTP)
39 sigaddset (set, SIGTSTP);
40 sigaddset (set, SIGTTOU);
41 sigaddset (set, SIGTTIN);
42 # endif
43 # if defined (SIGWINCH)
44 sigaddset (set, SIGWINCH);
45 # endif
46 #if defined (SIGQUIT)
47 sigaddset (set, SIGQUIT);
48 #endif
49 #if defined (SIGINT)
50 sigaddset (set, SIGINT);
51 #endif
52 #if defined (SIGTERM)
53 sigaddset (set, SIGTERM);
54 #endif
55 # if defined (SIGUSR1)
56 sigaddset (set, SIGUSR1);
57 # endif
58 }
59 #endif /* HAVE_SIGACTION || HAVE_SIGPROCMASK */
60
61 static void info_signal_proc (int sig);
62 #if defined (HAVE_SIGACTION)
63 typedef struct sigaction signal_info;
64 signal_info info_signal_handler;
65
66 static void
67 set_termsig (int sig, signal_info *old)
68 {
69 sigaction (sig, &info_signal_handler, old);
70 }
71
72 static void
73 restore_termsig (int sig, const signal_info *saved)
74 {
75 sigaction (sig, saved, NULL);
76 }
77 #else /* !HAVE_SIGACTION */
78 typedef void (*signal_info) ();
79 #define set_termsig(sig, old) (void)(*(old) = signal (sig, info_signal_proc))
80 #define restore_termsig(sig, saved) (void)signal (sig, *(saved))
81 #define info_signal_handler info_signal_proc
82 static int term_conf_busy = 0;
83 #endif /* !HAVE_SIGACTION */
84
85 static signal_info old_TSTP, old_TTOU, old_TTIN;
86 static signal_info old_WINCH, old_INT, old_TERM, old_USR1;
87 static signal_info old_QUIT;
88
89 void
90 initialize_info_signal_handler (void)
91 {
92 #ifdef SA_NOCLDSTOP
93 /* (Based on info from Paul Eggert found in coreutils.) Don't use
94 HAVE_SIGACTION to decide whether to use the sa_handler, sa_flags,
95 sa_mask members, as some systems (Solaris 7+) don't define them. Use
96 SA_NOCLDSTOP instead; it's been part of POSIX.1 since day 1 (in 1988). */
97 info_signal_handler.sa_handler = info_signal_proc;
98 info_signal_handler.sa_flags = 0;
99 mask_termsig (&info_signal_handler.sa_mask);
100 #endif /* SA_NOCLDSTOP */
101
102 #if defined (SIGTSTP)
103 set_termsig (SIGTSTP, &old_TSTP);
104 set_termsig (SIGTTOU, &old_TTOU);
105 set_termsig (SIGTTIN, &old_TTIN);
106 #endif /* SIGTSTP */
107
108 #if defined (SIGWINCH)
109 set_termsig (SIGWINCH, &old_WINCH);
110 #endif
111
112 #if defined (SIGQUIT)
113 set_termsig (SIGQUIT, &old_QUIT);
114 #endif
115
116 #if defined (SIGINT)
117 set_termsig (SIGINT, &old_INT);
118 #endif
119
120 #if defined (SIGTERM)
121 set_termsig (SIGTERM, &old_TERM);
122 #endif
123
124 #if defined (SIGUSR1)
125 /* Used by DJGPP to simulate SIGTSTP on Ctrl-Z. */
126 set_termsig (SIGUSR1, &old_USR1);
127 #endif
128 }
129
130 void
131 redisplay_after_signal (void)
132 {
133 terminal_clear_screen ();
134 display_clear_display (the_display);
135 if (auto_footnotes_p)
136 info_get_or_remove_footnotes (active_window);
137 window_mark_chain (windows, W_UpdateWindow);
138 display_update_display ();
139 display_cursor_at_point (active_window);
140 fflush (stdout);
141 }
142
143 void
144 reset_info_window_sizes (void)
145 {
146 terminal_get_screen_size ();
147 display_initialize_display (screenwidth, screenheight);
148 window_new_screen_size (screenwidth, screenheight);
149 redisplay_after_signal ();
150 }
151
152 /* Number of times we were told to ignore SIGWINCH. */
153 static int sigwinch_block_count = 0;
154
155 void
156 signal_block_winch (void)
157 {
158 #if defined (SIGWINCH)
159 if (sigwinch_block_count == 0)
160 BLOCK_SIGNAL (SIGWINCH);
161 sigwinch_block_count++;
162 #endif
163 }
164
165 void
166 signal_unblock_winch (void)
167 {
168 #if defined (SIGWINCH)
169 sigwinch_block_count--;
170 if (sigwinch_block_count == 0)
171 UNBLOCK_SIGNAL (SIGWINCH);
172 #endif
173 }
174
175 static void
176 info_signal_proc (int sig)
177 {
178 signal_info *old_signal_handler = NULL;
179
180 #if !defined (HAVE_SIGACTION)
181 /* best effort: first increment this counter and later block signals */
182 if (term_conf_busy)
183 return;
184 term_conf_busy++;
185 #if defined (HAVE_SIGPROCMASK)
186 {
187 sigset_t nvar, ovar;
188 sigemptyset (&nvar);
189 mask_termsig (&nvar);
190 sigprocmask (SIG_BLOCK, &nvar, &ovar);
191 }
192 #endif /* HAVE_SIGPROCMASK */
193 #endif /* !HAVE_SIGACTION */
194 switch (sig)
195 {
196 #if defined (SIGTSTP)
197 case SIGTSTP:
198 case SIGTTOU:
199 case SIGTTIN:
200 #endif
201 #if defined (SIGQUIT)
202 case SIGQUIT:
203 #endif
204 #if defined (SIGINT)
205 case SIGINT:
206 #endif
207 #if defined (SIGTERM)
208 case SIGTERM:
209 #endif
210 {
211 #if defined (SIGTSTP)
212 if (sig == SIGTSTP)
213 old_signal_handler = &old_TSTP;
214 if (sig == SIGTTOU)
215 old_signal_handler = &old_TTOU;
216 if (sig == SIGTTIN)
217 old_signal_handler = &old_TTIN;
218 #endif /* SIGTSTP */
219 #if defined (SIGQUIT)
220 if (sig == SIGQUIT)
221 old_signal_handler = &old_QUIT;
222 #endif /* SIGQUIT */
223 #if defined (SIGINT)
224 if (sig == SIGINT)
225 old_signal_handler = &old_INT;
226 #endif /* SIGINT */
227 #if defined (SIGTERM)
228 if (sig == SIGTERM)
229 old_signal_handler = &old_TERM;
230 #endif /* SIGTERM */
231
232 /* For stop signals, restore the terminal IO, leave the cursor
233 at the bottom of the window, and stop us. */
234 terminal_goto_xy (0, screenheight - 1);
235 terminal_clear_to_eol ();
236 fflush (stdout);
237 terminal_unprep_terminal ();
238 restore_termsig (sig, old_signal_handler);
239 UNBLOCK_SIGNAL (sig);
240 kill (getpid (), sig);
241
242 /* The program is returning now. Restore our signal handler,
243 turn on terminal handling, redraw the screen, and place the
244 cursor where it belongs. */
245 terminal_prep_terminal ();
246 set_termsig (sig, old_signal_handler);
247 /* window size might be changed while sleeping */
248 reset_info_window_sizes ();
249 }
250 break;
251
252 #if defined (SIGWINCH) || defined (SIGUSR1)
253 #ifdef SIGWINCH
254 case SIGWINCH:
255 #endif
256 #ifdef SIGUSR1
257 case SIGUSR1:
258 #endif
259 {
260 /* Turn off terminal IO, tell our parent that the window has changed,
261 then reinitialize the terminal and rebuild our windows. */
262 #ifdef SIGWINCH
263 if (sig == SIGWINCH)
264 old_signal_handler = &old_WINCH;
265 #endif
266 #ifdef SIGUSR1
267 if (sig == SIGUSR1)
268 old_signal_handler = &old_USR1;
269 #endif
270
271 /* This seems risky: what if we receive a (real) signal before
272 the next line is reached? */
273 #if 0
274 restore_termsig (sig, old_signal_handler);
275 kill (getpid (), sig);
276 #endif
277
278 /* After our old signal handler returns... */
279 set_termsig (sig, old_signal_handler); /* needless? */
280
281 if (sigwinch_block_count != 0)
282 abort ();
283
284 /* Avoid any of the code unblocking the signal too early. This
285 should set the variable to 1 because we shouldn't be here if
286 sigwinch_block_count > 0. */
287 sigwinch_block_count++;
288
289 reset_info_window_sizes ();
290
291 sigwinch_block_count--;
292 /* Don't unblock the signal until after we've finished. */
293 UNBLOCK_SIGNAL (sig);
294 }
295 break;
296 #endif /* SIGWINCH || SIGUSR1 */
297 }
298 #if !defined (HAVE_SIGACTION)
299 /* at this time it is safer to perform unblock after decrement */
300 term_conf_busy--;
301 #if defined (HAVE_SIGPROCMASK)
302 {
303 sigset_t nvar, ovar;
304 sigemptyset (&nvar);
305 mask_termsig (&nvar);
306 sigprocmask (SIG_UNBLOCK, &nvar, &ovar);
307 }
308 #endif /* HAVE_SIGPROCMASK */
309 #endif /* !HAVE_SIGACTION */
310 }
311
312
313 /* vim: set sw=2 cino={1s>2sn-s^-se-s: */