17c478bd9Sstevel@tonic-gate /* 2*9525b14bSRao Shoaib * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 3*9525b14bSRao Shoaib * Copyright (c) 1995-1999 by Internet Software Consortium 47c478bd9Sstevel@tonic-gate * 57c478bd9Sstevel@tonic-gate * Permission to use, copy, modify, and distribute this software for any 67c478bd9Sstevel@tonic-gate * purpose with or without fee is hereby granted, provided that the above 77c478bd9Sstevel@tonic-gate * copyright notice and this permission notice appear in all copies. 87c478bd9Sstevel@tonic-gate * 9*9525b14bSRao Shoaib * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 10*9525b14bSRao Shoaib * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11*9525b14bSRao Shoaib * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 12*9525b14bSRao Shoaib * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13*9525b14bSRao Shoaib * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14*9525b14bSRao Shoaib * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 15*9525b14bSRao Shoaib * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 167c478bd9Sstevel@tonic-gate */ 177c478bd9Sstevel@tonic-gate 187c478bd9Sstevel@tonic-gate /* ev_files.c - implement asynch file IO for the eventlib 197c478bd9Sstevel@tonic-gate * vix 11sep95 [initial] 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate 227c478bd9Sstevel@tonic-gate #if !defined(LINT) && !defined(CODECENTER) 23*9525b14bSRao Shoaib static const char rcsid[] = "$Id: ev_files.c,v 1.8 2005/07/28 06:51:48 marka Exp $"; 247c478bd9Sstevel@tonic-gate #endif 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include "port_before.h" 277c478bd9Sstevel@tonic-gate #include "fd_setsize.h" 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate #include <sys/types.h> 307c478bd9Sstevel@tonic-gate #include <sys/time.h> 317c478bd9Sstevel@tonic-gate #include <sys/ioctl.h> 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate #include <errno.h> 347c478bd9Sstevel@tonic-gate #include <fcntl.h> 357c478bd9Sstevel@tonic-gate #include <unistd.h> 367c478bd9Sstevel@tonic-gate 377c478bd9Sstevel@tonic-gate #include <isc/eventlib.h> 387c478bd9Sstevel@tonic-gate #include "eventlib_p.h" 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate #include "port_after.h" 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate static evFile *FindFD(const evContext_p *ctx, int fd, int eventmask); 437c478bd9Sstevel@tonic-gate 447c478bd9Sstevel@tonic-gate int 457c478bd9Sstevel@tonic-gate evSelectFD(evContext opaqueCtx, 467c478bd9Sstevel@tonic-gate int fd, 477c478bd9Sstevel@tonic-gate int eventmask, 487c478bd9Sstevel@tonic-gate evFileFunc func, 497c478bd9Sstevel@tonic-gate void *uap, 507c478bd9Sstevel@tonic-gate evFileID *opaqueID 517c478bd9Sstevel@tonic-gate ) { 527c478bd9Sstevel@tonic-gate evContext_p *ctx = opaqueCtx.opaque; 537c478bd9Sstevel@tonic-gate evFile *id; 547c478bd9Sstevel@tonic-gate int mode; 557c478bd9Sstevel@tonic-gate 567c478bd9Sstevel@tonic-gate evPrintf(ctx, 1, 577c478bd9Sstevel@tonic-gate "evSelectFD(ctx %p, fd %d, mask 0x%x, func %p, uap %p)\n", 587c478bd9Sstevel@tonic-gate ctx, fd, eventmask, func, uap); 597c478bd9Sstevel@tonic-gate if (eventmask == 0 || (eventmask & ~EV_MASK_ALL) != 0) 607c478bd9Sstevel@tonic-gate EV_ERR(EINVAL); 61*9525b14bSRao Shoaib #ifndef USE_POLL 627c478bd9Sstevel@tonic-gate if (fd > ctx->highestFD) 637c478bd9Sstevel@tonic-gate EV_ERR(EINVAL); 64*9525b14bSRao Shoaib #endif 65*9525b14bSRao Shoaib OK(mode = fcntl(fd, F_GETFL, NULL)); /*%< side effect: validate fd. */ 667c478bd9Sstevel@tonic-gate /* 677c478bd9Sstevel@tonic-gate * The first time we touch a file descriptor, we need to check to see 687c478bd9Sstevel@tonic-gate * if the application already had it in O_NONBLOCK mode and if so, all 697c478bd9Sstevel@tonic-gate * of our deselect()'s have to leave it in O_NONBLOCK. If not, then 707c478bd9Sstevel@tonic-gate * all but our last deselect() has to leave it in O_NONBLOCK. 717c478bd9Sstevel@tonic-gate */ 72*9525b14bSRao Shoaib #ifdef USE_POLL 737c478bd9Sstevel@tonic-gate /* Make sure both ctx->pollfds[] and ctx->fdTable[] are large enough */ 74*9525b14bSRao Shoaib if (fd >= ctx->maxnfds && evPollfdRealloc(ctx, 1, fd) != 0) 75*9525b14bSRao Shoaib EV_ERR(ENOMEM); 76*9525b14bSRao Shoaib #endif /* USE_POLL */ 777c478bd9Sstevel@tonic-gate id = FindFD(ctx, fd, EV_MASK_ALL); 787c478bd9Sstevel@tonic-gate if (id == NULL) { 797c478bd9Sstevel@tonic-gate if (mode & PORT_NONBLOCK) 807c478bd9Sstevel@tonic-gate FD_SET(fd, &ctx->nonblockBefore); 817c478bd9Sstevel@tonic-gate else { 827c478bd9Sstevel@tonic-gate #ifdef USE_FIONBIO_IOCTL 837c478bd9Sstevel@tonic-gate int on = 1; 847c478bd9Sstevel@tonic-gate OK(ioctl(fd, FIONBIO, (char *)&on)); 857c478bd9Sstevel@tonic-gate #else 867c478bd9Sstevel@tonic-gate OK(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK)); 877c478bd9Sstevel@tonic-gate #endif 887c478bd9Sstevel@tonic-gate FD_CLR(fd, &ctx->nonblockBefore); 897c478bd9Sstevel@tonic-gate } 907c478bd9Sstevel@tonic-gate } 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate /* 937c478bd9Sstevel@tonic-gate * If this descriptor is already in use, search for it again to see 947c478bd9Sstevel@tonic-gate * if any of the eventmask bits we want to set are already captured. 957c478bd9Sstevel@tonic-gate * We cannot usefully capture the same fd event more than once in the 967c478bd9Sstevel@tonic-gate * same context. 977c478bd9Sstevel@tonic-gate */ 987c478bd9Sstevel@tonic-gate if (id != NULL && FindFD(ctx, fd, eventmask) != NULL) 997c478bd9Sstevel@tonic-gate EV_ERR(ETOOMANYREFS); 1007c478bd9Sstevel@tonic-gate 1017c478bd9Sstevel@tonic-gate /* Allocate and fill. */ 1027c478bd9Sstevel@tonic-gate OKNEW(id); 1037c478bd9Sstevel@tonic-gate id->func = func; 1047c478bd9Sstevel@tonic-gate id->uap = uap; 1057c478bd9Sstevel@tonic-gate id->fd = fd; 1067c478bd9Sstevel@tonic-gate id->eventmask = eventmask; 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate /* 1097c478bd9Sstevel@tonic-gate * Insert at head. Order could be important for performance if we 1107c478bd9Sstevel@tonic-gate * believe that evGetNext()'s accesses to the fd_sets will be more 1117c478bd9Sstevel@tonic-gate * serial and therefore more cache-lucky if the list is ordered by 1127c478bd9Sstevel@tonic-gate * ``fd.'' We do not believe these things, so we don't do it. 1137c478bd9Sstevel@tonic-gate * 1147c478bd9Sstevel@tonic-gate * The interesting sequence is where GetNext() has cached a select() 1157c478bd9Sstevel@tonic-gate * result and the caller decides to evSelectFD() on some descriptor. 1167c478bd9Sstevel@tonic-gate * Since GetNext() starts at the head, it can miss new entries we add 1177c478bd9Sstevel@tonic-gate * at the head. This is not a serious problem since the event being 1187c478bd9Sstevel@tonic-gate * evSelectFD()'d for has to occur before evSelectFD() is called for 1197c478bd9Sstevel@tonic-gate * the file event to be considered "missed" -- a real corner case. 1207c478bd9Sstevel@tonic-gate * Maintaining a "tail" pointer for ctx->files would fix this, but I'm 1217c478bd9Sstevel@tonic-gate * not sure it would be ``more correct.'' 1227c478bd9Sstevel@tonic-gate */ 1237c478bd9Sstevel@tonic-gate if (ctx->files != NULL) 1247c478bd9Sstevel@tonic-gate ctx->files->prev = id; 1257c478bd9Sstevel@tonic-gate id->prev = NULL; 1267c478bd9Sstevel@tonic-gate id->next = ctx->files; 1277c478bd9Sstevel@tonic-gate ctx->files = id; 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate /* Insert into fd table. */ 1307c478bd9Sstevel@tonic-gate if (ctx->fdTable[fd] != NULL) 1317c478bd9Sstevel@tonic-gate ctx->fdTable[fd]->fdprev = id; 1327c478bd9Sstevel@tonic-gate id->fdprev = NULL; 1337c478bd9Sstevel@tonic-gate id->fdnext = ctx->fdTable[fd]; 1347c478bd9Sstevel@tonic-gate ctx->fdTable[fd] = id; 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate /* Turn on the appropriate bits in the {rd,wr,ex}Next fd_set's. */ 1377c478bd9Sstevel@tonic-gate if (eventmask & EV_READ) 1387c478bd9Sstevel@tonic-gate FD_SET(fd, &ctx->rdNext); 1397c478bd9Sstevel@tonic-gate if (eventmask & EV_WRITE) 1407c478bd9Sstevel@tonic-gate FD_SET(fd, &ctx->wrNext); 1417c478bd9Sstevel@tonic-gate if (eventmask & EV_EXCEPT) 1427c478bd9Sstevel@tonic-gate FD_SET(fd, &ctx->exNext); 1437c478bd9Sstevel@tonic-gate 1447c478bd9Sstevel@tonic-gate /* Update fdMax. */ 1457c478bd9Sstevel@tonic-gate if (fd > ctx->fdMax) 1467c478bd9Sstevel@tonic-gate ctx->fdMax = fd; 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate /* Remember the ID if the caller provided us a place for it. */ 1497c478bd9Sstevel@tonic-gate if (opaqueID) 1507c478bd9Sstevel@tonic-gate opaqueID->opaque = id; 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate return (0); 1537c478bd9Sstevel@tonic-gate } 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate int 1567c478bd9Sstevel@tonic-gate evDeselectFD(evContext opaqueCtx, evFileID opaqueID) { 1577c478bd9Sstevel@tonic-gate evContext_p *ctx = opaqueCtx.opaque; 1587c478bd9Sstevel@tonic-gate evFile *del = opaqueID.opaque; 1597c478bd9Sstevel@tonic-gate evFile *cur; 1607c478bd9Sstevel@tonic-gate int mode, eventmask; 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate if (!del) { 1637c478bd9Sstevel@tonic-gate evPrintf(ctx, 11, "evDeselectFD(NULL) ignored\n"); 1647c478bd9Sstevel@tonic-gate errno = EINVAL; 1657c478bd9Sstevel@tonic-gate return (-1); 1667c478bd9Sstevel@tonic-gate } 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate evPrintf(ctx, 1, "evDeselectFD(fd %d, mask 0x%x)\n", 1697c478bd9Sstevel@tonic-gate del->fd, del->eventmask); 1707c478bd9Sstevel@tonic-gate 1717c478bd9Sstevel@tonic-gate /* Get the mode. Unless the file has been closed, errors are bad. */ 1727c478bd9Sstevel@tonic-gate mode = fcntl(del->fd, F_GETFL, NULL); 1737c478bd9Sstevel@tonic-gate if (mode == -1 && errno != EBADF) 1747c478bd9Sstevel@tonic-gate EV_ERR(errno); 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate /* Remove from the list of files. */ 1777c478bd9Sstevel@tonic-gate if (del->prev != NULL) 1787c478bd9Sstevel@tonic-gate del->prev->next = del->next; 1797c478bd9Sstevel@tonic-gate else 1807c478bd9Sstevel@tonic-gate ctx->files = del->next; 1817c478bd9Sstevel@tonic-gate if (del->next != NULL) 1827c478bd9Sstevel@tonic-gate del->next->prev = del->prev; 1837c478bd9Sstevel@tonic-gate 1847c478bd9Sstevel@tonic-gate /* Remove from the fd table. */ 1857c478bd9Sstevel@tonic-gate if (del->fdprev != NULL) 1867c478bd9Sstevel@tonic-gate del->fdprev->fdnext = del->fdnext; 1877c478bd9Sstevel@tonic-gate else 1887c478bd9Sstevel@tonic-gate ctx->fdTable[del->fd] = del->fdnext; 1897c478bd9Sstevel@tonic-gate if (del->fdnext != NULL) 1907c478bd9Sstevel@tonic-gate del->fdnext->fdprev = del->fdprev; 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate /* 1937c478bd9Sstevel@tonic-gate * If the file descriptor does not appear in any other select() entry, 1947c478bd9Sstevel@tonic-gate * and if !EV_WASNONBLOCK, and if we got no EBADF when we got the mode 1957c478bd9Sstevel@tonic-gate * earlier, then: restore the fd to blocking status. 1967c478bd9Sstevel@tonic-gate */ 1977c478bd9Sstevel@tonic-gate if (!(cur = FindFD(ctx, del->fd, EV_MASK_ALL)) && 1987c478bd9Sstevel@tonic-gate !FD_ISSET(del->fd, &ctx->nonblockBefore) && 1997c478bd9Sstevel@tonic-gate mode != -1) { 2007c478bd9Sstevel@tonic-gate /* 2017c478bd9Sstevel@tonic-gate * Note that we won't return an error status to the caller if 2027c478bd9Sstevel@tonic-gate * this fcntl() fails since (a) we've already done the work 2037c478bd9Sstevel@tonic-gate * and (b) the caller didn't ask us anything about O_NONBLOCK. 2047c478bd9Sstevel@tonic-gate */ 2057c478bd9Sstevel@tonic-gate #ifdef USE_FIONBIO_IOCTL 206*9525b14bSRao Shoaib int off = 0; 2077c478bd9Sstevel@tonic-gate (void) ioctl(del->fd, FIONBIO, (char *)&off); 2087c478bd9Sstevel@tonic-gate #else 2097c478bd9Sstevel@tonic-gate (void) fcntl(del->fd, F_SETFL, mode & ~PORT_NONBLOCK); 2107c478bd9Sstevel@tonic-gate #endif 2117c478bd9Sstevel@tonic-gate } 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate /* 2147c478bd9Sstevel@tonic-gate * Now find all other uses of this descriptor and OR together an event 2157c478bd9Sstevel@tonic-gate * mask so that we don't turn off {rd,wr,ex}Next bits that some other 2167c478bd9Sstevel@tonic-gate * file event is using. As an optimization, stop if the event mask 2177c478bd9Sstevel@tonic-gate * fills. 2187c478bd9Sstevel@tonic-gate */ 2197c478bd9Sstevel@tonic-gate eventmask = 0; 2207c478bd9Sstevel@tonic-gate for ((void)NULL; 2217c478bd9Sstevel@tonic-gate cur != NULL && eventmask != EV_MASK_ALL; 2227c478bd9Sstevel@tonic-gate cur = cur->next) 2237c478bd9Sstevel@tonic-gate if (cur->fd == del->fd) 2247c478bd9Sstevel@tonic-gate eventmask |= cur->eventmask; 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate /* OK, now we know which bits we can clear out. */ 2277c478bd9Sstevel@tonic-gate if (!(eventmask & EV_READ)) { 2287c478bd9Sstevel@tonic-gate FD_CLR(del->fd, &ctx->rdNext); 2297c478bd9Sstevel@tonic-gate if (FD_ISSET(del->fd, &ctx->rdLast)) { 2307c478bd9Sstevel@tonic-gate FD_CLR(del->fd, &ctx->rdLast); 2317c478bd9Sstevel@tonic-gate ctx->fdCount--; 2327c478bd9Sstevel@tonic-gate } 2337c478bd9Sstevel@tonic-gate } 2347c478bd9Sstevel@tonic-gate if (!(eventmask & EV_WRITE)) { 2357c478bd9Sstevel@tonic-gate FD_CLR(del->fd, &ctx->wrNext); 2367c478bd9Sstevel@tonic-gate if (FD_ISSET(del->fd, &ctx->wrLast)) { 2377c478bd9Sstevel@tonic-gate FD_CLR(del->fd, &ctx->wrLast); 2387c478bd9Sstevel@tonic-gate ctx->fdCount--; 2397c478bd9Sstevel@tonic-gate } 2407c478bd9Sstevel@tonic-gate } 2417c478bd9Sstevel@tonic-gate if (!(eventmask & EV_EXCEPT)) { 2427c478bd9Sstevel@tonic-gate FD_CLR(del->fd, &ctx->exNext); 2437c478bd9Sstevel@tonic-gate if (FD_ISSET(del->fd, &ctx->exLast)) { 2447c478bd9Sstevel@tonic-gate FD_CLR(del->fd, &ctx->exLast); 2457c478bd9Sstevel@tonic-gate ctx->fdCount--; 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate /* If this was the maxFD, find the new one. */ 2507c478bd9Sstevel@tonic-gate if (del->fd == ctx->fdMax) { 2517c478bd9Sstevel@tonic-gate ctx->fdMax = -1; 2527c478bd9Sstevel@tonic-gate for (cur = ctx->files; cur; cur = cur->next) 2537c478bd9Sstevel@tonic-gate if (cur->fd > ctx->fdMax) 2547c478bd9Sstevel@tonic-gate ctx->fdMax = cur->fd; 2557c478bd9Sstevel@tonic-gate } 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate /* If this was the fdNext, cycle that to the next entry. */ 2587c478bd9Sstevel@tonic-gate if (del == ctx->fdNext) 2597c478bd9Sstevel@tonic-gate ctx->fdNext = del->next; 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate /* Couldn't free it before now since we were using fields out of it. */ 2627c478bd9Sstevel@tonic-gate FREE(del); 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate return (0); 2657c478bd9Sstevel@tonic-gate } 2667c478bd9Sstevel@tonic-gate 2677c478bd9Sstevel@tonic-gate static evFile * 2687c478bd9Sstevel@tonic-gate FindFD(const evContext_p *ctx, int fd, int eventmask) { 2697c478bd9Sstevel@tonic-gate evFile *id; 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate for (id = ctx->fdTable[fd]; id != NULL; id = id->fdnext) 2727c478bd9Sstevel@tonic-gate if (id->fd == fd && (id->eventmask & eventmask) != 0) 2737c478bd9Sstevel@tonic-gate break; 2747c478bd9Sstevel@tonic-gate return (id); 2757c478bd9Sstevel@tonic-gate } 276*9525b14bSRao Shoaib 277*9525b14bSRao Shoaib /*! \file */ 278