xref: /dragonfly/contrib/nvi2/cl/README.signal (revision b1ac2ebb)
1*e0b8e63eSJohn MarinoThere are six (normally) asynchronous actions about which vi cares:
2*e0b8e63eSJohn MarinoSIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP and SIGWINCH.
3*e0b8e63eSJohn Marino
4*e0b8e63eSJohn MarinoThe assumptions:
5*e0b8e63eSJohn Marino	1: The DB routines are not reentrant.
6*e0b8e63eSJohn Marino	2: The curses routines may not be reentrant.
7*e0b8e63eSJohn Marino	3: Neither DB nor curses will restart system calls.
8*e0b8e63eSJohn Marino
9*e0b8e63eSJohn MarinoXXX
10*e0b8e63eSJohn MarinoNote, most C library functions don't restart system calls.  So, we should
11*e0b8e63eSJohn Marino*probably* start blocking around any imported function that we don't know
12*e0b8e63eSJohn Marinodoesn't make a system call.  This is going to be a genuine annoyance...
13*e0b8e63eSJohn Marino
14*e0b8e63eSJohn MarinoSIGHUP, SIGTERM
15*e0b8e63eSJohn Marino	Used for file recovery.  The DB routines can't be reentered, nor
16*e0b8e63eSJohn Marino	can they handle interrupted system calls, so the vi routines that
17*e0b8e63eSJohn Marino	call DB block signals.  This means that DB routines could be
18*e0b8e63eSJohn Marino	called at interrupt time, if necessary.
19*e0b8e63eSJohn Marino
20*e0b8e63eSJohn MarinoSIGQUIT
21*e0b8e63eSJohn Marino	Disabled by the signal initialization routines.  Historically, ^\
22*e0b8e63eSJohn Marino	switched vi into ex mode, and we continue that practice.
23*e0b8e63eSJohn Marino
24*e0b8e63eSJohn MarinoSIGWINCH:
25*e0b8e63eSJohn Marino	The interrupt routine sets a global bit which is checked by the
26*e0b8e63eSJohn Marino 	key-read routine, so there are no reentrancy issues.  This means
27*e0b8e63eSJohn Marino	that the screen will not resize until vi runs out of keys, but
28*e0b8e63eSJohn Marino	that doesn't seem like a problem.
29*e0b8e63eSJohn Marino
30*e0b8e63eSJohn MarinoSIGINT and SIGTSTP are a much more difficult issue to resolve.  Vi has
31*e0b8e63eSJohn Marinoto permit the user to interrupt long-running operations.  Generally, a
32*e0b8e63eSJohn Marinosearch, substitution or read/write is done on a large file, or, the user
33*e0b8e63eSJohn Marinocreates a key mapping with an infinite loop.  This problem will become
34*e0b8e63eSJohn Marinoworse as more complex semantics are added to vi, especially things like
35*e0b8e63eSJohn Marinomaking it a pure text widget.  There are four major solutions on the table,
36*e0b8e63eSJohn Marinoeach of which have minor permutations.
37*e0b8e63eSJohn Marino
38*e0b8e63eSJohn Marino1:	Run in raw mode.
39*e0b8e63eSJohn Marino
40*e0b8e63eSJohn Marino	The up side is that there's no asynchronous behavior to worry about,
41*e0b8e63eSJohn Marino	and obviously no reentrancy problems.  The down side is that it's easy
42*e0b8e63eSJohn Marino	to misinterpret characters (e.g. :w big_file^Mi^V^C is going to look
43*e0b8e63eSJohn Marino	like an interrupt) and it's easy to get into places where we won't see
44*e0b8e63eSJohn Marino	interrupt characters (e.g. ":map a ixx^[hxxaXXX" infinitely loops in
45*e0b8e63eSJohn Marino	historic implementations of vi).  Periodically reading the terminal
46*e0b8e63eSJohn Marino	input buffer might solve the latter problem, but it's not going to be
47*e0b8e63eSJohn Marino	pretty.
48*e0b8e63eSJohn Marino
49*e0b8e63eSJohn Marino	Also, we're going to be checking for ^C's and ^Z's both, all over
50*e0b8e63eSJohn Marino	the place -- I hate to litter the source code with that.  For example,
51*e0b8e63eSJohn Marino	the historic version of vi didn't permit you to suspend the screen if
52*e0b8e63eSJohn Marino	you were on the colon command line.  This isn't right.  ^Z isn't a vi
53*e0b8e63eSJohn Marino	command, it's a terminal event.  (Dammit.)
54*e0b8e63eSJohn Marino
55*e0b8e63eSJohn Marino2:	Run in cbreak mode.  There are two problems in this area.  First, the
56*e0b8e63eSJohn Marino	current curses implementations (both System V and Berkeley) don't give
57*e0b8e63eSJohn Marino	you clean cbreak modes. For example, the IEXTEN bit is left on, turning
58*e0b8e63eSJohn Marino	on DISCARD and LNEXT.  To clarify, what vi WANTS is 8-bit clean, with
59*e0b8e63eSJohn Marino	the exception that flow control and signals are turned on, and curses
60*e0b8e63eSJohn Marino	cbreak mode doesn't give you this.
61*e0b8e63eSJohn Marino
62*e0b8e63eSJohn Marino	We can either set raw mode and twiddle the tty, or cbreak mode and
63*e0b8e63eSJohn Marino	twiddle the tty.  I chose to use raw mode, on the grounds that raw
64*e0b8e63eSJohn Marino	mode is better defined and I'm less likely to be surprised by a curses
65*e0b8e63eSJohn Marino	implementation down the road.  The twiddling consists of setting ISIG,
66*e0b8e63eSJohn Marino	IXON/IXOFF, and disabling some of the interrupt characters (see the
67*e0b8e63eSJohn Marino	comments in cl_init.c).  This is all found in historic System V (SVID
68*e0b8e63eSJohn Marino	3) and POSIX 1003.1-1992, so it should be fairly portable.
69*e0b8e63eSJohn Marino
70*e0b8e63eSJohn Marino	The second problem is that vi permits you to enter literal signal
71*e0b8e63eSJohn Marino	characters, e.g. ^V^C.  There are two possible solutions.  First, you
72*e0b8e63eSJohn Marino	can turn off signals when you get a ^V, but that means that a network
73*e0b8e63eSJohn Marino	packet containing ^V and ^C will lose, since the ^C may take effect
74*e0b8e63eSJohn Marino	before vi reads the ^V.  (This is particularly problematic if you're
75*e0b8e63eSJohn Marino	talking over a protocol that recognizes signals locally and sends OOB
76*e0b8e63eSJohn Marino	packets when it sees them.)  Second, you can turn the ^C into a literal
77*e0b8e63eSJohn Marino	character in vi, but that means that there's a race between entering
78*e0b8e63eSJohn Marino	^V<character>^C, i.e. the sequence may end up being ^V^C<character>.
79*e0b8e63eSJohn Marino	Also, the second solution doesn't work for flow control characters, as
80*e0b8e63eSJohn Marino	they aren't delivered to the program as signals.
81*e0b8e63eSJohn Marino
82*e0b8e63eSJohn Marino	Generally, this is what historic vi did.  (It didn't have the curses
83*e0b8e63eSJohn Marino	problems because it didn't use curses.)  It entered signals following
84*e0b8e63eSJohn Marino	^V characters into the input stream, (which is why there's no way to
85*e0b8e63eSJohn Marino	enter a literal flow control character).
86*e0b8e63eSJohn Marino
87*e0b8e63eSJohn Marino3:	Run in mostly raw mode; turn signals on when doing an operation the
88*e0b8e63eSJohn Marino	user might want to interrupt, but leave them off most of the time.
89*e0b8e63eSJohn Marino
90*e0b8e63eSJohn Marino	This works well for things like file reads and writes.  This doesn't
91*e0b8e63eSJohn Marino	work well for trying to detect infinite maps.  The problem is that
92*e0b8e63eSJohn Marino	you can write the code so that you don't have to turn on interrupts
93*e0b8e63eSJohn Marino	per keystroke, but the code isn't pretty and it's hard to make sure
94*e0b8e63eSJohn Marino	that an optimization doesn't cover up an infinite loop.  This also
95*e0b8e63eSJohn Marino	requires interaction or state between the vi parser and the key
96*e0b8e63eSJohn Marino	reading routines, as an infinite loop may still be returning keys
97*e0b8e63eSJohn Marino	to the parser.
98*e0b8e63eSJohn Marino
99*e0b8e63eSJohn Marino	Also, if the user inserts an interrupt into the tty queue while the
100*e0b8e63eSJohn Marino	interrupts are turned off, the key won't be treated as an interrupt,
101*e0b8e63eSJohn Marino	and requiring the user to pound the keyboard to catch an interrupt
102*e0b8e63eSJohn Marino	window is nasty.
103*e0b8e63eSJohn Marino
104*e0b8e63eSJohn Marino4:	Run in mostly raw mode, leaving signals on all of the time.  Done
105*e0b8e63eSJohn Marino	by setting raw mode, and twiddling the tty's termios ISIG bit.
106*e0b8e63eSJohn Marino
107*e0b8e63eSJohn Marino	This works well for the interrupt cases, because the code only has
108*e0b8e63eSJohn Marino	to check to see if the interrupt flag has been set, and can otherwise
109*e0b8e63eSJohn Marino	ignore signals.  It's also less likely that we'll miss a case, and we
110*e0b8e63eSJohn Marino	don't have to worry about synchronizing between the vi parser and the
111*e0b8e63eSJohn Marino	key read routines.
112*e0b8e63eSJohn Marino
113*e0b8e63eSJohn Marino	The down side is that we have to turn signals off if the user wants
114*e0b8e63eSJohn Marino	to enter a literal character (e.g. ^V^C).  If the user enters the
115*e0b8e63eSJohn Marino	combination fast enough, or as part of a single network packet,
116*e0b8e63eSJohn Marino	the text input routines will treat it as a signal instead of as a
117*e0b8e63eSJohn Marino	literal character.  To some extent, we have this problem already,
118*e0b8e63eSJohn Marino	since we turn off flow control so that the user can enter literal
119*e0b8e63eSJohn Marino	XON/XOFF characters.
120*e0b8e63eSJohn Marino
121*e0b8e63eSJohn Marino	This is probably the easiest to code, and provides the smoothest
122*e0b8e63eSJohn Marino	programming interface.
123*e0b8e63eSJohn Marino
124*e0b8e63eSJohn MarinoThere are a couple of other problems to consider.
125*e0b8e63eSJohn Marino
126*e0b8e63eSJohn MarinoFirst, System V's curses doesn't handle SIGTSTP correctly.  If you use the
127*e0b8e63eSJohn Marinonewterm() interface, the TSTP signal will leave you in raw mode, and the
128*e0b8e63eSJohn Marinofinal endwin() will leave you in the correct shell mode.  If you use the
129*e0b8e63eSJohn Marinoinitscr() interface, the TSTP signal will return you to the correct shell
130*e0b8e63eSJohn Marinomode, but the final endwin() will leave you in raw mode.  There you have
131*e0b8e63eSJohn Marinoit: proof that drug testing is not making any significant headway in the
132*e0b8e63eSJohn Marinocomputer industry.  The 4BSD curses is deficient in that it does not have
133*e0b8e63eSJohn Marinoan interface to the terminal keypad.  So, regardless, we have to do our
134*e0b8e63eSJohn Marinoown SIGTSTP handling.
135*e0b8e63eSJohn Marino
136*e0b8e63eSJohn MarinoThe problem with this is that if we do our own SIGTSTP handling, in either
137*e0b8e63eSJohn Marinomodels #3 or #4, we're going to have to call curses routines at interrupt
138*e0b8e63eSJohn Marinotime, which means that we might be reentering curses, which is something we
139*e0b8e63eSJohn Marinodon't want to do.
140*e0b8e63eSJohn Marino
141*e0b8e63eSJohn MarinoSecond, SIGTSTP has its own little problems.  It's broadcast to the entire
142*e0b8e63eSJohn Marinoprocess group, not sent to a single process.  The scenario goes something
143*e0b8e63eSJohn Marinolike this: the shell execs the mail program, which execs vi.  The user hits
144*e0b8e63eSJohn Marino^Z, and all three programs get the signal, in some random order.  The mail
145*e0b8e63eSJohn Marinoprogram goes to sleep immediately (since it probably didn't have a SIGTSTP
146*e0b8e63eSJohn Marinohandler in place).  The shell gets a SIGCHLD, does a wait, and finds out
147*e0b8e63eSJohn Marinothat the only child in its foreground process group (of which it's aware)
148*e0b8e63eSJohn Marinois asleep.  It then optionally resets the terminal (because the modes aren't
149*e0b8e63eSJohn Marinohow it left them), and starts prompting the user for input.  The problem is
150*e0b8e63eSJohn Marinothat somewhere in the middle of all of this, vi is resetting the terminal,
151*e0b8e63eSJohn Marinoand getting ready to send a SIGTSTP to the process group in order to put
152*e0b8e63eSJohn Marinoitself to sleep.  There's a solution to all of this: when vi starts, it puts
153*e0b8e63eSJohn Marinoitself into its own process group, and then only it (and possible child
154*e0b8e63eSJohn Marinoprocesses) receive the SIGTSTP.  This permits it to clean up the terminal
155*e0b8e63eSJohn Marinoand switch back to the original process group, where it sends that process
156*e0b8e63eSJohn Marinogroup a SIGTSTP, putting everyone to sleep and waking the shell.
157*e0b8e63eSJohn Marino
158*e0b8e63eSJohn MarinoThird, handing SIGTSTP asynchronously is further complicated by the child
159*e0b8e63eSJohn Marinoprocesses vi may fork off.  If vi calls ex, ex resets the terminal and
160*e0b8e63eSJohn Marinostarts running some filter, and SIGTSTP stops them both, vi has to know
161*e0b8e63eSJohn Marinowhen it restarts that it can't repaint the screen until ex's child has
162*e0b8e63eSJohn Marinofinished running.  This is solveable, but it's annoying.
163*e0b8e63eSJohn Marino
164*e0b8e63eSJohn MarinoWell, somebody had to make a decision, and this is the way it's going to be
165*e0b8e63eSJohn Marino(unless I get talked out of it).  SIGINT is handled asynchronously, so
166*e0b8e63eSJohn Marinothat we can pretty much guarantee that the user can interrupt any operation
167*e0b8e63eSJohn Marinoat any time.  SIGTSTP is handled synchronously, so that we don't have to
168*e0b8e63eSJohn Marinoreenter curses and so that we don't have to play the process group games.
169*e0b8e63eSJohn Marino^Z is recognized in the standard text input and command modes.  (^Z should
170*e0b8e63eSJohn Marinoalso be recognized during operations that may potentially take a long time.
171*e0b8e63eSJohn MarinoThe simplest solution is probably to twiddle the tty, install a handler for
172*e0b8e63eSJohn MarinoSIGTSTP, and then restore normal tty modes when the operation is complete.)
173