/*** *winsig.c - C signal support * * Copyright (c) 1991-1997, Microsoft Corporation. All rights reserved * *Purpose: * Defines signal(), raise() and supporting functions. * *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include /* * look up the first entry in the exception-action table corresponding to * the given signal */ #ifdef _MT static struct _XCPT_ACTION * __cdecl siglookup(int, struct _XCPT_ACTION *); #else /* _MT */ static struct _XCPT_ACTION * __cdecl siglookup(int); #endif /* _MT */ /* * variables holding action codes (and code pointers) for SIGINT, SIGBRK, * SIGABRT and SIGTERM. * * note that the disposition (i.e., action to be taken upon receipt) of * these signals is defined on a per-process basis (not per-thread)!! */ static _PHNDLR ctrlc_action = SIG_DFL; /* SIGINT */ static _PHNDLR ctrlbreak_action = SIG_DFL; /* SIGBREAK */ static _PHNDLR abort_action = SIG_DFL; /* SIGABRT */ static _PHNDLR term_action = SIG_DFL; /* SIGTERM */ /* * flag indicated whether or not a handler has been installed to capture * ^C and ^Break events. */ static int ConsoleCtrlHandler_Installed = 0; /*** *static BOOL WINAPI ctrlevent_capture(DWORD CtrlType) - capture ^C and ^Break events * *Purpose: * Capture ^C and ^Break events from the console and dispose of them * according the values in ctrlc_action and ctrlbreak_action, resp. * This is the routine that evokes the user-defined action for SIGINT * (^C) or SIGBREAK (^Break) installed by a call to signal(). * *Entry: * DWORD CtrlType - indicates type of event, two values: * CTRL_C_EVENT * CTRL_BREAK_EVENT * *Exit: * Returns TRUE to indicate the event (signal) has been handled. * Otherwise, returns FALSE. * *Exceptions: * *******************************************************************************/ static BOOL WINAPI ctrlevent_capture ( DWORD CtrlType ) { _PHNDLR ctrl_action; _PHNDLR *pctrl_action; int sigcode; _mlock(_SIGNAL_LOCK); /* * Identify the type of event and fetch the corresponding action * description. */ if ( CtrlType == CTRL_C_EVENT ) { ctrl_action = *(pctrl_action = &ctrlc_action); sigcode = SIGINT; } else { ctrl_action = *(pctrl_action = &ctrlbreak_action); sigcode = SIGBREAK; } if ( ctrl_action == SIG_DFL ) { /* * return FALSE, indicating the event has NOT been handled */ _munlock(_SIGNAL_LOCK); return FALSE; } if ( ctrl_action != SIG_IGN ) { /* * Reset the action to be SIG_DFL and call the user's handler. */ *pctrl_action = SIG_DFL; _munlock(_SIGNAL_LOCK); (*ctrl_action)(sigcode); } else /* * SIG_IGN - nothing special to do except release the lock */ _munlock(_SIGNAL_LOCK); /* * Return TRUE, indicating the event has been handled (which may * mean it's being ignored) */ return TRUE; } /*** *_PHNDLR signal(signum, sigact) - Define a signal handler * *Purpose: * The signal routine allows the user to define what action should * be taken when various signals occur. The Win32/Dosx32 implementation * supports seven signals, divided up into three general groups * * 1. Signals corresponding to OS exceptions. These are: * SIGFPE * SIGILL * SIGSEGV * Signal actions for these signals are installed by altering the * XcptAction and SigAction fields for the appropriate entry in the * exception-action table (XcptActTab[]). * * 2. Signals corresponding to ^C and ^Break. These are: * SIGINT * SIGBREAK * Signal actions for these signals are installed by altering the * _ctrlc_action and _ctrlbreak_action variables. * * 3. Signals which are implemented only in the runtime. That is, they * occur only as the result of a call to raise(). * SIGABRT * SIGTERM * * *Entry: * int signum signal type. recognized signal types are: * * SIGABRT (ANSI) * SIGBREAK * SIGFPE (ANSI) * SIGILL (ANSI) * SIGINT (ANSI) * SIGSEGV (ANSI) * SIGTERM (ANSI) * * _PHNDLR sigact signal handling function or action code. the action * codes are: * * SIG_DFL - take the default action, whatever that may * be, upon receipt of this type type of signal. * * SIG_DIE - *** ILLEGAL *** * special code used in the XcptAction field of an * XcptActTab[] entry to indicate that the runtime is * to terminate the process upon receipt of the exception. * not accepted as a value for sigact. * * SIG_IGN - ignore this type of signal * * [function address] - transfer control to this address * when a signal of this type occurs. * *Exit: * Good return: * Signal returns the previous value of the signal handling function * (e.g., SIG_DFL, SIG_IGN, etc., or [function address]). This value is * returned in DX:AX. * * Error return: * Signal returns -1 and errno is set to EINVAL. The error return is * generally taken if the user submits bogus input values. * *Exceptions: * None. * *******************************************************************************/ _PHNDLR __cdecl signal( int signum, _PHNDLR sigact ) { struct _XCPT_ACTION *pxcptact; _PHNDLR oldsigact; #ifdef _MT _ptiddata ptd; #endif /* _MT */ /* * Check for values of sigact supported on other platforms but not * on this one. Also, make sure sigact is not SIG_DIE */ if ( (sigact == SIG_ACK) || (sigact == SIG_SGE) ) goto sigreterror; /* * Take care of all signals which do not correspond to exceptions * in the host OS. Those are: * * SIGINT * SIGBREAK * SIGABRT * SIGTERM * */ if ( (signum == SIGINT) || (signum == SIGBREAK) || (signum == SIGABRT) || (signum == SIGTERM) ) { _mlock(_SIGNAL_LOCK); /* * if SIGINT or SIGBREAK, make sure the handler is installed * to capture ^C and ^Break events. */ if ( ((signum == SIGINT) || (signum == SIGBREAK)) && !ConsoleCtrlHandler_Installed ) if ( SetConsoleCtrlHandler(ctrlevent_capture, TRUE) == TRUE ) ConsoleCtrlHandler_Installed = TRUE; else { _doserrno = GetLastError(); _munlock(_SIGNAL_LOCK); goto sigreterror; } switch (signum) { case SIGINT: oldsigact = ctrlc_action; ctrlc_action = sigact; break; case SIGBREAK: oldsigact = ctrlbreak_action; ctrlbreak_action = sigact; break; case SIGABRT: oldsigact = abort_action; abort_action = sigact; break; case SIGTERM: oldsigact = term_action; term_action = sigact; break; } _munlock(_SIGNAL_LOCK); goto sigretok; } /* * If we reach here, signum is supposed to be one the signals which * correspond to exceptions in the host OS. Those are: * * SIGFPE * SIGILL * SIGSEGV */ /* * Make sure signum is one of the remaining supported signals. */ if ( (signum != SIGFPE) && (signum != SIGILL) && (signum != SIGSEGV) ) goto sigreterror; #ifdef _MT /* * Fetch the tid data table entry for this thread */ ptd = _getptd(); /* * Check that there a per-thread instance of the exception-action * table for this thread. if there isn't, create one. */ if ( ptd->_pxcptacttab == _XcptActTab ) /* * allocate space for an exception-action table */ if ( (ptd->_pxcptacttab = _malloc_crt(_XcptActTabSize)) != NULL ) /* * initialize the table by copying over the contents * of _XcptActTab[] */ (void) memcpy(ptd->_pxcptacttab, _XcptActTab, _XcptActTabSize); else /* * cannot create exception-action table, return * error to caller */ goto sigreterror; #endif /* _MT */ /* * look up the proper entry in the exception-action table. note that * if several exceptions are mapped to the same signal, this returns * the pointer to first such entry in the exception action table. it * is assumed that the other entries immediately follow this one. */ #ifdef _MT if ( (pxcptact = siglookup(signum, ptd->_pxcptacttab)) == NULL ) #else /* _MT */ if ( (pxcptact = siglookup(signum)) == NULL ) #endif /* _MT */ goto sigreterror; /* * SIGSEGV, SIGILL and SIGFPE all have more than one exception mapped * to them. the code below depends on the exceptions corresponding to * the same signal being grouped together in the exception-action * table. */ /* * store old signal action code for return value */ oldsigact = pxcptact->XcptAction; /* * loop through all entries corresponding to the * given signal and update the SigAction and XcptAction * fields as appropriate */ while ( pxcptact->SigNum == signum ) { /* * take care of the SIG_IGN and SIG_DFL action * codes */ pxcptact->XcptAction = sigact; /* * make sure we don't run off the end of the table */ #ifdef _MT if ( ++pxcptact >= ((struct _XCPT_ACTION *)(ptd->_pxcptacttab) + _XcptActTabCount) ) #else /* _MT */ if ( ++pxcptact >= (_XcptActTab + _XcptActTabCount) ) #endif /* _MT */ break; } sigretok: return(oldsigact); sigreterror: errno = EINVAL; return(SIG_ERR); } /*** *int raise(signum) - Raise a signal * *Purpose: * This routine raises a signal (i.e., performs the action currently * defined for this signal). The action associated with the signal is * evoked directly without going through intermediate dispatching or * handling. * *Entry: * int signum - signal type (e.g., SIGINT) * *Exit: * returns 0 on good return, -1 on bad return. * *Exceptions: * May not return. Raise has no control over the action * routines defined for the various signals. Those routines may * abort, terminate, etc. In particular, the default actions for * certain signals will terminate the program. * *******************************************************************************/ int __cdecl raise ( int signum ) { _PHNDLR sigact; _PHNDLR *psigact; PEXCEPTION_POINTERS oldpxcptinfoptrs; int oldfpecode; int indx; #ifdef _MT int siglock = 0; _ptiddata ptd; #endif /* _MT */ switch (signum) { case SIGINT: sigact = *(psigact = &ctrlc_action); #ifdef _MT siglock++; #endif /* _MT */ break; case SIGBREAK: sigact = *(psigact = &ctrlbreak_action); #ifdef _MT siglock++; #endif /* _MT */ break; case SIGABRT: sigact = *(psigact = &abort_action); #ifdef _MT siglock++; #endif /* _MT */ break; case SIGTERM: sigact = *(psigact = &term_action); #ifdef _MT siglock++; #endif /* _MT */ break; case SIGFPE: case SIGILL: case SIGSEGV: #ifdef _MT ptd = _getptd(); sigact = *(psigact = &(siglookup( signum, ptd->_pxcptacttab )->XcptAction)); #else /* _MT */ sigact = *(psigact = &(siglookup( signum )-> XcptAction)); #endif /* _MT */ break; default: /* * unsupported signal, return an error */ return (-1); } #ifdef _MT /* * if signum is one of the 'process-wide' signals (i.e., SIGINT, * SIGBREAK, SIGABRT or SIGTERM), assert _SIGNAL_LOCK. */ if ( siglock ) _mlock(_SIGNAL_LOCK); #endif /* _MT */ /* * If the current action is SIG_IGN, just return */ if ( sigact == SIG_IGN ) { #ifdef _MT if ( siglock ) _munlock(_SIGNAL_LOCK); #endif /* _MT */ return(0); } /* * If the current action is SIG_DFL, take the default action */ if ( sigact == SIG_DFL ) { #ifdef _MT if ( siglock ) _munlock(_SIGNAL_LOCK); #endif /* _MT */ /* * The current default action for all of the supported * signals is to terminate with an exit code of 3. * */ _exit(3); } /* * From here on, sigact is assumed to be a pointer to a user-supplied * handler. */ /* * For signals which correspond to exceptions, set the pointer * to the EXCEPTION_POINTERS structure to NULL */ if ( (signum == SIGFPE) || (signum == SIGSEGV) || (signum == SIGILL) ) { #ifdef _MT oldpxcptinfoptrs = ptd->_tpxcptinfoptrs; ptd->_tpxcptinfoptrs = NULL; #else /* _MT */ oldpxcptinfoptrs = _pxcptinfoptrs; _pxcptinfoptrs = NULL; #endif /* _MT */ /* * If signum is SIGFPE, also set _fpecode to * _FPE_EXPLICITGEN */ if ( signum == SIGFPE ) { #ifdef _MT oldfpecode = ptd->_tfpecode; ptd->_tfpecode = _FPE_EXPLICITGEN; #else /* _MT */ oldfpecode = _fpecode; _fpecode = _FPE_EXPLICITGEN; #endif /* _MT */ } } /* * Reset the action to SIG_DFL and call the user specified handler * routine. */ if ( signum == SIGFPE ) /* * for SIGFPE, must reset the action for all of the floating * point exceptions */ for ( indx = _First_FPE_Indx ; indx < _First_FPE_Indx + _Num_FPE ; indx++ ) { #ifdef _MT ( (struct _XCPT_ACTION *)(ptd->_pxcptacttab) + indx )->XcptAction = SIG_DFL; #else /* _MT */ _XcptActTab[indx].XcptAction = SIG_DFL; #endif /* _MT */ } else *psigact = SIG_DFL; #ifdef _MT if ( siglock ) _munlock(_SIGNAL_LOCK); #endif /* _MT */ if ( signum == SIGFPE ) /* * Special code to support old SIGFPE handlers which * expect the value of _fpecode as the second argument. */ #ifdef _MT (*(void (__cdecl *)(int,int))sigact)(SIGFPE, ptd->_tfpecode); #else /* _MT */ (*(void (__cdecl *)(int,int))sigact)(SIGFPE, _fpecode); #endif /* _MT */ else (*sigact)(signum); /* * For signals which correspond to exceptions, restore the pointer * to the EXCEPTION_POINTERS structure. */ if ( (signum == SIGFPE) || (signum == SIGSEGV) || (signum == SIGILL) ) { #ifdef _MT ptd->_tpxcptinfoptrs = oldpxcptinfoptrs; #else /* _MT */ _pxcptinfoptrs = oldpxcptinfoptrs; #endif /* _MT */ /* * If signum is SIGFPE, also restore _fpecode */ if ( signum == SIGFPE ) #ifdef _MT ptd->_tfpecode = oldfpecode; #else /* _MT */ _fpecode = oldfpecode; #endif /* _MT */ } return(0); } /*** *struct _XCPT_ACTION *siglookup(int signum) - look up exception-action table * entry for signal. * *Purpose: * Find the first entry int _XcptActTab[] whose SigNum field is signum. * *Entry: * int signum - C signal type (e.g., SIGINT) * *Exit: * If successful, pointer to the table entry. If no such entry, NULL is * returned. * *Exceptions: * *******************************************************************************/ #ifdef _MT static struct _XCPT_ACTION * __cdecl siglookup ( int signum, struct _XCPT_ACTION *pxcptacttab ) { struct _XCPT_ACTION *pxcptact = pxcptacttab; #else /* _MT */ static struct _XCPT_ACTION * __cdecl siglookup(int signum) { struct _XCPT_ACTION *pxcptact = _XcptActTab; #endif /* _MT */ /* * walk thru the _xcptactab table looking for the proper entry. note * that in the case where more than one exception corresponds to the * same signal, the first such instance in the table is the one * returned. */ #ifdef _MT while ( (pxcptact->SigNum != signum) && (++pxcptact < pxcptacttab + _XcptActTabCount) ) ; #else /* _MT */ while ( (pxcptact->SigNum != signum) && (++pxcptact < _XcptActTab + _XcptActTabCount) ) ; #endif /* _MT */ #ifdef _MT if ( (pxcptact < (pxcptacttab + _XcptActTabCount)) && #else /* _MT */ if ( (pxcptact < (_XcptActTab + _XcptActTabCount)) && #endif /* _MT */ (pxcptact->SigNum == signum) ) /* * found a table entry corresponding to the signal */ return(pxcptact); else /* * found no table entry corresponding to the signal */ return(NULL); } #ifdef _MT /*** *int *__fpecode(void) - return pointer to _fpecode field of the tidtable entry * for the current thread * *Purpose: * *Entry: * *Exit: * *Exceptions: * *******************************************************************************/ int * __cdecl __fpecode ( void ) { return( &(_getptd()->_tfpecode) ); } /*** *void **__pxcptinfoptrs(void) - return pointer to _pxcptinfoptrs field of the * tidtable entry for the current thread * *Purpose: * *Entry: * *Exit: * *Exceptions: * *******************************************************************************/ void ** __cdecl __pxcptinfoptrs ( void ) { return( &(_getptd()->_tpxcptinfoptrs) ); } #endif /* _MT */