| /* |
| * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved. |
| * |
| * This copyrighted material is made available to anyone wishing to use, |
| * modify, copy, or redistribute it subject to the terms and conditions |
| * of the GNU Lesser General Public License v.2.1. |
| * |
| * You should have received a copy of the GNU Lesser General Public License |
| * along with this program; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| #include "logging.h" |
| #include "link_mon.h" |
| |
| #include <errno.h> |
| #include <poll.h> |
| #include <stdlib.h> |
| |
| struct link_callback { |
| int fd; |
| const char *name; |
| void *data; |
| int (*callback)(void *data); |
| |
| struct link_callback *next; |
| }; |
| |
| static unsigned used_pfds = 0; |
| static unsigned free_pfds = 0; |
| static struct pollfd *pfds = NULL; |
| static struct link_callback *callbacks = NULL; |
| |
| int links_register(int fd, const char *name, int (*callback)(void *data), void *data) |
| { |
| unsigned i; |
| struct link_callback *lc; |
| |
| for (i = 0; i < used_pfds; i++) { |
| if (fd == pfds[i].fd) { |
| LOG_ERROR("links_register: Duplicate file descriptor"); |
| return -EINVAL; |
| } |
| } |
| |
| lc = malloc(sizeof(*lc)); |
| if (!lc) |
| return -ENOMEM; |
| |
| lc->fd = fd; |
| lc->name = name; |
| lc->data = data; |
| lc->callback = callback; |
| |
| if (!free_pfds) { |
| struct pollfd *tmp; |
| tmp = realloc(pfds, sizeof(struct pollfd) * ((used_pfds*2) + 1)); |
| if (!tmp) { |
| free(lc); |
| return -ENOMEM; |
| } |
| |
| pfds = tmp; |
| free_pfds = used_pfds + 1; |
| } |
| |
| free_pfds--; |
| pfds[used_pfds].fd = fd; |
| pfds[used_pfds].events = POLLIN; |
| pfds[used_pfds].revents = 0; |
| used_pfds++; |
| |
| lc->next = callbacks; |
| callbacks = lc; |
| LOG_DBG("Adding %s/%d", lc->name, lc->fd); |
| LOG_DBG(" used_pfds = %u, free_pfds = %u", |
| used_pfds, free_pfds); |
| |
| return 0; |
| } |
| |
| int links_unregister(int fd) |
| { |
| unsigned i; |
| struct link_callback *p, *c; |
| |
| for (i = 0; i < used_pfds; i++) |
| if (fd == pfds[i].fd) { |
| /* entire struct is copied (overwritten) */ |
| pfds[i] = pfds[used_pfds - 1]; |
| used_pfds--; |
| free_pfds++; |
| } |
| |
| for (p = NULL, c = callbacks; c; p = c, c = c->next) |
| if (fd == c->fd) { |
| LOG_DBG("Freeing up %s/%d", c->name, c->fd); |
| LOG_DBG(" used_pfds = %u, free_pfds = %u", |
| used_pfds, free_pfds); |
| if (p) |
| p->next = c->next; |
| else |
| callbacks = c->next; |
| free(c); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| int links_monitor(void) |
| { |
| unsigned i; |
| int r; |
| |
| for (i = 0; i < used_pfds; i++) { |
| pfds[i].revents = 0; |
| } |
| |
| r = poll(pfds, used_pfds, -1); |
| if (r <= 0) |
| return r; |
| |
| r = 0; |
| /* FIXME: handle POLLHUP */ |
| for (i = 0; i < used_pfds; i++) |
| if (pfds[i].revents & POLLIN) { |
| LOG_DBG("Data ready on %d", pfds[i].fd); |
| |
| /* FIXME: Add this back return 1;*/ |
| r++; |
| } |
| |
| return r; |
| } |
| |
| int links_issue_callbacks(void) |
| { |
| unsigned i; |
| struct link_callback *lc; |
| |
| for (i = 0; i < used_pfds; i++) |
| if (pfds[i].revents & POLLIN) |
| for (lc = callbacks; lc; lc = lc->next) |
| if (pfds[i].fd == lc->fd) { |
| LOG_DBG("Issuing callback on %s/%d", |
| lc->name, lc->fd); |
| lc->callback(lc->data); |
| break; |
| } |
| return 0; |
| } |