From: Harlan Stenn Date: Sun, 31 Aug 2008 08:59:46 +0000 (-0400) Subject: Prepare for GSoC2008 NTP MIB daemon import X-Git-Tag: NTP_4_2_5P128~3^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ea98f9203776f2a8b08f18d49285fbddd15e2da4;p=thirdparty%2Fntp.git Prepare for GSoC2008 NTP MIB daemon import bk: 48ba5d82-A_hloOKjFZ84UxFxLWPNg --- diff --git a/ChangeLog b/ChangeLog index 6f54cee4c..8e12fbaef 100644 --- 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: diff --git a/Makefile.am b/Makefile.am index 6cf8188bb..ee58f0e4e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/configure.ac b/configure.ac index 41048b263..d2d665d6e 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/ntpq/Makefile.am b/ntpq/Makefile.am index 388008c08..2a1f9b865 100644 --- a/ntpq/Makefile.am +++ b/ntpq/Makefile.am @@ -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 index 000000000..56c446370 --- /dev/null +++ b/ntpq/libntpq.c @@ -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 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 index 000000000..c760efefd --- /dev/null +++ b/ntpq/libntpq.h @@ -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 index 000000000..be94fd561 --- /dev/null +++ b/ntpq/libntpq_subs.c @@ -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 ); +} + diff --git a/ntpq/ntpq.c b/ntpq/ntpq.c index 4e904f815..54d265f5e 100644 --- a/ntpq/ntpq.c +++ b/ntpq/ntpq.c @@ -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 index 000000000..7131b017c --- /dev/null +++ b/ntpsnmpd/Makefile.am @@ -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 index 000000000..5227f40d3 --- /dev/null +++ b/ntpsnmpd/README @@ -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 index 000000000..8e6883ae9 --- /dev/null +++ b/ntpsnmpd/ntpSnmpSubagentObject.c @@ -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 +#include +#include +#include "ntpSnmpSubagentObject.h" +#include + +/* 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 index 000000000..6dcab4e38 --- /dev/null +++ b/ntpsnmpd/ntpSnmpSubagentObject.h @@ -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 index 000000000..a21d43027 --- /dev/null +++ b/ntpsnmpd/ntpsnmpd.c @@ -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 +#include +#include +#include +#include +#include +#include +#ifdef SOLARIS /* needed with at least Solaris 8 */ +#include +#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; +} +