]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
Prepare for GSoC2008 NTP MIB daemon import
authorHarlan Stenn <stenn@ntp.org>
Sun, 31 Aug 2008 08:59:46 +0000 (04:59 -0400)
committerHarlan Stenn <stenn@ntp.org>
Sun, 31 Aug 2008 08:59:46 +0000 (04:59 -0400)
bk: 48ba5d82-A_hloOKjFZ84UxFxLWPNg

13 files changed:
ChangeLog
Makefile.am
configure.ac
ntpq/Makefile.am
ntpq/libntpq.c [new file with mode: 0644]
ntpq/libntpq.h [new file with mode: 0644]
ntpq/libntpq_subs.c [new file with mode: 0644]
ntpq/ntpq.c
ntpsnmpd/Makefile.am [new file with mode: 0644]
ntpsnmpd/README [new file with mode: 0644]
ntpsnmpd/ntpSnmpSubagentObject.c [new file with mode: 0644]
ntpsnmpd/ntpSnmpSubagentObject.h [new file with mode: 0644]
ntpsnmpd/ntpsnmpd.c [new file with mode: 0644]

index 6f54cee4cfda1917f078ad045f691fe8e4dea753..8e12fbaef807e24d0d8d2fec10d6e69a91d1c528 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,4 @@
+* Prepare for GSoC2008 NTP MIB daemon import.
 * [BUG 610] Documentation update for NMEA reference clock driver.
 * [Bug 828] Fix IPv4/IPv6 address parsing.
 * Changes from Dave Mills:
index 6cf8188bbe9a86b08eb4dc59f39eac24e8c72559..ee58f0e4e26028039f7eb01fa9038793ff069dc3 100644 (file)
@@ -20,6 +20,7 @@ SUBDIRS+=             \
        ntpdate         \
        ntpdc           \
        ntpq            \
+       @NTPSNMPD_DIR@  \
        parseutil       \
        adjtimed        \
        clockstuff      \
@@ -40,6 +41,7 @@ DIST_SUBDIRS=         \
        ntpdate         \
        ntpdc           \
        ntpq            \
+       ntpsnmpd        \
        parseutil       \
        adjtimed        \
        clockstuff      \
index 41048b2639dc977e03bdafa674a550c413a2c019..d2d665d6ebf0f02b04796254663e2aed572bb828 100644 (file)
@@ -362,6 +362,7 @@ AC_C_VOLATILE
 AC_ISC_POSIX
 AC_PATH_PROG(PATH_SH, sh)
 AC_PATH_PROG(PATH_PERL, perl)
+AC_PATH_PROG(PATH_NET_SNMP_CONFIG, net-snmp-config)
 
 case "$host" in
  *-*-vxworks*)
@@ -3776,6 +3777,31 @@ case "$build" in
     ;;
 esac
 
+AC_MSG_CHECKING(if we want to build ntpsnmpd)
+AC_ARG_WITH(ntpsnmpd,
+       AC_HELP_STRING([--with-ntpsnmpd], [? Build the ntpsnmpd code?]),
+       [ans=$withval],
+       [case "$PATH_NET_SNMP_CONFIG" in
+        /*)    ans=yes ;;
+        *)     ans=no  ;;
+       esac])
+AC_MSG_RESULT($ans)
+
+case "$ans" in
+ yes)
+    case "$PATH_NET_SNMP_CONFIG" in
+     /*)
+       NTPSNMPD_DIR=ntpsnmpd
+       SNMP_LIBS=`$PATH_NET_SNMP_CONFIG --agent-libs`
+       AC_SUBST(SNMP_LIBS)
+       ;;
+     *) AC_MSG_WARN([Cannot build ntpsnmpd as desired - net-snmp-config cannot be found])
+       ;;
+    esac
+    ;;
+esac
+AC_SUBST(NTPSNMPD_DIR)
+
 AC_CACHE_CHECK(if we should always slew the time, ac_cv_var_slew_always,
 [AC_ARG_ENABLE(slew-always,
    AC_HELP_STRING([--enable-slew-always], [s always slew the time]),
@@ -4193,6 +4219,7 @@ AC_CONFIG_FILES(ntpdate/Makefile)
 AC_CONFIG_FILES(ntpdc/Makefile)
 AC_CONFIG_FILES(ntpdc/nl.pl,           [chmod +x ntpdc/nl.pl])
 AC_CONFIG_FILES(ntpq/Makefile)
+AC_CONFIG_FILES(ntpsnmpd/Makefile)
 AC_CONFIG_FILES(parseutil/Makefile)
 AC_CONFIG_FILES(scripts/Makefile)
 AC_CONFIG_FILES(scripts/calc_tickadj,  [chmod +x scripts/calc_tickadj])
index 388008c08825907e298503caef030e2120538674..2a1f9b865551a82b803ee9d764c500dba4f33f1e 100644 (file)
@@ -2,11 +2,13 @@ AUTOMAKE_OPTIONS=
 
 bin_PROGRAMS=  ntpq
 AM_CPPFLAGS=   -I$(top_srcdir)/include $(LIBOPTS_CFLAGS)
-
 # LDADD might need RESLIB and ADJLIB
 ntpq_LDADD=    version.o @EDITLINE_LIBS@ $(LIBOPTS_LDADD) ../libntp/libntp.a
-DISTCLEANFILES=        .version version.c
 noinst_HEADERS=        ntpq.h
+noinst_LIBRARIES=      libntpq.a
+libntpq_LDADD= version.o @EDITLINE_LIBS@ $(LIBOPTS_LDADD) ../libntp/libntp.a
+libntpq_CFLAGS=        -DNO_MAIN_ALLOWED -DBUILD_AS_LIB
+DISTCLEANFILES=        .version version.c
 ETAGS_ARGS=    Makefile.am
 EXTRA_DIST=    ntpq-opts.def ntpq.1 ntpq-opts.texi ntpq-opts.menu
 BUILT_SOURCES= ntpq-opts.c ntpq-opts.h ntpq.1 ntpq-opts.texi ntpq-opts.menu
@@ -19,6 +21,8 @@ std_def_list= $(top_srcdir)/include/debug-opt.def             \
                $(top_srcdir)/include/version.def
 ntpq_SOURCES=  ntpq.c ntpq-subs.c ntpq-opts.c ntpq-opts.h
 
+libntpq_a_SOURCES =    libntpq.c libntpq.h libntpq_subs.c 
+
 $(srcdir)/ntpq-opts.h: $(srcdir)/ntpq-opts.c
 $(srcdir)/ntpq-opts.c: $(srcdir)/ntpq-opts.def $(std_def_list)
        $(run_ag) ntpq-opts.def
@@ -31,9 +35,6 @@ $(srcdir)/ntpq-opts.texi $(srcdir)/ntpq-opts.menu: $(srcdir)/ntpq-opts.def $(std
 
 $(PROGRAMS): $(LDADD)
 
-../libntp/libntp.a:
-       cd ../libntp && $(MAKE)
-
 $(top_srcdir)/version :
        cd $(top_srcdir) && $(MAKE) version
 
diff --git a/ntpq/libntpq.c b/ntpq/libntpq.c
new file mode 100644 (file)
index 0000000..56c4463
--- /dev/null
@@ -0,0 +1,790 @@
+/*****************************************************************************
+ *
+ *  libntpq.c
+ *
+ *  This is the wrapper library for ntpq, the NTP query utility. 
+ *  This library reuses the sourcecode from ntpq and exports a number
+ *  of useful functions in a library that can be linked against applications
+ *  that need to query the status of a running ntpd. The whole 
+ *  communcation is based on mode 6 packets.
+ *
+ ****************************************************************************/
+#define _LIBNTPQC
+#define NO_MAIN_ALLOWED 1
+#define BUILD_AS_LIB
+
+#include "ntpq.c"
+#include "libntpq.h"
+
+/* Function Prototypes */
+int ntpq_openhost(char *);
+int ntpq_closehost(void);
+int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen);
+int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen );
+int ntpq_queryhost_peervars(unsigned short association, char *resultbuf, int maxlen);
+int ntpq_getvar( char *resultbuf, int datalen, const char *varname, char *varvalue, int maxlen);
+int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen);
+int ntpq_read_associations ( unsigned short resultbuf[], int max_entries );
+int ntpq_read_sysvars( char *resultbuf, int maxsize );
+int ntpq_get_assoc_allvars( int associd  );
+int ntpq_get_sysvars( void );
+int ntpq_get_assocs ( void );
+int ntpq_read_assoc_peervars( int associd, char *resultbuf, int maxsize );
+int ntpq_read_assoc_clockvars( int associd, char *resultbuf, int maxsize );
+int ntpq_get_assoc_number ( int associd );
+int ntpq_get_assoc_peervars( int associd );
+int ntpq_get_assoc_clockvars( int associd );
+int ntpq_get_assoc_clocktype ( int assoc_number );
+
+const char *Version = "libntpq 0.3beta";
+
+/* global variables used for holding snapshots of data */
+ char peervars[NTPQ_BUFLEN];
+ int peervarlen = 0;
+ int peervar_assoc = 0;
+ char clockvars[NTPQ_BUFLEN];
+ int clockvarlen = 0;
+ int clockvar_assoc = 0;
+ char sysvars[NTPQ_BUFLEN];
+ int sysvarlen = 0;
+ char *ntpq_resultbuffer[NTPQ_BUFLEN];
+ unsigned short ntpq_associations[MAXASSOC];
+
+struct ntpq_varlist ntpq_varlist[MAXLIST];
+
+/*****************************************************************************
+ *
+ *  ntpq_stripquotes
+ *
+ *  Parses a given character buffer srcbuf and removes all quoted
+ *  characters. The resulting string is copied to the specified 
+ *  resultbuf character buffer.  E.g. \" will be translated into "
+ * 
+ ****************************************************************************
+ * Parameters:
+ *     resultbuf       char*   The resulting string without quoted
+ *                             characters
+ *     srcbuf          char*   The buffer holding the original string
+ *     datalen         int     The number of bytes stored in srcbuf
+ *     maxlen          int     Max. number of bytes for resultbuf
+ *
+ * Returns:
+ *     int             number of chars that have been copied to 
+ *                     resultbuf 
+ ****************************************************************************/
+
+int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen )
+{
+       char* tmpbuf = srcbuf;
+
+       while ( *tmpbuf != 0 )
+       {
+               if ( *tmpbuf == '\"' )
+               {
+                       tmpbuf++;
+                       continue;
+               }
+               
+               if ( *tmpbuf == '\\' )
+               {
+                       tmpbuf++;
+                       switch ( *tmpbuf )
+                       {
+                               /* ignore if end of string */
+                               case 0:
+                                       continue;
+                               /* skip and do not copy */
+                               case '\"': /* quotes */
+                               case 'n': /*newline*/
+                               case 'r': /*carriage return*/
+                               case 'g': /*bell*/
+                               case 't': /*tab*/
+                                       tmpbuf++;
+                                       continue;
+                       }
+               } 
+
+               *resultbuf++ = *tmpbuf++;
+               
+       }
+       
+       *resultbuf = 0;
+       return strlen(resultbuf);
+}                      
+
+
+/*****************************************************************************
+ *
+ *  ntpq_getvar
+ *
+ *  This function parses a given buffer for a variable/value pair and
+ *  copies the value of the requested variable into the specified
+ *  varvalue buffer.
+ *
+ *  It returns the number of bytes copied or zero for an empty result
+ *  (=no matching variable found or empty value)
+ *
+ ****************************************************************************
+ * Parameters:
+ *     resultbuf       char*   The resulting string without quoted
+ *                             characters
+ *     datalen         int     The number of bytes stored in 
+ *                                                     resultbuf
+ *     varname         char*   Name of the required variable 
+ *     varvalue        char*   Where the value of the variable should
+ *                                                     be stored
+ *     maxlen          int     Max. number of bytes for varvalue
+ *
+ * Returns:
+ *     int             number of chars that have been copied to 
+ *                     varvalue
+ ****************************************************************************/
+
+int ntpq_getvar( char *resultbuf, int datalen, const char *varname, char *varvalue, int maxlen)
+{
+    char *name;
+    char *value = NULL;
+
+            while (nextvar(&datalen, &resultbuf, &name, &value)) {
+
+                if ( strcmp(varname, name) == 0 ) {
+                       ntpq_stripquotes(varvalue,value,strlen(value),maxlen);
+                       return strlen(varvalue);
+                }
+            }
+
+            return 0;
+}
+
+
+/*****************************************************************************
+ *
+ *  ntpq_queryhost
+ *
+ *  Sends a mode 6 query packet to the current open host (see 
+ *  ntpq_openhost) and stores the requested variable set in the specified
+ *  character buffer. 
+ *  It returns the number of bytes read or zero for an empty result
+ *  (=no answer or empty value)
+ *
+ ****************************************************************************
+ * Parameters:
+ *      VARSET         u_short Which variable set should be
+ *                             read (PEERVARS or CLOCKVARS)
+ *     association     int     The association ID that should be read
+ *                             0 represents the ntpd instance itself
+ *     resultbuf       char*   The resulting string without quoted
+ *                             characters
+ *     maxlen          int     Max. number of bytes for varvalue
+ *
+ * Returns:
+ *     int             number of bytes that have been copied to 
+ *                     resultbuf
+ *                     - OR -
+ *                     0 (zero) if no reply has been received or
+ *                     another failure occured
+ ****************************************************************************/
+
+int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen)
+{
+       char *datap;
+       int res;
+       int dsize;
+       u_short rstatus;
+       
+       if ( numhosts > 0 )
+               res = doquery(VARSET,association,0,0, (char *)0, &rstatus, &dsize, &datap);
+       else
+               return 0;
+       
+       if ( ( res != 0) || ( dsize == 0 ) ) /* no data */
+               return 0;
+       
+       if ( dsize > maxlen) 
+               dsize = maxlen;
+       
+       
+       /* fill result resultbuf */
+       memcpy(resultbuf, datap, dsize);
+       
+       return dsize;
+}
+
+
+
+/*****************************************************************************
+ *
+ *  ntpq_openhost
+ *
+ *  Sets up a connection to the ntpd instance of a specified host. Note:
+ *  There is no real "connection" established because NTP solely works
+ *  based on UDP.
+ *
+ ****************************************************************************
+ * Parameters:
+ *     hostname        char*   Hostname/IP of the host running ntpd
+ *
+ * Returns:
+ *     int             1 if the host connection could be set up, i.e. 
+ *                     name resolution was succesful and/or IP address
+ *                     has been validated
+ *                     - OR -
+ *                     0 (zero) if a failure occured
+ ****************************************************************************/
+
+int ntpq_openhost(char *hostname)
+{
+       if ( openhost(hostname) )
+       {
+               numhosts = 1;
+       } else {
+               numhosts = 0;
+       }
+       
+       return numhosts;
+       
+}
+
+
+/*****************************************************************************
+ *
+ *  ntpq_closehost
+ *
+ *  Cleans up a connection by closing the used socket. Should be called
+ *  when no further queries are required for the currently used host.
+ *
+ ****************************************************************************
+ * Parameters:
+ *     - none -
+ *
+ * Returns:
+ *     int             0 (zero) if no host has been opened before
+ *                     - OR -
+ *                     the resultcode from the closesocket function call
+ ****************************************************************************/
+
+int ntpq_closehost(void)
+{
+       if ( numhosts )
+        return closesocket(sockfd);
+       
+       return 0;
+}
+
+
+/*****************************************************************************
+ *
+ *  ntpq_read_associations
+ *
+ *  This function queries the ntp host for its associations and returns the 
+ *  number of associations found.
+ *
+ *  It takes an u_short array as its first parameter, this array holds the 
+ *  IDs of the associations, 
+ *  the function will not write more entries than specified with the 
+ *  max_entries parameter.
+ *
+ *  However, if more than max_entries associations were found, the return 
+ *  value of this function will reflect the real number, even if not all 
+ *  associations have been stored in the array.
+ *
+ ****************************************************************************
+ * Parameters:
+ *     resultbuf       u_short*Array that should hold the list of
+ *                             association IDs
+ *     maxentries      int     maximum number of association IDs that can
+ *                             be stored in resultbuf
+ *
+ * Returns:
+ *     int             number of association IDs stored in resultbuf
+ *                     - OR -
+ *                     0 (zero) if a failure occured or no association has
+ *                     been returned.
+ ****************************************************************************/
+ int  ntpq_read_associations ( u_short resultbuf[], int max_entries )
+{
+    int i = 0;
+
+    if (ntpq_dogetassoc()) {       
+        
+        if(numassoc < max_entries)
+          max_entries = numassoc;
+
+        for (i=0;i<max_entries;i++)
+            resultbuf[i] = assoc_cache[i].assid;
+
+        return numassoc;
+    }
+
+    return 0;
+}
+
+
+
+
+/*****************************************************************************
+ *
+ *  ntpq_get_assocs
+ *
+ *  This function reads the associations of a previously selected (with 
+ *  ntpq_openhost) NTP host into its own (global) array and returns the 
+ *  number of associations found. 
+ *
+ *  The obtained association IDs can be read by using the ntpq_get_assoc_id 
+ *  function.
+ *
+ ****************************************************************************
+ * Parameters:
+ *     - none -
+ *
+ * Returns:
+ *     int             number of association IDs stored in resultbuf
+ *                     - OR -
+ *                     0 (zero) if a failure occured or no association has
+ *                     been returned.
+ ****************************************************************************/
+ int  ntpq_get_assocs ( void )
+{
+    return ntpq_read_associations( ntpq_associations, MAXASSOC );
+}
+
+
+/*****************************************************************************
+ *  
+ *  ntpq_get_assoc_number
+ *
+ *  This function returns for a given Association ID the association number 
+ *  in the internal association array, which is filled by the ntpq_get_assocs 
+ *  function.
+ * 
+ ****************************************************************************
+ * Parameters:
+ *     associd         int     requested associaton ID 
+ *
+ * Returns:
+ *     int             the number of the association array element that is
+ *                     representing the given association ID
+ *                     - OR -
+ *                     -1 if a failure occured or no matching association 
+ *                     ID has been found
+ ****************************************************************************/
+int ntpq_get_assoc_number ( int associd )
+{
+   int i = 0;
+
+   for (i=0;i<numassoc;i++) {
+     if (assoc_cache[i].assid == associd)
+       return i;
+    }
+
+   return (-1);
+
+}
+
+
+/*****************************************************************************
+ *  
+ *  ntpq_read_assoc_peervars
+ *
+ *  This function reads the peervars variable-set of a specified association 
+ *  from a NTP host and writes it to the result buffer specified, honoring 
+ *  the maxsize limit.
+ *
+ *  It returns the number of bytes written or 0 when the variable-set is 
+ *  empty or failed to read.
+ *  
+ ****************************************************************************
+ * Parameters:
+ *     associd         int     requested associaton ID 
+ *     resultbuf       char*   character buffer where the variable set
+ *                             should be stored
+ *     maxsize         int     the maximum number of bytes that can be
+ *                             written to resultbuf
+ *
+ * Returns:
+ *     int             number of chars that have been copied to 
+ *                     resultbuf
+ *                     - OR - 
+ *                     0 (zero) if an error occured
+ ****************************************************************************/
+
+  int ntpq_read_assoc_peervars( int associd, char *resultbuf, int maxsize )
+{
+
+    char *datap;
+    int res;
+    int dsize;
+    u_short rstatus;
+       l_fp rec;
+         l_fp ts;
+    char value[NTPQ_BUFLEN];
+
+
+    res = doquery(CTL_OP_READVAR, associd, 0, 0, (char *)0, &rstatus,
+              &dsize, &datap);
+    
+    if (res != 0)
+        return 0;
+
+       get_systime(&ts);
+
+    if (dsize == 0) {
+        if (numhosts > 1)
+            (void) fprintf(stderr, "server=%s ", currenthost);
+        (void) fprintf(stderr,
+                   "***No information returned for association %d\n",
+                   associd);
+        return 0;
+    } else {
+        if ( dsize > maxsize ) 
+            dsize = maxsize;
+
+        memcpy(resultbuf,datap,dsize);        
+        resultbuf[dsize]=0x0;
+        
+        ntpq_getvar(resultbuf, dsize, "rec", value, sizeof (value) );
+
+        if (!decodets(value, &rec))
+                                 L_CLR(&rec);
+
+        memcpy(resultbuf,value,maxsize);        
+        resultbuf[dsize]=0x0;
+        dsize=strlen(resultbuf);
+        
+
+    }
+    return dsize;
+
+}
+
+
+
+
+/*****************************************************************************
+ *  
+ *  ntpq_read_sysvars
+ *
+ *  This function reads the sysvars variable-set from a NTP host and writes it
+ *  to the result buffer specified, honoring the maxsize limit.
+ *
+ *  It returns the number of bytes written or 0 when the variable-set is empty
+ *  or could not be read.
+ *  
+ ****************************************************************************
+ * Parameters:
+ *     resultbuf       char*   character buffer where the variable set
+ *                             should be stored
+ *     maxsize         int     the maximum number of bytes that can be
+ *                             written to resultbuf
+ *
+ * Returns:
+ *     int             number of chars that have been copied to 
+ *                     resultbuf
+ *                     - OR - 
+ *                     0 (zero) if an error occured
+ ****************************************************************************/
+int ntpq_read_sysvars( char *resultbuf, int maxsize )
+{
+
+    char *datap;
+    int res;
+    int dsize;
+    u_short rstatus;
+
+    res = doquery(CTL_OP_READVAR, 0, 0, 0, (char *)0, &rstatus,
+              &dsize, &datap);
+    
+    if (res != 0)
+        return 0;
+
+    if (dsize == 0) {
+        if (numhosts > 1)
+            (void) fprintf(stderr, "server=%s ", currenthost);
+        (void) fprintf(stderr,
+                   "***No sysvar information returned \n");
+        return 0;
+    } else {
+        if ( dsize > maxsize ) 
+            dsize = maxsize;
+
+        memcpy(resultbuf,datap,dsize);
+    }
+
+    return dsize;
+
+}
+
+
+/*****************************************************************************
+ *  ntpq_get_assoc_allvars
+ *
+ *  With this function all association variables for the specified association
+ *  ID can be requested from a NTP host. They are stored internally and can be
+ *  read by using the ntpq_get_peervar or ntpq_get_clockvar functions.
+ *
+ *  Basically this is only a combination of the ntpq_get_assoc_peervars and 
+ *  ntpq_get_assoc_clockvars functions.
+ *
+ *  It returns 1 if both variable-sets (peervars and clockvars) were 
+ *  received successfully. If one variable-set or both of them weren't 
+ *  received,
+ *
+ ****************************************************************************
+ * Parameters:
+ *     associd         int     requested associaton ID 
+ *
+ * Returns:
+ *     int             nonzero if at least one variable set could be read
+ *                     - OR - 
+ *                     0 (zero) if an error occured and both variable sets
+ *                     could not be read
+ ****************************************************************************/
+ int  ntpq_get_assoc_allvars( int associd  )
+{
+    return ( ntpq_get_assoc_peervars ( associd ) & ntpq_get_assoc_clockvars( associd ) );
+}
+
+
+
+
+/*****************************************************************************
+ *
+ *  ntpq_get_sysvars
+ *
+ *  The system variables of a NTP host can be requested by using this function
+ *  and afterwards using ntpq_get_sysvar to read the single variable values.
+ *
+ ****************************************************************************
+ * Parameters:
+ *     - none -
+ *
+ * Returns:
+ *     int             nonzero if the variable set could be read
+ *                     - OR - 
+ *                     0 (zero) if an error occured and the sysvars
+ *                     could not be read
+ ****************************************************************************/
+ int  ntpq_get_sysvars( void )
+{
+        sysvarlen = ( ntpq_read_sysvars( sysvars, sizeof(sysvars )) );
+        if ( sysvarlen <= 0 ) {
+            return 0;
+        } else {
+            return 1;
+        }
+}
+
+
+/*****************************************************************************
+ *  
+ *  ntp_get_peervar
+ *
+ *  This function uses the variable-set which was read by using 
+ *  ntp_get_peervars and searches for a variable specified with varname. If 
+ *  such a variable exists, it writes its value into
+ *  varvalue (maxlen specifies the size of this target buffer).
+ *  
+ ****************************************************************************
+ * Parameters:
+ *     varname         char*   requested variable name
+ *     varvalue        char*   the buffer where the value should go into
+ *     maxlen          int     maximum number of bytes that can be copied to
+ *                             varvalue
+ *
+ * Returns:
+ *     int             number of bytes copied to varvalue
+ *                     - OR - 
+ *                     0 (zero) if an error occured or the variable could 
+ *                     not be found
+ ****************************************************************************/
+int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen)
+{
+    return ( ntpq_getvar(peervars,peervarlen,varname,varvalue,maxlen) );
+}
+
+
+
+/*****************************************************************************
+ *  
+ *  ntpq_get_assoc_peervars
+ *
+ *  This function requests the peer variables of the specified association 
+ *  from a NTP host. In order to access the variable values, the function 
+ *  ntpq_get_peervar must be used.
+ *
+ ****************************************************************************
+ * Parameters:
+ *     associd         int     requested associaton ID 
+ *
+ * Returns:
+ *     int             1 (one) if the peervars have been read
+ *                     - OR - 
+ *                     0 (zero) if an error occured and the variable set
+ *                     could not be read
+ ****************************************************************************/
+ int  ntpq_get_assoc_peervars( int associd )
+{
+        peervarlen = ( ntpq_read_assoc_peervars( associd, peervars, sizeof(peervars )) );
+        if ( peervarlen <= 0 ) {
+            peervar_assoc = 0;
+            return 0;
+        } else {
+            peervar_assoc = associd;
+            return 1;
+        }
+}
+
+
+/*****************************************************************************
+ *  
+ *  ntp_read_assoc_clockvars
+ *
+ *  This function reads the clockvars variable-set of a specified association
+ *  from a NTP host and writes it to the result buffer specified, honoring 
+ *  the maxsize limit.
+ *
+ *  It returns the number of bytes written or 0 when the variable-set is 
+ *  empty or failed to read.
+ *  
+ ****************************************************************************
+ * Parameters:
+ *     associd         int     requested associaton ID 
+ *     resultbuf       char*   character buffer where the variable set
+ *                             should be stored
+ *     maxsize         int     the maximum number of bytes that can be
+ *                             written to resultbuf
+ *
+ * Returns:
+ *     int             number of chars that have been copied to 
+ *                     resultbuf
+ *                     - OR - 
+ *                     0 (zero) if an error occured
+ ****************************************************************************/
+
+int ntpq_read_assoc_clockvars( int associd, char *resultbuf, int maxsize )
+{
+
+    char *datap;
+    int res;
+    int dsize;
+    u_short rstatus;
+    
+    res = ntpq_doquerylist(ntpq_varlist, CTL_OP_READCLOCK, associd, 0, &rstatus, &dsize, &datap);
+    
+    if (res != 0)
+        return 0;
+
+    if (dsize == 0) {
+        if (numhosts > 1) /* no information returned from server */
+       return 0;
+    } else {
+        if ( dsize > maxsize ) 
+            dsize = maxsize;
+
+        memcpy(resultbuf,datap,dsize);
+    }
+
+    return dsize;
+}
+
+
+
+/*****************************************************************************
+ *  
+ *  ntpq_get_assoc_clocktype
+ *
+ *  This function returns a clocktype value for a given association number 
+ *  (not ID!):
+ *
+ *  NTP_CLOCKTYPE_UNKNOWN   Unknown clock type
+ *  NTP_CLOCKTYPE_BROADCAST Broadcast server
+ *  NTP_CLOCKTYPE_LOCAL     Local clock
+ *  NTP_CLOCKTYPE_UNICAST   Unicast server
+ *  NTP_CLOCKTYPE_MULTICAST Multicast server
+ * 
+ ****************************************************************************/
+ int ntpq_get_assoc_clocktype ( int assoc_number )
+{           
+    int type = 0;
+    int i, rc = 0;
+    struct sockaddr_storage dum_store;
+    char value[LENHOSTNAME];
+    char resultbuf[1024];
+
+
+    if ( assoc_number < 0 || assoc_number > numassoc ) {
+        return -1;
+    } else {
+        if ( peervar_assoc != assoc_cache[assoc_number].assid ) {
+            
+            i=ntpq_read_assoc_peervars(assoc_cache[assoc_number].assid, resultbuf, sizeof(resultbuf));
+            if ( i <= 0 ) {
+                return -1;
+            }
+
+            rc = ntpq_getvar(resultbuf, i, "dstadr", value, LENHOSTNAME );
+
+
+        } else {
+            
+            rc = ntpq_get_peervar("dstadr",value,LENHOSTNAME);
+
+        }
+
+        if ( rc ) {
+            if (decodenetnum(value, &dum_store)) {
+                type = ntpq_decodeaddrtype(&dum_store);
+                return type;
+            }
+        }
+
+        return -1;
+    }
+
+    return -1;
+
+}
+
+
+
+/*****************************************************************************
+ *  
+ *  ntpq_get_assoc_clockvars
+ *
+ *  With this function the clock variables of the specified association are 
+ *  requested from a NTP host. This makes only sense for associations with 
+ *  the type 'l' (Local Clock) and you should check this with 
+ *  ntpq_get_assoc_clocktype for each association, before you use this function
+ *  on it.
+ *
+ ****************************************************************************
+ * Parameters:
+ *     associd         int     requested associaton ID 
+ *
+ * Returns:
+ *     int             1 (one) if the clockvars have been read
+ *                     - OR - 
+ *                     0 (zero) if an error occured and the variable set
+ *                     could not be read
+ ****************************************************************************/
+ int  ntpq_get_assoc_clockvars( int associd )
+{
+        
+        if ( ntpq_get_assoc_clocktype(ntpq_get_assoc_number(associd)) != NTP_CLOCKTYPE_LOCAL )
+            return 0;
+  
+        clockvarlen = ( ntpq_read_assoc_clockvars( associd, clockvars, sizeof(clockvars )) );
+        if ( clockvarlen <= 0 ) {
+            clockvar_assoc = 0;
+            return 0;
+        } else {
+            clockvar_assoc = associd;
+            return 1;
+        }
+}
+
+
diff --git a/ntpq/libntpq.h b/ntpq/libntpq.h
new file mode 100644 (file)
index 0000000..c760efe
--- /dev/null
@@ -0,0 +1,108 @@
+/*****************************************************************************
+ *
+ *  libntpq.h
+ *
+ *  This is the wrapper library for ntpq, the NTP query utility. 
+ *  This library reuses the sourcecode from ntpq and exports a number
+ *  of useful functions in a library that can be linked against applications
+ *  that need to query the status of a running ntpd. The whole 
+ *  communcation is based on mode 6 packets.
+ *
+ * This header file can be used in applications that want to link against 
+ * libntpq.
+ *
+ ****************************************************************************/
+
+/* general purpose buffer size */
+#define NTPQ_BUFLEN 2048
+
+/* max. number of associations */
+#ifndef MAXASSOC
+#define MAXASSOC    1024
+#endif
+
+/* general purpose max array size definition */
+#ifndef MAXLIST
+#define MAXLIST 64
+#endif
+
+#ifndef LENHOSTNAME
+#define LENHOSTNAME 256     /* host name is max. 256 characters long */
+#endif
+
+/* NTP Status codes */
+#define NTP_STATUS_INVALID      0
+#define NTP_STATUS_FALSETICKER  1
+#define NTP_STATUS_EXCESS       2
+#define NTP_STATUS_OUTLYER      3
+#define NTP_STATUS_CANDIDATE    4
+#define NTP_STATUS_SELECTED     5
+#define NTP_STATUS_SYSPEER      6
+#define NTP_STATUS_PPSPEER      7
+
+/* NTP association type identifier */
+#define NTP_CLOCKTYPE_UNKNOWN   '-'
+#define NTP_CLOCKTYPE_BROADCAST 'b'
+#define NTP_CLOCKTYPE_LOCAL     'l'
+#define NTP_CLOCKTYPE_UNICAST   'u'
+#define NTP_CLOCKTYPE_MULTICAST 'm'
+
+/* Variable Sets */
+#define PEERVARS CTL_OP_READVAR
+#define CLOCKVARS CTL_OP_CLOCKVAR
+
+/* Variable list struct */
+struct ntpq_varlist {
+       char *name;
+       char *value;
+};
+
+/* global variables used for holding snapshots of data */
+#ifndef _LIBNTPQC
+extern char peervars[];
+extern int peervarlen;
+extern int peervar_assoc;
+extern char clockvars[];
+extern int clockvarlen;
+extern int clockvar_assoc;
+extern char sysvars[];
+extern int sysvarlen;
+extern char *ntpq_resultbuffer[];
+extern struct ntpq_varlist ntpq_varlist[MAXLIST];
+#endif
+
+
+
+/* 
+ * Prototypes of exported libary functions
+ */
+
+/* from libntpq.c */
+#ifndef _LIBNTPQC
+extern int ntpq_openhost(char *);
+extern int ntpq_closehost(void);
+extern int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen);
+extern int ntpq_getvar( char *resultbuf, int datalen, const char *varname, char *varvalue, int maxlen);
+extern int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen );
+extern int ntpq_queryhost_peervars(unsigned short association, char *resultbuf, int maxlen);
+extern int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen);
+extern int ntpq_read_sysvars( char *resultbuf, int maxsize );
+extern int ntpq_get_sysvars( void );
+extern int ntpq_read_associations ( unsigned short resultbuf[], int max_entries );
+extern int ntpq_get_assocs ( void );
+extern int ntpq_get_assoc_number ( int associd );
+extern int ntpq_get_assoc_peervars( int associd );
+extern int ntpq_get_assoc_clockvars( int associd );
+extern int ntpq_get_assoc_allvars( int associd  );
+extern int ntpq_get_assoc_clocktype ( int assoc_number );
+extern int ntpq_read_assoc_peervars( int associd, char *resultbuf, int maxsize );
+extern int ntpq_read_assoc_clockvars( int associd, char *resultbuf, int maxsize );
+ #endif
+
+/* in libntpq_subs.c */
+#ifndef _LIBNTPQSUBSC
+extern int ntpq_dogetassoc(void);
+extern char ntpq_decodeaddrtype(struct sockaddr_storage *sock);
+extern int ntpq_doquerylist(struct ntpq_varlist *, int , int , int , u_short *, int *, char **datap );
+#endif
+
diff --git a/ntpq/libntpq_subs.c b/ntpq/libntpq_subs.c
new file mode 100644 (file)
index 0000000..be94fd5
--- /dev/null
@@ -0,0 +1,50 @@
+/*****************************************************************************
+ *
+ *  libntpq_subs.c
+ *
+ *  This is the second part of the wrapper library for ntpq, the NTP query utility. 
+ *  This library reuses the sourcecode from ntpq and exports a number
+ *  of useful functions in a library that can be linked against applications
+ *  that need to query the status of a running ntpd. The whole 
+ *  communcation is based on mode 6 packets.
+ *
+ *  This source file exports the (private) functions from ntpq-subs.c 
+ *
+ ****************************************************************************/
+
+
+#define _LIBNTPQSUBSC
+#include "ntpq-subs.c"
+#include "libntpq.h"
+
+/* Function Prototypes */
+int ntpq_dogetassoc(void);
+char ntpq_decodeaddrtype(struct sockaddr_storage *sock);
+int ntpq_doquerylist(struct varlist *, int , int , int , u_short *, int *, char **datap );
+
+
+/* the following functions are required internally by a number of libntpq functions 
+ * and since they are defined as static in ntpq-subs.c, they need to be exported here
+ */
+int ntpq_dogetassoc(void)
+{
+       
+       if ( dogetassoc(NULL))
+       {
+               return numassoc;
+       } else {
+               return 0;
+       }
+}
+
+char ntpq_decodeaddrtype(struct sockaddr_storage *sock)
+{
+       return decodeaddrtype(sock);
+}
+
+int ntpq_doquerylist(struct varlist *vlist, int op, int associd, int auth, u_short *rstatus, int *dsize, char **datap )
+{
+    return doquerylist(vlist, op, associd, auth, rstatus, dsize,  &*datap );
+}
+
index 4e904f8150feacdcb9bd5e5054cb01ce35adbd33..54d265f5e0a674aa91880ca384171cfb5f06089f 100644 (file)
@@ -247,6 +247,7 @@ int         ntpqmain        (int,   char **);
  * Built in command handler declarations
  */
 static int     openhost        (const char *);
+
 static int     sendpkt         (char *, int);
 static int     getresponse     (int, int, u_short *, int *, char **, int);
 static int     sendrequest     (int, int, int, int, char *);
@@ -472,7 +473,9 @@ char *progname;
 volatile int debug;
 
 #ifdef NO_MAIN_ALLOWED
+#ifndef BUILD_AS_LIB
 CALL(ntpq,"ntpq",ntpqmain);
+#endif
 
 void clear_globals(void)
 {
@@ -501,6 +504,7 @@ main(
 }
 #endif
 
+#ifndef BUILD_AS_LIB
 int
 ntpqmain(
        int argc,
@@ -644,12 +648,12 @@ ntpqmain(
 #endif /* SYS_WINNT */
        return 0;
 }
-
+#endif // build as lib
 
 /*
  * openhost - open a socket to a host
  */
-static int
+static int
 openhost(
        const char *hname
        )
@@ -3333,3 +3337,4 @@ assoccmp(
        return 0;
 }
 #endif /* not QSORT_USES_VOID_P */
+
diff --git a/ntpsnmpd/Makefile.am b/ntpsnmpd/Makefile.am
new file mode 100644 (file)
index 0000000..7131b01
--- /dev/null
@@ -0,0 +1,19 @@
+AUTOMAKE_OPTIONS= 
+
+sbin_PROGRAMS= ntpsnmpd
+ntpsnmpd_SOURCES= ntpsnmpd.c ntpSnmpSubagentObject.c
+# HMS: we probably want a version.o file here, too.
+LDADD=         ../ntpq/libntpq.a ../libntp/libntp.a @SNMP_LIBS@
+AM_CPPFLAGS=   -I$(top_srcdir)/ntpq -I$(top_srcdir)/include `net-snmp-config --cflags`
+
+#CFLAGS=-I. -I../ntp-dev/ntpq -I../ntp-dev/include -I../net-snmp/include `net-snmp-config --cflags`  -g
+#BUILDLIBS=`net-snmp-config --libs` 
+#BUILDAGENTLIBS=`net-snmp-config --agent-libs`  
+#BUILDNTPLIBS=-L ../ntp-dev/libntp -lntp -L../ntp-dev/ntpq -lntpq 
+
+#ntpsnmpd: $(OBJS)
+#      $(CC)  -o ntpsnmpd  $(OBJS) $(BUILDNTPLIBS) $(BUILDAGENTLIBS) 
+#
+#ntpSnmpPluginObject.so: ntpSnmpPluginObject.c Makefile
+#      $(CC) $(CFLAGS) $(DLFLAGS) -c -o ntpSnmpPluginObject.o ntpSnmpPluginObject.c
+#      $(CC) $(CFLAGS) $(DLFLAGS) -o ntpSnmpPluginObject.so ntpSnmpPluginObject.o
diff --git a/ntpsnmpd/README b/ntpsnmpd/README
new file mode 100644 (file)
index 0000000..5227f40
--- /dev/null
@@ -0,0 +1,40 @@
+NTP SNMP subagent for Net-SNMP
+
+Installation Guides:
+
+- install net-snmp from source (configure, make;, make install)
+- edit the snmpd configuration file (/usr/local/share/snmp/snmpd.conf):
+  add the lines
+   master agentx
+   agentXSocket   tcp:localhost:705
+  and check which read-only community is configured (should be "rocommunity  public")  
+- start snmpd (sudo /usr/local/sbin/snmpd) and check that it is running correctly by running the command
+  snmpwalk -v2c -c public localhost
+  (which should output a lot of data values for the supported built-in MIBs of net-snmp)
+- build the libntpq and the libntp library
+- build the ntpsnmpd application (make) and run it (./ntpsnmpd)
+- now you can run 
+   snmpwalk -v2c -c public localhost enterprises.5597.99
+  which should give you a list of all currently supported NTP MIB objects and their current values
+  
+Please note that currently I use a private MIB OID (enterprises.5597 is the Meinberg top level OEM OID and 99 is my temporary working space for this project). 
+The final OID has to be registered with IANA and this is done by the RFC Editor when the NTPv4 MIB RFC is standardized. 
+I will try to do this earlier in order to be able to have a working solution at the end of this project.
+
+In its current state the daemon supports these objects:
+
+ntpEntSoftwareName
+ntpEntSoftwareVersion
+ntpEntSoftwareVersionVal
+ntpEntSoftwareVendor
+ntpEntSystemType
+ntpEntTimeResolution
+ntpEntTimeResolutionVal
+ntpEntTimePrecision
+ntpEntTimePrecisionVal
+ntpEntTimeDistance
+
+They all use the libntpq library to access information from the ntpd instance with mode 6 packets.
+
+Next step is to implement the status section of the MIB (section 2). 
+
diff --git a/ntpsnmpd/ntpSnmpSubagentObject.c b/ntpsnmpd/ntpSnmpSubagentObject.c
new file mode 100644 (file)
index 0000000..8e6883a
--- /dev/null
@@ -0,0 +1,490 @@
+/*****************************************************************************
+ *
+ *  ntpSnmpSubAgentObject.c
+ *
+ *  This file provides the callback functions for net-snmp and registers the 
+ *  serviced MIB objects with the master agent.
+ * 
+ *  Each object has its own callback function that is called by the 
+ *  master agent process whenever someone queries the corresponding MIB
+ *  object. 
+ * 
+ *  At the moment this triggers a full send/receive procedure for each
+ *  queried MIB object, one of the things that are still on my todo list:
+ *  a caching mechanism that reduces the number of requests sent to the
+ *  ntpd process.
+ *
+ ****************************************************************************/
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include "ntpSnmpSubagentObject.h"
+#include <libntpq.h>
+
+/* general purpose buffer length definition */
+#define NTPQ_BUFLEN 2048
+
+static int      ntpSnmpSubagentObject = 3;
+
+
+char ntpvalue[NTPQ_BUFLEN];
+
+/*****************************************************************************
+ *
+ *  read_ntp_value
+ *
+ *  This function retrieves the value for a given variable, currently
+ *  this only supports sysvars. It starts a full mode 6 send/receive/parse
+ *  iteration and needs to be optimized, e.g. by using a caching mechanism
+ *  
+ ****************************************************************************
+ * Parameters:
+ *     variable        char*   The name of the required variable
+ *     rbuffer         char*   The buffer where the value goes
+ *     maxlength       int     Max. number of bytes for resultbuf
+ *
+ * Returns:
+ *     u_int           number of chars that have been copied to 
+ *                     rbuffer 
+ ****************************************************************************/
+
+unsigned int read_ntp_value(char *variable, char *rbuffer, unsigned int maxlength)
+{
+       unsigned int i, sv_len = 0;
+       char sv_data[NTPQ_BUFLEN];
+       
+       memset (sv_data,0, NTPQ_BUFLEN);
+       sv_len= ntpq_read_sysvars ( sv_data, NTPQ_BUFLEN );
+               
+       if ( sv_len )
+       {
+               i=ntpq_getvar( sv_data, sv_len , variable, rbuffer, maxlength);
+               return i;
+       } else {
+               return 0;
+       }
+
+}
+
+
+/*****************************************************************************
+ *
+ *  The get_xxx functions
+ *
+ *  The following function calls are callback functions that will be 
+ *  used by the master agent process to retrieve a value for a requested 
+ *  MIB object. 
+ *
+ ****************************************************************************/
+
+
+int get_ntpEntSoftwareName (netsnmp_mib_handler *handler,
+                               netsnmp_handler_registration *reginfo,
+                               netsnmp_agent_request_info *reqinfo,
+                               netsnmp_request_info *requests)
+{
+ char ntp_softwarename[NTPQ_BUFLEN];
+       
+   memset (ntp_softwarename, 0, NTPQ_BUFLEN);
+       
+   switch (reqinfo->mode) {
+   case MODE_GET:
+   {
+       if ( read_ntp_value("product", ntpvalue, NTPQ_BUFLEN) )
+    {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)ntpvalue,
+                             strlen(ntpvalue)
+                            );
+    }
+    break;
+
+    if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) )
+    {
+       ntpq_parsestring(ntp_softwarename, ntpvalue, strlen(ntpvalue), NTPQ_BUFLEN, 'd', 1);
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)ntp_softwarename,
+                             strlen(ntp_softwarename)
+                            );
+    } else {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)"N/A",
+                             3
+                            );
+    }
+    break;
+    
+  }
+
+
+  default:
+         /* If we cannot get the information we need, we will return a generic error to the SNMP client */
+        return SNMP_ERR_GENERR;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+int get_ntpEntSoftwareVersion (netsnmp_mib_handler *handler,
+                               netsnmp_handler_registration *reginfo,
+                               netsnmp_agent_request_info *reqinfo,
+                               netsnmp_request_info *requests)
+{
+
+   switch (reqinfo->mode) {
+   case MODE_GET:
+   {
+    
+    if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) )
+    {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)ntpvalue,
+                             strlen(ntpvalue)
+                            );
+    } else {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)"N/A",
+                             3
+                            );
+    }
+    break;
+    
+  }
+
+
+  default:
+         /* If we cannot get the information we need, we will return a generic error to the SNMP client */
+        return SNMP_ERR_GENERR;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+int get_ntpEntSoftwareVersionVal (netsnmp_mib_handler *handler,
+                               netsnmp_handler_registration *reginfo,
+                               netsnmp_agent_request_info *reqinfo,
+                               netsnmp_request_info *requests)
+{
+
+    int i = 0;
+   switch (reqinfo->mode) {
+   case MODE_GET:
+   {
+    
+    if ( read_ntp_value("versionval", ntpvalue, NTPQ_BUFLEN) )
+    {
+       i=atoi(ntpvalue);
+       snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+                             (u_char *) &i,
+                             sizeof (i)
+                            );
+    } else {
+       i = 0;
+       snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+                             (u_char *) &i,
+                             sizeof(i)
+                            );
+    }
+    break;
+    
+  }
+
+
+  default:
+         /* If we cannot get the information we need, we will return a generic error to the SNMP client */
+        return SNMP_ERR_GENERR;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+
+int get_ntpEntSoftwareVendor (netsnmp_mib_handler *handler,
+                               netsnmp_handler_registration *reginfo,
+                               netsnmp_agent_request_info *reqinfo,
+                               netsnmp_request_info *requests)
+{
+
+   switch (reqinfo->mode) {
+   case MODE_GET:
+   {
+    
+    if ( read_ntp_value("vendor", ntpvalue, NTPQ_BUFLEN) )
+    {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)ntpvalue,
+                             strlen(ntpvalue)
+                            );
+    } else {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)"N/A",
+                             3
+                            );
+    }
+    break;
+
+  default:
+         /* If we cannot get the information we need, we will return a generic error to the SNMP client */
+        return SNMP_ERR_GENERR;
+   }
+  }
+  return SNMP_ERR_NOERROR;
+}
+
+
+int get_ntpEntSystemType (netsnmp_mib_handler *handler,
+                               netsnmp_handler_registration *reginfo,
+                               netsnmp_agent_request_info *reqinfo,
+                               netsnmp_request_info *requests)
+{
+
+   switch (reqinfo->mode) {
+   case MODE_GET:
+   {
+    
+    if ( read_ntp_value("systemtype", ntpvalue, NTPQ_BUFLEN) )
+    {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)ntpvalue,
+                             strlen(ntpvalue)
+                            );
+    }
+          
+    if ( read_ntp_value("system", ntpvalue, NTPQ_BUFLEN) )
+    {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)ntpvalue,
+                             strlen(ntpvalue)
+                            );
+    } else {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)"N/A",
+                             3
+                            );
+    }
+    break;
+    
+  }
+
+
+  default:
+         /* If we cannot get the information we need, we will return a generic error to the SNMP client */
+        return SNMP_ERR_GENERR;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+int get_ntpEntTimeResolution (netsnmp_mib_handler *handler,
+                               netsnmp_handler_registration *reginfo,
+                               netsnmp_agent_request_info *reqinfo,
+                               netsnmp_request_info *requests)
+{
+
+   switch (reqinfo->mode) {
+   case MODE_GET:
+   {
+    
+    if ( read_ntp_value("resolution", ntpvalue, NTPQ_BUFLEN) )
+    {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)ntpvalue,
+                             strlen(ntpvalue)
+                            );
+    } else {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)"N/A",
+                             3
+                            );
+    }
+    break;
+    
+  }
+
+
+  default:
+         /* If we cannot get the information we need, we will return a generic error to the SNMP client */
+        return SNMP_ERR_GENERR;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+int get_ntpEntTimeResolutionVal (netsnmp_mib_handler *handler,
+                               netsnmp_handler_registration *reginfo,
+                               netsnmp_agent_request_info *reqinfo,
+                               netsnmp_request_info *requests)
+{
+
+    int i = 0;
+   switch (reqinfo->mode) {
+   case MODE_GET:
+   {
+    
+    if ( read_ntp_value("resolutionval", ntpvalue, NTPQ_BUFLEN) )
+    {
+       i=atoi(ntpvalue);
+       snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+                             (u_char *) &i,
+                             sizeof (i)
+                            );
+    } else {
+       i = 0;
+       snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+                             (u_char *) &i,
+                             sizeof(i)
+                            );
+    }
+    break;
+    
+  }
+
+
+  default:
+         /* If we cannot get the information we need, we will return a generic error to the SNMP client */
+        return SNMP_ERR_GENERR;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+int get_ntpEntTimePrecision (netsnmp_mib_handler *handler,
+                               netsnmp_handler_registration *reginfo,
+                               netsnmp_agent_request_info *reqinfo,
+                               netsnmp_request_info *requests)
+{
+   switch (reqinfo->mode) {
+   case MODE_GET:
+   {
+    
+    if ( read_ntp_value("precision", ntpvalue, NTPQ_BUFLEN) )
+    {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)ntpvalue,
+                             strlen(ntpvalue)
+                            );
+    } else {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)"N/A",
+                             3
+                            );
+    }
+    break;
+    
+  }
+
+
+  default:
+         /* If we cannot get the information we need, we will return a generic error to the SNMP client */
+        return SNMP_ERR_GENERR;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+int get_ntpEntTimePrecisionVal (netsnmp_mib_handler *handler,
+                               netsnmp_handler_registration *reginfo,
+                               netsnmp_agent_request_info *reqinfo,
+                               netsnmp_request_info *requests)
+{
+
+    int i = 0;
+   switch (reqinfo->mode) {
+   case MODE_GET:
+   {
+    
+    if ( read_ntp_value("precision", ntpvalue, NTPQ_BUFLEN) )
+    {
+       i=atoi(ntpvalue);
+       snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+                             (u_char *) &i,
+                             sizeof (i)
+                            );
+    } else {
+       i = 0;
+       snmp_set_var_typed_value(requests->requestvb, ASN_INTEGER,
+                             (u_char *) &i,
+                             sizeof(i)
+                            );
+    }
+    break;
+    
+  }
+
+
+  default:
+         /* If we cannot get the information we need, we will return a generic error to the SNMP client */
+        return SNMP_ERR_GENERR;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+
+int get_ntpEntTimeDistance (netsnmp_mib_handler *handler,
+                               netsnmp_handler_registration *reginfo,
+                               netsnmp_agent_request_info *reqinfo,
+                               netsnmp_request_info *requests)
+{
+   switch (reqinfo->mode) {
+   case MODE_GET:
+   {
+    
+    if ( read_ntp_value("rootdelay", ntpvalue, NTPQ_BUFLEN) )
+    {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)ntpvalue,
+                             strlen(ntpvalue)
+                            );
+    } else {
+       snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
+                             (u_char *)"N/A",
+                             3
+                            );
+    }
+    break;
+    
+  }
+
+
+  default:
+         /* If we cannot get the information we need, we will return a generic error to the SNMP client */
+        return SNMP_ERR_GENERR;
+  }
+
+  return SNMP_ERR_NOERROR;
+}
+
+
+/*
+ *
+ * Initialize sub agent
+ * TODO: Define NTP MIB OID (has to be assigned by IANA)
+ * At the moment we use a private MIB branch (enterprises.5597.99)
+ */
+
+void
+init_ntpSnmpSubagentObject(void)
+{
+       
+    /* Register all MIB objects with the agentx master */
+       
+  _SETUP_OID_RO( ntpEntSoftwareName ,                  NTPV4_OID , 1, 1, 0  );
+  _SETUP_OID_RO( ntpEntSoftwareVersion ,       NTPV4_OID , 1, 2, 0  );
+  _SETUP_OID_RO( ntpEntSoftwareVersionVal ,    NTPV4_OID , 1, 3, 0  );
+  _SETUP_OID_RO( ntpEntSoftwareVendor ,        NTPV4_OID , 1, 4, 0  );
+  _SETUP_OID_RO( ntpEntSystemType ,            NTPV4_OID , 1, 5, 0  );
+  _SETUP_OID_RO( ntpEntTimeResolution ,        NTPV4_OID , 1, 6, 0  );
+  _SETUP_OID_RO( ntpEntTimeResolutionVal ,     NTPV4_OID , 1, 7, 0  );
+  _SETUP_OID_RO( ntpEntTimePrecision ,         NTPV4_OID , 1, 8, 0  );
+  _SETUP_OID_RO( ntpEntTimePrecisionVal ,      NTPV4_OID , 1, 9, 0  );
+  _SETUP_OID_RO( ntpEntTimeDistance ,                  NTPV4_OID , 1,10, 0  );
+       
+}
+
diff --git a/ntpsnmpd/ntpSnmpSubagentObject.h b/ntpsnmpd/ntpSnmpSubagentObject.h
new file mode 100644 (file)
index 0000000..6dcab4e
--- /dev/null
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ *
+ *  ntpSnmpSubAgentObject.h
+ *
+ *     Definitions and macros for ntpSnmpSubAgentObject.c
+ *
+ ****************************************************************************/
+
+
+#ifndef NTPSNMPSUBAGENTOBJECT_H
+#define NTPSNMPSUBAGENTOBJECT_H
+
+/* Function Prototypes */
+
+
+/* Initialization */
+void            init_ntpSnmpSubagentObject(void);
+
+/* MIB Section 1 Callback Functions*/
+Netsnmp_Node_Handler get_ntpEntSoftwareName;
+Netsnmp_Node_Handler get_ntpEntSoftwareVersion;
+Netsnmp_Node_Handler get_ntpEntSoftwareVersionVal;
+Netsnmp_Node_Handler get_ntpEntSoftwareVendor;
+Netsnmp_Node_Handler get_ntpEntSystemType;
+Netsnmp_Node_Handler get_ntpEntTimeResolution;
+Netsnmp_Node_Handler get_ntpEntTimeResolutionVal;
+Netsnmp_Node_Handler get_ntpEntTimePrecision;
+Netsnmp_Node_Handler get_ntpEntTimePrecisionVal;
+Netsnmp_Node_Handler get_ntpEntTimeDistance;
+
+/* MIB Section 2 Callback Functions (TODO) */
+Netsnmp_Node_Handler get_ntpEntStatusCurrentMode;
+Netsnmp_Node_Handler get_ntpEntStatusCurrentModeVal;
+Netsnmp_Node_Handler get_ntpEntStatusStratum;
+Netsnmp_Node_Handler get_ntpEntStatusActiveRefSourceId;
+Netsnmp_Node_Handler get_ntpEntStatusActiveRefSourceName;
+Netsnmp_Node_Handler get_ntpEntStatusActiveOffset;
+
+/* TODO: This needs to be changed as soon as the official OID has been registered with IANA */
+#define NTPV4_OID 1,3,6,1,4,1,5597,99
+
+
+/* The following two macros simplify the registration of the callback functions 
+ * and allow to easily specify the name and OID of either read-only (RO) or read-write (RW) functions
+ */
+#define _SETUP_OID_RO( _oidname, ... )  \
+    static oid _oidname##_oid [] = { __VA_ARGS__ };                                                                             \
+   {                                                                                                                                                                            \
+         netsnmp_register_read_only_instance(netsnmp_create_handler_registration                        \
+                                        ("#_oidname",                                                                                                             \
+                                         get_##_oidname,                                                                                                        \
+                                         _oidname##_oid,                                                                                                        \
+                                         OID_LENGTH                                                                                                             \
+                                         ( _oidname##_oid ),                                                                                                    \
+                                         HANDLER_CAN_RONLY));                                                                                           \
+   }
+
+#define _SETUP_OID_RW( _oidname, ... )                                                                                                          \
+    static oid _oidname##_oid [] = { __VA_ARGS__ };                                                                             \
+   {                                                                                                                                                                            \
+         netsnmp_register_instance(netsnmp_create_handler_registration                                          \
+                                        ("#_oidname",                                                                                                             \
+                                         do_##_oidname,                                                                                                         \
+                                         _oidname##_oid,                                                                                                        \
+                                         OID_LENGTH                                                                                                             \
+                                         ( _oidname##_oid ),                                                                                                    \
+                                         HANDLER_CAN_RWRITE));                                                                                          \
+   }
+
+
+#endif
diff --git a/ntpsnmpd/ntpsnmpd.c b/ntpsnmpd/ntpsnmpd.c
new file mode 100644 (file)
index 0000000..a21d430
--- /dev/null
@@ -0,0 +1,107 @@
+/*****************************************************************************
+ *
+ *  ntpsnmpd.c
+ *
+ *  The NTP SNMP daemon is an Agent X subagent application that 
+ *  registers itself with a running SNMP Agent X master process running
+ *  on the same machine on port TCP 705. It utilizes the libntqp library
+ *  which accesses status and general data of a running ntpd process on
+ *  the same machine and enables the user to monitor the status of a
+ *  ntp daemon process via SNMP. 
+ *
+ *  This started as a Google Summer of Code 2008 project, 
+ *  including the ntpsnmpd sub agent and the libntpq library. 
+ *
+ *  For more information please visit
+ *     http://support.ntp.org/bin/view/Dev/GSoC2008snmp
+ *  Or contact:
+ *   Harlan Stenn   (Mentor) at stenn@ntp.org
+ *   Heiko Gerstung (Student) at gerstung@ntp.org 
+ *
+ ****************************************************************************/
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <signal.h>
+#include <ntpSnmpSubagentObject.h>
+#include <sys/time.h>
+#include <libntpq.h>
+#ifdef SOLARIS /* needed with at least Solaris 8 */
+#include <siginfo.h>
+#endif
+
+static int keep_running;
+
+RETSIGTYPE
+stop_server(int a) {
+    keep_running = 0;
+}
+
+/* The main function just sets up a few things and then enters a loop in which it will 
+ * wait for SNMP requests coming from the master agent 
+ */
+
+int
+main (int argc, char **argv) {
+  int background = 0; /* start as background process */
+  int syslog = 1; /* use syslog for logging */
+  char varvalue[1024];
+       
+
+  /* using the net-snmp syslog facility */
+  if (syslog)
+    snmp_enable_calllog();
+  else
+    snmp_enable_stderrlog();
+
+  /* Become Subagent */
+    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1);
+  /* go into background mode, if requested */
+  if (background && netsnmp_daemonize(1, !syslog))
+      exit(1);
+
+  /* Now register with the master Agent X process */
+
+  /* call Netsnmp socket startup macro, which will initialize the network stuff if required */
+  SOCK_STARTUP;
+
+  /* Set AgentX socket interface */
+  netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID,
+                            NETSNMP_DS_AGENT_X_SOCKET, "tcp:localhost:705");
+
+  init_agent("ntpsnmpd");
+
+  /* Try to connect to ntpd */
+  if ( ntpq_openhost("localhost") == 0 )
+  {
+       fprintf(stderr, "Error: Could not connect to ntpd. Aborting.\n");
+       exit(1);
+  }
+  
+
+  /* Register callback functions ...  */
+  init_ntpSnmpSubagentObject();  
+  init_snmp("ntpsnmpd");
+
+  /* Signal handler */
+  keep_running = 1;
+  signal(SIGTERM, stop_server);
+  signal(SIGINT, stop_server);
+
+  snmp_log(LOG_INFO,"ntpsnmpd started.\n");
+  
+  /* main loop here... */
+  while(keep_running) {
+       agent_check_and_process(1); /* 0 == don't block */
+  }
+
+  /* at shutdown time */
+  ntpq_closehost();
+  snmp_shutdown("ntpsnmpd");
+  SOCK_CLEANUP;
+
+  return 0;
+}
+