BSD 4_4_Lite2 release
[unix-history] / usr / src / contrib / nvi.1.43 / common / signal.c
CommitLineData
fd88f5c5
C
1/*-
2 * Copyright (c) 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char sccsid[] = "@(#)signal.c 9.3 (Berkeley) 11/13/94";
36#endif /* not lint */
37
38#include <sys/queue.h>
39#include <sys/time.h>
40
41#include <bitstring.h>
42#include <errno.h>
43#include <limits.h>
44#include <signal.h>
45#include <stdio.h>
46#include <termios.h>
47#include <unistd.h>
48
49#include "compat.h"
50#include <db.h>
51#include <regex.h>
52
53#include "vi.h"
54
55static void h_alrm __P((int));
56static void h_hup __P((int));
57static void h_int __P((int));
58static void h_term __P((int));
59static void h_winch __P((int));
60static void sig_sync __P((int, u_int));
61
62/*
63 * There are seven normally asynchronous actions about which vi cares:
64 * SIGALRM, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP and SIGWINCH.
65 *
66 * The assumptions:
67 * 1: The DB routines are not reentrant.
68 * 2: The curses routines may not be reentrant.
69 *
70 * SIGALRM, SIGHUP, SIGTERM
71 * Used for file recovery. The DB routines can't be reentered, so
72 * the vi routines that call DB block all three signals (see line.c).
73 * This means that DB routines can be called at interrupt time.
74 *
75 * SIGALRM
76 * Used to paint busy messages on the screen. The curses routines
77 * can't be reentered, so this function of SIGALRM can only be used
78 * in sections of code that do not use any curses functions (see
79 * busy_on, busy_off in signal.c). This means that curses can be
80 * called at interrupt time.
81 *
82 * SIGQUIT
83 * Disabled by the signal initialization routines. Historically,
84 * ^\ switched vi into ex mode, and we continue that practice.
85 *
86 * SIGWINCH:
87 * The interrupt routine sets a global bit which is checked by the
88 * key-read routine, so there are no reentrancy issues. This means
89 * that the screen will not resize until vi runs out of keys, but
90 * that doesn't seem like a problem.
91 *
92 * SIGINT and SIGTSTP are a much more difficult issue to resolve. Vi has
93 * to permit the user to interrupt long-running operations. Generally, a
94 * search, substitution or read/write is done on a large file, or, the user
95 * creates a key mapping with an infinite loop. This problem will become
96 * worse as more complex semantics are added to vi. There are four major
97 * solutions on the table, each of which have minor permutations.
98 *
99 * 1: Run in raw mode.
100 *
101 * The up side is that there's no asynchronous behavior to worry about,
102 * and obviously no reentrancy problems. The down side is that it's easy
103 * to misinterpret characters (e.g. :w big_file^Mi^V^C is going to look
104 * like an interrupt) and it's easy to get into places where we won't see
105 * interrupt characters (e.g. ":map a ixx^[hxxaXXX" infinitely loops in
106 * historic implementations of vi). Periodically reading the terminal
107 * input buffer might solve the latter problem, but it's not going to be
108 * pretty.
109 *
110 * Also, we're going to be checking for ^C's and ^Z's both, all over
111 * the place -- I hate to litter the source code with that. For example,
112 * the historic version of vi didn't permit you to suspend the screen if
113 * you were on the colon command line. This isn't right. ^Z isn't a vi
114 * command, it's a terminal event. (Dammit.)
115 *
116 * 2: Run in cbreak mode. There are two problems in this area. First, the
117 * current curses implementations (both System V and Berkeley) don't give
118 * you clean cbreak modes. For example, the IEXTEN bit is left on, turning
119 * on DISCARD and LNEXT. To clarify, what vi WANTS is 8-bit clean, with
120 * the exception that flow control and signals are turned on, and curses
121 * cbreak mode doesn't give you this.
122 *
123 * We can either set raw mode and twiddle the tty, or cbreak mode and
124 * twiddle the tty. I chose to use raw mode, on the grounds that raw
125 * mode is better defined and I'm less likely to be surprised by a curses
126 * implementation down the road. The twiddling consists of setting ISIG,
127 * IXON/IXOFF, and disabling some of the interrupt characters (see the
128 * comments in svi/svi_screen.c). This is all found in historic System
129 * V (SVID 3) and POSIX 1003.1-1992, so it should be fairly portable.
130 *
131 * The second problem is that vi permits you to enter literal signal
132 * characters, e.g. ^V^C. There are two possible solutions. First, you
133 * can turn off signals when you get a ^V, but that means that a network
134 * packet containing ^V and ^C will lose, since the ^C may take effect
135 * before vi reads the ^V. (This is particularly problematic if you're
136 * talking over a protocol that recognizes signals locally and sends OOB
137 * packets when it sees them.) Second, you can turn the ^C into a literal
138 * character in vi, but that means that there's a race between entering
139 * ^V<character>^C, i.e. the sequence may end up being ^V^C<character>.
140 * Also, the second solution doesn't work for flow control characters, as
141 * they aren't delivered to the program as signals.
142 *
143 * Generally, this is what historic vi did. (It didn't have the curses
144 * problems because it didn't use curses.) It entered signals following
145 * ^V characters into the input stream, (which is why there's no way to
146 * enter a literal flow control character).
147 *
148 * 3: Run in mostly raw mode; turn signals on when doing an operation the
149 * user might want to interrupt, but leave them off most of the time.
150 *
151 * This works well for things like file reads and writes. This doesn't
152 * work well for trying to detect infinite maps. The problem is that
153 * you can write the code so that you don't have to turn on interrupts
154 * per keystroke, but the code isn't pretty and it's hard to make sure
155 * that an optimization doesn't cover up an infinite loop. This also
156 * requires interaction or state between the vi parser and the key
157 * reading routines, as an infinite loop may still be returning keys
158 * to the parser.
159 *
160 * Also, if the user inserts an interrupt into the tty queue while the
161 * interrupts are turned off, the key won't be treated as an interrupt,
162 * and requiring the user to pound the keyboard to catch an interrupt
163 * window is nasty.
164 *
165 * 4: Run in mostly raw mode, leaving signals on all of the time. Done
166 * by setting raw mode, and twiddling the tty's termios ISIG bit.
167 *
168 * This works well for the interrupt cases, because the code only has
169 * to check to see if the interrupt flag has been set, and can otherwise
170 * ignore signals. It's also less likely that we'll miss a case, and we
171 * don't have to worry about synchronizing between the vi parser and the
172 * key read routines.
173 *
174 * The down side is that we have to turn signals off if the user wants
175 * to enter a literal character (e.g. ^V^C). If the user enters the
176 * combination fast enough, or as part of a single network packet,
177 * the text input routines will treat it as a signal instead of as a
178 * literal character. To some extent, we have this problem already,
179 * since we turn off flow control so that the user can enter literal
180 * XON/XOFF characters.
181 *
182 * This is probably the easiest to code, and provides the smoothest
183 * programming interface.
184 *
185 * There are a couple of other problems to consider.
186 *
187 * First, System V's curses doesn't handle SIGTSTP correctly. If you use the
188 * newterm() interface, the TSTP signal will leave you in raw mode, and the
189 * final endwin() will leave you in the correct shell mode. If you use the
190 * initscr() interface, the TSTP signal will return you to the correct shell
191 * mode, but the final endwin() will leave you in raw mode. There you have
192 * it: proof that drug testing is not making any significant headway in the
193 * computer industry. The 4BSD curses is deficient in that it does not have
194 * an interface to the terminal keypad. So, regardless, we have to do our
195 * own SIGTSTP handling.
196 *
197 * The problem with this is that if we do our own SIGTSTP handling, in either
198 * models #3 or #4, we're going to have to call curses routines at interrupt
199 * time, which means that we might be reentering curses, which is something we
200 * don't want to do.
201 *
202 * Second, SIGTSTP has its own little problems. It's broadcast to the entire
203 * process group, not sent to a single process. The scenario goes something
204 * like this: the shell execs the mail program, which execs vi. The user hits
205 * ^Z, and all three programs get the signal, in some random order. The mail
206 * program goes to sleep immediately (since it probably didn't have a SIGTSTP
207 * handler in place). The shell gets a SIGCHLD, does a wait, and finds out
208 * that the only child in its foreground process group (of which it's aware)
209 * is asleep. It then optionally resets the terminal (because the modes aren't
210 * how it left them), and starts prompting the user for input. The problem is
211 * that somewhere in the middle of all of this, vi is resetting the terminal,
212 * and getting ready to send a SIGTSTP to the process group in order to put
213 * itself to sleep. There's a solution to all of this: when vi starts, it puts
214 * itself into its own process group, and then only it (and possible child
215 * processes) receive the SIGTSTP. This permits it to clean up the terminal
216 * and switch back to the original process group, where it sends that process
217 * group a SIGTSTP, putting everyone to sleep and waking the shell.
218 *
219 * Third, handing SIGTSTP asynchronously is further complicated by the child
220 * processes vi may fork off. If vi calls ex, ex resets the terminal and
221 * starts running some filter, and SIGTSTP stops them both, vi has to know
222 * when it restarts that it can't repaint the screen until ex's child has
223 * finished running. This is solveable, but it's annoying.
224 *
225 * Well, somebody had to make a decision, and this is the way it's going to be
226 * (unless I get talked out of it). SIGINT is handled asynchronously, so
227 * that we can pretty much guarantee that the user can interrupt any operation
228 * at any time. SIGTSTP is handled synchronously, so that we don't have to
229 * reenter curses and so that we don't have to play the process group games.
230 * ^Z is recognized in the standard text input and command modes. (^Z should
231 * also be recognized during operations that may potentially take a long time.
232 * The simplest solution is probably to twiddle the tty, install a handler for
233 * SIGTSTP, and then restore normal tty modes when the operation is complete.)
234 */
235
236/*
237 * sig_init --
238 * Initialize signals.
239 */
240int
241sig_init(sp)
242 SCR *sp;
243{
244 GS *gp;
245 struct sigaction act;
246
247 /* Initialize the signals. */
248 gp = sp->gp;
249 (void)sigemptyset(&gp->blockset);
250
251 /*
252 * Use sigaction(2), not signal(3), since we don't always want to
253 * restart system calls. The example is when waiting for a command
254 * mode keystroke and SIGWINCH arrives. Try to set the restart bit
255 * (SA_RESTART) on SIGALRM anyway, it should result in a lot fewer
256 * interruptions. We also block every other signal that we can block
257 * when a signal arrives. This is because the signal functions call
258 * other nvi functions, which aren't guaranteed to be reentrant.
259 */
260
261#ifndef SA_RESTART
262#define SA_RESTART 0
263#endif
264#define SETSIG(signal, flags, handler) { \
265 if (sigaddset(&gp->blockset, signal)) \
266 goto err; \
267 act.sa_handler = handler; \
268 sigfillset(&act.sa_mask); \
269 act.sa_flags = flags; \
270 if (sigaction(signal, &act, NULL)) \
271 goto err; \
272}
273 SETSIG(SIGALRM, SA_RESTART, h_alrm);
274 SETSIG(SIGHUP, 0, h_hup);
275 SETSIG(SIGINT, 0, h_int);
276 SETSIG(SIGTERM, 0, h_term);
277 SETSIG(SIGWINCH, 0, h_winch);
278 return (0);
279
280err: msgq(sp, M_SYSERR, "signal init");
281 return (1);
282}
283
284/*
285 * sig_end --
286 * End signal setup.
287 */
288void
289sig_end(sp)
290 SCR *sp;
291{
292 /*
293 * POSIX 1003.1b-1993 states:
294 * Fork (and, presumably, vfork) clear pending signals, but that
295 * other than that, the child and parent have equivalent signal
296 * behavior.
297 *
298 * Exec leaves SIG_DFL and SIG_IGN signals alone; signals caught
299 * by the process are reset to SIG_DFL. The process signal mask
300 * and pending signals are left alone.
301 *
302 * We don't currently ignore signals, so there's no cleanup to be done
303 * there. No signals should be pending, so there's no cleanup to be
304 * done there. However, any signals that are currently blocked should
305 * be unblocked, so that they behave normally.
306 */
307 SIGUNBLOCK(sp->gp);
308}
309
310/*
311 * busy_on --
312 * Set a busy message timer.
313 */
314int
315busy_on(sp, msg)
316 SCR *sp;
317 char const *msg;
318{
319 struct itimerval value;
320 struct timeval tod;
321
322 /*
323 * Give the oldest busy message precedence, since it's
324 * the longer running operation.
325 */
326 if (sp->busy_msg != NULL)
327 return (1);
328
329 /* Get the current time of day, and create a target time. */
330 if (gettimeofday(&tod, NULL))
331 return (1);
332#define USER_PATIENCE_USECS (8 * 100000L)
333 sp->busy_tod.tv_sec = tod.tv_sec;
334 sp->busy_tod.tv_usec = tod.tv_usec + USER_PATIENCE_USECS;
335
336 /* We depend on this being an atomic instruction. */
337 sp->busy_msg = msg;
338
339 /*
340 * Busy messages turn around fast. Reset the timer regardless
341 * of its current state.
342 */
343 value.it_value.tv_sec = 0;
344 value.it_value.tv_usec = USER_PATIENCE_USECS;
345 value.it_interval.tv_sec = 0;
346 value.it_interval.tv_usec = 0;
347 if (setitimer(ITIMER_REAL, &value, NULL))
348 msgq(sp, M_SYSERR, "timer: setitimer");
349 return (0);
350}
351
352/*
353 * busy_off --
354 * Turn off a busy message timer.
355 */
356void
357busy_off(sp)
358 SCR *sp;
359{
360 /* We depend on this being an atomic instruction. */
361 sp->busy_msg = NULL;
362}
363
364/*
365 * rcv_on --
366 * Turn on recovery timer.
367 */
368int
369rcv_on(sp)
370 SCR *sp;
371{
372 EXF *ep;
373 struct itimerval value;
374 struct timeval tod;
375
376 /* Get the current time of day. */
377 if (gettimeofday(&tod, NULL))
378 return (1);
379
380 /* Create target time of day. */
381 ep = sp->ep;
382 ep->rcv_tod.tv_sec = tod.tv_sec + RCV_PERIOD;
383 ep->rcv_tod.tv_usec = 0;
384
385 /*
386 * If there's a busy message happening, we're done, the
387 * interrupt handler will start our timer as necessary.
388 */
389 if (sp->busy_msg != NULL)
390 return (0);
391
392 value.it_value.tv_sec = RCV_PERIOD;
393 value.it_value.tv_usec = 0;
394 value.it_interval.tv_sec = 0;
395 value.it_interval.tv_usec = 0;
396 if (setitimer(ITIMER_REAL, &value, NULL)) {
397 msgq(sp, M_SYSERR, "timer: setitimer");
398 return (1);
399 }
400 return (0);
401}
402
403/*
404 * h_alrm --
405 * Handle SIGALRM.
406 *
407 * There are two uses of the ITIMER_REAL timer (SIGALRM) in nvi. The first
408 * is to push the recovery information out to disk at periodic intervals.
409 * The second is to display a "busy" message if an operation takes more time
410 * that users are willing to wait before seeing something happen. The SCR
411 * structure has a wall clock timer structure for each of these. Since the
412 * busy timer has a much faster timeout than the recovery timer, most of the
413 * code ignores the recovery timer unless it's the only thing running.
414 *
415 * XXX
416 * It would be nice to reimplement this with two timers, a la POSIX 1003.1,
417 * but not many systems offer them yet.
418 */
419static void
420h_alrm(signo)
421 int signo;
422{
423 struct itimerval value;
424 struct timeval ntod, tod;
425 SCR *sp;
426 EXF *ep;
427 int sverrno;
428
429 sverrno = errno;
430
431 /* XXX: Get the current time of day; if this fails, we're dead. */
432 if (gettimeofday(&tod, NULL))
433 goto ret;
434
435 /*
436 * Fire any timers that are past due, or any that are due
437 * in a tenth of a second or less.
438 */
439 for (ntod.tv_sec = 0, sp = __global_list->dq.cqh_first;
440 sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) {
441
442 /* Check the busy timer if the msg pointer is set. */
443 if (sp->busy_msg == NULL)
444 goto skip_busy;
445 if (sp->busy_tod.tv_sec > tod.tv_sec ||
446 sp->busy_tod.tv_sec == tod.tv_sec &&
447 sp->busy_tod.tv_usec > tod.tv_usec &&
448 sp->busy_tod.tv_usec - tod.tv_usec > 100000L) {
449 if (ntod.tv_sec == 0 ||
450 ntod.tv_sec > sp->busy_tod.tv_sec ||
451 ntod.tv_sec == sp->busy_tod.tv_sec &&
452 ntod.tv_usec > sp->busy_tod.tv_usec)
453 ntod = sp->busy_tod;
454 } else {
455 (void)sp->s_busy(sp, sp->busy_msg);
456 sp->busy_msg = NULL;
457 }
458
459 /*
460 * Sync the file if the recovery timer has fired. If
461 * the sync fails, we don't reschedule future sync's.
462 */
463skip_busy: ep = sp->ep;
464 if (ep == NULL)
465 continue;
466 if (ep->rcv_tod.tv_sec < tod.tv_sec ||
467 ep->rcv_tod.tv_sec == tod.tv_sec &&
468 ep->rcv_tod.tv_usec < tod.tv_usec + 100000L) {
469 if (rcv_sync(sp, 0))
470 continue;
471 ep->rcv_tod = tod;
472 ep->rcv_tod.tv_sec += RCV_PERIOD;
473 }
474 if (ntod.tv_sec == 0 ||
475 ntod.tv_sec > ep->rcv_tod.tv_sec ||
476 ntod.tv_sec == ep->rcv_tod.tv_sec &&
477 ntod.tv_usec > ep->rcv_tod.tv_usec)
478 ntod = ep->rcv_tod;
479 }
480
481 if (ntod.tv_sec == 0)
482 goto ret;
483
484 /* XXX: Set the timer; if this fails, we're dead. */
485 value.it_value.tv_sec = ntod.tv_sec - tod.tv_sec;
486 value.it_value.tv_usec = ntod.tv_usec - tod.tv_usec;
487 value.it_interval.tv_sec = 0;
488 value.it_interval.tv_usec = 0;
489 (void)setitimer(ITIMER_REAL, &value, NULL);
490
491ret: errno = sverrno;
492}
493
494/*
495 * h_hup --
496 * Handle SIGHUP.
497 */
498static void
499h_hup(signo)
500 int signo;
501{
502 sig_sync(SIGHUP, RCV_EMAIL);
503 /* NOTREACHED */
504}
505
506/*
507 * h_int --
508 * Handle SIGINT.
509 *
510 * XXX
511 * This isn't right if windows are independent of each other.
512 */
513static void
514h_int(signo)
515 int signo;
516{
517 F_SET(__global_list, G_SIGINT);
518}
519
520/*
521 * h_term --
522 * Handle SIGTERM.
523 */
524static void
525h_term(signo)
526 int signo;
527{
528 sig_sync(SIGTERM, 0);
529 /* NOTREACHED */
530}
531
532/*
533 * h_winch --
534 * Handle SIGWINCH.
535 *
536 * XXX
537 * This isn't right if windows are independent of each other.
538 */
539static void
540h_winch(signo)
541 int signo;
542{
543 F_SET(__global_list, G_SIGWINCH);
544}
545
546
547/*
548 * sig_sync --
549 *
550 * Sync the files based on a signal.
551 */
552static void
553sig_sync(signo, flags)
554 int signo;
555 u_int flags;
556{
557 SCR *sp;
558
559 /*
560 * Walk the lists of screens, sync'ing the files; only sync
561 * each file once.
562 */
563 for (sp = __global_list->dq.cqh_first;
564 sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
565 rcv_sync(sp, RCV_ENDSESSION | RCV_PRESERVE | flags);
566 for (sp = __global_list->hq.cqh_first;
567 sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
568 rcv_sync(sp, RCV_ENDSESSION | RCV_PRESERVE | flags);
569
570 /*
571 * Die with the proper exit status. Don't bother using
572 * sigaction(2) 'cause we want the default behavior.
573 */
574 (void)signal(signo, SIG_DFL);
575 (void)kill(getpid(), signo);
576 /* NOTREACHED */
577
578 exit (1);
579}