Logo Search packages:      
Sourcecode: fair version File versions  Download package

fd.c

/******************************************************************************

      fd.c -- UNIX file descriptor bookkeeping and handling
      Copyright (C) 2004  Wessel Dankers <wsl@uvt.nl>

      This program is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      This program is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with this program; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

      $Id: fd.c 23144 2007-12-21 15:36:48Z wsl $
      $URL: https://infix.uvt.nl/its-id/trunk/sources/fair-0.5.1/src/fd.c $

******************************************************************************/

#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>

#include <avl.h>

#include "fair.h"
#include "chrono.h"
#include "error.h"
#include "fd.h"

static int fd_cmp(const fd_t *a, const fd_t *b) {
      return a->fd - b->fd;
}

static bool fds_changed = FALSE;
static avl_tree_t fds = {NULL, NULL, NULL, (avl_compare_t)fd_cmp, NULL};

static const fd_t fd_0 = {{0}};

unsigned int fd_count(void) {
      return avl_count(&fds);
}

fd_t *fd_new(int fd) {
      int f;
      unless(fd != -1) return NULL;
      f = fcntl(fd, F_GETFL);
      if(f == -1)
            return syslog(LOG_ERR, "fcntl(%d, F_GETFL): %m", fd), NULL;
      if(!(f & O_NONBLOCK)) {
            f = fcntl(fd, F_SETFL, f | O_NONBLOCK);
            if(f == -1)
                  return syslog(LOG_ERR, "fcntl(%d, F_SETFL, O_NONBLOCK): %m", fd), NULL;
      }
      fd_t *r = xalloc(sizeof *r);
      *r = fd_0;
      avl_init_node(&r->node, r);
      avl_insert_node(&fds, &r->node);
      r->fd = fd;
      fds_changed = TRUE;
      return r;
}

void fd_close(fd_t *victim) {
      unless(victim) return;
      if(close(victim->fd) == -1)
            syslog(LOG_ERR, "close(): %m (internal error).");
      fd_delete(victim);
}

void fd_delete(fd_t *victim) {
      unless(victim) return;
      avl_unlink_node(&fds, &victim->node);
      free(victim);
      fds_changed = TRUE;
}

void fd_read(fd_t *fd, fd_hook_t func, void *data) {
      unless(fd) return;
      fd->rfunc = func;
      fd->rdata = data;
}

void fd_write(fd_t *fd, fd_hook_t func, void *data) {
      unless(fd) return;
      fd->wfunc = func;
      fd->wdata = data;
}

void fd_oob(fd_t *fd, fd_hook_t func, void *data) {
      unless(fd) return;
      fd->ofunc = func;
      fd->odata = data;
}

fd_t *fd_byfd(int key) {
      avl_node_t *n;
      fd_t reference;
      reference.fd = key;
      n = avl_search(&fds, &reference);
      return n ? n->item : NULL;
}

static int build_fdsets(fd_set *rfds, fd_set *wfds, fd_set *ofds) {
      avl_node_t *n;
      fd_t *fdinfo;
      int fd, maxfd = -1;

      assert(rfds && wfds && ofds);

      FD_ZERO(rfds);
      FD_ZERO(wfds);
      FD_ZERO(ofds);

      for(n = fds.head; n; n = n->next) {
            fdinfo = n->item;
            unless(fdinfo)
                  continue;
            fd = fdinfo->fd;
            unless(fd != -1) continue;
            if(fdinfo->rfunc)
                  FD_SET(fd, rfds);
            if(fdinfo->wfunc)
                  FD_SET(fd, wfds);
            if(fdinfo->ofunc)
                  FD_SET(fd, ofds);
            if(fd > maxfd)
                  maxfd = fd;
      }

      return maxfd;
}

static void handle_fdsets(fd_set *rfds, fd_set *wfds, fd_set *ofds) {
      avl_node_t *n;
      fd_t *fdinfo;
      int fd;

      assert(rfds && wfds && ofds);

      for(n = fds.head; n; n = fds_changed ? fds.head : n->next) {
            fds_changed = FALSE;
            fdinfo = n->item;
            unless(fdinfo)
                  continue;
            fd = fdinfo->fd;
            unless(fd != -1) continue;
            if(FD_ISSET(fd, rfds) && fdinfo->rfunc)
                  fdinfo->rfunc(fdinfo, FD_EVENT_READ);
            if(FD_ISSET(fd, wfds) && fdinfo->wfunc)
                  fdinfo->wfunc(fdinfo, FD_EVENT_WRITE);
            if(FD_ISSET(fd, ofds) && fdinfo->ofunc)
                  fdinfo->ofunc(fdinfo, FD_EVENT_OOB);
            FD_CLR(fd, rfds);
            FD_CLR(fd, wfds);
            FD_CLR(fd, ofds);
      }
}

int fd_select(stamp_t timeout) {
      fd_set rfds, wfds, ofds;
      struct timeval tv;
      int r, maxfd;

      maxfd = build_fdsets(&rfds, &wfds, &ofds);
      assert(maxfd < FD_SETSIZE);

      if(timeout)
            stamp_tv(&tv, timeout);
      r = select(maxfd + 1, &rfds, &wfds, &ofds, timeout ? &tv : NULL);
      if(r == -1 && errno != EINTR)
            syslog_exit(LOG_CRIT, "select(): %m. Program exit.");

      stamp_sync();

      if(r > 0)
            handle_fdsets(&rfds, &wfds, &ofds);

      return r;
}

Generated by  Doxygen 1.6.0   Back to index