]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/ntpsnmpd/ntpSnmpSubagentObject.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / ntp / ntpsnmpd / ntpSnmpSubagentObject.c
1 /*****************************************************************************
2  *
3  *  ntpSnmpSubAgentObject.c
4  *
5  *  This file provides the callback functions for net-snmp and registers the 
6  *  serviced MIB objects with the master agent.
7  * 
8  *  Each object has its own callback function that is called by the 
9  *  master agent process whenever someone queries the corresponding MIB
10  *  object. 
11  * 
12  *  At the moment this triggers a full send/receive procedure for each
13  *  queried MIB object, one of the things that are still on my todo list:
14  *  a caching mechanism that reduces the number of requests sent to the
15  *  ntpd process.
16  *
17  ****************************************************************************/
18 #include <ntp_snmp.h>
19 #include <ctype.h>
20 #include <ntp.h>
21 #include <libntpq.h>
22
23 /* general purpose buffer length definition */
24 #define NTPQ_BUFLEN 2048
25
26 char ntpvalue[NTPQ_BUFLEN];
27
28
29 /*****************************************************************************
30  *
31  * ntpsnmpd_parse_string
32  *
33  *  This function will parse a given NULL terminated string and cut it
34  *  into a fieldname and a value part (using the '=' as the delimiter. 
35  *  The fieldname will be converted to uppercase and all whitespace 
36  *  characters are removed from it.
37  *  The value part is stripped, e.g. all whitespace characters are removed
38  *  from the beginning and end of the string.
39  *  If the value is started and ended with quotes ("), they will be removed
40  *  and everything between the quotes is left untouched (including 
41  *  whitespace)
42  *  Example:
43  *     server host name =   hello world!
44  *  will result in a field string "SERVERHOSTNAME" and a value
45  *  of "hello world!".
46  *     My first Parameter               =               "  is this!    "
47   * results in a field string "MYFIRSTPARAMETER" and a value " is this!    "
48  ****************************************************************************
49  * Parameters:
50  *      string          const char *    The source string to parse.
51  *                                      NOTE: must be NULL terminated!
52  *      field           char *          The buffer for the field name.
53  *      fieldsize       size_t          The size of the field buffer.
54  *      value           char *          The buffer for the value.
55  *      valuesize       size_t          The size of the value buffer.
56  *
57  * Returns:
58  *      size_t                  length of value string 
59  ****************************************************************************/
60
61 size_t
62 ntpsnmpd_parse_string(
63         const char *    string,
64         char *          field,
65         size_t          fieldsize,
66         char *          value,
67         size_t          valuesize
68         )
69 {
70         int i;
71         int j;
72         int loop;
73         size_t str_cnt;
74         size_t val_cnt;
75
76         /* we need at least one byte to work with to simplify */
77         if (fieldsize < 1 || valuesize < 1)
78                 return 0;
79
80         str_cnt = strlen(string);
81
82         /* Parsing the field name */
83         j = 0;
84         loop = TRUE;
85         for (i = 0; loop && i <= str_cnt; i++) {
86                 switch (string[i]) {
87
88                 case '\t':      /* Tab */
89                 case '\n':      /* LF */
90                 case '\r':      /* CR */
91                 case ' ':       /* Space */
92                         break;
93
94                 case '=':
95                         loop = FALSE;
96                         break;
97
98                 default:
99                         if (j < fieldsize)
100                                 field[j++] = toupper(string[i]);
101                 }
102         }
103
104         j = min(j, fieldsize - 1);
105         field[j] = '\0';
106
107         /* Now parsing the value */
108         value[0] = '\0';
109         j = 0; 
110         for (val_cnt = 0; i < str_cnt; i++) {
111                 if (string[i] > 0x0D && string[i] != ' ')
112                         val_cnt = min(j + 1, valuesize - 1);
113                 
114                 if (value[0] != '\0' ||
115                     (string[i] > 0x0D && string[i] != ' ')) {
116                         if (j < valuesize)
117                                 value[j++] = string[i];
118                 }
119         }
120         value[val_cnt] = '\0';
121
122         if (value[0] == '"') {
123                 val_cnt--;
124                 strlcpy(value, &value[1], valuesize);
125                 if (val_cnt > 0 && value[val_cnt - 1] == '"') {
126                         val_cnt--;
127                         value[val_cnt] = '\0';
128                 }
129         }
130
131         return val_cnt;
132 }
133
134
135 /*****************************************************************************
136  *
137  * ntpsnmpd_cut_string
138  *
139  *  This function will parse a given NULL terminated string and cut it
140  *  into fields using the specified delimiter character. 
141  *  It will then copy the requested field into a destination buffer
142  *  Example:
143  *     ntpsnmpd_cut_string(read:my:lips:fool, RESULT, ':', 2, sizeof(RESULT))
144  *  will copy "lips" to RESULT.
145  ****************************************************************************
146  * Parameters:
147  *      src             const char *    The name of the source string variable
148  *                                      NOTE: must be NULL terminated!
149  *      dest            char *          The name of the string which takes the
150  *                                      requested field content
151  *      delim           char            The delimiter character
152  *      fieldnumber     int             The number of the required field
153  *                                      (start counting with 0)
154  *      maxsize         size_t          The maximum size of dest
155  *
156  * Returns:
157  *      size_t          length of resulting dest string 
158  ****************************************************************************/
159
160 size_t
161 ntpsnmpd_cut_string(
162         const char *    string,
163         char *          dest,
164         char            delim,
165         int             fieldnumber,
166         size_t          maxsize
167         )
168 {
169         size_t i;
170         size_t j;
171         int l;
172         size_t str_cnt;
173
174         if (maxsize < 1)
175                 return 0;
176
177         str_cnt = strlen(string);
178         j = 0;
179         memset(dest, 0, maxsize);
180
181         /* Parsing the field name */
182         for (i = 0, l = 0; i < str_cnt && l <= fieldnumber; i++) {
183                 if (string[i] == delim)
184                         l++;    /* next field */
185                 else if (l == fieldnumber && j < maxsize)
186                         dest[j++] = string[i]; 
187         }
188         j = min(j, maxsize - 1);
189         dest[j] = '\0';
190
191         return j;
192 }
193
194
195 /*****************************************************************************
196  *
197  *  read_ntp_value
198  *
199  *  This function retrieves the value for a given variable, currently
200  *  this only supports sysvars. It starts a full mode 6 send/receive/parse
201  *  iteration and needs to be optimized, e.g. by using a caching mechanism
202  *  
203  ****************************************************************************
204  * Parameters:
205  *      variable        char*   The name of the required variable
206  *      rbuffer         char*   The buffer where the value goes
207  *      maxlength       int     Max. number of bytes for resultbuf
208  *
209  * Returns:
210  *      u_int           number of chars that have been copied to 
211  *                      rbuffer 
212  ****************************************************************************/
213
214 size_t
215 read_ntp_value(
216         const char *    variable,
217         char *          value,
218         size_t          valuesize
219         )
220 {
221         size_t  sv_len;
222         char    sv_data[NTPQ_BUFLEN];
223         
224         memset(sv_data, 0, sizeof(sv_data));
225         sv_len = ntpq_read_sysvars(sv_data, sizeof(sv_data));
226
227         if (0 == sv_len)
228                 return 0;
229         else
230                 return ntpq_getvar(sv_data, sv_len, variable, value,
231                                    valuesize);
232 }
233
234
235 /*****************************************************************************
236  *
237  *  The get_xxx functions
238  *
239  *  The following function calls are callback functions that will be 
240  *  used by the master agent process to retrieve a value for a requested 
241  *  MIB object. 
242  *
243  ****************************************************************************/
244
245
246 int get_ntpEntSoftwareName (netsnmp_mib_handler *handler,
247                                netsnmp_handler_registration *reginfo,
248                                netsnmp_agent_request_info *reqinfo,
249                                netsnmp_request_info *requests)
250 {
251  char ntp_softwarename[NTPQ_BUFLEN];
252         
253    memset (ntp_softwarename, 0, NTPQ_BUFLEN);
254         
255    switch (reqinfo->mode) {
256    case MODE_GET:
257    {
258         if ( read_ntp_value("product", ntpvalue, NTPQ_BUFLEN) )
259        {
260         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
261                              (u_char *)ntpvalue,
262                              strlen(ntpvalue)
263                             );
264        } 
265     else  if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) )
266     {
267         ntpsnmpd_cut_string(ntpvalue, ntp_softwarename, ' ', 0, sizeof(ntp_softwarename)-1);
268         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
269                              (u_char *)ntp_softwarename,
270                              strlen(ntp_softwarename)
271                             );
272     } else {
273         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
274                              (u_char *)"N/A",
275                              3
276                             );
277     }
278     break;
279     
280   }
281
282
283   default:
284           /* If we cannot get the information we need, we will return a generic error to the SNMP client */
285         return SNMP_ERR_GENERR;
286   }
287
288   return SNMP_ERR_NOERROR;
289 }
290
291
292 int get_ntpEntSoftwareVersion (netsnmp_mib_handler *handler,
293                                netsnmp_handler_registration *reginfo,
294                                netsnmp_agent_request_info *reqinfo,
295                                netsnmp_request_info *requests)
296 {
297
298    switch (reqinfo->mode) {
299    case MODE_GET:
300    {
301     
302     if ( read_ntp_value("version", ntpvalue, NTPQ_BUFLEN) )
303     {
304         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
305                              (u_char *)ntpvalue,
306                              strlen(ntpvalue)
307                             );
308     } else {
309         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
310                              (u_char *)"N/A",
311                              3
312                             );
313     }
314     break;
315     
316   }
317
318
319   default:
320           /* If we cannot get the information we need, we will return a generic error to the SNMP client */
321         return SNMP_ERR_GENERR;
322   }
323
324   return SNMP_ERR_NOERROR;
325 }
326
327
328 int get_ntpEntSoftwareVendor (netsnmp_mib_handler *handler,
329                                netsnmp_handler_registration *reginfo,
330                                netsnmp_agent_request_info *reqinfo,
331                                netsnmp_request_info *requests)
332 {
333
334    switch (reqinfo->mode) {
335    case MODE_GET:
336    {
337     
338     if ( read_ntp_value("vendor", ntpvalue, NTPQ_BUFLEN) )
339     {
340         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
341                              (u_char *)ntpvalue,
342                              strlen(ntpvalue)
343                             );
344     } else {
345         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
346                              (u_char *)"N/A",
347                              3
348                             );
349     }
350     break;
351
352   default:
353           /* If we cannot get the information we need, we will return a generic error to the SNMP client */
354         return SNMP_ERR_GENERR;
355    }
356   }
357   return SNMP_ERR_NOERROR;
358 }
359
360
361 int get_ntpEntSystemType (netsnmp_mib_handler *handler,
362                                netsnmp_handler_registration *reginfo,
363                                netsnmp_agent_request_info *reqinfo,
364                                netsnmp_request_info *requests)
365 {
366
367    switch (reqinfo->mode) {
368    case MODE_GET:
369    {
370     
371     if ( read_ntp_value("systemtype", ntpvalue, NTPQ_BUFLEN) )
372     {
373         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
374                              (u_char *)ntpvalue,
375                              strlen(ntpvalue)
376                             );
377     }
378            
379     if ( read_ntp_value("system", ntpvalue, NTPQ_BUFLEN) )
380     {
381         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
382                              (u_char *)ntpvalue,
383                              strlen(ntpvalue)
384                             );
385     } else {
386         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
387                              (u_char *)"N/A",
388                              3
389                             );
390     }
391     break;
392     
393   }
394
395
396   default:
397           /* If we cannot get the information we need, we will return a generic error to the SNMP client */
398         return SNMP_ERR_GENERR;
399   }
400
401   return SNMP_ERR_NOERROR;
402 }
403
404
405 /*
406  * ntpEntTimeResolution
407  *      "The time resolution in integer format, where the resolution
408  *       is represented as divisions of a second, e.g., a value of 1000
409  *       translates to 1.0 ms."
410  *
411  * ntpEntTimeResolution is a challenge for ntpd, as the resolution is
412  * not known nor exposed by ntpd, only the measured precision (time to
413  * read the clock).
414  *
415  * Logically the resolution must be at least the precision, so report
416  * it as our best approximation of resolution until/unless ntpd provides
417  * better.
418  */
419 int
420 get_ntpEntTimeResolution(
421         netsnmp_mib_handler *           handler,
422         netsnmp_handler_registration *  reginfo,
423         netsnmp_agent_request_info *    reqinfo,
424         netsnmp_request_info *          requests
425         )
426 {
427         int     precision;
428         u_int32 resolution;
429
430         switch (reqinfo->mode) {
431
432         case MODE_GET:
433                 if (!read_ntp_value("precision", ntpvalue,
434                                     sizeof(ntpvalue)))
435                         return SNMP_ERR_GENERR;
436                 if (1 != sscanf(ntpvalue, "%d", &precision))
437                         return SNMP_ERR_GENERR;
438                 if (precision >= 0)
439                         return SNMP_ERR_GENERR;
440                 precision = max(precision, -31);
441                 resolution = 1 << -precision;
442                 snmp_set_var_typed_value(
443                         requests->requestvb,
444                         ASN_UNSIGNED,
445                         (void *)&resolution,
446                         sizeof(resolution));
447                 break;
448
449         default:
450                 return SNMP_ERR_GENERR;
451         }
452
453         return SNMP_ERR_NOERROR;
454 }
455
456
457 /*
458  * ntpEntTimePrecision
459  *      "The entity's precision in integer format, shows the precision.
460  *       A value of -5 would mean 2^-5 = 31.25 ms."
461  */
462 int 
463 get_ntpEntTimePrecision(
464         netsnmp_mib_handler *           handler,
465         netsnmp_handler_registration *  reginfo,
466         netsnmp_agent_request_info *    reqinfo,
467         netsnmp_request_info *          requests
468         )
469 {
470         int     precision;
471         int32   precision32;
472
473         switch (reqinfo->mode) {
474
475         case MODE_GET:
476                 if (!read_ntp_value("precision", ntpvalue, 
477                                     sizeof(ntpvalue)))
478                         return SNMP_ERR_GENERR;
479                 if (1 != sscanf(ntpvalue, "%d", &precision))
480                         return SNMP_ERR_GENERR;
481                 precision32 = (int32)precision;
482                 snmp_set_var_typed_value(
483                         requests->requestvb,
484                         ASN_INTEGER,
485                         (void *)&precision32,
486                         sizeof(precision32));
487                 break;
488
489         default:
490                 return SNMP_ERR_GENERR;
491         }
492
493         return SNMP_ERR_NOERROR;
494 }
495
496
497 int get_ntpEntTimeDistance (netsnmp_mib_handler *handler,
498                                netsnmp_handler_registration *reginfo,
499                                netsnmp_agent_request_info *reqinfo,
500                                netsnmp_request_info *requests)
501 {
502    switch (reqinfo->mode) {
503    case MODE_GET:
504    {
505     
506     if ( read_ntp_value("rootdelay", ntpvalue, NTPQ_BUFLEN) )
507     {
508         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
509                              (u_char *)ntpvalue,
510                              strlen(ntpvalue)
511                             );
512     } else {
513         snmp_set_var_typed_value(requests->requestvb, ASN_OCTET_STR,
514                              (u_char *)"N/A",
515                              3
516                             );
517     }
518     break;
519     
520   }
521
522
523   default:
524           /* If we cannot get the information we need, we will return a generic error to the SNMP client */
525         return SNMP_ERR_GENERR;
526   }
527
528   return SNMP_ERR_NOERROR;
529 }
530
531
532 /*
533  *
534  * Initialize sub agent
535  */
536
537 void
538 init_ntpSnmpSubagentObject(void)
539 {
540         /* Register all MIB objects with the agentx master */
541         NTP_OID_RO( ntpEntSoftwareName,         1, 1, 1, 0);
542         NTP_OID_RO( ntpEntSoftwareVersion,      1, 1, 2, 0);
543         NTP_OID_RO( ntpEntSoftwareVendor,       1, 1, 3, 0);
544         NTP_OID_RO( ntpEntSystemType,           1, 1, 4, 0);
545         NTP_OID_RO( ntpEntTimeResolution,       1, 1, 5, 0);
546         NTP_OID_RO( ntpEntTimePrecision,        1, 1, 6, 0);
547         NTP_OID_RO( ntpEntTimeDistance,         1, 1, 7, 0);
548 }
549