1 /* 2 * netio.c -- network I/O support. 3 * 4 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved. 5 * 6 * See LICENSE for the license. 7 * 8 */ 9 #include "config.h" 10 11 #include <assert.h> 12 #include <errno.h> 13 #include <sys/time.h> 14 #include <string.h> 15 #include <stdlib.h> 16 17 #include "netio.h" 18 #include "util.h" 19 20 21 #ifndef HAVE_PSELECT 22 int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, 23 const struct timespec *timeout, const sigset_t *sigmask); 24 #else 25 #include <sys/select.h> 26 #endif 27 28 netio_type * 29 netio_create(region_type *region) 30 { 31 netio_type *result; 32 33 assert(region); 34 35 result = (netio_type *) region_alloc(region, sizeof(netio_type)); 36 result->region = region; 37 result->handlers = NULL; 38 result->deallocated = NULL; 39 result->dispatch_next = NULL; 40 return result; 41 } 42 43 void 44 netio_add_handler(netio_type *netio, netio_handler_type *handler) 45 { 46 netio_handler_list_type *elt; 47 48 assert(netio); 49 assert(handler); 50 51 if (netio->deallocated) { 52 /* 53 * If we have deallocated handler list elements, reuse 54 * the first one. 55 */ 56 elt = netio->deallocated; 57 netio->deallocated = elt->next; 58 } else { 59 /* 60 * Allocate a new one. 61 */ 62 elt = (netio_handler_list_type *) region_alloc( 63 netio->region, sizeof(netio_handler_list_type)); 64 } 65 66 elt->next = netio->handlers; 67 elt->handler = handler; 68 netio->handlers = elt; 69 } 70 71 void 72 netio_remove_handler(netio_type *netio, netio_handler_type *handler) 73 { 74 netio_handler_list_type **elt_ptr; 75 76 assert(netio); 77 assert(handler); 78 79 for (elt_ptr = &netio->handlers; *elt_ptr; elt_ptr = &(*elt_ptr)->next) { 80 if ((*elt_ptr)->handler == handler) { 81 netio_handler_list_type *next = (*elt_ptr)->next; 82 if ((*elt_ptr) == netio->dispatch_next) 83 netio->dispatch_next = next; 84 (*elt_ptr)->handler = NULL; 85 (*elt_ptr)->next = netio->deallocated; 86 netio->deallocated = *elt_ptr; 87 *elt_ptr = next; 88 break; 89 } 90 } 91 } 92 93 const struct timespec * 94 netio_current_time(netio_type *netio) 95 { 96 assert(netio); 97 98 if (!netio->have_current_time) { 99 struct timeval current_timeval; 100 if (gettimeofday(¤t_timeval, NULL) == -1) { 101 log_msg(LOG_CRIT, "gettimeofday: %s, aborting.", strerror(errno)); 102 abort(); 103 } 104 timeval_to_timespec(&netio->cached_current_time, ¤t_timeval); 105 netio->have_current_time = 1; 106 } 107 108 return &netio->cached_current_time; 109 } 110 111 int 112 netio_dispatch(netio_type *netio, const struct timespec *timeout, const sigset_t *sigmask) 113 { 114 fd_set readfds, writefds, exceptfds; 115 int max_fd; 116 int have_timeout = 0; 117 struct timespec minimum_timeout; 118 netio_handler_type *timeout_handler = NULL; 119 netio_handler_list_type *elt; 120 int rc; 121 int result = 0; 122 123 assert(netio); 124 125 /* 126 * Clear the cached current time. 127 */ 128 netio->have_current_time = 0; 129 130 /* 131 * Initialize the minimum timeout with the timeout parameter. 132 */ 133 if (timeout) { 134 have_timeout = 1; 135 memcpy(&minimum_timeout, timeout, sizeof(struct timespec)); 136 } 137 138 /* 139 * Initialize the fd_sets and timeout based on the handler 140 * information. 141 */ 142 max_fd = -1; 143 FD_ZERO(&readfds); 144 FD_ZERO(&writefds); 145 FD_ZERO(&exceptfds); 146 147 for (elt = netio->handlers; elt; elt = elt->next) { 148 netio_handler_type *handler = elt->handler; 149 if (handler->fd != -1 && handler->fd < (int)FD_SETSIZE) { 150 if (handler->fd > max_fd) { 151 max_fd = handler->fd; 152 } 153 if (handler->event_types & NETIO_EVENT_READ) { 154 FD_SET(handler->fd, &readfds); 155 } 156 if (handler->event_types & NETIO_EVENT_WRITE) { 157 FD_SET(handler->fd, &writefds); 158 } 159 if (handler->event_types & NETIO_EVENT_EXCEPT) { 160 FD_SET(handler->fd, &exceptfds); 161 } 162 } 163 if (handler->timeout && (handler->event_types & NETIO_EVENT_TIMEOUT)) { 164 struct timespec relative; 165 166 relative.tv_sec = handler->timeout->tv_sec; 167 relative.tv_nsec = handler->timeout->tv_nsec; 168 timespec_subtract(&relative, netio_current_time(netio)); 169 170 if (!have_timeout || 171 timespec_compare(&relative, &minimum_timeout) < 0) 172 { 173 have_timeout = 1; 174 minimum_timeout.tv_sec = relative.tv_sec; 175 minimum_timeout.tv_nsec = relative.tv_nsec; 176 timeout_handler = handler; 177 } 178 } 179 } 180 181 if (have_timeout && minimum_timeout.tv_sec < 0) { 182 /* 183 * On negative timeout for a handler, immediatly 184 * dispatch the timeout event without checking for 185 * other events. 186 */ 187 if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) { 188 timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT); 189 } 190 return result; 191 } 192 193 /* Check for events. */ 194 rc = pselect(max_fd + 1, &readfds, &writefds, &exceptfds, 195 have_timeout ? &minimum_timeout : NULL, 196 sigmask); 197 if (rc == -1) { 198 if(errno == EINVAL || errno == EACCES || errno == EBADF) { 199 log_msg(LOG_ERR, "fatal error pselect: %s.", 200 strerror(errno)); 201 exit(1); 202 } 203 return -1; 204 } 205 206 /* 207 * Clear the cached current_time (pselect(2) may block for 208 * some time so the cached value is likely to be old). 209 */ 210 netio->have_current_time = 0; 211 212 if (rc == 0) { 213 /* 214 * No events before the minimum timeout expired. 215 * Dispatch to handler if interested. 216 */ 217 if (timeout_handler && (timeout_handler->event_types & NETIO_EVENT_TIMEOUT)) { 218 timeout_handler->event_handler(netio, timeout_handler, NETIO_EVENT_TIMEOUT); 219 } 220 } else { 221 /* 222 * Dispatch all the events to interested handlers 223 * based on the fd_sets. Note that a handler might 224 * deinstall itself, so store the next handler before 225 * calling the current handler! 226 */ 227 assert(netio->dispatch_next == NULL); 228 for (elt = netio->handlers; elt && rc; ) { 229 netio_handler_type *handler = elt->handler; 230 netio->dispatch_next = elt->next; 231 if (handler->fd != -1 && handler->fd < (int)FD_SETSIZE) { 232 netio_event_types_type event_types 233 = NETIO_EVENT_NONE; 234 if (FD_ISSET(handler->fd, &readfds)) { 235 event_types |= NETIO_EVENT_READ; 236 FD_CLR(handler->fd, &readfds); 237 rc--; 238 } 239 if (FD_ISSET(handler->fd, &writefds)) { 240 event_types |= NETIO_EVENT_WRITE; 241 FD_CLR(handler->fd, &writefds); 242 rc--; 243 } 244 if (FD_ISSET(handler->fd, &exceptfds)) { 245 event_types |= NETIO_EVENT_EXCEPT; 246 FD_CLR(handler->fd, &exceptfds); 247 rc--; 248 } 249 250 if (event_types & handler->event_types) { 251 handler->event_handler(netio, handler, event_types & handler->event_types); 252 ++result; 253 } 254 } 255 elt = netio->dispatch_next; 256 } 257 netio->dispatch_next = NULL; 258 } 259 260 return result; 261 } 262