lib_tstp.c revision 66963
1/**************************************************************************** 2 * Copyright (c) 1998,1999,2000 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 ****************************************************************************/ 33 34/* 35** lib_tstp.c 36** 37** The routine _nc_signal_handler(). 38** 39*/ 40 41#include <curses.priv.h> 42 43#include <signal.h> 44#include <SigAction.h> 45 46#if SVR4_ACTION && !defined(_POSIX_SOURCE) 47#define _POSIX_SOURCE 48#endif 49 50MODULE_ID("$Id: lib_tstp.c,v 1.22 2000/09/02 18:33:17 tom Exp $") 51 52#if defined(SIGTSTP) && (HAVE_SIGACTION || HAVE_SIGVEC) 53#define USE_SIGTSTP 1 54#else 55#define USE_SIGTSTP 0 56#endif 57 58/* 59 * Note: This code is fragile! Its problem is that different OSs 60 * handle restart of system calls interrupted by signals differently. 61 * The ncurses code needs signal-call restart to happen -- otherwise, 62 * interrupted wgetch() calls will return FAIL, probably making the 63 * application think the input stream has ended and it should 64 * terminate. In particular, you know you have this problem if, when 65 * you suspend an ncurses-using lynx with ^Z and resume, it dies 66 * immediately. 67 * 68 * Default behavior of POSIX sigaction(2) is not to restart 69 * interrupted system calls, but Linux's sigaction does it anyway (at 70 * least, on and after the 1.1.47 I (esr) use). Thus this code works 71 * OK under Linux. The 4.4BSD sigaction(2) supports a (non-portable) 72 * SA_RESTART flag that forces the right behavior. Thus, this code 73 * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it 74 * does not). 75 * 76 * Stock System Vs (and anything else using a strict-POSIX 77 * sigaction(2) without SA_RESTART) may have a problem. Possible 78 * solutions: 79 * 80 * sigvec restarts by default (SV_INTERRUPT flag to not restart) 81 * signal restarts by default in SVr4 (assuming you link with -lucb) 82 * and BSD, but not SVr3. 83 * sigset restarts, but is only available under SVr4/Solaris. 84 * 85 * The signal(3) call is mandated by the ANSI standard, and its 86 * interaction with sigaction(2) is described in the POSIX standard 87 * (3.3.4.2, page 72,line 934). According to section 8.1, page 191, 88 * however, signal(3) itself is not required by POSIX.1. And POSIX is 89 * silent on whether it is required to restart signals. 90 * 91 * So. The present situation is, we use sigaction(2) with no 92 * guarantee of restart anywhere but on Linux and BSD. We could 93 * switch to signal(3) and collar Linux, BSD, and SVr4. Any way 94 * we slice it, System V UNIXes older than SVr4 will probably lose 95 * (this may include XENIX). 96 * 97 * This implementation will probably be changed to use signal(3) in 98 * the future. If nothing else, it's simpler... 99 */ 100 101#if USE_SIGTSTP 102static void 103tstp(int dummy GCC_UNUSED) 104{ 105 sigset_t mask, omask; 106 sigaction_t act, oact; 107 108#ifdef SIGTTOU 109 int sigttou_blocked; 110#endif 111 112 T(("tstp() called")); 113 114 /* 115 * The user may have changed the prog_mode tty bits, so save them. 116 * 117 * But first try to detect whether we still are in the foreground 118 * process group - if not, an interactive shell may already have 119 * taken ownership of the tty and modified the settings when our 120 * parent was stopped before us, and we would likely pick up the 121 * settings already modified by the shell. 122 */ 123 if (SP != 0 && !SP->_endwin) /* don't do this if we're not in curses */ 124#if HAVE_TCGETPGRP 125 if (tcgetpgrp(STDIN_FILENO) == getpgrp()) 126#endif 127 def_prog_mode(); 128 129 /* 130 * Block window change and timer signals. The latter 131 * is because applications use timers to decide when 132 * to repaint the screen. 133 */ 134 (void) sigemptyset(&mask); 135 (void) sigaddset(&mask, SIGALRM); 136#if USE_SIGWINCH 137 (void) sigaddset(&mask, SIGWINCH); 138#endif 139 (void) sigprocmask(SIG_BLOCK, &mask, &omask); 140 141#ifdef SIGTTOU 142 sigttou_blocked = sigismember(&omask, SIGTTOU); 143 if (!sigttou_blocked) { 144 (void) sigemptyset(&mask); 145 (void) sigaddset(&mask, SIGTTOU); 146 (void) sigprocmask(SIG_BLOCK, &mask, NULL); 147 } 148#endif 149 150 /* 151 * End window mode, which also resets the terminal state to the 152 * original (pre-curses) modes. 153 */ 154 endwin(); 155 156 /* Unblock SIGTSTP. */ 157 (void) sigemptyset(&mask); 158 (void) sigaddset(&mask, SIGTSTP); 159#ifdef SIGTTOU 160 if (!sigttou_blocked) { 161 /* Unblock this too if it wasn't blocked on entry */ 162 (void) sigaddset(&mask, SIGTTOU); 163 } 164#endif 165 (void) sigprocmask(SIG_UNBLOCK, &mask, NULL); 166 167 /* Now we want to resend SIGSTP to this process and suspend it */ 168 act.sa_handler = SIG_DFL; 169 sigemptyset(&act.sa_mask); 170 act.sa_flags = 0; 171#ifdef SA_RESTART 172 act.sa_flags |= SA_RESTART; 173#endif /* SA_RESTART */ 174 sigaction(SIGTSTP, &act, &oact); 175 kill(getpid(), SIGTSTP); 176 177 /* Process gets suspended...time passes...process resumes */ 178 179 T(("SIGCONT received")); 180 sigaction(SIGTSTP, &oact, NULL); 181 flushinp(); 182 183 /* 184 * If the user modified the tty state while suspended, he wants 185 * those changes to stick. So save the new "default" terminal state. 186 */ 187 def_shell_mode(); 188 189 /* 190 * This relies on the fact that doupdate() will restore the 191 * program-mode tty state, and issue enter_ca_mode if need be. 192 */ 193 doupdate(); 194 195 /* Reset the signals. */ 196 (void) sigprocmask(SIG_SETMASK, &omask, NULL); 197} 198#endif /* USE_SIGTSTP */ 199 200static void 201cleanup(int sig) 202{ 203 static int nested; 204 205 /* 206 * Actually, doing any sort of I/O from within an signal handler is 207 * "unsafe". But we'll _try_ to clean up the screen and terminal 208 * settings on the way out. 209 */ 210 if (!nested++ 211 && (sig == SIGINT 212 || sig == SIGQUIT)) { 213#if HAVE_SIGACTION || HAVE_SIGVEC 214 sigaction_t act; 215 sigemptyset(&act.sa_mask); 216 act.sa_flags = 0; 217 act.sa_handler = SIG_IGN; 218 if (sigaction(sig, &act, (sigaction_t *) 0) == 0) 219#else 220 if (signal(sig, SIG_IGN) != SIG_ERR) 221#endif 222 { 223 SCREEN *scan = _nc_screen_chain; 224 while (scan) { 225 if (SP != 0 226 && SP->_ofp != 0 227 && isatty(fileno(SP->_ofp))) { 228 SP->_cleanup = TRUE; 229 SP->_outch = _nc_outch; 230 } 231 set_term(scan); 232 endwin(); 233 if (SP) 234 SP->_endwin = FALSE; /* in case we have an atexit! */ 235 scan = scan->_next_screen; 236 } 237 } 238 } 239 exit(EXIT_FAILURE); 240} 241 242#if USE_SIGWINCH 243static void 244sigwinch(int sig GCC_UNUSED) 245{ 246 SCREEN *scan = _nc_screen_chain; 247 while (scan) { 248 scan->_sig_winch = TRUE; 249 scan = scan->_next_screen; 250 } 251} 252#endif /* USE_SIGWINCH */ 253 254/* 255 * If the given signal is still in its default state, set it to the given 256 * handler. 257 */ 258#if HAVE_SIGACTION || HAVE_SIGVEC 259static int 260CatchIfDefault(int sig, sigaction_t * act) 261{ 262 sigaction_t old_act; 263 264 if (sigaction(sig, (sigaction_t *) 0, &old_act) == 0 265 && (old_act.sa_handler == SIG_DFL 266#if USE_SIGWINCH 267 || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN) 268#endif 269 )) { 270 (void) sigaction(sig, act, (sigaction_t *) 0); 271 return TRUE; 272 } 273 return FALSE; 274} 275#else 276static int 277CatchIfDefault(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 */ 307void 308_nc_signal_handler(bool enable) 309{ 310#if USE_SIGTSTP /* Xenix 2.x doesn't have SIGTSTP, for example */ 311 static sigaction_t act, oact; 312 static int ignore; 313 314 if (!ignore) { 315 if (!enable) { 316 act.sa_handler = SIG_IGN; 317 sigaction(SIGTSTP, &act, &oact); 318 } else if (act.sa_handler) { 319 sigaction(SIGTSTP, &oact, NULL); 320 } else { /*initialize */ 321 sigemptyset(&act.sa_mask); 322 act.sa_flags = 0; 323#if USE_SIGWINCH 324 act.sa_handler = sigwinch; 325 CatchIfDefault(SIGWINCH, &act); 326#endif 327 328#ifdef SA_RESTART 329 act.sa_flags |= SA_RESTART; 330#endif /* SA_RESTART */ 331 act.sa_handler = cleanup; 332 CatchIfDefault(SIGINT, &act); 333 CatchIfDefault(SIGTERM, &act); 334 335 act.sa_handler = tstp; 336 if (!CatchIfDefault(SIGTSTP, &act)) 337 ignore = TRUE; 338 } 339 } 340#else /* !USE_SIGTSTP */ 341 if (enable) { 342#if HAVE_SIGACTION || HAVE_SIGVEC 343 static sigaction_t act; 344 sigemptyset(&act.sa_mask); 345#if USE_SIGWINCH 346 act.sa_handler = sigwinch; 347 CatchIfDefault(SIGWINCH, &act); 348#endif 349#ifdef SA_RESTART 350 act.sa_flags |= SA_RESTART; 351#endif /* SA_RESTART */ 352 act.sa_handler = cleanup; 353 CatchIfDefault(SIGINT, &act); 354 CatchIfDefault(SIGTERM, &act); 355 356#else /* !(HAVE_SIGACTION || HAVE_SIGVEC) */ 357 358 CatchIfDefault(SIGINT, cleanup); 359 CatchIfDefault(SIGTERM, cleanup); 360#if USE_SIGWINCH 361 CatchIfDefault(SIGWINCH, sigwinch); 362#endif 363#endif /* !(HAVE_SIGACTION || HAVE_SIGVEC) */ 364 } 365#endif /* !USE_SIGTSTP */ 366} 367