419 lines
8.9 KiB
C
419 lines
8.9 KiB
C
/* sighandle.c -- Library routines for manipulating chains of signal handlers
|
||
Copyright (C) 1992 Free Software Foundation, Inc.
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 2, or (at your option)
|
||
any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details. */
|
||
|
||
/* Written by Paul Sander, HaL Computer Systems, Inc. <paul@hal.com>
|
||
Brian Berliner <berliner@Sun.COM> added POSIX support */
|
||
|
||
/*************************************************************************
|
||
*
|
||
* signal.c -- This file contains code that manipulates chains of signal
|
||
* handlers.
|
||
*
|
||
* Facilities are provided to register a signal handler for
|
||
* any specific signal. When a signal is received, all of the
|
||
* registered signal handlers are invoked in the reverse order
|
||
* in which they are registered. Note that the signal handlers
|
||
* must not themselves make calls to the signal handling
|
||
* facilities.
|
||
*
|
||
*************************************************************************/
|
||
|
||
#ifdef HAVE_CONFIG_H
|
||
#include "config.h"
|
||
#endif
|
||
#include "system.h"
|
||
|
||
#include <sys/types.h>
|
||
#include <stdio.h>
|
||
#include <signal.h>
|
||
|
||
/* Add prototype support. */
|
||
#ifndef PROTO
|
||
#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
|
||
#define PROTO(ARGS) ARGS
|
||
#else
|
||
#define PROTO(ARGS) ()
|
||
#endif
|
||
#endif
|
||
|
||
#ifdef STDC_HEADERS
|
||
#include <stdlib.h>
|
||
#else
|
||
#if __STDC__
|
||
char *calloc(unsigned nelem, unsigned size);
|
||
char *malloc(unsigned size);
|
||
#else
|
||
char *calloc();
|
||
char *malloc();
|
||
#endif /* __STDC__ */
|
||
#endif /* STDC_HEADERS */
|
||
|
||
/* Define the highest signal number (usually) */
|
||
#ifndef SIGMAX
|
||
#define SIGMAX 64
|
||
#endif
|
||
|
||
/* Define linked list of signal handlers structure */
|
||
struct SIG_hlist {
|
||
RETSIGTYPE (*handler)();
|
||
struct SIG_hlist *next;
|
||
};
|
||
|
||
/*
|
||
* Define array of lists of signal handlers. Note that this depends on
|
||
* the implementation to initialize each element to a null pointer.
|
||
*/
|
||
|
||
static struct SIG_hlist **SIG_handlers;
|
||
|
||
/* Define array of default signal vectors */
|
||
|
||
#ifdef POSIX_SIGNALS
|
||
static struct sigaction *SIG_defaults;
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
static struct sigvec *SIG_defaults;
|
||
#else
|
||
static RETSIGTYPE (**SIG_defaults) PROTO ((int));
|
||
#endif
|
||
#endif
|
||
|
||
/* Critical section housekeeping */
|
||
static int SIG_crSectNest = 0; /* Nesting level */
|
||
#ifdef POSIX_SIGNALS
|
||
static sigset_t SIG_crSectMask; /* Signal mask */
|
||
#else
|
||
static int SIG_crSectMask; /* Signal mask */
|
||
#endif
|
||
|
||
/*
|
||
* Initialize the signal handler arrays
|
||
*/
|
||
|
||
static int SIG_init()
|
||
{
|
||
int i;
|
||
#ifdef POSIX_SIGNALS
|
||
sigset_t sigset_test;
|
||
#endif
|
||
|
||
if (SIG_defaults && SIG_handlers) /* already allocated */
|
||
return (0);
|
||
|
||
#ifdef POSIX_SIGNALS
|
||
(void) sigfillset(&sigset_test);
|
||
for (i = 1; i < SIGMAX && sigismember(&sigset_test, i) == 1; i++)
|
||
;
|
||
if (i < SIGMAX)
|
||
i = SIGMAX;
|
||
i++;
|
||
if (!SIG_defaults)
|
||
SIG_defaults = (struct sigaction *)
|
||
calloc(i, sizeof(struct sigaction));
|
||
(void) sigemptyset(&SIG_crSectMask);
|
||
#else
|
||
i = SIGMAX+1;
|
||
#ifdef BSD_SIGNALS
|
||
if (!SIG_defaults)
|
||
SIG_defaults = (struct sigvec *)
|
||
calloc(i, sizeof(struct sigvec));
|
||
#else
|
||
if (!SIG_defaults)
|
||
SIG_defaults = (RETSIGTYPE (**) PROTO ((int)) )
|
||
calloc(i, sizeof(RETSIGTYPE (**) PROTO ((int)) ));
|
||
#endif
|
||
SIG_crSectMask = 0;
|
||
#endif
|
||
if (!SIG_handlers)
|
||
SIG_handlers = (struct SIG_hlist **)
|
||
calloc(i, sizeof(struct SIG_hlist *));
|
||
return (!SIG_defaults || !SIG_handlers);
|
||
}
|
||
|
||
/*
|
||
* The following invokes each signal handler in the reverse order in which
|
||
* they were registered.
|
||
*/
|
||
static RETSIGTYPE SIG_handle PROTO ((int));
|
||
|
||
static RETSIGTYPE SIG_handle(sig)
|
||
int sig;
|
||
{
|
||
struct SIG_hlist *this;
|
||
|
||
/* Dispatch signal handlers */
|
||
this = SIG_handlers[sig];
|
||
while (this != (struct SIG_hlist *) NULL)
|
||
{
|
||
(*this->handler)(sig);
|
||
this = this->next;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* The following registers a signal handler. If the handler is already
|
||
* registered, it is not registered twice, nor is the order in which signal
|
||
* handlers are invoked changed. If this is the first signal handler
|
||
* registered for a given signal, the old sigvec structure is saved for
|
||
* restoration later.
|
||
*/
|
||
|
||
int SIG_register(sig,fn)
|
||
int sig;
|
||
RETSIGTYPE (*fn)();
|
||
{
|
||
int val;
|
||
struct SIG_hlist *this;
|
||
#ifdef POSIX_SIGNALS
|
||
struct sigaction act;
|
||
sigset_t sigset_mask, sigset_omask;
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
struct sigvec vec;
|
||
int mask;
|
||
#endif
|
||
#endif
|
||
|
||
/* Initialize */
|
||
if (SIG_init() != 0)
|
||
return (-1);
|
||
val = 0;
|
||
|
||
/* Block this signal while we look at handler chain */
|
||
#ifdef POSIX_SIGNALS
|
||
(void) sigemptyset(&sigset_mask);
|
||
(void) sigaddset(&sigset_mask, sig);
|
||
(void) sigprocmask(SIG_BLOCK, &sigset_mask, &sigset_omask);
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
mask = sigblock(sigmask(sig));
|
||
#endif
|
||
#endif
|
||
|
||
/* See if this handler was already registered */
|
||
this = SIG_handlers[sig];
|
||
while (this != (struct SIG_hlist *) NULL)
|
||
{
|
||
if (this->handler == fn) break;
|
||
this = this->next;
|
||
}
|
||
|
||
/* Register the new handler only if it is not already registered. */
|
||
if (this == (struct SIG_hlist *) NULL)
|
||
{
|
||
|
||
/*
|
||
* If this is the first handler registered for this signal,
|
||
* set up the signal handler dispatcher
|
||
*/
|
||
|
||
if (SIG_handlers[sig] == (struct SIG_hlist *) NULL)
|
||
{
|
||
#ifdef POSIX_SIGNALS
|
||
act.sa_handler = SIG_handle;
|
||
(void) sigemptyset(&act.sa_mask);
|
||
act.sa_flags = 0;
|
||
val = sigaction(sig, &act, &SIG_defaults[sig]);
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
memset (&vec, 0, sizeof (vec));
|
||
vec.sv_handler = SIG_handle;
|
||
val = sigvec(sig, &vec, &SIG_defaults[sig]);
|
||
#else
|
||
if ((SIG_defaults[sig] = signal(sig, SIG_handle)) == SIG_ERR)
|
||
val = -1;
|
||
#endif
|
||
#endif
|
||
}
|
||
|
||
/* If not, register it */
|
||
if ((val == 0) && (this == (struct SIG_hlist *) NULL))
|
||
{
|
||
this = (struct SIG_hlist *)
|
||
malloc(sizeof(struct SIG_hlist));
|
||
if (this == NULL)
|
||
{
|
||
val = -1;
|
||
}
|
||
else
|
||
{
|
||
this->handler = fn;
|
||
this->next = SIG_handlers[sig];
|
||
SIG_handlers[sig] = this;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Unblock the signal */
|
||
#ifdef POSIX_SIGNALS
|
||
(void) sigprocmask(SIG_SETMASK, &sigset_omask, NULL);
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
(void) sigsetmask(mask);
|
||
#endif
|
||
#endif
|
||
|
||
return val;
|
||
}
|
||
|
||
/*
|
||
* The following deregisters a signal handler. If the last signal handler for
|
||
* a given signal is deregistered, the default sigvec information is restored.
|
||
*/
|
||
|
||
int SIG_deregister(sig,fn)
|
||
int sig;
|
||
RETSIGTYPE (*fn)();
|
||
{
|
||
int val;
|
||
struct SIG_hlist *this;
|
||
struct SIG_hlist *last;
|
||
#ifdef POSIX_SIGNALS
|
||
sigset_t sigset_mask, sigset_omask;
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
int mask;
|
||
#endif
|
||
#endif
|
||
|
||
/* Initialize */
|
||
if (SIG_init() != 0)
|
||
return (-1);
|
||
val = 0;
|
||
last = (struct SIG_hlist *) NULL;
|
||
|
||
/* Block this signal while we look at handler chain */
|
||
#ifdef POSIX_SIGNALS
|
||
(void) sigemptyset(&sigset_mask);
|
||
(void) sigaddset(&sigset_mask, sig);
|
||
(void) sigprocmask(SIG_BLOCK, &sigset_mask, &sigset_omask);
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
mask = sigblock(sigmask(sig));
|
||
#endif
|
||
#endif
|
||
|
||
/* Search for the signal handler */
|
||
this = SIG_handlers[sig];
|
||
while ((this != (struct SIG_hlist *) NULL) && (this->handler != fn))
|
||
{
|
||
last = this;
|
||
this = this->next;
|
||
}
|
||
|
||
/* If it was registered, remove it */
|
||
if (this != (struct SIG_hlist *) NULL)
|
||
{
|
||
if (last == (struct SIG_hlist *) NULL)
|
||
{
|
||
SIG_handlers[sig] = this->next;
|
||
}
|
||
else
|
||
{
|
||
last->next = this->next;
|
||
}
|
||
free((char *) this);
|
||
}
|
||
|
||
/* Restore default behavior if there are no registered handlers */
|
||
if (SIG_handlers[sig] == (struct SIG_hlist *) NULL)
|
||
{
|
||
#ifdef POSIX_SIGNALS
|
||
val = sigaction(sig, &SIG_defaults[sig],
|
||
(struct sigaction *) NULL);
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
val = sigvec(sig, &SIG_defaults[sig], (struct sigvec *) NULL);
|
||
#else
|
||
if (signal(sig, SIG_defaults[sig]) == SIG_ERR)
|
||
val = -1;
|
||
#endif
|
||
#endif
|
||
}
|
||
|
||
/* Unblock the signal */
|
||
#ifdef POSIX_SIGNALS
|
||
(void) sigprocmask(SIG_SETMASK, &sigset_omask, NULL);
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
(void) sigsetmask(mask);
|
||
#endif
|
||
#endif
|
||
|
||
return val;
|
||
}
|
||
|
||
/*
|
||
* The following begins a critical section.
|
||
*/
|
||
|
||
void SIG_beginCrSect()
|
||
{
|
||
if (SIG_init() == 0)
|
||
{
|
||
if (SIG_crSectNest == 0)
|
||
{
|
||
#ifdef POSIX_SIGNALS
|
||
sigset_t sigset_mask;
|
||
|
||
(void) sigfillset(&sigset_mask);
|
||
(void) sigprocmask(SIG_SETMASK,
|
||
&sigset_mask, &SIG_crSectMask);
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
SIG_crSectMask = sigblock(~0);
|
||
#else
|
||
/* TBD */
|
||
#endif
|
||
#endif
|
||
}
|
||
SIG_crSectNest++;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Return nonzero if currently in a critical section.
|
||
* Otherwise return zero.
|
||
*/
|
||
|
||
int SIG_inCrSect()
|
||
{
|
||
return SIG_crSectNest > 0;
|
||
}
|
||
|
||
/*
|
||
* The following ends a critical section.
|
||
*/
|
||
|
||
void SIG_endCrSect()
|
||
{
|
||
if (SIG_init() == 0)
|
||
{
|
||
SIG_crSectNest--;
|
||
if (SIG_crSectNest == 0)
|
||
{
|
||
#ifdef POSIX_SIGNALS
|
||
(void) sigprocmask(SIG_SETMASK, &SIG_crSectMask, NULL);
|
||
#else
|
||
#ifdef BSD_SIGNALS
|
||
(void) sigsetmask(SIG_crSectMask);
|
||
#else
|
||
/* TBD */
|
||
#endif
|
||
#endif
|
||
}
|
||
}
|
||
}
|