/* * Copyright (C) 1996-2000,2012 Michael R. Elkins * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #if HAVE_CONFIG_H # include "config.h" #endif #include "mutt.h" #include "mutt_curses.h" #include #include #include #include #include static sigset_t Sigset; static sigset_t SigsetSys; static struct sigaction SysOldInt; static struct sigaction SysOldQuit; static int IsEndwin = 0; static char *Caught_Signal_L10N = NULL; static char *Exiting_L10N = NULL; static void exit_print_int_recursive (int n) { char digit; if (n > 9) exit_print_int_recursive (n / 10); digit = '0' + (n % 10); write (1, &digit, 1); } static void exit_print_int (int n) { if (n < 0) { write (1, "-", 1); n = -n; } exit_print_int_recursive (n); } static void exit_print_string (const char *str) { size_t len = 0; if (!str) return; while (str[len]) len++; if (len > 0) write (1, str, len); } /* Attempt to catch "ordinary" signals and shut down gracefully. * Do not do l10n translations as _() can invoke malloc(), (__MEM_CHECKED__) * which may not be re-entrant. */ static void exit_handler (int sig) { curs_set (1); endwin (); /* just to be safe */ exit_print_string (Caught_Signal_L10N ? Caught_Signal_L10N : "Caught signal "); #if SYS_SIGLIST_DECLARED exit_print_string (sys_siglist[sig]); #else #if (__sun__ && __svr4__) exit_print_string (_sys_siglist[sig]); #else #if (__alpha && __osf__) exit_print_string (__sys_siglist[sig]); #else exit_print_int (sig); #endif #endif #endif exit_print_string (Exiting_L10N ? Exiting_L10N : "... Exiting.\n"); exit (0); } /* These are gettext() translated in advance because that * is not guaranteed reentrant. */ static void exit_handler_l10n_init (void) { if (!Caught_Signal_L10N) { /* L10N: This is printed in the exit handler when a signal is caught. The whole string is "Caught signal [XXX]... Exiting\n". This is the first part of the string: note with a trailing space. */ Caught_Signal_L10N = safe_strdup (_("Caught signal ")); } if (!Exiting_L10N) { /* L10N: This is printed in the exit handler when a signal is caught. The whole string is "Caught signal [XXX]... Exiting\n". This is the second part of the string, printed after the signal number or name. */ Exiting_L10N = safe_strdup (_("... Exiting.\n")); } } static void exit_handler_l10n_cleanup (void) { FREE (&Caught_Signal_L10N); FREE (&Exiting_L10N); } static void chld_handler (int sig) { SigChld = 1; } static void sighandler (int sig) { int save_errno = errno; switch (sig) { case SIGTSTP: /* user requested a suspend */ if (!option (OPTSUSPEND)) break; IsEndwin = isendwin (); curs_set (1); if (!IsEndwin) endwin (); kill (0, SIGSTOP); /* fall through */ case SIGCONT: if (!IsEndwin) refresh (); mutt_curs_set (-1); #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM) /* We don't receive SIGWINCH when suspended; however, no harm is done by * just assuming we received one, and triggering the 'resize' anyway. */ SigWinch = 1; #endif break; #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM) case SIGWINCH: SigWinch = 1; break; #endif case SIGINT: SigInt = 1; break; } errno = save_errno; } #ifdef USE_SLANG_CURSES int mutt_intr_hook (void) { return (-1); } #endif /* USE_SLANG_CURSES */ void mutt_signal_init (void) { struct sigaction act; exit_handler_l10n_init (); sigemptyset (&act.sa_mask); act.sa_flags = 0; act.sa_handler = SIG_IGN; sigaction (SIGPIPE, &act, NULL); act.sa_handler = exit_handler; sigaction (SIGTERM, &act, NULL); sigaction (SIGHUP, &act, NULL); sigaction (SIGQUIT, &act, NULL); /* we want to avoid race conditions */ sigaddset (&act.sa_mask, SIGTSTP); act.sa_handler = sighandler; /* we want SIGALRM to abort the current syscall, so we do this before * setting the SA_RESTART flag below. currently this is only used to * timeout on a connect() call in a reasonable amount of time. */ sigaction (SIGALRM, &act, NULL); /* we also don't want to mess with interrupted system calls */ #ifdef SA_RESTART act.sa_flags = SA_RESTART; #endif sigaction (SIGCONT, &act, NULL); sigaction (SIGTSTP, &act, NULL); sigaction (SIGINT, &act, NULL); #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM) sigaction (SIGWINCH, &act, NULL); #endif /* Handle SIGCHLD. Tracked for background editing processes. */ act.sa_handler = chld_handler; /* don't need to block any other signals here */ sigemptyset (&act.sa_mask); /* we don't want to mess with stopped children */ act.sa_flags |= SA_NOCLDSTOP; sigaction (SIGCHLD, &act, NULL); #ifdef USE_SLANG_CURSES /* This bit of code is required because of the implementation of * SLcurses_wgetch(). If a signal is received (like SIGWINCH) when we * are in blocking mode, SLsys_getkey() will not return an error unless * a handler function is defined and it returns -1. This is needed so * that if the user resizes the screen while at a prompt, it will just * abort and go back to the main-menu. */ SLang_getkey_intr_hook = mutt_intr_hook; #endif } void mutt_signal_cleanup (void) { exit_handler_l10n_cleanup (); } /* signals which are important to block while doing critical ops */ void mutt_block_signals (void) { if (!option (OPTSIGNALSBLOCKED)) { sigemptyset (&Sigset); sigaddset (&Sigset, SIGTERM); sigaddset (&Sigset, SIGHUP); sigaddset (&Sigset, SIGTSTP); sigaddset (&Sigset, SIGINT); #if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM) sigaddset (&Sigset, SIGWINCH); #endif sigprocmask (SIG_BLOCK, &Sigset, 0); set_option (OPTSIGNALSBLOCKED); } } /* restore the previous signal mask */ void mutt_unblock_signals (void) { if (option (OPTSIGNALSBLOCKED)) { sigprocmask (SIG_UNBLOCK, &Sigset, 0); unset_option (OPTSIGNALSBLOCKED); } } void mutt_block_signals_system (void) { struct sigaction sa; if (! option (OPTSYSSIGNALSBLOCKED)) { /* POSIX: ignore SIGINT and SIGQUIT & block SIGCHLD before exec */ sa.sa_handler = SIG_IGN; sa.sa_flags = 0; sigemptyset (&sa.sa_mask); sigaction (SIGINT, &sa, &SysOldInt); sigaction (SIGQUIT, &sa, &SysOldQuit); sigemptyset (&SigsetSys); sigaddset (&SigsetSys, SIGCHLD); sigprocmask (SIG_BLOCK, &SigsetSys, 0); set_option (OPTSYSSIGNALSBLOCKED); } } void mutt_unblock_signals_system (int catch) { if (option (OPTSYSSIGNALSBLOCKED)) { sigprocmask (SIG_UNBLOCK, &SigsetSys, NULL); if (catch) { sigaction (SIGQUIT, &SysOldQuit, NULL); sigaction (SIGINT, &SysOldInt, NULL); } else { struct sigaction sa; sa.sa_handler = SIG_DFL; sigemptyset (&sa.sa_mask); sa.sa_flags = 0; sigaction (SIGQUIT, &sa, NULL); sigaction (SIGINT, &sa, NULL); } unset_option (OPTSYSSIGNALSBLOCKED); } } /* Resets ignored signals back to the default. * * See sigaction(2): * A child created via fork(2) inherits a copy of its parent's * signal dispositions. During an execve(2), the dispositions of * handled signals are reset to the default; the dispositions of * ignored signals are left unchanged. */ void mutt_reset_child_signals (void) { struct sigaction act; act.sa_handler = SIG_DFL; act.sa_flags = 0; sigemptyset (&act.sa_mask); /* These signals are set to SIG_IGN and must be reset */ sigaction (SIGPIPE, &act, NULL); /* These technically don't need to be reset, but the code has been * doing so for a long time. */ sigaction (SIGTERM, &act, NULL); sigaction (SIGTSTP, &act, NULL); sigaction (SIGCONT, &act, NULL); } void mutt_allow_interrupt (int disposition) { struct sigaction sa; memset (&sa, 0, sizeof sa); sa.sa_handler = sighandler; #ifdef SA_RESTART if (disposition == 0) sa.sa_flags |= SA_RESTART; #endif sigaction (SIGINT, &sa, NULL); }