lib_tstp.c revision 97049
150276Speter/**************************************************************************** 297049Speter * Copyright (c) 1998-2001,2002 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> * 3297049Speter * and: Thomas Dickey 1995-2001 * 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 4997049SpeterMODULE_ID("$Id: lib_tstp.c,v 1.30 2002/05/18 19:55:38 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 static int nested; 23856639Speter 23966963Speter /* 24066963Speter * Actually, doing any sort of I/O from within an signal handler is 24166963Speter * "unsafe". But we'll _try_ to clean up the screen and terminal 24266963Speter * settings on the way out. 24366963Speter */ 24466963Speter if (!nested++ 24566963Speter && (sig == SIGINT 24666963Speter || sig == SIGQUIT)) { 24750276Speter#if HAVE_SIGACTION || HAVE_SIGVEC 24866963Speter sigaction_t act; 24966963Speter sigemptyset(&act.sa_mask); 25066963Speter act.sa_flags = 0; 25166963Speter act.sa_handler = SIG_IGN; 25297049Speter if (sigaction(sig, &act, NULL) == 0) 25350276Speter#else 25466963Speter if (signal(sig, SIG_IGN) != SIG_ERR) 25550276Speter#endif 25666963Speter { 25766963Speter SCREEN *scan = _nc_screen_chain; 25866963Speter while (scan) { 25966963Speter if (SP != 0 26066963Speter && SP->_ofp != 0 26166963Speter && isatty(fileno(SP->_ofp))) { 26266963Speter SP->_cleanup = TRUE; 26366963Speter SP->_outch = _nc_outch; 26450276Speter } 26566963Speter set_term(scan); 26666963Speter endwin(); 26766963Speter if (SP) 26866963Speter SP->_endwin = FALSE; /* in case we have an atexit! */ 26966963Speter scan = scan->_next_screen; 27066963Speter } 27150276Speter } 27266963Speter } 27366963Speter exit(EXIT_FAILURE); 27450276Speter} 27550276Speter 27650276Speter#if USE_SIGWINCH 27766963Speterstatic void 27866963Spetersigwinch(int sig GCC_UNUSED) 27950276Speter{ 28050276Speter SCREEN *scan = _nc_screen_chain; 28166963Speter while (scan) { 28250276Speter scan->_sig_winch = TRUE; 28350276Speter scan = scan->_next_screen; 28450276Speter } 28550276Speter} 28650276Speter#endif /* USE_SIGWINCH */ 28750276Speter 28850276Speter/* 28950276Speter * If the given signal is still in its default state, set it to the given 29050276Speter * handler. 29150276Speter */ 29266963Speterstatic int 29397049SpeterCatchIfDefault(int sig, RETSIGTYPE (*handler) (int)) 29450276Speter{ 29597049Speter int result; 29697049Speter#if HAVE_SIGACTION || HAVE_SIGVEC 29766963Speter sigaction_t old_act; 29897049Speter sigaction_t new_act; 29950276Speter 30097049Speter memset(&new_act, 0, sizeof(new_act)); 30197049Speter sigemptyset(&new_act.sa_mask); 30297049Speter#ifdef SA_RESTART 30397049Speter#ifdef SIGWINCH 30497049Speter if (sig != SIGWINCH) 30597049Speter#endif 30697049Speter new_act.sa_flags |= SA_RESTART; 30797049Speter#endif /* SA_RESTART */ 30897049Speter new_act.sa_handler = handler; 30997049Speter 31097049Speter if (sigaction(sig, NULL, &old_act) == 0 31166963Speter && (old_act.sa_handler == SIG_DFL 31297049Speter || old_act.sa_handler == handler 31350276Speter#if USE_SIGWINCH 31450276Speter || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN) 31550276Speter#endif 31666963Speter )) { 31797049Speter (void) sigaction(sig, &new_act, NULL); 31897049Speter result = TRUE; 31997049Speter } else { 32097049Speter result = FALSE; 32166963Speter } 32297049Speter#else /* !HAVE_SIGACTION */ 32397049Speter RETSIGTYPE (*ohandler) (int); 32450276Speter 32566963Speter ohandler = signal(sig, SIG_IGN); 32666963Speter if (ohandler == SIG_DFL 32797049Speter || ohandler == handler 32850276Speter#if USE_SIGWINCH 32966963Speter || (sig == SIGWINCH && ohandler == SIG_IGN) 33050276Speter#endif 33150276Speter ) { 33266963Speter signal(sig, handler); 33397049Speter result = TRUE; 33466963Speter } else { 33566963Speter signal(sig, ohandler); 33697049Speter result = FALSE; 33766963Speter } 33897049Speter#endif 33997049Speter T(("CatchIfDefault - will %scatch %s", 34097049Speter result ? "" : "not ", signal_name(sig))); 34197049Speter return result; 34250276Speter} 34350276Speter 34450276Speter/* 34550276Speter * This is invoked once at the beginning (e.g., from 'initscr()'), to 34650276Speter * initialize the signal catchers, and thereafter when spawning a shell (and 34750276Speter * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher. 34850276Speter * 34950276Speter * If the application has already set one of the signals, we'll not modify it 35050276Speter * (during initialization). 35150276Speter * 35250276Speter * The XSI document implies that we shouldn't keep the SIGTSTP handler if 35350276Speter * the caller later changes its mind, but that doesn't seem correct. 35450276Speter */ 35576726SpeterNCURSES_EXPORT(void) 35666963Speter_nc_signal_handler(bool enable) 35750276Speter{ 35897049Speter static bool initialized = FALSE; 35997049Speter 36097049Speter T((T_CALLED("_nc_signal_handler(%d)"), enable)); 36166963Speter#if USE_SIGTSTP /* Xenix 2.x doesn't have SIGTSTP, for example */ 36297049Speter { 36397049Speter static bool ignore_tstp = FALSE; 36450276Speter 36597049Speter if (!ignore_tstp) { 36697049Speter static sigaction_t act, oact; 36750276Speter 36897049Speter if (!enable) { 36997049Speter act.sa_handler = SIG_IGN; 37097049Speter sigaction(SIGTSTP, &act, &oact); 37197049Speter } else if (act.sa_handler != SIG_DFL) { 37297049Speter sigaction(SIGTSTP, &oact, NULL); 37397049Speter } else if (sigaction(SIGTSTP, NULL, &oact) == 0 37497049Speter && (oact.sa_handler == SIG_DFL)) { 37597049Speter sigemptyset(&act.sa_mask); 37650276Speter#ifdef SA_RESTART 37797049Speter act.sa_flags |= SA_RESTART; 37850276Speter#endif /* SA_RESTART */ 37997049Speter act.sa_handler = tstp; 38097049Speter (void) sigaction(SIGTSTP, &act, NULL); 38197049Speter } else { 38297049Speter ignore_tstp = TRUE; 38397049Speter } 38450276Speter } 38566963Speter } 38697049Speter#endif /* !USE_SIGTSTP */ 38750276Speter 38897049Speter if (!initialized) { 38997049Speter if (enable) { 39097049Speter CatchIfDefault(SIGINT, cleanup); 39197049Speter CatchIfDefault(SIGTERM, cleanup); 39250276Speter#if USE_SIGWINCH 39397049Speter CatchIfDefault(SIGWINCH, sigwinch); 39450276Speter#endif 39597049Speter initialized = TRUE; 39697049Speter } 39766963Speter } 39897049Speter returnVoid; 39950276Speter} 400