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