/** 
 * -- Handle signals in forth
 *
 *  Copyright (C) Tektronix, Inc. 1998 - 2001. All rights reserved.
 *
 *  @see     GNU LGPL
 *  @author  Tektronix CTE            @(#) %derived_by: guidod %
 *  @version %version: bln_mpt1!5.21 %
 *    (%date_modified: Tue Jun 04 13:19:30 2002 %)
 *
 *  @description
 *  The signal-callback system divides signals internally
 *  into the following classes:
 *
 *  
*
P4_ON_XXXXX:
* a signal which will be assigned a corresponding THROW * on forth level, e.g. for SIGFPE *
*
Abort:
* a signal that will not kill the current forth process * but which has not forth-level THROW either, e.g. SIGILL. * It will result in an ABORT" Signal-Description" *
*
Fatal:
* the current forth process will die gracefully. *
*
Default:
* A signal with some unknown meaning, exported to allow * a forth-programmer to hook it anyway, e.g. to let * a programmer on AIX to intercept SIGGRANT and run * a forth word, otherwise the signal will be left untouched. *
*
Chandled:
* A signal used internally be PFE and initially hooked * by the runtime system, in general this would be the * Job-Control signals and SIGWINCH that will get send * when an xterm changes its size. *
*
* * The first three classes will go to the default Forth Signal-Handler. * Its execution will look for user-routine being registered (in * which cases that forth-routine will be executed) and otherwise * do its default action (to throw, abort" or exit). * * The fourth class is not hooked until some user-code requests that * signal in which case the user-defiend forth-routine is executed * as its action, otherwise the system-defined default-action will * be left untouched. * * The signal of the fourth type are not handled by the default * signal handler defined herein, so can not just call a * forth word, if I still get this right ;-) *

Dirk-Uwe Zoller

* * note: forth-level callbacks might not work as expected on * all systems that the rest of PFE runs on. Be careful. *

Guido Draheim

*/
/*@{*/
#if defined(__version_control__) && defined(__GNUC__)
static char* id __attribute__((unused)) = 
"@(#) $Id: %full_filespec:  signals-ext.c~bln_mpt1!5.20:csrc:bln_12xx!1 % $";
#endif

#define _P4_SOURCE 1

#include <pfe/pfe-base.h>
#include <pfe/def-xtra.h>

#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <pfe/def-comp.h>
#include <pfe/term-sub.h>
#include <pfe/option-ext.h>
#include <pfe/_nonansi.h>
#include <pfe/_missing.h>
#include <pfe/logging.h>
#include <pfe/def-restore.h>

typedef void (*SigHdl) (int);	
/* signal handler function type */
enum				/* Classification of signals: The */
{				/* signal class is either a THROW code or: */
    Fatal,			/*   p4th terminates if such a signal arrives */
    Abort,			/*   executes ABORT" */
    Chandled,			/*   handled by C code, e.g. stop/continue */
    Default			/*   left alone by p4th, cannot be caught */
};
typedef struct			/* all we need to know about a signal */
{
    short sig;			/* the signal */
    short cLass;		/* a classification */
    char const * name;		/* the name of the signal */
#ifndef PFE_HAVE_SYS_SIGLIST
    char const * msg;		/* a textual signal description */
#endif
    SigHdl old;			/* state of signal before we took it */
    p4xt hdl;			/* a forth word to handle the signal */
} Siginfo;

#ifdef PFE_OLD_STRINGIZE
# ifdef PFE_HAVE_SYS_SIGLIST
# define SIG(NM,CL,MSG)		{ NM, CL, "NM", SIG_DFL }
# else
# define SIG(NM,CL,MSG)		{ NM, CL, "NM", MSG, SIG_DFL }
# endif
#else
# ifdef PFE_HAVE_SYS_SIGLIST
# define SIG(NM,CL,MSG)		{ NM, CL, #NM, SIG_DFL }
# else
# define SIG(NM,CL,MSG)		{ NM, CL, #NM, MSG, SIG_DFL }
# endif
#endif

/*
 * With the means of the above structures and classifications we
 * describe a lot of signals to p4th conditionally. Fine if a system
 * has a signal. If not it's just as well.
 */
static Siginfo siginfo[] =
{

/**
 * signal constants as being defined on the local system.
 * not all signal names must be valid in a specific system,
 * the pfe will only export those being available.
 * 

* The set of signal names being tested for during pfe compilation * are described in W.R.Stevens'
* "Advanced Programming in the UNIX Environment" *

* The signals constants are generated by their * signal constructor <<load_signals>> */ /*"SIGABRT"*/ #ifdef SIGABRT SIG (SIGABRT, Fatal, "abnormal termination (abort)"), #endif /*"SIGALRM"*/ #ifdef SIGALRM SIG (SIGALRM, Abort, "time out (alarm)"), #endif /*"SIGBUS"*/ #ifdef SIGBUS SIG (SIGBUS, P4_ON_ADDRESS_ALIGNMENT, NULL), #endif /*"SIGCHLD"*/ #ifdef SIGCHLD SIG (SIGCHLD, Default, "change in status of child"), #endif /*"SIGCONT"*/ #ifdef SIGCONT SIG (SIGCONT, Default, "continue stopped process"), #endif /*"SIGEMT"*/ #ifdef SIGEMT SIG (SIGEMT, Abort, "hardware fault (EMT)"), #endif /*"SIGPFE"*/ #ifdef SIGFPE SIG (SIGFPE, P4_ON_FP_FAULT, NULL), #endif /*"SIGHUP"*/ #ifdef SIGHUP SIG (SIGHUP, Fatal, "hangup"), #endif /*"SIGILL"*/ #ifdef SIGILL SIG (SIGILL, Abort, "illegal hardware instruction"), #endif /*"SIGINFO"*/ #ifdef SIGINFO SIG (SIGINFO, Default, "status request from keyboard"), #endif /*"SIGINT"*/ #ifdef SIGINT SIG (SIGINT, P4_ON_USER_INTERRUPT, NULL), #endif /*"SIGIO"*/ #ifdef SIGIO SIG (SIGIO, Default, "asynchronous io"), #endif /*"SIGIOT"*/ #ifdef SIGIOT SIG (SIGIOT, Abort, "hardware fault (IOT)"), #endif /*"SIGKILL"*/ #ifdef SIGKILL SIG (SIGKILL, Default, "kill"), #endif /*"SIGPIPE"*/ #ifdef SIGPIPE SIG (SIGPIPE, Fatal, "write to pipe with no readers"), #endif /*"SIGPOLL"*/ #ifdef SIGPOLL SIG (SIGPOLL, Abort, "pollable event (poll)"), #endif /*"SIGPROF"*/ #ifdef SIGPROF SIG (SIGPROF, Abort, "profiling time alarm (timer)"), #endif /*"SIGPWR"*/ #ifdef SIGPWR SIG (SIGPWR, Default, "power fail/restart"), #endif /*"SIGQUIT"*/ #ifdef SIGQUIT SIG (SIGQUIT, Fatal, "terminal quit key"), #endif /*"SIGSEGV"*/ #ifdef SIGSEGV SIG (SIGSEGV, P4_ON_INVALID_MEMORY, NULL), #endif /*"SIGSTOP"*/ #ifdef SIGSTOP SIG (SIGSTOP, Default, "stop"), #endif /*"SIGSYS"*/ #ifdef SIGSYS SIG (SIGSYS, Abort, "invalid system call"), #endif /*"SIGTERM"*/ #ifdef SIGTERM SIG (SIGTERM, Fatal, "terminated"), #endif /*"SIGTRAP"*/ #ifdef SIGTRAP SIG (SIGTRAP, Abort, "hardware fault (trace trap)"), #endif /*"SIGTSTP"*/ #ifdef SIGTSTP SIG (SIGTSTP, Chandled, "terminal stop character"), #endif /*"SIGTTIN"*/ #ifdef SIGTTIN SIG (SIGTTIN, Chandled, "background read from control tty"), #endif /*"SIGTTOU"*/ #ifdef SIGTTOU SIG (SIGTTOU, Chandled, "background write to control tty"), #endif /*"SIGURG"*/ #ifdef SIGURG SIG (SIGURG, Abort, "urgent condition"), #endif /*"SIGUSR1"*/ #ifdef SIGUSR1 SIG (SIGUSR1, Abort, "user defined signal 1"), #endif /*"SIGUSR2"*/ #ifdef SIGUSR2 SIG (SIGUSR2, Abort, "user defined signal 2"), #endif /*"SIGVTALRM"*/ #ifdef SIGVTALRM SIG (SIGVTALRM, Abort, "virtual time alarm (timer)"), #endif /*"SIGWINCH"*/ #ifdef SIGWINCH SIG (SIGWINCH, Chandled, "terminal window size change"), #endif /*"SIGXCPU"*/ #ifdef SIGXCPU SIG (SIGXCPU, Fatal, "CPU limit exceeded"), #endif /*"SIGXFSZ"*/ #ifdef SIGXFSZ SIG (SIGXFSZ, Abort, "file size limit exceeded"), #endif /** * Some Signals are specific to certain systems. They don't belong * to the normal set of UNIX signals like SIGALRM or SIGQUIT *

* The signals constants are generated by their * signal constructor <<load_signals>> */ /*"SIGSTKFLT"*/ #ifdef SIGSTKFLT /* Linux */ SIG (SIGSTKFLT, Abort, "SIGSTKFLT"), #endif /*"SIGBREAK"*/ #ifdef SIGBREAK /* EMX, Watcom */ SIG (SIGBREAK, P4_ON_USER_INTERRUPT, NULL), #endif /*"SIGMSG"*/ #ifdef SIGMSG /* AIX 3.2 */ SIG (SIGMSG, Default, "input data is in the HFT ring buffer"), #endif /*"SIGDANGER"*/ #ifdef SIGDANGER SIG (SIGDANGER, Default, "system crash imminent; free up some page space"), #endif /*"SIGMIGRATE"*/ #ifdef SIGMIGRATE SIG (SIGMIGRATE, Default, "migrate process (see TCF)"), #endif /*"SIGPRE"*/ #ifdef SIGPRE SIG (SIGPRE, Default, "programming exception"), #endif /*"SIGVIRT"*/ #ifdef SIGVIRT SIG (SIGVIRT, Default, "AIX virtual time alarm"), #endif /*"SIGGRANT"*/ #ifdef SIGGRANT SIG (SIGGRANT, Default, "HFT monitor mode granted"), #endif /*"SIGRETRACT"*/ #ifdef SIGRETRACT SIG (SIGRETRACT, Default, "HFT monitor mode should be relinguished"), #endif /*"SIGSOUND"*/ #ifdef SIGSOUND SIG (SIGSOUND, Default, "HFT sound control has completed"), #endif /*"SIGSAK"*/ #ifdef SIGSAK SIG (SIGSAK, Default, "secure attention key"), #endif };

static int
getinfo (int sig)
{
    int i;

    for (i = 0; i < DIM (siginfo); i++)
        if (siginfo[i].sig == sig)
            return i;

    p4_throw (P4_ON_ARG_TYPE);
    return i;
}

#define SIGHANDLER (PFE_RETSIGTYPE (*)(int))

/* in vxworks, signal-handlers might possibly be in another context... */
#if defined P4_REGTH && ! defined unix
#define P4_REGTH_SIGNAL_SAVEALL P4_CALLER_SAVEALL; p4TH = p4_main_threadP;
#define P4_REGTH_SIGNAL_RESTORE P4_CALLER_RESTORE;
#else
#define P4_REGTH_SIGNAL_SAVEALL 
#define P4_REGTH_SIGNAL_RESTORE
#endif

static void
sig_handler (int sig)		/* Signal handler for all signals */
{
    Siginfo *s;
    const char *msg;

# if !KEEPS_SIGNALS
    signal (sig, SIGHANDLER sig_handler);	/* reinstall handler */
# endif
# if defined SYS_EMX || defined SYS_WC_OS2V2
    signal (sig, SIG_ACK);	/* OS/2: acknowledge signal */
# endif
# if defined SYS_EMX
    _control87 (EM_DENORMAL | EM_INEXACT, MCW_EM);
# endif

    {
        P4_REGTH_SIGNAL_SAVEALL;

        s = &siginfo[getinfo (sig)];
        if (s->hdl)
            p4_call (s->hdl);	/* a p4sys.handled signal */
        else
        {
#          ifdef PFE_HAVE_SYS_SIGLIST
            msg = sys_siglist[sig];
#          else
            msg = s->msg;
#          endif
            switch (s->cLass)
            {
             default:		  /* an ANSI-Forth defined condition */
                 P4_info2 ("signal %i throw %i", sig, s->cLass);
                 p4_throw (s->cLass);
             case Abort:		/* another catchable signal */
                 P4_info2 ("signal %i abort %s", sig, msg); 
                 p4_throws (-256 - sig, msg, 0);
             case Fatal:		/* a signal that kills us */
                 P4_fatal2 ("Received signal %s, %s", s->name, msg);
                 PFE.exitcode = 1;
                 p4_longjmp_exit ();
            }
        }
        P4_REGTH_SIGNAL_RESTORE;
    }
}
/*
 * Actions to take when job control interferes or on window size change:
 */
#ifdef SIGTSTP
static void
stop_hdl (int sig)
{
#  if !KEEPS_SIGNALS
    signal (sig, SIGHANDLER stop_hdl);
#  endif
    { 
        P4_REGTH_SIGNAL_SAVEALL;
        PFE.on_stop ();
        p4_swap_signals ();
#      if _BSD
        _pfe_raise (SIGSTOP);
#      else
        _pfe_raise (SIGTSTP);
#      endif
        p4_swap_signals ();
        PFE.on_continue ();
        P4_REGTH_SIGNAL_RESTORE;
    }
}
#endif

#ifdef SIGWINCH
static void
winchg_hdl (int sig)
{
#  if !KEEPS_SIGNALS
    signal (sig, winchg_hdl);
#  endif
    {
        P4_REGTH_SIGNAL_SAVEALL;
        PFE.on_winchg ();
        P4_REGTH_SIGNAL_RESTORE;
    }
}
#endif

#ifdef _AIX_CC
#define volatile
#endif

#ifdef SIGALRM
static volatile void
handle_sigalrm (int sig)
{
#  if !KEEPS_SIGNALS
    signal (sig, SIGHANDLER handle_sigalrm);
#  endif
    {
        P4_REGTH_SIGNAL_SAVEALL;
        if (PFE.on_sigalrm)
            (*PFE.on_sigalrm)();
        P4_REGTH_SIGNAL_RESTORE;
    }
}
#endif

/**
 * install all signal handlers:
 */
_export void
p4_install_signal_handlers (void)
{
    int i, j;
    const char use_signals[] = "signals";

    if (p4_search_option_value (use_signals, sizeof(use_signals)-1, 
                                P4_TRUE, PFE.set))
    for (i = 0; i < DIM (siginfo); i++)
    {
        /* some systems may have more than one name for the same signal,
         * take care not to install it twice: */
        for (j = 0; j < i; j++)
            if (siginfo[i].sig == siginfo[j].sig)
                goto cont;
        switch (siginfo[i].cLass)
	{
         default:
             siginfo[i].old = signal (siginfo[i].sig, sig_handler);
             if (0) { P4_fail3("signal %s @ %i, hooked %p", 
                        siginfo[i].name, siginfo[i].sig, siginfo[i].old); }
         case Chandled:
         case Default:;
	}
     cont:;
    }
#ifdef SIGTSTP
    if (signal (SIGTSTP, SIG_IGN) == SIG_DFL)
    {
        signal (SIGTSTP, stop_hdl);
        siginfo[getinfo (SIGTSTP)].old = SIG_DFL;
        siginfo[getinfo (SIGTTIN)].old = signal (SIGTTIN, stop_hdl);
        siginfo[getinfo (SIGTTOU)].old = signal (SIGTTOU, stop_hdl);
    }
#endif
#ifdef SIGWINCH
#ifdef KEEPS_SIGNALS
    signal (SIGWINCH, winchg_hdl);
#endif
    winchg_hdl (SIGWINCH);
#endif

#ifdef SIGALRM
    signal (SIGALRM, SIGHANDLER handle_sigalrm);
#endif
}
/**
 * switch between p4th setting of signals and state before 
 */
_export void
p4_swap_signals (void)
{
    int i;

    for (i = 0; i < DIM (siginfo); i++)
        if (siginfo[i].cLass != Default || siginfo[i].hdl)
            siginfo[i].old = signal (siginfo[i].sig, siginfo[i].old);
}
/**
 * xt != NULL: install forth word as signal handler for signal
 * xt == NULL: install p4th default signal handler for signal
 */
_export p4xt
p4_forth_signal (int sig, p4xt xt)
{
    int i = getinfo (sig);
    p4xt old;
    
    old = siginfo[i].hdl;
    siginfo[i].hdl = xt;

    if (siginfo[i].cLass == Default)
    {
        if (xt == NULL)
        {
            siginfo[i].old = signal (sig, siginfo[i].old);
        }
        else
        {
            siginfo[i].old = signal (sig, sig_handler);
        }
    }

    return old;
}
/**
 * Load constants for each signal found into the dictionary.
 */
_export void
p4_load_signals (p4_Wordl *wid)
{
    Siginfo *s;

    for (s = siginfo; s < siginfo + DIM (siginfo); s++)
    {
        p4_header_comma (s->name, strlen (s->name), wid);
	FX_RUNTIME1(p4_constant);
        FX_UCOMMA (s->sig);
    }
}
/** 
 * the signals-constructor will declare the available
 * system signals as contants - usually sth. like
 * SIGALRM or SIGHUP or SIGABRT
 * 

* some signals are only valid in specific systems, * like SIGBREAK or SIGMSG or SIGVIRT */ FCode (p4_load_signals) { p4_load_signals (CURRENT); }

P4_LISTWORDS (signals) =
{
     (, ),
     (, ),
};
P4_COUNTWORDS (signals, "Signals Extension");
/*@}*/