lib_tstp.c revision 50276
1193323Sed/**************************************************************************** 2193323Sed * Copyright (c) 1998,1999 Free Software Foundation, Inc. * 3193323Sed * * 4193323Sed * Permission is hereby granted, free of charge, to any person obtaining a * 5193323Sed * copy of this software and associated documentation files (the * 6193323Sed * "Software"), to deal in the Software without restriction, including * 7193323Sed * without limitation the rights to use, copy, modify, merge, publish, * 8193323Sed * distribute, distribute with modifications, sublicense, and/or sell * 9193323Sed * copies of the Software, and to permit persons to whom the Software is * 10193323Sed * furnished to do so, subject to the following conditions: * 11193323Sed * * 12193323Sed * The above copyright notice and this permission notice shall be included * 13193323Sed * in all copies or substantial portions of the Software. * 14193323Sed * * 15193323Sed * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16193323Sed * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17193323Sed * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18193323Sed * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19193323Sed * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20193323Sed * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21249423Sdim * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22198090Srdivacky * * 23249423Sdim * Except as contained in this notice, the name(s) of the above copyright * 24249423Sdim * holders shall not be used in advertising or otherwise to promote the * 25194612Sed * sale, use or other dealings in this Software without prior written * 26193323Sed * authorization. * 27193323Sed ****************************************************************************/ 28193323Sed 29193323Sed/**************************************************************************** 30198090Srdivacky * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 31193323Sed * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32218893Sdim ****************************************************************************/ 33193323Sed 34193323Sed 35193323Sed/* 36193323Sed** lib_tstp.c 37206274Srdivacky** 38205218Srdivacky** The routine _nc_signal_handler(). 39203954Srdivacky** 40193323Sed*/ 41193323Sed 42218893Sdim#include <curses.priv.h> 43193323Sed 44193323Sed#include <signal.h> 45193323Sed#include <SigAction.h> 46193323Sed 47198090Srdivacky#if defined(SVR4_ACTION) && !defined(_POSIX_SOURCE) 48193323Sed#define _POSIX_SOURCE 49193323Sed#endif 50193323Sed 51193323SedMODULE_ID("$Id: lib_tstp.c,v 1.19 1999/07/24 22:47:20 tom Exp $") 52193323Sed 53193323Sed#if defined(SIGTSTP) && (HAVE_SIGACTION || HAVE_SIGVEC) 54193323Sed#define USE_SIGTSTP 1 55193323Sed#else 56193323Sed#define USE_SIGTSTP 0 57193323Sed#endif 58193323Sed 59193323Sed/* 60193323Sed * Note: This code is fragile! Its problem is that different OSs 61193323Sed * handle restart of system calls interrupted by signals differently. 62193323Sed * The ncurses code needs signal-call restart to happen -- otherwise, 63193323Sed * interrupted wgetch() calls will return FAIL, probably making the 64193323Sed * application think the input stream has ended and it should 65193323Sed * terminate. In particular, you know you have this problem if, when 66193323Sed * you suspend an ncurses-using lynx with ^Z and resume, it dies 67193323Sed * immediately. 68193323Sed * 69193323Sed * Default behavior of POSIX sigaction(2) is not to restart 70193323Sed * interrupted system calls, but Linux's sigaction does it anyway (at 71193323Sed * least, on and after the 1.1.47 I (esr) use). Thus this code works 72198090Srdivacky * OK under Linux. The 4.4BSD sigaction(2) supports a (non-portable) 73193323Sed * SA_RESTART flag that forces the right behavior. Thus, this code 74193323Sed * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it 75198090Srdivacky * does not). 76207618Srdivacky * 77193323Sed * Stock System Vs (and anything else using a strict-POSIX 78205218Srdivacky * sigaction(2) without SA_RESTART) may have a problem. Possible 79206274Srdivacky * solutions: 80218893Sdim * 81206274Srdivacky * sigvec restarts by default (SV_INTERRUPT flag to not restart) 82193323Sed * signal restarts by default in SVr4 (assuming you link with -lucb) 83193323Sed * and BSD, but not SVr3. 84193323Sed * sigset restarts, but is only available under SVr4/Solaris. 85193323Sed * 86193323Sed * The signal(3) call is mandated by the ANSI standard, and its 87193323Sed * interaction with sigaction(2) is described in the POSIX standard 88193323Sed * (3.3.4.2, page 72,line 934). According to section 8.1, page 191, 89193323Sed * however, signal(3) itself is not required by POSIX.1. And POSIX is 90193323Sed * silent on whether it is required to restart signals. 91193323Sed * 92193323Sed * So. The present situation is, we use sigaction(2) with no 93193323Sed * guarantee of restart anywhere but on Linux and BSD. We could 94193323Sed * switch to signal(3) and collar Linux, BSD, and SVr4. Any way 95193323Sed * we slice it, System V UNIXes older than SVr4 will probably lose 96193323Sed * (this may include XENIX). 97193323Sed * 98193323Sed * This implementation will probably be changed to use signal(3) in 99193323Sed * the future. If nothing else, it's simpler... 100193323Sed */ 101193323Sed 102193323Sed#if USE_SIGTSTP 103193323Sedstatic void tstp(int dummy GCC_UNUSED) 104193323Sed{ 105193323Sed sigset_t mask, omask; 106193323Sed sigaction_t act, oact; 107193323Sed 108193323Sed#ifdef SIGTTOU 109249423Sdim int sigttou_blocked; 110249423Sdim#endif 111249423Sdim 112193323Sed T(("tstp() called")); 113193323Sed 114193323Sed /* 115193323Sed * The user may have changed the prog_mode tty bits, so save them. 116193323Sed * 117193323Sed * But first try to detect whether we still are in the foreground 118193323Sed * process group - if not, an interactive shell may already have 119203954Srdivacky * taken ownership of the tty and modified the settings when our 120203954Srdivacky * parent was stopped before us, and we would likely pick up the 121203954Srdivacky * settings already modified by the shell. 122203954Srdivacky */ 123203954Srdivacky if (SP != 0 && !SP->_endwin) /* don't do this if we're not in curses */ 124208599Srdivacky#if HAVE_TCGETPGRP 125195340Sed if (tcgetpgrp(STDIN_FILENO) == getpgrp()) 126195340Sed#endif 127234353Sdim def_prog_mode(); 128234353Sdim 129234353Sdim /* 130234353Sdim * Block window change and timer signals. The latter 131234353Sdim * is because applications use timers to decide when 132234353Sdim * to repaint the screen. 133208599Srdivacky */ 134263765Sdim (void)sigemptyset(&mask); 135263765Sdim (void)sigaddset(&mask, SIGALRM); 136249423Sdim#if USE_SIGWINCH 137243830Sdim (void)sigaddset(&mask, SIGWINCH); 138243830Sdim#endif 139193323Sed (void)sigprocmask(SIG_BLOCK, &mask, &omask); 140207618Srdivacky 141218893Sdim#ifdef SIGTTOU 142218893Sdim sigttou_blocked = sigismember(&omask, SIGTTOU); 143193323Sed if (!sigttou_blocked) { 144193323Sed (void)sigemptyset(&mask); 145206274Srdivacky (void)sigaddset(&mask, SIGTTOU); 146218893Sdim (void)sigprocmask(SIG_BLOCK, &mask, NULL); 147205218Srdivacky } 148243830Sdim#endif 149193323Sed 150193323Sed /* 151207618Srdivacky * End window mode, which also resets the terminal state to the 152193323Sed * original (pre-curses) modes. 153243830Sdim */ 154243830Sdim endwin(); 155243830Sdim 156243830Sdim /* Unblock SIGTSTP. */ 157203954Srdivacky (void)sigemptyset(&mask); 158203954Srdivacky (void)sigaddset(&mask, SIGTSTP); 159203954Srdivacky#ifdef SIGTTOU 160243830Sdim if (!sigttou_blocked) { 161193323Sed /* Unblock this too if it wasn't blocked on entry */ 162193323Sed (void)sigaddset(&mask, SIGTTOU); 163193323Sed } 164193323Sed#endif 165193323Sed (void)sigprocmask(SIG_UNBLOCK, &mask, NULL); 166193323Sed 167193323Sed /* Now we want to resend SIGSTP to this process and suspend it */ 168193323Sed act.sa_handler = SIG_DFL; 169193323Sed sigemptyset(&act.sa_mask); 170193323Sed act.sa_flags = 0; 171193323Sed#ifdef SA_RESTART 172193323Sed act.sa_flags |= SA_RESTART; 173193323Sed#endif /* SA_RESTART */ 174193323Sed sigaction(SIGTSTP, &act, &oact); 175193323Sed kill(getpid(), SIGTSTP); 176193323Sed 177193323Sed /* Process gets suspended...time passes...process resumes */ 178203954Srdivacky 179203954Srdivacky T(("SIGCONT received")); 180203954Srdivacky sigaction(SIGTSTP, &oact, NULL); 181203954Srdivacky flushinp(); 182193323Sed 183203954Srdivacky /* 184203954Srdivacky * If the user modified the tty state while suspended, he wants 185203954Srdivacky * those changes to stick. So save the new "default" terminal state. 186203954Srdivacky */ 187203954Srdivacky def_shell_mode(); 188193323Sed 189193323Sed /* 190193323Sed * This relies on the fact that doupdate() will restore the 191193323Sed * program-mode tty state, and issue enter_ca_mode if need be. 192193323Sed */ 193193323Sed doupdate(); 194193323Sed 195195340Sed /* Reset the signals. */ 196195340Sed (void)sigprocmask(SIG_SETMASK, &omask, NULL); 197195340Sed} 198195340Sed#endif /* USE_SIGTSTP */ 199195340Sed 200195340Sedstatic void cleanup(int sig) 201195340Sed{ 202195340Sed /* 203239462Sdim * Actually, doing any sort of I/O from within an signal handler is 204239462Sdim * "unsafe". But we'll _try_ to clean up the screen and terminal 205203954Srdivacky * settings on the way out. 206203954Srdivacky */ 207208599Srdivacky if (sig == SIGINT 208234353Sdim || sig == SIGQUIT) { 209234353Sdim#if HAVE_SIGACTION || HAVE_SIGVEC 210234353Sdim sigaction_t act; 211234353Sdim sigemptyset(&act.sa_mask); 212234353Sdim act.sa_flags = 0; 213208599Srdivacky act.sa_handler = SIG_IGN; 214208599Srdivacky if (sigaction(sig, &act, (sigaction_t *)0) == 0) 215234353Sdim#else 216234353Sdim if (signal(sig, SIG_IGN) != SIG_ERR) 217234353Sdim#endif 218234353Sdim { 219208599Srdivacky SCREEN *scan = _nc_screen_chain; 220249423Sdim while(scan) 221263765Sdim { 222263765Sdim set_term(scan); 223263765Sdim endwin(); 224249423Sdim if (SP) 225249423Sdim SP->_endwin = FALSE; /* in case we have an atexit! */ 226263765Sdim scan = scan->_next_screen; 227263765Sdim } 228263765Sdim } 229249423Sdim } 230203954Srdivacky exit(EXIT_FAILURE); 231198090Srdivacky} 232198090Srdivacky 233193323Sed#if USE_SIGWINCH 234193323Sedstatic void sigwinch(int sig GCC_UNUSED) 235193323Sed{ 236193323Sed SCREEN *scan = _nc_screen_chain; 237193323Sed while(scan) 238193323Sed { 239193323Sed scan->_sig_winch = TRUE; 240193323Sed scan = scan->_next_screen; 241193323Sed } 242193323Sed} 243193323Sed#endif /* USE_SIGWINCH */ 244193323Sed 245193323Sed/* 246193323Sed * If the given signal is still in its default state, set it to the given 247193323Sed * handler. 248193323Sed */ 249193323Sed#if HAVE_SIGACTION || HAVE_SIGVEC 250193323Sedstatic int CatchIfDefault(int sig, sigaction_t *act) 251193323Sed{ 252193323Sed sigaction_t old_act; 253193323Sed 254193323Sed if (sigaction(sig, (sigaction_t *)0, &old_act) == 0 255193323Sed && (old_act.sa_handler == SIG_DFL 256193323Sed#if USE_SIGWINCH 257193323Sed || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN) 258193323Sed#endif 259193323Sed )) { 260193323Sed (void)sigaction(sig, act, (sigaction_t *)0); 261193323Sed return TRUE; 262193323Sed } 263193323Sed return FALSE; 264193323Sed} 265193323Sed#else 266193323Sedstatic int CatchIfDefault(int sig, RETSIGTYPE (*handler)(int)) 267193323Sed{ 268193323Sed void (*ohandler)(int); 269193323Sed 270193323Sed ohandler = signal(sig, SIG_IGN); 271193323Sed if (ohandler == SIG_DFL 272193323Sed#if USE_SIGWINCH 273193323Sed || (sig == SIGWINCH && ohandler == SIG_IGN) 274193323Sed#endif 275193323Sed ) { 276218893Sdim signal(sig, handler); 277193323Sed return TRUE; 278193323Sed } else { 279193323Sed signal(sig, ohandler); 280193323Sed return FALSE; 281193323Sed } 282193323Sed} 283193323Sed#endif 284193323Sed 285193323Sed/* 286193323Sed * This is invoked once at the beginning (e.g., from 'initscr()'), to 287193323Sed * initialize the signal catchers, and thereafter when spawning a shell (and 288193323Sed * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher. 289193323Sed * 290193323Sed * If the application has already set one of the signals, we'll not modify it 291193323Sed * (during initialization). 292193323Sed * 293193323Sed * The XSI document implies that we shouldn't keep the SIGTSTP handler if 294193323Sed * the caller later changes its mind, but that doesn't seem correct. 295193323Sed */ 296193323Sedvoid _nc_signal_handler(bool enable) 297199481Srdivacky{ 298199481Srdivacky#if USE_SIGTSTP /* Xenix 2.x doesn't have SIGTSTP, for example */ 299218893Sdimstatic sigaction_t act, oact; 300199481Srdivackystatic int ignore; 301193323Sed 302193323Sed if (!ignore) 303193323Sed { 304193323Sed if (!enable) 305193323Sed { 306193323Sed act.sa_handler = SIG_IGN; 307193323Sed sigaction(SIGTSTP, &act, &oact); 308193323Sed } 309219077Sdim else if (act.sa_handler) 310193323Sed { 311193323Sed sigaction(SIGTSTP, &oact, NULL); 312193323Sed } 313193323Sed else /*initialize */ 314193323Sed { 315193323Sed sigemptyset(&act.sa_mask); 316193323Sed act.sa_flags = 0; 317193323Sed#if USE_SIGWINCH 318193323Sed act.sa_handler = sigwinch; 319193323Sed CatchIfDefault(SIGWINCH, &act); 320193323Sed#endif 321193323Sed 322193323Sed#ifdef SA_RESTART 323193323Sed act.sa_flags |= SA_RESTART; 324193323Sed#endif /* SA_RESTART */ 325193323Sed act.sa_handler = cleanup; 326193323Sed CatchIfDefault(SIGINT, &act); 327193323Sed CatchIfDefault(SIGTERM, &act); 328193323Sed 329193323Sed act.sa_handler = tstp; 330193323Sed if (!CatchIfDefault(SIGTSTP, &act)) 331193323Sed ignore = TRUE; 332193323Sed } 333193323Sed } 334193323Sed#else /* !USE_SIGTSTP */ 335193323Sed if (enable) 336193323Sed { 337193323Sed#if HAVE_SIGACTION || HAVE_SIGVEC 338193323Sed static sigaction_t act; 339198396Srdivacky sigemptyset(&act.sa_mask); 340198396Srdivacky#if USE_SIGWINCH 341198396Srdivacky act.sa_handler = sigwinch; 342193323Sed CatchIfDefault(SIGWINCH, &act); 343193323Sed#endif 344193323Sed#ifdef SA_RESTART 345193323Sed act.sa_flags |= SA_RESTART; 346193323Sed#endif /* SA_RESTART */ 347193323Sed act.sa_handler = cleanup; 348193323Sed CatchIfDefault(SIGINT, &act); 349193323Sed CatchIfDefault(SIGTERM, &act); 350193323Sed 351193323Sed#else /* !(HAVE_SIGACTION || HAVE_SIGVEC) */ 352193323Sed 353193323Sed CatchIfDefault(SIGINT, cleanup); 354251662Sdim CatchIfDefault(SIGTERM, cleanup); 355251662Sdim#if USE_SIGWINCH 356193323Sed CatchIfDefault(SIGWINCH, sigwinch); 357193323Sed#endif 358193323Sed#endif /* !(HAVE_SIGACTION || HAVE_SIGVEC) */ 359193323Sed } 360193323Sed#endif /* !USE_SIGTSTP */ 361193323Sed} 362193323Sed