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