150276Speter/**************************************************************************** 2262685Sdelphij * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc. * 350276Speter * * 450276Speter * Permission is hereby granted, free of charge, to any person obtaining a * 550276Speter * copy of this software and associated documentation files (the * 650276Speter * "Software"), to deal in the Software without restriction, including * 750276Speter * without limitation the rights to use, copy, modify, merge, publish, * 850276Speter * distribute, distribute with modifications, sublicense, and/or sell * 950276Speter * copies of the Software, and to permit persons to whom the Software is * 1050276Speter * furnished to do so, subject to the following conditions: * 1150276Speter * * 1250276Speter * The above copyright notice and this permission notice shall be included * 1350276Speter * in all copies or substantial portions of the Software. * 1450276Speter * * 1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 2250276Speter * * 2350276Speter * Except as contained in this notice, the name(s) of the above copyright * 2450276Speter * holders shall not be used in advertising or otherwise to promote the * 2550276Speter * sale, use or other dealings in this Software without prior written * 2650276Speter * authorization. * 2750276Speter ****************************************************************************/ 2850276Speter 2950276Speter/**************************************************************************** 3050276Speter * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 3150276Speter * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32166124Srafan * and: Thomas E. Dickey 1995-on * 3350276Speter ****************************************************************************/ 3450276Speter 3550276Speter/* 3650276Speter** lib_tstp.c 3750276Speter** 3850276Speter** The routine _nc_signal_handler(). 3950276Speter** 4050276Speter*/ 4150276Speter#include <curses.priv.h> 4250276Speter 4350276Speter#include <SigAction.h> 4450276Speter 45262685SdelphijMODULE_ID("$Id: lib_tstp.c,v 1.47 2013/04/27 19:50:17 tom Exp $") 4650276Speter 4750276Speter#if defined(SIGTSTP) && (HAVE_SIGACTION || HAVE_SIGVEC) 4850276Speter#define USE_SIGTSTP 1 4950276Speter#else 5050276Speter#define USE_SIGTSTP 0 5150276Speter#endif 5250276Speter 5397049Speter#ifdef TRACE 5497049Speterstatic const char * 5597049Spetersignal_name(int sig) 5697049Speter{ 5797049Speter switch (sig) { 58262629Sdelphij#ifdef SIGALRM 5997049Speter case SIGALRM: 6097049Speter return "SIGALRM"; 61262629Sdelphij#endif 6297049Speter#ifdef SIGCONT 6397049Speter case SIGCONT: 6497049Speter return "SIGCONT"; 6597049Speter#endif 6697049Speter case SIGINT: 6797049Speter return "SIGINT"; 68262629Sdelphij#ifdef SIGQUIT 6997049Speter case SIGQUIT: 7097049Speter return "SIGQUIT"; 71262629Sdelphij#endif 7297049Speter case SIGTERM: 7397049Speter return "SIGTERM"; 7497049Speter#ifdef SIGTSTP 7597049Speter case SIGTSTP: 7697049Speter return "SIGTSTP"; 7797049Speter#endif 7897049Speter#ifdef SIGTTOU 7997049Speter case SIGTTOU: 8097049Speter return "SIGTTOU"; 8197049Speter#endif 8297049Speter#ifdef SIGWINCH 8397049Speter case SIGWINCH: 8497049Speter return "SIGWINCH"; 8597049Speter#endif 8697049Speter default: 8797049Speter return "unknown signal"; 8897049Speter } 8997049Speter} 9097049Speter#endif 9197049Speter 9250276Speter/* 9350276Speter * Note: This code is fragile! Its problem is that different OSs 9450276Speter * handle restart of system calls interrupted by signals differently. 9550276Speter * The ncurses code needs signal-call restart to happen -- otherwise, 9650276Speter * interrupted wgetch() calls will return FAIL, probably making the 9750276Speter * application think the input stream has ended and it should 9850276Speter * terminate. In particular, you know you have this problem if, when 9950276Speter * you suspend an ncurses-using lynx with ^Z and resume, it dies 10050276Speter * immediately. 10150276Speter * 10250276Speter * Default behavior of POSIX sigaction(2) is not to restart 10350276Speter * interrupted system calls, but Linux's sigaction does it anyway (at 10450276Speter * least, on and after the 1.1.47 I (esr) use). Thus this code works 10550276Speter * OK under Linux. The 4.4BSD sigaction(2) supports a (non-portable) 10650276Speter * SA_RESTART flag that forces the right behavior. Thus, this code 10750276Speter * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it 10850276Speter * does not). 10950276Speter * 11050276Speter * Stock System Vs (and anything else using a strict-POSIX 11150276Speter * sigaction(2) without SA_RESTART) may have a problem. Possible 11250276Speter * solutions: 11350276Speter * 11450276Speter * sigvec restarts by default (SV_INTERRUPT flag to not restart) 11550276Speter * signal restarts by default in SVr4 (assuming you link with -lucb) 11650276Speter * and BSD, but not SVr3. 11750276Speter * sigset restarts, but is only available under SVr4/Solaris. 11850276Speter * 11950276Speter * The signal(3) call is mandated by the ANSI standard, and its 12050276Speter * interaction with sigaction(2) is described in the POSIX standard 12150276Speter * (3.3.4.2, page 72,line 934). According to section 8.1, page 191, 12250276Speter * however, signal(3) itself is not required by POSIX.1. And POSIX is 12350276Speter * silent on whether it is required to restart signals. 12450276Speter * 12550276Speter * So. The present situation is, we use sigaction(2) with no 12650276Speter * guarantee of restart anywhere but on Linux and BSD. We could 12750276Speter * switch to signal(3) and collar Linux, BSD, and SVr4. Any way 12850276Speter * we slice it, System V UNIXes older than SVr4 will probably lose 12950276Speter * (this may include XENIX). 13050276Speter * 13150276Speter * This implementation will probably be changed to use signal(3) in 13250276Speter * the future. If nothing else, it's simpler... 13350276Speter */ 13450276Speter 13550276Speter#if USE_SIGTSTP 13666963Speterstatic void 137262685Sdelphijhandle_SIGTSTP(int dummy GCC_UNUSED) 13850276Speter{ 139262629Sdelphij SCREEN *sp = CURRENT_SCREEN; 14066963Speter sigset_t mask, omask; 14166963Speter sigaction_t act, oact; 14250276Speter 14350276Speter#ifdef SIGTTOU 14466963Speter int sigttou_blocked; 14550276Speter#endif 14650276Speter 147262685Sdelphij _nc_globals.have_sigtstp = 1; 148262685Sdelphij T(("handle_SIGTSTP() called")); 14950276Speter 15066963Speter /* 15166963Speter * The user may have changed the prog_mode tty bits, so save them. 15266963Speter * 15366963Speter * But first try to detect whether we still are in the foreground 15466963Speter * process group - if not, an interactive shell may already have 15566963Speter * taken ownership of the tty and modified the settings when our 15666963Speter * parent was stopped before us, and we would likely pick up the 15766963Speter * settings already modified by the shell. 15866963Speter */ 159262629Sdelphij if (sp != 0 && !sp->_endwin) /* don't do this if we're not in curses */ 16050276Speter#if HAVE_TCGETPGRP 16150276Speter if (tcgetpgrp(STDIN_FILENO) == getpgrp()) 16250276Speter#endif 163262629Sdelphij NCURSES_SP_NAME(def_prog_mode) (NCURSES_SP_ARG); 16450276Speter 16566963Speter /* 16666963Speter * Block window change and timer signals. The latter 16766963Speter * is because applications use timers to decide when 16866963Speter * to repaint the screen. 16966963Speter */ 17066963Speter (void) sigemptyset(&mask); 171262629Sdelphij#ifdef SIGALRM 17266963Speter (void) sigaddset(&mask, SIGALRM); 173262629Sdelphij#endif 17450276Speter#if USE_SIGWINCH 17566963Speter (void) sigaddset(&mask, SIGWINCH); 17650276Speter#endif 17766963Speter (void) sigprocmask(SIG_BLOCK, &mask, &omask); 17850276Speter 17950276Speter#ifdef SIGTTOU 18066963Speter sigttou_blocked = sigismember(&omask, SIGTTOU); 18166963Speter if (!sigttou_blocked) { 18266963Speter (void) sigemptyset(&mask); 18366963Speter (void) sigaddset(&mask, SIGTTOU); 18466963Speter (void) sigprocmask(SIG_BLOCK, &mask, NULL); 18566963Speter } 18650276Speter#endif 18750276Speter 18866963Speter /* 18966963Speter * End window mode, which also resets the terminal state to the 19066963Speter * original (pre-curses) modes. 19166963Speter */ 192262629Sdelphij NCURSES_SP_NAME(endwin) (NCURSES_SP_ARG); 19350276Speter 19466963Speter /* Unblock SIGTSTP. */ 19566963Speter (void) sigemptyset(&mask); 19666963Speter (void) sigaddset(&mask, SIGTSTP); 19750276Speter#ifdef SIGTTOU 19866963Speter if (!sigttou_blocked) { 19966963Speter /* Unblock this too if it wasn't blocked on entry */ 20066963Speter (void) sigaddset(&mask, SIGTTOU); 20166963Speter } 20250276Speter#endif 20366963Speter (void) sigprocmask(SIG_UNBLOCK, &mask, NULL); 20450276Speter 20566963Speter /* Now we want to resend SIGSTP to this process and suspend it */ 20666963Speter act.sa_handler = SIG_DFL; 20766963Speter sigemptyset(&act.sa_mask); 20866963Speter act.sa_flags = 0; 20950276Speter#ifdef SA_RESTART 21066963Speter act.sa_flags |= SA_RESTART; 21150276Speter#endif /* SA_RESTART */ 21266963Speter sigaction(SIGTSTP, &act, &oact); 21366963Speter kill(getpid(), SIGTSTP); 21450276Speter 21566963Speter /* Process gets suspended...time passes...process resumes */ 21650276Speter 21766963Speter T(("SIGCONT received")); 21866963Speter sigaction(SIGTSTP, &oact, NULL); 219262629Sdelphij NCURSES_SP_NAME(flushinp) (NCURSES_SP_ARG); 22050276Speter 22166963Speter /* 22266963Speter * If the user modified the tty state while suspended, he wants 22366963Speter * those changes to stick. So save the new "default" terminal state. 22466963Speter */ 225262629Sdelphij NCURSES_SP_NAME(def_shell_mode) (NCURSES_SP_ARG); 22650276Speter 22766963Speter /* 22866963Speter * This relies on the fact that doupdate() will restore the 22966963Speter * program-mode tty state, and issue enter_ca_mode if need be. 23066963Speter */ 231262629Sdelphij NCURSES_SP_NAME(doupdate) (NCURSES_SP_ARG); 23250276Speter 23366963Speter /* Reset the signals. */ 23466963Speter (void) sigprocmask(SIG_SETMASK, &omask, NULL); 23550276Speter} 23666963Speter#endif /* USE_SIGTSTP */ 23750276Speter 23866963Speterstatic void 239262685Sdelphijhandle_SIGINT(int sig) 24050276Speter{ 241262629Sdelphij SCREEN *sp = CURRENT_SCREEN; 242262629Sdelphij 24366963Speter /* 244262685Sdelphij * Much of this is unsafe from a signal handler. But we'll _try_ to clean 245262685Sdelphij * up the screen and terminal settings on the way out. 246262685Sdelphij * 247262685Sdelphij * There are at least the following problems: 248262685Sdelphij * 1) Walking the SCREEN list is unsafe, since all list management 249262685Sdelphij * is done without any signal blocking. 250262685Sdelphij * 2) On systems which have REENTRANT turned on, set_term() uses 251262685Sdelphij * _nc_lock_global() which could deadlock or misbehave in other ways. 252262685Sdelphij * 3) endwin() calls all sorts of stuff, many of which use stdio or 253262685Sdelphij * other library functions which are clearly unsafe. 25466963Speter */ 255174993Srafan if (!_nc_globals.cleanup_nested++ 256262685Sdelphij && (sig == SIGINT || sig == SIGTERM)) { 25750276Speter#if HAVE_SIGACTION || HAVE_SIGVEC 25866963Speter sigaction_t act; 25966963Speter sigemptyset(&act.sa_mask); 26066963Speter act.sa_flags = 0; 26166963Speter act.sa_handler = SIG_IGN; 26297049Speter if (sigaction(sig, &act, NULL) == 0) 26350276Speter#else 26466963Speter if (signal(sig, SIG_IGN) != SIG_ERR) 26550276Speter#endif 26666963Speter { 267178866Srafan SCREEN *scan; 268178866Srafan for (each_screen(scan)) { 269178866Srafan if (scan->_ofp != 0 270178866Srafan && isatty(fileno(scan->_ofp))) { 271262629Sdelphij scan->_outch = NCURSES_SP_NAME(_nc_outch); 27250276Speter } 27366963Speter set_term(scan); 274262629Sdelphij NCURSES_SP_NAME(endwin) (NCURSES_SP_ARG); 275262629Sdelphij if (sp) 276262685Sdelphij sp->_endwin = FALSE; /* in case of reuse */ 27766963Speter } 27850276Speter } 27966963Speter } 280262685Sdelphij _exit(EXIT_FAILURE); 28150276Speter} 28250276Speter 28350276Speter#if USE_SIGWINCH 28466963Speterstatic void 285262685Sdelphijhandle_SIGWINCH(int sig GCC_UNUSED) 28650276Speter{ 287174993Srafan _nc_globals.have_sigwinch = 1; 288262629Sdelphij# if USE_PTHREADS_EINTR 289262629Sdelphij if (_nc_globals.read_thread) { 290262629Sdelphij if (!pthread_equal(pthread_self(), _nc_globals.read_thread)) 291262629Sdelphij pthread_kill(_nc_globals.read_thread, SIGWINCH); 292262629Sdelphij _nc_globals.read_thread = 0; 293262629Sdelphij } 294262629Sdelphij# endif 29550276Speter} 29650276Speter#endif /* USE_SIGWINCH */ 29750276Speter 29850276Speter/* 29950276Speter * If the given signal is still in its default state, set it to the given 30050276Speter * handler. 30150276Speter */ 30266963Speterstatic int 303262685SdelphijCatchIfDefault(int sig, void (*handler) (int)) 30450276Speter{ 30597049Speter int result; 30697049Speter#if HAVE_SIGACTION || HAVE_SIGVEC 30766963Speter sigaction_t old_act; 30897049Speter sigaction_t new_act; 30950276Speter 31097049Speter memset(&new_act, 0, sizeof(new_act)); 31197049Speter sigemptyset(&new_act.sa_mask); 31297049Speter#ifdef SA_RESTART 31397049Speter#ifdef SIGWINCH 31497049Speter if (sig != SIGWINCH) 31597049Speter#endif 31697049Speter new_act.sa_flags |= SA_RESTART; 31797049Speter#endif /* SA_RESTART */ 31897049Speter new_act.sa_handler = handler; 31997049Speter 32097049Speter if (sigaction(sig, NULL, &old_act) == 0 32166963Speter && (old_act.sa_handler == SIG_DFL 32297049Speter || old_act.sa_handler == handler 32350276Speter#if USE_SIGWINCH 32450276Speter || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN) 32550276Speter#endif 32666963Speter )) { 32797049Speter (void) sigaction(sig, &new_act, NULL); 32897049Speter result = TRUE; 32997049Speter } else { 33097049Speter result = FALSE; 33166963Speter } 33297049Speter#else /* !HAVE_SIGACTION */ 333262685Sdelphij void (*ohandler) (int); 33450276Speter 33566963Speter ohandler = signal(sig, SIG_IGN); 33666963Speter if (ohandler == SIG_DFL 33797049Speter || ohandler == handler 33850276Speter#if USE_SIGWINCH 33966963Speter || (sig == SIGWINCH && ohandler == SIG_IGN) 34050276Speter#endif 34150276Speter ) { 34266963Speter signal(sig, handler); 34397049Speter result = TRUE; 34466963Speter } else { 34566963Speter signal(sig, ohandler); 34697049Speter result = FALSE; 34766963Speter } 34897049Speter#endif 34997049Speter T(("CatchIfDefault - will %scatch %s", 35097049Speter result ? "" : "not ", signal_name(sig))); 35197049Speter return result; 35250276Speter} 35350276Speter 35450276Speter/* 35550276Speter * This is invoked once at the beginning (e.g., from 'initscr()'), to 35650276Speter * initialize the signal catchers, and thereafter when spawning a shell (and 35750276Speter * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher. 35850276Speter * 35950276Speter * If the application has already set one of the signals, we'll not modify it 36050276Speter * (during initialization). 36150276Speter * 36250276Speter * The XSI document implies that we shouldn't keep the SIGTSTP handler if 36350276Speter * the caller later changes its mind, but that doesn't seem correct. 36450276Speter */ 36576726SpeterNCURSES_EXPORT(void) 366262685Sdelphij_nc_signal_handler(int enable) 36750276Speter{ 36897049Speter T((T_CALLED("_nc_signal_handler(%d)"), enable)); 36966963Speter#if USE_SIGTSTP /* Xenix 2.x doesn't have SIGTSTP, for example */ 37097049Speter { 37197049Speter static bool ignore_tstp = FALSE; 37250276Speter 37397049Speter if (!ignore_tstp) { 374174993Srafan static sigaction_t new_sigaction, old_sigaction; 37550276Speter 37697049Speter if (!enable) { 377174993Srafan new_sigaction.sa_handler = SIG_IGN; 378174993Srafan sigaction(SIGTSTP, &new_sigaction, &old_sigaction); 379174993Srafan } else if (new_sigaction.sa_handler != SIG_DFL) { 380174993Srafan sigaction(SIGTSTP, &old_sigaction, NULL); 381174993Srafan } else if (sigaction(SIGTSTP, NULL, &old_sigaction) == 0 382174993Srafan && (old_sigaction.sa_handler == SIG_DFL)) { 383174993Srafan sigemptyset(&new_sigaction.sa_mask); 38450276Speter#ifdef SA_RESTART 385174993Srafan new_sigaction.sa_flags |= SA_RESTART; 38650276Speter#endif /* SA_RESTART */ 387262685Sdelphij new_sigaction.sa_handler = handle_SIGTSTP; 388174993Srafan (void) sigaction(SIGTSTP, &new_sigaction, NULL); 38997049Speter } else { 39097049Speter ignore_tstp = TRUE; 39197049Speter } 39250276Speter } 39366963Speter } 39497049Speter#endif /* !USE_SIGTSTP */ 39550276Speter 396174993Srafan if (!_nc_globals.init_signals) { 39797049Speter if (enable) { 398262685Sdelphij CatchIfDefault(SIGINT, handle_SIGINT); 399262685Sdelphij CatchIfDefault(SIGTERM, handle_SIGINT); 40050276Speter#if USE_SIGWINCH 401262685Sdelphij CatchIfDefault(SIGWINCH, handle_SIGWINCH); 40250276Speter#endif 403174993Srafan _nc_globals.init_signals = TRUE; 40497049Speter } 40566963Speter } 40697049Speter returnVoid; 40750276Speter} 408