lib_tstp.c revision 50276
1193323Sed/****************************************************************************
2193323Sed * Copyright (c) 1998,1999 Free Software Foundation, Inc.                   *
3193323Sed *                                                                          *
4193323Sed * Permission is hereby granted, free of charge, to any person obtaining a  *
5193323Sed * copy of this software and associated documentation files (the            *
6193323Sed * "Software"), to deal in the Software without restriction, including      *
7193323Sed * without limitation the rights to use, copy, modify, merge, publish,      *
8193323Sed * distribute, distribute with modifications, sublicense, and/or sell       *
9193323Sed * copies of the Software, and to permit persons to whom the Software is    *
10193323Sed * furnished to do so, subject to the following conditions:                 *
11193323Sed *                                                                          *
12193323Sed * The above copyright notice and this permission notice shall be included  *
13193323Sed * in all copies or substantial portions of the Software.                   *
14193323Sed *                                                                          *
15193323Sed * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16193323Sed * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17193323Sed * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18193323Sed * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19193323Sed * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20193323Sed * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21249423Sdim * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22198090Srdivacky *                                                                          *
23249423Sdim * Except as contained in this notice, the name(s) of the above copyright   *
24249423Sdim * holders shall not be used in advertising or otherwise to promote the     *
25194612Sed * sale, use or other dealings in this Software without prior written       *
26193323Sed * authorization.                                                           *
27193323Sed ****************************************************************************/
28193323Sed
29193323Sed/****************************************************************************
30198090Srdivacky *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31193323Sed *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32218893Sdim ****************************************************************************/
33193323Sed
34193323Sed
35193323Sed/*
36193323Sed**	lib_tstp.c
37206274Srdivacky**
38205218Srdivacky**	The routine _nc_signal_handler().
39203954Srdivacky**
40193323Sed*/
41193323Sed
42218893Sdim#include <curses.priv.h>
43193323Sed
44193323Sed#include <signal.h>
45193323Sed#include <SigAction.h>
46193323Sed
47198090Srdivacky#if defined(SVR4_ACTION) && !defined(_POSIX_SOURCE)
48193323Sed#define _POSIX_SOURCE
49193323Sed#endif
50193323Sed
51193323SedMODULE_ID("$Id: lib_tstp.c,v 1.19 1999/07/24 22:47:20 tom Exp $")
52193323Sed
53193323Sed#if defined(SIGTSTP) && (HAVE_SIGACTION || HAVE_SIGVEC)
54193323Sed#define USE_SIGTSTP 1
55193323Sed#else
56193323Sed#define USE_SIGTSTP 0
57193323Sed#endif
58193323Sed
59193323Sed/*
60193323Sed * Note: This code is fragile!  Its problem is that different OSs
61193323Sed * handle restart of system calls interrupted by signals differently.
62193323Sed * The ncurses code needs signal-call restart to happen -- otherwise,
63193323Sed * interrupted wgetch() calls will return FAIL, probably making the
64193323Sed * application think the input stream has ended and it should
65193323Sed * terminate.  In particular, you know you have this problem if, when
66193323Sed * you suspend an ncurses-using lynx with ^Z and resume, it dies
67193323Sed * immediately.
68193323Sed *
69193323Sed * Default behavior of POSIX sigaction(2) is not to restart
70193323Sed * interrupted system calls, but Linux's sigaction does it anyway (at
71193323Sed * least, on and after the 1.1.47 I (esr) use).  Thus this code works
72198090Srdivacky * OK under Linux.  The 4.4BSD sigaction(2) supports a (non-portable)
73193323Sed * SA_RESTART flag that forces the right behavior.  Thus, this code
74193323Sed * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it
75198090Srdivacky * does not).
76207618Srdivacky *
77193323Sed * Stock System Vs (and anything else using a strict-POSIX
78205218Srdivacky * sigaction(2) without SA_RESTART) may have a problem.  Possible
79206274Srdivacky * solutions:
80218893Sdim *
81206274Srdivacky *    sigvec      restarts by default (SV_INTERRUPT flag to not restart)
82193323Sed *    signal      restarts by default in SVr4 (assuming you link with -lucb)
83193323Sed *                and BSD, but not SVr3.
84193323Sed *    sigset      restarts, but is only available under SVr4/Solaris.
85193323Sed *
86193323Sed * The signal(3) call is mandated by the ANSI standard, and its
87193323Sed * interaction with sigaction(2) is described in the POSIX standard
88193323Sed * (3.3.4.2, page 72,line 934).  According to section 8.1, page 191,
89193323Sed * however, signal(3) itself is not required by POSIX.1.  And POSIX is
90193323Sed * silent on whether it is required to restart signals.
91193323Sed *
92193323Sed * So.  The present situation is, we use sigaction(2) with no
93193323Sed * guarantee of restart anywhere but on Linux and BSD.  We could
94193323Sed * switch to signal(3) and collar Linux, BSD, and SVr4.  Any way
95193323Sed * we slice it, System V UNIXes older than SVr4 will probably lose
96193323Sed * (this may include XENIX).
97193323Sed *
98193323Sed * This implementation will probably be changed to use signal(3) in
99193323Sed * the future.  If nothing else, it's simpler...
100193323Sed */
101193323Sed
102193323Sed#if USE_SIGTSTP
103193323Sedstatic void tstp(int dummy GCC_UNUSED)
104193323Sed{
105193323Sed	sigset_t mask, omask;
106193323Sed	sigaction_t act, oact;
107193323Sed
108193323Sed#ifdef SIGTTOU
109249423Sdim	int sigttou_blocked;
110249423Sdim#endif
111249423Sdim
112193323Sed	T(("tstp() called"));
113193323Sed
114193323Sed	/*
115193323Sed	 * The user may have changed the prog_mode tty bits, so save them.
116193323Sed	 *
117193323Sed	 * But first try to detect whether we still are in the foreground
118193323Sed	 * process group - if not, an interactive shell may already have
119203954Srdivacky	 * taken ownership of the tty and modified the settings when our
120203954Srdivacky	 * parent was stopped before us, and we would likely pick up the
121203954Srdivacky	 * settings already modified by the shell.
122203954Srdivacky	 */
123203954Srdivacky	if (SP != 0 && !SP->_endwin) /* don't do this if we're not in curses */
124208599Srdivacky#if HAVE_TCGETPGRP
125195340Sed	if (tcgetpgrp(STDIN_FILENO) == getpgrp())
126195340Sed#endif
127234353Sdim	    def_prog_mode();
128234353Sdim
129234353Sdim	/*
130234353Sdim	 * Block window change and timer signals.  The latter
131234353Sdim	 * is because applications use timers to decide when
132234353Sdim	 * to repaint the screen.
133208599Srdivacky	 */
134263765Sdim	(void)sigemptyset(&mask);
135263765Sdim	(void)sigaddset(&mask, SIGALRM);
136249423Sdim#if USE_SIGWINCH
137243830Sdim	(void)sigaddset(&mask, SIGWINCH);
138243830Sdim#endif
139193323Sed	(void)sigprocmask(SIG_BLOCK, &mask, &omask);
140207618Srdivacky
141218893Sdim#ifdef SIGTTOU
142218893Sdim	sigttou_blocked = sigismember(&omask, SIGTTOU);
143193323Sed	if (!sigttou_blocked) {
144193323Sed	    (void)sigemptyset(&mask);
145206274Srdivacky	    (void)sigaddset(&mask, SIGTTOU);
146218893Sdim	    (void)sigprocmask(SIG_BLOCK, &mask, NULL);
147205218Srdivacky	}
148243830Sdim#endif
149193323Sed
150193323Sed	/*
151207618Srdivacky	 * End window mode, which also resets the terminal state to the
152193323Sed	 * original (pre-curses) modes.
153243830Sdim	 */
154243830Sdim	endwin();
155243830Sdim
156243830Sdim	/* Unblock SIGTSTP. */
157203954Srdivacky	(void)sigemptyset(&mask);
158203954Srdivacky	(void)sigaddset(&mask, SIGTSTP);
159203954Srdivacky#ifdef SIGTTOU
160243830Sdim	if (!sigttou_blocked) {
161193323Sed            /* Unblock this too if it wasn't blocked on entry */
162193323Sed	    (void)sigaddset(&mask, SIGTTOU);
163193323Sed	}
164193323Sed#endif
165193323Sed	(void)sigprocmask(SIG_UNBLOCK, &mask, NULL);
166193323Sed
167193323Sed	/* Now we want to resend SIGSTP to this process and suspend it */
168193323Sed	act.sa_handler = SIG_DFL;
169193323Sed	sigemptyset(&act.sa_mask);
170193323Sed	act.sa_flags = 0;
171193323Sed#ifdef SA_RESTART
172193323Sed	act.sa_flags |= SA_RESTART;
173193323Sed#endif /* SA_RESTART */
174193323Sed	sigaction(SIGTSTP, &act, &oact);
175193323Sed	kill(getpid(), SIGTSTP);
176193323Sed
177193323Sed	/* Process gets suspended...time passes...process resumes */
178203954Srdivacky
179203954Srdivacky	T(("SIGCONT received"));
180203954Srdivacky	sigaction(SIGTSTP, &oact, NULL);
181203954Srdivacky	flushinp();
182193323Sed
183203954Srdivacky	/*
184203954Srdivacky	 * If the user modified the tty state while suspended, he wants
185203954Srdivacky	 * those changes to stick.  So save the new "default" terminal state.
186203954Srdivacky	 */
187203954Srdivacky	def_shell_mode();
188193323Sed
189193323Sed	/*
190193323Sed	 * This relies on the fact that doupdate() will restore the
191193323Sed	 * program-mode tty state, and issue enter_ca_mode if need be.
192193323Sed	 */
193193323Sed	doupdate();
194193323Sed
195195340Sed	/* Reset the signals. */
196195340Sed	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
197195340Sed}
198195340Sed#endif	/* USE_SIGTSTP */
199195340Sed
200195340Sedstatic void cleanup(int sig)
201195340Sed{
202195340Sed	/*
203239462Sdim	 * Actually, doing any sort of I/O from within an signal handler is
204239462Sdim	 * "unsafe".  But we'll _try_ to clean up the screen and terminal
205203954Srdivacky	 * settings on the way out.
206203954Srdivacky	 */
207208599Srdivacky	if (sig == SIGINT
208234353Sdim	 || sig == SIGQUIT) {
209234353Sdim#if HAVE_SIGACTION || HAVE_SIGVEC
210234353Sdim		sigaction_t act;
211234353Sdim		sigemptyset(&act.sa_mask);
212234353Sdim		act.sa_flags = 0;
213208599Srdivacky		act.sa_handler = SIG_IGN;
214208599Srdivacky		if (sigaction(sig, &act, (sigaction_t *)0) == 0)
215234353Sdim#else
216234353Sdim		if (signal(sig, SIG_IGN) != SIG_ERR)
217234353Sdim#endif
218234353Sdim		{
219208599Srdivacky		    SCREEN *scan = _nc_screen_chain;
220249423Sdim		    while(scan)
221263765Sdim		    {
222263765Sdim			set_term(scan);
223263765Sdim			endwin();
224249423Sdim			if (SP)
225249423Sdim			    SP->_endwin = FALSE; /* in case we have an atexit! */
226263765Sdim			scan = scan->_next_screen;
227263765Sdim		    }
228263765Sdim		}
229249423Sdim	}
230203954Srdivacky	exit(EXIT_FAILURE);
231198090Srdivacky}
232198090Srdivacky
233193323Sed#if USE_SIGWINCH
234193323Sedstatic void sigwinch(int sig GCC_UNUSED)
235193323Sed{
236193323Sed    SCREEN *scan = _nc_screen_chain;
237193323Sed    while(scan)
238193323Sed    {
239193323Sed	scan->_sig_winch = TRUE;
240193323Sed	scan = scan->_next_screen;
241193323Sed    }
242193323Sed}
243193323Sed#endif /* USE_SIGWINCH */
244193323Sed
245193323Sed/*
246193323Sed * If the given signal is still in its default state, set it to the given
247193323Sed * handler.
248193323Sed */
249193323Sed#if HAVE_SIGACTION || HAVE_SIGVEC
250193323Sedstatic int CatchIfDefault(int sig, sigaction_t *act)
251193323Sed{
252193323Sed	sigaction_t old_act;
253193323Sed
254193323Sed	if (sigaction(sig, (sigaction_t *)0, &old_act) == 0
255193323Sed	 && (old_act.sa_handler == SIG_DFL
256193323Sed#if USE_SIGWINCH
257193323Sed	    || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN)
258193323Sed#endif
259193323Sed	    )) {
260193323Sed		(void)sigaction(sig, act, (sigaction_t *)0);
261193323Sed		return TRUE;
262193323Sed	}
263193323Sed	return FALSE;
264193323Sed}
265193323Sed#else
266193323Sedstatic int CatchIfDefault(int sig, RETSIGTYPE (*handler)(int))
267193323Sed{
268193323Sed	void	(*ohandler)(int);
269193323Sed
270193323Sed	ohandler = signal(sig, SIG_IGN);
271193323Sed	if (ohandler == SIG_DFL
272193323Sed#if USE_SIGWINCH
273193323Sed	    || (sig == SIGWINCH && ohandler == SIG_IGN)
274193323Sed#endif
275193323Sed	) {
276218893Sdim		signal(sig, handler);
277193323Sed		return TRUE;
278193323Sed	} else {
279193323Sed		signal(sig, ohandler);
280193323Sed		return FALSE;
281193323Sed	}
282193323Sed}
283193323Sed#endif
284193323Sed
285193323Sed/*
286193323Sed * This is invoked once at the beginning (e.g., from 'initscr()'), to
287193323Sed * initialize the signal catchers, and thereafter when spawning a shell (and
288193323Sed * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher.
289193323Sed *
290193323Sed * If the application has already set one of the signals, we'll not modify it
291193323Sed * (during initialization).
292193323Sed *
293193323Sed * The XSI document implies that we shouldn't keep the SIGTSTP handler if
294193323Sed * the caller later changes its mind, but that doesn't seem correct.
295193323Sed */
296193323Sedvoid _nc_signal_handler(bool enable)
297199481Srdivacky{
298199481Srdivacky#if USE_SIGTSTP		/* Xenix 2.x doesn't have SIGTSTP, for example */
299218893Sdimstatic sigaction_t act, oact;
300199481Srdivackystatic int ignore;
301193323Sed
302193323Sed	if (!ignore)
303193323Sed	{
304193323Sed		if (!enable)
305193323Sed		{
306193323Sed			act.sa_handler = SIG_IGN;
307193323Sed			sigaction(SIGTSTP, &act, &oact);
308193323Sed		}
309219077Sdim		else if (act.sa_handler)
310193323Sed		{
311193323Sed			sigaction(SIGTSTP, &oact, NULL);
312193323Sed		}
313193323Sed		else	/*initialize */
314193323Sed		{
315193323Sed			sigemptyset(&act.sa_mask);
316193323Sed			act.sa_flags = 0;
317193323Sed#if USE_SIGWINCH
318193323Sed			act.sa_handler = sigwinch;
319193323Sed			CatchIfDefault(SIGWINCH, &act);
320193323Sed#endif
321193323Sed
322193323Sed#ifdef SA_RESTART
323193323Sed			act.sa_flags |= SA_RESTART;
324193323Sed#endif /* SA_RESTART */
325193323Sed			act.sa_handler = cleanup;
326193323Sed			CatchIfDefault(SIGINT,  &act);
327193323Sed			CatchIfDefault(SIGTERM, &act);
328193323Sed
329193323Sed			act.sa_handler = tstp;
330193323Sed			if (!CatchIfDefault(SIGTSTP, &act))
331193323Sed				ignore = TRUE;
332193323Sed		}
333193323Sed	}
334193323Sed#else /* !USE_SIGTSTP */
335193323Sed	if (enable)
336193323Sed	{
337193323Sed#if HAVE_SIGACTION || HAVE_SIGVEC
338193323Sed		static sigaction_t act;
339198396Srdivacky		sigemptyset(&act.sa_mask);
340198396Srdivacky#if USE_SIGWINCH
341198396Srdivacky		act.sa_handler = sigwinch;
342193323Sed		CatchIfDefault(SIGWINCH, &act);
343193323Sed#endif
344193323Sed#ifdef SA_RESTART
345193323Sed		act.sa_flags |= SA_RESTART;
346193323Sed#endif /* SA_RESTART */
347193323Sed		act.sa_handler = cleanup;
348193323Sed		CatchIfDefault(SIGINT,  &act);
349193323Sed		CatchIfDefault(SIGTERM, &act);
350193323Sed
351193323Sed#else /* !(HAVE_SIGACTION || HAVE_SIGVEC) */
352193323Sed
353193323Sed		CatchIfDefault(SIGINT,  cleanup);
354251662Sdim		CatchIfDefault(SIGTERM, cleanup);
355251662Sdim#if USE_SIGWINCH
356193323Sed		CatchIfDefault(SIGWINCH, sigwinch);
357193323Sed#endif
358193323Sed#endif /* !(HAVE_SIGACTION || HAVE_SIGVEC) */
359193323Sed	}
360193323Sed#endif /* !USE_SIGTSTP */
361193323Sed}
362193323Sed