Hi all,

Currently the netsnmp library crashes when you try to use file
desriptors larger than FD_SETSIZE. The snmp_read and snmp_select_info
methods rely on filedescriptors being communicated through fd_sets,
which can contain only filedescriptors up to FD_SETSIZE (which is 1024
on most linux systems).

The attached patch contains an extension of the netsnmp api to deal with
this problem. It allows the user to interact with netsnmp using arrays
with arbitrarily large filehandles. 

Is there already someone working on this? If not, what would be the
procedure to get this fix into the official distribution? 

Enabling netsnmp to deal with arbitrarily large filedescriptors is
achieved (while maintinging backward compatibility) by introducing an
interface in the style of the poll() command.

The patch introduces four new functions:
* snmp_read_extd
* snmp_sess_read_extd
* snmp_poll_info
* _sess_selpol_info
and modifies the interface of the function
* _sess_read

These methods all stick as closely as possible to the interface of the
poll() command. They been tested with a couple of small examples on aix
and linux.

Who would be the person to talk to about this stuff? 

Feedback and questions are most welcome.

Best regards, and happy new year! :-)
Thijs Brouwer
diff -ur net-snmp-5.4-orig/configure net-snmp-5.4/configure
--- net-snmp-5.4-orig/configure	2006-11-24 17:56:15.000000000 +0000
+++ net-snmp-5.4/configure	2006-12-17 20:19:02.000000000 +0000
@@ -31768,7 +31768,7 @@
 
 
 
-for ac_header in sys/timeout.h sys/un.h fstab.h sys/fs.h mtab.h ufs/fs.h sys/fixpoint.h machine/param.h sys/vm.h vm/vm.h sys/vmmeter.h sys/vmparam.h sys/vmmac.h sys/vmsystm.h sys/time.h sys/times.h sys/statvfs.h sys/vfs.h sys/mnttab.h sys/select.h mntent.h sys/mntent.h kstat.h utsname.h sys/utsname.h sys/cdefs.h getopt.h locale.h pthread.h sys/loadavg.h regex.h linux/tasks.h pwd.h grp.h utmpx.h
+for ac_header in sys/timeout.h sys/un.h fstab.h sys/fs.h mtab.h ufs/fs.h sys/fixpoint.h machine/param.h sys/vm.h vm/vm.h sys/vmmeter.h sys/vmparam.h sys/vmmac.h sys/vmsystm.h sys/time.h sys/times.h sys/statvfs.h sys/vfs.h sys/mnttab.h sys/select.h sys/poll.h mntent.h sys/mntent.h kstat.h utsname.h sys/utsname.h sys/cdefs.h getopt.h locale.h pthread.h sys/loadavg.h regex.h linux/tasks.h pwd.h grp.h utmpx.h
 do
 as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
 if eval "test \"\${$as_ac_Header+set}\" = set"; then
@@ -37703,7 +37703,7 @@
 
 
 
-for ac_func in setmntent gethostname uname gettimeofday select socket strtol strtoul strlcpy
+for ac_func in setmntent gethostname uname gettimeofday select poll socket strtol strtoul strlcpy
 do
 as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
 echo "$as_me:$LINENO: checking for $ac_func" >&5
diff -ur net-snmp-5.4-orig/configure.in net-snmp-5.4/configure.in
--- net-snmp-5.4-orig/configure.in	2006-11-24 17:53:18.000000000 +0000
+++ net-snmp-5.4/configure.in	2006-12-17 20:19:02.000000000 +0000
@@ -3168,7 +3168,7 @@
 #include <sys/param.h>
 #endif
 ])
-AC_CHECK_HEADERS(sys/timeout.h sys/un.h fstab.h sys/fs.h mtab.h ufs/fs.h sys/fixpoint.h machine/param.h sys/vm.h vm/vm.h sys/vmmeter.h sys/vmparam.h sys/vmmac.h sys/vmsystm.h sys/time.h sys/times.h sys/statvfs.h sys/vfs.h sys/mnttab.h sys/select.h mntent.h sys/mntent.h kstat.h utsname.h sys/utsname.h sys/cdefs.h getopt.h locale.h pthread.h sys/loadavg.h regex.h linux/tasks.h pwd.h grp.h utmpx.h)
+AC_CHECK_HEADERS(sys/timeout.h sys/un.h fstab.h sys/fs.h mtab.h ufs/fs.h sys/fixpoint.h machine/param.h sys/vm.h vm/vm.h sys/vmmeter.h sys/vmparam.h sys/vmmac.h sys/vmsystm.h sys/time.h sys/times.h sys/statvfs.h sys/vfs.h sys/mnttab.h sys/select.h poll.h mntent.h sys/mntent.h kstat.h utsname.h sys/utsname.h sys/cdefs.h getopt.h locale.h pthread.h sys/loadavg.h regex.h linux/tasks.h pwd.h grp.h utmpx.h)
 # Network headers
 AC_CHECK_HEADERS(arpa/inet.h netinet/in_systm.h netinet/in.h netinet/ip_var.h netinet/tcp.h netinet/tcpip.h netinet/udp.h net/if.h netinet/in_var.h netinet/ip.h netinet/ip_icmp.h net/if_arp.h net/if_mib.h net/if_var.h netinet/if_ether.h netinet/tcp_timer.h netinet/tcp_var.h netinet/udp_var.h netinet/icmp_var.h netdb.h net/route.h,,,
 [[
@@ -3575,7 +3575,7 @@
 AC_FUNC_MEMCMP
 AC_TYPE_SIGNAL
 AC_FUNC_GETMNTENT
-AC_CHECK_FUNCS(setmntent gethostname uname gettimeofday select socket strtol strtoul strlcpy)
+AC_CHECK_FUNCS(setmntent gethostname uname gettimeofday select poll socket strtol strtoul strlcpy)
 AC_CHECK_FUNCS(strchr strtok_r strdup memcpy memmove index bcopy strcasestr regcomp)
 AC_CHECK_FUNCS(signal setsid sigset sigblock sighold strerror setenv vsnprintf snprintf)
 AC_CHECK_FUNCS(sigaction)
diff -ur net-snmp-5.4-orig/include/net-snmp/library/snmp_api.h net-snmp-5.4/include/net-snmp/library/snmp_api.h
--- net-snmp-5.4-orig/include/net-snmp/library/snmp_api.h	2006-10-27 20:19:43.000000000 +0000
+++ net-snmp-5.4/include/net-snmp/library/snmp_api.h	2006-12-17 20:19:02.000000000 +0000
@@ -57,6 +57,8 @@
 SOFTWARE.
 ******************************************************************/
 
+#include <sys/poll.h>
+
 /** @typedef struct variable_list netsnmp_variable_list
     * Typedefs the variable_list struct into netsnmp_variable_list */
 struct variable_list;
@@ -614,7 +616,13 @@
      */
     void            snmp_read(fd_set *);
 
-
+    /*
+     * Same as snmp_read, but with the poll() interface, to allow for large
+     * file handles. I.e. checks to see if any of the fd's in fds belongs to
+     * snmp. nfds is the number of filedescriptors in fds (<= size of the
+     * array).
+     */
+    void            snmp_read_extd(struct pollfd *fds, nfds_t nfds);
 
     /*
      * void
@@ -658,8 +666,20 @@
      */
     int             snmp_select_info(int *, fd_set *, struct timeval *,
                                      int *);
-
-
+struct pollfdarr {
+   /** Number of file descriptors in fds */
+   nfds_t nfds; 
+   /** Array of file descriptors */
+   struct pollfd *fds; 
+   /** Array size (which may be bigger than the number of file descriptors
+    ** in it). */
+   int arrsize;
+};
+    /*
+     * Same as snmp_select_info but using the poll() interface, to allow
+     * for large file handles.
+     */
+    int             snmp_poll_info(struct pollfdarr *, int *, int *);
 
     /*
      * void snmp_timeout();
Only in net-snmp-5.4/include/net-snmp/library: snmp_api.h.orig
diff -ur net-snmp-5.4-orig/include/net-snmp/net-snmp-config.h.in net-snmp-5.4/include/net-snmp/net-snmp-config.h.in
--- net-snmp-5.4-orig/include/net-snmp/net-snmp-config.h.in	2006-10-12 09:54:57.000000000 +0000
+++ net-snmp-5.4/include/net-snmp/net-snmp-config.h.in	2006-12-17 20:19:02.000000000 +0000
@@ -902,6 +902,9 @@
 /* Define to 1 if you have the <sys/sema.h> header file. */
 #undef HAVE_SYS_SEMA_H
 
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#undef HAVE_SYS_POLL_H
+
 /* Define to 1 if you have the <sys/socketvar.h> header file. */
 #undef HAVE_SYS_SOCKETVAR_H
 
diff -ur net-snmp-5.4-orig/snmplib/snmp_api.c net-snmp-5.4/snmplib/snmp_api.c
--- net-snmp-5.4-orig/snmplib/snmp_api.c	2006-10-25 16:16:24.000000000 +0000
+++ net-snmp-5.4/snmplib/snmp_api.c	2006-12-17 20:19:02.000000000 +0000
@@ -5419,9 +5419,12 @@
  * returns 0 if success, -1 if fail 
  * MTR: can't lock here and at snmp_read 
  * Beware recursive send maybe inside snmp_read callback function. 
+ * 
+ * Extended interface for compatibility with poll() interface.
+ * Assumes fdset is zero when this interface is used.
  */
 int
-_sess_read(void *sessp, fd_set * fdset)
+_sess_read(void *sessp, fd_set * fdset, struct pollfd fds[], nfds_t nfds)
 {
     struct session_list *slp = (struct session_list *) sessp;
     netsnmp_session *sp = slp ? slp->session : NULL;
@@ -5443,13 +5446,61 @@
         return 0; 
     }
 
-    if (!fdset || !(FD_ISSET(transport->sock, fdset))) {
+    if ((fdset && fds) || (!fdset && !fds)) {
+        DEBUGMSGTL(("sess_read", 
+                    "either fdset or fds should be used, not both.\n"));
+        return 0;
+    }
+
+
+    /* 
+     * Check if there is something to read for this session on fdset. 
+     */
+    if (fdset && !(FD_ISSET(transport->sock, fdset))) {
         DEBUGMSGTL(("sess_read", "not reading %d (fdset %p set %d)\n",
                     transport->sock, fdset,
-                    fdset ? FD_ISSET(transport->sock, fdset) : -9));
+                    FD_ISSET(transport->sock, fdset)));
         return 0;
     }
 
+    /* 
+     * Check if there is something to read for this session on fds. 
+     * If there is nothing to read, return 0 and send out debug message.
+     */
+    if (fds) {
+        int somethingtoread = 0;
+        nfds_t i;
+        /* 
+         * Set revents bitmask for incoming messages.
+         * The first two should be available on every system that has poll()
+         */
+        short bitmask = 0;
+        bitmask |= POLLIN | POLLPRI;
+#ifdef POLLRDNORM
+        bitmask |= POLLRDNORM;
+#endif
+#ifdef POLLRDBAND
+        bitmask |= POLLRDBAND;
+#endif
+        /*
+         * FIXME?: this linear search in the list of fds leads to quadratic
+         * behaviour in the number of sessions (this is only relevant when
+         * dealing with really large numbers of sessions). Possible solution
+         * is the application of a hash table to look up the file descriptors.
+         */
+        for (i = 0; i < nfds && !somethingtoread; i++) {
+            if (fds[i].fd == transport->sock &&
+                    fds[i].revents & bitmask) {
+                somethingtoread = 1;
+            }
+        }
+        if (!somethingtoread) {
+            DEBUGMSGTL(("sess_read", "not reading %d\n",
+                        transport->sock));
+            return 0;
+        }
+    }
+
     sp->s_snmp_errno = 0;
     sp->s_errno = 0;
 
@@ -5783,19 +5834,57 @@
     }
 }
 
+/*
+ * returns 0 if success, -1 if fail 
+ */
+int
+snmp_sess_read(void *sessp, fd_set * fdset)
+{
+    struct session_list *psl;
+    netsnmp_session *pss;
+    int             rc;
+
+    rc = _sess_read(sessp, fdset, NULL, 0);
+    psl = (struct session_list *) sessp;
+    pss = psl->session;
+    if (rc && pss->s_snmp_errno) {
+        SET_SNMP_ERROR(pss->s_snmp_errno);
+    }
+    return rc;
+}
+
+/*
+ * Extension of snmp_read with the poll() interface, i.e.
+ * allow for an unlimited number of filehandles to be passed.
+ * Checks to see if any of the fd's set in the fdset belong to
+ * snmp. Each socket with it's fd set has a packet read from it
+ * and snmp_parse is called on the packet received.  The resulting pdu
+ * is passed to the callback routine for that session.  If the callback
+ * routine returns successfully, the pdu and it's request are deleted.
+ */
+void
+snmp_read_extd(struct pollfd fds[], nfds_t nfds)
+{
+    struct session_list *slp;
+    snmp_res_lock(MT_LIBRARY_ID, MT_LIB_SESSION);
+    for (slp = Sessions; slp; slp = slp->next) {
+        snmp_sess_read_extd((void *) slp, fds, nfds);
+    }
+    snmp_res_unlock(MT_LIBRARY_ID, MT_LIB_SESSION);
+}
 
 
 /*
  * returns 0 if success, -1 if fail 
  */
 int
-snmp_sess_read(void *sessp, fd_set * fdset)
+snmp_sess_read_extd(void *sessp, struct pollfd fds[], nfds_t nfds)
 {
     struct session_list *psl;
     netsnmp_session *pss;
     int             rc;
 
-    rc = _sess_read(sessp, fdset);
+    rc = _sess_read(sessp, NULL, fds, nfds);
     psl = (struct session_list *) sessp;
     pss = psl->session;
     if (rc && pss->s_snmp_errno) {
@@ -5842,6 +5931,33 @@
                                  block);
 }
 
+    int
+snmp_poll_info(struct pollfdarr *fdarr, int *timeout, int *block)
+/*
+ * same as snmp_select_info but with poll() interface.
+ * Appends filedescriptors for snmp at the end of fdarr
+ * increasing the size of the array when necessary.
+ */
+{
+    int numfds = 0;
+    struct timeval stimeout;
+    int numsessions = _sess_selpol_info(NULL, &stimeout, block,
+            &numfds, NULL, fdarr);
+    if (numfds) { /* something went wrong, only nfds should be set. */
+        DEBUGMSGTL(("snmp_select_info_extd", 
+                    "error in call of _sess_select_info\n"));
+    }
+    if (!block) {
+        int rettimeout = stimeout.tv_sec * 1000 + stimeout.tv_usec / 1000;
+        if ((rettimeout > 0) &&
+            (rettimeout < *timeout)) {
+            *timeout = rettimeout;
+        }
+    }
+
+    return numsessions;
+}
+
 /*
  * Same as snmp_select_info, but works just one session. 
  */
@@ -5850,12 +5966,35 @@
                       int *numfds,
                       fd_set * fdset, struct timeval *timeout, int *block)
 {
+    nfds_t nfds = 0;
+    int numsessions = _sess_selpol_info(sessp, timeout, block, numfds, fdset, NULL);
+    if (nfds) {
+    DEBUGMSGTL(("snmp_sess_select_info", 
+      "error in call of _sess_select_info\n"));
+    }
+    return numsessions;
+}
+
+/*
+ * Implements snmp_sess_select_info and snmp_sess_poll_info
+ * (two interfaces for the same functionality, just the poll interface
+ * allows for more file handles).
+ * If fd_set is used (old interface), afterwards numfds contains the max
+ * filedescriptor + 1 (needed for select), otherwise nfds contains the
+ * number of elements in the fds list (and numfds is zero).
+ * If the poll() interface is used, the array is resized when necessary.
+ */
+int
+_sess_selpol_info(void *sessp, struct timeval *timeout, int *block,
+                  int *numfds, fd_set *fdset, struct pollfdarr *fdarr)
+{
     struct session_list *slptest = (struct session_list *) sessp;
     struct session_list *slp, *next = NULL;
     netsnmp_request_list *rp;
     struct timeval  now, earliest, delta;
     int             active = 0, requests = 0;
     int             next_alarm = 0;
+    int             fdssize = 0;
 
     timerclear(&earliest);
 
@@ -5901,11 +6040,47 @@
         }
 
         DEBUGMSG(("sess_select", "%d ", slp->transport->sock));
-        if ((slp->transport->sock + 1) > *numfds) {
-            *numfds = (slp->transport->sock + 1);
-        }
 
-        FD_SET(slp->transport->sock, fdset);
+        if (fdset) { /* Use fdset from the select() interface */
+            if ((slp->transport->sock + 1) > *numfds) {
+                *numfds = (slp->transport->sock + 1);
+            }
+            FD_SET(slp->transport->sock, fdset);
+        } else { /* Use fdarr for the poll() interface. */
+            /* Append fd at the end of the array, and increase number of fds */
+            int fdindex = fdarr->nfds++;
+            int fdas = fdarr->arrsize;
+            if (fdindex >= fdas) { 
+                /* increase array size *2 if it's not big enough */
+                int newfdas = (fdas > 0) ? fdas * 2 : 1;
+                struct pollfd *prev = fdarr->fds;
+                fdarr->fds = realloc(fdarr->fds,
+                                     newfdas * sizeof(struct pollfd));
+                if (fdarr->fds == NULL) {
+                    /* realloc failed, revert to old list and abort */
+                    fdarr->fds = prev;
+                    DEBUGMSG(("_sess_select_info",
+                              "realloc failed, aborting.\n"));
+                    break;
+                }
+                /*
+                 * set the new memory to zero
+                 */
+                memset(&((fdarr->fds)[fdas]), 0,
+                        (newfdas - fdas) * sizeof(struct pollfd));
+                fdarr->arrsize = newfdas;
+            }
+            (fdarr->fds)[fdindex].fd = slp->transport->sock;
+            (fdarr->fds)[fdindex].events = 0;
+            (fdarr->fds)[fdindex].events |= POLLIN | POLLPRI;
+#ifdef POLLRDNORM
+            (fdarr->fds)[fdindex].events |= POLLRDNORM;
+#endif
+#ifdef POLLRDBAND
+            (fdarr->fds)[fdindex].events |= POLLRDBAND;
+#endif
+            (fdarr->fds)[fdindex].revents = 0;
+        } 
         if (slp->internal != NULL && slp->internal->requests) {
             /*
              * Found another session with outstanding requests.  
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Net-snmp-coders mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/net-snmp-coders

Reply via email to