1 /* Terminal control for outputting styled text to a terminal.
2 Copyright (C) 2006-2008, 2017, 2019-2021 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2019.
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 <https://www.gnu.org/licenses/>. */
17
18 #include <config.h>
19
20 /* Specification. */
21 #include "term-style-control.h"
22
23 /* Set to 1 to get debugging output regarding signals. */
24 #define DEBUG_SIGNALS 0
25
26 #include <errno.h>
27 #include <signal.h>
28 #include <stdbool.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #if DEBUG_SIGNALS
33 # include <stdio.h>
34 #endif
35 #if HAVE_TCGETATTR
36 # include <termios.h>
37 # if !defined NOFLSH /* QNX */
38 # define NOFLSH 0
39 # endif
40 #endif
41 #if HAVE_TCGETATTR
42 # include <sys/stat.h>
43 #endif
44
45 #include "fatal-signal.h"
46 #include "sig-handler.h"
47 #include "full-write.h"
48 #include "same-inode.h"
49 #include "xalloc.h"
50
51 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
52
53
54 /* ============================ EINTR handling ============================ */
55
56 /* EINTR handling for tcgetattr(), tcsetattr().
57 These functions can return -1/EINTR even when we don't have any
58 signal handlers set up, namely when we get interrupted via SIGSTOP. */
59
60 #if HAVE_TCGETATTR
61
62 static inline int
63 nonintr_tcgetattr (int fd, struct termios *tcp)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
64 {
65 int retval;
66
67 do
68 retval = tcgetattr (fd, tcp);
69 while (retval < 0 && errno == EINTR);
70
71 return retval;
72 }
73
74 static inline int
75 nonintr_tcsetattr (int fd, int flush_mode, const struct termios *tcp)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
76 {
77 int retval;
78
79 do
80 retval = tcsetattr (fd, flush_mode, tcp);
81 while (retval < 0 && errno == EINTR);
82
83 return retval;
84 }
85
86 #endif
87
88
89 /* ========================== Logging primitives ========================== */
90
91 /* We need logging, especially for the signal handling, because
92 - Debugging through gdb is hardly possible, because gdb produces output
93 by itself and interferes with the process states.
94 - strace is buggy when it comes to SIGTSTP handling: By default, it
95 sends the process a SIGSTOP signal instead of SIGTSTP. It supports
96 an option '-D -I4' to mitigate this, though. Also, race conditions
97 appear with different probability with and without strace.
98 fprintf(stderr) is not possible within async-safe code, because fprintf()
99 may invoke malloc(). */
100
101 #if DEBUG_SIGNALS
102
103 /* Log a simple message. */
104 static _GL_ASYNC_SAFE void
105 log_message (const char *message)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
106 {
107 full_write (STDERR_FILENO, message, strlen (message));
108 }
109
110 #else
111
112 # define log_message(message)
113
114 #endif
115
116 #if HAVE_TCGETATTR || DEBUG_SIGNALS
117
118 /* Async-safe implementation of sprintf (str, "%d", n). */
119 static _GL_ASYNC_SAFE void
120 sprintf_integer (char *str, int x)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
121 {
122 unsigned int y;
123 char buf[20];
124 char *p;
125 size_t n;
126
127 if (x < 0)
128 {
129 *str++ = '-';
130 y = (unsigned int) (-1 - x) + 1;
131 }
132 else
133 y = x;
134
135 p = buf + sizeof (buf);
136 do
137 {
138 *--p = '0' + (y % 10);
139 y = y / 10;
140 }
141 while (y > 0);
142 n = buf + sizeof (buf) - p;
143 memcpy (str, p, n);
144 str[n] = '\0';
145 }
146
147 #endif
148
149 #if HAVE_TCGETATTR
150
151 /* Async-safe conversion of errno value to string. */
152 static _GL_ASYNC_SAFE void
153 simple_errno_string (char *str, int errnum)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
154 {
155 switch (errnum)
156 {
157 case EBADF: strcpy (str, "EBADF"); break;
158 case EINTR: strcpy (str, "EINTR"); break;
159 case EINVAL: strcpy (str, "EINVAL"); break;
160 case EIO: strcpy (str, "EIO"); break;
161 case ENOTTY: strcpy (str, "ENOTTY"); break;
162 default: sprintf_integer (str, errnum); break;
163 }
164 }
165
166 #endif
167
168 #if DEBUG_SIGNALS
169
170 /* Async-safe conversion of signal number to name. */
171 static _GL_ASYNC_SAFE void
172 simple_signal_string (char *str, int sig)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
173 {
174 switch (sig)
175 {
176 /* Fatal signals (see fatal-signal.c). */
177 #ifdef SIGINT
178 case SIGINT: strcpy (str, "SIGINT"); break;
179 #endif
180 #ifdef SIGTERM
181 case SIGTERM: strcpy (str, "SIGTERM"); break;
182 #endif
183 #ifdef SIGHUP
184 case SIGHUP: strcpy (str, "SIGHUP"); break;
185 #endif
186 #ifdef SIGPIPE
187 case SIGPIPE: strcpy (str, "SIGPIPE"); break;
188 #endif
189 #ifdef SIGXCPU
190 case SIGXCPU: strcpy (str, "SIGXCPU"); break;
191 #endif
192 #ifdef SIGXFSZ
193 case SIGXFSZ: strcpy (str, "SIGXFSZ"); break;
194 #endif
195 #ifdef SIGBREAK
196 case SIGBREAK: strcpy (str, "SIGBREAK"); break;
197 #endif
198 /* Stopping signals. */
199 #ifdef SIGTSTP
200 case SIGTSTP: strcpy (str, "SIGTSTP"); break;
201 #endif
202 #ifdef SIGTTIN
203 case SIGTTIN: strcpy (str, "SIGTTIN"); break;
204 #endif
205 #ifdef SIGTTOU
206 case SIGTTOU: strcpy (str, "SIGTTOU"); break;
207 #endif
208 /* Continuing signals. */
209 #ifdef SIGCONT
210 case SIGCONT: strcpy (str, "SIGCONT"); break;
211 #endif
212 default: sprintf_integer (str, sig); break;
213 }
214 }
215
216 /* Emit a message that a given signal handler is being run. */
217 static _GL_ASYNC_SAFE void
218 log_signal_handler_called (int sig)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
219 {
220 char message[100];
221 strcpy (message, "Signal handler for signal ");
222 simple_signal_string (message + strlen (message), sig);
223 strcat (message, " called.\n");
224 log_message (message);
225 }
226
227 #else
228
229 # define log_signal_handler_called(sig)
230
231 #endif
232
233
234 /* ============================ Signal handling ============================ */
235
236 /* There are several situations which can cause garbled output on the terminal's
237 screen:
238 (1) When the program calls exit() after calling flush_to_current_style,
239 the program would terminate and leave the terminal in a non-default
240 state.
241 (2) When the program is interrupted through a fatal signal, the terminal
242 would be left in a non-default state.
243 (3) When the program is stopped through a stopping signal, the terminal
244 would be left (for temporary use by other programs) in a non-default
245 state.
246 (4) When a foreground process receives a SIGINT, the kernel(!) prints '^C'.
247 On Linux, the place where this happens is
248 linux-5.0/drivers/tty/n_tty.c:713..730
249 within a call sequence
250 n_tty_receive_signal_char (n_tty.c:1245..1246)
251 -> commit_echoes (n_tty.c:792)
252 -> __process_echoes (n_tty.c:713..730).
253 (5) When a signal is sent, the output buffer is cleared.
254 On Linux, this output buffer consists of the "echo buffer" in the tty
255 and the "output buffer" in the driver. The place where this happens is
256 linux-5.0/drivers/tty/n_tty.c:1133..1140
257 within a call
258 isig (n_tty.c:1133..1140).
259
260 How do we mitigate these problems?
261 (1) We install an exit handler that restores the terminal to the default
262 state.
263 (2) If tty_control is TTYCTL_PARTIAL or TTYCTL_FULL:
264 For some of the fatal signals (see gnulib's 'fatal-signal' module for
265 the precise list), we install a handler that attempts to restore the
266 terminal to the default state. Since the terminal may be in the middle
267 of outputting an escape sequence at this point, the first escape
268 sequence emitted from this handler may have no effect and produce
269 garbled characters instead. Therefore the handler outputs the cleanup
270 sequence twice.
271 For the other fatal signals, we don't do anything.
272 (3) If tty_control is TTYCTL_PARTIAL or TTYCTL_FULL:
273 For some of the stopping signals (SIGTSTP, SIGTTIN, SIGTTOU), we install
274 a handler that attempts to restore the terminal to the default state.
275 For SIGCONT, we install a handler that does the opposite: it puts the
276 terminal into the desired state again.
277 For SIGSTOP, we cannot do anything.
278 (4) If tty_control is TTYCTL_FULL:
279 The kernel's action depends on L_ECHO(tty) and L_ISIG(tty), that is, on
280 the local modes of the tty (see
281 <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>
282 section 11.2.5). We don't want to change L_ISIG; hence we change L_ECHO.
283 So, we disable the ECHO local flag of the tty; the equivalent command is
284 'stty -echo'.
285 (5) If tty_control is TTYCTL_FULL:
286 The kernel's action depends on !L_NOFLSH(tty), that is, again on the
287 local modes of the tty (see
288 <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>
289 section 11.2.5). So, we enable the NOFLSH local flag of the tty; the
290 equivalent command is 'stty noflsh'.
291 For terminals with a baud rate < 9600 this is suboptimal. For this case
292 - where the traditional flushing behaviour makes sense - we would use a
293 technique that involves tcdrain(), TIOCOUTQ, and usleep() when it is OK
294 to disable NOFLSH.
295
296 Regarding (4) and (5), there is a complication: Changing the local modes is
297 done through tcsetattr(). However, when the process is put into the
298 background, tcsetattr() does not operate the same way as when the process is
299 running in the foreground.
300 To test this kind of behaviour, use the 'color-filter' example like this:
301 $ yes | ./filter '.*'
302 <Ctrl-Z>
303 $ bg 1
304 We have three possible implementation options:
305 * If we don't ignore the signal SIGTTOU:
306 If the TOSTOP bit in the terminal's local mode is clear (command
307 equivalent: 'stty -tostop') and the process is put into the background,
308 normal output would continue (per POSIX
309 <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html>
310 section 11.2.5) but tcsetattr() calls would cause it to stop due to
311 a SIGTTOU signal (per POSIX
312 <https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html>).
313 Thus, the program would behave differently with term-style-control than
314 without.
315 * If we ignore the signal SIGTTOU when the TOSTOP bit in the terminal's
316 local mode is clear (i.e. when (tc.c_lflag & TOSTOP) == 0):
317 The tcsetattr() calls do not stop the process, but they don't have the
318 desired effect.
319 On Linux, when I put the process into the background and then kill it with
320 signal SIGINT, I can see that the last operation on the terminal settings
321 (as shown by 'strace') is
322 ioctl(1, TCSETSW, {B38400 opost isig icanon echo ...}) = 0
323 and yet, once the process is terminated, the terminal settings contain
324 '-echo', not 'echo'.
325 * Don't call tcsetattr() if the process is not in the foreground.
326 This approach produces reliable results.
327
328 Blocking some signals while a non-default style is active is *not* useful:
329 - It does not help against (1), since exit() is not a signal.
330 - Signal handlers are the better approach against (2) and (3).
331 - It does not help against (4) and (5), because the kernel's actions happen
332 outside the process. */
333 #define BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT 0
334
335 /* File descriptor of the currently active 'struct term_style_controller' and
336 'struct term_style_user_data'. */
337 static int volatile term_fd = -1;
338
339 #if HAVE_TCGETATTR
340
341 /* Status of the process group of term_fd. */
342 typedef enum
343 {
344 PGRP_UNKNOWN = 0, /* term_fd < 0. Unknown status. */
345 PGRP_NO_TTY, /* term_fd >= 0 but is not connected to a tty. */
346 PGRP_IN_FOREGROUND, /* term_fd >= 0 is a tty. This process is running in
347 the foreground. */
348 PGRP_IN_BACKGROUND /* term_fd >= 0 is a tty. This process is running in
349 the background. */
350 } pgrp_status_t;
351 static pgrp_status_t volatile pgrp_status = PGRP_UNKNOWN;
352
353 /* Update pgrp_status, depending on term_fd. */
354 static _GL_ASYNC_SAFE void
355 update_pgrp_status (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
356 {
357 int fd = term_fd;
358 if (fd < 0)
359 {
360 pgrp_status = PGRP_UNKNOWN;
361 log_message ("pgrp_status = PGRP_UNKNOWN\n");
362 }
363 else
364 {
365 pid_t p = tcgetpgrp (fd);
366 if (p < 0)
367 {
368 pgrp_status = PGRP_NO_TTY;
369 log_message ("pgrp_status = PGRP_NO_TTY\n");
370 }
371 else
372 {
373 /* getpgrp () changes when the process gets put into the background
374 by a shell that implements job control. */
375 if (p == getpgrp ())
376 {
377 pgrp_status = PGRP_IN_FOREGROUND;
378 log_message ("pgrp_status = PGRP_IN_FOREGROUND\n");
379 }
380 else
381 {
382 pgrp_status = PGRP_IN_BACKGROUND;
383 log_message ("pgrp_status = PGRP_IN_BACKGROUND\n");
384 }
385 }
386 }
387 }
388
389 #else
390
391 # define update_pgrp_status()
392
393 #endif
394
395 /* Controller and its user_data that contain information about how to do
396 output. */
397 static const struct term_style_controller * volatile active_controller;
398 static struct term_style_user_data * volatile active_user_data;
399
400 /* The 'struct term_style_control_data' embedded in active_user_data.
401 Same as
402 (active_controller != NULL
403 ? active_controller->get_control_data (active_user_data)
404 : NULL). */
405 static struct term_style_control_data * volatile active_control_data;
406
407 /* The fd contained in active_control_data.
408 Same as
409 (active_controller != NULL
410 ? active_control_data->fd
411 : -1). */
412 static int volatile active_fd = -1;
413
414 /* The exit handler. */
415 static void
416 atexit_handler (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
417 {
418 /* Only do something while some output was started but not completed. */
419 if (active_controller != NULL)
420 {
421 active_controller->restore (active_user_data);
422 deactivate_term_non_default_mode (active_controller, active_user_data);
423 #if 0 /* not needed */
424 deactivate_term_style_controller (active_controller, active_user_data);
425 #endif
426 }
427 }
428
429 #if HAVE_TCGETATTR
430
431 /* Return a failure message after tcsetattr() failed. */
432 static _GL_ASYNC_SAFE void
433 tcsetattr_failed (char message[100], const char *caller)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
434 {
435 int errnum = errno;
436 strcpy (message, caller);
437 strcat (message, ": tcsetattr(fd=");
438 sprintf_integer (message + strlen (message), active_fd);
439 strcat (message, ") failed, errno=");
440 simple_errno_string (message + strlen (message), errnum);
441 strcat (message, "\n");
442 }
443
444 /* True when orig_lflag represents the original tc.c_lflag. */
445 static bool volatile orig_lflag_set;
446 static tcflag_t volatile orig_lflag;
447
448 /* Modifies the tty's local mode, preparing for non-default terminal state.
449 Used only when the active_control_data's tty_control is TTYCTL_FULL. */
450 static _GL_ASYNC_SAFE void
451 clobber_local_mode (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
452 {
453 /* Here, active_fd == term_fd. */
454 if (pgrp_status == PGRP_IN_FOREGROUND)
455 {
456 struct termios tc;
457 if (nonintr_tcgetattr (active_fd, &tc) >= 0)
458 {
459 if (!orig_lflag_set)
460 orig_lflag = tc.c_lflag;
461 /* Set orig_lflag_set to true before actually modifying the tty's
462 local mode, because restore_local_mode does nothing if
463 orig_lflag_set is false. */
464 orig_lflag_set = true;
465 tc.c_lflag &= ~ECHO;
466 tc.c_lflag |= NOFLSH;
467 if (nonintr_tcsetattr (active_fd, TCSANOW, &tc) < 0)
468 {
469 /* Since tcsetattr failed, restore_local_mode does not need to
470 restore anything. Set orig_lflag_set to false to indicate
471 this. */
472 orig_lflag_set = false;
473 {
474 char message[100];
475 tcsetattr_failed (message,
476 "term-style-control:clobber_local_mode");
477 full_write (STDERR_FILENO, message, strlen (message));
478 }
479 }
480 }
481 }
482 }
483
484 /* Modifies the tty's local mode, once the terminal is back to the default state.
485 Returns true if ECHO was turned off.
486 Used only when the active_control_data's tty_control is TTYCTL_FULL. */
487 static _GL_ASYNC_SAFE bool
488 restore_local_mode (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
489 {
490 /* Here, active_fd == term_fd. */
491 bool echo_was_off = false;
492 /* Nothing to do if !orig_lflag_set. */
493 if (orig_lflag_set)
494 {
495 struct termios tc;
496 if (nonintr_tcgetattr (active_fd, &tc) >= 0)
497 {
498 echo_was_off = (tc.c_lflag & ECHO) == 0;
499 tc.c_lflag = orig_lflag;
500 if (nonintr_tcsetattr (active_fd, TCSADRAIN, &tc) < 0)
501 {
502 char message[100];
503 tcsetattr_failed (message,
504 "term-style-control:restore_local_mode");
505 full_write (STDERR_FILENO, message, strlen (message));
506 }
507 }
508 orig_lflag_set = false;
509 }
510 return echo_was_off;
511 }
512
513 #endif
514
515 #if defined SIGCONT
516
517 /* The list of signals whose default behaviour is to stop or continue the
518 program. */
519 static int const job_control_signals[] =
520 {
521 #ifdef SIGTSTP
522 SIGTSTP,
523 #endif
524 #ifdef SIGTTIN
525 SIGTTIN,
526 #endif
527 #ifdef SIGTTOU
528 SIGTTOU,
529 #endif
530 #ifdef SIGCONT
531 SIGCONT,
532 #endif
533 0
534 };
535
536 # define num_job_control_signals (SIZEOF (job_control_signals) - 1)
537
538 #endif
539
540 /* The following signals are relevant because they output escape sequences to
541 the terminal:
542 - fatal signals,
543 - stopping signals,
544 - continuing signals (SIGCONT). */
545
546 static sigset_t relevant_signal_set;
547 static bool relevant_signal_set_initialized = false;
548
549 static void
550 init_relevant_signal_set ()
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
551 {
552 if (!relevant_signal_set_initialized)
553 {
554 int fatal_signals[64];
555 size_t num_fatal_signals;
556 size_t i;
557
558 num_fatal_signals = get_fatal_signals (fatal_signals);
559
560 sigemptyset (&relevant_signal_set);
561 for (i = 0; i < num_fatal_signals; i++)
562 sigaddset (&relevant_signal_set, fatal_signals[i]);
563 #if defined SIGCONT
564 for (i = 0; i < num_job_control_signals; i++)
565 sigaddset (&relevant_signal_set, job_control_signals[i]);
566 #endif
567
568 relevant_signal_set_initialized = true;
569 }
570 }
571
572 /* Temporarily delay the relevant signals. */
573 static _GL_ASYNC_SAFE inline void
574 block_relevant_signals ()
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
575 {
576 /* The caller must ensure that init_relevant_signal_set () was already
577 called. */
578 if (!relevant_signal_set_initialized)
579 abort ();
580
581 sigprocmask (SIG_BLOCK, &relevant_signal_set, NULL);
582 }
583
584 /* Stop delaying the relevant signals. */
585 static _GL_ASYNC_SAFE inline void
586 unblock_relevant_signals ()
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
587 {
588 sigprocmask (SIG_UNBLOCK, &relevant_signal_set, NULL);
589 }
590
591 #if defined SIGCONT
592
593 /* Determines whether a signal is ignored. */
594 static _GL_ASYNC_SAFE bool
595 is_ignored (int sig)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
596 {
597 struct sigaction action;
598
599 return (sigaction (sig, NULL, &action) >= 0
600 && get_handler (&action) == SIG_IGN);
601 }
602
603 #endif
604
605 #if HAVE_TCGETATTR
606
607 /* Write the same signal marker that the kernel would have printed if ECHO had
608 been turned on. See (4) above.
609 This is a makeshift and is not perfect:
610 - When stderr refers to a different target than active_control_data->fd,
611 it is too hairy to write the signal marker.
612 - In some cases, when the signal was generated right before and delivered
613 right after a clobber_local_mode invocation, the result is that the
614 marker appears twice, e.g. ^C^C. This occurs only with a small
615 probability.
616 - In some cases, when the signal was generated right before and delivered
617 right after a restore_local_mode invocation, the result is that the
618 marker does not appear at all. This occurs only with a small
619 probability.
620 To test this kind of behaviour, use the 'test-term-style-control-yes' example
621 like this:
622 $ ./test-term-style-control-yes
623 */
624 static _GL_ASYNC_SAFE void
625 show_signal_marker (int sig)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
626 {
627 /* Write to stderr, not to active_control_data->fd, because
628 active_control_data->fd is often logged or used with 'less -R'. */
629 if (active_controller != NULL && active_control_data->same_as_stderr)
630 switch (sig)
631 {
632 /* The kernel's action when the user presses the INTR key. */
633 case SIGINT:
634 full_write (STDERR_FILENO, "^C", 2); break;
635 /* The kernel's action when the user presses the SUSP key. */
636 case SIGTSTP:
637 full_write (STDERR_FILENO, "^Z", 2); break;
638 /* The kernel's action when the user presses the QUIT key. */
639 case SIGQUIT:
640 full_write (STDERR_FILENO, "^\\", 2); break;
641 default: break;
642 }
643 }
644
645 #endif
646
647 /* The main code of the signal handler for fatal signals and stopping signals.
648 It is reentrant. */
649 static _GL_ASYNC_SAFE void
650 fatal_or_stopping_signal_handler (int sig)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
651 {
652 #if HAVE_TCGETATTR
653 bool echo_was_off = false;
654 #endif
655 /* Only do something while some output was interrupted. */
656 if (active_controller != NULL
657 && active_control_data->tty_control != TTYCTL_NONE)
658 {
659 unsigned int i;
660
661 /* Block the relevant signals. This is needed, because the output
662 of escape sequences below (usually through tputs invocations) is
663 not reentrant. */
664 block_relevant_signals ();
665
666 /* Restore the terminal to the default state. */
667 for (i = 0; i < 2; i++)
668 active_controller->async_restore (active_user_data);
669 #if HAVE_TCGETATTR
670 if (active_control_data->tty_control == TTYCTL_FULL)
671 {
672 /* Restore the local mode, once the escape sequences output above
673 have reached their destination. */
674 echo_was_off = restore_local_mode ();
675 }
676 #endif
677
678 /* Unblock the relevant signals. */
679 unblock_relevant_signals ();
680 }
681
682 #if HAVE_TCGETATTR
683 if (echo_was_off)
684 show_signal_marker (sig);
685 #endif
686 }
687
688 /* The signal handler for fatal signals.
689 It is reentrant. */
690 static _GL_ASYNC_SAFE void
691 fatal_signal_handler (int sig)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
692 {
693 log_signal_handler_called (sig);
694 fatal_or_stopping_signal_handler (sig);
695 }
696
697 #if defined SIGCONT
698
699 /* The signal handler for stopping signals.
700 It is reentrant. */
701 static _GL_ASYNC_SAFE void
702 stopping_signal_handler (int sig)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
703 {
704 int saved_errno = errno;
705
706 log_signal_handler_called (sig);
707 fatal_or_stopping_signal_handler (sig);
708
709 /* Now execute the signal's default action.
710 We reinstall the handler later, during the SIGCONT handler. */
711 {
712 struct sigaction action;
713 action.sa_handler = SIG_DFL;
714 action.sa_flags = SA_NODEFER;
715 sigemptyset (&action.sa_mask);
716 sigaction (sig, &action, NULL);
717 }
718 errno = saved_errno;
719 raise (sig);
720 }
721
722 /* The signal handler for SIGCONT.
723 It is reentrant. */
724 static _GL_ASYNC_SAFE void
725 continuing_signal_handler (int sig)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
726 {
727 int saved_errno = errno;
728
729 log_signal_handler_called (sig);
730 update_pgrp_status ();
731 /* Only do something while some output was interrupted. */
732 if (active_controller != NULL
733 && active_control_data->tty_control != TTYCTL_NONE)
734 {
735 /* Reinstall the signals handlers removed in stopping_signal_handler. */
736 {
737 unsigned int i;
738
739 for (i = 0; i < num_job_control_signals; i++)
740 {
741 int sig = job_control_signals[i];
742
743 if (sig != SIGCONT && !is_ignored (sig))
744 {
745 struct sigaction action;
746 action.sa_handler = &stopping_signal_handler;
747 /* If we get a stopping or continuing signal while executing
748 stopping_signal_handler or continuing_signal_handler, enter
749 it recursively, since it is reentrant.
750 Hence no SA_RESETHAND. */
751 action.sa_flags = SA_NODEFER;
752 sigemptyset (&action.sa_mask);
753 sigaction (sig, &action, NULL);
754 }
755 }
756 }
757
758 /* Block the relevant signals. This is needed, because the output of
759 escape sequences done inside the async_set_attributes_from_default
760 call below is not reentrant. */
761 block_relevant_signals ();
762
763 #if HAVE_TCGETATTR
764 if (active_control_data->tty_control == TTYCTL_FULL)
765 {
766 /* Modify the local mode. */
767 clobber_local_mode ();
768 }
769 #endif
770 /* Set the terminal attributes. */
771 active_controller->async_set_attributes_from_default (active_user_data);
772
773 /* Unblock the relevant signals. */
774 unblock_relevant_signals ();
775 }
776
777 errno = saved_errno;
778 }
779
780 /* Ensure the signal handlers are installed.
781 Once they are installed, we leave them installed. It's not worth
782 installing and uninstalling them each time we switch the terminal to a
783 non-default state and back; instead we set active_controller to tell the
784 signal handler whether it has something to do or not. */
785
786 static void
787 ensure_continuing_signal_handler (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
788 {
789 static bool signal_handler_installed = false;
790
791 if (!signal_handler_installed)
792 {
793 int sig = SIGCONT;
794 struct sigaction action;
795 action.sa_handler = &continuing_signal_handler;
796 /* If we get a stopping or continuing signal while executing
797 continuing_signal_handler, enter it recursively, since it is
798 reentrant. Hence no SA_RESETHAND. */
799 action.sa_flags = SA_NODEFER;
800 sigemptyset (&action.sa_mask);
801 sigaction (sig, &action, NULL);
802
803 signal_handler_installed = true;
804 }
805 }
806
807 #endif
808
809 static void
810 ensure_other_signal_handlers (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
811 {
812 static bool signal_handlers_installed = false;
813
814 if (!signal_handlers_installed)
815 {
816 /* Install the handlers for the fatal signals. */
817 if (at_fatal_signal (fatal_signal_handler) < 0)
818 xalloc_die ();
819
820 #if defined SIGCONT
821
822 /* Install the handlers for the stopping and continuing signals. */
823 {
824 unsigned int i;
825
826 for (i = 0; i < num_job_control_signals; i++)
827 {
828 int sig = job_control_signals[i];
829
830 if (sig == SIGCONT)
831 /* Already handled in ensure_continuing_signal_handler. */
832 ;
833 else if (!is_ignored (sig))
834 {
835 struct sigaction action;
836 action.sa_handler = &stopping_signal_handler;
837 /* If we get a stopping or continuing signal while executing
838 stopping_signal_handler, enter it recursively, since it is
839 reentrant. Hence no SA_RESETHAND. */
840 action.sa_flags = SA_NODEFER;
841 sigemptyset (&action.sa_mask);
842 sigaction (sig, &action, NULL);
843 }
844 #if DEBUG_SIGNALS
845 else
846 {
847 fprintf (stderr, "Signal %d is ignored. Not installing a handler!\n",
848 sig);
849 fflush (stderr);
850 }
851 #endif
852 }
853 }
854
855 #endif
856
857 signal_handlers_installed = true;
858 }
859 }
860
861
862 /* ============================== Public API ============================== */
863
864 void
865 activate_term_non_default_mode (const struct term_style_controller *controller,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
866 struct term_style_user_data *user_data)
867 {
868 struct term_style_control_data *control_data =
869 controller->get_control_data (user_data);
870
871 if (!control_data->non_default_active)
872 {
873 if (control_data->tty_control != TTYCTL_NONE)
874 ensure_other_signal_handlers ();
875
876 #if BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT
877 /* Block fatal signals, so that a SIGINT or similar doesn't interrupt
878 us without the possibility of restoring the terminal's state.
879 Likewise for SIGTSTP etc. */
880 block_relevant_signals ();
881 #endif
882
883 /* Enable the exit handler for restoring the terminal's state,
884 and make the signal handlers effective. */
885 if (active_controller != NULL)
886 {
887 /* We can't support two active controllers with non-default
888 attributes at the same time. */
889 abort ();
890 }
891 /* The uses of 'volatile' (and ISO C 99 section 5.1.2.3.(5)) ensure that
892 we set active_controller to a non-NULL value only after the memory
893 locations active_user_data, active_control_data, active_fd have been
894 filled. */
895 active_fd = control_data->fd;
896 active_control_data = control_data;
897 active_user_data = user_data;
898 active_controller = controller;
899
900 #if HAVE_TCGETATTR
901 /* Now that the signal handlers are effective, modify the tty. */
902 if (active_control_data->tty_control == TTYCTL_FULL)
903 {
904 /* Modify the local mode. */
905 clobber_local_mode ();
906 }
907 #endif
908
909 control_data->non_default_active = true;
910 }
911 }
912
913 void
914 deactivate_term_non_default_mode (const struct term_style_controller *controller,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
915 struct term_style_user_data *user_data)
916 {
917 struct term_style_control_data *control_data =
918 controller->get_control_data (user_data);
919
920 if (control_data->non_default_active)
921 {
922 #if HAVE_TCGETATTR
923 /* Before we make the signal handlers ineffective, modify the tty. */
924 if (active_control_data->tty_control == TTYCTL_FULL)
925 {
926 /* Restore the local mode, once the tputs calls from out_attr_change
927 have reached their destination. */
928 restore_local_mode ();
929 }
930 #endif
931
932 /* Disable the exit handler, and make the signal handlers ineffective. */
933 /* The uses of 'volatile' (and ISO C 99 section 5.1.2.3.(5)) ensure that
934 we reset active_user_data, active_control_data, active_fd only after
935 the memory location active_controller has been cleared. */
936 active_controller = NULL;
937 active_user_data = NULL;
938 active_control_data = NULL;
939 active_fd = -1;
940
941 #if BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT
942 /* Unblock the relevant signals. */
943 unblock_relevant_signals ();
944 #endif
945
946 control_data->non_default_active = false;
947 }
948 }
949
950 void
951 activate_term_style_controller (const struct term_style_controller *controller,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
952 struct term_style_user_data *user_data,
953 int fd, ttyctl_t tty_control)
954 {
955 struct term_style_control_data *control_data =
956 controller->get_control_data (user_data);
957
958 control_data->fd = fd;
959
960 /* Prepare tty control. */
961 if (tty_control == TTYCTL_AUTO)
962 tty_control = TTYCTL_FULL;
963 control_data->tty_control = tty_control;
964 if (control_data->tty_control != TTYCTL_NONE)
965 init_relevant_signal_set ();
966 #if HAVE_TCGETATTR
967 if (control_data->tty_control == TTYCTL_FULL)
968 {
969 struct stat statbuf1;
970 struct stat statbuf2;
971 if (fd == STDERR_FILENO
972 || (fstat (fd, &statbuf1) >= 0
973 && fstat (STDERR_FILENO, &statbuf2) >= 0
974 && SAME_INODE (statbuf1, statbuf2)))
975 control_data->same_as_stderr = true;
976 else
977 control_data->same_as_stderr = false;
978 }
979 else
980 /* This value is actually not used. */
981 control_data->same_as_stderr = false;
982 #endif
983
984 control_data->non_default_active = false;
985
986 /* Start keeping track of the process group status. */
987 term_fd = fd;
988 #if defined SIGCONT
989 ensure_continuing_signal_handler ();
990 #endif
991 update_pgrp_status ();
992
993 /* Register an exit handler. */
994 {
995 static bool registered = false;
996 if (!registered)
997 {
998 atexit (atexit_handler);
999 registered = true;
1000 }
1001 }
1002 }
1003
1004 void
1005 deactivate_term_style_controller (const struct term_style_controller *controller,
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
1006 struct term_style_user_data *user_data)
1007 {
1008 struct term_style_control_data *control_data =
1009 controller->get_control_data (user_data);
1010
1011 /* Verify that the non-default attributes mode is turned off. */
1012 if (control_data->non_default_active)
1013 abort ();
1014
1015 term_fd = -1;
1016 update_pgrp_status ();
1017 }