1 GL_IO_MODE(3TECLA) Interactive Command-line Input Library Functions 2 3 4 5 NAME 6 gl_io_mode, gl_raw_io, gl_normal_io, gl_tty_signals, gl_abandon_line, 7 gl_handle_signal, gl_pending_io - use gl_get_line() from an external 8 event loop 9 10 SYNOPSIS 11 cc [ flag... ] file... -ltecla [ library... ] 12 #include <libtecla.h> 13 14 int gl_io_mode(GetLine *gl, GlIOMode mode); 15 16 17 int gl_raw_io(GetLine *gl); 18 19 20 int gl_normal_io(GetLine *gl); 21 22 23 int gl_tty_signals(void (*term_handler)(int), void (*susp_handler)(int), 24 void (*cont_handler)(int), void (*size_handler)(int)); 25 26 27 void gl_abandon_line(GetLine *gl); 28 29 30 void gl_handle_signal(int signo, GetLine *gl, int ngl); 31 32 33 GlPendingIO gl_pending_io(GetLine *gl); 34 35 36 DESCRIPTION 37 The gl_get_line(3TECLA) function supports two different I/O modes. 38 These are selected by calling the gl_io_mode() function. The mode 39 argument of gl_io_mode() specifies the new I/O mode and must be one of 40 the following. 41 42 GL_NORMAL_MODE 43 Select the normal blocking-I/O mode. In this mode 44 gl_get_line() does not return until either an error 45 occurs of the user finishes entering a new line. 46 47 48 GL_SERVER_MODE 49 Select non-blocking server I/O mode. In this mode, 50 since non-blocking terminal I/O is used, the entry of 51 each new input line typically requires many calls to 52 gl_get_line() from an external I/O-driven event loop. 53 54 55 56 Newly created GetLine objects start in normal I/O mode, so to switch to 57 non-blocking server mode requires an initial call to gl_io_mode(). 58 59 Server I/O Mode 60 In non-blocking server I/O mode, the application is required to have an 61 event loop that calls gl_get_line() whenever the terminal file 62 descriptor can perform the type of I/O that gl_get_line() is waiting 63 for. To determine which type of I/O gl_get_line() is waiting for, the 64 application calls the gl_pending_io() function. The return value is 65 one of the following two enumerated values. 66 67 GLP_READ 68 gl_get_line() is waiting to write a character to the 69 terminal. 70 71 72 GLP_WRITE 73 gl_get_line() is waiting to read a character from the 74 keyboard. 75 76 77 78 If the application is using either the select(3C) or poll(2) function 79 to watch for I/O on a group of file descriptors, then it should call 80 the gl_pending_io() function before each call to these functions to 81 determine which direction of I/O it should tell them to watch for, and 82 configure their arguments accordingly. In the case of the select() 83 function, this means using the FD_SET() macro to add the terminal file 84 descriptor either to the set of file descriptors to be watched for 85 readability or the set to be watched for writability. 86 87 88 As in normal I/O mode, the return value of gl_get_line() is either a 89 pointer to a completed input line or NULL. However, whereas in normal 90 I/O mode a NULL return value always means that an error occurred, in 91 non-blocking server mode, NULL is also returned when gl_get_line() 92 cannot read or write to the terminal without blocking. Thus in non- 93 blocking server mode, in order to determine when a NULL return value 94 signifies that an error occurred or not, it is necessary to call the 95 gl_return_status() function. If this function returns the enumerated 96 value GLR_BLOCKED, gl_get_line() is waiting for I/O and no error has 97 occurred. 98 99 100 When gl_get_line() returns NULL and gl_return_status() indicates that 101 this is due to blocked terminal I/O, the application should call 102 gl_get_line() again when the type of I/O reported by gl_pending_io() 103 becomes possible. The prompt, start_line and start_pos arguments of 104 gl_get_line() will be ignored on these calls. If you need to change the 105 prompt of the line that is currently being edited, you can call the 106 gl_replace_prompt(3TECLA) function between calls to gl_get_line(). 107 108 Giving Up The Terminal 109 A complication that is unique to non-blocking server mode is that it 110 requires that the terminal be left in raw mode between calls to 111 gl_get_line(). If this were not the case, the external event loop would 112 not be able to detect individual key-presses, and the basic line 113 editing implemented by the terminal driver would clash with the editing 114 provided by gl_get_line(). When the terminal needs to be used for 115 purposes other than entering a new input line with gl_get_line(), it 116 needs to be restored to a usable state. In particular, whenever the 117 process is suspended or terminated, the terminal must be returned to a 118 normal state. If this is not done, then depending on the 119 characteristics of the shell that was used to invoke the program, the 120 user could end up with a hung terminal. To this end, the gl_normal_io() 121 function is provided for switching the terminal back to the state that 122 it was in when raw mode was last established. 123 124 125 The gl_normal_io() function first flushes any pending output to the 126 terminal, then moves the cursor to the start of the terminal line which 127 follows the end of the incompletely entered input line. At this point 128 it is safe to suspend or terminate the process, and it is safe for the 129 application to read and write to the terminal. To resume entry of the 130 input line, the application should call the gl_raw_io() function. 131 132 133 The gl_normal_io() function starts a new line, redisplays the partially 134 completed input line (if any), restores the cursor position within this 135 line to where it was when gl_normal_io() was called, then switches back 136 to raw, non-blocking terminal mode ready to continue entry of the input 137 line when gl_get_line() is next called. 138 139 140 Note that in non-blocking server mode, if gl_get_line() is called after 141 a call to gl_normal_io(), without an intervening call to gl_raw_io(), 142 gl_get_line() will call gl_raw_mode() itself, and the terminal will 143 remain in this mode when gl_get_line() returns. 144 145 Signal Handling 146 In the previous section it was pointed out that in non-blocking server 147 mode, the terminal must be restored to a sane state whenever a signal 148 is received that either suspends or terminates the process. In normal 149 I/O mode, this is done for you by gl_get_line(), but in non-blocking 150 server mode, since the terminal is left in raw mode between calls to 151 gl_get_line(), this signal handling has to be done by the application. 152 Since there are many signals that can suspend or terminate a process, 153 as well as other signals that are important to gl_get_line(), such as 154 the SIGWINCH signal, which tells it when the terminal size has changed, 155 the gl_tty_signals() function is provided for installing signal 156 handlers for all pertinent signals. 157 158 159 The gl_tty_signals() function uses gl_get_line()'s internal list of 160 signals to assign specified signal handlers to groups of signals. The 161 arguments of this function are as follows. 162 163 term_handler 164 This is the signal handler that is used to trap signals 165 that by default terminate any process that receives 166 them (for example, SIGINT or SIGTERM). 167 168 169 susp_handler 170 This is the signal handler that is used to trap signals 171 that by default suspend any process that receives them, 172 (for example, SIGTSTP or SIGTTOU). 173 174 175 cont_handler 176 This is the signal handler that is used to trap signals 177 that are usually sent when a process resumes after 178 being suspended (usually SIGCONT). Beware that there is 179 nothing to stop a user from sending one of these 180 signals at other times. 181 182 183 size_handler 184 This signal handler is used to trap signals that are 185 sent to processes when their controlling terminals are 186 resized by the user (for example, SIGWINCH). 187 188 189 190 These arguments can all be the same, if so desired, and SIG_IGN (ignore 191 this signal) or SIG_DFL (use the system-provided default signal 192 handler) can be specified instead of a function where pertinent. In 193 particular, it is rarely useful to trap SIGCONT, so the cont_handler 194 argument will usually be SIG_DFL or SIG_IGN. 195 196 197 The gl_tty_signals() function uses the POSIX sigaction(2) function to 198 install these signal handlers, and it is careful to use the sa_mask 199 member of each sigaction structure to ensure that only one of these 200 signals is ever delivered at a time. This guards against different 201 instances of these signal handlers from simultaneously trying to write 202 to common global data, such as a shared sigsetjmp(3C) buffer or a 203 signal-received flag. The signal handlers installed by this function 204 should call the gl_handle_signal(). 205 206 207 The signo argument tells this function which signal it is being asked 208 to respond to, and the gl argument should be a pointer to the first 209 element of an array of ngl GetLine objects. If your application has 210 only one of these objects, pass its pointer as the gl argument and 211 specify ngl as 1. 212 213 214 Depending on the signal that is being handled, this function does 215 different things. 216 217 Process termination signals 218 If the signal that was caught is one of those that by default 219 terminates any process that receives it, then gl_handle_signal() does 220 the following steps. 221 222 1. First it blocks the delivery of all signals that can be 223 blocked (ie. SIGKILL and SIGSTOP cannot be blocked). 224 225 2. Next it calls gl_normal_io() for each of the ngl GetLine 226 objects. Note that this does nothing to any of the GetLine 227 objects that are not currently in raw mode. 228 229 3. Next it sets the signal handler of the signal to its 230 default, process-termination disposition. 231 232 4. Next it re-sends the process the signal that was caught. 233 234 5. Finally it unblocks delivery of this signal, which results 235 in the process being terminated. 236 237 Process suspension signals 238 If the default disposition of the signal is to suspend the process, the 239 same steps are executed as for process termination signals, except that 240 when the process is later resumed, gl_handle_signal() continues, and 241 does the following steps. 242 243 1. It re-blocks delivery of the signal. 244 245 2. It reinstates the signal handler of the signal to the one 246 that was displaced when its default disposition was 247 substituted. 248 249 3. For any of the GetLine objects that were in raw mode when 250 gl_handle_signal() was called, gl_handle_signal() then calls 251 gl_raw_io(), to resume entry of the input lines on those 252 terminals. 253 254 4. Finally, it restores the signal process mask to how it was 255 when gl_handle_signal() was called. 256 257 258 Note that the process is suspended or terminated using the original 259 signal that was caught, rather than using the uncatchable SIGSTOP and 260 SIGKILL signals. This is important, because when a process is suspended 261 or terminated, the parent of the process may wish to use the status 262 value returned by the wait system call to figure out which signal was 263 responsible. In particular, most shells use this information to print a 264 corresponding message to the terminal. Users would be rightly confused 265 if when their process received a SIGPIPE signal, the program responded 266 by sending itself a SIGKILL signal, and the shell then printed out the 267 provocative statement, "Killed!". 268 269 Interrupting The Event Loop 270 If a signal is caught and handled when the application's event loop is 271 waiting in select() or poll(), these functions will be aborted with 272 errno set to EINTR. When this happens the event loop should call 273 gl_pending_io() before calling select() or poll() again. It should then 274 arrange for select() or poll() to wait for the type of I/O that 275 gl_pending_io() reports. This is necessary because any signal handler 276 that calls gl_handle_signal() will frequently change the type of I/O 277 that gl_get_line() is waiting for. 278 279 280 If a signal arrives between the statements that configure the arguments 281 of select() or poll() and the calls to these functions, the signal will 282 not be seen by these functions, which will then not be aborted. If 283 these functions are waiting for keyboard input from the user when the 284 signal is received, and the signal handler arranges to redraw the input 285 line to accommodate a terminal resize or the resumption of the process. 286 This redisplay will be delayed until the user presses the next key. 287 Apart from puzzling the user, this clearly is not a serious problem. 288 However there is a way, albeit complicated, to completely avoid this 289 race condition. The following steps illustrate this. 290 291 1. Block all of the signals that gl_get_line() catches, by 292 passing the signal set returned by gl_list_signals() to 293 sigprocmask(2). 294 295 2. Call gl_pending_io() and set up the arguments of select() or 296 poll() accordingly. 297 298 3. Call sigsetjmp(3C) with a non-zero savemask argument. 299 300 4. Initially this sigsetjmp() statement will return zero, 301 indicating that control is not resuming there after a 302 matching call to siglongjmp(3C). 303 304 5. Replace all of the handlers of the signals that 305 gl_get_line() is configured to catch, with a signal handler 306 that first records the number of the signal that was caught, 307 in a file-scope variable, then calls siglongjmp() with a 308 non-zero val argument, to return execution to the above 309 sigsetjmp() statement. Registering these signal handlers can 310 conveniently be done using the gl_tty_signals() function. 311 312 6. Set the file-scope variable that the above signal handler 313 uses to record any signal that is caught to -1, so that we 314 can check whether a signal was caught by seeing if it 315 contains a valid signal number. 316 317 7. Now unblock the signals that were blocked in step 1. Any 318 signal that was received by the process in between step 1 319 and now will now be delivered, and trigger our signal 320 handler, as will any signal that is received until we block 321 these signals again. 322 323 8. Now call select() or poll(). 324 325 9. When select returns, again block the signals that were 326 unblocked in step 7. 327 328 If a signal is arrived any time during the above steps, our 329 signal handler will be triggered and cause control to return 330 to the sigsetjmp() statement, where this time, sigsetjmp() 331 will return non-zero, indicating that a signal was caught. 332 When this happens we simply skip the above block of 333 statements, and continue with the following statements, 334 which are executed regardless of whether or not a signal is 335 caught. Note that when sigsetjmp() returns, regardless of 336 why it returned, the process signal mask is returned to how 337 it was when sigsetjmp() was called. Thus the following 338 statements are always executed with all of our signals 339 blocked. 340 341 10. Reinstate the signal handlers that were displaced in step 5. 342 343 11. Check whether a signal was caught, by checking the file- 344 scope variable that the signal handler records signal 345 numbers in. 346 347 12. If a signal was caught, send this signal to the application 348 again and unblock only this signal so that it invokes the 349 signal handler which was just reinstated in step 10. 350 351 13. Unblock all of the signals that were blocked in step 7. 352 353 Signals Caught By gl_get_line() 354 Since the application is expected to handle signals in non-blocking 355 server mode, gl_get_line() does not attempt to duplicate this when it 356 is being called. If one of the signals that it is configured to catch 357 is sent to the application while gl_get_line() is being called, 358 gl_get_line() reinstates the caller's signal handlers, then immediately 359 before returning, re-sends the signal to the process to let the 360 application's signal handler handle it. If the process is not 361 terminated by this signal, gl_get_line() returns NULL, and a following 362 call to gl_return_status() returns the enumerated value GLR_SIGNAL. 363 364 Aborting Line Input 365 Often, rather than letting it terminate the process, applications 366 respond to the SIGINT user-interrupt signal by aborting the current 367 input line. This can be accomplished in non-blocking server-I/O mode by 368 not calling gl_handle_signal() when this signal is caught, but by 369 calling instead the gl_abandon_line() function. This function arranges 370 that when gl_get_line() is next called, it first flushes any pending 371 output to the terminal, discards the current input line, outputs a new 372 prompt on the next line, and finally starts accepting input of a new 373 input line from the user. 374 375 Signal Safe Functions 376 Provided that certain rules are followed, the gl_normal_io(), 377 gl_raw_io(), gl_handle_signal(), and gl_abandon_line() functions can be 378 written to be safely callable from signal handlers. Other functions in 379 this library should not be called from signal handlers. For this to be 380 true, all signal handlers that call these functions must be registered 381 in such a way that only one instance of any one of them can be running 382 at one time. The way to do this is to use the POSIX sigaction() 383 function to register all signal handlers, and when doing this, use the 384 sa_mask member of the corresponding sigaction structure to indicate 385 that all of the signals whose handlers invoke the above functions 386 should be blocked when the current signal is being handled. This 387 prevents two signal handlers from operating on a GetLine object at the 388 same time. 389 390 391 To prevent signal handlers from accessing a GetLine object while 392 gl_get_line() or any of its associated public functions are operating 393 on it, all public functions associated with gl_get_line(), including 394 gl_get_line() itself, temporarily block the delivery of signals when 395 they are accessing GetLine objects. Beware that the only signals that 396 they block are the signals that gl_get_line() is currently configured 397 to catch, so be sure that if you call any of the above functions from 398 signal handlers, that the signals that these handlers are assigned to 399 are configured to be caught by gl_get_line(). See 400 gl_trap_signal(3TECLA). 401 402 Using Timeouts To Poll 403 If instead of using select() or poll() to wait for I/O your application 404 needs only to get out of gl_get_line() periodically to briefly do 405 something else before returning to accept input from the user, use the 406 gl_inactivity_timeout(3TECLA) function in non-blocking server mode to 407 specify that a callback function that returns GLTO_CONTINUE should be 408 called whenever gl_get_line() has been waiting for I/O for more than a 409 specified amount of time. When this callback is triggered, 410 gl_get_line() will return NULL and a following call to 411 gl_return_status() will return GLR_BLOCKED. 412 413 414 The gl_get_line() function will not return until the user has not typed 415 a key for the specified interval, so if the interval is long and the 416 user keeps typing, gl_get_line() might not return for a while. There is 417 no guarantee that it will return in the time specified. 418 419 ATTRIBUTES 420 See attributes(5) for descriptions of the following attributes: 421 422 423 424 425 +--------------------+-----------------+ 426 | ATTRIBUTE TYPE | ATTRIBUTE VALUE | 427 +--------------------+-----------------+ 428 |Interface Stability | Evolving | 429 +--------------------+-----------------+ 430 |MT-Level | MT-Safe | 431 +--------------------+-----------------+ 432 433 SEE ALSO 434 cpl_complete_word(3TECLA), ef_expand_file(3TECLA), gl_get_line(3TECLA), 435 libtecla(3LIB), pca_lookup_file(3TECLA), attributes(5), tecla(5) 436 437 438 439 January 18, 2020 GL_IO_MODE(3TECLA)