signals.c 9.2 KB

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