1/* 2 * (C) 2006-2012 by Pablo Neira Ayuso <pablo@netfilter.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 * 18 * Part of this code has been sponsored by Vyatta Inc. <http://www.vyatta.com> 19 */ 20#include <stdlib.h> 21#include <string.h> 22#include <errno.h> 23#include <signal.h> 24 25#include "conntrackd.h" 26#include "date.h" 27#include "fds.h" 28 29struct fds *create_fds(void) 30{ 31 struct fds *fds; 32 33 fds = (struct fds *) calloc(sizeof(struct fds), 1); 34 if (fds == NULL) 35 return NULL; 36 37 INIT_LIST_HEAD(&fds->list); 38 39 return fds; 40} 41 42void destroy_fds(struct fds *fds) 43{ 44 struct fds_item *this, *tmp; 45 46 list_for_each_entry_safe(this, tmp, &fds->list, head) { 47 list_del(&this->head); 48 FD_CLR(this->fd, &fds->readfds); 49 free(this); 50 } 51 free(fds); 52} 53 54int register_fd(int fd, void (*cb)(void *data), void *data, struct fds *fds) 55{ 56 struct fds_item *item; 57 58 FD_SET(fd, &fds->readfds); 59 60 if (fd > fds->maxfd) 61 fds->maxfd = fd; 62 63 item = calloc(sizeof(struct fds_item), 1); 64 if (item == NULL) 65 return -1; 66 67 item->fd = fd; 68 item->cb = cb; 69 item->data = data; 70 /* Order matters: the descriptors are served in FIFO basis. */ 71 list_add_tail(&item->head, &fds->list); 72 73 return 0; 74} 75 76int unregister_fd(int fd, struct fds *fds) 77{ 78 int found = 0, maxfd = -1; 79 struct fds_item *this, *tmp; 80 81 list_for_each_entry_safe(this, tmp, &fds->list, head) { 82 if (this->fd == fd) { 83 list_del(&this->head); 84 FD_CLR(this->fd, &fds->readfds); 85 free(this); 86 found = 1; 87 /* ... and recalculate maxfd, see below. */ 88 } 89 } 90 /* not found, report an error. */ 91 if (!found) 92 return -1; 93 94 /* calculate the new maximum fd. */ 95 list_for_each_entry(this, &fds->list, head) { 96 if (maxfd < this->fd) { 97 maxfd = this->fd; 98 } 99 } 100 fds->maxfd = maxfd; 101 102 return 0; 103} 104 105static void select_main_step(struct timeval *next_alarm) 106{ 107 int ret; 108 fd_set readfds = STATE(fds)->readfds; 109 struct fds_item *cur, *tmp; 110 111 ret = select(STATE(fds)->maxfd + 1, &readfds, NULL, NULL, next_alarm); 112 if (ret == -1) { 113 /* interrupted syscall, retry */ 114 if (errno == EINTR) 115 return; 116 117 STATE(stats).select_failed++; 118 return; 119 } 120 121 /* signals are racy */ 122 sigprocmask(SIG_BLOCK, &STATE(block), NULL); 123 124 list_for_each_entry_safe(cur, tmp, &STATE(fds)->list, head) { 125 if (FD_ISSET(cur->fd, &readfds)) 126 cur->cb(cur->data); 127 } 128 129 sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); 130} 131 132void __attribute__((noreturn)) select_main_loop(void) 133{ 134 struct timeval next_alarm; 135 struct timeval *next = NULL; 136 137 while(1) { 138 do_gettimeofday(); 139 140 sigprocmask(SIG_BLOCK, &STATE(block), NULL); 141 if (next != NULL && !timerisset(next)) 142 next = do_alarm_run(&next_alarm); 143 else 144 next = get_next_alarm_run(&next_alarm); 145 sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); 146 147 select_main_step(next); 148 } 149} 150