150276Speter/****************************************************************************
2178866Srafan * Copyright (c) 1998-2007,2008 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
4566963Speter#if SVR4_ACTION && !defined(_POSIX_SOURCE)
4650276Speter#define _POSIX_SOURCE
4750276Speter#endif
4850276Speter
49178866SrafanMODULE_ID("$Id: lib_tstp.c,v 1.37 2008/05/03 16:24:56 tom Exp $")
5050276Speter
5150276Speter#if defined(SIGTSTP) && (HAVE_SIGACTION || HAVE_SIGVEC)
5250276Speter#define USE_SIGTSTP 1
5350276Speter#else
5450276Speter#define USE_SIGTSTP 0
5550276Speter#endif
5650276Speter
5797049Speter#ifdef TRACE
5897049Speterstatic const char *
5997049Spetersignal_name(int sig)
6097049Speter{
6197049Speter    switch (sig) {
6297049Speter    case SIGALRM:
6397049Speter	return "SIGALRM";
6497049Speter#ifdef SIGCONT
6597049Speter    case SIGCONT:
6697049Speter	return "SIGCONT";
6797049Speter#endif
6897049Speter    case SIGINT:
6997049Speter	return "SIGINT";
7097049Speter    case SIGQUIT:
7197049Speter	return "SIGQUIT";
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
13766963Spetertstp(int dummy GCC_UNUSED)
13850276Speter{
13966963Speter    sigset_t mask, omask;
14066963Speter    sigaction_t act, oact;
14150276Speter
14250276Speter#ifdef SIGTTOU
14366963Speter    int sigttou_blocked;
14450276Speter#endif
14550276Speter
14666963Speter    T(("tstp() called"));
14750276Speter
14866963Speter    /*
14966963Speter     * The user may have changed the prog_mode tty bits, so save them.
15066963Speter     *
15166963Speter     * But first try to detect whether we still are in the foreground
15266963Speter     * process group - if not, an interactive shell may already have
15366963Speter     * taken ownership of the tty and modified the settings when our
15466963Speter     * parent was stopped before us, and we would likely pick up the
15566963Speter     * settings already modified by the shell.
15666963Speter     */
15766963Speter    if (SP != 0 && !SP->_endwin)	/* don't do this if we're not in curses */
15850276Speter#if HAVE_TCGETPGRP
15950276Speter	if (tcgetpgrp(STDIN_FILENO) == getpgrp())
16050276Speter#endif
16150276Speter	    def_prog_mode();
16250276Speter
16366963Speter    /*
16466963Speter     * Block window change and timer signals.  The latter
16566963Speter     * is because applications use timers to decide when
16666963Speter     * to repaint the screen.
16766963Speter     */
16866963Speter    (void) sigemptyset(&mask);
16966963Speter    (void) sigaddset(&mask, SIGALRM);
17050276Speter#if USE_SIGWINCH
17166963Speter    (void) sigaddset(&mask, SIGWINCH);
17250276Speter#endif
17366963Speter    (void) sigprocmask(SIG_BLOCK, &mask, &omask);
17450276Speter
17550276Speter#ifdef SIGTTOU
17666963Speter    sigttou_blocked = sigismember(&omask, SIGTTOU);
17766963Speter    if (!sigttou_blocked) {
17866963Speter	(void) sigemptyset(&mask);
17966963Speter	(void) sigaddset(&mask, SIGTTOU);
18066963Speter	(void) sigprocmask(SIG_BLOCK, &mask, NULL);
18166963Speter    }
18250276Speter#endif
18350276Speter
18466963Speter    /*
18566963Speter     * End window mode, which also resets the terminal state to the
18666963Speter     * original (pre-curses) modes.
18766963Speter     */
18866963Speter    endwin();
18950276Speter
19066963Speter    /* Unblock SIGTSTP. */
19166963Speter    (void) sigemptyset(&mask);
19266963Speter    (void) sigaddset(&mask, SIGTSTP);
19350276Speter#ifdef SIGTTOU
19466963Speter    if (!sigttou_blocked) {
19566963Speter	/* Unblock this too if it wasn't blocked on entry */
19666963Speter	(void) sigaddset(&mask, SIGTTOU);
19766963Speter    }
19850276Speter#endif
19966963Speter    (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
20050276Speter
20166963Speter    /* Now we want to resend SIGSTP to this process and suspend it */
20266963Speter    act.sa_handler = SIG_DFL;
20366963Speter    sigemptyset(&act.sa_mask);
20466963Speter    act.sa_flags = 0;
20550276Speter#ifdef SA_RESTART
20666963Speter    act.sa_flags |= SA_RESTART;
20750276Speter#endif /* SA_RESTART */
20866963Speter    sigaction(SIGTSTP, &act, &oact);
20966963Speter    kill(getpid(), SIGTSTP);
21050276Speter
21166963Speter    /* Process gets suspended...time passes...process resumes */
21250276Speter
21366963Speter    T(("SIGCONT received"));
21466963Speter    sigaction(SIGTSTP, &oact, NULL);
21566963Speter    flushinp();
21650276Speter
21766963Speter    /*
21866963Speter     * If the user modified the tty state while suspended, he wants
21966963Speter     * those changes to stick.  So save the new "default" terminal state.
22066963Speter     */
22166963Speter    def_shell_mode();
22250276Speter
22366963Speter    /*
22466963Speter     * This relies on the fact that doupdate() will restore the
22566963Speter     * program-mode tty state, and issue enter_ca_mode if need be.
22666963Speter     */
22766963Speter    doupdate();
22850276Speter
22966963Speter    /* Reset the signals. */
23066963Speter    (void) sigprocmask(SIG_SETMASK, &omask, NULL);
23150276Speter}
23266963Speter#endif /* USE_SIGTSTP */
23350276Speter
23466963Speterstatic void
23566963Spetercleanup(int sig)
23650276Speter{
23766963Speter    /*
23866963Speter     * Actually, doing any sort of I/O from within an signal handler is
23966963Speter     * "unsafe".  But we'll _try_ to clean up the screen and terminal
24066963Speter     * settings on the way out.
24166963Speter     */
242174993Srafan    if (!_nc_globals.cleanup_nested++
24366963Speter	&& (sig == SIGINT
24466963Speter	    || sig == SIGQUIT)) {
24550276Speter#if HAVE_SIGACTION || HAVE_SIGVEC
24666963Speter	sigaction_t act;
24766963Speter	sigemptyset(&act.sa_mask);
24866963Speter	act.sa_flags = 0;
24966963Speter	act.sa_handler = SIG_IGN;
25097049Speter	if (sigaction(sig, &act, NULL) == 0)
25150276Speter#else
25266963Speter	if (signal(sig, SIG_IGN) != SIG_ERR)
25350276Speter#endif
25466963Speter	{
255178866Srafan	    SCREEN *scan;
256178866Srafan	    for (each_screen(scan)) {
257178866Srafan		if (scan->_ofp != 0
258178866Srafan		    && isatty(fileno(scan->_ofp))) {
259178866Srafan		    scan->_cleanup = TRUE;
260178866Srafan		    scan->_outch = _nc_outch;
26150276Speter		}
26266963Speter		set_term(scan);
26366963Speter		endwin();
26466963Speter		if (SP)
26566963Speter		    SP->_endwin = FALSE;	/* in case we have an atexit! */
26666963Speter	    }
26750276Speter	}
26866963Speter    }
26966963Speter    exit(EXIT_FAILURE);
27050276Speter}
27150276Speter
27250276Speter#if USE_SIGWINCH
27366963Speterstatic void
27466963Spetersigwinch(int sig GCC_UNUSED)
27550276Speter{
276174993Srafan    _nc_globals.have_sigwinch = 1;
27750276Speter}
27850276Speter#endif /* USE_SIGWINCH */
27950276Speter
28050276Speter/*
28150276Speter * If the given signal is still in its default state, set it to the given
28250276Speter * handler.
28350276Speter */
28466963Speterstatic int
28597049SpeterCatchIfDefault(int sig, RETSIGTYPE (*handler) (int))
28650276Speter{
28797049Speter    int result;
28897049Speter#if HAVE_SIGACTION || HAVE_SIGVEC
28966963Speter    sigaction_t old_act;
29097049Speter    sigaction_t new_act;
29150276Speter
29297049Speter    memset(&new_act, 0, sizeof(new_act));
29397049Speter    sigemptyset(&new_act.sa_mask);
29497049Speter#ifdef SA_RESTART
29597049Speter#ifdef SIGWINCH
29697049Speter    if (sig != SIGWINCH)
29797049Speter#endif
29897049Speter	new_act.sa_flags |= SA_RESTART;
29997049Speter#endif /* SA_RESTART */
30097049Speter    new_act.sa_handler = handler;
30197049Speter
30297049Speter    if (sigaction(sig, NULL, &old_act) == 0
30366963Speter	&& (old_act.sa_handler == SIG_DFL
30497049Speter	    || old_act.sa_handler == handler
30550276Speter#if USE_SIGWINCH
30650276Speter	    || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN)
30750276Speter#endif
30866963Speter	)) {
30997049Speter	(void) sigaction(sig, &new_act, NULL);
31097049Speter	result = TRUE;
31197049Speter    } else {
31297049Speter	result = FALSE;
31366963Speter    }
31497049Speter#else /* !HAVE_SIGACTION */
31597049Speter    RETSIGTYPE (*ohandler) (int);
31650276Speter
31766963Speter    ohandler = signal(sig, SIG_IGN);
31866963Speter    if (ohandler == SIG_DFL
31997049Speter	|| ohandler == handler
32050276Speter#if USE_SIGWINCH
32166963Speter	|| (sig == SIGWINCH && ohandler == SIG_IGN)
32250276Speter#endif
32350276Speter	) {
32466963Speter	signal(sig, handler);
32597049Speter	result = TRUE;
32666963Speter    } else {
32766963Speter	signal(sig, ohandler);
32897049Speter	result = FALSE;
32966963Speter    }
33097049Speter#endif
33197049Speter    T(("CatchIfDefault - will %scatch %s",
33297049Speter       result ? "" : "not ", signal_name(sig)));
33397049Speter    return result;
33450276Speter}
33550276Speter
33650276Speter/*
33750276Speter * This is invoked once at the beginning (e.g., from 'initscr()'), to
33850276Speter * initialize the signal catchers, and thereafter when spawning a shell (and
33950276Speter * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher.
34050276Speter *
34150276Speter * If the application has already set one of the signals, we'll not modify it
34250276Speter * (during initialization).
34350276Speter *
34450276Speter * The XSI document implies that we shouldn't keep the SIGTSTP handler if
34550276Speter * the caller later changes its mind, but that doesn't seem correct.
34650276Speter */
34776726SpeterNCURSES_EXPORT(void)
34866963Speter_nc_signal_handler(bool enable)
34950276Speter{
35097049Speter    T((T_CALLED("_nc_signal_handler(%d)"), enable));
35166963Speter#if USE_SIGTSTP			/* Xenix 2.x doesn't have SIGTSTP, for example */
35297049Speter    {
35397049Speter	static bool ignore_tstp = FALSE;
35450276Speter
35597049Speter	if (!ignore_tstp) {
356174993Srafan	    static sigaction_t new_sigaction, old_sigaction;
35750276Speter
35897049Speter	    if (!enable) {
359174993Srafan		new_sigaction.sa_handler = SIG_IGN;
360174993Srafan		sigaction(SIGTSTP, &new_sigaction, &old_sigaction);
361174993Srafan	    } else if (new_sigaction.sa_handler != SIG_DFL) {
362174993Srafan		sigaction(SIGTSTP, &old_sigaction, NULL);
363174993Srafan	    } else if (sigaction(SIGTSTP, NULL, &old_sigaction) == 0
364174993Srafan		       && (old_sigaction.sa_handler == SIG_DFL)) {
365174993Srafan		sigemptyset(&new_sigaction.sa_mask);
36650276Speter#ifdef SA_RESTART
367174993Srafan		new_sigaction.sa_flags |= SA_RESTART;
36850276Speter#endif /* SA_RESTART */
369174993Srafan		new_sigaction.sa_handler = tstp;
370174993Srafan		(void) sigaction(SIGTSTP, &new_sigaction, NULL);
37197049Speter	    } else {
37297049Speter		ignore_tstp = TRUE;
37397049Speter	    }
37450276Speter	}
37566963Speter    }
37697049Speter#endif /* !USE_SIGTSTP */
37750276Speter
378174993Srafan    if (!_nc_globals.init_signals) {
37997049Speter	if (enable) {
38097049Speter	    CatchIfDefault(SIGINT, cleanup);
38197049Speter	    CatchIfDefault(SIGTERM, cleanup);
38250276Speter#if USE_SIGWINCH
38397049Speter	    CatchIfDefault(SIGWINCH, sigwinch);
38450276Speter#endif
385174993Srafan	    _nc_globals.init_signals = TRUE;
38697049Speter	}
38766963Speter    }
38897049Speter    returnVoid;
38950276Speter}
390