150276Speter/**************************************************************************** 2178866Srafan * Copyright (c) 1998-2007,2008 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 4566963Speter#if SVR4_ACTION && !defined(_POSIX_SOURCE) 4650276Speter#define _POSIX_SOURCE 4750276Speter#endif 4850276Speter 49178866SrafanMODULE_ID("$Id: lib_tstp.c,v 1.37 2008/05/03 16:24:56 tom Exp $") 5050276Speter 5150276Speter#if defined(SIGTSTP) && (HAVE_SIGACTION || HAVE_SIGVEC) 5250276Speter#define USE_SIGTSTP 1 5350276Speter#else 5450276Speter#define USE_SIGTSTP 0 5550276Speter#endif 5650276Speter 5797049Speter#ifdef TRACE 5897049Speterstatic const char * 5997049Spetersignal_name(int sig) 6097049Speter{ 6197049Speter switch (sig) { 6297049Speter case SIGALRM: 6397049Speter return "SIGALRM"; 6497049Speter#ifdef SIGCONT 6597049Speter case SIGCONT: 6697049Speter return "SIGCONT"; 6797049Speter#endif 6897049Speter case SIGINT: 6997049Speter return "SIGINT"; 7097049Speter case SIGQUIT: 7197049Speter return "SIGQUIT"; 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 13766963Spetertstp(int dummy GCC_UNUSED) 13850276Speter{ 13966963Speter sigset_t mask, omask; 14066963Speter sigaction_t act, oact; 14150276Speter 14250276Speter#ifdef SIGTTOU 14366963Speter int sigttou_blocked; 14450276Speter#endif 14550276Speter 14666963Speter T(("tstp() called")); 14750276Speter 14866963Speter /* 14966963Speter * The user may have changed the prog_mode tty bits, so save them. 15066963Speter * 15166963Speter * But first try to detect whether we still are in the foreground 15266963Speter * process group - if not, an interactive shell may already have 15366963Speter * taken ownership of the tty and modified the settings when our 15466963Speter * parent was stopped before us, and we would likely pick up the 15566963Speter * settings already modified by the shell. 15666963Speter */ 15766963Speter if (SP != 0 && !SP->_endwin) /* don't do this if we're not in curses */ 15850276Speter#if HAVE_TCGETPGRP 15950276Speter if (tcgetpgrp(STDIN_FILENO) == getpgrp()) 16050276Speter#endif 16150276Speter def_prog_mode(); 16250276Speter 16366963Speter /* 16466963Speter * Block window change and timer signals. The latter 16566963Speter * is because applications use timers to decide when 16666963Speter * to repaint the screen. 16766963Speter */ 16866963Speter (void) sigemptyset(&mask); 16966963Speter (void) sigaddset(&mask, SIGALRM); 17050276Speter#if USE_SIGWINCH 17166963Speter (void) sigaddset(&mask, SIGWINCH); 17250276Speter#endif 17366963Speter (void) sigprocmask(SIG_BLOCK, &mask, &omask); 17450276Speter 17550276Speter#ifdef SIGTTOU 17666963Speter sigttou_blocked = sigismember(&omask, SIGTTOU); 17766963Speter if (!sigttou_blocked) { 17866963Speter (void) sigemptyset(&mask); 17966963Speter (void) sigaddset(&mask, SIGTTOU); 18066963Speter (void) sigprocmask(SIG_BLOCK, &mask, NULL); 18166963Speter } 18250276Speter#endif 18350276Speter 18466963Speter /* 18566963Speter * End window mode, which also resets the terminal state to the 18666963Speter * original (pre-curses) modes. 18766963Speter */ 18866963Speter endwin(); 18950276Speter 19066963Speter /* Unblock SIGTSTP. */ 19166963Speter (void) sigemptyset(&mask); 19266963Speter (void) sigaddset(&mask, SIGTSTP); 19350276Speter#ifdef SIGTTOU 19466963Speter if (!sigttou_blocked) { 19566963Speter /* Unblock this too if it wasn't blocked on entry */ 19666963Speter (void) sigaddset(&mask, SIGTTOU); 19766963Speter } 19850276Speter#endif 19966963Speter (void) sigprocmask(SIG_UNBLOCK, &mask, NULL); 20050276Speter 20166963Speter /* Now we want to resend SIGSTP to this process and suspend it */ 20266963Speter act.sa_handler = SIG_DFL; 20366963Speter sigemptyset(&act.sa_mask); 20466963Speter act.sa_flags = 0; 20550276Speter#ifdef SA_RESTART 20666963Speter act.sa_flags |= SA_RESTART; 20750276Speter#endif /* SA_RESTART */ 20866963Speter sigaction(SIGTSTP, &act, &oact); 20966963Speter kill(getpid(), SIGTSTP); 21050276Speter 21166963Speter /* Process gets suspended...time passes...process resumes */ 21250276Speter 21366963Speter T(("SIGCONT received")); 21466963Speter sigaction(SIGTSTP, &oact, NULL); 21566963Speter flushinp(); 21650276Speter 21766963Speter /* 21866963Speter * If the user modified the tty state while suspended, he wants 21966963Speter * those changes to stick. So save the new "default" terminal state. 22066963Speter */ 22166963Speter def_shell_mode(); 22250276Speter 22366963Speter /* 22466963Speter * This relies on the fact that doupdate() will restore the 22566963Speter * program-mode tty state, and issue enter_ca_mode if need be. 22666963Speter */ 22766963Speter doupdate(); 22850276Speter 22966963Speter /* Reset the signals. */ 23066963Speter (void) sigprocmask(SIG_SETMASK, &omask, NULL); 23150276Speter} 23266963Speter#endif /* USE_SIGTSTP */ 23350276Speter 23466963Speterstatic void 23566963Spetercleanup(int sig) 23650276Speter{ 23766963Speter /* 23866963Speter * Actually, doing any sort of I/O from within an signal handler is 23966963Speter * "unsafe". But we'll _try_ to clean up the screen and terminal 24066963Speter * settings on the way out. 24166963Speter */ 242174993Srafan if (!_nc_globals.cleanup_nested++ 24366963Speter && (sig == SIGINT 24466963Speter || sig == SIGQUIT)) { 24550276Speter#if HAVE_SIGACTION || HAVE_SIGVEC 24666963Speter sigaction_t act; 24766963Speter sigemptyset(&act.sa_mask); 24866963Speter act.sa_flags = 0; 24966963Speter act.sa_handler = SIG_IGN; 25097049Speter if (sigaction(sig, &act, NULL) == 0) 25150276Speter#else 25266963Speter if (signal(sig, SIG_IGN) != SIG_ERR) 25350276Speter#endif 25466963Speter { 255178866Srafan SCREEN *scan; 256178866Srafan for (each_screen(scan)) { 257178866Srafan if (scan->_ofp != 0 258178866Srafan && isatty(fileno(scan->_ofp))) { 259178866Srafan scan->_cleanup = TRUE; 260178866Srafan scan->_outch = _nc_outch; 26150276Speter } 26266963Speter set_term(scan); 26366963Speter endwin(); 26466963Speter if (SP) 26566963Speter SP->_endwin = FALSE; /* in case we have an atexit! */ 26666963Speter } 26750276Speter } 26866963Speter } 26966963Speter exit(EXIT_FAILURE); 27050276Speter} 27150276Speter 27250276Speter#if USE_SIGWINCH 27366963Speterstatic void 27466963Spetersigwinch(int sig GCC_UNUSED) 27550276Speter{ 276174993Srafan _nc_globals.have_sigwinch = 1; 27750276Speter} 27850276Speter#endif /* USE_SIGWINCH */ 27950276Speter 28050276Speter/* 28150276Speter * If the given signal is still in its default state, set it to the given 28250276Speter * handler. 28350276Speter */ 28466963Speterstatic int 28597049SpeterCatchIfDefault(int sig, RETSIGTYPE (*handler) (int)) 28650276Speter{ 28797049Speter int result; 28897049Speter#if HAVE_SIGACTION || HAVE_SIGVEC 28966963Speter sigaction_t old_act; 29097049Speter sigaction_t new_act; 29150276Speter 29297049Speter memset(&new_act, 0, sizeof(new_act)); 29397049Speter sigemptyset(&new_act.sa_mask); 29497049Speter#ifdef SA_RESTART 29597049Speter#ifdef SIGWINCH 29697049Speter if (sig != SIGWINCH) 29797049Speter#endif 29897049Speter new_act.sa_flags |= SA_RESTART; 29997049Speter#endif /* SA_RESTART */ 30097049Speter new_act.sa_handler = handler; 30197049Speter 30297049Speter if (sigaction(sig, NULL, &old_act) == 0 30366963Speter && (old_act.sa_handler == SIG_DFL 30497049Speter || old_act.sa_handler == handler 30550276Speter#if USE_SIGWINCH 30650276Speter || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN) 30750276Speter#endif 30866963Speter )) { 30997049Speter (void) sigaction(sig, &new_act, NULL); 31097049Speter result = TRUE; 31197049Speter } else { 31297049Speter result = FALSE; 31366963Speter } 31497049Speter#else /* !HAVE_SIGACTION */ 31597049Speter RETSIGTYPE (*ohandler) (int); 31650276Speter 31766963Speter ohandler = signal(sig, SIG_IGN); 31866963Speter if (ohandler == SIG_DFL 31997049Speter || ohandler == handler 32050276Speter#if USE_SIGWINCH 32166963Speter || (sig == SIGWINCH && ohandler == SIG_IGN) 32250276Speter#endif 32350276Speter ) { 32466963Speter signal(sig, handler); 32597049Speter result = TRUE; 32666963Speter } else { 32766963Speter signal(sig, ohandler); 32897049Speter result = FALSE; 32966963Speter } 33097049Speter#endif 33197049Speter T(("CatchIfDefault - will %scatch %s", 33297049Speter result ? "" : "not ", signal_name(sig))); 33397049Speter return result; 33450276Speter} 33550276Speter 33650276Speter/* 33750276Speter * This is invoked once at the beginning (e.g., from 'initscr()'), to 33850276Speter * initialize the signal catchers, and thereafter when spawning a shell (and 33950276Speter * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher. 34050276Speter * 34150276Speter * If the application has already set one of the signals, we'll not modify it 34250276Speter * (during initialization). 34350276Speter * 34450276Speter * The XSI document implies that we shouldn't keep the SIGTSTP handler if 34550276Speter * the caller later changes its mind, but that doesn't seem correct. 34650276Speter */ 34776726SpeterNCURSES_EXPORT(void) 34866963Speter_nc_signal_handler(bool enable) 34950276Speter{ 35097049Speter T((T_CALLED("_nc_signal_handler(%d)"), enable)); 35166963Speter#if USE_SIGTSTP /* Xenix 2.x doesn't have SIGTSTP, for example */ 35297049Speter { 35397049Speter static bool ignore_tstp = FALSE; 35450276Speter 35597049Speter if (!ignore_tstp) { 356174993Srafan static sigaction_t new_sigaction, old_sigaction; 35750276Speter 35897049Speter if (!enable) { 359174993Srafan new_sigaction.sa_handler = SIG_IGN; 360174993Srafan sigaction(SIGTSTP, &new_sigaction, &old_sigaction); 361174993Srafan } else if (new_sigaction.sa_handler != SIG_DFL) { 362174993Srafan sigaction(SIGTSTP, &old_sigaction, NULL); 363174993Srafan } else if (sigaction(SIGTSTP, NULL, &old_sigaction) == 0 364174993Srafan && (old_sigaction.sa_handler == SIG_DFL)) { 365174993Srafan sigemptyset(&new_sigaction.sa_mask); 36650276Speter#ifdef SA_RESTART 367174993Srafan new_sigaction.sa_flags |= SA_RESTART; 36850276Speter#endif /* SA_RESTART */ 369174993Srafan new_sigaction.sa_handler = tstp; 370174993Srafan (void) sigaction(SIGTSTP, &new_sigaction, NULL); 37197049Speter } else { 37297049Speter ignore_tstp = TRUE; 37397049Speter } 37450276Speter } 37566963Speter } 37697049Speter#endif /* !USE_SIGTSTP */ 37750276Speter 378174993Srafan if (!_nc_globals.init_signals) { 37997049Speter if (enable) { 38097049Speter CatchIfDefault(SIGINT, cleanup); 38197049Speter CatchIfDefault(SIGTERM, cleanup); 38250276Speter#if USE_SIGWINCH 38397049Speter CatchIfDefault(SIGWINCH, sigwinch); 38450276Speter#endif 385174993Srafan _nc_globals.init_signals = TRUE; 38697049Speter } 38766963Speter } 38897049Speter returnVoid; 38950276Speter} 390