1 /* signals.c: signal handling */
2
3 /* This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License , or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; see the file COPYING. If not, write to
15 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
16
17 You may contact the author by:
18 e-mail: hanslub42@gmail.com
19 */
20
21 #include "rlwrap.h"
22
23 volatile int command_is_dead = FALSE;
24 int commands_exit_status = 0;
25 int filter_is_dead = FALSE;
26 int filters_exit_status = 0;
27 int deferred_adapt_commands_window_size = FALSE; /* whether we have to adapt clients winsize when accepting a line */
28 int signal_handlers_were_installed = FALSE;
29 int received_sigALRM = FALSE;
30
31 static void change_signalmask(int, int *);
32 static void child_died(int);
33 static void pass_on_signal(int);
34 static void handle_sigTSTP(int);
35
36
37
38 int adapt_tty_winsize(int, int);
39 static void wipe_textarea(struct winsize *old_size);
40 static int signals_program_error(int signal);
41
42 static int signals_to_be_passed_on[] = {
43 SIGHUP, SIGINT,
44 SIGQUIT, SIGTERM, SIGCONT, SIGUSR1,
45 SIGUSR2, SIGWINCH, 0
46 };
47
48 #ifndef MAX_SIG /* is this POSIX? couldn't find it, so: */
49 #define MAX_SIG 100
50 #endif
51
52
53 #ifdef DEBUG
54 static void log_named_signal(int);
55 #else
56 static void handle_program_error_signal(int);
57 #endif
58
59
60
61 int sigterm_received = FALSE;
62
63
64 void
mysignal(int sig,sighandler_type handler,const char * handler_name)65 mysignal(int sig, sighandler_type handler, const char *handler_name) {
66
67 #ifdef HAVE_SIGACTION
68 struct sigaction action;
69 if (handler == SIG_DFL)
70 DPRINTF2(DEBUG_SIGNALS,"Re-setting handler for signal %d (%s) to its default", sig, signal_name(sig));
71 else if (handler == SIG_IGN)
72 DPRINTF2(DEBUG_SIGNALS,"Ignoring signal %d (%s)", sig, signal_name(sig));
73 else
74 DPRINTF3(DEBUG_SIGNALS,"Setting handler for signal %d (%s) to %s()", sig, signal_name(sig), handler_name);
75
76 action.sa_handler = handler;
77 sigfillset(&action.sa_mask); /* don't bother making our signal handlers re-entrant (they aren't) */
78 action.sa_flags = (sig == SIGCHLD ? SA_NOCLDSTOP : 0); /* no SA_RESTART */
79 if (sigaction(sig, &action, NULL) != 0)
80 # else /* rlwrap running in Ye Olde Computer Museum?? */
81 if (signal(sig, handler) == SIG_ERR)
82 # endif
83 if(handler != SIG_DFL) /* allow e.g. KILL to be set to its default */
84 myerror(FATAL|USE_ERRNO, "Failed setting handler for signal %d (%s)", sig, signal_name(sig));
85 }
86
87
88 void
install_signal_handlers()89 install_signal_handlers()
90 {
91 int i;
92 mysignal(SIGCHLD, HANDLER(child_died));
93 mysignal(SIGTSTP, HANDLER(handle_sigTSTP));
94 #ifndef DEBUG /* we want core dumps when debugging, no polite excuses! */
95 for (i = 1; i<MAX_SIG; i++)
96 if (signals_program_error(i))
97 mysignal(i, HANDLER(handle_program_error_signal)); /* make polite excuse */
98 #endif
99 mysignal(SIGALRM, HANDLER(handle_sigALRM));
100 for (i = 1; signals_to_be_passed_on[i]; i++) {
101 assert(!signals_program_error(signals_to_be_passed_on[i]));
102 mysignal(signals_to_be_passed_on[i], HANDLER(pass_on_signal));
103 }
104 signal_handlers_were_installed = TRUE;
105 }
106
107
uninstall_signal_handlers()108 static void uninstall_signal_handlers() {
109 int i;
110 for(i=1; i<MAX_SIG; i++)
111 mysignal(i, SIG_DFL, NULL);
112 signal_handlers_were_installed = FALSE;
113 }
114
115
116 void
ignore_sigchld()117 ignore_sigchld()
118 {
119 mysignal(SIGCHLD, HANDLER(do_nothing));
120 }
121
122
123 /* we'll call signals whose handlers mess with readline's internal state "messy signals"
124 at present SIGTSTP and SIGWINCH are considered "messy". The following 2 functions are used
125 in main.c to block messy signals except when waiting for I/O */
126
127 void
block_signals(int * sigs)128 block_signals(int *sigs)
129 {
130 change_signalmask(SIG_BLOCK, sigs);
131 }
132
133
134
135 void
unblock_signals(int * sigs)136 unblock_signals(int *sigs)
137 {
138 change_signalmask(SIG_UNBLOCK, sigs);
139 }
140
141
142 void
block_all_passed_on_signals(void)143 block_all_passed_on_signals(void)
144 {
145 change_signalmask(SIG_BLOCK, signals_to_be_passed_on);
146 }
147
148 static void
change_signalmask(int how,int * sigs)149 change_signalmask(int how, int *sigs)
150 { /* sigs should point to a *zero-terminated* list of signals */
151 int i;
152 sigset_t mask;
153
154 sigemptyset(&mask);
155 for (i = 0; sigs[i]; i++) {
156 DPRINTF2(DEBUG_SIGNALS, "%sblocking signal %s", how == SIG_UNBLOCK ? "un-" : "", signal_name(sigs[i]));
157 sigaddset(&mask, sigs[i]);
158 }
159 sigprocmask(how, &mask, NULL);
160 }
161
162
163 void
unblock_all_signals()164 unblock_all_signals()
165 {
166 sigset_t mask;
167 sigfillset(&mask);
168 sigprocmask(SIG_UNBLOCK, &mask, NULL);
169 }
170
171 void
block_all_signals()172 block_all_signals()
173 {
174 sigset_t mask;
175 int i;
176 sigfillset(&mask); /* block all signals */
177 for (i = 0; i<MAX_SIG; i++)
178 if (signals_program_error(i))
179 sigdelset(&mask, i); /* ... except those that were generated by errors within rlwrap */
180 sigprocmask(SIG_BLOCK, &mask, NULL);
181 }
182
183
184 static void
handle_sigTSTP(int signo)185 handle_sigTSTP(int signo)
186 {
187 sigset_t all_signals;
188 int error, saved_errno = errno;
189
190 DEBUG_RANDOM_SLEEP;
191 sigfillset(&all_signals);
192
193 DPRINTF2(DEBUG_SIGNALS, "got %s, sending it to pgid %d", signal_name(signo), command_pid);
194 zero_select_timeout();
195 /* Hand the SIGTSTP down to command and its process group */
196 if (command_pid && (error = kill(-command_pid, SIGTSTP))) {
197 myerror(FATAL|USE_ERRNO, "Failed to deliver SIGTSTP");
198 }
199
200 if (within_line_edit)
201 save_rl_state();
202
203
204 mysignal(SIGTSTP, SIG_DFL, NULL); /* reset disposition to default (i.e. suspend) */
205 sigprocmask(SIG_UNBLOCK, &all_signals, NULL); /* respond to sleep- and wake-up signals */
206 kill(getpid(), SIGTSTP); /* suspend */
207 /* keyboard gathers dust, kingdoms crumble,.... */
208
209 /* .... */
210
211 /* Beautiful princess types "fg", (or her father tries to kill us...) and we wake up HERE: */
212 sigprocmask(SIG_BLOCK, &all_signals, NULL);
213 mysignal(SIGTSTP, HANDLER(handle_sigTSTP));
214 DPRINTF0(DEBUG_SIGNALS, "woken up");
215
216 /* On most systems, command's process group will have been woken up by the handler of
217 the signal that woke us up. This doesn't seem to happen for SIGCONT on QNX, so here goes: */
218
219 #ifdef __QNX__
220 if (command_pid && (error = kill(-command_pid, SIGCONT))) {
221 myerror(FATAL|USE_ERRNO, "Failed to deliver SIGCONT");
222 }
223 #endif
224
225 if (within_line_edit) {
226 restore_rl_state();
227 } else {
228 set_echo(FALSE);
229 cr();
230 if (skip_rlwrap())
231 return;
232 move_cursor_to_start_of_prompt(ERASE);
233 cook_prompt_if_necessary();
234 my_putstr(saved_rl_state.cooked_prompt);
235 }
236 adapt_tty_winsize(STDIN_FILENO, master_pty_fd); /* just in case */
237 errno = saved_errno;
238 sigprocmask(SIG_UNBLOCK, &all_signals, NULL);
239 }
240
241 /* signal handler, passes the signal to the child process group
242 SIGWINCH is only passed after calling adapt_tty_winsize()
243 */
244
245 static void
pass_on_signal(int signo)246 pass_on_signal(int signo)
247 {
248 int ret, saved_errno = errno, pass_it_on = TRUE;
249 char signo_as_str[4];
250
251 DEBUG_RANDOM_SLEEP;
252 zero_select_timeout();
253 #ifdef DEBUG
254 log_named_signal(signo);
255 #endif
256
257 if(pass_on_sigINT_as_sigTERM && signo == SIGINT)
258 signo=SIGTERM;
259
260 assert(MAX_SIG < 1000); errno = 0 ; /* prevent overflow of sprintf and underflow of strtol on the next line */
261 sprintf(signo_as_str, "%d", signo);
262 signo = strtol(pass_through_filter(TAG_SIGNAL, signo_as_str), NULL, 10);
263
264 switch (signo) {
265 case SIGWINCH: /* non-POSIX, but present on most systems */
266 /* Make slave pty's winsize equal to that of STDIN. Pass the signal on *only if*
267 winsize has changed. This is particularly important because we have the slave pty
268 still open - when we pass on the signal the child will probably do a TIOCSWINSZ ioctl()
269 on the slave pty - causing us (the parent) to get a SIGWINCH again, thus getting stuck in a loop */
270 pass_it_on = adapt_tty_winsize(STDIN_FILENO, master_pty_fd);
271 break;
272 case SIGTERM:
273 sigterm_received = TRUE;
274 break;
275 default:
276 break;
277 }
278 if (!command_pid)
279 pass_it_on = FALSE; /* no child (yet) to pass it on to */
280
281 if (pass_it_on) { /* we resend the signal to the process *group* of the child */
282 ret = kill( -command_pid, signo);
283 DPRINTF3(DEBUG_SIGNALS, "kill(%d,%s) = %d", -command_pid, signal_name(signo), ret);
284 we_just_got_a_signal_or_EOF = TRUE; /* signal to main loop that next command output should leave the prompt alone */
285 }
286 errno = saved_errno;
287
288 }
289
290
291 /* This function handles a terminal resize by copying from_fd's winsize
292 to that of to_fd, keeping displayed line tidy, if possible
293 (i.e. if terminal is capable enough, and we know enough about
294 readlines internals). A return value != 0 means that the size has
295 changed.
296 */
297
298 int
adapt_tty_winsize(int from_fd,int to_fd)299 adapt_tty_winsize(int from_fd, int to_fd)
300 {
301 int ret;
302
303 struct winsize old_winsize = winsize;
304
305 ret = ioctl(from_fd, TIOCGWINSZ, &winsize);
306 DPRINTF1(DEBUG_SIGNALS, "ioctl (..., TIOCGWINSZ) = %d", ret);
307 if (winsize.ws_col != old_winsize.ws_col || winsize.ws_row != old_winsize.ws_row) {
308 DPRINTF4(DEBUG_SIGNALS, "ws.col: %d -> %d, ws.row: %d -> %d", old_winsize.ws_col, winsize.ws_col, old_winsize.ws_row, winsize.ws_row);
309 if (always_readline &&!dont_wrap_command_waits()) /* if --always_readline option is set, the client will probably spew a */
310 deferred_adapt_commands_window_size = TRUE; /* volley of control chars at us when its terminal is resized. Hence we don't do it now */
311 else {
312 ret = ioctl(to_fd, TIOCSWINSZ, &winsize);
313 DPRINTF1(DEBUG_SIGNALS, "ioctl (..., TIOCSWINSZ) = %d", ret);
314 }
315 DPRINTF2(DEBUG_READLINE, "rl_prompt: %s, line_buffer: %s", mangle_string_for_debug_log(rl_prompt, 100), rl_line_buffer);
316 rl_set_screen_size(winsize.ws_row, winsize.ws_col); /* this is safe: we know that right now rlwrap is not calling
317 the readline library because we keep SIGWINCH blocked all the time */
318
319 if (!within_line_edit && !skip_rlwrap()) {
320 wipe_textarea(&old_winsize);
321
322 received_WINCH = TRUE; /* we can't start line edit in signal handler, so we only set a flag */
323 } else if (within_line_edit) { /* try to keep displayed line tidy */
324 wipe_textarea(&old_winsize);
325 rl_on_new_line();
326 rl_redisplay();
327
328 }
329
330 return (!always_readline || dont_wrap_command_waits()); /* pass the signal on (except when always_readline is set and command is not waiting) */
331 } else { /* window size has not changed */
332 return FALSE;
333 }
334
335 }
336
337 /* After a resize, clear all the lines that were occupied by prompt + line buffer before the resize */
338 static
wipe_textarea(struct winsize * old_winsize)339 void wipe_textarea(struct winsize *old_winsize)
340 {
341 int point, lineheight, linelength, cursor_height, i, promptlength;
342 if (!prompt_is_single_line()) { /* Don't need to do anything in horizontal_scroll_mode */
343 promptlength = colourless_strlen((saved_rl_state.cooked_prompt ? saved_rl_state.cooked_prompt: saved_rl_state.raw_prompt), NULL, old_winsize -> ws_col);
344 linelength = (within_line_edit ? strlen(rl_line_buffer) : 0) + promptlength;
345 point = (within_line_edit ? rl_point : 0) + promptlength;
346 assert(old_winsize -> ws_col > 0);
347 lineheight = (linelength == 0 ? 0 : (1 + (max(point, (linelength - 1)) / old_winsize -> ws_col)));
348 if (lineheight > 1 && term_cursor_up != NULL && term_cursor_down != NULL) {
349 /* i.e. if we have multiple rows displayed, and we can clean them up first */
350 cr();
351 cursor_height = point / old_winsize -> ws_col; /* cursor is still on old line */
352 DPRINTF2(DEBUG_SIGNALS, "lineheight: %d, cursor_height: %d", lineheight, cursor_height);
353 for (i = 1 + cursor_height; i < lineheight; i++)
354 curs_down(); /* ...move it down to last line */
355 for (i = lineheight; i > 1; i--) { /* go up again, erasing every line */
356 clear_line();
357 curs_up();
358 }
359 }
360 clear_line();
361 cr();
362 }
363 }
364
365 static void
child_died(int UNUSED (signo))366 child_died(int UNUSED(signo))
367 {
368 int saved_errno;
369 DEBUG_RANDOM_SLEEP;
370 zero_select_timeout();
371 saved_errno = errno;
372 DPRINTF0(DEBUG_SIGNALS, "Caught SIGCHLD");
373
374 if(command_pid && waitpid(command_pid, &commands_exit_status, WNOHANG)) {
375 DPRINTF2(DEBUG_SIGNALS, "child (pid %d) has died, exit status: %x", command_pid, commands_exit_status);
376 command_is_dead = TRUE;
377 command_pid = 0; /* thus we know that there is no child anymore to pass signals to */
378 } else if (filter_pid && waitpid(filter_pid, &filters_exit_status, WNOHANG)) {
379 DPRINTF2(DEBUG_SIGNALS, "filter (pid %d) has died, exit status: %x", filter_pid, filters_exit_status);
380 filter_is_dead = TRUE;
381 filter_pid = 0;
382 } else {
383 DPRINTF0(DEBUG_ALL, "Whoa, got a SIGCHLD, but not from slave command or filter! I must have children I don't know about (blush...)!");
384 /* ignore */
385 }
386
387 errno = saved_errno;
388 return; /* allow remaining output from child to be processed in main loop */
389 /* (so that we will see childs good-bye talk) */
390 /* this will then clean up and terminate */
391
392 }
393
394
395 #ifdef DEBUG
396 static void
log_named_signal(int signo)397 log_named_signal(int signo)
398 {
399 if (debug)
400 DPRINTF1(DEBUG_SIGNALS, "got %s", signal_name(signo));
401 }
402 #else
403 static void
handle_program_error_signal(int sig)404 handle_program_error_signal(int sig)
405 { /* Even after sudden and unexpected death, leave the terminal in a tidy state */
406 int res;
407
408 printf("\n%s: Oops, crashed (caught %s) - this should not have happened!\n"
409 "If you need a core dump, re-configure with --enable-debug and rebuild\n"
410 "Resetting terminal and cleaning up...\n", program_name, signal_name(sig));
411 if (colour_the_prompt || filter_pid)
412 res = write(STDOUT_FILENO,"\033[0m",4); /* reset terminal colours */
413 if (terminal_settings_saved)
414 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_terminal_settings);
415
416 exit(EXIT_FAILURE);
417 }
418 #endif
419
420
coredump(int status)421 static int coredump(int status) {
422 #ifdef WCOREDUMP
423 return WCOREDUMP(status);
424 #else
425 return 0;
426 #endif
427 }
428
suicide_by(int signal,int status)429 void suicide_by(int signal, int status) {
430 /* Some signals suggest a program error. When rlwrap kills itself with one of those,
431 the shell may tell the user that rlwrap itself has failed. Make clear that
432 it didn't. @@@ We could also try argv[0] = command_name just before dying ? */
433
434 if (signals_program_error(signal)) {
435 myerror(WARNING|NOERRNO, "%s crashed, killed by %s%s.\n%s itself has not crashed, but for transparency,\n"
436 "it will now kill itself %swith the same signal\n",
437 command_name, signal_name(signal), (coredump(status) ? " (core dumped)" : ""),
438 program_name, (coredump(status) ? "" : "(without dumping core) ") );
439 }
440 uninstall_signal_handlers();
441 unblock_all_signals();
442 set_ulimit(RLIMIT_CORE,0); /* prevent our own useless core dump from clobbering the interesting one created by command */
443 DPRINTF1(DEBUG_SIGNALS, "suicide by signal %s", signal_name(signal));
444 kill(getpid(), signal);
445 /* still alive? */
446 sleep(1);
447 exit(0);
448 }
449
450 static int myalarm_was_set = FALSE;
451
452
453 /* drop-in replacement for alarm (*but* with arg in msecs, not secs). Also sets global flag
454 myalarm_was_set */
455
myalarm(int msecs)456 void myalarm(int msecs) {
457 #ifdef HAVE_SETITIMER
458 int retval;
459 struct itimerval awhile = {{0,0},{0,0}};
460 int secs = msecs/1000;
461 awhile.it_value.tv_sec = secs;
462 awhile.it_value.tv_usec = (msecs - secs * 1000) * 1000;
463 received_sigALRM = FALSE;
464 retval = setitimer(ITIMER_REAL, &awhile, NULL);
465 DPRINTF3(DEBUG_AD_HOC, "setitimer() = %d (tv_sec = %d, tv_usec=%ld)", retval, secs, awhile.it_value.tv_usec);
466 #else
467 received_sigALRM = FALSE;
468 alarm(msecs == 0 ? 0 : 1 + msecs/1000));
469 #endif
470 DPRINTF1(DEBUG_AD_HOC, "set alarm (%d msecs)", msecs);
471 if (msecs == 0)
472 return;
473 myalarm_was_set = TRUE;
474 }
475
476 void
handle_sigALRM(int UNUSED (signo))477 handle_sigALRM(int UNUSED(signo)) {
478 received_sigALRM = TRUE;
479 assert(myalarm_was_set); /* cry wolf if sigALRM is caught when none was requested by myalarm */
480 myalarm_was_set= FALSE;
481 DPRINTF0(DEBUG_SIGNALS, "got sigALRM");
482 }
483
484
485 /* Give name of signal. A case() construct is not appropriate here as on some
486 architectures signal values may coincide */
signal_name(int signal)487 char *signal_name(int signal) {
488 return
489 signal == SIGHUP ? "SIGHUP" :
490 signal == SIGINT ? "SIGINT" :
491 signal == SIGQUIT ? "SIGQUIT" :
492 signal == SIGILL ? "SIGILL" :
493 signal == SIGABRT ? "SIGABRT" :
494 signal == SIGTRAP ? "SIGTRAP" :
495 #ifdef SIGIOT /* 4.2 BSD (IOT trap ) */
496 signal == SIGIOT ? "SIGIOT" :
497 #endif
498 #ifdef SIGEMT /* 4.2 BSD (EMT trap ) */
499 signal == SIGEMT ? "SIGEMT" :
500 #endif
501 signal == SIGFPE ? "SIGFPE" :
502 signal == SIGKILL ? "SIGKILL" :
503 #ifdef SIGBUS /* 4.2 BSD (Bus error ) */
504 signal == SIGBUS ? "SIGBUS" :
505 #endif
506 signal == SIGSEGV ? "SIGSEGV" :
507 #ifdef SIGSYS /* 4.2 BSD (Bad argument to system call ) */
508 signal == SIGSYS ? "SIGSYS" :
509 #endif
510 signal == SIGPIPE ? "SIGPIPE" :
511 signal == SIGALRM ? "SIGALRM" :
512 signal == SIGTERM ? "SIGTERM" :
513 signal == SIGUSR1 ? "SIGUSR1" :
514 signal == SIGUSR2 ? "SIGUSR2" :
515 signal == SIGCHLD ? "SIGCHLD" :
516 signal == SIGSTOP ? "SIGSTOP" :
517 signal == SIGTSTP ? "SIGTSTP" :
518 signal == SIGCONT ? "SIGCONT" :
519 #ifdef SIGCLD /* System V (Same as SIGCHLD ) */
520 signal == SIGCLD ? "SIGCLD" :
521 #endif
522 #ifdef SIGPWR /* System V (Power failure restart ) */
523 signal == SIGPWR ? "SIGPWR" :
524 #endif
525 signal == SIGXCPU ? "SIGXCPU" :
526 signal == SIGXFSZ ? "SIGXFSZ" :
527 signal == SIGWINCH ? "SIGWINCH" : /* non-POSIX, but present on most systems */
528 as_string(signal);
529 }
530
531
signals_program_error(int signal)532 static int signals_program_error(int signal) {
533 return
534 signal == SIGILL ||
535 signal == SIGABRT ||
536 signal == SIGTRAP ||
537 #ifdef SIGIOT /* 4.2 BSD (IOT trap ) */
538 signal == SIGIOT ||
539 #endif
540 #ifdef SIGEMT /* 4.2 BSD (EMT trap ) */
541 signal == SIGEMT ||
542 #endif
543 signal == SIGFPE ||
544 #ifdef SIGBUS /* 4.2 BSD (Bus error ) */
545 signal == SIGBUS ||
546 #endif
547 signal == SIGSEGV ||
548 #ifdef SIGSYS /* 4.2 BSD (Bad argument to system call ) */
549 signal == SIGSYS ||
550 #endif
551 signal == SIGXCPU ||
552 signal == SIGXFSZ ||
553 FALSE ? TRUE : FALSE;
554 }
555
556