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