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