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