1 /*****************************************************************************
5 * This is the wrapper library for ntpq, the NTP query utility.
6 * This library reuses the sourcecode from ntpq and exports a number
7 * of useful functions in a library that can be linked against applications
8 * that need to query the status of a running ntpd. The whole
9 * communcation is based on mode 6 packets.
11 ****************************************************************************/
13 #define NO_MAIN_ALLOWED 1
14 /* #define BUILD_AS_LIB Already provided by the Makefile */
19 /* Function Prototypes */
22 const char *Version = "libntpq 0.3beta";
24 /* global variables used for holding snapshots of data */
25 char peervars[NTPQ_BUFLEN];
27 associd_t peervar_assoc = 0;
28 char clockvars[NTPQ_BUFLEN];
30 int clockvar_assoc = 0;
31 char sysvars[NTPQ_BUFLEN];
33 char *ntpq_resultbuffer[NTPQ_BUFLEN];
34 unsigned short ntpq_associations[MAXASSOC];
35 struct ntpq_varlist ntpq_varlist[MAXLIST];
37 /*****************************************************************************
41 * Parses a given character buffer srcbuf and removes all quoted
42 * characters. The resulting string is copied to the specified
43 * resultbuf character buffer. E.g. \" will be translated into "
45 ****************************************************************************
47 * resultbuf char* The resulting string without quoted
49 * srcbuf char* The buffer holding the original string
50 * datalen int The number of bytes stored in srcbuf
51 * maxlen int Max. number of bytes for resultbuf
54 * int number of chars that have been copied to
56 ****************************************************************************/
58 int ntpq_stripquotes ( char *resultbuf, char *srcbuf, int datalen, int maxlen )
60 char* dst = resultbuf;
61 char* dep = resultbuf + maxlen - 1;
63 char* sep = srcbuf + (datalen >= 0 ? datalen : 0);
70 while ((dst != dep) && (src != sep) && (ch = (u_char)*src++) != 0) {
74 /* skip and do not copy */
75 /* case '"':*/ /* quotes */
77 case 'r': /*carriage return*/
97 return (int)(dst - resultbuf);
101 /*****************************************************************************
105 * This function parses a given buffer for a variable/value pair and
106 * copies the value of the requested variable into the specified
109 * It returns the number of bytes copied or zero for an empty result
110 * (=no matching variable found or empty value)
112 ****************************************************************************
114 * resultbuf char* The resulting string without quoted
116 * datalen size_t The number of bytes stored in
118 * varname char* Name of the required variable
119 * varvalue char* Where the value of the variable should
121 * maxlen size_t Max. number of bytes for varvalue
124 * size_t number of chars that have been copied to
126 ****************************************************************************/
130 const char * resultbuf,
132 const char * varname,
141 idatalen = (int)datalen;
143 while (nextvar(&idatalen, &resultbuf, &name, &value)) {
144 if (strcmp(varname, name) == 0) {
145 ntpq_stripquotes(varvalue, value, strlen(value), maxlen);
147 return strlen(varvalue);
155 /*****************************************************************************
159 * Sends a mode 6 query packet to the current open host (see
160 * ntpq_openhost) and stores the requested variable set in the specified
162 * It returns the number of bytes read or zero for an empty result
163 * (=no answer or empty value)
165 ****************************************************************************
167 * VARSET u_short Which variable set should be
168 * read (PEERVARS or CLOCKVARS)
169 * association int The association ID that should be read
170 * 0 represents the ntpd instance itself
171 * resultbuf char* The resulting string without quoted
173 * maxlen int Max. number of bytes for varvalue
176 * int number of bytes that have been copied to
179 * 0 (zero) if no reply has been received or
180 * another failure occured
181 ****************************************************************************/
183 int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen)
191 res = doquery(VARSET,association,0,0, (char *)0, &rstatus, &dsize, &datap);
195 if ( ( res != 0) || ( dsize == 0 ) ) /* no data */
202 /* fill result resultbuf */
203 memcpy(resultbuf, datap, dsize);
210 /*****************************************************************************
214 * Sets up a connection to the ntpd instance of a specified host. Note:
215 * There is no real "connection" established because NTP solely works
218 ****************************************************************************
220 * hostname char* Hostname/IP of the host running ntpd
221 * fam int Address Family (AF_INET, AF_INET6, or 0)
224 * int 1 if the host connection could be set up, i.e.
225 * name resolution was succesful and/or IP address
228 * 0 (zero) if a failure occured
229 ****************************************************************************/
237 if ( openhost(hostname, fam) )
249 /*****************************************************************************
253 * Cleans up a connection by closing the used socket. Should be called
254 * when no further queries are required for the currently used host.
256 ****************************************************************************
261 * int 0 (zero) if no host has been opened before
263 * the resultcode from the closesocket function call
264 ****************************************************************************/
266 int ntpq_closehost(void)
269 return closesocket(sockfd);
275 /*****************************************************************************
277 * ntpq_read_associations
279 * This function queries the ntp host for its associations and returns the
280 * number of associations found.
282 * It takes an u_short array as its first parameter, this array holds the
283 * IDs of the associations,
284 * the function will not write more entries than specified with the
285 * max_entries parameter.
287 * However, if more than max_entries associations were found, the return
288 * value of this function will reflect the real number, even if not all
289 * associations have been stored in the array.
291 ****************************************************************************
293 * resultbuf u_short*Array that should hold the list of
295 * maxentries int maximum number of association IDs that can
296 * be stored in resultbuf
299 * int number of association IDs stored in resultbuf
301 * 0 (zero) if a failure occured or no association has
303 ****************************************************************************/
305 int ntpq_read_associations ( u_short resultbuf[], int max_entries )
309 if (ntpq_dogetassoc()) {
311 if(numassoc < max_entries)
312 max_entries = numassoc;
314 for (i=0;i<max_entries;i++)
315 resultbuf[i] = assoc_cache[i].assid;
326 /*****************************************************************************
330 * This function reads the associations of a previously selected (with
331 * ntpq_openhost) NTP host into its own (global) array and returns the
332 * number of associations found.
334 * The obtained association IDs can be read by using the ntpq_get_assoc_id
337 ****************************************************************************
342 * int number of association IDs stored in resultbuf
344 * 0 (zero) if a failure occured or no association has
346 ****************************************************************************/
348 int ntpq_get_assocs ( void )
350 return ntpq_read_associations( ntpq_associations, MAXASSOC );
354 /*****************************************************************************
356 * ntpq_get_assoc_number
358 * This function returns for a given Association ID the association number
359 * in the internal association array, which is filled by the ntpq_get_assocs
362 ****************************************************************************
364 * associd int requested associaton ID
367 * int the number of the association array element that is
368 * representing the given association ID
370 * -1 if a failure occured or no matching association
372 ****************************************************************************/
374 int ntpq_get_assoc_number ( associd_t associd )
378 for (i=0;i<numassoc;i++) {
379 if (assoc_cache[i].assid == associd)
388 /*****************************************************************************
390 * ntpq_read_assoc_peervars
392 * This function reads the peervars variable-set of a specified association
393 * from a NTP host and writes it to the result buffer specified, honoring
396 * It returns the number of bytes written or 0 when the variable-set is
397 * empty or failed to read.
399 ****************************************************************************
401 * associd int requested associaton ID
402 * resultbuf char* character buffer where the variable set
404 * maxsize int the maximum number of bytes that can be
405 * written to resultbuf
408 * int number of chars that have been copied to
411 * 0 (zero) if an error occured
412 ****************************************************************************/
415 ntpq_read_assoc_peervars(
426 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
432 fprintf(stderr, "server=%s ", currenthost);
434 "***No information returned for association %d\n",
441 memcpy(resultbuf, datap, dsize);
449 /*****************************************************************************
453 * This function reads the sysvars variable-set from a NTP host and writes it
454 * to the result buffer specified, honoring the maxsize limit.
456 * It returns the number of bytes written or 0 when the variable-set is empty
457 * or could not be read.
459 ****************************************************************************
461 * resultbuf char* character buffer where the variable set
463 * maxsize int the maximum number of bytes that can be
464 * written to resultbuf
467 * int number of chars that have been copied to
470 * 0 (zero) if an error occured
471 ****************************************************************************/
483 res = doquery(CTL_OP_READVAR, 0, 0, 0, NULL, &rstatus,
491 fprintf(stderr, "server=%s ", currenthost);
492 fprintf(stderr, "***No sysvar information returned\n");
496 dsize = min(dsize, maxsize);
497 memcpy(resultbuf, datap, dsize);
504 /*****************************************************************************
505 * ntpq_get_assoc_allvars
507 * With this function all association variables for the specified association
508 * ID can be requested from a NTP host. They are stored internally and can be
509 * read by using the ntpq_get_peervar or ntpq_get_clockvar functions.
511 * Basically this is only a combination of the ntpq_get_assoc_peervars and
512 * ntpq_get_assoc_clockvars functions.
514 * It returns 1 if both variable-sets (peervars and clockvars) were
515 * received successfully. If one variable-set or both of them weren't
518 ****************************************************************************
520 * associd int requested associaton ID
523 * int nonzero if at least one variable set could be read
525 * 0 (zero) if an error occured and both variable sets
527 ****************************************************************************/
528 int ntpq_get_assoc_allvars( associd_t associd )
530 return ntpq_get_assoc_peervars ( associd ) &
531 ntpq_get_assoc_clockvars( associd );
537 /*****************************************************************************
541 * The system variables of a NTP host can be requested by using this function
542 * and afterwards using ntpq_get_sysvar to read the single variable values.
544 ****************************************************************************
549 * int nonzero if the variable set could be read
551 * 0 (zero) if an error occured and the sysvars
553 ****************************************************************************/
555 ntpq_get_sysvars(void)
557 sysvarlen = ntpq_read_sysvars(sysvars, sizeof(sysvars));
565 /*****************************************************************************
569 * This function uses the variable-set which was read by using
570 * ntp_get_peervars and searches for a variable specified with varname. If
571 * such a variable exists, it writes its value into
572 * varvalue (maxlen specifies the size of this target buffer).
574 ****************************************************************************
576 * varname char* requested variable name
577 * varvalue char* the buffer where the value should go into
578 * maxlen int maximum number of bytes that can be copied to
582 * int number of bytes copied to varvalue
584 * 0 (zero) if an error occured or the variable could
586 ****************************************************************************/
587 int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen)
589 return ( ntpq_getvar(peervars,peervarlen,varname,varvalue,maxlen) );
594 /*****************************************************************************
596 * ntpq_get_assoc_peervars
598 * This function requests the peer variables of the specified association
599 * from a NTP host. In order to access the variable values, the function
600 * ntpq_get_peervar must be used.
602 ****************************************************************************
604 * associd int requested associaton ID
607 * int 1 (one) if the peervars have been read
609 * 0 (zero) if an error occured and the variable set
611 ****************************************************************************/
613 ntpq_get_assoc_peervars(
617 peervarlen = ntpq_read_assoc_peervars(associd, peervars,
619 if (peervarlen <= 0) {
624 peervar_assoc = associd;
630 /*****************************************************************************
632 * ntp_read_assoc_clockvars
634 * This function reads the clockvars variable-set of a specified association
635 * from a NTP host and writes it to the result buffer specified, honoring
638 * It returns the number of bytes written or 0 when the variable-set is
639 * empty or failed to read.
641 ****************************************************************************
643 * associd int requested associaton ID
644 * resultbuf char* character buffer where the variable set
646 * maxsize int the maximum number of bytes that can be
647 * written to resultbuf
650 * int number of chars that have been copied to
653 * 0 (zero) if an error occured
654 ****************************************************************************/
657 ntpq_read_assoc_clockvars(
668 res = ntpq_doquerylist(ntpq_varlist, CTL_OP_READCLOCK, associd,
669 0, &rstatus, &dsize, &datap);
674 if (numhosts > 1) /* no information returned from server */
679 memcpy(resultbuf, datap, dsize);
687 /*****************************************************************************
689 * ntpq_get_assoc_clocktype
691 * This function returns a clocktype value for a given association number
694 * NTP_CLOCKTYPE_UNKNOWN Unknown clock type
695 * NTP_CLOCKTYPE_BROADCAST Broadcast server
696 * NTP_CLOCKTYPE_LOCAL Local clock
697 * NTP_CLOCKTYPE_UNICAST Unicast server
698 * NTP_CLOCKTYPE_MULTICAST Multicast server
700 ****************************************************************************/
702 ntpq_get_assoc_clocktype(
709 sockaddr_u dum_store;
710 char dstadr[LENHOSTNAME];
711 char resultbuf[NTPQ_BUFLEN];
713 if (assoc_index < 0 || assoc_index >= numassoc)
716 associd = assoc_cache[assoc_index].assid;
717 if (associd == peervar_assoc) {
718 rc = ntpq_get_peervar("dstadr", dstadr, sizeof(dstadr));
720 i = ntpq_read_assoc_peervars(associd, resultbuf,
724 rc = ntpq_getvar(resultbuf, i, "dstadr", dstadr,
728 if (0 != rc && decodenetnum(dstadr, &dum_store))
729 return ntpq_decodeaddrtype(&dum_store);
736 /*****************************************************************************
738 * ntpq_get_assoc_clockvars
740 * With this function the clock variables of the specified association are
741 * requested from a NTP host. This makes only sense for associations with
742 * the type 'l' (Local Clock) and you should check this with
743 * ntpq_get_assoc_clocktype for each association, before you use this function
746 ****************************************************************************
748 * associd int requested associaton ID
751 * int 1 (one) if the clockvars have been read
753 * 0 (zero) if an error occured and the variable set
755 ****************************************************************************/
756 int ntpq_get_assoc_clockvars( associd_t associd )
758 if (NTP_CLOCKTYPE_LOCAL != ntpq_get_assoc_clocktype(
759 ntpq_get_assoc_number(associd)))
761 clockvarlen = ntpq_read_assoc_clockvars( associd, clockvars,
763 if ( clockvarlen <= 0 ) {
767 clockvar_assoc = associd;