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