1/**************************************************************************** 2 * Copyright (c) 1998-2001,2002 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 Dickey 1995-2001 * 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.30 2002/05/18 19:55:38 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 static int nested; 238 239 /* 240 * Actually, doing any sort of I/O from within an signal handler is 241 * "unsafe". But we'll _try_ to clean up the screen and terminal 242 * settings on the way out. 243 */ 244 if (!nested++ 245 && (sig == SIGINT 246 || sig == SIGQUIT)) { 247#if HAVE_SIGACTION || HAVE_SIGVEC 248 sigaction_t act; 249 sigemptyset(&act.sa_mask); 250 act.sa_flags = 0; 251 act.sa_handler = SIG_IGN; 252 if (sigaction(sig, &act, NULL) == 0) 253#else 254 if (signal(sig, SIG_IGN) != SIG_ERR) 255#endif 256 { 257 SCREEN *scan = _nc_screen_chain; 258 while (scan) { 259 if (SP != 0 260 && SP->_ofp != 0 261 && isatty(fileno(SP->_ofp))) { 262 SP->_cleanup = TRUE; 263 SP->_outch = _nc_outch; 264 } 265 set_term(scan); 266 endwin(); 267 if (SP) 268 SP->_endwin = FALSE; /* in case we have an atexit! */ 269 scan = scan->_next_screen; 270 } 271 } 272 } 273 exit(EXIT_FAILURE); 274} 275 276#if USE_SIGWINCH 277static void 278sigwinch(int sig GCC_UNUSED) 279{ 280 SCREEN *scan = _nc_screen_chain; 281 while (scan) { 282 scan->_sig_winch = TRUE; 283 scan = scan->_next_screen; 284 } 285} 286#endif /* USE_SIGWINCH */ 287 288/* 289 * If the given signal is still in its default state, set it to the given 290 * handler. 291 */ 292static int 293CatchIfDefault(int sig, RETSIGTYPE (*handler) (int)) 294{ 295 int result; 296#if HAVE_SIGACTION || HAVE_SIGVEC 297 sigaction_t old_act; 298 sigaction_t new_act; 299 300 memset(&new_act, 0, sizeof(new_act)); 301 sigemptyset(&new_act.sa_mask); 302#ifdef SA_RESTART 303#ifdef SIGWINCH 304 if (sig != SIGWINCH) 305#endif 306 new_act.sa_flags |= SA_RESTART; 307#endif /* SA_RESTART */ 308 new_act.sa_handler = handler; 309 310 if (sigaction(sig, NULL, &old_act) == 0 311 && (old_act.sa_handler == SIG_DFL 312 || old_act.sa_handler == handler 313#if USE_SIGWINCH 314 || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN) 315#endif 316 )) { 317 (void) sigaction(sig, &new_act, NULL); 318 result = TRUE; 319 } else { 320 result = FALSE; 321 } 322#else /* !HAVE_SIGACTION */ 323 RETSIGTYPE (*ohandler) (int); 324 325 ohandler = signal(sig, SIG_IGN); 326 if (ohandler == SIG_DFL 327 || ohandler == handler 328#if USE_SIGWINCH 329 || (sig == SIGWINCH && ohandler == SIG_IGN) 330#endif 331 ) { 332 signal(sig, handler); 333 result = TRUE; 334 } else { 335 signal(sig, ohandler); 336 result = FALSE; 337 } 338#endif 339 T(("CatchIfDefault - will %scatch %s", 340 result ? "" : "not ", signal_name(sig))); 341 return result; 342} 343 344/* 345 * This is invoked once at the beginning (e.g., from 'initscr()'), to 346 * initialize the signal catchers, and thereafter when spawning a shell (and 347 * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher. 348 * 349 * If the application has already set one of the signals, we'll not modify it 350 * (during initialization). 351 * 352 * The XSI document implies that we shouldn't keep the SIGTSTP handler if 353 * the caller later changes its mind, but that doesn't seem correct. 354 */ 355NCURSES_EXPORT(void) 356_nc_signal_handler(bool enable) 357{ 358 static bool initialized = FALSE; 359 360 T((T_CALLED("_nc_signal_handler(%d)"), enable)); 361#if USE_SIGTSTP /* Xenix 2.x doesn't have SIGTSTP, for example */ 362 { 363 static bool ignore_tstp = FALSE; 364 365 if (!ignore_tstp) { 366 static sigaction_t act, oact; 367 368 if (!enable) { 369 act.sa_handler = SIG_IGN; 370 sigaction(SIGTSTP, &act, &oact); 371 } else if (act.sa_handler != SIG_DFL) { 372 sigaction(SIGTSTP, &oact, NULL); 373 } else if (sigaction(SIGTSTP, NULL, &oact) == 0 374 && (oact.sa_handler == SIG_DFL)) { 375 sigemptyset(&act.sa_mask); 376#ifdef SA_RESTART 377 act.sa_flags |= SA_RESTART; 378#endif /* SA_RESTART */ 379 act.sa_handler = tstp; 380 (void) sigaction(SIGTSTP, &act, NULL); 381 } else { 382 ignore_tstp = TRUE; 383 } 384 } 385 } 386#endif /* !USE_SIGTSTP */ 387 388 if (!initialized) { 389 if (enable) { 390 CatchIfDefault(SIGINT, cleanup); 391 CatchIfDefault(SIGTERM, cleanup); 392#if USE_SIGWINCH 393 CatchIfDefault(SIGWINCH, sigwinch); 394#endif 395 initialized = TRUE; 396 } 397 } 398 returnVoid; 399} 400