ncurses 4.2
[ncurses.git] / ncurses / lib_tstp.c
1 /****************************************************************************
2  * Copyright (c) 1998 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 /*
36 **      lib_tstp.c
37 **
38 **      The routine _nc_signal_handler().
39 **
40 */
41
42 #include <curses.priv.h>
43
44 #include <signal.h>
45 #include <SigAction.h>
46
47 #if defined(SVR4_ACTION) && !defined(_POSIX_SOURCE)
48 #define _POSIX_SOURCE
49 #endif
50
51 MODULE_ID("$Id: lib_tstp.c,v 1.15 1998/02/11 12:13:57 tom Exp $")
52
53 /*
54  * Note: This code is fragile!  Its problem is that different OSs
55  * handle restart of system calls interrupted by signals differently.
56  * The ncurses code needs signal-call restart to happen -- otherwise,
57  * interrupted wgetch() calls will return FAIL, probably making the
58  * application think the input stream has ended and it should
59  * terminate.  In particular, you know you have this problem if, when
60  * you suspend an ncurses-using lynx with ^Z and resume, it dies
61  * immediately.
62  *
63  * Default behavior of POSIX sigaction(2) is not to restart
64  * interrupted system calls, but Linux's sigaction does it anyway (at
65  * least, on and after the 1.1.47 I (esr) use).  Thus this code works
66  * OK under Linux.  The 4.4BSD sigaction(2) supports a (non-portable)
67  * SA_RESTART flag that forces the right behavior.  Thus, this code
68  * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it
69  * does not).
70  *
71  * Stock System Vs (and anything else using a strict-POSIX
72  * sigaction(2) without SA_RESTART) may have a problem.  Possible
73  * solutions:
74  *
75  *    sigvec      restarts by default (SV_INTERRUPT flag to not restart)
76  *    signal      restarts by default in SVr4 (assuming you link with -lucb)
77  *                and BSD, but not SVr3.
78  *    sigset      restarts, but is only available under SVr4/Solaris.
79  *
80  * The signal(3) call is mandated by the ANSI standard, and its
81  * interaction with sigaction(2) is described in the POSIX standard
82  * (3.3.4.2, page 72,line 934).  According to section 8.1, page 191,
83  * however, signal(3) itself is not required by POSIX.1.  And POSIX is
84  * silent on whether it is required to restart signals.
85  *
86  * So.  The present situation is, we use sigaction(2) with no
87  * guarantee of restart anywhere but on Linux and BSD.  We could
88  * switch to signal(3) and collar Linux, BSD, and SVr4.  Any way
89  * we slice it, System V UNIXes older than SVr4 will probably lose
90  * (this may include XENIX).
91  *
92  * This implementation will probably be changed to use signal(3) in
93  * the future.  If nothing else, it's simpler...
94  */
95
96 #ifdef SIGTSTP
97 static void tstp(int dummy GCC_UNUSED)
98 {
99         sigset_t mask, omask;
100         sigaction_t act, oact;
101
102         T(("tstp() called"));
103
104         /*
105          * The user may have changed the prog_mode tty bits, so save them.
106          */
107         def_prog_mode();
108
109         /*
110          * Block window change and timer signals.  The latter
111          * is because applications use timers to decide when
112          * to repaint the screen.
113          */
114         (void)sigemptyset(&mask);
115         (void)sigaddset(&mask, SIGALRM);
116 #if USE_SIGWINCH
117         (void)sigaddset(&mask, SIGWINCH);
118 #endif
119         (void)sigprocmask(SIG_BLOCK, &mask, &omask);
120
121         /*
122          * End window mode, which also resets the terminal state to the
123          * original (pre-curses) modes.
124          */
125         endwin();
126
127         /* Unblock SIGTSTP. */
128         (void)sigemptyset(&mask);
129         (void)sigaddset(&mask, SIGTSTP);
130         (void)sigprocmask(SIG_UNBLOCK, &mask, NULL);
131
132         /* Now we want to resend SIGSTP to this process and suspend it */
133         act.sa_handler = SIG_DFL;
134         sigemptyset(&act.sa_mask);
135         act.sa_flags = 0;
136 #ifdef SA_RESTART
137         act.sa_flags |= SA_RESTART;
138 #endif /* SA_RESTART */
139         sigaction(SIGTSTP, &act, &oact);
140         kill(getpid(), SIGTSTP);
141
142         /* Process gets suspended...time passes...process resumes */
143
144         T(("SIGCONT received"));
145         sigaction(SIGTSTP, &oact, NULL);
146         flushinp();
147
148         /*
149          * If the user modified the tty state while suspended, he wants
150          * those changes to stick.  So save the new "default" terminal state.
151          */
152         def_shell_mode();
153
154         /*
155          * This relies on the fact that doupdate() will restore the
156          * program-mode tty state, and issue enter_ca_mode if need be.
157          */
158         doupdate();
159
160         /* Reset the signals. */
161         (void)sigprocmask(SIG_SETMASK, &omask, NULL);
162 }
163 #endif  /* defined(SIGTSTP) */
164
165 static void cleanup(int sig)
166 {
167         /*
168          * Actually, doing any sort of I/O from within an signal handler is
169          * "unsafe".  But we'll _try_ to clean up the screen and terminal
170          * settings on the way out.
171          */
172         if (sig == SIGINT
173          || sig == SIGQUIT) {
174 #if HAVE_SIGACTION || HAVE_SIGVEC
175                 sigaction_t act;
176                 sigemptyset(&act.sa_mask);
177                 act.sa_flags = 0;
178                 act.sa_handler = SIG_IGN;
179                 if (sigaction(sig, &act, (sigaction_t *)0) == 0)
180 #else
181                 if (signal(sig, SIG_IGN) != SIG_ERR)
182 #endif
183                 {
184                     SCREEN *scan = _nc_screen_chain;
185                     while(scan)
186                     {
187                         set_term(scan);
188                         endwin();
189                         SP->_endwin = FALSE; /* in case we have an atexit! */
190                         scan = scan->_next_screen;
191                     }
192                 }
193         }
194         exit(EXIT_FAILURE);
195 }
196
197 #if USE_SIGWINCH
198 static void sigwinch(int sig GCC_UNUSED)
199 {
200     SCREEN *scan = _nc_screen_chain;
201     while(scan)
202     {
203         scan->_sig_winch = TRUE;
204         scan = scan->_next_screen;
205     }
206 }
207 #endif /* USE_SIGWINCH */
208
209 /*
210  * If the given signal is still in its default state, set it to the given
211  * handler.
212  */
213 #if HAVE_SIGACTION || HAVE_SIGVEC
214 static int CatchIfDefault(int sig, sigaction_t *act)
215 {
216         sigaction_t old_act;
217
218         if (sigaction(sig, (sigaction_t *)0, &old_act) == 0
219          && (old_act.sa_handler == SIG_DFL
220 #if USE_SIGWINCH
221             || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN)
222 #endif
223             )) {
224                 (void)sigaction(sig, act, (sigaction_t *)0);
225                 return TRUE;
226         }
227         return FALSE;
228 }
229 #else
230 static int CatchIfDefault(int sig, RETSIGTYPE (*handler)())
231 {
232         void    (*ohandler)();
233
234         ohandler = signal(sig, SIG_IGN);
235         if (ohandler == SIG_DFL
236 #if USE_SIGWINCH
237             || (sig == SIGWINCH && ohandler == SIG_IGN)
238 #endif
239         ) {
240                 signal(sig, handler);
241                 return TRUE;
242         } else {
243                 signal(sig, ohandler);
244                 return FALSE;
245         }
246 }
247 #endif
248
249 /*
250  * This is invoked once at the beginning (e.g., from 'initscr()'), to
251  * initialize the signal catchers, and thereafter when spawning a shell (and
252  * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher.
253  *
254  * If the application has already set one of the signals, we'll not modify it
255  * (during initialization).
256  *
257  * The XSI document implies that we shouldn't keep the SIGTSTP handler if
258  * the caller later changes its mind, but that doesn't seem correct.
259  */
260 void _nc_signal_handler(bool enable)
261 {
262 #ifdef SIGTSTP          /* Xenix 2.x doesn't have this */
263 static sigaction_t act, oact;
264 static int ignore;
265
266         if (!ignore)
267         {
268                 if (!enable)
269                 {
270                         act.sa_handler = SIG_IGN;
271                         sigaction(SIGTSTP, &act, &oact);
272                 }
273                 else if (act.sa_handler)
274                 {
275                         sigaction(SIGTSTP, &oact, NULL);
276                 }
277                 else    /*initialize */
278                 {
279                         sigemptyset(&act.sa_mask);
280                         act.sa_flags = 0;
281 #if USE_SIGWINCH
282                         act.sa_handler = sigwinch;
283                         CatchIfDefault(SIGWINCH, &act);
284 #endif
285
286 #ifdef SA_RESTART
287                         act.sa_flags |= SA_RESTART;
288 #endif /* SA_RESTART */
289                         act.sa_handler = cleanup;
290                         CatchIfDefault(SIGINT,  &act);
291                         CatchIfDefault(SIGTERM, &act);
292
293                         act.sa_handler = tstp;
294                         if (!CatchIfDefault(SIGTSTP, &act))
295                                 ignore = TRUE;
296                 }
297         }
298 #else /* !SIGTSTP */
299         if (enable)
300         {
301 #if HAVE_SIGACTION || HAVE_SIGVEC
302                 static sigaction_t act;
303                 sigemptyset(&act.sa_mask);
304 #if USE_SIGWINCH
305                 act.sa_handler = sigwinch;
306                 CatchIfDefault(SIGWINCH, &act);
307 #endif
308 #ifdef SA_RESTART
309                 act.sa_flags |= SA_RESTART;
310 #endif /* SA_RESTART */
311                 act.sa_handler = cleanup;
312                 CatchIfDefault(SIGINT,  &act);
313                 CatchIfDefault(SIGTERM, &act);
314
315 #else /* !(HAVE_SIGACTION || HAVE_SIGVEC) */
316
317                 CatchIfDefault(SIGINT,  cleanup);
318                 CatchIfDefault(SIGTERM, cleanup);
319 #if USE_SIGWINCH
320                 CatchIfDefault(SIGWINCH, sigwinch);
321 #endif
322 #endif /* !(HAVE_SIGACTION || HAVE_SIGVEC) */
323         }
324 #endif /* !SIGTSTP */
325 }