lib_tstp.c revision 178866
1/**************************************************************************** 2 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc. * 3 * * 4 * Permission is hereby granted, free of charge, to any person obtaining a * 5 * copy of this software and associated documentation files (the * 6 * "Software"), to deal in the Software without restriction, including * 7 * without limitation the rights to use, copy, modify, merge, publish, * 8 * distribute, distribute with modifications, sublicense, and/or sell * 9 * copies of the Software, and to permit persons to whom the Software is * 10 * furnished to do so, subject to the following conditions: * 11 * * 12 * The above copyright notice and this permission notice shall be included * 13 * in all copies or substantial portions of the Software. * 14 * * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 * * 23 * Except as contained in this notice, the name(s) of the above copyright * 24 * holders shall not be used in advertising or otherwise to promote the * 25 * sale, use or other dealings in this Software without prior written * 26 * authorization. * 27 ****************************************************************************/ 28 29/**************************************************************************** 30 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 31 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 32 * and: Thomas E. Dickey 1995-on * 33 ****************************************************************************/ 34 35/* 36** lib_tstp.c 37** 38** The routine _nc_signal_handler(). 39** 40*/ 41#include <curses.priv.h> 42 43#include <SigAction.h> 44 45#if SVR4_ACTION && !defined(_POSIX_SOURCE) 46#define _POSIX_SOURCE 47#endif 48 49MODULE_ID("$Id: lib_tstp.c,v 1.37 2008/05/03 16:24:56 tom Exp $") 50 51#if defined(SIGTSTP) && (HAVE_SIGACTION || HAVE_SIGVEC) 52#define USE_SIGTSTP 1 53#else 54#define USE_SIGTSTP 0 55#endif 56 57#ifdef TRACE 58static const char * 59signal_name(int sig) 60{ 61 switch (sig) { 62 case SIGALRM: 63 return "SIGALRM"; 64#ifdef SIGCONT 65 case SIGCONT: 66 return "SIGCONT"; 67#endif 68 case SIGINT: 69 return "SIGINT"; 70 case SIGQUIT: 71 return "SIGQUIT"; 72 case SIGTERM: 73 return "SIGTERM"; 74#ifdef SIGTSTP 75 case SIGTSTP: 76 return "SIGTSTP"; 77#endif 78#ifdef SIGTTOU 79 case SIGTTOU: 80 return "SIGTTOU"; 81#endif 82#ifdef SIGWINCH 83 case SIGWINCH: 84 return "SIGWINCH"; 85#endif 86 default: 87 return "unknown signal"; 88 } 89} 90#endif 91 92/* 93 * Note: This code is fragile! Its problem is that different OSs 94 * handle restart of system calls interrupted by signals differently. 95 * The ncurses code needs signal-call restart to happen -- otherwise, 96 * interrupted wgetch() calls will return FAIL, probably making the 97 * application think the input stream has ended and it should 98 * terminate. In particular, you know you have this problem if, when 99 * you suspend an ncurses-using lynx with ^Z and resume, it dies 100 * immediately. 101 * 102 * Default behavior of POSIX sigaction(2) is not to restart 103 * interrupted system calls, but Linux's sigaction does it anyway (at 104 * least, on and after the 1.1.47 I (esr) use). Thus this code works 105 * OK under Linux. The 4.4BSD sigaction(2) supports a (non-portable) 106 * SA_RESTART flag that forces the right behavior. Thus, this code 107 * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it 108 * does not). 109 * 110 * Stock System Vs (and anything else using a strict-POSIX 111 * sigaction(2) without SA_RESTART) may have a problem. Possible 112 * solutions: 113 * 114 * sigvec restarts by default (SV_INTERRUPT flag to not restart) 115 * signal restarts by default in SVr4 (assuming you link with -lucb) 116 * and BSD, but not SVr3. 117 * sigset restarts, but is only available under SVr4/Solaris. 118 * 119 * The signal(3) call is mandated by the ANSI standard, and its 120 * interaction with sigaction(2) is described in the POSIX standard 121 * (3.3.4.2, page 72,line 934). According to section 8.1, page 191, 122 * however, signal(3) itself is not required by POSIX.1. And POSIX is 123 * silent on whether it is required to restart signals. 124 * 125 * So. The present situation is, we use sigaction(2) with no 126 * guarantee of restart anywhere but on Linux and BSD. We could 127 * switch to signal(3) and collar Linux, BSD, and SVr4. Any way 128 * we slice it, System V UNIXes older than SVr4 will probably lose 129 * (this may include XENIX). 130 * 131 * This implementation will probably be changed to use signal(3) in 132 * the future. If nothing else, it's simpler... 133 */ 134 135#if USE_SIGTSTP 136static void 137tstp(int dummy GCC_UNUSED) 138{ 139 sigset_t mask, omask; 140 sigaction_t act, oact; 141 142#ifdef SIGTTOU 143 int sigttou_blocked; 144#endif 145 146 T(("tstp() called")); 147 148 /* 149 * The user may have changed the prog_mode tty bits, so save them. 150 * 151 * But first try to detect whether we still are in the foreground 152 * process group - if not, an interactive shell may already have 153 * taken ownership of the tty and modified the settings when our 154 * parent was stopped before us, and we would likely pick up the 155 * settings already modified by the shell. 156 */ 157 if (SP != 0 && !SP->_endwin) /* don't do this if we're not in curses */ 158#if HAVE_TCGETPGRP 159 if (tcgetpgrp(STDIN_FILENO) == getpgrp()) 160#endif 161 def_prog_mode(); 162 163 /* 164 * Block window change and timer signals. The latter 165 * is because applications use timers to decide when 166 * to repaint the screen. 167 */ 168 (void) sigemptyset(&mask); 169 (void) sigaddset(&mask, SIGALRM); 170#if USE_SIGWINCH 171 (void) sigaddset(&mask, SIGWINCH); 172#endif 173 (void) sigprocmask(SIG_BLOCK, &mask, &omask); 174 175#ifdef SIGTTOU 176 sigttou_blocked = sigismember(&omask, SIGTTOU); 177 if (!sigttou_blocked) { 178 (void) sigemptyset(&mask); 179 (void) sigaddset(&mask, SIGTTOU); 180 (void) sigprocmask(SIG_BLOCK, &mask, NULL); 181 } 182#endif 183 184 /* 185 * End window mode, which also resets the terminal state to the 186 * original (pre-curses) modes. 187 */ 188 endwin(); 189 190 /* Unblock SIGTSTP. */ 191 (void) sigemptyset(&mask); 192 (void) sigaddset(&mask, SIGTSTP); 193#ifdef SIGTTOU 194 if (!sigttou_blocked) { 195 /* Unblock this too if it wasn't blocked on entry */ 196 (void) sigaddset(&mask, SIGTTOU); 197 } 198#endif 199 (void) sigprocmask(SIG_UNBLOCK, &mask, NULL); 200 201 /* Now we want to resend SIGSTP to this process and suspend it */ 202 act.sa_handler = SIG_DFL; 203 sigemptyset(&act.sa_mask); 204 act.sa_flags = 0; 205#ifdef SA_RESTART 206 act.sa_flags |= SA_RESTART; 207#endif /* SA_RESTART */ 208 sigaction(SIGTSTP, &act, &oact); 209 kill(getpid(), SIGTSTP); 210 211 /* Process gets suspended...time passes...process resumes */ 212 213 T(("SIGCONT received")); 214 sigaction(SIGTSTP, &oact, NULL); 215 flushinp(); 216 217 /* 218 * If the user modified the tty state while suspended, he wants 219 * those changes to stick. So save the new "default" terminal state. 220 */ 221 def_shell_mode(); 222 223 /* 224 * This relies on the fact that doupdate() will restore the 225 * program-mode tty state, and issue enter_ca_mode if need be. 226 */ 227 doupdate(); 228 229 /* Reset the signals. */ 230 (void) sigprocmask(SIG_SETMASK, &omask, NULL); 231} 232#endif /* USE_SIGTSTP */ 233 234static void 235cleanup(int sig) 236{ 237 /* 238 * Actually, doing any sort of I/O from within an signal handler is 239 * "unsafe". But we'll _try_ to clean up the screen and terminal 240 * settings on the way out. 241 */ 242 if (!_nc_globals.cleanup_nested++ 243 && (sig == SIGINT 244 || sig == SIGQUIT)) { 245#if HAVE_SIGACTION || HAVE_SIGVEC 246 sigaction_t act; 247 sigemptyset(&act.sa_mask); 248 act.sa_flags = 0; 249 act.sa_handler = SIG_IGN; 250 if (sigaction(sig, &act, NULL) == 0) 251#else 252 if (signal(sig, SIG_IGN) != SIG_ERR) 253#endif 254 { 255 SCREEN *scan; 256 for (each_screen(scan)) { 257 if (scan->_ofp != 0 258 && isatty(fileno(scan->_ofp))) { 259 scan->_cleanup = TRUE; 260 scan->_outch = _nc_outch; 261 } 262 set_term(scan); 263 endwin(); 264 if (SP) 265 SP->_endwin = FALSE; /* in case we have an atexit! */ 266 } 267 } 268 } 269 exit(EXIT_FAILURE); 270} 271 272#if USE_SIGWINCH 273static void 274sigwinch(int sig GCC_UNUSED) 275{ 276 _nc_globals.have_sigwinch = 1; 277} 278#endif /* USE_SIGWINCH */ 279 280/* 281 * If the given signal is still in its default state, set it to the given 282 * handler. 283 */ 284static int 285CatchIfDefault(int sig, RETSIGTYPE (*handler) (int)) 286{ 287 int result; 288#if HAVE_SIGACTION || HAVE_SIGVEC 289 sigaction_t old_act; 290 sigaction_t new_act; 291 292 memset(&new_act, 0, sizeof(new_act)); 293 sigemptyset(&new_act.sa_mask); 294#ifdef SA_RESTART 295#ifdef SIGWINCH 296 if (sig != SIGWINCH) 297#endif 298 new_act.sa_flags |= SA_RESTART; 299#endif /* SA_RESTART */ 300 new_act.sa_handler = handler; 301 302 if (sigaction(sig, NULL, &old_act) == 0 303 && (old_act.sa_handler == SIG_DFL 304 || old_act.sa_handler == handler 305#if USE_SIGWINCH 306 || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN) 307#endif 308 )) { 309 (void) sigaction(sig, &new_act, NULL); 310 result = TRUE; 311 } else { 312 result = FALSE; 313 } 314#else /* !HAVE_SIGACTION */ 315 RETSIGTYPE (*ohandler) (int); 316 317 ohandler = signal(sig, SIG_IGN); 318 if (ohandler == SIG_DFL 319 || ohandler == handler 320#if USE_SIGWINCH 321 || (sig == SIGWINCH && ohandler == SIG_IGN) 322#endif 323 ) { 324 signal(sig, handler); 325 result = TRUE; 326 } else { 327 signal(sig, ohandler); 328 result = FALSE; 329 } 330#endif 331 T(("CatchIfDefault - will %scatch %s", 332 result ? "" : "not ", signal_name(sig))); 333 return result; 334} 335 336/* 337 * This is invoked once at the beginning (e.g., from 'initscr()'), to 338 * initialize the signal catchers, and thereafter when spawning a shell (and 339 * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher. 340 * 341 * If the application has already set one of the signals, we'll not modify it 342 * (during initialization). 343 * 344 * The XSI document implies that we shouldn't keep the SIGTSTP handler if 345 * the caller later changes its mind, but that doesn't seem correct. 346 */ 347NCURSES_EXPORT(void) 348_nc_signal_handler(bool enable) 349{ 350 T((T_CALLED("_nc_signal_handler(%d)"), enable)); 351#if USE_SIGTSTP /* Xenix 2.x doesn't have SIGTSTP, for example */ 352 { 353 static bool ignore_tstp = FALSE; 354 355 if (!ignore_tstp) { 356 static sigaction_t new_sigaction, old_sigaction; 357 358 if (!enable) { 359 new_sigaction.sa_handler = SIG_IGN; 360 sigaction(SIGTSTP, &new_sigaction, &old_sigaction); 361 } else if (new_sigaction.sa_handler != SIG_DFL) { 362 sigaction(SIGTSTP, &old_sigaction, NULL); 363 } else if (sigaction(SIGTSTP, NULL, &old_sigaction) == 0 364 && (old_sigaction.sa_handler == SIG_DFL)) { 365 sigemptyset(&new_sigaction.sa_mask); 366#ifdef SA_RESTART 367 new_sigaction.sa_flags |= SA_RESTART; 368#endif /* SA_RESTART */ 369 new_sigaction.sa_handler = tstp; 370 (void) sigaction(SIGTSTP, &new_sigaction, NULL); 371 } else { 372 ignore_tstp = TRUE; 373 } 374 } 375 } 376#endif /* !USE_SIGTSTP */ 377 378 if (!_nc_globals.init_signals) { 379 if (enable) { 380 CatchIfDefault(SIGINT, cleanup); 381 CatchIfDefault(SIGTERM, cleanup); 382#if USE_SIGWINCH 383 CatchIfDefault(SIGWINCH, sigwinch); 384#endif 385 _nc_globals.init_signals = TRUE; 386 } 387 } 388 returnVoid; 389} 390