1 /* $OpenBSD: select.c,v 1.5 2003/07/09 10:54:38 markus Exp $ */ 2 3 /* 4 * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Niels Provos. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 #ifdef HAVE_CONFIG_H 33 #include "config.h" 34 #endif 35 36 #include <sys/types.h> 37 #ifdef HAVE_SYS_TIME_H 38 #include <sys/time.h> 39 #else 40 #include <sys/_time.h> 41 #endif 42 #include <sys/queue.h> 43 #include <signal.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 #include <errno.h> 49 #include <err.h> 50 51 #ifdef USE_LOG 52 #include "log.h" 53 #else 54 #define LOG_DBG(x) 55 #define log_error(x) perror(x) 56 #endif 57 58 #include "event.h" 59 #include "evsignal.h" 60 61 extern struct event_list eventqueue; 62 63 #ifndef howmany 64 #define howmany(x, y) (((x)+((y)-1))/(y)) 65 #endif 66 67 extern volatile sig_atomic_t evsignal_caught; 68 69 struct selectop { 70 int event_fds; /* Highest fd in fd set */ 71 int event_fdsz; 72 fd_set *event_readset; 73 fd_set *event_writeset; 74 sigset_t evsigmask; 75 } sop; 76 77 void *select_init (void); 78 int select_add (void *, struct event *); 79 int select_del (void *, struct event *); 80 int select_recalc (void *, int); 81 int select_dispatch (void *, struct timeval *); 82 83 const struct eventop selectops = { 84 "select", 85 select_init, 86 select_add, 87 select_del, 88 select_recalc, 89 select_dispatch 90 }; 91 92 void * 93 select_init(void) 94 { 95 /* Disable kqueue when this environment variable is set */ 96 if (!issetugid() && getenv("EVENT_NOSELECT")) 97 return (NULL); 98 99 memset(&sop, 0, sizeof(sop)); 100 101 evsignal_init(&sop.evsigmask); 102 103 return (&sop); 104 } 105 106 /* 107 * Called with the highest fd that we know about. If it is 0, completely 108 * recalculate everything. 109 */ 110 111 int 112 select_recalc(void *arg, int max) 113 { 114 struct selectop *sop = arg; 115 fd_set *readset, *writeset; 116 struct event *ev; 117 int fdsz; 118 119 if (sop->event_fds < max) 120 sop->event_fds = max; 121 122 if (!sop->event_fds) { 123 TAILQ_FOREACH(ev, &eventqueue, ev_next) 124 if (ev->ev_fd > sop->event_fds) 125 sop->event_fds = ev->ev_fd; 126 } 127 128 fdsz = howmany(sop->event_fds + 1, NFDBITS) * sizeof(fd_mask); 129 if (fdsz > sop->event_fdsz) { 130 if ((readset = realloc(sop->event_readset, fdsz)) == NULL) { 131 log_error("malloc"); 132 return (-1); 133 } 134 135 if ((writeset = realloc(sop->event_writeset, fdsz)) == NULL) { 136 log_error("malloc"); 137 free(readset); 138 return (-1); 139 } 140 141 memset((char *)readset + sop->event_fdsz, 0, 142 fdsz - sop->event_fdsz); 143 memset((char *)writeset + sop->event_fdsz, 0, 144 fdsz - sop->event_fdsz); 145 146 sop->event_readset = readset; 147 sop->event_writeset = writeset; 148 sop->event_fdsz = fdsz; 149 } 150 151 return (evsignal_recalc(&sop->evsigmask)); 152 } 153 154 int 155 select_dispatch(void *arg, struct timeval *tv) 156 { 157 int maxfd, res; 158 struct event *ev, *next; 159 struct selectop *sop = arg; 160 161 memset(sop->event_readset, 0, sop->event_fdsz); 162 memset(sop->event_writeset, 0, sop->event_fdsz); 163 164 TAILQ_FOREACH(ev, &eventqueue, ev_next) { 165 if (ev->ev_events & EV_WRITE) 166 FD_SET(ev->ev_fd, sop->event_writeset); 167 if (ev->ev_events & EV_READ) 168 FD_SET(ev->ev_fd, sop->event_readset); 169 } 170 171 if (evsignal_deliver(&sop->evsigmask) == -1) 172 return (-1); 173 174 res = select(sop->event_fds + 1, sop->event_readset, 175 sop->event_writeset, NULL, tv); 176 177 if (evsignal_recalc(&sop->evsigmask) == -1) 178 return (-1); 179 180 if (res == -1) { 181 if (errno != EINTR) { 182 log_error("select"); 183 return (-1); 184 } 185 186 evsignal_process(); 187 return (0); 188 } else if (evsignal_caught) 189 evsignal_process(); 190 191 LOG_DBG((LOG_MISC, 80, "%s: select reports %d", __func__, res)); 192 193 maxfd = 0; 194 for (ev = TAILQ_FIRST(&eventqueue); ev != NULL; ev = next) { 195 next = TAILQ_NEXT(ev, ev_next); 196 197 res = 0; 198 if (FD_ISSET(ev->ev_fd, sop->event_readset)) 199 res |= EV_READ; 200 if (FD_ISSET(ev->ev_fd, sop->event_writeset)) 201 res |= EV_WRITE; 202 res &= ev->ev_events; 203 204 if (res) { 205 if (!(ev->ev_events & EV_PERSIST)) 206 event_del(ev); 207 event_active(ev, res, 1); 208 } else if (ev->ev_fd > maxfd) 209 maxfd = ev->ev_fd; 210 } 211 212 sop->event_fds = maxfd; 213 214 return (0); 215 } 216 217 int 218 select_add(void *arg, struct event *ev) 219 { 220 struct selectop *sop = arg; 221 222 if (ev->ev_events & EV_SIGNAL) 223 return (evsignal_add(&sop->evsigmask, ev)); 224 225 /* 226 * Keep track of the highest fd, so that we can calculate the size 227 * of the fd_sets for select(2) 228 */ 229 if (sop->event_fds < ev->ev_fd) 230 sop->event_fds = ev->ev_fd; 231 232 return (0); 233 } 234 235 /* 236 * Nothing to be done here. 237 */ 238 239 int 240 select_del(void *arg, struct event *ev) 241 { 242 struct selectop *sop = arg; 243 244 if (!(ev->ev_events & EV_SIGNAL)) 245 return (0); 246 247 return (evsignal_del(&sop->evsigmask, ev)); 248 } 249