1 /* $OpenBSD: lib_tstp.c,v 1.6 2000/06/19 03:53:54 millert Exp $ */ 2 3 /**************************************************************************** 4 * Copyright (c) 1998,1999 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 /* 38 ** lib_tstp.c 39 ** 40 ** The routine _nc_signal_handler(). 41 ** 42 */ 43 44 #include <curses.priv.h> 45 46 #include <signal.h> 47 #include <SigAction.h> 48 49 #if defined(SVR4_ACTION) && !defined(_POSIX_SOURCE) 50 #define _POSIX_SOURCE 51 #endif 52 53 MODULE_ID("$From: lib_tstp.c,v 1.21 2000/05/20 23:28:56 tom Exp $") 54 55 #if defined(SIGTSTP) && (HAVE_SIGACTION || HAVE_SIGVEC) 56 #define USE_SIGTSTP 1 57 #else 58 #define USE_SIGTSTP 0 59 #endif 60 61 /* 62 * Note: This code is fragile! Its problem is that different OSs 63 * handle restart of system calls interrupted by signals differently. 64 * The ncurses code needs signal-call restart to happen -- otherwise, 65 * interrupted wgetch() calls will return FAIL, probably making the 66 * application think the input stream has ended and it should 67 * terminate. In particular, you know you have this problem if, when 68 * you suspend an ncurses-using lynx with ^Z and resume, it dies 69 * immediately. 70 * 71 * Default behavior of POSIX sigaction(2) is not to restart 72 * interrupted system calls, but Linux's sigaction does it anyway (at 73 * least, on and after the 1.1.47 I (esr) use). Thus this code works 74 * OK under Linux. The 4.4BSD sigaction(2) supports a (non-portable) 75 * SA_RESTART flag that forces the right behavior. Thus, this code 76 * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it 77 * does not). 78 * 79 * Stock System Vs (and anything else using a strict-POSIX 80 * sigaction(2) without SA_RESTART) may have a problem. Possible 81 * solutions: 82 * 83 * sigvec restarts by default (SV_INTERRUPT flag to not restart) 84 * signal restarts by default in SVr4 (assuming you link with -lucb) 85 * and BSD, but not SVr3. 86 * sigset restarts, but is only available under SVr4/Solaris. 87 * 88 * The signal(3) call is mandated by the ANSI standard, and its 89 * interaction with sigaction(2) is described in the POSIX standard 90 * (3.3.4.2, page 72,line 934). According to section 8.1, page 191, 91 * however, signal(3) itself is not required by POSIX.1. And POSIX is 92 * silent on whether it is required to restart signals. 93 * 94 * So. The present situation is, we use sigaction(2) with no 95 * guarantee of restart anywhere but on Linux and BSD. We could 96 * switch to signal(3) and collar Linux, BSD, and SVr4. Any way 97 * we slice it, System V UNIXes older than SVr4 will probably lose 98 * (this may include XENIX). 99 * 100 * This implementation will probably be changed to use signal(3) in 101 * the future. If nothing else, it's simpler... 102 */ 103 104 #if USE_SIGTSTP 105 static void 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 cleanup(int sig) 203 { 204 static int nested; 205 206 /* 207 * Actually, doing any sort of I/O from within an signal handler is 208 * "unsafe". But we'll _try_ to clean up the screen and terminal 209 * settings on the way out. 210 */ 211 if (!nested++ 212 && (sig == SIGINT 213 || sig == SIGQUIT)) { 214 #if HAVE_SIGACTION || HAVE_SIGVEC 215 sigaction_t act; 216 sigemptyset(&act.sa_mask); 217 act.sa_flags = 0; 218 act.sa_handler = SIG_IGN; 219 if (sigaction(sig, &act, (sigaction_t *)0) == 0) 220 #else 221 if (signal(sig, SIG_IGN) != SIG_ERR) 222 #endif 223 { 224 SCREEN *scan = _nc_screen_chain; 225 while(scan) 226 { 227 if (SP != 0 228 && SP->_ofp != 0 229 && isatty(fileno(SP->_ofp))) { 230 SP->_cleanup = TRUE; 231 SP->_outch = _nc_outch; 232 } 233 set_term(scan); 234 endwin(); 235 if (SP) 236 SP->_endwin = FALSE; /* in case we have an atexit! */ 237 scan = scan->_next_screen; 238 } 239 } 240 } 241 exit(EXIT_FAILURE); 242 } 243 244 #if USE_SIGWINCH 245 static void sigwinch(int sig GCC_UNUSED) 246 { 247 SCREEN *scan = _nc_screen_chain; 248 while(scan) 249 { 250 scan->_sig_winch = TRUE; 251 scan = scan->_next_screen; 252 } 253 } 254 #endif /* USE_SIGWINCH */ 255 256 /* 257 * If the given signal is still in its default state, set it to the given 258 * handler. 259 */ 260 #if HAVE_SIGACTION || HAVE_SIGVEC 261 static int CatchIfDefault(int sig, sigaction_t *act) 262 { 263 sigaction_t old_act; 264 265 if (sigaction(sig, (sigaction_t *)0, &old_act) == 0 266 && (old_act.sa_handler == SIG_DFL 267 #if USE_SIGWINCH 268 || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN) 269 #endif 270 )) { 271 (void)sigaction(sig, act, (sigaction_t *)0); 272 return TRUE; 273 } 274 return FALSE; 275 } 276 #else 277 static int CatchIfDefault(int sig, RETSIGTYPE (*handler)(int)) 278 { 279 void (*ohandler)(int); 280 281 ohandler = signal(sig, SIG_IGN); 282 if (ohandler == SIG_DFL 283 #if USE_SIGWINCH 284 || (sig == SIGWINCH && ohandler == SIG_IGN) 285 #endif 286 ) { 287 signal(sig, handler); 288 return TRUE; 289 } else { 290 signal(sig, ohandler); 291 return FALSE; 292 } 293 } 294 #endif 295 296 /* 297 * This is invoked once at the beginning (e.g., from 'initscr()'), to 298 * initialize the signal catchers, and thereafter when spawning a shell (and 299 * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher. 300 * 301 * If the application has already set one of the signals, we'll not modify it 302 * (during initialization). 303 * 304 * The XSI document implies that we shouldn't keep the SIGTSTP handler if 305 * the caller later changes its mind, but that doesn't seem correct. 306 */ 307 void _nc_signal_handler(bool enable) 308 { 309 #if USE_SIGTSTP /* Xenix 2.x doesn't have SIGTSTP, for example */ 310 static sigaction_t act, oact; 311 static int ignore; 312 313 if (!ignore) 314 { 315 if (!enable) 316 { 317 act.sa_handler = SIG_IGN; 318 sigaction(SIGTSTP, &act, &oact); 319 } 320 else if (act.sa_handler) 321 { 322 sigaction(SIGTSTP, &oact, NULL); 323 } 324 else /*initialize */ 325 { 326 sigemptyset(&act.sa_mask); 327 act.sa_flags = 0; 328 #if USE_SIGWINCH 329 act.sa_handler = sigwinch; 330 CatchIfDefault(SIGWINCH, &act); 331 #endif 332 333 #ifdef SA_RESTART 334 act.sa_flags |= SA_RESTART; 335 #endif /* SA_RESTART */ 336 act.sa_handler = cleanup; 337 CatchIfDefault(SIGINT, &act); 338 CatchIfDefault(SIGTERM, &act); 339 340 act.sa_handler = tstp; 341 if (!CatchIfDefault(SIGTSTP, &act)) 342 ignore = TRUE; 343 } 344 } 345 #else /* !USE_SIGTSTP */ 346 if (enable) 347 { 348 #if HAVE_SIGACTION || HAVE_SIGVEC 349 static sigaction_t act; 350 sigemptyset(&act.sa_mask); 351 #if USE_SIGWINCH 352 act.sa_handler = sigwinch; 353 CatchIfDefault(SIGWINCH, &act); 354 #endif 355 #ifdef SA_RESTART 356 act.sa_flags |= SA_RESTART; 357 #endif /* SA_RESTART */ 358 act.sa_handler = cleanup; 359 CatchIfDefault(SIGINT, &act); 360 CatchIfDefault(SIGTERM, &act); 361 362 #else /* !(HAVE_SIGACTION || HAVE_SIGVEC) */ 363 364 CatchIfDefault(SIGINT, cleanup); 365 CatchIfDefault(SIGTERM, cleanup); 366 #if USE_SIGWINCH 367 CatchIfDefault(SIGWINCH, sigwinch); 368 #endif 369 #endif /* !(HAVE_SIGACTION || HAVE_SIGVEC) */ 370 } 371 #endif /* !USE_SIGTSTP */ 372 } 373