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