1 /* 2 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 3 * Copyright (c) 1995-1999 by Internet Software Consortium 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 15 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /* ev_files.c - implement asynch file IO for the eventlib 19 * vix 11sep95 [initial] 20 */ 21 22 #if !defined(LINT) && !defined(CODECENTER) 23 static const char rcsid[] = "$Id: ev_files.c,v 1.8 2005/07/28 06:51:48 marka Exp $"; 24 #endif 25 26 #include "port_before.h" 27 #include "fd_setsize.h" 28 29 #include <sys/types.h> 30 #include <sys/time.h> 31 #include <sys/ioctl.h> 32 33 #include <errno.h> 34 #include <fcntl.h> 35 #include <unistd.h> 36 37 #include <isc/eventlib.h> 38 #include "eventlib_p.h" 39 40 #include "port_after.h" 41 42 static evFile *FindFD(const evContext_p *ctx, int fd, int eventmask); 43 44 int 45 evSelectFD(evContext opaqueCtx, 46 int fd, 47 int eventmask, 48 evFileFunc func, 49 void *uap, 50 evFileID *opaqueID 51 ) { 52 evContext_p *ctx = opaqueCtx.opaque; 53 evFile *id; 54 int mode; 55 56 evPrintf(ctx, 1, 57 "evSelectFD(ctx %p, fd %d, mask 0x%x, func %p, uap %p)\n", 58 ctx, fd, eventmask, func, uap); 59 if (eventmask == 0 || (eventmask & ~EV_MASK_ALL) != 0) 60 EV_ERR(EINVAL); 61 #ifndef USE_POLL 62 if (fd > ctx->highestFD) 63 EV_ERR(EINVAL); 64 #endif 65 OK(mode = fcntl(fd, F_GETFL, NULL)); /*%< side effect: validate fd. */ 66 /* 67 * The first time we touch a file descriptor, we need to check to see 68 * if the application already had it in O_NONBLOCK mode and if so, all 69 * of our deselect()'s have to leave it in O_NONBLOCK. If not, then 70 * all but our last deselect() has to leave it in O_NONBLOCK. 71 */ 72 #ifdef USE_POLL 73 /* Make sure both ctx->pollfds[] and ctx->fdTable[] are large enough */ 74 if (fd >= ctx->maxnfds && evPollfdRealloc(ctx, 1, fd) != 0) 75 EV_ERR(ENOMEM); 76 #endif /* USE_POLL */ 77 id = FindFD(ctx, fd, EV_MASK_ALL); 78 if (id == NULL) { 79 if (mode & PORT_NONBLOCK) 80 FD_SET(fd, &ctx->nonblockBefore); 81 else { 82 #ifdef USE_FIONBIO_IOCTL 83 int on = 1; 84 OK(ioctl(fd, FIONBIO, (char *)&on)); 85 #else 86 OK(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK)); 87 #endif 88 FD_CLR(fd, &ctx->nonblockBefore); 89 } 90 } 91 92 /* 93 * If this descriptor is already in use, search for it again to see 94 * if any of the eventmask bits we want to set are already captured. 95 * We cannot usefully capture the same fd event more than once in the 96 * same context. 97 */ 98 if (id != NULL && FindFD(ctx, fd, eventmask) != NULL) 99 EV_ERR(ETOOMANYREFS); 100 101 /* Allocate and fill. */ 102 OKNEW(id); 103 id->func = func; 104 id->uap = uap; 105 id->fd = fd; 106 id->eventmask = eventmask; 107 108 /* 109 * Insert at head. Order could be important for performance if we 110 * believe that evGetNext()'s accesses to the fd_sets will be more 111 * serial and therefore more cache-lucky if the list is ordered by 112 * ``fd.'' We do not believe these things, so we don't do it. 113 * 114 * The interesting sequence is where GetNext() has cached a select() 115 * result and the caller decides to evSelectFD() on some descriptor. 116 * Since GetNext() starts at the head, it can miss new entries we add 117 * at the head. This is not a serious problem since the event being 118 * evSelectFD()'d for has to occur before evSelectFD() is called for 119 * the file event to be considered "missed" -- a real corner case. 120 * Maintaining a "tail" pointer for ctx->files would fix this, but I'm 121 * not sure it would be ``more correct.'' 122 */ 123 if (ctx->files != NULL) 124 ctx->files->prev = id; 125 id->prev = NULL; 126 id->next = ctx->files; 127 ctx->files = id; 128 129 /* Insert into fd table. */ 130 if (ctx->fdTable[fd] != NULL) 131 ctx->fdTable[fd]->fdprev = id; 132 id->fdprev = NULL; 133 id->fdnext = ctx->fdTable[fd]; 134 ctx->fdTable[fd] = id; 135 136 /* Turn on the appropriate bits in the {rd,wr,ex}Next fd_set's. */ 137 if (eventmask & EV_READ) 138 FD_SET(fd, &ctx->rdNext); 139 if (eventmask & EV_WRITE) 140 FD_SET(fd, &ctx->wrNext); 141 if (eventmask & EV_EXCEPT) 142 FD_SET(fd, &ctx->exNext); 143 144 /* Update fdMax. */ 145 if (fd > ctx->fdMax) 146 ctx->fdMax = fd; 147 148 /* Remember the ID if the caller provided us a place for it. */ 149 if (opaqueID) 150 opaqueID->opaque = id; 151 152 return (0); 153 } 154 155 int 156 evDeselectFD(evContext opaqueCtx, evFileID opaqueID) { 157 evContext_p *ctx = opaqueCtx.opaque; 158 evFile *del = opaqueID.opaque; 159 evFile *cur; 160 int mode, eventmask; 161 162 if (!del) { 163 evPrintf(ctx, 11, "evDeselectFD(NULL) ignored\n"); 164 errno = EINVAL; 165 return (-1); 166 } 167 168 evPrintf(ctx, 1, "evDeselectFD(fd %d, mask 0x%x)\n", 169 del->fd, del->eventmask); 170 171 /* Get the mode. Unless the file has been closed, errors are bad. */ 172 mode = fcntl(del->fd, F_GETFL, NULL); 173 if (mode == -1 && errno != EBADF) 174 EV_ERR(errno); 175 176 /* Remove from the list of files. */ 177 if (del->prev != NULL) 178 del->prev->next = del->next; 179 else 180 ctx->files = del->next; 181 if (del->next != NULL) 182 del->next->prev = del->prev; 183 184 /* Remove from the fd table. */ 185 if (del->fdprev != NULL) 186 del->fdprev->fdnext = del->fdnext; 187 else 188 ctx->fdTable[del->fd] = del->fdnext; 189 if (del->fdnext != NULL) 190 del->fdnext->fdprev = del->fdprev; 191 192 /* 193 * If the file descriptor does not appear in any other select() entry, 194 * and if !EV_WASNONBLOCK, and if we got no EBADF when we got the mode 195 * earlier, then: restore the fd to blocking status. 196 */ 197 if (!(cur = FindFD(ctx, del->fd, EV_MASK_ALL)) && 198 !FD_ISSET(del->fd, &ctx->nonblockBefore) && 199 mode != -1) { 200 /* 201 * Note that we won't return an error status to the caller if 202 * this fcntl() fails since (a) we've already done the work 203 * and (b) the caller didn't ask us anything about O_NONBLOCK. 204 */ 205 #ifdef USE_FIONBIO_IOCTL 206 int off = 0; 207 (void) ioctl(del->fd, FIONBIO, (char *)&off); 208 #else 209 (void) fcntl(del->fd, F_SETFL, mode & ~PORT_NONBLOCK); 210 #endif 211 } 212 213 /* 214 * Now find all other uses of this descriptor and OR together an event 215 * mask so that we don't turn off {rd,wr,ex}Next bits that some other 216 * file event is using. As an optimization, stop if the event mask 217 * fills. 218 */ 219 eventmask = 0; 220 for ((void)NULL; 221 cur != NULL && eventmask != EV_MASK_ALL; 222 cur = cur->next) 223 if (cur->fd == del->fd) 224 eventmask |= cur->eventmask; 225 226 /* OK, now we know which bits we can clear out. */ 227 if (!(eventmask & EV_READ)) { 228 FD_CLR(del->fd, &ctx->rdNext); 229 if (FD_ISSET(del->fd, &ctx->rdLast)) { 230 FD_CLR(del->fd, &ctx->rdLast); 231 ctx->fdCount--; 232 } 233 } 234 if (!(eventmask & EV_WRITE)) { 235 FD_CLR(del->fd, &ctx->wrNext); 236 if (FD_ISSET(del->fd, &ctx->wrLast)) { 237 FD_CLR(del->fd, &ctx->wrLast); 238 ctx->fdCount--; 239 } 240 } 241 if (!(eventmask & EV_EXCEPT)) { 242 FD_CLR(del->fd, &ctx->exNext); 243 if (FD_ISSET(del->fd, &ctx->exLast)) { 244 FD_CLR(del->fd, &ctx->exLast); 245 ctx->fdCount--; 246 } 247 } 248 249 /* If this was the maxFD, find the new one. */ 250 if (del->fd == ctx->fdMax) { 251 ctx->fdMax = -1; 252 for (cur = ctx->files; cur; cur = cur->next) 253 if (cur->fd > ctx->fdMax) 254 ctx->fdMax = cur->fd; 255 } 256 257 /* If this was the fdNext, cycle that to the next entry. */ 258 if (del == ctx->fdNext) 259 ctx->fdNext = del->next; 260 261 /* Couldn't free it before now since we were using fields out of it. */ 262 FREE(del); 263 264 return (0); 265 } 266 267 static evFile * 268 FindFD(const evContext_p *ctx, int fd, int eventmask) { 269 evFile *id; 270 271 for (id = ctx->fdTable[fd]; id != NULL; id = id->fdnext) 272 if (id->fd == fd && (id->eventmask & eventmask) != 0) 273 break; 274 return (id); 275 } 276 277 /*! \file */ 278