summaryrefslogtreecommitdiff
path: root/ntpsnmpd/ntpSnmpSubagentObject.c
diff options
context:
space:
mode:
Diffstat (limited to 'ntpsnmpd/ntpSnmpSubagentObject.c')
-rw-r--r--ntpsnmpd/ntpSnmpSubagentObject.c549
1 files changed, 549 insertions, 0 deletions
diff --git a/ntpsnmpd/ntpSnmpSubagentObject.c b/ntpsnmpd/ntpSnmpSubagentObject.c
new file mode 100644
index 0000000..f65fedf
--- /dev/null
+++ b/ntpsnmpd/ntpSnmpSubagentObject.c
@@ -0,0 +1,549 @@
+/*****************************************************************************
+ *
+ * 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 <ntp_snmp.h>
+#include <ctype.h>
+#include <ntp.h>
+#include <libntpq.h>
+
+/* general purpose buffer length definition */
+#define NTPQ_BUFLEN 2048
+
+char ntpvalue[NTPQ_BUFLEN];
+
+
+/*****************************************************************************
+ *
+ * ntpsnmpd_parse_string
+ *
+ * This function will parse a given NULL terminated string and cut it
+ * into a fieldname and a value part (using the '=' as the delimiter.
+ * The fieldname will be converted to uppercase and all whitespace
+ * characters are removed from it.
+ * The value part is stripped, e.g. all whitespace characters are removed
+ * from the beginning and end of the string.
+ * If the value is started and ended with quotes ("), they will be removed
+ * and everything between the quotes is left untouched (including
+ * whitespace)
+ * Example:
+ * server host name = hello world!
+ * will result in a field string "SERVERHOSTNAME" and a value
+ * of "hello world!".
+ * My first Parameter = " is this! "
+ * results in a field string "MYFIRSTPARAMETER" and a value " is this! "
+ ****************************************************************************
+ * Parameters:
+ * string const char * The source string to parse.
+ * NOTE: must be NULL terminated!
+ * field char * The buffer for the field name.
+ * fieldsize size_t The size of the field buffer.
+ * value char * The buffer for the value.
+ * valuesize size_t The size of the value buffer.
+ *
+ * Returns:
+ * size_t length of value string
+ ****************************************************************************/
+
+size_t
+ntpsnmpd_parse_string(
+ const char * string,
+ char * field,
+ size_t fieldsize,
+ char * value,
+ size_t valuesize
+ )
+{
+ int i;
+ int j;
+ int loop;
+ size_t str_cnt;
+ size_t val_cnt;
+
+ /* we need at least one byte to work with to simplify */
+ if (fieldsize < 1 || valuesize < 1)
+ return 0;
+
+ str_cnt = strlen(string);
+
+ /* Parsing the field name */
+ j = 0;
+ loop = TRUE;
+ for (i = 0; loop && i <= str_cnt; i++) {
+ switch (string[i]) {
+
+ case '\t': /* Tab */
+ case '\n': /* LF */
+ case '\r': /* CR */
+ case ' ': /* Space */
+ break;
+
+ case '=':
+ loop = FALSE;
+ break;
+
+ default:
+ if (j < fieldsize)
+ field[j++] = toupper(string[i]);
+ }
+ }
+
+ j = min(j, fieldsize - 1);
+ field[j] = '\0';
+
+ /* Now parsing the value */
+ value[0] = '\0';
+ j = 0;
+ for (val_cnt = 0; i < str_cnt; i++) {
+ if (string[i] > 0x0D && string[i] != ' ')
+ val_cnt = min(j + 1, valuesize - 1);
+
+ if (value[0] != '\0' ||
+ (string[i] > 0x0D && string[i] != ' ')) {
+ if (j < valuesize)
+ value[j++] = string[i];
+ }
+ }
+ value[val_cnt] = '\0';
+
+ if (value[0] == '"') {
+ val_cnt--;
+ strlcpy(value, &value[1], valuesize);
+ if (val_cnt > 0 && value[val_cnt - 1] == '"') {
+ val_cnt--;
+ value[val_cnt] = '\0';
+ }
+ }
+
+ return val_cnt;
+}
+
+
+/*****************************************************************************
+ *
+ * ntpsnmpd_cut_string
+ *
+ * This function will parse a given NULL terminated string and cut it
+ * into fields using the specified delimiter character.
+ * It will then copy the requested field into a destination buffer
+ * Example:
+ * ntpsnmpd_cut_string(read:my:lips:fool, RESULT, ':', 2, sizeof(RESULT))
+ * will copy "lips" to RESULT.
+ ****************************************************************************
+ * Parameters:
+ * src const char * The name of the source string variable
+ * NOTE: must be NULL terminated!
+ * dest char * The name of the string which takes the
+ * requested field content
+ * delim char The delimiter character
+ * fieldnumber int The number of the required field
+ * (start counting with 0)
+ * maxsize size_t The maximum size of dest
+ *
+ * Returns:
+ * size_t length of resulting dest string
+ ****************************************************************************/
+
+size_t
+ntpsnmpd_cut_string(
+ const char * string,
+ char * dest,
+ char delim,
+ int fieldnumber,
+ size_t maxsize
+ )
+{
+ size_t i;
+ size_t j;
+ int l;
+ size_t str_cnt;
+
+ if (maxsize < 1)
+ return 0;
+
+ str_cnt = strlen(string);
+ j = 0;
+ memset(dest, 0, maxsize);
+
+ /* Parsing the field name */
+ for (i = 0, l = 0; i < str_cnt && l <= fieldnumber; i++) {
+ if (string[i] == delim)
+ l++; /* next field */
+ else if (l == fieldnumber && j < maxsize)
+ dest[j++] = string[i];
+ }
+ j = min(j, maxsize - 1);
+ dest[j] = '\0';
+
+ return j;
+}
+
+
+/*****************************************************************************
+ *
+ * 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
+ ****************************************************************************/
+
+size_t
+read_ntp_value(
+ const char * variable,
+ char * value,
+ size_t valuesize
+ )
+{
+ size_t sv_len;
+ char sv_data[NTPQ_BUFLEN];
+
+ memset(sv_data, 0, sizeof(sv_data));
+ sv_len = ntpq_read_sysvars(sv_data, sizeof(sv_data));
+
+ if (0 == sv_len)
+ return 0;
+ else
+ return ntpq_getvar(sv_data, sv_len, variable, value,
+ valuesize);
+}
+
+
+/*****************************************************************************
+ *
+ * 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)
+ );
+ }
+ else if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) )
+ {
+ ntpsnmpd_cut_string(ntpvalue, ntp_softwarename, ' ', 0, sizeof(ntp_softwarename)-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_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;
+}
+
+
+/*
+ * ntpEntTimeResolution
+ * "The time resolution in integer format, where the resolution
+ * is represented as divisions of a second, e.g., a value of 1000
+ * translates to 1.0 ms."
+ *
+ * ntpEntTimeResolution is a challenge for ntpd, as the resolution is
+ * not known nor exposed by ntpd, only the measured precision (time to
+ * read the clock).
+ *
+ * Logically the resolution must be at least the precision, so report
+ * it as our best approximation of resolution until/unless ntpd provides
+ * better.
+ */
+int
+get_ntpEntTimeResolution(
+ netsnmp_mib_handler * handler,
+ netsnmp_handler_registration * reginfo,
+ netsnmp_agent_request_info * reqinfo,
+ netsnmp_request_info * requests
+ )
+{
+ int precision;
+ u_int32 resolution;
+
+ switch (reqinfo->mode) {
+
+ case MODE_GET:
+ if (!read_ntp_value("precision", ntpvalue,
+ sizeof(ntpvalue)))
+ return SNMP_ERR_GENERR;
+ if (1 != sscanf(ntpvalue, "%d", &precision))
+ return SNMP_ERR_GENERR;
+ if (precision >= 0)
+ return SNMP_ERR_GENERR;
+ precision = max(precision, -31);
+ resolution = 1 << -precision;
+ snmp_set_var_typed_value(
+ requests->requestvb,
+ ASN_UNSIGNED,
+ (void *)&resolution,
+ sizeof(resolution));
+ break;
+
+ default:
+ return SNMP_ERR_GENERR;
+ }
+
+ return SNMP_ERR_NOERROR;
+}
+
+
+/*
+ * ntpEntTimePrecision
+ * "The entity's precision in integer format, shows the precision.
+ * A value of -5 would mean 2^-5 = 31.25 ms."
+ */
+int
+get_ntpEntTimePrecision(
+ netsnmp_mib_handler * handler,
+ netsnmp_handler_registration * reginfo,
+ netsnmp_agent_request_info * reqinfo,
+ netsnmp_request_info * requests
+ )
+{
+ int precision;
+ int32 precision32;
+
+ switch (reqinfo->mode) {
+
+ case MODE_GET:
+ if (!read_ntp_value("precision", ntpvalue,
+ sizeof(ntpvalue)))
+ return SNMP_ERR_GENERR;
+ if (1 != sscanf(ntpvalue, "%d", &precision))
+ return SNMP_ERR_GENERR;
+ precision32 = (int32)precision;
+ snmp_set_var_typed_value(
+ requests->requestvb,
+ ASN_INTEGER,
+ (void *)&precision32,
+ sizeof(precision32));
+ break;
+
+ default:
+ 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
+ */
+
+void
+init_ntpSnmpSubagentObject(void)
+{
+ /* Register all MIB objects with the agentx master */
+ NTP_OID_RO( ntpEntSoftwareName, 1, 1, 1, 0);
+ NTP_OID_RO( ntpEntSoftwareVersion, 1, 1, 2, 0);
+ NTP_OID_RO( ntpEntSoftwareVendor, 1, 1, 3, 0);
+ NTP_OID_RO( ntpEntSystemType, 1, 1, 4, 0);
+ NTP_OID_RO( ntpEntTimeResolution, 1, 1, 5, 0);
+ NTP_OID_RO( ntpEntTimePrecision, 1, 1, 6, 0);
+ NTP_OID_RO( ntpEntTimeDistance, 1, 1, 7, 0);
+}
+