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