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-2020 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 ----------------------------------------------------------------------*/
242 ps_global
->signal_in_progress
= 1;
243 end_signals(1); /* don't catch any more signals */
244 dprint((1, "\n\n** Received SIGHUP **\n\n\n\n"));
245 cleanup_called_from_sig_handler
= 1;
252 _exit(0); /* cleaning up can crash */
256 /*----------------------------------------------------------------------
257 Timeout when no user input for a long, long time.
258 Treat it pretty much the same as if we got a HUP.
259 Only difference is we sometimes turns the timeout off (when composing).
260 ----------------------------------------------------------------------*/
262 user_input_timeout_exit(int to_hours
)
267 "\n\n** Exiting: user input timeout (%d hours) **\n\n\n\n",
269 snprintf(msg
, sizeof(msg
), _("\n\nAlpine timed out (No user input for %d %s)\n"), to_hours
,
270 to_hours
> 1 ? "hours" : "hour");
271 msg
[sizeof(msg
)-1] = '\0';
276 end_keyboard(F_ON(F_USE_FK
,ps_global
));
277 end_tty_driver(ps_global
);
279 #if defined(DEBUG) && (!defined(DOS) || defined(_WINDOWS))
287 /*----------------------------------------------------------------------
288 handle terminate signal -- SIGTERM
290 Not much to do. Rely on periodic mail file check pointing.
291 ----------------------------------------------------------------------*/
295 #if !defined(DOS) && !defined(OS2)
296 end_signals(1); /* don't catch any more signals */
297 dprint((1, "\n\n** Received SIGTERM **\n\n\n\n"));
298 cleanup_called_from_sig_handler
= 1;
300 #if defined(DEBUG) && (!defined(DOS) || defined(_WINDOWS))
304 printf(_("\n\nAlpine finished. Received terminate signal\n\n"));
310 /*----------------------------------------------------------------------
311 Handle cleaning up mail streams and tty modes...
312 Not much to do. Rely on periodic mail file check pointing. Don't try
313 cleaning up screen or flushing output since stdout is likely already
314 gone. To be safe, though, we'll at least restore the original tty mode.
315 Also delete any remnant _DATAFILE_ from sending-filters.
316 ----------------------------------------------------------------------*/
320 #if !defined(DOS) && !defined(OS2)
325 dprint((1, "fast_clean_up()\n"));
327 if(ps_global
->expunge_in_progress
){
333 * This gets rid of temporary cache files for remote addrbooks.
335 completely_done_with_adrbks();
338 * This flushes out deferred changes and gets rid of temporary cache
339 * files for remote config files.
342 if(ps_global
->prc
->outstanding_pinerc_changes
)
343 write_pinerc(ps_global
, Main
,
344 cleanup_called_from_sig_handler
? WRP_NOUSER
: WRP_NONE
);
346 if(ps_global
->prc
->rd
)
347 rd_close_remdata(&ps_global
->prc
->rd
);
349 free_pinerc_s(&ps_global
->prc
);
353 if(ps_global
->post_prc
){
354 if(ps_global
->post_prc
->outstanding_pinerc_changes
)
355 write_pinerc(ps_global
, Post
,
356 cleanup_called_from_sig_handler
? WRP_NOUSER
: WRP_NONE
);
358 if(ps_global
->post_prc
->rd
)
359 rd_close_remdata(&ps_global
->post_prc
->rd
);
361 free_pinerc_s(&ps_global
->post_prc
);
365 * Can't figure out why this section is inside the ifdef, but no
366 * harm leaving it that way, I guess.
368 #if !defined(DOS) && !defined(OS2)
369 for(i
= 0; i
< ps_global
->s_pool
.nstream
; i
++){
370 m
= ps_global
->s_pool
.streams
[i
];
372 pine_mail_actually_close(m
);
379 imap_flush_passwd_cache(TRUE
);
381 dprint((1, "done with fast_clean_up\n"));
385 #if !defined(DOS) && !defined(OS2)
386 /*----------------------------------------------------------------------
387 handle hang up signal -- SIGUSR2
389 Not much to do. Rely on periodic mail file check pointing.
390 ----------------------------------------------------------------------*/
394 char c
, *mbox
, mboxbuf
[20];
399 for(i
= 0; i
< ps_global
->s_pool
.nstream
; i
++){
400 stream
= ps_global
->s_pool
.streams
[i
];
402 && sp_flagged(stream
, SP_LOCKED
)
403 && !sp_dead_stream(stream
)
407 && (c
= *stream
->mailbox
) != '{' && c
!= '*'){
408 pine_mail_check(stream
); /* write latest state */
409 stream
->rdonly
= 1; /* and become read-only */
410 (void) pine_mail_ping(stream
);
411 mbox
= stream
->mailbox
;
412 if(!strucmp(stream
->mailbox
, ps_global
->inbox_name
)
413 || !strcmp(stream
->mailbox
, ps_global
->VAR_INBOX_PATH
)
414 || !strucmp(stream
->original_mailbox
, ps_global
->inbox_name
)
415 || !strcmp(stream
->original_mailbox
, ps_global
->VAR_INBOX_PATH
))
417 else if(mail_valid_net_parse(stream
->mailbox
, &mb
) && mb
.mailbox
)
420 q_status_message1(SM_ASYNC
, 3, 7,
421 _("Another email program is accessing %s. Session now Read-Only."),
422 short_str((mbox
&& *mbox
) ? mbox
: "folder",
423 mboxbuf
, sizeof(mboxbuf
), 19, FrontDots
));
424 dprint((1, "** folder %s went read-only **\n\n",
432 /*----------------------------------------------------------------------
433 Install signal handler to deal with window resize signal -- SIGWINCH
435 ----------------------------------------------------------------------*/
439 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
440 signal(SIGWINCH
, winch_signal
);
445 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
446 /*----------------------------------------------------------------------
447 Handle window resize signal -- SIGWINCH
449 The planned strategy is just force a redraw command. This is similar
450 to new mail handling which forces a noop command. The signals are
451 help until pine reads input. Then a KEY_RESIZE is forced into the command
453 Note that ready_for_winch is only non-zero inside the read_char function,
454 so the longjmp only ever happens there, and it is really just a jump
455 from lower down in the function up to the top of that function. Its
456 purpose is to return a KEY_RESIZE from read_char when interrupted
457 out of the select lower down in read_char.
458 ----------------------------------------------------------------------*/
459 extern jmp_buf winch_state
;
460 extern int ready_for_winch
, winch_occured
;
463 winch_signal(int sig
)
478 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
480 longjmp(winch_state
, 1);
488 /*----------------------------------------------------------------------
489 Handle child status change -- SIGCHLD
491 The strategy here is to install the handler when we spawn a child, and
492 to let the system tell us when the child's state has changed. In the
493 mean time, we can do whatever. Typically, "whatever" is spinning in a
494 loop alternating between sleep and new_mail calls intended to keep the
497 ----------------------------------------------------------------------*/
498 static short child_signalled
, child_jump
;
499 static jmp_buf child_state
;
502 child_signal(int sig
)
504 #ifdef BACKGROUND_POST
506 * reap background posting process
508 if(ps_global
->post
&& ps_global
->post
->pid
){
512 pid
= process_reap(ps_global
->post
->pid
, &es
, PR_NOHANG
);
513 if(pid
== ps_global
->post
->pid
){
514 ps_global
->post
->status
= es
;
515 ps_global
->post
->pid
= 0;
518 else if(pid
< 0 && errno
!= EINTR
){
519 fs_give((void **) &ps_global
->post
);
522 #endif /* BACKGROUND_POST */
526 longjmp(child_state
, 1);
532 * pipe_callback - handle pine-specific pipe setup like child
533 * signal handler and possibly any display stuff
534 * BUG: this function should probably be in a "alpine/pipe.c"
537 pipe_callback(PIPE_S
*syspipe
, int flags
, void *data
)
542 if(flags
& OSB_PRE_OPEN
){
543 dprint((5, "Opening pipe: (%s%s%s%s%s%s)\n",
544 (syspipe
->mode
& PIPE_WRITE
) ? "W":"", (syspipe
->mode
& PIPE_READ
) ? "R":"",
545 (syspipe
->mode
& PIPE_NOSHELL
) ? "N":"", (syspipe
->mode
& PIPE_PROT
) ? "P":"",
546 (syspipe
->mode
& PIPE_USER
) ? "U":"", (syspipe
->mode
& PIPE_RESET
) ? "T":""));
549 q_status_message(SM_ORDER
, 0, 0,
550 "Waiting for called program to finish...");
552 flush_status_messages(1);
554 draw_keymenu(&pipe_cancel_keymenu
, bitmap
, ps_global
->ttyo
->screen_cols
,
555 1-FOOTER_ROWS(ps_global
), 0, FirstMenu
);
558 if(!(syspipe
->mode
& (PIPE_WRITE
| PIPE_READ
)) && !(syspipe
->mode
& PIPE_SILENT
)){
559 flush_status_messages(0); /* just clean up display */
564 if(syspipe
->mode
& PIPE_RESET
)
569 * Prepare for demise of child. Use SIGCHLD if it's available so
570 * we can do useful things, like keep the IMAP stream alive, while
571 * we're waiting on the child. The handler may have been changed by
572 * something in the composer, in particular, by an alt_editor call.
573 * So we need to re-set it to child_signal and then set it back
576 child_signalled
= child_jump
= 0;
577 syspipe
->chld
= signal(SIGCHLD
, child_signal
);
581 else if(flags
& OSB_POST_OPEN
){
584 clearfooter(ps_global
);
585 ps_global
->mangled_footer
= 1;
588 q_status_message1(SM_ORDER
, 2, 3, "Ignoring completion of %s", syspipe
->command
);
591 if(!(syspipe
->mode
& (PIPE_WRITE
| PIPE_READ
)) && !(syspipe
->mode
& PIPE_SILENT
)){
593 ps_global
->mangled_screen
= 1;
596 if(syspipe
->mode
& PIPE_RESET
){
598 ps_global
->mangled_screen
= 1;
602 (void) signal(SIGCHLD
, SIG_DFL
);
606 else if(flags
& OSB_PRE_CLOSE
){
609 * this is here so close_system_pipe so it has something
610 * to do while we're waiting on the other end of the
611 * pipe to complete. When we're in the background for
612 * a shell, the the side effect is pinging
614 RETSIGTYPE (*alarm_sig
)();
615 int old_cue
= F_ON(F_SHOW_DELAY_CUE
, ps_global
);
618 * remember the current SIGALRM handler, and make sure it's
619 * installed when we're finished just in case the longjmp
620 * out of the SIGCHLD handler caused sleep() to lose it.
621 * Don't pay any attention to that man behind the curtain.
623 alarm_sig
= signal(SIGALRM
, SIG_IGN
);
624 (void) F_SET(F_SHOW_DELAY_CUE
, ps_global
, 0);
625 ps_global
->noshow_timeout
= 1;
626 while(!child_signalled
){
627 /* wake up and prod server */
628 if(!(syspipe
->mode
& PIPE_NONEWMAIL
))
630 (syspipe
->mode
& PIPE_RESET
) ? NM_NONE
: NM_DEFER_SORT
);
632 if(!child_signalled
){
633 if(setjmp(child_state
) == 0){
634 child_jump
= 1; /* prepare to wake up */
635 sleep(600); /* give it 5mins to happen */
638 our_sigunblock(SIGCHLD
);
644 ps_global
->noshow_timeout
= 0;
645 F_SET(F_SHOW_DELAY_CUE
, ps_global
, old_cue
);
646 (void) signal(SIGALRM
, alarm_sig
);
647 (void) signal(SIGCHLD
, syspipe
->chld
);
650 else if(flags
& OSB_POST_CLOSE
){
651 if(syspipe
->mode
& PIPE_RESET
){ /* restore our tty modes */
653 ps_global
->mangled_screen
= 1;
656 if(!(syspipe
->mode
& (PIPE_WRITE
| PIPE_READ
| PIPE_SILENT
))){
657 ClearScreen(); /* No I/O to forked child */
658 ps_global
->mangled_screen
= 1;
665 * Command interrupt support.
671 ps_global
->intr_pending
= 1;
676 intr_handling_on(void)
679 return 0; /* No interrupts in Windows */
681 if(signal(SIGINT
, intr_signal
) == intr_signal
)
682 return 0; /* already installed */
685 if(ps_global
&& ps_global
->ttyo
)
686 draw_cancel_keymenu();
694 intr_handling_off(void)
698 if(signal(SIGINT
, SIG_IGN
) == SIG_IGN
) /* already off! */
701 ps_global
->intr_pending
= 0;
703 if(ps_global
&& ps_global
->ttyo
)
704 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
706 ps_global
->mangled_footer
= 1;
711 /*----------------------------------------------------------------------
712 Set or reset what needs to be set when coming out of pico to run
715 Args: come_back -- If come_back is 0 we're going out of our environment
716 to set up for an external editor.
717 If come_back is 1 we're coming back into pine.
718 ----------------------------------------------------------------------*/
720 ttyfix(int come_back
)
722 #if defined(DEBUG) && (!defined(DOS) || defined(_WINDOWS))
729 enter_text_mode(NULL
);
732 init_tty_driver(ps_global
);
733 init_keyboard(F_ON(F_USE_FK
,ps_global
));
737 fix_windsize(ps_global
);
741 end_keyboard(F_ON(F_USE_FK
,ps_global
));
742 end_tty_driver(ps_global
);
753 /*----------------------------------------------------------------------
754 Suspend Pine. Reset tty and suspend. Suspend is finished when this returns
756 Args: The pine structure
758 Result: Execution suspended for a while. Screen will need redrawing
761 Instead of the usual handling of ^Z by catching a signal, we actually read
762 the ^Z and then clean up the tty driver, then kill ourself to stop, and
763 pick up where we left off when execution resumes.
764 ----------------------------------------------------------------------*/
768 struct pine
*pine
= ps_global
;
771 #if defined(DOS) || defined(OS2)
772 int result
, orig_cols
, orig_rows
;
777 static char *shell
= NULL
;
778 #define STD_SHELL "COMMAND.COM"
781 static char *shell
= NULL
;
782 #define STD_SHELL "CMD.EXE"
786 if(!have_job_control()){
787 bogus_command(ctrl('Z'), F_ON(F_USE_FK
, pine
) ? "F1" : "?");
788 return(NO_OP_COMMAND
);
791 if(F_OFF(F_CAN_SUSPEND
, pine
)){
792 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
793 _("Alpine suspension not enabled - see help text"));
794 return(NO_OP_COMMAND
);
799 return(NO_OP_COMMAND
);
802 isremote
= (ps_global
->mail_stream
&& ps_global
->mail_stream
->mailbox
803 && (*ps_global
->mail_stream
->mailbox
== '{'
804 || (*ps_global
->mail_stream
->mailbox
== '*'
805 && *(ps_global
->mail_stream
->mailbox
+ 1) == '{')));
807 now
= time((time_t *)0);
808 dprint((1, "\n\n --- %s - SUSPEND ---- %s",
809 isremote
? "REMOTE" : "LOCAL", ctime(&now
)));
812 #if defined(DOS) || defined(OS2)
813 suspend_notice("exit");
817 if (!((shell
= getenv("SHELL")) || (shell
= getenv("COMSPEC"))))
820 shell
= cpystr(shell
); /* copy in free storage */
821 for(p
= shell
; (p
= strchr(p
, '/')); p
++)
825 result
= system(shell
);
827 if(F_ON(F_SUSPEND_SPAWNS
, ps_global
)){
829 int flag
= some_stream_is_locked() ? PIPE_NONEWMAIL
: 0;
831 flag
|= PIPE_USER
|PIPE_RESET
;
832 if((syspipe
= open_system_pipe(NULL
, NULL
, NULL
, flag
,
833 0, pipe_callback
, pipe_report_error
)) != NULL
){
834 suspend_notice("exit");
839 (void) close_system_pipe(&syspipe
, NULL
, pipe_callback
);
843 suspend_notice("fg");
852 now
= time((time_t *)0);
853 dprint((1, "\n\n ---- RETURN FROM SUSPEND ---- %s",
858 #if defined(DOS) || defined(OS2)
859 orig_cols
= pine
->ttyo
->screen_cols
;
860 orig_rows
= pine
->ttyo
->screen_rows
;
865 #if defined(DOS) || defined(OS2)
866 if(orig_cols
!= pine
->ttyo
->screen_cols
||
867 orig_rows
!= pine
->ttyo
->screen_rows
)
873 #if defined(DOS) || defined(OS2)
875 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
876 _("Error loading \"%s\""), shell
);
879 if(isremote
&& !ps_global
->mail_stream
->lock
880 && !pine_mail_ping(ps_global
->mail_stream
))
881 q_status_message(SM_ORDER
| SM_DING
, 4, 9,
882 _("Suspended for too long, IMAP connection broken"));
885 #endif /* !_WINDOWS */
890 /*----------------------------------------------------------------------
893 suspend_notice(char *s
)
895 printf(_("\nAlpine suspended. Give the \"%s\" command to come back.\n"), s
);
900 /*----------------------------------------------------------------------
903 suspend_warning(void)
905 puts(_("Warning: Your IMAP connection will be closed if Alpine"));
906 puts(_("is suspended for more than 30 minutes\n"));
911 /*----------------------------------------------------------------------
914 fix_windsize(struct pine
*pine
)
916 int old_width
= pine
->ttyo
->screen_cols
;
918 dprint((9, "fix_windsize()\n"));
919 mark_keymenu_dirty();
921 mark_titlebar_dirty();
923 get_windsize(pine
->ttyo
);
925 if(old_width
!= pine
->ttyo
->screen_cols
)
926 clear_index_cache(pine
->mail_stream
, 0);