864 lines
24 KiB
C
864 lines
24 KiB
C
#ifndef lint
|
|
#ifdef sccs
|
|
static char sccsid[] = "@(#)ndet_loop.c 1.1 94/10/31 Copyr 1985 Sun Micro";
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
* Copyright (c) 1985 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* Ndet_loop.c - Notification loop.
|
|
*/
|
|
|
|
#include <sunwindow/ntfy.h>
|
|
#include <sunwindow/ndet.h>
|
|
#include <sunwindow/nint.h>
|
|
#include <sunwindow/ndis.h> /* For ndis_dispatch */
|
|
#include <syscall.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdio.h> /* For temp debugging */
|
|
|
|
extern int errno;
|
|
|
|
pkg_private_data u_int ndet_flags = 0;
|
|
pkg_private_data NTFY_CLIENT *ndet_clients = 0;
|
|
pkg_private_data NTFY_CLIENT *ndet_client_latest = 0;
|
|
pkg_private_data u_int ndet_sigs_received = 0;
|
|
pkg_private_data u_int ndet_sigs_managing = 0;
|
|
pkg_private_data fd_set ndet_fasync_mask = 0; /* Initialized to 0 by the compiler*/
|
|
pkg_private_data fd_set ndet_fndelay_mask = 0; /* Maintained, but really used? */
|
|
|
|
pkg_private_data struct timeval ndet_polling_tv = {0,0};
|
|
|
|
pkg_private_data fd_set ndet_ibits = 0, ndet_obits = 0, ndet_ebits = 0; /* = 0; */
|
|
static struct timeval ndet_signal_check; /* Minimum select timeout */
|
|
|
|
pkg_private_data struct sigvec ndet_prev_sigvec[NSIG] = 0;
|
|
|
|
pkg_private void ndet_signal_catcher();
|
|
pkg_private_data struct sigvec ndet_sigvec = {ndet_signal_catcher, 0, 0};
|
|
static int ndet_signal_code;
|
|
static struct sigcontext *ndet_signal_context;
|
|
|
|
static void ndet_update_itimer();
|
|
static void ndet_send_async_sigs();
|
|
static void ndet_fig_fd_change(), ndet_fig_wait3_change(),
|
|
ndet_fig_destroy_change(), ndet_fig_sig_change();
|
|
static NTFY_ENUM ndet_sig_send(), ndet_poll_send();
|
|
|
|
static NTFY_ENUM ndet_itimer_change();
|
|
static NTFY_ENUM ndet_destroy_change();
|
|
static NTFY_ENUM ndet_sig_change();
|
|
static NTFY_ENUM ndet_fd_change();
|
|
static NTFY_ENUM ndet_wait3_change();
|
|
static NTFY_ENUM ndet_virtual_set_tv_update();
|
|
static NTFY_ENUM ndet_async_sig_send();
|
|
|
|
#ifdef lint
|
|
/* VARARGS */
|
|
/* ARGSUSED */
|
|
int syscall(a) { return(0);} /* When syscall put in llib-lc then remove */
|
|
#endif lint
|
|
|
|
extern Notify_error
|
|
notify_start()
|
|
{
|
|
register struct timeval *timer;
|
|
fd_set ibits, obits, ebits;
|
|
register int nfds;
|
|
NDET_ENUM_SEND enum_send;
|
|
int errno_remember = errno;
|
|
Notify_error return_code;
|
|
|
|
FD_ZERO(&ibits); FD_ZERO(&obits); FD_ZERO(&ebits);
|
|
/* Notify_start is not reentrant */
|
|
if (ndet_flags & NDET_STARTED) {
|
|
ntfy_set_errno(NOTIFY_INVAL);
|
|
return(notify_errno);
|
|
}
|
|
/* Always go around the loop at least once */
|
|
ndet_flags |= NDET_STARTED;
|
|
do {
|
|
NTFY_BEGIN_CRITICAL;
|
|
/* Ndet_update_*_itimer (below) may set up NDET_ITIMER_ENQ*/
|
|
ndet_flags &= ~NDET_ITIMER_ENQ;
|
|
/* If nothing has changed then do no set up */
|
|
if (ndet_flags & NDET_CONDITION_CHANGE) {
|
|
if (ndet_flags & NDET_REAL_CHANGE)
|
|
ndet_update_real_itimer();
|
|
if (ndet_flags & NDET_VIRTUAL_CHANGE)
|
|
ndet_update_virtual_itimer();
|
|
if (ndet_flags & NDET_FD_CHANGE)
|
|
ndet_fig_fd_change();
|
|
if (ndet_flags & NDET_WAIT3_CHANGE)
|
|
ndet_fig_wait3_change();
|
|
if (ndet_flags & NDET_DESTROY_CHANGE)
|
|
ndet_fig_destroy_change();
|
|
/*
|
|
* Always handle signal changes last because other
|
|
* ndet_*_change and ndet_update_*_itimer calls may
|
|
* have set NDET_SIGNAL_CHANGE in ndet_flags.
|
|
*/
|
|
if (ndet_flags & NDET_SIGNAL_CHANGE)
|
|
ndet_fig_sig_change();
|
|
}
|
|
/*
|
|
* Set up select parameters. Wouldn't get changes from
|
|
* signal processing this time around. Poll if stuff in
|
|
* the dispatch queue.
|
|
*/
|
|
timer = (
|
|
ndet_flags & (NDET_POLL | NDET_ITIMER_ENQ | NDET_NO_DELAY) ||
|
|
ndis_clients != NTFY_CLIENT_NULL)?
|
|
&ndet_polling_tv: NTFY_TIMEVAL_NULL;
|
|
ibits = ndet_ibits;
|
|
obits = ndet_obits;
|
|
ebits = ndet_ebits;
|
|
NTFY_END_CRITICAL;
|
|
/*
|
|
* From the tests of ndet_sigs_received until get into
|
|
* pause or select below is a race condition.
|
|
* It is possible to get a signal during this time that wouldn't
|
|
* get serviced until something breaks us out of the block.
|
|
* Can't get away from this. UNIX needs a version of select
|
|
* and pause that will simultaneously release blocked signals.
|
|
* We test ndet_sigs_received as late as possible in order
|
|
* to reduce this window of vulnerability.
|
|
*/
|
|
if (!ntfy_fd_anyset(&ibits) && !ntfy_fd_anyset(&obits) &&
|
|
!ntfy_fd_anyset(&ebits) && timer == NTFY_TIMEVAL_NULL &&
|
|
!ndet_sigs_received) {
|
|
if (ndet_sigs_managing != 0) {
|
|
/* Wait for interrupt */
|
|
pause();
|
|
} else {
|
|
/* Not detecting ANY conditions */
|
|
return_code = NOTIFY_NO_CONDITION;
|
|
goto Finish;
|
|
}
|
|
} else {
|
|
/*
|
|
* Hack to avoid race condition described below.
|
|
* notify_signal_check must be called explicitly
|
|
* to enable this mechanism.
|
|
*/
|
|
if ((timer == NTFY_TIMEVAL_NULL) &&
|
|
(timerisset(&ndet_signal_check)))
|
|
timer = &ndet_signal_check;
|
|
/*
|
|
* Wait for select to return.
|
|
*
|
|
* Do this ndet_sigs_received test at the last possible
|
|
* nano in order to reduce the unavoidable race
|
|
* condition between detecting signal arrival and
|
|
* making the call to select (which will return with an
|
|
* EINTR when a signal arrives while IN select,
|
|
* not ON THE WAY into select).
|
|
*/
|
|
nfds = syscall(SYS_select,
|
|
FD_SETSIZE, &ibits, &obits, &ebits,
|
|
(ndet_sigs_received)? &ndet_polling_tv: timer);
|
|
errno_remember = errno;
|
|
/* See if select returned unconventionally */
|
|
if (nfds == -1) {
|
|
/* Clear *bits when in an undefined situation */
|
|
FD_ZERO(&ibits); FD_ZERO(&obits);
|
|
FD_ZERO(&ebits);
|
|
switch (errno) {
|
|
case EINTR:
|
|
/* Signals received */
|
|
if (ndet_flags & NDET_STOP_ON_SIG)
|
|
(void) notify_stop();
|
|
break; /* Out of switch */
|
|
case EBADF:
|
|
ntfy_set_errno(NOTIFY_BADF);
|
|
goto Error;
|
|
default:
|
|
ntfy_set_errno(NOTIFY_INVAL);
|
|
goto Error;
|
|
}
|
|
} else {
|
|
/*
|
|
* Terminate notification loop if ask to
|
|
* stop on sig and received signal before
|
|
* entering select.
|
|
*/
|
|
if (ndet_flags & NDET_STOP_ON_SIG &&
|
|
ndet_sigs_received) {
|
|
(void) notify_stop();
|
|
/*
|
|
* Modify errno to indicate that an
|
|
* signal was received during select.
|
|
*/
|
|
errno_remember = errno = EINTR;
|
|
}
|
|
/*
|
|
* (timer && (nfds == 0)) means select timer expired.
|
|
* Since will only go off in polling situation and
|
|
* we send poll notifications every time around loop,
|
|
* fall through.
|
|
*/
|
|
}
|
|
}
|
|
/* Enqueue detected notifications with the dispatcher */
|
|
NTFY_BEGIN_CRITICAL;
|
|
/* Set up enumeration record */
|
|
enum_send.ibits = ibits;
|
|
enum_send.obits = obits;
|
|
enum_send.ebits = ebits;
|
|
enum_send.wait3 = NTFY_WAIT3_DATA_NULL;
|
|
timerclear(&(enum_send.current_tv));
|
|
enum_send.sigs = ndet_sigs_received;
|
|
ndet_sigs_received = 0;
|
|
/* Check for fd related condition */
|
|
/*if (ibits || obits || ebits) */
|
|
if (ntfy_fd_anyset(&ibits) || ntfy_fd_anyset(&obits) ||
|
|
ntfy_fd_anyset(&ebits))
|
|
if (ntfy_enum_conditions(ndet_clients, ndet_fd_send,
|
|
(NTFY_ENUM_DATA)&enum_send) == NTFY_ENUM_TERM)
|
|
goto Protected_Error;
|
|
/* Check for signal related condition */
|
|
if (enum_send.sigs)
|
|
/*
|
|
* Use paranoid enum because when get in to
|
|
* ndet_auto_sig_send will do another enumeration
|
|
* that can axe client/condition but not have the
|
|
* opportunity to return NTFY_ENUM_SKIP to the
|
|
* original enumeration (this one).
|
|
*/
|
|
if(ntfy_paranoid_enum_conditions(ndet_clients,
|
|
ndet_sig_send, (NTFY_ENUM_DATA)&enum_send) ==
|
|
NTFY_ENUM_TERM)
|
|
goto Protected_Error;
|
|
if (ndet_flags & NDET_POLL)
|
|
if (ntfy_enum_conditions(ndet_clients, ndet_poll_send,
|
|
NTFY_ENUM_DATA_NULL) == NTFY_ENUM_TERM)
|
|
goto Protected_Error;
|
|
NTFY_END_CRITICAL;
|
|
/* Dispatch any notification enqueued with the dispatcher */
|
|
if (ndis_dispatch() != NOTIFY_OK)
|
|
goto Error;
|
|
} while (!(ndet_flags & (NDET_STOP | NDET_NO_DELAY)));
|
|
return_code = NOTIFY_OK;
|
|
goto Finish;
|
|
Protected_Error:
|
|
NTFY_END_CRITICAL;
|
|
Error:
|
|
return_code = notify_errno;
|
|
Finish:
|
|
ndet_flags &= ~(NDET_STOP | NDET_STARTED);
|
|
if (ndet_flags & NDET_EXIT_SOON) {
|
|
/* Allow everyone to clean up */
|
|
(void) notify_die(DESTROY_PROCESS_DEATH);
|
|
exit(1);
|
|
}
|
|
if (ndet_flags & NDET_STOP_ON_SIG) {
|
|
#ifdef NTFY_DEBUG_SELECT
|
|
char sb[100];
|
|
|
|
sprintf(sb,
|
|
"errno changed (old=%ld, cur=%ld); reset to old",
|
|
errno_remember, errno);
|
|
ntfy_assert(errno == errno_remember, sb);
|
|
#endif NTFY_DEBUG_SELECT
|
|
errno = errno_remember;
|
|
}
|
|
return(return_code);
|
|
}
|
|
|
|
/*
|
|
* Set flag indicating that should return from notify_start.
|
|
*/
|
|
extern Notify_error
|
|
notify_stop()
|
|
{
|
|
if (ndet_flags & NDET_STARTED) {
|
|
ndet_flags |= NDET_STOP;
|
|
return(NOTIFY_OK);
|
|
} else
|
|
return(NOTIFY_NOT_STARTED);
|
|
}
|
|
|
|
/*
|
|
* Some fd related condition has changed. Reset all ndet_*bits.
|
|
* Enable(disable) notifier auto signal catching of SIGIO and SIGURG.
|
|
*/
|
|
static void
|
|
ndet_fig_fd_change()
|
|
{
|
|
u_int sigs_tmp;
|
|
|
|
ndet_flags &= ~NDET_FD_CHANGE;
|
|
/* Remember bits of notifier auto signal catcher */
|
|
sigs_tmp = ndet_sigs_auto;
|
|
/* Zero out bits */
|
|
FD_ZERO(&ndet_ibits); FD_ZERO(&ndet_obits); FD_ZERO(&ndet_ebits);
|
|
ndet_sigs_auto &= ~(SIG_BIT(SIGIO) | SIG_BIT(SIGURG));
|
|
/* Recompute all bits */
|
|
(void) ntfy_enum_conditions(ndet_clients, ndet_fd_change,
|
|
NTFY_ENUM_DATA_NULL);
|
|
/* Toggle notifier auto signal catching if situation changed */
|
|
ndet_toggle_auto(sigs_tmp, SIGIO);
|
|
ndet_toggle_auto(sigs_tmp, SIGURG);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static NTFY_ENUM
|
|
ndet_fd_change(client, condition, context)
|
|
NTFY_CLIENT *client;
|
|
NTFY_CONDITION *condition;
|
|
NTFY_ENUM_DATA context;
|
|
{
|
|
|
|
switch (condition->type) {
|
|
case NTFY_INPUT:
|
|
if (FD_ISSET(condition->data.fd, &ndet_fasync_mask))
|
|
ndet_sigs_auto |= SIG_BIT(SIGIO);
|
|
else
|
|
/*ndet_ibits |= bit; */
|
|
FD_SET(condition->data.fd, &ndet_ibits);
|
|
break;
|
|
case NTFY_OUTPUT:
|
|
if (FD_ISSET(condition->data.fd, &ndet_fasync_mask))
|
|
ndet_sigs_auto |= SIG_BIT(SIGIO);
|
|
else
|
|
/*ndet_obits |= bit; */
|
|
FD_SET(condition->data.fd, &ndet_obits);
|
|
break;
|
|
case NTFY_EXCEPTION:
|
|
if (FD_ISSET(condition->data.fd, &ndet_fasync_mask))
|
|
ndet_sigs_auto |= SIG_BIT(SIGURG);
|
|
else
|
|
/*ndet_ebits |= bit; */
|
|
FD_SET(condition->data.fd, &ndet_ebits);
|
|
break;
|
|
default: {}
|
|
}
|
|
return(NTFY_ENUM_NEXT);
|
|
}
|
|
|
|
/*
|
|
* Some wait3 condition has changed.
|
|
* Enable(disable) notifier auto signal catching of SIGCHLD.
|
|
*/
|
|
static void
|
|
ndet_fig_wait3_change()
|
|
{
|
|
u_int sigs_tmp;
|
|
|
|
ndet_flags &= ~NDET_WAIT3_CHANGE;
|
|
/* Remember bits of notifier auto signal catcher */
|
|
sigs_tmp = ndet_sigs_auto;
|
|
/* Zero out bits */
|
|
ndet_sigs_auto &= ~SIG_BIT(SIGCHLD);
|
|
/* See if any wait conditions */
|
|
(void) ntfy_enum_conditions(ndet_clients, ndet_wait3_change,
|
|
NTFY_ENUM_DATA_NULL);
|
|
/* Toggle notifier auto signal catching if situation changed */
|
|
ndet_toggle_auto(sigs_tmp, SIGCHLD);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static NTFY_ENUM
|
|
ndet_wait3_change(client, condition, context)
|
|
NTFY_CLIENT *client;
|
|
NTFY_CONDITION *condition;
|
|
NTFY_ENUM_DATA context;
|
|
{
|
|
if (condition->type == NTFY_WAIT3)
|
|
ndet_sigs_auto |= SIG_BIT(SIGCHLD);
|
|
return(NTFY_ENUM_NEXT);
|
|
}
|
|
|
|
/*
|
|
* Some virtual itimer related condition has changed. Update all virtual
|
|
* itimers. Determine minimum wait and set virtual itimer.
|
|
* Enable(disable) notifier auto signal catching of SIGVTALRM.
|
|
*/
|
|
pkg_private void
|
|
ndet_update_virtual_itimer()
|
|
{
|
|
struct timeval ndet_virtual_min();
|
|
struct itimerval current_itimer;
|
|
NDET_ENUM_ITIMER enum_itimer;
|
|
int n;
|
|
|
|
ndet_flags &= ~(NDET_VIRTUAL_CHANGE | NDET_VIRTUAL_POLL);
|
|
/* Initialize virtual itimer update probe */
|
|
enum_itimer.type = NTFY_VIRTUAL_ITIMER;
|
|
enum_itimer.polling_bit = NDET_VIRTUAL_POLL;
|
|
enum_itimer.signal = SIGVTALRM;
|
|
enum_itimer.which = ITIMER_VIRTUAL;
|
|
enum_itimer.min_func = ndet_virtual_min;
|
|
/* Virtual itimers are relative to current state of process itimer */
|
|
n = getitimer(ITIMER_VIRTUAL, ¤t_itimer); /* SYSTEM CALL */
|
|
ntfy_assert(n == 0, "Unexpected error: getitimer");
|
|
enum_itimer.current_tv = current_itimer.it_value;
|
|
/* enum_itimer.min_tv is initialized in ndet_update_itimer. */
|
|
/*
|
|
* Update existing virtual itimer conditions and find what process
|
|
* itimer was set to.
|
|
*/
|
|
ndet_update_itimer(&enum_itimer);
|
|
/* Update set_tv in existing virtual itimers */
|
|
(void) ntfy_enum_conditions(ndet_clients, ndet_virtual_set_tv_update,
|
|
(NTFY_ENUM_DATA)&enum_itimer.min_tv);
|
|
}
|
|
|
|
/*
|
|
* Some real itimer related condition has changed.
|
|
* Determine minimum wait and set real itimer.
|
|
* Enable(disable) notifier auto signal catching of SIGALRM.
|
|
*/
|
|
pkg_private void
|
|
ndet_update_real_itimer()
|
|
{
|
|
struct timeval ndet_real_min();
|
|
NDET_ENUM_ITIMER enum_itimer;
|
|
int n;
|
|
|
|
ndet_flags &= ~(NDET_REAL_CHANGE | NDET_REAL_POLL);
|
|
/* Initialize virtual itimer update probe */
|
|
enum_itimer.type = NTFY_REAL_ITIMER;
|
|
enum_itimer.polling_bit = NDET_REAL_POLL;
|
|
enum_itimer.signal = SIGALRM;
|
|
enum_itimer.which = ITIMER_REAL;
|
|
enum_itimer.min_func = ndet_real_min;
|
|
/* Real itimers are relative to current time of day */
|
|
n = gettimeofday(&enum_itimer.current_tv,
|
|
(struct timezone *)0); /* SYSTEM CALL */
|
|
ntfy_assert(n == 0, "Unexpected error: gettimeofday");
|
|
/* enum_itimer.min_tv is initialized in ndet_update_itimer. */
|
|
/* Determine and set process real itimer */
|
|
ndet_update_itimer(&enum_itimer);
|
|
}
|
|
|
|
static struct timeval NDET_END_OF_TIME = {100000000,0};
|
|
/* For explanation of why 100000000, see ndet_check_tv */
|
|
|
|
static void
|
|
ndet_update_itimer(enum_itimer)
|
|
NDET_ENUM_ITIMER *enum_itimer;
|
|
{
|
|
u_int sigs_tmp;
|
|
struct itimerval process_itimer;
|
|
int n;
|
|
|
|
/* Remember bits of notifier auto signal catcher */
|
|
sigs_tmp = ndet_sigs_auto;
|
|
/* Zero out polling bit */
|
|
ndet_flags &= ~enum_itimer->polling_bit;
|
|
ndet_sigs_auto &= ~SIG_BIT(enum_itimer->signal);
|
|
/* Recompute interval timer */
|
|
enum_itimer->min_tv = NDET_END_OF_TIME;
|
|
(void) ntfy_enum_conditions(ndet_clients, ndet_itimer_change,
|
|
(NTFY_ENUM_DATA)enum_itimer);
|
|
/* Toggle notifier auto signal catching if situation changed */
|
|
ndet_toggle_auto(sigs_tmp, enum_itimer->signal);
|
|
/* Set interval timer */
|
|
timerclear(&process_itimer.it_interval);
|
|
if (ndet_tv_equal(enum_itimer->min_tv, NDET_END_OF_TIME))
|
|
/* No one reset min_tv */
|
|
timerclear(&enum_itimer->min_tv);
|
|
process_itimer.it_value = enum_itimer->min_tv;
|
|
n = setitimer(enum_itimer->which, &process_itimer, /* SYSTEM CALL */
|
|
(struct itimerval *)0);
|
|
ntfy_assert(n == 0, "Unexpected error: setitimer");
|
|
}
|
|
|
|
/*
|
|
* Called to update global polling flags and find global minimum until next
|
|
* itimer expiration.
|
|
*/
|
|
static NTFY_ENUM
|
|
ndet_itimer_change(client, condition, context)
|
|
NTFY_CLIENT *client;
|
|
NTFY_CONDITION *condition;
|
|
NTFY_ENUM_DATA context;
|
|
{
|
|
struct timeval local_min;
|
|
register NDET_ENUM_ITIMER *enum_itimer = (NDET_ENUM_ITIMER *)context;
|
|
register NTFY_ITIMER *n_itimer;
|
|
|
|
switch (condition->type) {
|
|
case NTFY_VIRTUAL_ITIMER:
|
|
case NTFY_REAL_ITIMER:
|
|
n_itimer = condition->data.ntfy_itimer;
|
|
if (condition->type != enum_itimer->type)
|
|
break;
|
|
/* See if polling itimer */
|
|
if (ndet_tv_polling(n_itimer->itimer.it_value))
|
|
ndet_flags |= enum_itimer->polling_bit;
|
|
else {
|
|
/* Figure time to go until expiration for this client */
|
|
local_min = enum_itimer->min_func(n_itimer,
|
|
enum_itimer->current_tv);
|
|
/* See if expired */
|
|
if (!timerisset(&local_min)) {
|
|
/*
|
|
* Dispatch notification, reset itimer value,
|
|
* remove if nothing to wait for (returns !0).
|
|
*/
|
|
ndet_flags |= NDET_ITIMER_ENQ;
|
|
if (ndet_itimer_expired(client, condition))
|
|
/*
|
|
* Know can skip rest of clients
|
|
* conditions because only one itimer
|
|
* of each type is allowed per client.
|
|
*/
|
|
return(NTFY_ENUM_SKIP);
|
|
/* Else update local_min and set time */
|
|
local_min = n_itimer->itimer.it_value;
|
|
n_itimer->set_tv = enum_itimer->current_tv;
|
|
}
|
|
/* Figure global minimum time to go until expiration */
|
|
enum_itimer->min_tv = ndet_tv_min(local_min,
|
|
enum_itimer->min_tv);
|
|
/*
|
|
* Tell automatic signal mechanism to watch for this
|
|
* kind of interval timer expiration.
|
|
*/
|
|
ndet_sigs_auto |= SIG_BIT(enum_itimer->signal);
|
|
}
|
|
/*
|
|
* Know can skip rest of clients conditions because only one
|
|
* itimer of each type is allowed per client.
|
|
*/
|
|
return(NTFY_ENUM_SKIP);
|
|
default: {}
|
|
}
|
|
return(NTFY_ENUM_NEXT);
|
|
}
|
|
|
|
/* Update every virtual itimer's set_tv field */
|
|
/* ARGSUSED */
|
|
static NTFY_ENUM
|
|
ndet_virtual_set_tv_update(client, condition, context)
|
|
NTFY_CLIENT *client;
|
|
NTFY_CONDITION *condition;
|
|
NTFY_ENUM_DATA context;
|
|
{
|
|
struct timeval *set_tv = (struct timeval *)context;
|
|
|
|
if (condition->type == NTFY_VIRTUAL_ITIMER) {
|
|
condition->data.ntfy_itimer->set_tv = *set_tv;
|
|
/*
|
|
* Know can skip rest of clients conditions because only one
|
|
* itimer of each type is allowed per client.
|
|
*/
|
|
return(NTFY_ENUM_SKIP);
|
|
} else
|
|
return(NTFY_ENUM_NEXT);
|
|
}
|
|
|
|
static void
|
|
ndet_fig_destroy_change()
|
|
{
|
|
u_int sigs_auto_tmp;
|
|
|
|
ndet_flags &= ~NDET_DESTROY_CHANGE;
|
|
/* Remember what signals were catching for auto client */
|
|
sigs_auto_tmp = ndet_sigs_auto;
|
|
/* Zero out what used to collect the data for auto client */
|
|
ndet_sigs_auto &= ~(SIG_BIT(SIGTERM));
|
|
/* Recompute SIGTERM managing */
|
|
(void) ntfy_enum_conditions(ndet_clients, ndet_destroy_change,
|
|
NTFY_ENUM_DATA_NULL);
|
|
/* Toggle notifier auto signal catching if situation changed */
|
|
ndet_toggle_auto(sigs_auto_tmp, SIGTERM);
|
|
}
|
|
|
|
static void
|
|
ndet_fig_sig_change()
|
|
{
|
|
register u_int sig_bit, sigs_tmp, sigs_dif;
|
|
register int sig;
|
|
int n;
|
|
|
|
ndet_flags &= ~NDET_SIGNAL_CHANGE;
|
|
/* Remember what signals were catching */
|
|
sigs_tmp = ndet_sigs_managing;
|
|
/* Zero out what used to collect the data */
|
|
/*
|
|
* Note: ndet_signal_catcher shouldn't look at
|
|
* ndet_sigs_managing when NTFY_IN_CRITICAL.
|
|
*/
|
|
ndet_sigs_managing = 0;
|
|
/* Recompute signals managing */
|
|
(void) ntfy_enum_conditions(ndet_clients, ndet_sig_change,
|
|
NTFY_ENUM_DATA_NULL);
|
|
/* Update signal catching */
|
|
sigs_dif = ndet_sigs_managing^sigs_tmp;
|
|
for (sig = 1;sig < NSIG;sig++) {
|
|
if ((sig_bit = SIG_BIT(sig) & sigs_dif)) {
|
|
if (sig_bit & ndet_sigs_managing) {
|
|
ndet_enable_sig(sig);
|
|
} if (sig_bit & sigs_tmp) {
|
|
/*
|
|
* Don't catch this signal,
|
|
* currently we are
|
|
*/
|
|
n = sigvec(sig, &ndet_prev_sigvec[sig],
|
|
(struct sigvec *)0); /* SYSTEM CALL */
|
|
ntfy_assert(n == 0, "Unexpected error: sigvec");
|
|
} else
|
|
ntfy_set_errno(NOTIFY_INTERNAL_ERROR);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Call this routine (other than from ndet_fig_sig_change) when you need
|
|
* to make sure that a signal is being caught but don't want to go through
|
|
* the whole process of globally finding out who else needs it.
|
|
*/
|
|
pkg_private void
|
|
ndet_enable_sig(sig)
|
|
int sig;
|
|
{
|
|
if (!(SIG_BIT(sig) & ndet_sigs_managing)) {
|
|
int n;
|
|
|
|
/* Arrange to catch this signal, currently we are not */
|
|
n = sigvec(sig, &ndet_sigvec, &ndet_prev_sigvec[sig]);
|
|
/* SYSTEM CALL */
|
|
ntfy_assert(n == 0, "Unexpected error: sigvec");
|
|
ndet_sigs_managing |= SIG_BIT(sig);
|
|
}
|
|
}
|
|
|
|
pkg_private_data int ndet_track_sigs = 0;
|
|
|
|
pkg_private void /* Should be static but there might be clients of it */
|
|
ndet_signal_catcher(sig, code, scp)
|
|
int sig;
|
|
int code;
|
|
struct sigcontext *scp;
|
|
{
|
|
int oldmask = sigblock((int)ndet_sigs_managing); /* SYSTEM CALL */
|
|
void (*old_handler)() = ndet_prev_sigvec[sig].sv_handler;
|
|
|
|
if (NTFY_IN_CRITICAL || ntfy_nodes_avail < NTFY_PRE_ALLOCED_MIN) {
|
|
ntfy_sigs_delayed |= SIG_BIT(sig);
|
|
(void) sigsetmask(oldmask); /* SYSTEM CALL */
|
|
#ifdef NTFY_DEBUG
|
|
if (ndet_track_sigs)
|
|
(void) fprintf(stdout, "SIG caught when CRITICAL %ld\n", sig);
|
|
#endif NTFY_DEBUG
|
|
goto Done;
|
|
}
|
|
NTFY_BEGIN_INTERRUPT;
|
|
ndet_signal_code = code;
|
|
ndet_signal_context = scp;
|
|
ndet_send_async_sigs(SIG_BIT(sig));
|
|
(void) sigsetmask(oldmask); /* SYSTEM CALL */
|
|
NTFY_END_INTERRUPT;
|
|
Done:
|
|
/*
|
|
* Call previous handler. This feature is not part of the
|
|
* notifier definition but is included as a means of reducing
|
|
* compatibility problems.
|
|
*/
|
|
if (old_handler != SIG_DFL && old_handler != SIG_IGN)
|
|
old_handler(sig, code, scp);
|
|
#ifdef NTFY_DEBUG
|
|
if (ndet_track_sigs)
|
|
(void) fprintf(stdout, "SIG caught %ld\n", sig);
|
|
#endif NTFY_DEBUG
|
|
return;
|
|
}
|
|
|
|
pkg_private void
|
|
ndet_send_delayed_sigs()
|
|
{
|
|
register int sigs;
|
|
int oldmask;
|
|
|
|
ntfy_assert(!NTFY_IN_INTERRUPT, "Tried send delayed sig in interrupt");
|
|
ntfy_assert(!NTFY_IN_CRITICAL, "Tried send delayed sig when protected");
|
|
/* Don't need to enter critical section because blocking signals. */
|
|
/* Carefully reset ntfy_sigs_delayed so don't loose signal. */
|
|
oldmask = sigblock((int)ndet_sigs_managing); /* SYSTEM CALL */
|
|
sigs = ntfy_sigs_delayed;
|
|
ntfy_sigs_delayed = 0;
|
|
/* Send delayed signals */
|
|
ndet_send_async_sigs(sigs);
|
|
(void) sigsetmask(oldmask); /* SYSTEM CALL */
|
|
}
|
|
|
|
/* Don't need to enter critical section because blocking signals when called. */
|
|
static void
|
|
ndet_send_async_sigs(sigs)
|
|
int sigs;
|
|
{
|
|
|
|
ndet_sigs_received |= sigs;
|
|
(void) ntfy_paranoid_enum_conditions(ndet_clients, ndet_async_sig_send,
|
|
(NTFY_ENUM_DATA)sigs);
|
|
}
|
|
|
|
static NTFY_ENUM
|
|
ndet_async_sig_send(client, condition, context)
|
|
NTFY_CLIENT *client;
|
|
register NTFY_CONDITION *condition;
|
|
NTFY_ENUM_DATA context;
|
|
{
|
|
u_int sigs = (u_int)context;
|
|
|
|
if (condition->type == NTFY_ASYNC_SIGNAL &&
|
|
(SIG_BIT(condition->data.signal) & sigs)) {
|
|
Notify_func func;
|
|
|
|
/* Push condition on interposition stack */
|
|
func = nint_push_callout(client, condition);
|
|
/* The notifier doesn't catch any async sigs */
|
|
(void) func(client->nclient,
|
|
condition->data.signal, NOTIFY_ASYNC);
|
|
/* Pop condition from interposition stack */
|
|
nint_unprotected_pop_callout();
|
|
/* Note: condition/client may be undefined now! */
|
|
}
|
|
return(NTFY_ENUM_NEXT);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static NTFY_ENUM
|
|
ndet_destroy_change(client, condition, context)
|
|
NTFY_CLIENT *client;
|
|
NTFY_CONDITION *condition;
|
|
NTFY_ENUM_DATA context;
|
|
{
|
|
if (condition->type == NTFY_DESTROY)
|
|
/* Tell automatic signal mechanism to watch for SIGTERM */
|
|
ndet_sigs_auto |= SIG_BIT(SIGTERM);
|
|
return(NTFY_ENUM_NEXT);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static NTFY_ENUM
|
|
ndet_sig_change(client, condition, context)
|
|
NTFY_CLIENT *client;
|
|
NTFY_CONDITION *condition;
|
|
NTFY_ENUM_DATA context;
|
|
{
|
|
if ((condition->type == NTFY_SYNC_SIGNAL) ||
|
|
(condition->type == NTFY_ASYNC_SIGNAL))
|
|
ndet_sigs_managing |= SIG_BIT(condition->data.signal);
|
|
return(NTFY_ENUM_NEXT);
|
|
}
|
|
|
|
pkg_private NTFY_ENUM
|
|
ndet_fd_send(client, condition, context)
|
|
NTFY_CLIENT *client;
|
|
register NTFY_CONDITION *condition;
|
|
NTFY_ENUM_DATA context;
|
|
{
|
|
register NDET_ENUM_SEND *enum_send = (NDET_ENUM_SEND *)context;
|
|
|
|
switch (condition->type) {
|
|
case NTFY_INPUT:
|
|
if (FD_ISSET(condition->data.fd, &enum_send->ibits))
|
|
goto EnQ;
|
|
break;
|
|
case NTFY_OUTPUT:
|
|
if (FD_ISSET(condition->data.fd, &enum_send->obits))
|
|
goto EnQ;
|
|
break;
|
|
case NTFY_EXCEPTION:
|
|
if (FD_ISSET(condition->data.fd, &enum_send->ebits))
|
|
goto EnQ;
|
|
break;
|
|
default: {}
|
|
}
|
|
return(NTFY_ENUM_NEXT);
|
|
EnQ:
|
|
if (ndis_enqueue(client, condition) != NOTIFY_OK)
|
|
/* Internal fatal error */
|
|
return(NTFY_ENUM_TERM);
|
|
return(NTFY_ENUM_NEXT);
|
|
}
|
|
|
|
static NTFY_ENUM
|
|
ndet_sig_send(client, condition, context)
|
|
NTFY_CLIENT *client;
|
|
register NTFY_CONDITION *condition;
|
|
NTFY_ENUM_DATA context;
|
|
{
|
|
register NDET_ENUM_SEND *enum_send = (NDET_ENUM_SEND *)context;
|
|
|
|
if (condition->type == NTFY_SYNC_SIGNAL &&
|
|
(SIG_BIT(condition->data.signal) & enum_send->sigs)) {
|
|
/* Intercept conditions that were set by the notifier */
|
|
if (client->nclient == ndet_auto_nclient)
|
|
return(ndet_auto_sig_send(client, condition, context));
|
|
else {
|
|
if (ndis_enqueue(client, condition) != NOTIFY_OK)
|
|
ntfy_fatal_error("Error when enq condition");
|
|
}
|
|
}
|
|
return(NTFY_ENUM_NEXT);
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
static NTFY_ENUM
|
|
ndet_poll_send(client, condition, context)
|
|
NTFY_CLIENT *client;
|
|
register NTFY_CONDITION *condition;
|
|
NTFY_ENUM_DATA context;
|
|
{
|
|
if ((condition->type == NTFY_REAL_ITIMER ||
|
|
condition->type == NTFY_VIRTUAL_ITIMER) &&
|
|
ndet_tv_polling(condition->data.ntfy_itimer->itimer.it_value)) {
|
|
/*
|
|
* Dispatch notification, reset itimer value,
|
|
* remove if nothing to wait for an return -1 else 0.
|
|
*/
|
|
if (!ndet_itimer_expired(client, condition)) {
|
|
/*
|
|
* Avoid making system call in ndet_reset_itimer_set_tv
|
|
* if just only to be polling again.
|
|
*/
|
|
if (!ndet_tv_polling(
|
|
condition->data.ntfy_itimer->itimer.it_value))
|
|
ndet_reset_itimer_set_tv(condition);
|
|
}
|
|
/*
|
|
* Know can skip rest of clients conditions because
|
|
* only one itimer of each type is allowed per client.
|
|
*/
|
|
return(NTFY_ENUM_SKIP);
|
|
}
|
|
return(NTFY_ENUM_NEXT);
|
|
}
|
|
|
|
extern void
|
|
notify_set_signal_check(tv)
|
|
struct timeval tv;
|
|
{
|
|
ndet_signal_check = tv;
|
|
}
|
|
|
|
extern struct timeval
|
|
notify_get_signal_check()
|
|
{
|
|
return (ndet_signal_check);
|
|
}
|
|
|
|
extern int
|
|
notify_get_signal_code()
|
|
{
|
|
/* Could put check to see if in interrupt (should be) */
|
|
return (ndet_signal_code);
|
|
}
|
|
|
|
extern struct sigcontext *
|
|
notify_get_signal_context()
|
|
{
|
|
/* Could put check to see if in interrupt (should be) */
|
|
return (ndet_signal_context);
|
|
}
|
|
|