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

scamper_fds.c

/*
 * scamper_fds: manage events and file descriptors
 *
 * $Id: scamper_fds.c,v 1.37 2007/05/13 21:54:40 mjl Exp $
 *
 *          Matthew Luckie
 * 
 *          Supported by:
 *           The University of Waikato
 *           NLANR Measurement and Network Analysis
 *           CAIDA
 *           The WIDE Project
 *
 * Copyright (C) 2004-2007 The University of Waikato
 *
 * 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, version 2.
 *
 * 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
 *
 */

#include <sys/param.h>

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>

#if defined(__APPLE__)
#include <stdint.h>
#endif

#if defined(DMALLOC)
#include <dmalloc.h>
#endif

#include "scamper_fds.h"
#include "scamper_debug.h"
#include "scamper_icmp4.h"
#include "scamper_icmp6.h"
#include "scamper_udp4.h"
#include "scamper_udp6.h"
#include "scamper_tcp4.h"
#include "scamper_tcp6.h"
#include "scamper_dl.h"
#include "scamper_rtsock.h"
#include "utils.h"
#include "mjl_list.h"
#include "mjl_splaytree.h"

/*
 * scamper_fd_poll
 *
 * node to hold callback details for the fd.
 */
typedef struct scamper_fd_poll
{
  scamper_fd_t    *fdn;    /* back pointer to the fd struct */
  scamper_fd_cb_t  cb;     /* callback to use when event arises */
  void            *param;  /* user-defined parameter to pass to callback */
  dlist_t         *list;   /* which list the node is in */
  dlist_node_t    *node;   /* node in the poll list */
  uint8_t          flags;  /* flags associated with structure */
} scamper_fd_poll_t;

/*
 * scamper_fd
 *
 * a file descriptor, details of its type and other identifying information,
 * and what to do when read/write events are found with select.
 */
struct scamper_fd
{
  int               fd;     /* the file descriptor being polled */
  int               type;   /* the type of the file descriptor */
  int               refcnt; /* number of references to this structure */
  scamper_fd_poll_t read;   /* if monitored for read events */
  scamper_fd_poll_t write;  /* if monitored for write events */
  splaytree_node_t *node;   /* node for this fd in the splaytree */
  struct timeval    tv;     /* when this node should be expired */

  union
  {
    struct fd_poll_tcp
    {
      uint16_t sport;
    } fd_poll_tcp;

    struct fd_poll_udp
    {
      uint16_t sport;
    } fd_poll_udp;

    struct fd_poll_dl
    {
      int ifindex;
    } fd_poll_dl;

  } fd_un;
};

#define SCAMPER_FD_TYPE_PRIVATE  0x00
#define SCAMPER_FD_TYPE_ICMP4    0x01
#define SCAMPER_FD_TYPE_ICMP6    0x02
#define SCAMPER_FD_TYPE_UDP4     0x03
#define SCAMPER_FD_TYPE_UDP6     0x04
#define SCAMPER_FD_TYPE_TCP4     0x05
#define SCAMPER_FD_TYPE_TCP6     0x06
#define SCAMPER_FD_TYPE_DL       0x07
#define SCAMPER_FD_TYPE_RTSOCK   0x08

#define SCAMPER_FD_POLL_FLAG_INACTIVE 0x01 /* the fd should not be polled */

#define fd_tcp_sport  fd_un.fd_poll_tcp.sport
#define fd_udp_sport  fd_un.fd_poll_udp.sport
#define fd_dl_ifindex fd_un.fd_poll_dl.ifindex

static splaytree_t *fd_tree     = NULL;
static dlist_t     *read_fds    = NULL;
static dlist_t     *write_fds   = NULL;
static dlist_t     *read_queue  = NULL;
static dlist_t     *write_queue = NULL;
static dlist_t     *refcnt_0    = NULL;

#ifndef NDEBUG
static char *fd_tostr(scamper_fd_t *fdn)
{
  static char buf[16];

  switch(fdn->type)
    {
    case SCAMPER_FD_TYPE_PRIVATE:
      return "private";

    case SCAMPER_FD_TYPE_ICMP4:
      return "icmp4";

    case SCAMPER_FD_TYPE_ICMP6:
      return "icmp6";

    case SCAMPER_FD_TYPE_UDP4:
      snprintf(buf, sizeof(buf), "udp4 %d", fdn->fd_udp_sport);
      return buf;

    case SCAMPER_FD_TYPE_UDP6:
      snprintf(buf, sizeof(buf), "udp6 %d", fdn->fd_udp_sport);
      return buf;

    case SCAMPER_FD_TYPE_TCP4:
      snprintf(buf, sizeof(buf), "tcp4 %d", fdn->fd_tcp_sport);
      return buf;

    case SCAMPER_FD_TYPE_TCP6:
      snprintf(buf, sizeof(buf), "tcp6 %d", fdn->fd_tcp_sport);
      return buf;

    case SCAMPER_FD_TYPE_DL:
      snprintf(buf, sizeof(buf), "dl %d", fdn->fd_dl_ifindex);
      return buf;

    case SCAMPER_FD_TYPE_RTSOCK:
      return "rtsock";
    }

  return "?";
}
#endif

/*
 * fd_free
 *
 * free up memory allocated to scamper's monitoring of the file descriptor.
 */
static void fd_free(scamper_fd_t *fdn)
{
  scamper_debug(__func__, "fd %d type %s", fdn->fd, fd_tostr(fdn));

  if(fdn->read.node != NULL)
    {
      dlist_node_pop(fdn->read.list, fdn->read.node);
    }

  if(fdn->write.node != NULL)
    {
      dlist_node_pop(fdn->write.list, fdn->write.node);
    }

  if(fdn->node != NULL)
    {
      splaytree_remove_node(fd_tree, fdn->node);
    }

  if(fdn->type == SCAMPER_FD_TYPE_DL)
    {
      scamper_dl_state_free(fdn->read.param);
    }

  free(fdn);

  return;
}

/*
 * fd_refcnt_0
 *
 * this function is called whenever a fdn with a refcnt field of zero is
 * found.
 */
static void fd_refcnt_0(scamper_fd_t *fdn)
{
  /*
   * if the fd is in a list that is currently locked, then it can't be
   * removed just yet
   */
  if((fdn->read.list  != NULL && dlist_islocked(fdn->read.list)  != 0) ||
     (fdn->write.list != NULL && dlist_islocked(fdn->write.list) != 0))
    {
      return;
    }

  /*
   * if this is a private fd and the reference count has reached zero,
   * then the scamper_fd structure can be freed up completely now
   */
  if(fdn->type == SCAMPER_FD_TYPE_PRIVATE)
    {
      fd_free(fdn);
      return;
    }

  /*
   * this fd is a shared fd.  detach it from any poll lists it is in.
   */
  if(fdn->read.list != NULL)
    {
      scamper_fd_read_pause(fdn);
      dlist_node_eject(fdn->read.list, fdn->read.node);
      fdn->read.list = NULL;
    }
  if(fdn->write.list != NULL)
    {
      scamper_fd_write_pause(fdn);
      dlist_node_eject(fdn->write.list, fdn->write.node);
      fdn->write.list = NULL;
    }

  /*
   * set this fd to be closed in one minute unless something else comes
   * along and wants to use it.  use the structure's read-node for this.
   */
  gettimeofday_wrap(&fdn->tv);
  fdn->tv.tv_sec += 60;
  dlist_node_tail_push(refcnt_0, fdn->read.node);
  fdn->read.list = refcnt_0;

  return;
}

/*
 * fd_refcnt_0_reap
 *
 * loop through the list of fds with a refcnt of zero, and reap them if their
 * time has expired.
 */
static void fd_refcnt_0_reap(void)
{
  scamper_fd_poll_t *fdp;
  struct timeval tv;

  /* nothing to do */
  if(dlist_count(refcnt_0) == 0)
    {
      return;
    }

  gettimeofday_wrap(&tv);

  while((fdp = (scamper_fd_poll_t *)dlist_head_get(refcnt_0)) != NULL)
    {
      if(timeval_cmp(&fdp->fdn->tv, &tv) > 0)
      {
        break;
      }

      close(fdp->fdn->fd);
      fd_free(fdp->fdn);
    }

  return; 
}

/*
 * fd_set_assemble
 *
 * given a list of scamper_fd_poll_t structures held in a list, compose an
 * fd_set for them to pass to select.
 */
static fd_set *fd_set_assemble(dlist_t *fds, fd_set *fdset, int *nfds)
{
  scamper_fd_poll_t *fdp;
  dlist_node_t      *node;
  int                count = 0;

  FD_ZERO(fdset);

  node = dlist_head_node(fds);
  while(node != NULL)
    {
      /* file descriptor associated with the node */
      fdp = (scamper_fd_poll_t *)dlist_node_item(node);

      /* get the next node incase this node is subsequently removed */
      node = dlist_node_next(node);

      /* if there is nothing using this fdn any longer, then stop polling it */
      if(fdp->fdn->refcnt == 0)
      {
        fd_refcnt_0(fdp->fdn);
        continue;
      }

      /* if the inactive flag is set, then skip over this file descriptor */
      if((fdp->flags & SCAMPER_FD_POLL_FLAG_INACTIVE) != 0)
      {
        dlist_node_eject(fds, fdp->node);
        fdp->list = NULL;
        continue;
      }

      /* monitor this file descriptor */
      FD_SET(fdp->fdn->fd, fdset);
      count++;

      /* update the maxfd seen if appropriate */
      if(*nfds < fdp->fdn->fd)
      {
        *nfds = fdp->fdn->fd;
      }
    }

  /*
   * if there are no fds in the set to monitor, then return a null pointer
   * to pass to select
   */
  if(count == 0)
    {
      return NULL;
    }

  return fdset;
}

/*
 * fd_set_check
 *
 * given an fd_set that has been passed to select, as well as a list of
 * fds that are being monitored, figure out which ones have an event and
 * use the callback provided to deal with the event.
 */
static void fd_set_check(fd_set *fdset, dlist_t *fds, int *count)
{
  scamper_fd_poll_t *fdp;
  dlist_node_t *node;

  /* stop now if there is nothing to check */
  if(fdset == NULL || *count == 0)
    {
      return;
    }

  /* nodes in this list should not be removed while this function is called */
  dlist_lock(fds);

  /* loop through */
  node = dlist_head_node(fds);
  while(node != NULL && *count > 0)
    {
      fdp = (scamper_fd_poll_t *)dlist_node_item(node);
      node = dlist_node_next(node);      

      if(FD_ISSET(fdp->fdn->fd, fdset))
      {
        fdp->cb(fdp->fdn->fd, fdp->param);
        (*count)--;
      }
    }

  /* can modify the list now */
  dlist_unlock(fds);

  return;
}

/*
 * fd_cmp
 *
 * given two scamper_fd_t structures, determine if their properties are
 * the same.  used to maintain the splaytree of existing file descriptors
 * held by scamper.
 */
static int fd_cmp(const void *va, const void *vb)
{
  const scamper_fd_t *a = (const scamper_fd_t *)va;
  const scamper_fd_t *b = (const scamper_fd_t *)vb;

  if(a->type < b->type) return -1;
  if(a->type > b->type) return  1;

  switch(a->type)
    {
    case SCAMPER_FD_TYPE_TCP4:
    case SCAMPER_FD_TYPE_TCP6:
      if(a->fd_tcp_sport < b->fd_tcp_sport) return -1;
      if(a->fd_tcp_sport > b->fd_tcp_sport) return  1;
      return 0;

    case SCAMPER_FD_TYPE_UDP4:
    case SCAMPER_FD_TYPE_UDP6:
      if(a->fd_udp_sport < b->fd_udp_sport) return -1;
      if(a->fd_udp_sport > b->fd_udp_sport) return  1;
      return 0;

    case SCAMPER_FD_TYPE_DL:
      if(a->fd_dl_ifindex < b->fd_dl_ifindex) return -1;
      if(a->fd_dl_ifindex > b->fd_dl_ifindex) return  1;
      return 0;
    }

  return 0;
}

/*
 * fd_alloc
 *
 * allocate a scamper_fd_t structure and do generic setup tasks.
 */
static scamper_fd_t *fd_alloc(int type, int fd)
{
  scamper_fd_t *fdn = NULL;

  if((fdn = malloc_zero(sizeof(scamper_fd_t))) == NULL)
    {
      goto err;
    }
  fdn->type   = type;
  fdn->fd     = fd;
  fdn->refcnt = 1;

  /* set up to poll read ability */
  if((fdn->read.node = dlist_node_alloc(&fdn->read)) == NULL)
    {
      goto err;
    }
  fdn->read.fdn   = fdn;
  fdn->read.flags = SCAMPER_FD_POLL_FLAG_INACTIVE;

  /* set up to poll write ability */
  if((fdn->write.node = dlist_node_alloc(&fdn->write)) == NULL)
    {
      goto err;
    }
  fdn->write.fdn   = fdn;
  fdn->write.flags = SCAMPER_FD_POLL_FLAG_INACTIVE;

  return fdn;

 err:
  if(fdn != NULL) fd_free(fdn);
  return NULL;
}

/*
 * fd_find
 *
 * search the tree of file descriptors known to scamper for a matching
 * entry.  if one is found, increment its reference count and return it.
 */
static scamper_fd_t *fd_find(scamper_fd_t *findme)
{
  scamper_fd_t *fdn;

  if((fdn = splaytree_find(fd_tree, findme)) != NULL)
    {
      /*
       * remove the node from the refcnt_0 list otherwise it will be
       * reaped
       */
      if(fdn->refcnt == 0)
      {
        assert(fdn->read.list == refcnt_0);
        dlist_node_eject(fdn->read.list, fdn->read.node);
        fdn->read.list = NULL;
      }

      fdn->refcnt++;
    }

  return fdn;
}

/*
 * fd_null
 *
 * allocate a file descriptor of a specified type.
 */
static scamper_fd_t *fd_null(int type)
{
  scamper_fd_t *fdn = NULL, findme;
  int fd = -1;

  /* first check if a sharable fd exists for this type */
  findme.type = type;
  if((fdn = fd_find(&findme)) != NULL)
    {
      return fdn;
    }

  if(type == SCAMPER_FD_TYPE_ICMP4) fd = scamper_icmp4_open();
  else if(type == SCAMPER_FD_TYPE_ICMP6) fd = scamper_icmp6_open();
  else if(type == SCAMPER_FD_TYPE_RTSOCK) fd = scamper_rtsock_open();

  if(fd == -1 || (fdn = fd_alloc(type, fd)) == NULL ||
     (fdn->node = splaytree_insert(fd_tree, fdn)) == NULL)
    {
      goto err;
    }

  return fdn;

 err:
  if(fd != -1) close(fd);
  if(fdn != NULL) fd_free(fdn);
  return NULL;
}

static scamper_fd_t *fd_tcp(int type, uint16_t sport)
{
  scamper_fd_t *fdn = NULL, findme;
  int fd = -1;

  findme.type = type;
  findme.fd_tcp_sport = sport;

  if((fdn = fd_find(&findme)) != NULL)
    {
      return fdn;
    }

  if(type == SCAMPER_FD_TYPE_TCP4) fd = scamper_tcp4_open(sport);
  else if(type == SCAMPER_FD_TYPE_TCP6) fd = scamper_tcp6_open(sport);

  if(fd == -1 || (fdn = fd_alloc(type, fd)) == NULL)
    {
      goto err;
    }

  fdn->fd_tcp_sport = sport;

  if((fdn->node = splaytree_insert(fd_tree, fdn)) == NULL)
    {
      goto err;
    }

  return fdn;

 err:
  if(fd != -1) close(fd);
  if(fdn != NULL) fd_free(fdn);
  return NULL;
}

static scamper_fd_t *fd_udp(int type, uint16_t sport)
{
  scamper_fd_t *fdn, findme;
  int fd = -1;

  findme.type = type;
  findme.fd_udp_sport = sport;

  if((fdn = fd_find(&findme)) != NULL)
    {
      return fdn;
    }

  if(type == SCAMPER_FD_TYPE_UDP4) fd = scamper_udp4_open(sport);
  else if(type == SCAMPER_FD_TYPE_UDP6) fd = scamper_udp6_open(sport);

  if(fd == -1 || (fdn = fd_alloc(type, fd)) == NULL)
    {
      scamper_debug(__func__, "hello");
      goto err;
    }

  fdn->fd_udp_sport = sport;

  if((fdn->node = splaytree_insert(fd_tree, fdn)) == NULL)
    {
      goto err;
    }

  return fdn;

 err:
  if(fd != -1) close(fd);
  if(fdn != NULL) fd_free(fdn);
  return NULL;
}

static int fdp_list(void *item, void *param)
{
  ((scamper_fd_poll_t *)item)->list = (dlist_t *)param;
  return 0;
}

/*
 * scamper_fds_poll
 *
 * the money function: this function polls the file descriptors held by
 * scamper.  for each fd with an event, it calls the callback registered
 * with the fd.
 */
int scamper_fds_poll(struct timeval *timeout)
{
  fd_set rfds, *rfdsp;
  fd_set wfds, *wfdsp;
  int count, nfds = -1;

  /* concat any new fds to monitor now */
  dlist_foreach(read_queue, fdp_list, read_fds);
  dlist_concat(read_fds, read_queue);
  dlist_foreach(write_queue, fdp_list, write_fds);
  dlist_concat(write_fds, write_queue);

  /* compose the sets of file descriptors to monitor */
  rfdsp = fd_set_assemble(read_fds, &rfds, &nfds);
  wfdsp = fd_set_assemble(write_fds, &wfds, &nfds);

  /* find out which file descriptors have an event */
  if((count = select(nfds+1, rfdsp, wfdsp, NULL, timeout)) == -1)
    {
      printerror(errno, strerror, __func__, "select failed");
      return -1;
    }

  /* reap any expired fds */
  fd_refcnt_0_reap();

  fd_set_check(rfdsp, read_fds, &count);
  fd_set_check(wfdsp, write_fds, &count);

  return 0;
}

/*
 * scamper_fd_fd_get
 *
 * return the actual file descriptor associated with the scamper_fd_t
 */
int scamper_fd_fd_get(const scamper_fd_t *fdn)
{
  return fdn->fd;
}

/*
 * scamper_fd_fd_set
 *
 * set the file descriptor being monitored with the scamper_fd_t
 */
int scamper_fd_fd_set(scamper_fd_t *fdn, int fd)
{
  fdn->fd = fd;
  return 0;
}

/*
 * scamper_fd_read_pause
 *
 * ignore any read events on the fd.
 */
void scamper_fd_read_pause(scamper_fd_t *fdn)
{
  fdn->read.flags |= SCAMPER_FD_POLL_FLAG_INACTIVE;
  return;
}

/*
 * scamper_fd_read_unpause
 *
 * monitor read events on the fd.  unset the inactive flag, and push the
 * node back onto the read list
 */
void scamper_fd_read_unpause(scamper_fd_t *fdn)
{
  assert(fdn->read.cb != NULL);

  if((fdn->read.flags & SCAMPER_FD_POLL_FLAG_INACTIVE) != 0)
    {
      fdn->read.flags &= ~(SCAMPER_FD_POLL_FLAG_INACTIVE);

      /*
       * the fd may still be on the read fds list, just with the inactive bit
       * set.  if it isn't, then we have to put it on the queue.
       */
      if(fdn->read.list != read_fds)
      {
        dlist_node_head_push(read_queue, fdn->read.node);
        fdn->read.list = read_queue;
      }
    }

  return;
}

/*
 * scmaper_fd_write_pause
 *
 * ignore any write events on the fd
 */
void scamper_fd_write_pause(scamper_fd_t *fdn)
{
  fdn->write.flags |= SCAMPER_FD_POLL_FLAG_INACTIVE;
  return;
}

/*
 * scamper_fd_write_unpause
 *
 * monitor write events on the fd.  unset the inactive flag, and push the
 * node back onto the write list
 */
void scamper_fd_write_unpause(scamper_fd_t *fdn)
{
  assert(fdn->write.cb != NULL);

  if((fdn->write.flags & SCAMPER_FD_POLL_FLAG_INACTIVE) != 0)
    {
      fdn->write.flags &= ~(SCAMPER_FD_POLL_FLAG_INACTIVE);

      /*
       * the fd may still be on the write fds list, just with the inactive bit
       * set.  if it isn't, then we have to put it on the queue.
       */
      if(fdn->write.list != write_fds)
      {
        dlist_node_head_push(write_queue, fdn->write.node);
        fdn->write.list = write_queue;
      }
    }

  return;
}

void scamper_fd_read_set(scamper_fd_t *fdn, scamper_fd_cb_t cb, void *param)
{
  assert(fdn->type == SCAMPER_FD_TYPE_PRIVATE);
  fdn->read.cb = cb;
  fdn->read.param = param;
  return;
}

void scamper_fd_write_set(scamper_fd_t *fdn, scamper_fd_cb_t cb, void *param)
{
  assert(fdn->type == SCAMPER_FD_TYPE_PRIVATE);
  fdn->write.cb = cb;
  fdn->write.param = param;
  return;
}

void *scamper_fd_read_state(scamper_fd_t *fdn)
{
  return fdn->read.param;
}

void *scamper_fd_write_state(scamper_fd_t *fdn)
{
  return fdn->write.param;
}

scamper_fd_t *scamper_fd_icmp4(void)
{
  scamper_fd_t *fdn;

  if((fdn = fd_null(SCAMPER_FD_TYPE_ICMP4)) != NULL)
    {
      fdn->read.cb = scamper_icmp4_read_cb;
      scamper_fd_read_unpause(fdn);
    }

  return fdn;
}

scamper_fd_t *scamper_fd_icmp6(void)
{
  scamper_fd_t *fdn;

  if((fdn = fd_null(SCAMPER_FD_TYPE_ICMP6)) != NULL)
    {
      fdn->read.cb = scamper_icmp6_read_cb;
      scamper_fd_read_unpause(fdn);
    }

  return fdn;
}

scamper_fd_t *scamper_fd_rtsock(void)
{
  scamper_fd_t *fdn;

  if((fdn = fd_null(SCAMPER_FD_TYPE_RTSOCK)) != NULL)
    {
      fdn->read.cb = scamper_rtsock_read_cb;
      scamper_fd_read_unpause(fdn);
    }

  return fdn;
}

scamper_fd_t *scamper_fd_tcp4(uint16_t sport)
{
  return fd_tcp(SCAMPER_FD_TYPE_TCP4, sport);
}

scamper_fd_t *scamper_fd_tcp6(uint16_t sport)
{
  return fd_tcp(SCAMPER_FD_TYPE_TCP6, sport);
}

scamper_fd_t *scamper_fd_udp4(uint16_t sport)
{
  return fd_udp(SCAMPER_FD_TYPE_UDP4, sport);
}

scamper_fd_t *scamper_fd_udp6(uint16_t sport)
{
  return fd_udp(SCAMPER_FD_TYPE_UDP6, sport);
}

scamper_fd_t *scamper_fd_dl(int ifindex)
{
  scamper_fd_t *fdn = NULL, findme;
  scamper_dl_t *dl = NULL;
  int fd = -1;

  findme.type = SCAMPER_FD_TYPE_DL;
  findme.fd_dl_ifindex = ifindex;

  if((fdn = fd_find(&findme)) != NULL)
    {
      return fdn;
    }

  /*
   * open the file descriptor for the ifindex, and then allocate a scamper_fd
   * for the file descriptor
   */
  if((fd  = scamper_dl_open(ifindex)) == -1 ||
     (fdn = fd_alloc(SCAMPER_FD_TYPE_DL, fd)) == NULL)
    {
      goto err;
    }

  /*
   * record the ifindex for the file descriptor, and then allocate the state
   * that is maintained with it
   */
  fdn->fd_dl_ifindex = ifindex;

  /*
   * 1. add the file descriptor to the splay tree
   * 2. allocate state for the datalink file descriptor
   */
  if((fdn->node = splaytree_insert(fd_tree, fdn)) == NULL ||
     (dl = scamper_dl_state_alloc(fdn)) == NULL)
    {
      goto err;
    }

  /* set the file descriptor up for reading */
  fdn->read.cb     = scamper_dl_read_cb;
  fdn->read.param  = dl;
  fdn->write.cb    = NULL;
  fdn->write.param = dl;
  scamper_fd_read_unpause(fdn);

  return fdn;

 err:
  if(fdn != NULL) free(fdn);
  if(fd  != -1) close(fd);
  return NULL;
}

/*
 * scamper_fd_private
 *
 * allocate a private fd for scamper to manage.  this fd is not shared amongst
 * scamper.
 */
scamper_fd_t *scamper_fd_private(int fd,
                         scamper_fd_cb_t read_cb, void *read_param,
                         scamper_fd_cb_t write_cb, void *write_param)
{
  scamper_fd_t *fdn = NULL;

  if((fdn = fd_alloc(SCAMPER_FD_TYPE_PRIVATE, fd)) == NULL)
    {
      goto err;
    }

  if(read_cb != NULL)
    {
      scamper_fd_read_set(fdn, read_cb, read_param);
      scamper_fd_read_unpause(fdn);
    }

  if(write_cb != NULL)
    {
      scamper_fd_write_set(fdn, write_cb, write_param);
      scamper_fd_write_unpause(fdn);
    }

  return fdn;

 err:
  if(fdn != NULL) fd_free(fdn);
  return NULL;
}

/*
 * scamper_fd_ifindex
 *
 * if the file descriptor is associated with a known ifindex, return details
 * of it.
 */
int scamper_fd_ifindex(const scamper_fd_t *fdn, int *ifindex)
{
  if(fdn->type == SCAMPER_FD_TYPE_DL)
    {
      *ifindex = fdn->fd_dl_ifindex;
      return 0;
    }

  return -1;
}

/*
 * scamper_fd_sport
 *
 * if the file descriptor has a known source port, return details of it.
 */
int scamper_fd_sport(const scamper_fd_t *fdn, uint16_t *sport)
{
  switch(fdn->type)
    {
    case SCAMPER_FD_TYPE_UDP4:
    case SCAMPER_FD_TYPE_UDP6:
      *sport = fdn->fd_udp_sport;
      return 0;

    case SCAMPER_FD_TYPE_TCP4:
    case SCAMPER_FD_TYPE_TCP6:
      *sport = fdn->fd_tcp_sport;
      return 0;
    }

  return -1;
}

/*
 * scamper_fd_free
 *
 * this function reduces the reference count for a given file descriptor.
 *
 * if zero is reached, the fd will be dealt with when scamper_fd_poll is next
 * called.  the fd cannot be summarily removed here without the potential
 * to screw up any current call to scamper_fd_poll as that function assumes
 * the list remains intact for the duration of any events found with select.
 *
 */
void scamper_fd_free(scamper_fd_t *fdn)
{
  assert(fdn != NULL);
  assert(fdn->refcnt > 0);
  if(--fdn->refcnt == 0)
    {
      fd_refcnt_0(fdn);
    }
  return;
}

/*
 * alloc_list
 *
 * helper function to allocate a list for scamper_fds_init
 */
static dlist_t *alloc_list(char *name)
{
  dlist_t *list;
  if((list = dlist_alloc()) == NULL)
    {
      printerror(errno, strerror, __func__, "alloc %s failed", name);
    }
  return list;
}

/*
 * scamper_fds_init
 *
 * setup the global data structures necessary for scamper to manage a set of
 * file descriptors
 */
int scamper_fds_init()
{
  scamper_debug(__func__, "fd table size: %d", getdtablesize());

  if((read_fds    = alloc_list("read fd list"))   == NULL ||
     (read_queue  = alloc_list("read fd queue"))  == NULL ||
     (write_fds   = alloc_list("write fd list"))  == NULL ||
     (write_queue = alloc_list("write fd queue")) == NULL ||
     (refcnt_0    = alloc_list("refcnt_0 list"))  == NULL)
    {
      return -1;
    }

  if((fd_tree = splaytree_alloc(fd_cmp)) == NULL)
    {
      printerror(errno, strerror, __func__, "alloc fd tree failed");
      return -1;
    }

  return 0;
}

/*
 * cleanup_list
 *
 * helper function to remove scamper_fd_poll structures from any lists.
 */
static void cleanup_list(dlist_t *list)
{
  scamper_fd_poll_t *poll;

  if(list == NULL) return;

  while((poll = dlist_head_pop(list)) != NULL)
    {
      poll->list = NULL;
      poll->node = NULL;
    }

  dlist_free(list);

  return;
}

/*
 * scamper_fds_cleanup
 *
 * tidy up the state allocated to maintain fd records.
 */
void scamper_fds_cleanup()
{
  scamper_fd_poll_t *fdp;

  /* clean up the lists */
  cleanup_list(read_fds);    read_fds = NULL;
  cleanup_list(write_fds);   write_fds = NULL;
  cleanup_list(read_queue);  read_queue = NULL;
  cleanup_list(write_queue); write_queue = NULL;

  /* reap anything on the reap list */
  if(refcnt_0 != NULL)
    {
      while((fdp = (scamper_fd_poll_t *)dlist_head_get(refcnt_0)) != NULL)
      {
        close(fdp->fdn->fd);
        fd_free(fdp->fdn);
      }
      dlist_free(refcnt_0);
      refcnt_0 = NULL;
    }

  /* clean up the tree */
  if(fd_tree != NULL)
    {
      splaytree_free(fd_tree, NULL);
      fd_tree = NULL;
    }

  return;
}

Generated by  Doxygen 1.6.0   Back to index