1258945Sroberto/***************************************************************************** 2258945Sroberto * 3258945Sroberto * ntpSnmpSubAgentObject.c 4258945Sroberto * 5258945Sroberto * This file provides the callback functions for net-snmp and registers the 6258945Sroberto * serviced MIB objects with the master agent. 7258945Sroberto * 8258945Sroberto * Each object has its own callback function that is called by the 9258945Sroberto * master agent process whenever someone queries the corresponding MIB 10258945Sroberto * object. 11258945Sroberto * 12258945Sroberto * At the moment this triggers a full send/receive procedure for each 13258945Sroberto * queried MIB object, one of the things that are still on my todo list: 14258945Sroberto * a caching mechanism that reduces the number of requests sent to the 15258945Sroberto * ntpd process. 16258945Sroberto * 17258945Sroberto ****************************************************************************/ 18258945Sroberto#include <ntp_snmp.h> 19258945Sroberto#include <ctype.h> 20258945Sroberto#include <ntp.h> 21258945Sroberto#include <libntpq.h> 22258945Sroberto 23258945Sroberto/* general purpose buffer length definition */ 24258945Sroberto#define NTPQ_BUFLEN 2048 25258945Sroberto 26258945Srobertochar ntpvalue[NTPQ_BUFLEN]; 27258945Sroberto 28258945Sroberto 29258945Sroberto/***************************************************************************** 30258945Sroberto * 31258945Sroberto * ntpsnmpd_parse_string 32258945Sroberto * 33258945Sroberto * This function will parse a given NULL terminated string and cut it 34258945Sroberto * into a fieldname and a value part (using the '=' as the delimiter. 35258945Sroberto * The fieldname will be converted to uppercase and all whitespace 36258945Sroberto * characters are removed from it. 37258945Sroberto * The value part is stripped, e.g. all whitespace characters are removed 38258945Sroberto * from the beginning and end of the string. 39258945Sroberto * If the value is started and ended with quotes ("), they will be removed 40258945Sroberto * and everything between the quotes is left untouched (including 41258945Sroberto * whitespace) 42258945Sroberto * Example: 43258945Sroberto * server host name = hello world! 44258945Sroberto * will result in a field string "SERVERHOSTNAME" and a value 45258945Sroberto * of "hello world!". 46258945Sroberto * My first Parameter = " is this! " 47258945Sroberto * results in a field string "MYFIRSTPARAMETER" and a value " is this! " 48258945Sroberto **************************************************************************** 49258945Sroberto * Parameters: 50258945Sroberto * string const char * The source string to parse. 51258945Sroberto * NOTE: must be NULL terminated! 52258945Sroberto * field char * The buffer for the field name. 53258945Sroberto * fieldsize size_t The size of the field buffer. 54258945Sroberto * value char * The buffer for the value. 55258945Sroberto * valuesize size_t The size of the value buffer. 56258945Sroberto * 57258945Sroberto * Returns: 58258945Sroberto * size_t length of value string 59258945Sroberto ****************************************************************************/ 60258945Sroberto 61258945Srobertosize_t 62258945Srobertontpsnmpd_parse_string( 63258945Sroberto const char * string, 64258945Sroberto char * field, 65258945Sroberto size_t fieldsize, 66258945Sroberto char * value, 67258945Sroberto size_t valuesize 68258945Sroberto ) 69258945Sroberto{ 70258945Sroberto int i; 71258945Sroberto int j; 72258945Sroberto int loop; 73258945Sroberto size_t str_cnt; 74258945Sroberto size_t val_cnt; 75258945Sroberto 76258945Sroberto /* we need at least one byte to work with to simplify */ 77258945Sroberto if (fieldsize < 1 || valuesize < 1) 78258945Sroberto return 0; 79258945Sroberto 80258945Sroberto str_cnt = strlen(string); 81258945Sroberto 82258945Sroberto /* Parsing the field name */ 83258945Sroberto j = 0; 84258945Sroberto loop = TRUE; 85258945Sroberto for (i = 0; loop && i <= str_cnt; i++) { 86258945Sroberto switch (string[i]) { 87258945Sroberto 88258945Sroberto case '\t': /* Tab */ 89258945Sroberto case '\n': /* LF */ 90258945Sroberto case '\r': /* CR */ 91258945Sroberto case ' ': /* Space */ 92258945Sroberto break; 93258945Sroberto 94258945Sroberto case '=': 95258945Sroberto loop = FALSE; 96258945Sroberto break; 97258945Sroberto 98258945Sroberto default: 99258945Sroberto if (j < fieldsize) 100258945Sroberto field[j++] = toupper(string[i]); 101258945Sroberto } 102258945Sroberto } 103258945Sroberto 104258945Sroberto j = min(j, fieldsize - 1); 105258945Sroberto field[j] = '\0'; 106258945Sroberto 107258945Sroberto /* Now parsing the value */ 108258945Sroberto value[0] = '\0'; 109258945Sroberto j = 0; 110258945Sroberto for (val_cnt = 0; i < str_cnt; i++) { 111258945Sroberto if (string[i] > 0x0D && string[i] != ' ') 112258945Sroberto val_cnt = min(j + 1, valuesize - 1); 113258945Sroberto 114258945Sroberto if (value[0] != '\0' || 115258945Sroberto (string[i] > 0x0D && string[i] != ' ')) { 116258945Sroberto if (j < valuesize) 117258945Sroberto value[j++] = string[i]; 118258945Sroberto } 119258945Sroberto } 120258945Sroberto value[val_cnt] = '\0'; 121258945Sroberto 122258945Sroberto if (value[0] == '"') { 123258945Sroberto val_cnt--; 124280849Scy strlcpy(value, &value[1], valuesize); 125258945Sroberto if (val_cnt > 0 && value[val_cnt - 1] == '"') { 126258945Sroberto val_cnt--; 127258945Sroberto value[val_cnt] = '\0'; 128258945Sroberto } 129258945Sroberto } 130258945Sroberto 131258945Sroberto return val_cnt; 132258945Sroberto} 133258945Sroberto 134258945Sroberto 135258945Sroberto/***************************************************************************** 136258945Sroberto * 137258945Sroberto * ntpsnmpd_cut_string 138258945Sroberto * 139258945Sroberto * This function will parse a given NULL terminated string and cut it 140258945Sroberto * into fields using the specified delimiter character. 141258945Sroberto * It will then copy the requested field into a destination buffer 142258945Sroberto * Example: 143258945Sroberto * ntpsnmpd_cut_string(read:my:lips:fool, RESULT, ':', 2, sizeof(RESULT)) 144258945Sroberto * will copy "lips" to RESULT. 145258945Sroberto **************************************************************************** 146258945Sroberto * Parameters: 147258945Sroberto * src const char * The name of the source string variable 148258945Sroberto * NOTE: must be NULL terminated! 149258945Sroberto * dest char * The name of the string which takes the 150258945Sroberto * requested field content 151258945Sroberto * delim char The delimiter character 152258945Sroberto * fieldnumber int The number of the required field 153258945Sroberto * (start counting with 0) 154258945Sroberto * maxsize size_t The maximum size of dest 155258945Sroberto * 156258945Sroberto * Returns: 157258945Sroberto * size_t length of resulting dest string 158258945Sroberto ****************************************************************************/ 159258945Sroberto 160258945Srobertosize_t 161258945Srobertontpsnmpd_cut_string( 162258945Sroberto const char * string, 163258945Sroberto char * dest, 164258945Sroberto char delim, 165258945Sroberto int fieldnumber, 166258945Sroberto size_t maxsize 167258945Sroberto ) 168258945Sroberto{ 169258945Sroberto size_t i; 170258945Sroberto size_t j; 171258945Sroberto int l; 172258945Sroberto size_t str_cnt; 173258945Sroberto 174258945Sroberto if (maxsize < 1) 175258945Sroberto return 0; 176258945Sroberto 177258945Sroberto str_cnt = strlen(string); 178258945Sroberto j = 0; 179258945Sroberto memset(dest, 0, maxsize); 180258945Sroberto 181258945Sroberto /* Parsing the field name */ 182258945Sroberto for (i = 0, l = 0; i < str_cnt && l <= fieldnumber; i++) { 183258945Sroberto if (string[i] == delim) 184258945Sroberto l++; /* next field */ 185258945Sroberto else if (l == fieldnumber && j < maxsize) 186258945Sroberto dest[j++] = string[i]; 187258945Sroberto } 188258945Sroberto j = min(j, maxsize - 1); 189258945Sroberto dest[j] = '\0'; 190258945Sroberto 191258945Sroberto return j; 192258945Sroberto} 193258945Sroberto 194258945Sroberto 195258945Sroberto/***************************************************************************** 196258945Sroberto * 197258945Sroberto * read_ntp_value 198258945Sroberto * 199258945Sroberto * This function retrieves the value for a given variable, currently 200258945Sroberto * this only supports sysvars. It starts a full mode 6 send/receive/parse 201258945Sroberto * iteration and needs to be optimized, e.g. by using a caching mechanism 202258945Sroberto * 203258945Sroberto **************************************************************************** 204258945Sroberto * Parameters: 205258945Sroberto * variable char* The name of the required variable 206258945Sroberto * rbuffer char* The buffer where the value goes 207258945Sroberto * maxlength int Max. number of bytes for resultbuf 208258945Sroberto * 209258945Sroberto * Returns: 210258945Sroberto * u_int number of chars that have been copied to 211258945Sroberto * rbuffer 212258945Sroberto ****************************************************************************/ 213258945Sroberto 214258945Srobertosize_t 215258945Srobertoread_ntp_value( 216258945Sroberto const char * variable, 217258945Sroberto char * value, 218258945Sroberto size_t valuesize 219258945Sroberto ) 220258945Sroberto{ 221258945Sroberto size_t sv_len; 222258945Sroberto char sv_data[NTPQ_BUFLEN]; 223258945Sroberto 224258945Sroberto memset(sv_data, 0, sizeof(sv_data)); 225258945Sroberto sv_len = ntpq_read_sysvars(sv_data, sizeof(sv_data)); 226258945Sroberto 227258945Sroberto if (0 == sv_len) 228258945Sroberto return 0; 229258945Sroberto else 230258945Sroberto return ntpq_getvar(sv_data, sv_len, variable, value, 231258945Sroberto valuesize); 232258945Sroberto} 233258945Sroberto 234258945Sroberto 235258945Sroberto/***************************************************************************** 236258945Sroberto * 237258945Sroberto * The get_xxx functions 238258945Sroberto * 239258945Sroberto * The following function calls are callback functions that will be 240258945Sroberto * used by the master agent process to retrieve a value for a requested 241258945Sroberto * MIB object. 242258945Sroberto * 243258945Sroberto ****************************************************************************/ 244258945Sroberto 245258945Sroberto 246258945Srobertoint get_ntpEntSoftwareName (netsnmp_mib_handler *handler, 247258945Sroberto netsnmp_handler_registration *reginfo, 248258945Sroberto netsnmp_agent_request_info *reqinfo, 249258945Sroberto netsnmp_request_info *requests) 250258945Sroberto{ 251258945Sroberto char ntp_softwarename[NTPQ_BUFLEN]; 252258945Sroberto 253258945Sroberto memset (ntp_softwarename, 0, NTPQ_BUFLEN); 254258945Sroberto 255258945Sroberto switch (reqinfo->mode) { 256258945Sroberto case MODE_GET: 257258945Sroberto { 258258945Sroberto if ( read_ntp_value("product", ntpvalue, NTPQ_BUFLEN) ) 259258945Sroberto { 260258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 261258945Sroberto (u_char *)ntpvalue, 262258945Sroberto strlen(ntpvalue) 263258945Sroberto ); 264258945Sroberto } 265258945Sroberto else if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) ) 266258945Sroberto { 267258945Sroberto ntpsnmpd_cut_string(ntpvalue, ntp_softwarename, ' ', 0, sizeof(ntp_softwarename)-1); 268258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 269258945Sroberto (u_char *)ntp_softwarename, 270258945Sroberto strlen(ntp_softwarename) 271258945Sroberto ); 272258945Sroberto } else { 273258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 274258945Sroberto (u_char *)"N/A", 275258945Sroberto 3 276258945Sroberto ); 277258945Sroberto } 278258945Sroberto break; 279258945Sroberto 280258945Sroberto } 281258945Sroberto 282258945Sroberto 283258945Sroberto default: 284258945Sroberto /* If we cannot get the information we need, we will return a generic error to the SNMP client */ 285258945Sroberto return SNMP_ERR_GENERR; 286258945Sroberto } 287258945Sroberto 288258945Sroberto return SNMP_ERR_NOERROR; 289258945Sroberto} 290258945Sroberto 291258945Sroberto 292258945Srobertoint get_ntpEntSoftwareVersion (netsnmp_mib_handler *handler, 293258945Sroberto netsnmp_handler_registration *reginfo, 294258945Sroberto netsnmp_agent_request_info *reqinfo, 295258945Sroberto netsnmp_request_info *requests) 296258945Sroberto{ 297258945Sroberto 298258945Sroberto switch (reqinfo->mode) { 299258945Sroberto case MODE_GET: 300258945Sroberto { 301258945Sroberto 302258945Sroberto if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) ) 303258945Sroberto { 304258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 305258945Sroberto (u_char *)ntpvalue, 306258945Sroberto strlen(ntpvalue) 307258945Sroberto ); 308258945Sroberto } else { 309258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 310258945Sroberto (u_char *)"N/A", 311258945Sroberto 3 312258945Sroberto ); 313258945Sroberto } 314258945Sroberto break; 315258945Sroberto 316258945Sroberto } 317258945Sroberto 318258945Sroberto 319258945Sroberto default: 320258945Sroberto /* If we cannot get the information we need, we will return a generic error to the SNMP client */ 321258945Sroberto return SNMP_ERR_GENERR; 322258945Sroberto } 323258945Sroberto 324258945Sroberto return SNMP_ERR_NOERROR; 325258945Sroberto} 326258945Sroberto 327258945Sroberto 328258945Srobertoint get_ntpEntSoftwareVendor (netsnmp_mib_handler *handler, 329258945Sroberto netsnmp_handler_registration *reginfo, 330258945Sroberto netsnmp_agent_request_info *reqinfo, 331258945Sroberto netsnmp_request_info *requests) 332258945Sroberto{ 333258945Sroberto 334258945Sroberto switch (reqinfo->mode) { 335258945Sroberto case MODE_GET: 336258945Sroberto { 337258945Sroberto 338258945Sroberto if ( read_ntp_value("vendor", ntpvalue, NTPQ_BUFLEN) ) 339258945Sroberto { 340258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 341258945Sroberto (u_char *)ntpvalue, 342258945Sroberto strlen(ntpvalue) 343258945Sroberto ); 344258945Sroberto } else { 345258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 346258945Sroberto (u_char *)"N/A", 347258945Sroberto 3 348258945Sroberto ); 349258945Sroberto } 350258945Sroberto break; 351258945Sroberto 352258945Sroberto default: 353258945Sroberto /* If we cannot get the information we need, we will return a generic error to the SNMP client */ 354258945Sroberto return SNMP_ERR_GENERR; 355258945Sroberto } 356258945Sroberto } 357258945Sroberto return SNMP_ERR_NOERROR; 358258945Sroberto} 359258945Sroberto 360258945Sroberto 361258945Srobertoint get_ntpEntSystemType (netsnmp_mib_handler *handler, 362258945Sroberto netsnmp_handler_registration *reginfo, 363258945Sroberto netsnmp_agent_request_info *reqinfo, 364258945Sroberto netsnmp_request_info *requests) 365258945Sroberto{ 366258945Sroberto 367258945Sroberto switch (reqinfo->mode) { 368258945Sroberto case MODE_GET: 369258945Sroberto { 370258945Sroberto 371258945Sroberto if ( read_ntp_value("systemtype", ntpvalue, NTPQ_BUFLEN) ) 372258945Sroberto { 373258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 374258945Sroberto (u_char *)ntpvalue, 375258945Sroberto strlen(ntpvalue) 376258945Sroberto ); 377258945Sroberto } 378258945Sroberto 379258945Sroberto if ( read_ntp_value("system", ntpvalue, NTPQ_BUFLEN) ) 380258945Sroberto { 381258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 382258945Sroberto (u_char *)ntpvalue, 383258945Sroberto strlen(ntpvalue) 384258945Sroberto ); 385258945Sroberto } else { 386258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 387258945Sroberto (u_char *)"N/A", 388258945Sroberto 3 389258945Sroberto ); 390258945Sroberto } 391258945Sroberto break; 392258945Sroberto 393258945Sroberto } 394258945Sroberto 395258945Sroberto 396258945Sroberto default: 397258945Sroberto /* If we cannot get the information we need, we will return a generic error to the SNMP client */ 398258945Sroberto return SNMP_ERR_GENERR; 399258945Sroberto } 400258945Sroberto 401258945Sroberto return SNMP_ERR_NOERROR; 402258945Sroberto} 403258945Sroberto 404258945Sroberto 405258945Sroberto/* 406258945Sroberto * ntpEntTimeResolution 407258945Sroberto * "The time resolution in integer format, where the resolution 408258945Sroberto * is represented as divisions of a second, e.g., a value of 1000 409258945Sroberto * translates to 1.0 ms." 410258945Sroberto * 411258945Sroberto * ntpEntTimeResolution is a challenge for ntpd, as the resolution is 412258945Sroberto * not known nor exposed by ntpd, only the measured precision (time to 413258945Sroberto * read the clock). 414258945Sroberto * 415258945Sroberto * Logically the resolution must be at least the precision, so report 416258945Sroberto * it as our best approximation of resolution until/unless ntpd provides 417258945Sroberto * better. 418258945Sroberto */ 419258945Srobertoint 420258945Srobertoget_ntpEntTimeResolution( 421258945Sroberto netsnmp_mib_handler * handler, 422258945Sroberto netsnmp_handler_registration * reginfo, 423258945Sroberto netsnmp_agent_request_info * reqinfo, 424258945Sroberto netsnmp_request_info * requests 425258945Sroberto ) 426258945Sroberto{ 427258945Sroberto int precision; 428258945Sroberto u_int32 resolution; 429258945Sroberto 430258945Sroberto switch (reqinfo->mode) { 431258945Sroberto 432258945Sroberto case MODE_GET: 433258945Sroberto if (!read_ntp_value("precision", ntpvalue, 434258945Sroberto sizeof(ntpvalue))) 435258945Sroberto return SNMP_ERR_GENERR; 436258945Sroberto if (1 != sscanf(ntpvalue, "%d", &precision)) 437258945Sroberto return SNMP_ERR_GENERR; 438258945Sroberto if (precision >= 0) 439258945Sroberto return SNMP_ERR_GENERR; 440258945Sroberto precision = max(precision, -31); 441258945Sroberto resolution = 1 << -precision; 442258945Sroberto snmp_set_var_typed_value( 443258945Sroberto requests->requestvb, 444258945Sroberto ASN_UNSIGNED, 445258945Sroberto (void *)&resolution, 446258945Sroberto sizeof(resolution)); 447258945Sroberto break; 448258945Sroberto 449258945Sroberto default: 450258945Sroberto return SNMP_ERR_GENERR; 451258945Sroberto } 452258945Sroberto 453258945Sroberto return SNMP_ERR_NOERROR; 454258945Sroberto} 455258945Sroberto 456258945Sroberto 457258945Sroberto/* 458258945Sroberto * ntpEntTimePrecision 459258945Sroberto * "The entity's precision in integer format, shows the precision. 460258945Sroberto * A value of -5 would mean 2^-5 = 31.25 ms." 461258945Sroberto */ 462258945Srobertoint 463258945Srobertoget_ntpEntTimePrecision( 464258945Sroberto netsnmp_mib_handler * handler, 465258945Sroberto netsnmp_handler_registration * reginfo, 466258945Sroberto netsnmp_agent_request_info * reqinfo, 467258945Sroberto netsnmp_request_info * requests 468258945Sroberto ) 469258945Sroberto{ 470258945Sroberto int precision; 471258945Sroberto int32 precision32; 472258945Sroberto 473258945Sroberto switch (reqinfo->mode) { 474258945Sroberto 475258945Sroberto case MODE_GET: 476258945Sroberto if (!read_ntp_value("precision", ntpvalue, 477258945Sroberto sizeof(ntpvalue))) 478258945Sroberto return SNMP_ERR_GENERR; 479258945Sroberto if (1 != sscanf(ntpvalue, "%d", &precision)) 480258945Sroberto return SNMP_ERR_GENERR; 481258945Sroberto precision32 = (int32)precision; 482258945Sroberto snmp_set_var_typed_value( 483258945Sroberto requests->requestvb, 484258945Sroberto ASN_INTEGER, 485258945Sroberto (void *)&precision32, 486258945Sroberto sizeof(precision32)); 487258945Sroberto break; 488258945Sroberto 489258945Sroberto default: 490258945Sroberto return SNMP_ERR_GENERR; 491258945Sroberto } 492258945Sroberto 493258945Sroberto return SNMP_ERR_NOERROR; 494258945Sroberto} 495258945Sroberto 496258945Sroberto 497258945Srobertoint get_ntpEntTimeDistance (netsnmp_mib_handler *handler, 498258945Sroberto netsnmp_handler_registration *reginfo, 499258945Sroberto netsnmp_agent_request_info *reqinfo, 500258945Sroberto netsnmp_request_info *requests) 501258945Sroberto{ 502258945Sroberto switch (reqinfo->mode) { 503258945Sroberto case MODE_GET: 504258945Sroberto { 505258945Sroberto 506258945Sroberto if ( read_ntp_value("rootdelay", ntpvalue, NTPQ_BUFLEN) ) 507258945Sroberto { 508258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 509258945Sroberto (u_char *)ntpvalue, 510258945Sroberto strlen(ntpvalue) 511258945Sroberto ); 512258945Sroberto } else { 513258945Sroberto snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR, 514258945Sroberto (u_char *)"N/A", 515258945Sroberto 3 516258945Sroberto ); 517258945Sroberto } 518258945Sroberto break; 519258945Sroberto 520258945Sroberto } 521258945Sroberto 522258945Sroberto 523258945Sroberto default: 524258945Sroberto /* If we cannot get the information we need, we will return a generic error to the SNMP client */ 525258945Sroberto return SNMP_ERR_GENERR; 526258945Sroberto } 527258945Sroberto 528258945Sroberto return SNMP_ERR_NOERROR; 529258945Sroberto} 530258945Sroberto 531258945Sroberto 532258945Sroberto/* 533258945Sroberto * 534258945Sroberto * Initialize sub agent 535258945Sroberto */ 536258945Sroberto 537258945Srobertovoid 538258945Srobertoinit_ntpSnmpSubagentObject(void) 539258945Sroberto{ 540258945Sroberto /* Register all MIB objects with the agentx master */ 541258945Sroberto NTP_OID_RO( ntpEntSoftwareName, 1, 1, 1, 0); 542258945Sroberto NTP_OID_RO( ntpEntSoftwareVersion, 1, 1, 2, 0); 543258945Sroberto NTP_OID_RO( ntpEntSoftwareVendor, 1, 1, 3, 0); 544258945Sroberto NTP_OID_RO( ntpEntSystemType, 1, 1, 4, 0); 545258945Sroberto NTP_OID_RO( ntpEntTimeResolution, 1, 1, 5, 0); 546258945Sroberto NTP_OID_RO( ntpEntTimePrecision, 1, 1, 6, 0); 547258945Sroberto NTP_OID_RO( ntpEntTimeDistance, 1, 1, 7, 0); 548258945Sroberto} 549258945Sroberto 550