summaryrefslogtreecommitdiffstats
path: root/usr.bin/vi/common/signal.c
blob: 362c0fb7d5819c5d3f432e3762cf996d3acb7dd0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
/*-
 * Copyright (c) 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef lint
static char sccsid[] = "@(#)signal.c	8.34 (Berkeley) 8/17/94";
#endif /* not lint */

#include <sys/queue.h>
#include <sys/time.h>

#include <bitstring.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>

#include "compat.h"
#include <db.h>
#include <regex.h>

#include "vi.h"

static void	h_alrm __P((int));
static void	h_hup __P((int));
static void	h_int __P((int));
static void	h_term __P((int));
static void	h_winch __P((int));
static void	sig_sync __P((int, u_int));

/*
 * There are seven normally asynchronous actions about which vi cares:
 * SIGALRM, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP and SIGWINCH.
 *
 * The assumptions:
 *	1: The DB routines are not reentrant.
 *	2: The curses routines may not be reentrant.
 *
 * SIGALRM, SIGHUP, SIGTERM
 *	Used for file recovery.  The DB routines can't be reentered, so
 *	the vi routines that call DB block all three signals (see line.c).
 *	This means that DB routines can be called at interrupt time.
 *
 * SIGALRM
 *	Used to paint busy messages on the screen.  The curses routines
 *	can't be reentered, so this function of SIGALRM can only be used
 *	in sections of code that do not use any curses functions (see
 *	busy_on, busy_off in signal.c).  This means that curses can be
 *	called at interrupt time.
 *
 * SIGQUIT
 *	Disabled by the signal initialization routines.  Historically,
 *	^\ switched vi into ex mode, and we continue that practice.
 *
 * SIGWINCH:
 *	The interrupt routine sets a global bit which is checked by the
 *  	key-read routine, so there are no reentrancy issues.  This means
 *	that the screen will not resize until vi runs out of keys, but
 *	that doesn't seem like a problem.
 *
 * SIGINT and SIGTSTP are a much more difficult issue to resolve.  Vi has
 * to permit the user to interrupt long-running operations.  Generally, a
 * search, substitution or read/write is done on a large file, or, the user
 * creates a key mapping with an infinite loop.  This problem will become
 * worse as more complex semantics are added to vi.  There are four major
 * solutions on the table, each of which have minor permutations.
 *
 * 1:	Run in raw mode.
 *
 *	The up side is that there's no asynchronous behavior to worry about,
 *	and obviously no reentrancy problems.  The down side is that it's easy
 *	to misinterpret characters (e.g. :w big_file^Mi^V^C is going to look
 *	like an interrupt) and it's easy to get into places where we won't see
 *	interrupt characters (e.g. ":map a ixx^[hxxaXXX" infinitely loops in
 *	historic implementations of vi).  Periodically reading the terminal
 *	input buffer might solve the latter problem, but it's not going to be
 *	pretty.
 *
 *	Also, we're going to be checking for ^C's and ^Z's both, all over
 *	the place -- I hate to litter the source code with that.  For example,
 *	the historic version of vi didn't permit you to suspend the screen if
 *	you were on the colon command line.  This isn't right.  ^Z isn't a vi
 *	command, it's a terminal event.  (Dammit.)
 *
 * 2:	Run in cbreak mode.  There are two problems in this area.  First, the
 *	current curses implementations (both System V and Berkeley) don't give
 *	you clean cbreak modes. For example, the IEXTEN bit is left on, turning
 *	on DISCARD and LNEXT.  To clarify, what vi WANTS is 8-bit clean, with
 *	the exception that flow control and signals are turned on, and curses
 *	cbreak mode doesn't give you this.
 *
 *	We can either set raw mode and twiddle the tty, or cbreak mode and
 *	twiddle the tty.  I chose to use raw mode, on the grounds that raw
 *	mode is better defined and I'm less likely to be surprised by a curses
 *	implementation down the road.  The twiddling consists of setting ISIG,
 *	IXON/IXOFF, and disabling some of the interrupt characters (see the
 *	comments in svi/svi_screen.c).  This is all found in historic System
 *	V (SVID 3) and POSIX 1003.1-1992, so it should be fairly portable.
 *
 *	The second problem is that vi permits you to enter literal signal
 *	characters, e.g. ^V^C.  There are two possible solutions.  First, you
 *	can turn off signals when you get a ^V, but that means that a network
 *	packet containing ^V and ^C will lose, since the ^C may take effect
 *	before vi reads the ^V.  (This is particularly problematic if you're
 *	talking over a protocol that recognizes signals locally and sends OOB
 *	packets when it sees them.)  Second, you can turn the ^C into a literal
 *	character in vi, but that means that there's a race between entering
 *	^V<character>^C, i.e. the sequence may end up being ^V^C<character>.
 *	Also, the second solution doesn't work for flow control characters, as
 *	they aren't delivered to the program as signals.
 *
 *	Generally, this is what historic vi did.  (It didn't have the curses
 *	problems because it didn't use curses.)  It entered signals following
 *	^V characters into the input stream, (which is why there's no way to
 *	enter a literal flow control character).
 *
 * 3:	Run in mostly raw mode; turn signals on when doing an operation the
 *	user might want to interrupt, but leave them off most of the time.
 *
 *	This works well for things like file reads and writes.  This doesn't
 *	work well for trying to detect infinite maps.  The problem is that
 *	you can write the code so that you don't have to turn on interrupts
 *	per keystroke, but the code isn't pretty and it's hard to make sure
 *	that an optimization doesn't cover up an infinite loop.  This also
 *	requires interaction or state between the vi parser and the key
 *	reading routines, as an infinite loop may still be returning keys
 *	to the parser.
 *
 *	Also, if the user inserts an interrupt into the tty queue while the
 *	interrupts are turned off, the key won't be treated as an interrupt,
 *	and requiring the user to pound the keyboard to catch an interrupt
 *	window is nasty.
 *
 * 4:	Run in mostly raw mode, leaving signals on all of the time.  Done
 *	by setting raw mode, and twiddling the tty's termios ISIG bit.
 *
 *	This works well for the interrupt cases, because the code only has
 *	to check to see if the interrupt flag has been set, and can otherwise
 *	ignore signals.  It's also less likely that we'll miss a case, and we
 *	don't have to worry about synchronizing between the vi parser and the
 *	key read routines.
 *
 *	The down side is that we have to turn signals off if the user wants
 *	to enter a literal character (e.g. ^V^C).  If the user enters the
 *	combination fast enough, or as part of a single network packet,
 *	the text input routines will treat it as a signal instead of as a
 *	literal character.  To some extent, we have this problem already,
 *	since we turn off flow control so that the user can enter literal
 *	XON/XOFF characters.
 *
 *	This is probably the easiest to code, and provides the smoothest
 *	programming interface.
 *
 * There are a couple of other problems to consider.
 *
 * First, System V's curses doesn't handle SIGTSTP correctly.  If you use the
 * newterm() interface, the TSTP signal will leave you in raw mode, and the
 * final endwin() will leave you in the correct shell mode.  If you use the
 * initscr() interface, the TSTP signal will return you to the correct shell
 * mode, but the final endwin() will leave you in raw mode.  There you have
 * it: proof that drug testing is not making any significant headway in the
 * computer industry.  The 4BSD curses is deficient in that it does not have
 * an interface to the terminal keypad.  So, regardless, we have to do our
 * own SIGTSTP handling.
 *
 * The problem with this is that if we do our own SIGTSTP handling, in either
 * models #3 or #4, we're going to have to call curses routines at interrupt
 * time, which means that we might be reentering curses, which is something we
 * don't want to do.
 *
 * Second, SIGTSTP has its own little problems.  It's broadcast to the entire
 * process group, not sent to a single process.  The scenario goes something
 * like this: the shell execs the mail program, which execs vi.  The user hits
 * ^Z, and all three programs get the signal, in some random order.  The mail
 * program goes to sleep immediately (since it probably didn't have a SIGTSTP
 * handler in place).  The shell gets a SIGCHLD, does a wait, and finds out
 * that the only child in its foreground process group (of which it's aware)
 * is asleep.  It then optionally resets the terminal (because the modes aren't
 * how it left them), and starts prompting the user for input.  The problem is
 * that somewhere in the middle of all of this, vi is resetting the terminal,
 * and getting ready to send a SIGTSTP to the process group in order to put
 * itself to sleep.  There's a solution to all of this: when vi starts, it puts
 * itself into its own process group, and then only it (and possible child
 * processes) receive the SIGTSTP.  This permits it to clean up the terminal
 * and switch back to the original process group, where it sends that process
 * group a SIGTSTP, putting everyone to sleep and waking the shell.
 *
 * Third, handing SIGTSTP asynchronously is further complicated by the child
 * processes vi may fork off.  If vi calls ex, ex resets the terminal and
 * starts running some filter, and SIGTSTP stops them both, vi has to know
 * when it restarts that it can't repaint the screen until ex's child has
 * finished running.  This is solveable, but it's annoying.
 *
 * Well, somebody had to make a decision, and this is the way it's going to be
 * (unless I get talked out of it).  SIGINT is handled asynchronously, so
 * that we can pretty much guarantee that the user can interrupt any operation
 * at any time.  SIGTSTP is handled synchronously, so that we don't have to
 * reenter curses and so that we don't have to play the process group games.
 * ^Z is recognized in the standard text input and command modes.  (^Z should
 * also be recognized during operations that may potentially take a long time.
 * The simplest solution is probably to twiddle the tty, install a handler for
 * SIGTSTP, and then restore normal tty modes when the operation is complete.)
 */

/*
 * sig_init --
 *	Initialize signals.
 */
int
sig_init(sp)
	SCR *sp;
{
	GS *gp;
	struct sigaction act;

	/* Initialize the signals. */
	gp = sp->gp;
	(void)sigemptyset(&gp->blockset);

	/*
	 * Use sigaction(2), not signal(3), since we don't always want to
	 * restart system calls.  The example is when waiting for a command
	 * mode keystroke and SIGWINCH arrives.  Try to set the restart bit
	 * (SA_RESTART) on SIGALRM anyway, it should result in a lot fewer
	 * interruptions.  We also block every other signal that we can block
	 * when a signal arrives.  This is because the signal functions call
	 * other nvi functions, which aren't guaranteed to be reentrant.
	 */

#ifndef	SA_RESTART
#define	SA_RESTART	0
#endif
#define	SETSIG(signal, flags, handler) {				\
	if (sigaddset(&gp->blockset, signal))				\
		goto err;						\
	act.sa_handler = handler;					\
	sigfillset(&act.sa_mask);					\
	act.sa_flags = flags;						\
	if (sigaction(signal, &act, NULL))				\
		goto err;						\
}
	SETSIG(SIGALRM, SA_RESTART, h_alrm);
	SETSIG(SIGHUP, 0, h_hup);
	SETSIG(SIGINT, 0, h_int);
	SETSIG(SIGTERM, 0, h_term);
	SETSIG(SIGWINCH, 0, h_winch);
	return (0);

err:	msgq(sp, M_SYSERR, "signal init");
	return (1);
}

/*
 * sig_end --
 *	End signal setup.
 */
void
sig_end()
{
	/*
	 * POSIX 1003.1-1990 requires that fork (and, presumably, vfork) clear
	 * pending alarms, and that the exec functions clear pending signals.
	 * In addition, after an exec, the child continues to ignore signals
	 * ignored in the parent, and the child's action for signals caught in
	 * the parent is set to the default action.  So, as we currently don't
	 * ignore any signals, there's no cleanup to be done.  This routine is
	 * left here as a stub function.
	 */
	 return;
}

/*
 * busy_on --
 *	Set a busy message timer.
 */
int
busy_on(sp, msg)
	SCR *sp;
	char const *msg;
{
	struct itimerval value;
	struct timeval tod;

	/*
	 * Give the oldest busy message precedence, since it's
	 * the longer running operation.
	 */
	if (sp->busy_msg != NULL)
		return (1);

	/* Get the current time of day, and create a target time. */
	if (gettimeofday(&tod, NULL))
		return (1);
#define	USER_PATIENCE_USECS	(8 * 100000L)
	sp->busy_tod.tv_sec = tod.tv_sec;
	sp->busy_tod.tv_usec = tod.tv_usec + USER_PATIENCE_USECS;

	/* We depend on this being an atomic instruction. */
	sp->busy_msg = msg;

	/*
	 * Busy messages turn around fast.  Reset the timer regardless
	 * of its current state.
	 */
	value.it_value.tv_sec = 0;
	value.it_value.tv_usec = USER_PATIENCE_USECS;
	value.it_interval.tv_sec = 0;
	value.it_interval.tv_usec = 0;
	if (setitimer(ITIMER_REAL, &value, NULL))
		msgq(sp, M_SYSERR, "timer: setitimer");
	return (0);
}

/*
 * busy_off --
 *	Turn off a busy message timer.
 */
void
busy_off(sp)
	SCR *sp;
{
	/* We depend on this being an atomic instruction. */
	sp->busy_msg = NULL;
}

/*
 * rcv_on --
 *	Turn on recovery timer.
 */
int
rcv_on(sp, ep)
	SCR *sp;
	EXF *ep;
{
	struct itimerval value;
	struct timeval tod;

	/* Get the current time of day. */
	if (gettimeofday(&tod, NULL))
		return (1);

	/* Create target time of day. */
	ep->rcv_tod.tv_sec = tod.tv_sec + RCV_PERIOD;
	ep->rcv_tod.tv_usec = 0;

	/*
	 * If there's a busy message happening, we're done, the
	 * interrupt handler will start our timer as necessary.
	 */
	if (sp->busy_msg != NULL)
		return (0);

	value.it_value.tv_sec = RCV_PERIOD;
	value.it_value.tv_usec = 0;
	value.it_interval.tv_sec = 0;
	value.it_interval.tv_usec = 0;
	if (setitimer(ITIMER_REAL, &value, NULL)) {
		msgq(sp, M_SYSERR, "timer: setitimer");
		return (1);
	}
	return (0);
}

/*
 * h_alrm --
 *	Handle SIGALRM.
 *
 * There are two uses of the ITIMER_REAL timer (SIGALRM) in nvi.  The first
 * is to push the recovery information out to disk at periodic intervals.
 * The second is to display a "busy" message if an operation takes more time
 * that users are willing to wait before seeing something happen.  The SCR
 * structure has a wall clock timer structure for each of these.  Since the
 * busy timer has a much faster timeout than the recovery timer, most of the
 * code ignores the recovery timer unless it's the only thing running.
 *
 * XXX
 * It would be nice to reimplement this with two timers, a la POSIX 1003.1,
 * but not many systems offer them yet.
 */
static void
h_alrm(signo)
	int signo;
{
	struct itimerval value;
	struct timeval ntod, tod;
	SCR *sp;
	EXF *ep;
	int sverrno;

	sverrno = errno;

	/* XXX: Get the current time of day; if this fails, we're dead. */
	if (gettimeofday(&tod, NULL))
		goto ret;

	/*
	 * Fire any timers that are past due, or any that are due
	 * in a tenth of a second or less.
	 */
	for (ntod.tv_sec = 0, sp = __global_list->dq.cqh_first;
	    sp != (void *)&__global_list->dq; sp = sp->q.cqe_next) {

		/* Check the busy timer if the msg pointer is set. */
		if (sp->busy_msg == NULL)
			goto skip_busy;
		if (sp->busy_tod.tv_sec > tod.tv_sec ||
		    sp->busy_tod.tv_sec == tod.tv_sec &&
		    sp->busy_tod.tv_usec > tod.tv_usec &&
		    sp->busy_tod.tv_usec - tod.tv_usec > 100000L) {
			if (ntod.tv_sec == 0 ||
			    ntod.tv_sec > sp->busy_tod.tv_sec ||
			    ntod.tv_sec == sp->busy_tod.tv_sec &&
			    ntod.tv_usec > sp->busy_tod.tv_usec)
				ntod = sp->busy_tod;
		} else {
			(void)sp->s_busy(sp, sp->busy_msg);
			sp->busy_msg = NULL;
		}

		/*
		 * Sync the file if the recovery timer has fired.  If
		 * the sync fails, we don't reschedule future sync's.
		 */
skip_busy:	ep = sp->ep;
		if (ep->rcv_tod.tv_sec < tod.tv_sec ||
		    ep->rcv_tod.tv_sec == tod.tv_sec &&
		    ep->rcv_tod.tv_usec < tod.tv_usec + 100000L) {
			if (rcv_sync(sp, ep, 0))
				continue;
			ep->rcv_tod = tod;
			ep->rcv_tod.tv_sec += RCV_PERIOD;
		}
		if (ntod.tv_sec == 0 ||
		    ntod.tv_sec > ep->rcv_tod.tv_sec ||
		    ntod.tv_sec == ep->rcv_tod.tv_sec &&
		    ntod.tv_usec > ep->rcv_tod.tv_usec)
			ntod = ep->rcv_tod;
	}

	if (ntod.tv_sec == 0)
		goto ret;

	/* XXX: Set the timer; if this fails, we're dead. */
	value.it_value.tv_sec = ntod.tv_sec - tod.tv_sec;
	value.it_value.tv_usec = ntod.tv_usec - tod.tv_usec;
	value.it_interval.tv_sec = 0;
	value.it_interval.tv_usec = 0;
	(void)setitimer(ITIMER_REAL, &value, NULL);

ret:	errno = sverrno;
}

/*
 * h_hup --
 *	Handle SIGHUP.
 */
static void
h_hup(signo)
	int signo;
{
	sig_sync(SIGHUP, RCV_EMAIL);
	/* NOTREACHED */
}

/*
 * h_int --
 *	Handle SIGINT.
 *
 * XXX
 * This isn't right if windows are independent of each other.
 */
static void
h_int(signo)
	int signo;
{
	F_SET(__global_list, G_SIGINT);
}

/*
 * h_term --
 *	Handle SIGTERM.
 */
static void
h_term(signo)
	int signo;
{
	sig_sync(SIGTERM, 0);
	/* NOTREACHED */
}

/*
 * h_winch --
 *	Handle SIGWINCH.
 *
 * XXX
 * This isn't right if windows are independent of each other.
 */
static void
h_winch(signo)
	int signo;
{
	F_SET(__global_list, G_SIGWINCH);
}


/*
 * sig_sync --
 *
 *	Sync the files based on a signal.
 */
static void
sig_sync(signo, flags)
	int signo;
	u_int flags;
{
	SCR *sp;

	/*
	 * Walk the lists of screens, sync'ing the files; only sync
	 * each file once.
	 */
	for (sp = __global_list->dq.cqh_first;
	    sp != (void *)&__global_list->dq; sp = sp->q.cqe_next)
		rcv_sync(sp, sp->ep, RCV_ENDSESSION | RCV_PRESERVE | flags);
	for (sp = __global_list->hq.cqh_first;
	    sp != (void *)&__global_list->hq; sp = sp->q.cqe_next)
		rcv_sync(sp, sp->ep, RCV_ENDSESSION | RCV_PRESERVE | flags);

	/*
	 * Die with the proper exit status.  Don't bother using
	 * sigaction(2) 'cause we want the default behavior.
	 */
	(void)signal(signo, SIG_DFL);
	(void)kill(getpid(), signo);
	/* NOTREACHED */

	exit (1);
}
OpenPOWER on IntegriCloud