Blame snmplib/large_fd_set.c

Packit fcad23
/**
Packit fcad23
 * @file large_fd_set.c
Packit fcad23
 *
Packit fcad23
 * @brief Macro's and functions for manipulation of large file descriptor sets.
Packit fcad23
 *
Packit fcad23
 * Portions of this file are copyrighted by:
Packit fcad23
 * Copyright (c) 2016 VMware, Inc. All rights reserved.
Packit fcad23
 * Use is subject to license terms specified in the COPYING file
Packit fcad23
 * distributed with the Net-SNMP package.
Packit fcad23
 */
Packit fcad23
Packit fcad23
#include <net-snmp/net-snmp-config.h>
Packit fcad23
Packit fcad23
#ifdef HAVE_STDLIB_H
Packit fcad23
#include <stdlib.h>
Packit fcad23
#else
Packit fcad23
#error broken
Packit fcad23
#endif
Packit fcad23
Packit fcad23
#include <stdio.h>
Packit fcad23
#include <string.h> /* memset(), which is invoked by FD_ZERO() */
Packit fcad23
Packit fcad23
#include <stddef.h>
Packit fcad23
Packit fcad23
#include <net-snmp/net-snmp-includes.h>
Packit fcad23
#include <net-snmp/library/snmp_assert.h>
Packit fcad23
#include <net-snmp/library/large_fd_set.h>
Packit fcad23
Packit fcad23
Packit fcad23
#if !defined(cygwin) && defined(HAVE_WINSOCK_H)
Packit fcad23
Packit fcad23
#define LFD_SET(n, p)    FD_SET(n, p)
Packit fcad23
#define LFD_CLR(n, p)    FD_CLR(n, p)
Packit fcad23
#define LFD_ISSET(n, p)  FD_ISSET(n, p)
Packit fcad23
Packit fcad23
void
Packit fcad23
netsnmp_large_fd_setfd(SOCKET fd, netsnmp_large_fd_set * fdset)
Packit fcad23
{
Packit fcad23
    unsigned        i;
Packit fcad23
Packit fcad23
    netsnmp_assert(fd != INVALID_SOCKET);
Packit fcad23
Packit fcad23
    if (fdset->lfs_setptr->fd_count == fdset->lfs_setsize)
Packit fcad23
        netsnmp_large_fd_set_resize(fdset, 2 * (fdset->lfs_setsize + 1));
Packit fcad23
Packit fcad23
    for (i = 0; i < fdset->lfs_setptr->fd_count; i++) {
Packit fcad23
        if (fdset->lfs_setptr->fd_array[i] == fd)
Packit fcad23
            break;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    if (i == fdset->lfs_setptr->fd_count &&
Packit fcad23
        fdset->lfs_setptr->fd_count < fdset->lfs_setsize) {
Packit fcad23
        fdset->lfs_setptr->fd_count++;
Packit fcad23
        fdset->lfs_setptr->fd_array[i] = fd;
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
netsnmp_large_fd_clr(SOCKET fd, netsnmp_large_fd_set * fdset)
Packit fcad23
{
Packit fcad23
    unsigned        i;
Packit fcad23
Packit fcad23
    netsnmp_assert(fd != INVALID_SOCKET);
Packit fcad23
Packit fcad23
    for (i = 0; i < fdset->lfs_setptr->fd_count; i++) {
Packit fcad23
        if (fdset->lfs_setptr->fd_array[i] == fd) {
Packit fcad23
            while (i < fdset->lfs_setptr->fd_count - 1) {
Packit fcad23
                fdset->lfs_setptr->fd_array[i] =
Packit fcad23
                    fdset->lfs_setptr->fd_array[i + 1];
Packit fcad23
                i++;
Packit fcad23
            }
Packit fcad23
            fdset->lfs_setptr->fd_count--;
Packit fcad23
            break;
Packit fcad23
        }
Packit fcad23
    }
Packit fcad23
}
Packit fcad23
Packit fcad23
int
Packit fcad23
netsnmp_large_fd_is_set(SOCKET fd, netsnmp_large_fd_set * fdset)
Packit fcad23
{
Packit fcad23
    unsigned int    i;
Packit fcad23
Packit fcad23
    netsnmp_assert(fd != INVALID_SOCKET);
Packit fcad23
Packit fcad23
    for (i = 0; i < fdset->lfs_setptr->fd_count; i++) {
Packit fcad23
        if (fdset->lfs_setptr->fd_array[i] == fd)
Packit fcad23
            return 1;
Packit fcad23
    }
Packit fcad23
    return 0;
Packit fcad23
}
Packit fcad23
Packit fcad23
#else
Packit fcad23
Packit fcad23
/*
Packit fcad23
 * Recent versions of glibc trigger abort() if FD_SET(), FD_CLR() or
Packit fcad23
 * FD_ISSET() is invoked with n >= FD_SETSIZE. Hence these replacement
Packit fcad23
 * functions. However, since NFDBITS != 8 * sizeof(fd_set.fds_bits[0]) for at
Packit fcad23
 * least HP-UX on ia64 and since that combination uses big endian, use the
Packit fcad23
 * macros from <sys/select.h> on such systems.
Packit fcad23
 */
Packit fcad23
NETSNMP_STATIC_INLINE void LFD_SET(unsigned n, fd_set *p)
Packit fcad23
{
Packit fcad23
    enum { nfdbits = 8 * sizeof(p->fds_bits[0]) };
Packit fcad23
Packit fcad23
    if (nfdbits == NFDBITS)
Packit fcad23
        p->fds_bits[n / nfdbits] |= (1ULL << (n % nfdbits));
Packit fcad23
    else
Packit fcad23
        FD_SET(n, p);
Packit fcad23
}
Packit fcad23
Packit fcad23
NETSNMP_STATIC_INLINE void LFD_CLR(unsigned n, fd_set *p)
Packit fcad23
{
Packit fcad23
    enum { nfdbits = 8 * sizeof(p->fds_bits[0]) };
Packit fcad23
Packit fcad23
    if (nfdbits == NFDBITS)
Packit fcad23
        p->fds_bits[n / nfdbits] &= ~(1ULL << (n % nfdbits));
Packit fcad23
    else
Packit fcad23
        FD_CLR(n, p);
Packit fcad23
}
Packit fcad23
Packit fcad23
NETSNMP_STATIC_INLINE unsigned LFD_ISSET(unsigned n, const fd_set *p)
Packit fcad23
{
Packit fcad23
    enum { nfdbits = 8 * sizeof(p->fds_bits[0]) };
Packit fcad23
Packit fcad23
    if (nfdbits == NFDBITS)
Packit fcad23
        return (p->fds_bits[n / nfdbits] & (1ULL << (n % nfdbits))) != 0;
Packit fcad23
    else
Packit fcad23
        return FD_ISSET(n, p) != 0;
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
netsnmp_large_fd_setfd(int fd, netsnmp_large_fd_set * fdset)
Packit fcad23
{
Packit fcad23
    netsnmp_assert(fd >= 0);
Packit fcad23
Packit fcad23
    while (fd >= (int)fdset->lfs_setsize)
Packit fcad23
        netsnmp_large_fd_set_resize(fdset, 2 * (fdset->lfs_setsize + 1));
Packit fcad23
Packit fcad23
    LFD_SET(fd, fdset->lfs_setptr);
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
netsnmp_large_fd_clr(int fd, netsnmp_large_fd_set * fdset)
Packit fcad23
{
Packit fcad23
    netsnmp_assert(fd >= 0);
Packit fcad23
Packit fcad23
    if ((unsigned)fd < fdset->lfs_setsize)
Packit fcad23
        LFD_CLR(fd, fdset->lfs_setptr);
Packit fcad23
}
Packit fcad23
Packit fcad23
int
Packit fcad23
netsnmp_large_fd_is_set(int fd, netsnmp_large_fd_set * fdset)
Packit fcad23
{
Packit fcad23
    netsnmp_assert(fd >= 0);
Packit fcad23
Packit fcad23
    return ((unsigned)fd < fdset->lfs_setsize &&
Packit fcad23
            LFD_ISSET(fd, fdset->lfs_setptr));
Packit fcad23
}
Packit fcad23
Packit fcad23
#endif
Packit fcad23
Packit fcad23
void
Packit fcad23
netsnmp_large_fd_set_init(netsnmp_large_fd_set * fdset, int setsize)
Packit fcad23
{
Packit fcad23
    fdset->lfs_setsize = 0;
Packit fcad23
    fdset->lfs_setptr  = NULL;
Packit fcad23
#if !defined(cygwin) && defined(HAVE_WINSOCK_H)
Packit fcad23
    fdset->lfs_set.fd_count = 0;
Packit fcad23
#endif
Packit fcad23
    netsnmp_large_fd_set_resize(fdset, setsize);
Packit fcad23
}
Packit fcad23
Packit fcad23
int
Packit fcad23
netsnmp_large_fd_set_select(int numfds, netsnmp_large_fd_set *readfds,
Packit fcad23
                     netsnmp_large_fd_set *writefds,
Packit fcad23
                     netsnmp_large_fd_set *exceptfds,
Packit fcad23
                     struct timeval *timeout)
Packit fcad23
{
Packit fcad23
#if defined(cygwin) || !defined(HAVE_WINSOCK_H)
Packit fcad23
    /* Bit-set representation: make sure all fds have at least size 'numfds'. */
Packit fcad23
    if (readfds && readfds->lfs_setsize < numfds)
Packit fcad23
        netsnmp_large_fd_set_resize(readfds, numfds);
Packit fcad23
    if (writefds && writefds->lfs_setsize < numfds)
Packit fcad23
        netsnmp_large_fd_set_resize(writefds, numfds);
Packit fcad23
    if (exceptfds && exceptfds->lfs_setsize < numfds)
Packit fcad23
        netsnmp_large_fd_set_resize(exceptfds, numfds);
Packit fcad23
#else
Packit fcad23
    /* Array representation: no resizing is necessary. */
Packit fcad23
#endif
Packit fcad23
Packit fcad23
    return select(numfds, (readfds) ? readfds->lfs_setptr : NULL,
Packit fcad23
                  (writefds) ? writefds->lfs_setptr : NULL,
Packit fcad23
                  (exceptfds) ? exceptfds->lfs_setptr : NULL, timeout);
Packit fcad23
}
Packit fcad23
Packit fcad23
int
Packit fcad23
netsnmp_large_fd_set_resize(netsnmp_large_fd_set * fdset, int setsize)
Packit fcad23
{
Packit fcad23
    int             fd_set_bytes;
Packit fcad23
Packit fcad23
    if (fdset->lfs_setsize == setsize)
Packit fcad23
        goto success;
Packit fcad23
Packit fcad23
    if (setsize > FD_SETSIZE) {
Packit fcad23
        fd_set_bytes = NETSNMP_FD_SET_BYTES(setsize);
Packit fcad23
        if (fdset->lfs_setsize > FD_SETSIZE) {
Packit fcad23
            fdset->lfs_setptr = realloc(fdset->lfs_setptr, fd_set_bytes);
Packit fcad23
            if (!fdset->lfs_setptr)
Packit fcad23
                goto out_of_mem;
Packit fcad23
        } else {
Packit fcad23
            fdset->lfs_setptr = malloc(fd_set_bytes);
Packit fcad23
            if (!fdset->lfs_setptr)
Packit fcad23
                goto out_of_mem;
Packit fcad23
            *fdset->lfs_setptr = fdset->lfs_set;
Packit fcad23
        }
Packit fcad23
    } else {
Packit fcad23
        if (fdset->lfs_setsize > FD_SETSIZE) {
Packit fcad23
            fdset->lfs_set = *fdset->lfs_setptr;
Packit fcad23
            free(fdset->lfs_setptr);
Packit fcad23
        }
Packit fcad23
        fdset->lfs_setptr = &fdset->lfs_set;
Packit fcad23
    }
Packit fcad23
Packit fcad23
#if defined(cygwin) || !defined(HAVE_WINSOCK_H)
Packit fcad23
    /*
Packit fcad23
     * Unix: when enlarging, clear the file descriptors defined in the
Packit fcad23
     * resized *fdset but that were not defined in the original *fdset.
Packit fcad23
     */
Packit fcad23
    if ( fdset->lfs_setsize == 0 && setsize == FD_SETSIZE ) {
Packit fcad23
        /* In this case we can use the OS's FD_ZERO */
Packit fcad23
        FD_ZERO(fdset->lfs_setptr);
Packit fcad23
    } else {
Packit fcad23
        int             i;
Packit fcad23
Packit fcad23
        for (i = fdset->lfs_setsize; i < setsize; i++)
Packit fcad23
            LFD_CLR(i, fdset->lfs_setptr);
Packit fcad23
    }
Packit fcad23
#endif
Packit fcad23
Packit fcad23
    fdset->lfs_setsize = setsize;
Packit fcad23
#if !defined(cygwin) && defined(HAVE_WINSOCK_H)
Packit fcad23
    if (setsize < fdset->lfs_setptr->fd_count)
Packit fcad23
        fdset->lfs_setptr->fd_count = setsize;
Packit fcad23
#endif
Packit fcad23
success:
Packit fcad23
    return 1;
Packit fcad23
Packit fcad23
out_of_mem:
Packit fcad23
    fdset->lfs_setsize = 0;
Packit fcad23
#if !defined(cygwin) && defined(HAVE_WINSOCK_H)
Packit fcad23
    fdset->lfs_setptr->fd_count = 0;
Packit fcad23
#endif
Packit fcad23
    return 0;
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
netsnmp_large_fd_set_cleanup(netsnmp_large_fd_set * fdset)
Packit fcad23
{
Packit fcad23
    netsnmp_large_fd_set_resize(fdset, 0);
Packit fcad23
    fdset->lfs_setsize = 0;
Packit fcad23
    fdset->lfs_setptr  = NULL;
Packit fcad23
}
Packit fcad23
Packit fcad23
void
Packit fcad23
netsnmp_copy_fd_set_to_large_fd_set(netsnmp_large_fd_set * dst,
Packit fcad23
                                    const fd_set * src)
Packit fcad23
{
Packit fcad23
    netsnmp_large_fd_set_resize(dst, FD_SETSIZE);
Packit fcad23
    *dst->lfs_setptr = *src;
Packit fcad23
}
Packit fcad23
Packit fcad23
int
Packit fcad23
netsnmp_copy_large_fd_set_to_fd_set(fd_set * dst,
Packit fcad23
                                    const netsnmp_large_fd_set * src)
Packit fcad23
{
Packit fcad23
    /* Report failure if *src is larger than FD_SETSIZE. */
Packit fcad23
    if (src->lfs_setsize > FD_SETSIZE) {
Packit fcad23
        FD_ZERO(dst);
Packit fcad23
        return -1;
Packit fcad23
    }
Packit fcad23
Packit fcad23
    *dst = *src->lfs_setptr;
Packit fcad23
Packit fcad23
#if !(!defined(cygwin) && defined(HAVE_WINSOCK_H))
Packit fcad23
    {
Packit fcad23
        int             i;
Packit fcad23
Packit fcad23
        /* Unix: clear any file descriptors defined in *dst but not in *src. */
Packit fcad23
        for (i = src->lfs_setsize; i < FD_SETSIZE; ++i)
Packit fcad23
            FD_CLR(i, dst);
Packit fcad23
    }
Packit fcad23
#endif
Packit fcad23
Packit fcad23
    return 0;
Packit fcad23
}