150276Speter/****************************************************************************
2262685Sdelphij * Copyright (c) 1998-2012,2013 Free Software Foundation, Inc.              *
350276Speter *                                                                          *
450276Speter * Permission is hereby granted, free of charge, to any person obtaining a  *
550276Speter * copy of this software and associated documentation files (the            *
650276Speter * "Software"), to deal in the Software without restriction, including      *
750276Speter * without limitation the rights to use, copy, modify, merge, publish,      *
850276Speter * distribute, distribute with modifications, sublicense, and/or sell       *
950276Speter * copies of the Software, and to permit persons to whom the Software is    *
1050276Speter * furnished to do so, subject to the following conditions:                 *
1150276Speter *                                                                          *
1250276Speter * The above copyright notice and this permission notice shall be included  *
1350276Speter * in all copies or substantial portions of the Software.                   *
1450276Speter *                                                                          *
1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2250276Speter *                                                                          *
2350276Speter * Except as contained in this notice, the name(s) of the above copyright   *
2450276Speter * holders shall not be used in advertising or otherwise to promote the     *
2550276Speter * sale, use or other dealings in this Software without prior written       *
2650276Speter * authorization.                                                           *
2750276Speter ****************************************************************************/
2850276Speter
2950276Speter/****************************************************************************
3050276Speter *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3150276Speter *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32166124Srafan *     and: Thomas E. Dickey                        1995-on                 *
3350276Speter ****************************************************************************/
3450276Speter
3550276Speter/*
3650276Speter**	lib_tstp.c
3750276Speter**
3850276Speter**	The routine _nc_signal_handler().
3950276Speter**
4050276Speter*/
4150276Speter#include <curses.priv.h>
4250276Speter
4350276Speter#include <SigAction.h>
4450276Speter
45262685SdelphijMODULE_ID("$Id: lib_tstp.c,v 1.47 2013/04/27 19:50:17 tom Exp $")
4650276Speter
4750276Speter#if defined(SIGTSTP) && (HAVE_SIGACTION || HAVE_SIGVEC)
4850276Speter#define USE_SIGTSTP 1
4950276Speter#else
5050276Speter#define USE_SIGTSTP 0
5150276Speter#endif
5250276Speter
5397049Speter#ifdef TRACE
5497049Speterstatic const char *
5597049Spetersignal_name(int sig)
5697049Speter{
5797049Speter    switch (sig) {
58262629Sdelphij#ifdef SIGALRM
5997049Speter    case SIGALRM:
6097049Speter	return "SIGALRM";
61262629Sdelphij#endif
6297049Speter#ifdef SIGCONT
6397049Speter    case SIGCONT:
6497049Speter	return "SIGCONT";
6597049Speter#endif
6697049Speter    case SIGINT:
6797049Speter	return "SIGINT";
68262629Sdelphij#ifdef SIGQUIT
6997049Speter    case SIGQUIT:
7097049Speter	return "SIGQUIT";
71262629Sdelphij#endif
7297049Speter    case SIGTERM:
7397049Speter	return "SIGTERM";
7497049Speter#ifdef SIGTSTP
7597049Speter    case SIGTSTP:
7697049Speter	return "SIGTSTP";
7797049Speter#endif
7897049Speter#ifdef SIGTTOU
7997049Speter    case SIGTTOU:
8097049Speter	return "SIGTTOU";
8197049Speter#endif
8297049Speter#ifdef SIGWINCH
8397049Speter    case SIGWINCH:
8497049Speter	return "SIGWINCH";
8597049Speter#endif
8697049Speter    default:
8797049Speter	return "unknown signal";
8897049Speter    }
8997049Speter}
9097049Speter#endif
9197049Speter
9250276Speter/*
9350276Speter * Note: This code is fragile!  Its problem is that different OSs
9450276Speter * handle restart of system calls interrupted by signals differently.
9550276Speter * The ncurses code needs signal-call restart to happen -- otherwise,
9650276Speter * interrupted wgetch() calls will return FAIL, probably making the
9750276Speter * application think the input stream has ended and it should
9850276Speter * terminate.  In particular, you know you have this problem if, when
9950276Speter * you suspend an ncurses-using lynx with ^Z and resume, it dies
10050276Speter * immediately.
10150276Speter *
10250276Speter * Default behavior of POSIX sigaction(2) is not to restart
10350276Speter * interrupted system calls, but Linux's sigaction does it anyway (at
10450276Speter * least, on and after the 1.1.47 I (esr) use).  Thus this code works
10550276Speter * OK under Linux.  The 4.4BSD sigaction(2) supports a (non-portable)
10650276Speter * SA_RESTART flag that forces the right behavior.  Thus, this code
10750276Speter * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it
10850276Speter * does not).
10950276Speter *
11050276Speter * Stock System Vs (and anything else using a strict-POSIX
11150276Speter * sigaction(2) without SA_RESTART) may have a problem.  Possible
11250276Speter * solutions:
11350276Speter *
11450276Speter *    sigvec      restarts by default (SV_INTERRUPT flag to not restart)
11550276Speter *    signal      restarts by default in SVr4 (assuming you link with -lucb)
11650276Speter *                and BSD, but not SVr3.
11750276Speter *    sigset      restarts, but is only available under SVr4/Solaris.
11850276Speter *
11950276Speter * The signal(3) call is mandated by the ANSI standard, and its
12050276Speter * interaction with sigaction(2) is described in the POSIX standard
12150276Speter * (3.3.4.2, page 72,line 934).  According to section 8.1, page 191,
12250276Speter * however, signal(3) itself is not required by POSIX.1.  And POSIX is
12350276Speter * silent on whether it is required to restart signals.
12450276Speter *
12550276Speter * So.  The present situation is, we use sigaction(2) with no
12650276Speter * guarantee of restart anywhere but on Linux and BSD.  We could
12750276Speter * switch to signal(3) and collar Linux, BSD, and SVr4.  Any way
12850276Speter * we slice it, System V UNIXes older than SVr4 will probably lose
12950276Speter * (this may include XENIX).
13050276Speter *
13150276Speter * This implementation will probably be changed to use signal(3) in
13250276Speter * the future.  If nothing else, it's simpler...
13350276Speter */
13450276Speter
13550276Speter#if USE_SIGTSTP
13666963Speterstatic void
137262685Sdelphijhandle_SIGTSTP(int dummy GCC_UNUSED)
13850276Speter{
139262629Sdelphij    SCREEN *sp = CURRENT_SCREEN;
14066963Speter    sigset_t mask, omask;
14166963Speter    sigaction_t act, oact;
14250276Speter
14350276Speter#ifdef SIGTTOU
14466963Speter    int sigttou_blocked;
14550276Speter#endif
14650276Speter
147262685Sdelphij    _nc_globals.have_sigtstp = 1;
148262685Sdelphij    T(("handle_SIGTSTP() called"));
14950276Speter
15066963Speter    /*
15166963Speter     * The user may have changed the prog_mode tty bits, so save them.
15266963Speter     *
15366963Speter     * But first try to detect whether we still are in the foreground
15466963Speter     * process group - if not, an interactive shell may already have
15566963Speter     * taken ownership of the tty and modified the settings when our
15666963Speter     * parent was stopped before us, and we would likely pick up the
15766963Speter     * settings already modified by the shell.
15866963Speter     */
159262629Sdelphij    if (sp != 0 && !sp->_endwin)	/* don't do this if we're not in curses */
16050276Speter#if HAVE_TCGETPGRP
16150276Speter	if (tcgetpgrp(STDIN_FILENO) == getpgrp())
16250276Speter#endif
163262629Sdelphij	    NCURSES_SP_NAME(def_prog_mode) (NCURSES_SP_ARG);
16450276Speter
16566963Speter    /*
16666963Speter     * Block window change and timer signals.  The latter
16766963Speter     * is because applications use timers to decide when
16866963Speter     * to repaint the screen.
16966963Speter     */
17066963Speter    (void) sigemptyset(&mask);
171262629Sdelphij#ifdef SIGALRM
17266963Speter    (void) sigaddset(&mask, SIGALRM);
173262629Sdelphij#endif
17450276Speter#if USE_SIGWINCH
17566963Speter    (void) sigaddset(&mask, SIGWINCH);
17650276Speter#endif
17766963Speter    (void) sigprocmask(SIG_BLOCK, &mask, &omask);
17850276Speter
17950276Speter#ifdef SIGTTOU
18066963Speter    sigttou_blocked = sigismember(&omask, SIGTTOU);
18166963Speter    if (!sigttou_blocked) {
18266963Speter	(void) sigemptyset(&mask);
18366963Speter	(void) sigaddset(&mask, SIGTTOU);
18466963Speter	(void) sigprocmask(SIG_BLOCK, &mask, NULL);
18566963Speter    }
18650276Speter#endif
18750276Speter
18866963Speter    /*
18966963Speter     * End window mode, which also resets the terminal state to the
19066963Speter     * original (pre-curses) modes.
19166963Speter     */
192262629Sdelphij    NCURSES_SP_NAME(endwin) (NCURSES_SP_ARG);
19350276Speter
19466963Speter    /* Unblock SIGTSTP. */
19566963Speter    (void) sigemptyset(&mask);
19666963Speter    (void) sigaddset(&mask, SIGTSTP);
19750276Speter#ifdef SIGTTOU
19866963Speter    if (!sigttou_blocked) {
19966963Speter	/* Unblock this too if it wasn't blocked on entry */
20066963Speter	(void) sigaddset(&mask, SIGTTOU);
20166963Speter    }
20250276Speter#endif
20366963Speter    (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
20450276Speter
20566963Speter    /* Now we want to resend SIGSTP to this process and suspend it */
20666963Speter    act.sa_handler = SIG_DFL;
20766963Speter    sigemptyset(&act.sa_mask);
20866963Speter    act.sa_flags = 0;
20950276Speter#ifdef SA_RESTART
21066963Speter    act.sa_flags |= SA_RESTART;
21150276Speter#endif /* SA_RESTART */
21266963Speter    sigaction(SIGTSTP, &act, &oact);
21366963Speter    kill(getpid(), SIGTSTP);
21450276Speter
21566963Speter    /* Process gets suspended...time passes...process resumes */
21650276Speter
21766963Speter    T(("SIGCONT received"));
21866963Speter    sigaction(SIGTSTP, &oact, NULL);
219262629Sdelphij    NCURSES_SP_NAME(flushinp) (NCURSES_SP_ARG);
22050276Speter
22166963Speter    /*
22266963Speter     * If the user modified the tty state while suspended, he wants
22366963Speter     * those changes to stick.  So save the new "default" terminal state.
22466963Speter     */
225262629Sdelphij    NCURSES_SP_NAME(def_shell_mode) (NCURSES_SP_ARG);
22650276Speter
22766963Speter    /*
22866963Speter     * This relies on the fact that doupdate() will restore the
22966963Speter     * program-mode tty state, and issue enter_ca_mode if need be.
23066963Speter     */
231262629Sdelphij    NCURSES_SP_NAME(doupdate) (NCURSES_SP_ARG);
23250276Speter
23366963Speter    /* Reset the signals. */
23466963Speter    (void) sigprocmask(SIG_SETMASK, &omask, NULL);
23550276Speter}
23666963Speter#endif /* USE_SIGTSTP */
23750276Speter
23866963Speterstatic void
239262685Sdelphijhandle_SIGINT(int sig)
24050276Speter{
241262629Sdelphij    SCREEN *sp = CURRENT_SCREEN;
242262629Sdelphij
24366963Speter    /*
244262685Sdelphij     * Much of this is unsafe from a signal handler.  But we'll _try_ to clean
245262685Sdelphij     * up the screen and terminal settings on the way out.
246262685Sdelphij     *
247262685Sdelphij     * There are at least the following problems:
248262685Sdelphij     * 1) Walking the SCREEN list is unsafe, since all list management
249262685Sdelphij     *    is done without any signal blocking.
250262685Sdelphij     * 2) On systems which have REENTRANT turned on, set_term() uses
251262685Sdelphij     *    _nc_lock_global() which could deadlock or misbehave in other ways.
252262685Sdelphij     * 3) endwin() calls all sorts of stuff, many of which use stdio or
253262685Sdelphij     *    other library functions which are clearly unsafe.
25466963Speter     */
255174993Srafan    if (!_nc_globals.cleanup_nested++
256262685Sdelphij	&& (sig == SIGINT || sig == SIGTERM)) {
25750276Speter#if HAVE_SIGACTION || HAVE_SIGVEC
25866963Speter	sigaction_t act;
25966963Speter	sigemptyset(&act.sa_mask);
26066963Speter	act.sa_flags = 0;
26166963Speter	act.sa_handler = SIG_IGN;
26297049Speter	if (sigaction(sig, &act, NULL) == 0)
26350276Speter#else
26466963Speter	if (signal(sig, SIG_IGN) != SIG_ERR)
26550276Speter#endif
26666963Speter	{
267178866Srafan	    SCREEN *scan;
268178866Srafan	    for (each_screen(scan)) {
269178866Srafan		if (scan->_ofp != 0
270178866Srafan		    && isatty(fileno(scan->_ofp))) {
271262629Sdelphij		    scan->_outch = NCURSES_SP_NAME(_nc_outch);
27250276Speter		}
27366963Speter		set_term(scan);
274262629Sdelphij		NCURSES_SP_NAME(endwin) (NCURSES_SP_ARG);
275262629Sdelphij		if (sp)
276262685Sdelphij		    sp->_endwin = FALSE;	/* in case of reuse */
27766963Speter	    }
27850276Speter	}
27966963Speter    }
280262685Sdelphij    _exit(EXIT_FAILURE);
28150276Speter}
28250276Speter
28350276Speter#if USE_SIGWINCH
28466963Speterstatic void
285262685Sdelphijhandle_SIGWINCH(int sig GCC_UNUSED)
28650276Speter{
287174993Srafan    _nc_globals.have_sigwinch = 1;
288262629Sdelphij# if USE_PTHREADS_EINTR
289262629Sdelphij    if (_nc_globals.read_thread) {
290262629Sdelphij	if (!pthread_equal(pthread_self(), _nc_globals.read_thread))
291262629Sdelphij	    pthread_kill(_nc_globals.read_thread, SIGWINCH);
292262629Sdelphij	_nc_globals.read_thread = 0;
293262629Sdelphij    }
294262629Sdelphij# endif
29550276Speter}
29650276Speter#endif /* USE_SIGWINCH */
29750276Speter
29850276Speter/*
29950276Speter * If the given signal is still in its default state, set it to the given
30050276Speter * handler.
30150276Speter */
30266963Speterstatic int
303262685SdelphijCatchIfDefault(int sig, void (*handler) (int))
30450276Speter{
30597049Speter    int result;
30697049Speter#if HAVE_SIGACTION || HAVE_SIGVEC
30766963Speter    sigaction_t old_act;
30897049Speter    sigaction_t new_act;
30950276Speter
31097049Speter    memset(&new_act, 0, sizeof(new_act));
31197049Speter    sigemptyset(&new_act.sa_mask);
31297049Speter#ifdef SA_RESTART
31397049Speter#ifdef SIGWINCH
31497049Speter    if (sig != SIGWINCH)
31597049Speter#endif
31697049Speter	new_act.sa_flags |= SA_RESTART;
31797049Speter#endif /* SA_RESTART */
31897049Speter    new_act.sa_handler = handler;
31997049Speter
32097049Speter    if (sigaction(sig, NULL, &old_act) == 0
32166963Speter	&& (old_act.sa_handler == SIG_DFL
32297049Speter	    || old_act.sa_handler == handler
32350276Speter#if USE_SIGWINCH
32450276Speter	    || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN)
32550276Speter#endif
32666963Speter	)) {
32797049Speter	(void) sigaction(sig, &new_act, NULL);
32897049Speter	result = TRUE;
32997049Speter    } else {
33097049Speter	result = FALSE;
33166963Speter    }
33297049Speter#else /* !HAVE_SIGACTION */
333262685Sdelphij    void (*ohandler) (int);
33450276Speter
33566963Speter    ohandler = signal(sig, SIG_IGN);
33666963Speter    if (ohandler == SIG_DFL
33797049Speter	|| ohandler == handler
33850276Speter#if USE_SIGWINCH
33966963Speter	|| (sig == SIGWINCH && ohandler == SIG_IGN)
34050276Speter#endif
34150276Speter	) {
34266963Speter	signal(sig, handler);
34397049Speter	result = TRUE;
34466963Speter    } else {
34566963Speter	signal(sig, ohandler);
34697049Speter	result = FALSE;
34766963Speter    }
34897049Speter#endif
34997049Speter    T(("CatchIfDefault - will %scatch %s",
35097049Speter       result ? "" : "not ", signal_name(sig)));
35197049Speter    return result;
35250276Speter}
35350276Speter
35450276Speter/*
35550276Speter * This is invoked once at the beginning (e.g., from 'initscr()'), to
35650276Speter * initialize the signal catchers, and thereafter when spawning a shell (and
35750276Speter * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher.
35850276Speter *
35950276Speter * If the application has already set one of the signals, we'll not modify it
36050276Speter * (during initialization).
36150276Speter *
36250276Speter * The XSI document implies that we shouldn't keep the SIGTSTP handler if
36350276Speter * the caller later changes its mind, but that doesn't seem correct.
36450276Speter */
36576726SpeterNCURSES_EXPORT(void)
366262685Sdelphij_nc_signal_handler(int enable)
36750276Speter{
36897049Speter    T((T_CALLED("_nc_signal_handler(%d)"), enable));
36966963Speter#if USE_SIGTSTP			/* Xenix 2.x doesn't have SIGTSTP, for example */
37097049Speter    {
37197049Speter	static bool ignore_tstp = FALSE;
37250276Speter
37397049Speter	if (!ignore_tstp) {
374174993Srafan	    static sigaction_t new_sigaction, old_sigaction;
37550276Speter
37697049Speter	    if (!enable) {
377174993Srafan		new_sigaction.sa_handler = SIG_IGN;
378174993Srafan		sigaction(SIGTSTP, &new_sigaction, &old_sigaction);
379174993Srafan	    } else if (new_sigaction.sa_handler != SIG_DFL) {
380174993Srafan		sigaction(SIGTSTP, &old_sigaction, NULL);
381174993Srafan	    } else if (sigaction(SIGTSTP, NULL, &old_sigaction) == 0
382174993Srafan		       && (old_sigaction.sa_handler == SIG_DFL)) {
383174993Srafan		sigemptyset(&new_sigaction.sa_mask);
38450276Speter#ifdef SA_RESTART
385174993Srafan		new_sigaction.sa_flags |= SA_RESTART;
38650276Speter#endif /* SA_RESTART */
387262685Sdelphij		new_sigaction.sa_handler = handle_SIGTSTP;
388174993Srafan		(void) sigaction(SIGTSTP, &new_sigaction, NULL);
38997049Speter	    } else {
39097049Speter		ignore_tstp = TRUE;
39197049Speter	    }
39250276Speter	}
39366963Speter    }
39497049Speter#endif /* !USE_SIGTSTP */
39550276Speter
396174993Srafan    if (!_nc_globals.init_signals) {
39797049Speter	if (enable) {
398262685Sdelphij	    CatchIfDefault(SIGINT, handle_SIGINT);
399262685Sdelphij	    CatchIfDefault(SIGTERM, handle_SIGINT);
40050276Speter#if USE_SIGWINCH
401262685Sdelphij	    CatchIfDefault(SIGWINCH, handle_SIGWINCH);
40250276Speter#endif
403174993Srafan	    _nc_globals.init_signals = TRUE;
40497049Speter	}
40566963Speter    }
40697049Speter    returnVoid;
40750276Speter}
408