#if !defined(lint) && !defined(DOS)
static char rcsid[] = "$Id: signals.c,v 4.31 1994/10/11 00:27:33 mikes Exp $";
#endif
/*----------------------------------------------------------------------

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Builiding, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"

   Copyright 1989-1994  University of Washington

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee to the University of
   Washington is hereby granted, provided that the above copyright notice
   appears in all copies and that both the above copyright notice and this
   permission notice appear in supporting documentation, and that the name
   of the University of Washington not be used in advertising or publicity
   pertaining to distribution of the software without specific, written
   prior permission.  This software is made available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  
   Pine and Pico are trademarks of the University of Washington.
   No commercial use of these trademarks may be made without prior
   written permission of the University of Washington.

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  Revision: 2.13                             *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

  ----------------------------------------------------------------------*/

/*======================================================================
     signals.c
     Different signal handlers for different signals
       - Catches all the abort signals, cleans up tty modes and then coredumps
       - Not much to do for SIGHUP
       - Not much to do for SIGTERM
       - turn SIGWINCH into a KEY_RESIZE command
       - No signals for ^Z/suspend, but do it here anyway
       - Also set up the signal handlers, and hold signals for
         critical imap sections of code.

 ====*/

#include "headers.h"

/*
 * call used by TERM and HUP handlers to quickly close streams
 */
void  fast_clean_up();


              /* SigType is defined in os.h and is either int or void */
static SigType auger_in_signal(), winch_signal(), usr2_signal();



/*----------------------------------------------------------------------
    Install handlers for all the signals we care to catch
  ----------------------------------------------------------------------*/
void
init_signals()
{
#ifdef	_WINDOWS
    /* NO OP */
#else
#ifdef	DOS
    dont_interrupt();
#else
#ifdef DEBUG
#define	CUSHION_SIG	(debug < 7)
#else
#define	CUSHION_SIG	(1)
#endif

    if(CUSHION_SIG){
	signal(SIGILL,  auger_in_signal); 
	signal(SIGTRAP, auger_in_signal);
	signal(SIGIOT,  auger_in_signal); 
	signal(SIGEMT,  auger_in_signal);
	signal(SIGBUS,  auger_in_signal);
	signal(SIGSEGV, auger_in_signal);
	signal(SIGSYS,  auger_in_signal);
	signal(SIGQUIT, auger_in_signal);
	/* Don't catch SIGFPE cause it's rare and we use it in a hack below*/
    }

#ifdef RESIZING
    signal(SIGWINCH, winch_signal);
#endif

    signal(SIGHUP, hup_signal);
    signal(SIGTERM, term_signal);

    /*
     * Set up SIGUSR2 to catch signal from other software using the 
     * c-client to tell us that other access to the folder is being 
     * attempted.  THIS IS A TEST: if it turns out that simply
     * going R/O when another pine is started or the same folder is opened,
     * then we may want to install a smarter handler that uses idle time
     * or even prompts the user to see if it's ok to give up R/O access...
     */
    signal(SIGUSR2, usr2_signal);

    signal(SIGPIPE, SIG_IGN);

#ifdef SIGTSTP
    /* Some unexplained behaviour on Ultrix 4.2 (Hardy) seems to be
       resulting in Pine getting sent a SIGTSTP. Ignore it here.
       probably better to ignore it than let it happen in any case
     */
    signal(SIGTSTP, SIG_IGN); 
#endif /* SIGTSTP */
#endif	/* !DOS */
#endif	/* !_WINDOWS */
}



/*----------------------------------------------------------------------
    Return all signal handling back to normal
  ----------------------------------------------------------------------*/
void
end_signals(blockem)
    int blockem;
{
#ifdef	_WINDOWS
    /* NO OP */
#else
#ifdef	DOS
    interrupt_ok();
#else
    dprint(5, (debugfile, "end_signals(%d)\n", blockem));
    if((int)signal(SIGILL,  blockem ? SIG_IGN : SIG_DFL) < 0) {
        fprintf(stderr, "Error resetting signals: %s\n",
                error_description(errno));
        exit(-1);
    }

    signal(SIGTRAP, blockem ? SIG_IGN : SIG_DFL);
    signal(SIGIOT,  blockem ? SIG_IGN : SIG_DFL);
    signal(SIGEMT,  blockem ? SIG_IGN : SIG_DFL);
    signal(SIGFPE,  blockem ? SIG_IGN : SIG_DFL);
    signal(SIGBUS,  blockem ? SIG_IGN : SIG_DFL);
    signal(SIGSEGV, blockem ? SIG_IGN : SIG_DFL);
    signal(SIGSYS,  blockem ? SIG_IGN : SIG_DFL);
#ifdef RESIZING
    signal(SIGWINCH, blockem ? SIG_IGN : SIG_DFL);
#endif
    signal(SIGQUIT, blockem ? SIG_IGN : SIG_DFL);
#ifdef SIGTSTP
    signal(SIGTSTP, blockem ? SIG_IGN : SIG_DFL);
#endif /* SIGTSTP */
    signal(SIGHUP,  blockem ? SIG_IGN : SIG_DFL);
    signal(SIGTERM, blockem ? SIG_IGN : SIG_DFL);
#endif	/* !DOS */
#endif	/* !_WINDOWS */
}


/*----------------------------------------------------------------------
     Handle signals caused by aborts -- SIGSEGV, SIGILL, etc

Call panic which cleans up tty modes and then core dumps
  ----------------------------------------------------------------------*/
static SigType
auger_in_signal()
{
    end_signals(1);			/* don't catch any more signals */
    dprint(5, (debugfile, "auger_in_signal()\n"));
    panic("Received abort signal");	/* clean up and get out */
    /* No return from panic */
}


/*----------------------------------------------------------------------
      handle hang up signal -- SIGHUP

Not much to do. Rely on periodic mail file check pointing.
  ----------------------------------------------------------------------*/
SigType
hup_signal()
{
#ifndef	DOS
    end_signals(1);			/* don't catch any more signals */
    dprint(1, (debugfile, "\n\n** Received SIGHUP **\n\n\n\n"));
    fast_clean_up();
    printf("\n\nPine finished. Received hang up signal\n\n");
#endif	/* !DOS */
    exit(0);
}


/*----------------------------------------------------------------------
      handle terminate signal -- SIGTERM

Not much to do. Rely on periodic mail file check pointing.
  ----------------------------------------------------------------------*/
SigType
term_signal()
{
#ifndef	DOS
    end_signals(1);			/* don't catch any more signals */
    dprint(1, (debugfile, "\n\n** Received SIGTERM **\n\n\n\n"));
    fast_clean_up();
    printf("\n\nPine finished. Received terminate signal\n\n");
#endif	/* !DOS */
    exit(0);
}


/*----------------------------------------------------------------------
     Handle cleaning up mail streams and tty modes...
Not much to do. Rely on periodic mail file check pointing.  Don't try
cleaning up screen or flushing output since stdout is likely already
gone.  To be safe, though, we'll at least restore the original tty mode.
  ----------------------------------------------------------------------*/
void
fast_clean_up()
{
#ifndef	DOS
    if(ps_global->inbox_stream != NULL && !ps_global->inbox_stream->lock){
        if(ps_global->inbox_stream == ps_global->mail_stream)
          ps_global->mail_stream = NULL; 
        mail_close(ps_global->inbox_stream);
    }

    if(ps_global->mail_stream != NULL &&
       ps_global->mail_stream != ps_global->inbox_stream &&
       !ps_global->mail_stream->lock)
      mail_close(ps_global->mail_stream);

    Raw(0);

#ifdef DEBUG
    if(debugfile)
      fclose(debugfile);
#endif    
#endif	/* !DOS */
}


#ifndef	DOS
/*----------------------------------------------------------------------
      handle hang up signal -- SIGUSR2

Not much to do. Rely on periodic mail file check pointing.
  ----------------------------------------------------------------------*/
static SigType
usr2_signal()
{
    char c;
    dprint(1, (debugfile, "\n\n** Received SIGUSR2 **\n\n\n\n"));

    check_point(DoItNow);
    if(ps_global->inbox_stream != NULL && !ps_global->inbox_stream->lock
       && (c = *ps_global->inbox_stream->mailbox) != '{' && c != '*'){
	ps_global->inbox_stream->rdonly = 1; 
	mail_ping(ps_global->inbox_stream);
	q_status_message(0, 2, 2,
		   "Another Pine is accessing Inbox.  Session now Read-Only.");
	dprint(1, (debugfile, "** INBOX went read-only **\n\n"));
    }

    if(ps_global->mail_stream != NULL &&
       ps_global->mail_stream != ps_global->inbox_stream &&
       !ps_global->mail_stream->lock &&
       (c = *ps_global->mail_stream->mailbox) != '{' && c != '*'){
	ps_global->mail_stream->rdonly = 1; 
	mail_ping(ps_global->mail_stream);
	q_status_message(0, 2, 2,
		  "Another Pine is accessing folder.  Session now Read-Only.");
	dprint(1, (debugfile, "** secondary folder went read-only **\n\n"));
    }
}
#endif


#ifdef RESIZING
/*----------------------------------------------------------------------
   Handle window resize signal -- SIGWINCH
  
   The planned strategy is just force a redraw command. This is similar
  to new mail handling which forces a noop command. The signals are
  help until pine reads input. Then a KEY_RESIZE is forced into the command
  stream .
  ----------------------------------------------------------------------*/
extern jmp_buf  winch_state;
extern int      ready_for_winch, winch_occured;

SigType
static winch_signal()
{
    dprint(9,(debugfile, "SIGWINCH ready_for_winch: %d winch_occured:%d\n",
               ready_for_winch, winch_occured));
    get_windsize(ps_global->ttyo);
    if(ready_for_winch)
      longjmp(winch_state, 1);
    else
      winch_occured = 1;
}
#endif



/*----------------------------------------------------------------------
     Suspend Pine. Reset tty and suspend. Suspend is done when this returns

   Args:  The pine structure

 Result:  Execution suspended for a while. Screen will need redrawing 
          after this is done.

 Instead of the usual handling of ^Z by catching a signal, we actually read
the ^Z and then clean up the tty driver, then kill ourself to stop, and 
pick up where we left off when execution resumes.
  ----------------------------------------------------------------------*/
void
do_suspend(pine) 
    struct pine *pine;
{
#ifdef	_WINDOWS
    /* suspend what? */
#else
    long now, then;
    char curf[MAXPATH+1], *shell;
    int  result;

    if(!have_job_control())
      return;

    strcpy(curf, ps_global->cur_folder);
    expand_foldername(curf);
    then = time(0);
    dprint(1, (debugfile, "\n\n - %s --- SUSPEND ----  %s",curf,ctime(&then)));
    end_keyboard(F_ON(F_USE_FK,pine));
    end_tty_driver(pine);
    end_screen();
#ifdef	DOS
    printf("\nPine suspended. Give the \"exit\" command to come back.\n");
#else
    printf("\nPine suspended. Give the \"fg\" command to come back.\n");
#endif
    if(curf[0] == '{')
      printf("Warning: Your IMAP connection will be closed if Pine\nis suspended for more than 30 minutes\n\n");

#ifdef	DOS
    result = system((shell = getenv("COMSPEC")) ? shell : "command");
#else
    stop_process();
#endif	/* DOS */
    now = time(0);
    dprint(1, (debugfile, "\n\n ---- RETURN FROM SUSPEND ----  %s",
               ctime(&now)));
    init_screen();
    init_tty_driver(pine);
    init_keyboard(F_ON(F_USE_FK,pine));
 
#ifdef	DOS
    if(result == -1)
      q_status_message(0,2,2,"\007Error loading COMMAND.COM");
#endif
    if(curf[0] == '{' &&
       (char *)mail_ping(ps_global->mail_stream) == NULL) {
        q_status_message(1, 3, 6,
                         "\007Suspended for too long, IMAP connection broken");
    }
#endif	/* !_WINDOWS */
}


#ifdef	DOS
SigType (*hold_int)(int), (*hold_term)(int), (*hold_quit)(int);
#else
SigType (*hold_hup)(), (*hold_int)(), (*hold_term)(), (*hold_usr2)(),
	(*hold_quit)();
#endif

/*----------------------------------------------------------------------
     Ignore signals when imap is running through critical code

 Args: stream -- The stream on which critical operation is proceeding
 ----*/

void 
mm_critical(stream)
     MAILSTREAM *stream;
{
    stream = stream; /* For compiler complaints that this isn't used */
#ifndef	DOS
    hold_hup  = signal(SIGHUP, SIG_IGN);
    hold_usr2 = signal(SIGUSR2, SIG_IGN);
#endif
    hold_int  = signal(SIGINT, SIG_IGN);
    hold_term = signal(SIGTERM, SIG_IGN);
    dprint(9, (debugfile, "Done with IMAP critical on %s\n",
              stream ? stream->mailbox : "<no folder>" ));
}



/*----------------------------------------------------------------------
   Reset signals after critical imap code
 ----*/
void
mm_nocritical(stream)
     MAILSTREAM *stream;
{ 
    stream = stream; /* For compiler complaints that this isn't used */

#ifndef	DOS
    (void)signal(SIGHUP, hold_hup);
    (void)signal(SIGUSR2, hold_usr2);
#endif
    (void)signal(SIGINT, hold_int);
    (void)signal(SIGTERM, hold_term);
    dprint(9, (debugfile, "Done with IMAP critical on %s\n",
              stream ? stream->mailbox : "<no folder>" ));
}
