2N/A/*
2N/A * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
2N/A * Copyright (c) 1995-1999 by Internet Software Consortium
2N/A *
2N/A * Permission to use, copy, modify, and distribute this software for any
2N/A * purpose with or without fee is hereby granted, provided that the above
2N/A * copyright notice and this permission notice appear in all copies.
2N/A *
2N/A * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
2N/A * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
2N/A * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
2N/A * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2N/A * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2N/A * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
2N/A * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2N/A */
2N/A
2N/A/* ev_files.c - implement asynch file IO for the eventlib
2N/A * vix 11sep95 [initial]
2N/A */
2N/A
2N/A#if !defined(LINT) && !defined(CODECENTER)
2N/Astatic const char rcsid[] = "$Id: ev_files.c,v 1.8 2005/07/28 06:51:48 marka Exp $";
2N/A#endif
2N/A
2N/A#include "port_before.h"
2N/A#include "fd_setsize.h"
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/time.h>
2N/A#include <sys/ioctl.h>
2N/A
2N/A#include <errno.h>
2N/A#include <fcntl.h>
2N/A#include <unistd.h>
2N/A
2N/A#include <isc/eventlib.h>
2N/A#include "eventlib_p.h"
2N/A
2N/A#include "port_after.h"
2N/A
2N/Astatic evFile *FindFD(const evContext_p *ctx, int fd, int eventmask);
2N/A
2N/Aint
2N/AevSelectFD(evContext opaqueCtx,
2N/A int fd,
2N/A int eventmask,
2N/A evFileFunc func,
2N/A void *uap,
2N/A evFileID *opaqueID
2N/A) {
2N/A evContext_p *ctx = opaqueCtx.opaque;
2N/A evFile *id;
2N/A int mode;
2N/A
2N/A evPrintf(ctx, 1,
2N/A "evSelectFD(ctx %p, fd %d, mask 0x%x, func %p, uap %p)\n",
2N/A ctx, fd, eventmask, func, uap);
2N/A if (eventmask == 0 || (eventmask & ~EV_MASK_ALL) != 0)
2N/A EV_ERR(EINVAL);
2N/A#ifndef USE_POLL
2N/A if (fd > ctx->highestFD)
2N/A EV_ERR(EINVAL);
2N/A#endif
2N/A OK(mode = fcntl(fd, F_GETFL, NULL)); /*%< side effect: validate fd. */
2N/A /*
2N/A * The first time we touch a file descriptor, we need to check to see
2N/A * if the application already had it in O_NONBLOCK mode and if so, all
2N/A * of our deselect()'s have to leave it in O_NONBLOCK. If not, then
2N/A * all but our last deselect() has to leave it in O_NONBLOCK.
2N/A */
2N/A#ifdef USE_POLL
2N/A /* Make sure both ctx->pollfds[] and ctx->fdTable[] are large enough */
2N/A if (fd >= ctx->maxnfds && evPollfdRealloc(ctx, 1, fd) != 0)
2N/A EV_ERR(ENOMEM);
2N/A#endif /* USE_POLL */
2N/A id = FindFD(ctx, fd, EV_MASK_ALL);
2N/A if (id == NULL) {
2N/A if (mode & PORT_NONBLOCK)
2N/A FD_SET(fd, &ctx->nonblockBefore);
2N/A else {
2N/A#ifdef USE_FIONBIO_IOCTL
2N/A int on = 1;
2N/A OK(ioctl(fd, FIONBIO, (char *)&on));
2N/A#else
2N/A OK(fcntl(fd, F_SETFL, mode | PORT_NONBLOCK));
2N/A#endif
2N/A FD_CLR(fd, &ctx->nonblockBefore);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If this descriptor is already in use, search for it again to see
2N/A * if any of the eventmask bits we want to set are already captured.
2N/A * We cannot usefully capture the same fd event more than once in the
2N/A * same context.
2N/A */
2N/A if (id != NULL && FindFD(ctx, fd, eventmask) != NULL)
2N/A EV_ERR(ETOOMANYREFS);
2N/A
2N/A /* Allocate and fill. */
2N/A OKNEW(id);
2N/A id->func = func;
2N/A id->uap = uap;
2N/A id->fd = fd;
2N/A id->eventmask = eventmask;
2N/A
2N/A /*
2N/A * Insert at head. Order could be important for performance if we
2N/A * believe that evGetNext()'s accesses to the fd_sets will be more
2N/A * serial and therefore more cache-lucky if the list is ordered by
2N/A * ``fd.'' We do not believe these things, so we don't do it.
2N/A *
2N/A * The interesting sequence is where GetNext() has cached a select()
2N/A * result and the caller decides to evSelectFD() on some descriptor.
2N/A * Since GetNext() starts at the head, it can miss new entries we add
2N/A * at the head. This is not a serious problem since the event being
2N/A * evSelectFD()'d for has to occur before evSelectFD() is called for
2N/A * the file event to be considered "missed" -- a real corner case.
2N/A * Maintaining a "tail" pointer for ctx->files would fix this, but I'm
2N/A * not sure it would be ``more correct.''
2N/A */
2N/A if (ctx->files != NULL)
2N/A ctx->files->prev = id;
2N/A id->prev = NULL;
2N/A id->next = ctx->files;
2N/A ctx->files = id;
2N/A
2N/A /* Insert into fd table. */
2N/A if (ctx->fdTable[fd] != NULL)
2N/A ctx->fdTable[fd]->fdprev = id;
2N/A id->fdprev = NULL;
2N/A id->fdnext = ctx->fdTable[fd];
2N/A ctx->fdTable[fd] = id;
2N/A
2N/A /* Turn on the appropriate bits in the {rd,wr,ex}Next fd_set's. */
2N/A if (eventmask & EV_READ)
2N/A FD_SET(fd, &ctx->rdNext);
2N/A if (eventmask & EV_WRITE)
2N/A FD_SET(fd, &ctx->wrNext);
2N/A if (eventmask & EV_EXCEPT)
2N/A FD_SET(fd, &ctx->exNext);
2N/A
2N/A /* Update fdMax. */
2N/A if (fd > ctx->fdMax)
2N/A ctx->fdMax = fd;
2N/A
2N/A /* Remember the ID if the caller provided us a place for it. */
2N/A if (opaqueID)
2N/A opaqueID->opaque = id;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/AevDeselectFD(evContext opaqueCtx, evFileID opaqueID) {
2N/A evContext_p *ctx = opaqueCtx.opaque;
2N/A evFile *del = opaqueID.opaque;
2N/A evFile *cur;
2N/A int mode, eventmask;
2N/A
2N/A if (!del) {
2N/A evPrintf(ctx, 11, "evDeselectFD(NULL) ignored\n");
2N/A errno = EINVAL;
2N/A return (-1);
2N/A }
2N/A
2N/A evPrintf(ctx, 1, "evDeselectFD(fd %d, mask 0x%x)\n",
2N/A del->fd, del->eventmask);
2N/A
2N/A /* Get the mode. Unless the file has been closed, errors are bad. */
2N/A mode = fcntl(del->fd, F_GETFL, NULL);
2N/A if (mode == -1 && errno != EBADF)
2N/A EV_ERR(errno);
2N/A
2N/A /* Remove from the list of files. */
2N/A if (del->prev != NULL)
2N/A del->prev->next = del->next;
2N/A else
2N/A ctx->files = del->next;
2N/A if (del->next != NULL)
2N/A del->next->prev = del->prev;
2N/A
2N/A /* Remove from the fd table. */
2N/A if (del->fdprev != NULL)
2N/A del->fdprev->fdnext = del->fdnext;
2N/A else
2N/A ctx->fdTable[del->fd] = del->fdnext;
2N/A if (del->fdnext != NULL)
2N/A del->fdnext->fdprev = del->fdprev;
2N/A
2N/A /*
2N/A * If the file descriptor does not appear in any other select() entry,
2N/A * and if !EV_WASNONBLOCK, and if we got no EBADF when we got the mode
2N/A * earlier, then: restore the fd to blocking status.
2N/A */
2N/A if (!(cur = FindFD(ctx, del->fd, EV_MASK_ALL)) &&
2N/A !FD_ISSET(del->fd, &ctx->nonblockBefore) &&
2N/A mode != -1) {
2N/A /*
2N/A * Note that we won't return an error status to the caller if
2N/A * this fcntl() fails since (a) we've already done the work
2N/A * and (b) the caller didn't ask us anything about O_NONBLOCK.
2N/A */
2N/A#ifdef USE_FIONBIO_IOCTL
2N/A int off = 0;
2N/A (void) ioctl(del->fd, FIONBIO, (char *)&off);
2N/A#else
2N/A (void) fcntl(del->fd, F_SETFL, mode & ~PORT_NONBLOCK);
2N/A#endif
2N/A }
2N/A
2N/A /*
2N/A * Now find all other uses of this descriptor and OR together an event
2N/A * mask so that we don't turn off {rd,wr,ex}Next bits that some other
2N/A * file event is using. As an optimization, stop if the event mask
2N/A * fills.
2N/A */
2N/A eventmask = 0;
2N/A for ((void)NULL;
2N/A cur != NULL && eventmask != EV_MASK_ALL;
2N/A cur = cur->next)
2N/A if (cur->fd == del->fd)
2N/A eventmask |= cur->eventmask;
2N/A
2N/A /* OK, now we know which bits we can clear out. */
2N/A if (!(eventmask & EV_READ)) {
2N/A FD_CLR(del->fd, &ctx->rdNext);
2N/A if (FD_ISSET(del->fd, &ctx->rdLast)) {
2N/A FD_CLR(del->fd, &ctx->rdLast);
2N/A ctx->fdCount--;
2N/A }
2N/A }
2N/A if (!(eventmask & EV_WRITE)) {
2N/A FD_CLR(del->fd, &ctx->wrNext);
2N/A if (FD_ISSET(del->fd, &ctx->wrLast)) {
2N/A FD_CLR(del->fd, &ctx->wrLast);
2N/A ctx->fdCount--;
2N/A }
2N/A }
2N/A if (!(eventmask & EV_EXCEPT)) {
2N/A FD_CLR(del->fd, &ctx->exNext);
2N/A if (FD_ISSET(del->fd, &ctx->exLast)) {
2N/A FD_CLR(del->fd, &ctx->exLast);
2N/A ctx->fdCount--;
2N/A }
2N/A }
2N/A
2N/A /* If this was the maxFD, find the new one. */
2N/A if (del->fd == ctx->fdMax) {
2N/A ctx->fdMax = -1;
2N/A for (cur = ctx->files; cur; cur = cur->next)
2N/A if (cur->fd > ctx->fdMax)
2N/A ctx->fdMax = cur->fd;
2N/A }
2N/A
2N/A /* If this was the fdNext, cycle that to the next entry. */
2N/A if (del == ctx->fdNext)
2N/A ctx->fdNext = del->next;
2N/A
2N/A /* Couldn't free it before now since we were using fields out of it. */
2N/A FREE(del);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic evFile *
2N/AFindFD(const evContext_p *ctx, int fd, int eventmask) {
2N/A evFile *id;
2N/A
2N/A for (id = ctx->fdTable[fd]; id != NULL; id = id->fdnext)
2N/A if (id->fd == fd && (id->eventmask & eventmask) != 0)
2N/A break;
2N/A return (id);
2N/A}
2N/A
2N/A/*! \file */