/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
* Sun elects to license this software under the BSD license.
* See README for more details.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <poll.h>
#include "eloop.h"
static struct eloop_data eloop;
/*
* Initialize global event loop data - must be called before any other eloop_*
* function. user_data is a pointer to global data structure and will be passed
* as eloop_ctx to signal handlers.
*/
void
eloop_init(void *user_data)
{
(void) memset(&eloop, 0, sizeof (eloop));
eloop.user_data = user_data;
}
/*
* Register handler for read event
*/
int
eloop_register_read_sock(int sock,
void (*handler)(int sock, void *eloop_ctx,
void *sock_ctx), void *eloop_data, void *user_data)
{
struct eloop_sock *tmp;
tmp = (struct eloop_sock *)realloc(eloop.readers,
(eloop.reader_count + 1) * sizeof (struct eloop_sock));
if (tmp == NULL)
return (-1);
tmp[eloop.reader_count].sock = sock;
tmp[eloop.reader_count].eloop_data = eloop_data;
tmp[eloop.reader_count].user_data = user_data;
tmp[eloop.reader_count].handler = handler;
eloop.reader_count++;
eloop.readers = tmp;
if (sock > eloop.max_sock)
eloop.max_sock = sock;
return (0);
}
void
eloop_unregister_read_sock(int sock)
{
int i;
if (eloop.readers == NULL || eloop.reader_count == 0)
return;
for (i = 0; i < eloop.reader_count; i++) {
if (eloop.readers[i].sock == sock)
break;
}
if (i == eloop.reader_count)
return;
if (i != eloop.reader_count - 1) {
(void) memmove(&eloop.readers[i], &eloop.readers[i + 1],
(eloop.reader_count - i - 1) *
sizeof (struct eloop_sock));
}
eloop.reader_count--;
}
/*
* Register timeout routines
*/
int
eloop_register_timeout(unsigned int secs, unsigned int usecs,
void (*handler)(void *eloop_ctx, void *timeout_ctx),
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *tmp, *prev;
timeout = (struct eloop_timeout *)malloc(sizeof (*timeout));
if (timeout == NULL)
return (-1);
(void) gettimeofday(&timeout->time, NULL);
timeout->time.tv_sec += secs;
timeout->time.tv_usec += usecs;
while (timeout->time.tv_usec >= 1000000) {
timeout->time.tv_sec++;
timeout->time.tv_usec -= 1000000;
}
timeout->eloop_data = eloop_data;
timeout->user_data = user_data;
timeout->handler = handler;
timeout->next = NULL;
if (eloop.timeout == NULL) {
eloop.timeout = timeout;
return (0);
}
prev = NULL;
tmp = eloop.timeout;
while (tmp != NULL) {
if (timercmp(&timeout->time, &tmp->time, < /* */))
break;
prev = tmp;
tmp = tmp->next;
}
if (prev == NULL) {
timeout->next = eloop.timeout;
eloop.timeout = timeout;
} else {
timeout->next = prev->next;
prev->next = timeout;
}
return (0);
}
/*
* Cancel timeouts matching <handler,eloop_data,user_data>.
* ELOOP_ALL_CTX can be used as a wildcard for cancelling all timeouts
* regardless of eloop_data/user_data.
*/
void
eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx),
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *prev, *next;
prev = NULL;
timeout = eloop.timeout;
while (timeout != NULL) {
next = timeout->next;
if (timeout->handler == handler &&
(timeout->eloop_data == eloop_data ||
eloop_data == ELOOP_ALL_CTX) &&
(timeout->user_data == user_data ||
user_data == ELOOP_ALL_CTX)) {
if (prev == NULL)
eloop.timeout = next;
else
prev->next = next;
free(timeout);
} else
prev = timeout;
timeout = next;
}
}
static void eloop_handle_signal(int sig)
{
int i;
eloop.signaled++;
for (i = 0; i < eloop.signal_count; i++) {
if (eloop.signals[i].sig == sig) {
eloop.signals[i].signaled++;
break;
}
}
}
static void eloop_process_pending_signals(void)
{
int i;
if (eloop.signaled == 0)
return;
eloop.signaled = 0;
for (i = 0; i < eloop.signal_count; i++) {
if (eloop.signals[i].signaled) {
eloop.signals[i].signaled = 0;
eloop.signals[i].handler(eloop.signals[i].sig,
eloop.user_data, eloop.signals[i].user_data);
}
}
}
/*
* Register handler for signal.
* Note: signals are 'global' events and there is no local eloop_data pointer
* like with other handlers. The (global) pointer given to eloop_init() will be
* used as eloop_ctx for signal handlers.
*/
int
eloop_register_signal(int sig,
void (*handler)(int sig, void *eloop_ctx, void *signal_ctx),
void *user_data)
{
struct eloop_signal *tmp;
tmp = (struct eloop_signal *)
realloc(eloop.signals,
(eloop.signal_count + 1) *
sizeof (struct eloop_signal));
if (tmp == NULL)
return (-1);
tmp[eloop.signal_count].sig = sig;
tmp[eloop.signal_count].user_data = user_data;
tmp[eloop.signal_count].handler = handler;
tmp[eloop.signal_count].signaled = 0;
eloop.signal_count++;
eloop.signals = tmp;
(void) signal(sig, eloop_handle_signal);
return (0);
}
/*
* Start event loop and continue running as long as there are any registered
* event handlers.
*/
void
eloop_run(void)
{
struct pollfd pfds[MAX_POLLFDS]; /* array of polled fd */
int i, res;
int default_t, t;
struct timeval tv, now;
default_t = 5 * 1000; /* 5 seconds */
while (!eloop.terminate &&
(eloop.timeout || eloop.reader_count > 0)) {
if (eloop.timeout) {
(void) gettimeofday(&now, NULL);
if (timercmp(&now, &eloop.timeout->time, < /* */))
timersub(&eloop.timeout->time, &now, &tv);
else
tv.tv_sec = tv.tv_usec = 0;
}
t = (eloop.timeout == NULL ?
default_t : (tv.tv_sec * 1000 + tv.tv_usec / 1000));
for (i = 0; i < eloop.reader_count; i++) {
pfds[i].fd = eloop.readers[i].sock;
pfds[i].events = POLLIN | POLLPRI;
}
res = poll(pfds, eloop.reader_count, t);
if (res < 0 && errno != EINTR)
return;
eloop_process_pending_signals();
/* check if some registered timeouts have occurred */
if (eloop.timeout) {
struct eloop_timeout *tmp;
(void) gettimeofday(&now, NULL);
if (!timercmp(&now, &eloop.timeout->time, < /* */)) {
tmp = eloop.timeout;
eloop.timeout = eloop.timeout->next;
tmp->handler(tmp->eloop_data, tmp->user_data);
free(tmp);
}
}
if (res <= 0)
continue;
for (i = 0; i < eloop.reader_count; i++) {
if (pfds[i].revents) {
eloop.readers[i].handler(
eloop.readers[i].sock,
eloop.readers[i].eloop_data,
eloop.readers[i].user_data);
}
}
}
}
/*
* Terminate event loop even if there are registered events.
*/
void
eloop_terminate(void)
{
eloop.terminate = 1;
}
/*
* Free any reserved resources. After calling eloop_destoy(), other eloop_*
* functions must not be called before re-running eloop_init().
*/
void
eloop_destroy(void)
{
struct eloop_timeout *timeout, *prev;
timeout = eloop.timeout;
while (timeout != NULL) {
prev = timeout;
timeout = timeout->next;
free(prev);
}
free(eloop.readers);
free(eloop.signals);
}