blob: fcc3f6302fae69b67172fb046b54e12236d4267a [file] [log] [blame]
/*****************************************************************************
* *
* DH_SIG.C *
* *
* Freely redistributable and modifiable. Use at your own risk. *
* *
* Copyright 1994 The Downhill Project *
* *
*****************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#include "psignal.h"
extern int UserBreak;
/* Define stuff ************************************************************ */
#ifndef TRUE
# define TRUE 1
#endif
#ifndef FALSE
# define FALSE 0
#endif
/* Signal groupings ======================================================== */
#define SIGNAL_DEFAULT_EXIT case SIGABRT: \
case SIGFPE: \
case SIGILL: \
case SIGINT: \
case SIGSEGV: \
case SIGTERM:
#define IS_SIGNAL(a) ( ((a) > 0 ) && ((a) < NSIG))
/* Struct stuff **************************************************************/
struct downhill_Signal_Struct
{
void (*signal_Handler)(int);
int signal_Count;
int signal_Extra;
sigset_t signal_Mask;
int signal_Flags;
};
/* Static stuff **************************************************************/
static struct downhill_Signal_Struct* downhill_Signal_Info = NULL;
static sigset_t downhill_Sigset_Mask = 0;
static HANDLE IGotASignal;
/* Function stuff ************************************************************/
/* Handle a signal ========================================================= */
void raise(int signal_Number)
{
if (!IS_SIGNAL(signal_Number)) {
errno = EINVAL;
return;
}
/* Check to see if we're masking this signal */
if (sigismember(&downhill_Sigset_Mask,signal_Number))
{
downhill_Signal_Info[signal_Number].signal_Count++;
}
else
{
/* Do a signal's action */
if (downhill_Signal_Info[signal_Number].signal_Handler ==
SIG_DFL)
{
switch (signal_Number)
{
SIGNAL_DEFAULT_EXIT
exit(3);
default:
break;
}
}
else if (
(downhill_Signal_Info[signal_Number].signal_Handler ==
SIG_IGN)
)
{/* IGNORE */}
else
{
void (*signal_HandlerOld)(int);
/* Do we reset the handler? */
signal_HandlerOld =
downhill_Signal_Info[signal_Number].signal_Handler;
if (downhill_Signal_Info[signal_Number].signal_Flags&
SA_RESETHAND)
{
downhill_Signal_Info[signal_Number].
signal_Handler = SIG_DFL;
}
/* Do the action */
if ((signal_Number == SIGCHLD) &&
(downhill_Signal_Info[signal_Number].signal_Flags&
SA_NOCLDSTOP))
{
/* Ignore SIGCHLD */
}
else
{
sigset_t sigset_MaskOriginal =
downhill_Sigset_Mask;
sigset_t sigset_MaskNew = downhill_Signal_Info[
signal_Number].signal_Mask;
/* Set the new signal mask */
sigaddset(&sigset_MaskNew,signal_Number);
sigprocmask(SIG_BLOCK,&sigset_MaskNew,NULL);
/* Execute the handler */
signal_HandlerOld(signal_Number);
/* Restore the signal mask */
sigprocmask(SIG_SETMASK,&sigset_MaskOriginal,
NULL);
}
}
PulseEvent(IGotASignal);
}
}
/* Init the signal re-direction ============================================ */
/* Hardware interrupt handler. */
static BOOL CALLBACK hwIntrHandler (DWORD type)
{
int ret;
switch (type) {
case CTRL_C_EVENT :
case CTRL_BREAK_EVENT :
/*
Why SIGBREAK? SIGINT is used internally by R and the signal
handler for SIGINT ends with a 'longjmp'. But, under Windows,
hardware interrupt handler runs in a different thread. So,
longjmp fails (tipically it will crash R). So, we raise a SIGBREAK
to record that the user want to stop. This is then
processed at due time. Drawback of this approach: if R is lost
inside a C or Fortran routine we don't break it. I (g.m.) have
tried to raise a signal in the appropriate thread using
SuspendThread/GetThreadContext/setting Eip to the signal handler/
SetThreadContext/ResumeThread but I had success only under NT.
*/
raise(SIGBREAK);
/* Seems that SIGBREAK is not working under 1,4,0, so do it via
a semaphore, as RGui does */
UserBreak = 1;
ret = TRUE;
break;
default:
ret = FALSE;
}
return ret ;
}
static int downhill_Signal_Init(void)
{
/* Skip this if we've already done it */
if (downhill_Signal_Info == NULL)
{
if (!(downhill_Signal_Info =
calloc(sizeof(struct downhill_Signal_Struct),NSIG)) ||
!(IGotASignal=CreateEvent(NULL,FALSE,FALSE,NULL)) ||
!SetConsoleCtrlHandler (hwIntrHandler, TRUE))
{
if (downhill_Signal_Info) free(downhill_Signal_Info);
if (IGotASignal) CloseHandle(IGotASignal);
errno = ENOMEM;
return FALSE;
}
}
return TRUE;
}
/* Set a signal action ===================================================== */
int sigaction(int signal_Number,struct sigaction* sigaction_Info,
struct sigaction* sigaction_InfoOld)
{
/* Make sure we're init'd */
if (!downhill_Signal_Init())
{
return -1;
}
/* Set the signal */
if (IS_SIGNAL(signal_Number)) {
if (sigaction_InfoOld != NULL)
{
sigaction_InfoOld->sa_handler =
downhill_Signal_Info[signal_Number].
signal_Handler;
sigaction_InfoOld->sa_mask =
downhill_Signal_Info[signal_Number].
signal_Mask;
sigaction_InfoOld->sa_flags =
downhill_Signal_Info[signal_Number].
signal_Flags;
}
if (sigaction_Info != NULL)
{
downhill_Signal_Info[signal_Number].
signal_Handler = sigaction_Info->sa_handler;
downhill_Signal_Info[signal_Number].
signal_Count = 0;
downhill_Signal_Info[signal_Number].
signal_Extra = 0;
downhill_Signal_Info[signal_Number].
signal_Mask = sigaction_Info->sa_mask;
downhill_Signal_Info[signal_Number].
signal_Flags = sigaction_Info->sa_flags;
}
}
else {
errno = EINVAL;
return -1;
}
return 0;
}
/* Set the action of a signal ============================================== */
sighandler_t signal(int signal_Number, sighandler_t signal_Handler)
{
sighandler_t signal_HandlerOld;
/* Make sure we're init'd */
if (!IS_SIGNAL(signal_Number) || !downhill_Signal_Init() )
{
return SIG_ERR;
}
signal_HandlerOld =
downhill_Signal_Info[signal_Number].signal_Handler;
downhill_Signal_Info[signal_Number].signal_Handler =
signal_Handler;
downhill_Signal_Info[signal_Number].signal_Count = 0;
downhill_Signal_Info[signal_Number].signal_Extra = 0;
downhill_Signal_Info[signal_Number].signal_Mask = 0;
downhill_Signal_Info[signal_Number].signal_Flags = 0;
return signal_HandlerOld;
}
/* Add a signal to a set =================================================== */
int sigaddset(sigset_t* sigset_Info,int signal_Number)
{
if (IS_SIGNAL(signal_Number)) {
(*sigset_Info) |= (1 << (signal_Number - 1));
return 0;
}
else {
errno = EINVAL;
return -1;
}
return 0;
}
/* Remove a signal from a set ============================================== */
int sigdelset(sigset_t* sigset_Info,int signal_Number)
{
if (IS_SIGNAL(signal_Number)) {
*sigset_Info &= ~(1<< (signal_Number - 1));
return 0;
}
else {
errno = EINVAL;
return -1;
}
return 0;
}
/* Empty a set ============================================================= */
int sigemptyset(sigset_t* sigset_Info)
{
*sigset_Info = 0;
return 0;
}
/* Fill a set ============================================================== */
int sigfillset(sigset_t* sigset_Info)
{
*sigset_Info = (sigset_t)-1;
return 0;
}
/* Checks if a signal is in a set ========================================== */
int sigismember(sigset_t* sigset_Info,int signal_Number)
{
if (IS_SIGNAL(signal_Number)) {
if ( *sigset_Info & (1 << (signal_Number-1)))
return 1;
else
return 0;
}
errno = EINVAL;
return -1;
}
/* Returns the signals pending ============================================= */
int sigpending(sigset_t* sigset_Info)
{
int signal_Index;
/* Make sure we're init'd */
if (!downhill_Signal_Init())
{
return -1;
}
/* Check all the pending signals */
sigemptyset(sigset_Info);
for (signal_Index = 1;signal_Index < NSIG;signal_Index++)
{
if (downhill_Signal_Info[signal_Index].signal_Count > 0)
{
sigaddset(sigset_Info,signal_Index);
}
}
return 0;
}
/* Change the blocked signals ============================================== */
int sigprocmask(int mask_Function,sigset_t* sigset_Info,
sigset_t* sigset_InfoOld)
{
int signal_Index;
sigset_t sigset_MaskOld = downhill_Sigset_Mask;
/* Make sure we're init'd */
if (!downhill_Signal_Init())
{
return -1;
}
/* Return the current value */
if (sigset_InfoOld != NULL)
{
*sigset_InfoOld = sigset_MaskOld;
}
/* Set the new mask */
if (sigset_Info) {
switch (mask_Function)
{
case SIG_BLOCK:
downhill_Sigset_Mask |= (*sigset_Info);
break;
case SIG_UNBLOCK:
downhill_Sigset_Mask &= ~(*sigset_Info);
break;
case SIG_SETMASK:
downhill_Sigset_Mask = *sigset_Info;
break;
default:
errno = EINVAL;
return -1;
}
}
/* And release any signals that were pending */
for (signal_Index = 1;signal_Index < NSIG;signal_Index++)
{
if ((!sigismember(&downhill_Sigset_Mask,signal_Index)) &&
(sigismember(&sigset_MaskOld,signal_Index)) &&
(downhill_Signal_Info[signal_Index].signal_Count > 0))
{
downhill_Signal_Info[signal_Index].signal_Count = 0;
raise(signal_Index);
}
}
return 0;
}
/* Set signal mask ========================================================= */
int sigsetmask(int signal_MaskNew)
{
int signal_MaskOld = downhill_Sigset_Mask;
if (sigprocmask(SIG_SETMASK, &signal_MaskNew, NULL) == -1)
return (int)-1;
return signal_MaskOld;
}
/* Add signals to mask ===================================================== */
int sigblock(int signal_MaskNew)
{
/* Block a specific group of signals */
return sigsetmask(downhill_Sigset_Mask|signal_MaskNew);
}
/* Hold a signal =========================================================== */
int sighold(int signal_Number)
{
/* Block a specific signal */
if (sigblock(sigmask(signal_Number)) == -1)
{
return -1;
}
return 0;
}
/* Release a signal ======================================================== */
int sigrelse(int signal_Number)
{
/* Release a specific signal */
if (sigsetmask(downhill_Sigset_Mask&(~sigmask(signal_Number))) == -1)
{
return -1;
}
return 0;
}
/* Pause until a signal ==================================================== */
int pause(void)
{
/* Wait for a signal */
WaitForSingleObject(IGotASignal,INFINITE);
/* And return if we were interrupted */
errno = EINTR;
return -1;
}
/* Suspend the process until a signal ====================================== */
int sigsuspend(sigset_t* sigset_Info)
{
sigset_t sigset_MaskOriginal = downhill_Sigset_Mask;
/* Set the new mask */
sigprocmask(SIG_SETMASK,sigset_Info,NULL);
/* Wait for the signal */
pause();
/* Reset the old mask */
sigprocmask(SIG_SETMASK,&sigset_MaskOriginal,NULL);
return -1;
}