1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: signal.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
5 /* ========================================================================
6 * Copyright 2006-2008 University of Washington
7 * Copyright 2013-2015 Eduardo Chappa
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * ========================================================================
18 /*======================================================================
19 Different signal handlers for different signals
20 - Catches all the abort signals, cleans up tty modes and then coredumps
21 - Not much to do for SIGHUP
22 - Not much to do for SIGTERM
23 - turn SIGWINCH into a KEY_RESIZE command
24 - No signals for ^Z/suspend, but do it here anyway
25 - Also set up the signal handlers, and hold signals for
26 critical imap sections of code.
38 #include "../pith/state.h"
39 #include "../pith/conf.h"
40 #include "../pith/detach.h"
41 #include "../pith/pipe.h"
42 #include "../pith/util.h"
43 #include "../pith/icache.h"
44 #include "../pith/newmail.h"
45 #include "../pith/imap.h"
46 #include "../pith/adrbklib.h"
47 #include "../pith/remote.h"
48 #include "../pith/osdep/pipe.h"
54 static RETSIGTYPE
auger_in_signal(int);
55 void init_sighup(void);
56 void end_sighup(void);
57 RETSIGTYPE
term_signal(void);
58 void fast_clean_up(void);
59 static RETSIGTYPE
usr2_signal(int);
60 static RETSIGTYPE
winch_signal(int);
61 static RETSIGTYPE
intr_signal(int);
62 void suspend_notice(char *);
63 void suspend_warning(void);
67 static int cleanup_called_from_sig_handler
;
71 /*----------------------------------------------------------------------
72 Install handlers for all the signals we care to catch
73 ----------------------------------------------------------------------*/
77 dprint((9, "init_signals()\n"));
83 # define CUSHION_SIG (debug < 7)
85 # define CUSHION_SIG (1)
89 signal(SIGILL
, auger_in_signal
);
90 signal(SIGTRAP
, auger_in_signal
);
92 signal(SIGEMT
, auger_in_signal
);
94 signal(SIGBUS
, auger_in_signal
);
95 signal(SIGSEGV
, auger_in_signal
);
96 signal(SIGSYS
, auger_in_signal
);
97 signal(SIGQUIT
, auger_in_signal
);
98 /* Don't catch SIGFPE cause it's rare and we use it in a hack below*/
104 * Set up SIGUSR2 to catch signal from other software using the
105 * c-client to tell us that other access to the folder is being
106 * attempted. THIS IS A TEST: if it turns out that simply
107 * going R/O when another pine is started or the same folder is opened,
108 * then we may want to install a smarter handler that uses idle time
109 * or even prompts the user to see if it's ok to give up R/O access...
111 signal(SIGUSR2
, usr2_signal
);
113 signal(SIGPIPE
, SIG_IGN
);
114 signal(SIGINT
, SIG_IGN
);
117 /* Some unexplained behaviour on Ultrix 4.2 (Hardy) seems to be
118 resulting in Pine getting sent a SIGTSTP. Ignore it here.
119 probably better to ignore it than let it happen in any case
121 signal(SIGTSTP
, SIG_IGN
);
122 # endif /* SIGTSTP */
125 signal(SIGCHLD
, child_signal
);
127 #endif /* !_WINDOWS */
131 /*----------------------------------------------------------------------
132 Return all signal handling back to normal
133 ----------------------------------------------------------------------*/
135 end_signals(int blockem
)
139 signal(SIGHUP
, blockem
? SIG_IGN
: SIG_DFL
);
143 #define SIG_ERR (RETSIGTYPE (*)())-1
146 dprint((5, "end_signals(%d)\n", blockem
));
147 if(signal(SIGILL
, blockem
? SIG_IGN
: SIG_DFL
) == SIG_ERR
){
148 fprintf(stderr
, "Error resetting signals: %s\n",
149 error_description(errno
));
153 signal(SIGTRAP
, blockem
? SIG_IGN
: SIG_DFL
);
155 signal(SIGEMT
, blockem
? SIG_IGN
: SIG_DFL
);
157 signal(SIGBUS
, blockem
? SIG_IGN
: SIG_DFL
);
158 signal(SIGSEGV
, blockem
? SIG_IGN
: SIG_DFL
);
159 signal(SIGSYS
, blockem
? SIG_IGN
: SIG_DFL
);
160 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
161 signal(SIGWINCH
, blockem
? SIG_IGN
: SIG_DFL
);
163 signal(SIGQUIT
, blockem
? SIG_IGN
: SIG_DFL
);
165 signal(SIGTSTP
, blockem
? SIG_IGN
: SIG_DFL
);
167 signal(SIGHUP
, blockem
? SIG_IGN
: SIG_DFL
);
168 signal(SIGTERM
, blockem
? SIG_IGN
: SIG_DFL
);
169 signal(SIGINT
, blockem
? SIG_IGN
: SIG_DFL
);
171 #endif /* !_WINDOWS */
175 /*----------------------------------------------------------------------
176 Handle signals caused by aborts -- SIGSEGV, SIGILL, etc
178 Call panic which cleans up tty modes and then core dumps
179 ----------------------------------------------------------------------*/
181 auger_in_signal(int sig
)
185 end_signals(1); /* don't catch any more signals */
186 imap_flush_passwd_cache(FALSE
);
188 snprintf(buf
, sizeof(buf
), "Received abort signal(sig=%d)", sig
);
189 buf
[sizeof(buf
)-1] = '\0';
191 alpine_panic(buf
); /* clean up and get out */
193 exit(-1); /* in case panic doesn't kill us */
197 /*----------------------------------------------------------------------
198 Install signal handler to deal with hang up signals -- SIGHUP, SIGTERM
200 ----------------------------------------------------------------------*/
204 #if !(defined(DOS) && !defined(_WINDOWS))
205 #if defined(_WINDOWS) || defined(OS2)
206 signal(SIGHUP
, (void *) hup_signal
);
208 signal(SIGHUP
, hup_signal
);
211 #if !(defined(DOS) || defined(OS2))
212 signal(SIGTERM
, term_signal
);
217 /*----------------------------------------------------------------------
218 De-Install signal handler to deal with hang up signals -- SIGHUP, SIGTERM
220 ----------------------------------------------------------------------*/
224 #if !(defined(DOS) && !defined(_WINDOWS))
225 signal(SIGHUP
, SIG_IGN
);
227 #if !(defined(DOS) || defined(OS2))
228 signal(SIGTERM
, SIG_IGN
);
233 /*----------------------------------------------------------------------
234 handle hang up signal -- SIGHUP
236 Not much to do. Rely on periodic mail file check pointing.
237 ----------------------------------------------------------------------*/
241 end_signals(1); /* don't catch any more signals */
242 dprint((1, "\n\n** Received SIGHUP **\n\n\n\n"));
243 cleanup_called_from_sig_handler
= 1;
250 _exit(0); /* cleaning up can crash */
254 /*----------------------------------------------------------------------
255 Timeout when no user input for a long, long time.
256 Treat it pretty much the same as if we got a HUP.
257 Only difference is we sometimes turns the timeout off (when composing).
258 ----------------------------------------------------------------------*/
260 user_input_timeout_exit(int to_hours
)
265 "\n\n** Exiting: user input timeout (%d hours) **\n\n\n\n",
267 snprintf(msg
, sizeof(msg
), _("\n\nAlpine timed out (No user input for %d %s)\n"), to_hours
,
268 to_hours
> 1 ? "hours" : "hour");
269 msg
[sizeof(msg
)-1] = '\0';
274 end_keyboard(F_ON(F_USE_FK
,ps_global
));
275 end_tty_driver(ps_global
);
277 #if defined(DEBUG) && (!defined(DOS) || defined(_WINDOWS))
285 /*----------------------------------------------------------------------
286 handle terminate signal -- SIGTERM
288 Not much to do. Rely on periodic mail file check pointing.
289 ----------------------------------------------------------------------*/
293 #if !defined(DOS) && !defined(OS2)
294 end_signals(1); /* don't catch any more signals */
295 dprint((1, "\n\n** Received SIGTERM **\n\n\n\n"));
296 cleanup_called_from_sig_handler
= 1;
298 #if defined(DEBUG) && (!defined(DOS) || defined(_WINDOWS))
302 printf(_("\n\nAlpine finished. Received terminate signal\n\n"));
308 /*----------------------------------------------------------------------
309 Handle cleaning up mail streams and tty modes...
310 Not much to do. Rely on periodic mail file check pointing. Don't try
311 cleaning up screen or flushing output since stdout is likely already
312 gone. To be safe, though, we'll at least restore the original tty mode.
313 Also delete any remnant _DATAFILE_ from sending-filters.
314 ----------------------------------------------------------------------*/
318 #if !defined(DOS) && !defined(OS2)
323 dprint((1, "fast_clean_up()\n"));
325 if(ps_global
->expunge_in_progress
){
331 * This gets rid of temporary cache files for remote addrbooks.
333 completely_done_with_adrbks();
336 * This flushes out deferred changes and gets rid of temporary cache
337 * files for remote config files.
340 if(ps_global
->prc
->outstanding_pinerc_changes
)
341 write_pinerc(ps_global
, Main
,
342 cleanup_called_from_sig_handler
? WRP_NOUSER
: WRP_NONE
);
344 if(ps_global
->prc
->rd
)
345 rd_close_remdata(&ps_global
->prc
->rd
);
347 free_pinerc_s(&ps_global
->prc
);
351 if(ps_global
->post_prc
){
352 if(ps_global
->post_prc
->outstanding_pinerc_changes
)
353 write_pinerc(ps_global
, Post
,
354 cleanup_called_from_sig_handler
? WRP_NOUSER
: WRP_NONE
);
356 if(ps_global
->post_prc
->rd
)
357 rd_close_remdata(&ps_global
->post_prc
->rd
);
359 free_pinerc_s(&ps_global
->post_prc
);
363 * Can't figure out why this section is inside the ifdef, but no
364 * harm leaving it that way, I guess.
366 #if !defined(DOS) && !defined(OS2)
367 for(i
= 0; i
< ps_global
->s_pool
.nstream
; i
++){
368 m
= ps_global
->s_pool
.streams
[i
];
370 pine_mail_actually_close(m
);
377 imap_flush_passwd_cache(TRUE
);
379 dprint((1, "done with fast_clean_up\n"));
383 #if !defined(DOS) && !defined(OS2)
384 /*----------------------------------------------------------------------
385 handle hang up signal -- SIGUSR2
387 Not much to do. Rely on periodic mail file check pointing.
388 ----------------------------------------------------------------------*/
392 char c
, *mbox
, mboxbuf
[20];
397 for(i
= 0; i
< ps_global
->s_pool
.nstream
; i
++){
398 stream
= ps_global
->s_pool
.streams
[i
];
400 && sp_flagged(stream
, SP_LOCKED
)
401 && !sp_dead_stream(stream
)
405 && (c
= *stream
->mailbox
) != '{' && c
!= '*'){
406 pine_mail_check(stream
); /* write latest state */
407 stream
->rdonly
= 1; /* and become read-only */
408 (void) pine_mail_ping(stream
);
409 mbox
= stream
->mailbox
;
410 if(!strucmp(stream
->mailbox
, ps_global
->inbox_name
)
411 || !strcmp(stream
->mailbox
, ps_global
->VAR_INBOX_PATH
)
412 || !strucmp(stream
->original_mailbox
, ps_global
->inbox_name
)
413 || !strcmp(stream
->original_mailbox
, ps_global
->VAR_INBOX_PATH
))
415 else if(mail_valid_net_parse(stream
->mailbox
, &mb
) && mb
.mailbox
)
418 q_status_message1(SM_ASYNC
, 3, 7,
419 _("Another email program is accessing %s. Session now Read-Only."),
420 short_str((mbox
&& *mbox
) ? mbox
: "folder",
421 mboxbuf
, sizeof(mboxbuf
), 19, FrontDots
));
422 dprint((1, "** folder %s went read-only **\n\n",
430 /*----------------------------------------------------------------------
431 Install signal handler to deal with window resize signal -- SIGWINCH
433 ----------------------------------------------------------------------*/
437 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
438 signal(SIGWINCH
, winch_signal
);
443 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
444 /*----------------------------------------------------------------------
445 Handle window resize signal -- SIGWINCH
447 The planned strategy is just force a redraw command. This is similar
448 to new mail handling which forces a noop command. The signals are
449 help until pine reads input. Then a KEY_RESIZE is forced into the command
451 Note that ready_for_winch is only non-zero inside the read_char function,
452 so the longjmp only ever happens there, and it is really just a jump
453 from lower down in the function up to the top of that function. Its
454 purpose is to return a KEY_RESIZE from read_char when interrupted
455 out of the select lower down in read_char.
456 ----------------------------------------------------------------------*/
457 extern jmp_buf winch_state
;
458 extern int ready_for_winch
, winch_occured
;
461 winch_signal(int sig
)
476 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
478 longjmp(winch_state
, 1);
486 /*----------------------------------------------------------------------
487 Handle child status change -- SIGCHLD
489 The strategy here is to install the handler when we spawn a child, and
490 to let the system tell us when the child's state has changed. In the
491 mean time, we can do whatever. Typically, "whatever" is spinning in a
492 loop alternating between sleep and new_mail calls intended to keep the
495 ----------------------------------------------------------------------*/
496 static short child_signalled
, child_jump
;
497 static jmp_buf child_state
;
500 child_signal(int sig
)
502 #ifdef BACKGROUND_POST
504 * reap background posting process
506 if(ps_global
->post
&& ps_global
->post
->pid
){
510 pid
= process_reap(ps_global
->post
->pid
, &es
, PR_NOHANG
);
511 if(pid
== ps_global
->post
->pid
){
512 ps_global
->post
->status
= es
;
513 ps_global
->post
->pid
= 0;
516 else if(pid
< 0 && errno
!= EINTR
){
517 fs_give((void **) &ps_global
->post
);
520 #endif /* BACKGROUND_POST */
524 longjmp(child_state
, 1);
530 * pipe_callback - handle pine-specific pipe setup like child
531 * signal handler and possibly any display stuff
532 * BUG: this function should probably be in a "alpine/pipe.c"
535 pipe_callback(PIPE_S
*syspipe
, int flags
, void *data
)
540 if(flags
& OSB_PRE_OPEN
){
541 dprint((5, "Opening pipe: (%s%s%s%s%s%s)\n",
542 (syspipe
->mode
& PIPE_WRITE
) ? "W":"", (syspipe
->mode
& PIPE_READ
) ? "R":"",
543 (syspipe
->mode
& PIPE_NOSHELL
) ? "N":"", (syspipe
->mode
& PIPE_PROT
) ? "P":"",
544 (syspipe
->mode
& PIPE_USER
) ? "U":"", (syspipe
->mode
& PIPE_RESET
) ? "T":""));
547 q_status_message(SM_ORDER
, 0, 0,
548 "Waiting for called program to finish...");
550 flush_status_messages(1);
552 draw_keymenu(&pipe_cancel_keymenu
, bitmap
, ps_global
->ttyo
->screen_cols
,
553 1-FOOTER_ROWS(ps_global
), 0, FirstMenu
);
556 if(!(syspipe
->mode
& (PIPE_WRITE
| PIPE_READ
)) && !(syspipe
->mode
& PIPE_SILENT
)){
557 flush_status_messages(0); /* just clean up display */
562 if(syspipe
->mode
& PIPE_RESET
)
567 * Prepare for demise of child. Use SIGCHLD if it's available so
568 * we can do useful things, like keep the IMAP stream alive, while
569 * we're waiting on the child. The handler may have been changed by
570 * something in the composer, in particular, by an alt_editor call.
571 * So we need to re-set it to child_signal and then set it back
574 child_signalled
= child_jump
= 0;
575 syspipe
->chld
= signal(SIGCHLD
, child_signal
);
579 else if(flags
& OSB_POST_OPEN
){
582 clearfooter(ps_global
);
583 ps_global
->mangled_footer
= 1;
586 q_status_message1(SM_ORDER
, 2, 3, "Ignoring completion of %s", syspipe
->command
);
589 if(!(syspipe
->mode
& (PIPE_WRITE
| PIPE_READ
)) && !(syspipe
->mode
& PIPE_SILENT
)){
591 ps_global
->mangled_screen
= 1;
594 if(syspipe
->mode
& PIPE_RESET
){
596 ps_global
->mangled_screen
= 1;
600 (void) signal(SIGCHLD
, SIG_DFL
);
604 else if(flags
& OSB_PRE_CLOSE
){
607 * this is here so close_system_pipe so it has something
608 * to do while we're waiting on the other end of the
609 * pipe to complete. When we're in the background for
610 * a shell, the the side effect is pinging
612 RETSIGTYPE (*alarm_sig
)();
613 int old_cue
= F_ON(F_SHOW_DELAY_CUE
, ps_global
);
616 * remember the current SIGALRM handler, and make sure it's
617 * installed when we're finished just in case the longjmp
618 * out of the SIGCHLD handler caused sleep() to lose it.
619 * Don't pay any attention to that man behind the curtain.
621 alarm_sig
= signal(SIGALRM
, SIG_IGN
);
622 (void) F_SET(F_SHOW_DELAY_CUE
, ps_global
, 0);
623 ps_global
->noshow_timeout
= 1;
624 while(!child_signalled
){
625 /* wake up and prod server */
626 if(!(syspipe
->mode
& PIPE_NONEWMAIL
))
628 (syspipe
->mode
& PIPE_RESET
) ? NM_NONE
: NM_DEFER_SORT
);
630 if(!child_signalled
){
631 if(setjmp(child_state
) == 0){
632 child_jump
= 1; /* prepare to wake up */
633 sleep(600); /* give it 5mins to happend */
636 our_sigunblock(SIGCHLD
);
642 ps_global
->noshow_timeout
= 0;
643 F_SET(F_SHOW_DELAY_CUE
, ps_global
, old_cue
);
644 (void) signal(SIGALRM
, alarm_sig
);
645 (void) signal(SIGCHLD
, syspipe
->chld
);
648 else if(flags
& OSB_POST_CLOSE
){
649 if(syspipe
->mode
& PIPE_RESET
){ /* restore our tty modes */
651 ps_global
->mangled_screen
= 1;
654 if(!(syspipe
->mode
& (PIPE_WRITE
| PIPE_READ
| PIPE_SILENT
))){
655 ClearScreen(); /* No I/O to forked child */
656 ps_global
->mangled_screen
= 1;
663 * Command interrupt support.
669 ps_global
->intr_pending
= 1;
674 intr_handling_on(void)
677 return 0; /* No interrupts in Windows */
679 if(signal(SIGINT
, intr_signal
) == intr_signal
)
680 return 0; /* already installed */
683 if(ps_global
&& ps_global
->ttyo
)
684 draw_cancel_keymenu();
692 intr_handling_off(void)
696 if(signal(SIGINT
, SIG_IGN
) == SIG_IGN
) /* already off! */
699 ps_global
->intr_pending
= 0;
701 if(ps_global
&& ps_global
->ttyo
)
702 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
704 ps_global
->mangled_footer
= 1;
709 /*----------------------------------------------------------------------
710 Set or reset what needs to be set when coming out of pico to run
713 Args: come_back -- If come_back is 0 we're going out of our environment
714 to set up for an external editor.
715 If come_back is 1 we're coming back into pine.
716 ----------------------------------------------------------------------*/
718 ttyfix(int come_back
)
720 #if defined(DEBUG) && (!defined(DOS) || defined(_WINDOWS))
727 enter_text_mode(NULL
);
730 init_tty_driver(ps_global
);
731 init_keyboard(F_ON(F_USE_FK
,ps_global
));
735 fix_windsize(ps_global
);
739 end_keyboard(F_ON(F_USE_FK
,ps_global
));
740 end_tty_driver(ps_global
);
751 /*----------------------------------------------------------------------
752 Suspend Pine. Reset tty and suspend. Suspend is finished when this returns
754 Args: The pine structure
756 Result: Execution suspended for a while. Screen will need redrawing
759 Instead of the usual handling of ^Z by catching a signal, we actually read
760 the ^Z and then clean up the tty driver, then kill ourself to stop, and
761 pick up where we left off when execution resumes.
762 ----------------------------------------------------------------------*/
766 struct pine
*pine
= ps_global
;
769 #if defined(DOS) || defined(OS2)
770 int result
, orig_cols
, orig_rows
;
775 static char *shell
= NULL
;
776 #define STD_SHELL "COMMAND.COM"
779 static char *shell
= NULL
;
780 #define STD_SHELL "CMD.EXE"
784 if(!have_job_control()){
785 bogus_command(ctrl('Z'), F_ON(F_USE_FK
, pine
) ? "F1" : "?");
786 return(NO_OP_COMMAND
);
789 if(F_OFF(F_CAN_SUSPEND
, pine
)){
790 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
791 _("Alpine suspension not enabled - see help text"));
792 return(NO_OP_COMMAND
);
797 return(NO_OP_COMMAND
);
800 isremote
= (ps_global
->mail_stream
&& ps_global
->mail_stream
->mailbox
801 && (*ps_global
->mail_stream
->mailbox
== '{'
802 || (*ps_global
->mail_stream
->mailbox
== '*'
803 && *(ps_global
->mail_stream
->mailbox
+ 1) == '{')));
805 now
= time((time_t *)0);
806 dprint((1, "\n\n --- %s - SUSPEND ---- %s",
807 isremote
? "REMOTE" : "LOCAL", ctime(&now
)));
810 #if defined(DOS) || defined(OS2)
811 suspend_notice("exit");
815 if (!((shell
= getenv("SHELL")) || (shell
= getenv("COMSPEC"))))
818 shell
= cpystr(shell
); /* copy in free storage */
819 for(p
= shell
; (p
= strchr(p
, '/')); p
++)
823 result
= system(shell
);
825 if(F_ON(F_SUSPEND_SPAWNS
, ps_global
)){
827 int flag
= some_stream_is_locked() ? PIPE_NONEWMAIL
: 0;
829 flag
|= PIPE_USER
|PIPE_RESET
;
830 if((syspipe
= open_system_pipe(NULL
, NULL
, NULL
, flag
,
831 0, pipe_callback
, pipe_report_error
)) != NULL
){
832 suspend_notice("exit");
837 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
841 suspend_notice("fg");
850 now
= time((time_t *)0);
851 dprint((1, "\n\n ---- RETURN FROM SUSPEND ---- %s",
856 #if defined(DOS) || defined(OS2)
857 orig_cols
= pine
->ttyo
->screen_cols
;
858 orig_rows
= pine
->ttyo
->screen_rows
;
863 #if defined(DOS) || defined(OS2)
864 if(orig_cols
!= pine
->ttyo
->screen_cols
||
865 orig_rows
!= pine
->ttyo
->screen_rows
)
871 #if defined(DOS) || defined(OS2)
873 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
874 _("Error loading \"%s\""), shell
);
877 if(isremote
&& !ps_global
->mail_stream
->lock
878 && !pine_mail_ping(ps_global
->mail_stream
))
879 q_status_message(SM_ORDER
| SM_DING
, 4, 9,
880 _("Suspended for too long, IMAP connection broken"));
883 #endif /* !_WINDOWS */
888 /*----------------------------------------------------------------------
891 suspend_notice(char *s
)
893 printf(_("\nAlpine suspended. Give the \"%s\" command to come back.\n"), s
);
898 /*----------------------------------------------------------------------
901 suspend_warning(void)
903 puts(_("Warning: Your IMAP connection will be closed if Alpine"));
904 puts(_("is suspended for more than 30 minutes\n"));
909 /*----------------------------------------------------------------------
912 fix_windsize(struct pine
*pine
)
914 int old_width
= pine
->ttyo
->screen_cols
;
916 dprint((9, "fix_windsize()\n"));
917 mark_keymenu_dirty();
919 mark_titlebar_dirty();
921 get_windsize(pine
->ttyo
);
923 if(old_width
!= pine
->ttyo
->screen_cols
)
924 clear_index_cache(pine
->mail_stream
, 0);