2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <stdlib.h>
2N/A#include <errno.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stropts.h> /* INFTIM */
2N/A
2N/A#include <libinetutil.h>
2N/A#include "libinetutil_impl.h"
2N/A
2N/Astatic int grow_fds(iu_eh_t *, int);
2N/A
2N/A/*
2N/A * signal_to_eh[] is pretty much useless, since the event handler is
2N/A * really a singleton (we pass iu_eh_t *'s around to maintain an
2N/A * abstraction, not to allow multiple event handlers to exist). we
2N/A * need some way to get back our event handler in post_signal(),
2N/A * and since the signal model is too lame to provide opaque pointers,
2N/A * we have to resort to global variables.
2N/A */
2N/A
2N/Astatic iu_eh_t *signal_to_eh[NSIG];
2N/A
2N/A/*
2N/A * iu_eh_create(): creates, initializes, and returns an event handler for use
2N/A *
2N/A * input: void
2N/A * output: iu_eh_t *: the new event handler
2N/A */
2N/A
2N/Aiu_eh_t *
2N/Aiu_eh_create(void)
2N/A{
2N/A iu_eh_t *eh = malloc(sizeof (iu_eh_t));
2N/A int sig;
2N/A
2N/A if (eh == NULL)
2N/A return (NULL);
2N/A
2N/A eh->iueh_pollfds = NULL;
2N/A eh->iueh_events = NULL;
2N/A eh->iueh_shutdown = NULL;
2N/A eh->iueh_num_fds = 0;
2N/A eh->iueh_stop = B_FALSE;
2N/A eh->iueh_reason = 0;
2N/A eh->iueh_shutdown_arg = NULL;
2N/A
2N/A (void) sigemptyset(&eh->iueh_sig_regset);
2N/A for (sig = 0; sig < NSIG; sig++) {
2N/A eh->iueh_sig_info[sig].iues_pending = B_FALSE;
2N/A eh->iueh_sig_info[sig].iues_handler = NULL;
2N/A eh->iueh_sig_info[sig].iues_data = NULL;
2N/A }
2N/A
2N/A return (eh);
2N/A}
2N/A
2N/A/*
2N/A * iu_eh_destroy(): destroys an existing event handler
2N/A *
2N/A * input: iu_eh_t *: the event handler to destroy
2N/A * output: void
2N/A * notes: it is assumed all events related to this eh have been unregistered
2N/A * prior to calling iu_eh_destroy()
2N/A */
2N/A
2N/Avoid
2N/Aiu_eh_destroy(iu_eh_t *eh)
2N/A{
2N/A int sig;
2N/A
2N/A for (sig = 0; sig < NSIG; sig++)
2N/A if (signal_to_eh[sig] == eh)
2N/A (void) iu_eh_unregister_signal(eh, sig, NULL);
2N/A
2N/A free(eh->iueh_pollfds);
2N/A free(eh->iueh_events);
2N/A free(eh);
2N/A}
2N/A
2N/A/*
2N/A * iu_stop_handling_events(): informs the event handler to stop handling events
2N/A *
2N/A * input: iu_eh_t *: the event handler to stop.
2N/A * unsigned int: the (user-defined) reason why
2N/A * iu_eh_shutdown_t *: the shutdown callback. if it is NULL,
2N/A * the event handler will stop right away;
2N/A * otherwise, the event handler will not
2N/A * stop until the callback returns B_TRUE
2N/A * void *: data for the shutdown callback. it may be NULL
2N/A * output: void
2N/A * notes: the event handler in question must be in iu_handle_events()
2N/A */
2N/A
2N/Avoid
2N/Aiu_stop_handling_events(iu_eh_t *eh, unsigned int reason,
2N/A iu_eh_shutdown_t *shutdown, void *arg)
2N/A{
2N/A eh->iueh_stop = B_TRUE;
2N/A eh->iueh_reason = reason;
2N/A eh->iueh_shutdown = shutdown;
2N/A eh->iueh_shutdown_arg = arg;
2N/A}
2N/A
2N/A/*
2N/A * grow_fds(): grows the internal file descriptor set used by the event
2N/A * handler
2N/A *
2N/A * input: iu_eh_t *: the event handler whose descriptor set needs to be grown
2N/A * int: the new total number of descriptors needed in the set
2N/A * output: int: zero on failure, success otherwise
2N/A */
2N/A
2N/Astatic int
2N/Agrow_fds(iu_eh_t *eh, int total_fds)
2N/A{
2N/A unsigned int i;
2N/A struct pollfd *new_pollfds;
2N/A iu_event_node_t *new_events;
2N/A
2N/A if (total_fds <= eh->iueh_num_fds)
2N/A return (1);
2N/A
2N/A new_pollfds = realloc(eh->iueh_pollfds,
2N/A total_fds * sizeof (struct pollfd));
2N/A if (new_pollfds == NULL)
2N/A return (0);
2N/A
2N/A eh->iueh_pollfds = new_pollfds;
2N/A
2N/A new_events = realloc(eh->iueh_events,
2N/A total_fds * sizeof (iu_event_node_t));
2N/A if (new_events == NULL) {
2N/A
2N/A /*
2N/A * yow. one realloc failed, but the other succeeded.
2N/A * we will just leave the descriptor size at the
2N/A * original size. if the caller tries again, then the
2N/A * first realloc() will do nothing since the requested
2N/A * number of descriptors is already allocated.
2N/A */
2N/A
2N/A return (0);
2N/A }
2N/A
2N/A for (i = eh->iueh_num_fds; i < total_fds; i++)
2N/A eh->iueh_pollfds[i].fd = -1;
2N/A
2N/A eh->iueh_events = new_events;
2N/A eh->iueh_num_fds = total_fds;
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * when increasing the file descriptor set size, how much to increase by:
2N/A */
2N/A
2N/A#define EH_FD_SLACK 10
2N/A
2N/A/*
2N/A * iu_register_event(): adds an event to the set managed by an event handler
2N/A *
2N/A * input: iu_eh_t *: the event handler to add the event to
2N/A * int: the descriptor on which to listen for events. must be
2N/A * a descriptor which has not yet been registered.
2N/A * short: the events to listen for on that descriptor
2N/A * iu_eh_callback_t: the callback to execute when the event happens
2N/A * void *: the argument to pass to the callback function
2N/A * output: iu_event_id_t: -1 on failure, the new event id otherwise
2N/A */
2N/A
2N/Aiu_event_id_t
2N/Aiu_register_event(iu_eh_t *eh, int fd, short events, iu_eh_callback_t *callback,
2N/A void *arg)
2N/A{
2N/A if (eh->iueh_num_fds <= fd)
2N/A if (grow_fds(eh, fd + EH_FD_SLACK) == 0)
2N/A return (-1);
2N/A
2N/A /*
2N/A * the current implementation uses the file descriptor itself
2N/A * as the iu_event_id_t, since we know the kernel's gonna be
2N/A * pretty smart about managing file descriptors and we know
2N/A * that they're per-process unique. however, it does mean
2N/A * that the same descriptor cannot be registered multiple
2N/A * times for different callbacks depending on its events. if
2N/A * this behavior is desired, either use dup(2) to get a unique
2N/A * descriptor, or demultiplex in the callback function based
2N/A * on `events'.
2N/A */
2N/A
2N/A if (eh->iueh_pollfds[fd].fd != -1)
2N/A return (-1);
2N/A
2N/A eh->iueh_pollfds[fd].fd = fd;
2N/A eh->iueh_pollfds[fd].events = events;
2N/A eh->iueh_events[fd].iuen_callback = callback;
2N/A eh->iueh_events[fd].iuen_arg = arg;
2N/A
2N/A return (fd);
2N/A}
2N/A
2N/A/*
2N/A * iu_unregister_event(): removes an event from the set managed by an event
2N/A * handler
2N/A *
2N/A * input: iu_eh_t *: the event handler to remove the event from
2N/A * iu_event_id_t: the event to remove (from iu_register_event())
2N/A * void **: if non-NULL, will be set to point to the argument passed
2N/A * into iu_register_event()
2N/A * output: int: zero on failure, success otherwise
2N/A */
2N/A
2N/Aint
2N/Aiu_unregister_event(iu_eh_t *eh, iu_event_id_t event_id, void **arg)
2N/A{
2N/A if (event_id < 0 || event_id >= eh->iueh_num_fds ||
2N/A eh->iueh_pollfds[event_id].fd == -1)
2N/A return (0);
2N/A
2N/A /*
2N/A * fringe condition: in case this event was about to be called
2N/A * back in iu_handle_events(), zero revents to prevent it.
2N/A * (having an unregistered event get called back could be
2N/A * disastrous depending on if `arg' is reference counted).
2N/A */
2N/A
2N/A eh->iueh_pollfds[event_id].revents = 0;
2N/A eh->iueh_pollfds[event_id].fd = -1;
2N/A if (arg != NULL)
2N/A *arg = eh->iueh_events[event_id].iuen_arg;
2N/A
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * iu_handle_events(): begins handling events on an event handler
2N/A *
2N/A * input: iu_eh_t *: the event handler to begin event handling on
2N/A * tq_t *: a timer queue of timers to process while handling events
2N/A * (see timer_queue.h for details)
2N/A * output: int: the reason why we stopped, -1 if due to internal failure
2N/A */
2N/A
2N/Aint
2N/Aiu_handle_events(iu_eh_t *eh, iu_tq_t *tq)
2N/A{
2N/A int n_lit, timeout, sig, saved_errno;
2N/A unsigned int i;
2N/A sigset_t oset;
2N/A
2N/A eh->iueh_stop = B_FALSE;
2N/A do {
2N/A timeout = tq ? iu_earliest_timer(tq) : INFTIM;
2N/A
2N/A /*
2N/A * we only unblock registered signals around poll(); this
2N/A * way other parts of the code don't have to worry about
2N/A * restarting "non-restartable" system calls and so forth.
2N/A */
2N/A
2N/A (void) sigprocmask(SIG_UNBLOCK, &eh->iueh_sig_regset, &oset);
2N/A n_lit = poll(eh->iueh_pollfds, eh->iueh_num_fds, timeout);
2N/A saved_errno = errno;
2N/A (void) sigprocmask(SIG_SETMASK, &oset, NULL);
2N/A
2N/A switch (n_lit) {
2N/A
2N/A case -1:
2N/A if (saved_errno != EINTR)
2N/A return (-1);
2N/A
2N/A for (sig = 0; sig < NSIG; sig++) {
2N/A if (eh->iueh_sig_info[sig].iues_pending) {
2N/A eh->iueh_sig_info[sig].iues_pending =
2N/A B_FALSE;
2N/A eh->iueh_sig_info[sig].iues_handler(eh,
2N/A sig,
2N/A eh->iueh_sig_info[sig].iues_data);
2N/A }
2N/A }
2N/A
2N/A if (eh->iueh_shutdown != NULL)
2N/A break;
2N/A
2N/A continue;
2N/A
2N/A case 0:
2N/A /*
2N/A * timeout occurred. we must have a valid tq pointer
2N/A * since that's the only way a timeout can happen.
2N/A */
2N/A
2N/A (void) iu_expire_timers(tq);
2N/A continue;
2N/A
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A /* file descriptors are lit; call 'em back */
2N/A
2N/A for (i = 0; i < eh->iueh_num_fds && n_lit > 0; i++) {
2N/A
2N/A if (eh->iueh_pollfds[i].revents == 0)
2N/A continue;
2N/A
2N/A n_lit--;
2N/A
2N/A /*
2N/A * turn off any descriptors that have gone
2N/A * bad. shouldn't happen, but...
2N/A */
2N/A
2N/A if (eh->iueh_pollfds[i].revents & (POLLNVAL|POLLERR)) {
2N/A /* TODO: issue a warning here - but how? */
2N/A (void) iu_unregister_event(eh, i, NULL);
2N/A continue;
2N/A }
2N/A
2N/A eh->iueh_events[i].iuen_callback(eh, i,
2N/A eh->iueh_pollfds[i].revents, i,
2N/A eh->iueh_events[i].iuen_arg);
2N/A }
2N/A
2N/A } while (eh->iueh_stop == B_FALSE || (eh->iueh_shutdown != NULL &&
2N/A eh->iueh_shutdown(eh, eh->iueh_shutdown_arg) == B_FALSE));
2N/A
2N/A return (eh->iueh_reason);
2N/A}
2N/A
2N/A/*
2N/A * post_signal(): posts a signal for later consumption in iu_handle_events()
2N/A *
2N/A * input: int: the signal that's been received
2N/A * output: void
2N/A */
2N/A
2N/Astatic void
2N/Apost_signal(int sig)
2N/A{
2N/A if (signal_to_eh[sig] != NULL)
2N/A signal_to_eh[sig]->iueh_sig_info[sig].iues_pending = B_TRUE;
2N/A}
2N/A
2N/A/*
2N/A * iu_eh_register_signal(): registers a signal handler with an event handler
2N/A *
2N/A * input: iu_eh_t *: the event handler to register the signal handler with
2N/A * int: the signal to register
2N/A * iu_eh_sighandler_t *: the signal handler to call back
2N/A * void *: the argument to pass to the signal handler function
2N/A * output: int: zero on failure, success otherwise
2N/A */
2N/A
2N/Aint
2N/Aiu_eh_register_signal(iu_eh_t *eh, int sig, iu_eh_sighandler_t *handler,
2N/A void *data)
2N/A{
2N/A struct sigaction act;
2N/A
2N/A if (sig < 0 || sig >= NSIG || signal_to_eh[sig] != NULL)
2N/A return (0);
2N/A
2N/A act.sa_flags = 0;
2N/A act.sa_handler = &post_signal;
2N/A (void) sigemptyset(&act.sa_mask);
2N/A (void) sigaddset(&act.sa_mask, sig); /* used for sigprocmask() */
2N/A
2N/A if (sigaction(sig, &act, NULL) == -1)
2N/A return (0);
2N/A
2N/A (void) sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
2N/A
2N/A eh->iueh_sig_info[sig].iues_data = data;
2N/A eh->iueh_sig_info[sig].iues_handler = handler;
2N/A signal_to_eh[sig] = eh;
2N/A
2N/A (void) sigaddset(&eh->iueh_sig_regset, sig);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * iu_eh_unregister_signal(): unregisters a signal handler from an event handler
2N/A *
2N/A * input: iu_eh_t *: the event handler to unregister the signal handler from
2N/A * int: the signal to unregister
2N/A * void **: if non-NULL, will be set to point to the argument passed
2N/A * into iu_eh_register_signal()
2N/A * output: int: zero on failure, success otherwise
2N/A */
2N/A
2N/Aint
2N/Aiu_eh_unregister_signal(iu_eh_t *eh, int sig, void **datap)
2N/A{
2N/A sigset_t set;
2N/A
2N/A if (sig < 0 || sig >= NSIG || signal_to_eh[sig] != eh)
2N/A return (0);
2N/A
2N/A if (signal(sig, SIG_DFL) == SIG_ERR)
2N/A return (0);
2N/A
2N/A if (datap != NULL)
2N/A *datap = eh->iueh_sig_info[sig].iues_data;
2N/A
2N/A (void) sigemptyset(&set);
2N/A (void) sigaddset(&set, sig);
2N/A (void) sigprocmask(SIG_UNBLOCK, &set, NULL);
2N/A
2N/A eh->iueh_sig_info[sig].iues_data = NULL;
2N/A eh->iueh_sig_info[sig].iues_handler = NULL;
2N/A eh->iueh_sig_info[sig].iues_pending = B_FALSE;
2N/A signal_to_eh[sig] = NULL;
2N/A
2N/A (void) sigdelset(&eh->iueh_sig_regset, sig);
2N/A return (1);
2N/A}