This source file includes following definitions.
- nonintr_tcgetattr
- nonintr_tcsetattr
- log_message
- sprintf_integer
- simple_errno_string
- simple_signal_string
- log_signal_handler_called
- update_pgrp_status
- atexit_handler
- tcsetattr_failed
- clobber_local_mode
- restore_local_mode
- init_relevant_signal_set
- block_relevant_signals
- unblock_relevant_signals
- is_ignored
- show_signal_marker
- fatal_or_stopping_signal_handler
- fatal_signal_handler
- stopping_signal_handler
- continuing_signal_handler
- ensure_continuing_signal_handler
- ensure_other_signal_handlers
- activate_term_non_default_mode
- deactivate_term_non_default_mode
- activate_term_style_controller
- deactivate_term_style_controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 #include <config.h>
19
20
21 #include "term-style-control.h"
22
23
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
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
55
56
57
58
59
60 #if HAVE_TCGETATTR
61
62 static inline int
63 nonintr_tcgetattr (int fd, struct termios *tcp)
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)
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
90
91
92
93
94
95
96
97
98
99
100
101 #if DEBUG_SIGNALS
102
103
104 static _GL_ASYNC_SAFE void
105 log_message (const char *message)
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
119 static _GL_ASYNC_SAFE void
120 sprintf_integer (char *str, int x)
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
152 static _GL_ASYNC_SAFE void
153 simple_errno_string (char *str, int errnum)
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
171 static _GL_ASYNC_SAFE void
172 simple_signal_string (char *str, int sig)
173 {
174 switch (sig)
175 {
176
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
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
209 #ifdef SIGCONT
210 case SIGCONT: strcpy (str, "SIGCONT"); break;
211 #endif
212 default: sprintf_integer (str, sig); break;
213 }
214 }
215
216
217 static _GL_ASYNC_SAFE void
218 log_signal_handler_called (int sig)
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333 #define BLOCK_SIGNALS_DURING_NON_DEFAULT_STYLE_OUTPUT 0
334
335
336
337 static int volatile term_fd = -1;
338
339 #if HAVE_TCGETATTR
340
341
342 typedef enum
343 {
344 PGRP_UNKNOWN = 0,
345 PGRP_NO_TTY,
346 PGRP_IN_FOREGROUND,
347
348 PGRP_IN_BACKGROUND
349
350 } pgrp_status_t;
351 static pgrp_status_t volatile pgrp_status = PGRP_UNKNOWN;
352
353
354 static _GL_ASYNC_SAFE void
355 update_pgrp_status (void)
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
374
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
396
397 static const struct term_style_controller * volatile active_controller;
398 static struct term_style_user_data * volatile active_user_data;
399
400
401
402
403
404
405 static struct term_style_control_data * volatile active_control_data;
406
407
408
409
410
411
412 static int volatile active_fd = -1;
413
414
415 static void
416 atexit_handler (void)
417 {
418
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
424 deactivate_term_style_controller (active_controller, active_user_data);
425 #endif
426 }
427 }
428
429 #if HAVE_TCGETATTR
430
431
432 static _GL_ASYNC_SAFE void
433 tcsetattr_failed (char message[100], const char *caller)
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
445 static bool volatile orig_lflag_set;
446 static tcflag_t volatile orig_lflag;
447
448
449
450 static _GL_ASYNC_SAFE void
451 clobber_local_mode (void)
452 {
453
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
462
463
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
470
471
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
485
486
487 static _GL_ASYNC_SAFE bool
488 restore_local_mode (void)
489 {
490
491 bool echo_was_off = false;
492
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
518
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
541
542
543
544
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 ()
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
573 static _GL_ASYNC_SAFE inline void
574 block_relevant_signals ()
575 {
576
577
578 if (!relevant_signal_set_initialized)
579 abort ();
580
581 sigprocmask (SIG_BLOCK, &relevant_signal_set, NULL);
582 }
583
584
585 static _GL_ASYNC_SAFE inline void
586 unblock_relevant_signals ()
587 {
588 sigprocmask (SIG_UNBLOCK, &relevant_signal_set, NULL);
589 }
590
591 #if defined SIGCONT
592
593
594 static _GL_ASYNC_SAFE bool
595 is_ignored (int sig)
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
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624 static _GL_ASYNC_SAFE void
625 show_signal_marker (int sig)
626 {
627
628
629 if (active_controller != NULL && active_control_data->same_as_stderr)
630 switch (sig)
631 {
632
633 case SIGINT:
634 full_write (STDERR_FILENO, "^C", 2); break;
635
636 case SIGTSTP:
637 full_write (STDERR_FILENO, "^Z", 2); break;
638
639 case SIGQUIT:
640 full_write (STDERR_FILENO, "^\\", 2); break;
641 default: break;
642 }
643 }
644
645 #endif
646
647
648
649 static _GL_ASYNC_SAFE void
650 fatal_or_stopping_signal_handler (int sig)
651 {
652 #if HAVE_TCGETATTR
653 bool echo_was_off = false;
654 #endif
655
656 if (active_controller != NULL
657 && active_control_data->tty_control != TTYCTL_NONE)
658 {
659 unsigned int i;
660
661
662
663
664 block_relevant_signals ();
665
666
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
673
674 echo_was_off = restore_local_mode ();
675 }
676 #endif
677
678
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
689
690 static _GL_ASYNC_SAFE void
691 fatal_signal_handler (int sig)
692 {
693 log_signal_handler_called (sig);
694 fatal_or_stopping_signal_handler (sig);
695 }
696
697 #if defined SIGCONT
698
699
700
701 static _GL_ASYNC_SAFE void
702 stopping_signal_handler (int sig)
703 {
704 int saved_errno = errno;
705
706 log_signal_handler_called (sig);
707 fatal_or_stopping_signal_handler (sig);
708
709
710
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
723
724 static _GL_ASYNC_SAFE void
725 continuing_signal_handler (int sig)
726 {
727 int saved_errno = errno;
728
729 log_signal_handler_called (sig);
730 update_pgrp_status ();
731
732 if (active_controller != NULL
733 && active_control_data->tty_control != TTYCTL_NONE)
734 {
735
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
748
749
750
751 action.sa_flags = SA_NODEFER;
752 sigemptyset (&action.sa_mask);
753 sigaction (sig, &action, NULL);
754 }
755 }
756 }
757
758
759
760
761 block_relevant_signals ();
762
763 #if HAVE_TCGETATTR
764 if (active_control_data->tty_control == TTYCTL_FULL)
765 {
766
767 clobber_local_mode ();
768 }
769 #endif
770
771 active_controller->async_set_attributes_from_default (active_user_data);
772
773
774 unblock_relevant_signals ();
775 }
776
777 errno = saved_errno;
778 }
779
780
781
782
783
784
785
786 static void
787 ensure_continuing_signal_handler (void)
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
797
798
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)
811 {
812 static bool signal_handlers_installed = false;
813
814 if (!signal_handlers_installed)
815 {
816
817 if (at_fatal_signal (fatal_signal_handler) < 0)
818 xalloc_die ();
819
820 #if defined SIGCONT
821
822
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
832 ;
833 else if (!is_ignored (sig))
834 {
835 struct sigaction action;
836 action.sa_handler = &stopping_signal_handler;
837
838
839
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
863
864 void
865 activate_term_non_default_mode (const struct term_style_controller *controller,
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
878
879
880 block_relevant_signals ();
881 #endif
882
883
884
885 if (active_controller != NULL)
886 {
887
888
889 abort ();
890 }
891
892
893
894
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
902 if (active_control_data->tty_control == TTYCTL_FULL)
903 {
904
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,
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
924 if (active_control_data->tty_control == TTYCTL_FULL)
925 {
926
927
928 restore_local_mode ();
929 }
930 #endif
931
932
933
934
935
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
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,
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
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
981 control_data->same_as_stderr = false;
982 #endif
983
984 control_data->non_default_active = false;
985
986
987 term_fd = fd;
988 #if defined SIGCONT
989 ensure_continuing_signal_handler ();
990 #endif
991 update_pgrp_status ();
992
993
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,
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
1012 if (control_data->non_default_active)
1013 abort ();
1014
1015 term_fd = -1;
1016 update_pgrp_status ();
1017 }