lib_tstp.c revision 97049
150276Speter/****************************************************************************
297049Speter * Copyright (c) 1998-2001,2002 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>                         *
3297049Speter *     and: Thomas Dickey 1995-2001                                         *
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
4997049SpeterMODULE_ID("$Id: lib_tstp.c,v 1.30 2002/05/18 19:55:38 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    static int nested;
23856639Speter
23966963Speter    /*
24066963Speter     * Actually, doing any sort of I/O from within an signal handler is
24166963Speter     * "unsafe".  But we'll _try_ to clean up the screen and terminal
24266963Speter     * settings on the way out.
24366963Speter     */
24466963Speter    if (!nested++
24566963Speter	&& (sig == SIGINT
24666963Speter	    || sig == SIGQUIT)) {
24750276Speter#if HAVE_SIGACTION || HAVE_SIGVEC
24866963Speter	sigaction_t act;
24966963Speter	sigemptyset(&act.sa_mask);
25066963Speter	act.sa_flags = 0;
25166963Speter	act.sa_handler = SIG_IGN;
25297049Speter	if (sigaction(sig, &act, NULL) == 0)
25350276Speter#else
25466963Speter	if (signal(sig, SIG_IGN) != SIG_ERR)
25550276Speter#endif
25666963Speter	{
25766963Speter	    SCREEN *scan = _nc_screen_chain;
25866963Speter	    while (scan) {
25966963Speter		if (SP != 0
26066963Speter		    && SP->_ofp != 0
26166963Speter		    && isatty(fileno(SP->_ofp))) {
26266963Speter		    SP->_cleanup = TRUE;
26366963Speter		    SP->_outch = _nc_outch;
26450276Speter		}
26566963Speter		set_term(scan);
26666963Speter		endwin();
26766963Speter		if (SP)
26866963Speter		    SP->_endwin = FALSE;	/* in case we have an atexit! */
26966963Speter		scan = scan->_next_screen;
27066963Speter	    }
27150276Speter	}
27266963Speter    }
27366963Speter    exit(EXIT_FAILURE);
27450276Speter}
27550276Speter
27650276Speter#if USE_SIGWINCH
27766963Speterstatic void
27866963Spetersigwinch(int sig GCC_UNUSED)
27950276Speter{
28050276Speter    SCREEN *scan = _nc_screen_chain;
28166963Speter    while (scan) {
28250276Speter	scan->_sig_winch = TRUE;
28350276Speter	scan = scan->_next_screen;
28450276Speter    }
28550276Speter}
28650276Speter#endif /* USE_SIGWINCH */
28750276Speter
28850276Speter/*
28950276Speter * If the given signal is still in its default state, set it to the given
29050276Speter * handler.
29150276Speter */
29266963Speterstatic int
29397049SpeterCatchIfDefault(int sig, RETSIGTYPE (*handler) (int))
29450276Speter{
29597049Speter    int result;
29697049Speter#if HAVE_SIGACTION || HAVE_SIGVEC
29766963Speter    sigaction_t old_act;
29897049Speter    sigaction_t new_act;
29950276Speter
30097049Speter    memset(&new_act, 0, sizeof(new_act));
30197049Speter    sigemptyset(&new_act.sa_mask);
30297049Speter#ifdef SA_RESTART
30397049Speter#ifdef SIGWINCH
30497049Speter    if (sig != SIGWINCH)
30597049Speter#endif
30697049Speter	new_act.sa_flags |= SA_RESTART;
30797049Speter#endif /* SA_RESTART */
30897049Speter    new_act.sa_handler = handler;
30997049Speter
31097049Speter    if (sigaction(sig, NULL, &old_act) == 0
31166963Speter	&& (old_act.sa_handler == SIG_DFL
31297049Speter	    || old_act.sa_handler == handler
31350276Speter#if USE_SIGWINCH
31450276Speter	    || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN)
31550276Speter#endif
31666963Speter	)) {
31797049Speter	(void) sigaction(sig, &new_act, NULL);
31897049Speter	result = TRUE;
31997049Speter    } else {
32097049Speter	result = FALSE;
32166963Speter    }
32297049Speter#else /* !HAVE_SIGACTION */
32397049Speter    RETSIGTYPE (*ohandler) (int);
32450276Speter
32566963Speter    ohandler = signal(sig, SIG_IGN);
32666963Speter    if (ohandler == SIG_DFL
32797049Speter	|| ohandler == handler
32850276Speter#if USE_SIGWINCH
32966963Speter	|| (sig == SIGWINCH && ohandler == SIG_IGN)
33050276Speter#endif
33150276Speter	) {
33266963Speter	signal(sig, handler);
33397049Speter	result = TRUE;
33466963Speter    } else {
33566963Speter	signal(sig, ohandler);
33697049Speter	result = FALSE;
33766963Speter    }
33897049Speter#endif
33997049Speter    T(("CatchIfDefault - will %scatch %s",
34097049Speter       result ? "" : "not ", signal_name(sig)));
34197049Speter    return result;
34250276Speter}
34350276Speter
34450276Speter/*
34550276Speter * This is invoked once at the beginning (e.g., from 'initscr()'), to
34650276Speter * initialize the signal catchers, and thereafter when spawning a shell (and
34750276Speter * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher.
34850276Speter *
34950276Speter * If the application has already set one of the signals, we'll not modify it
35050276Speter * (during initialization).
35150276Speter *
35250276Speter * The XSI document implies that we shouldn't keep the SIGTSTP handler if
35350276Speter * the caller later changes its mind, but that doesn't seem correct.
35450276Speter */
35576726SpeterNCURSES_EXPORT(void)
35666963Speter_nc_signal_handler(bool enable)
35750276Speter{
35897049Speter    static bool initialized = FALSE;
35997049Speter
36097049Speter    T((T_CALLED("_nc_signal_handler(%d)"), enable));
36166963Speter#if USE_SIGTSTP			/* Xenix 2.x doesn't have SIGTSTP, for example */
36297049Speter    {
36397049Speter	static bool ignore_tstp = FALSE;
36450276Speter
36597049Speter	if (!ignore_tstp) {
36697049Speter	    static sigaction_t act, oact;
36750276Speter
36897049Speter	    if (!enable) {
36997049Speter		act.sa_handler = SIG_IGN;
37097049Speter		sigaction(SIGTSTP, &act, &oact);
37197049Speter	    } else if (act.sa_handler != SIG_DFL) {
37297049Speter		sigaction(SIGTSTP, &oact, NULL);
37397049Speter	    } else if (sigaction(SIGTSTP, NULL, &oact) == 0
37497049Speter		       && (oact.sa_handler == SIG_DFL)) {
37597049Speter		sigemptyset(&act.sa_mask);
37650276Speter#ifdef SA_RESTART
37797049Speter		act.sa_flags |= SA_RESTART;
37850276Speter#endif /* SA_RESTART */
37997049Speter		act.sa_handler = tstp;
38097049Speter		(void) sigaction(SIGTSTP, &act, NULL);
38197049Speter	    } else {
38297049Speter		ignore_tstp = TRUE;
38397049Speter	    }
38450276Speter	}
38566963Speter    }
38697049Speter#endif /* !USE_SIGTSTP */
38750276Speter
38897049Speter    if (!initialized) {
38997049Speter	if (enable) {
39097049Speter	    CatchIfDefault(SIGINT, cleanup);
39197049Speter	    CatchIfDefault(SIGTERM, cleanup);
39250276Speter#if USE_SIGWINCH
39397049Speter	    CatchIfDefault(SIGWINCH, sigwinch);
39450276Speter#endif
39597049Speter	    initialized = TRUE;
39697049Speter	}
39766963Speter    }
39897049Speter    returnVoid;
39950276Speter}
400