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* tmpbuf = srcbuf;
62 while ( *tmpbuf != 0 )
64 if ( *tmpbuf == '\"' )
70 if ( *tmpbuf == '\\' )
75 /* ignore if end of string */
78 /* skip and do not copy */
79 case '\"': /* quotes */
81 case 'r': /*carriage return*/
89 *resultbuf++ = *tmpbuf++;
94 return strlen(resultbuf);
98 /*****************************************************************************
102 * This function parses a given buffer for a variable/value pair and
103 * copies the value of the requested variable into the specified
106 * It returns the number of bytes copied or zero for an empty result
107 * (=no matching variable found or empty value)
109 ****************************************************************************
111 * resultbuf char* The resulting string without quoted
113 * datalen size_t The number of bytes stored in
115 * varname char* Name of the required variable
116 * varvalue char* Where the value of the variable should
118 * maxlen size_t Max. number of bytes for varvalue
121 * size_t number of chars that have been copied to
123 ****************************************************************************/
127 const char * resultbuf,
129 const char * varname,
138 idatalen = (int)datalen;
140 while (nextvar(&idatalen, &resultbuf, &name, &value)) {
141 if (strcmp(varname, name) == 0) {
142 ntpq_stripquotes(varvalue, value, strlen(value), maxlen);
144 return strlen(varvalue);
152 /*****************************************************************************
156 * Sends a mode 6 query packet to the current open host (see
157 * ntpq_openhost) and stores the requested variable set in the specified
159 * It returns the number of bytes read or zero for an empty result
160 * (=no answer or empty value)
162 ****************************************************************************
164 * VARSET u_short Which variable set should be
165 * read (PEERVARS or CLOCKVARS)
166 * association int The association ID that should be read
167 * 0 represents the ntpd instance itself
168 * resultbuf char* The resulting string without quoted
170 * maxlen int Max. number of bytes for varvalue
173 * int number of bytes that have been copied to
176 * 0 (zero) if no reply has been received or
177 * another failure occured
178 ****************************************************************************/
180 int ntpq_queryhost(unsigned short VARSET, unsigned short association, char *resultbuf, int maxlen)
188 res = doquery(VARSET,association,0,0, (char *)0, &rstatus, &dsize, &datap);
192 if ( ( res != 0) || ( dsize == 0 ) ) /* no data */
199 /* fill result resultbuf */
200 memcpy(resultbuf, datap, dsize);
207 /*****************************************************************************
211 * Sets up a connection to the ntpd instance of a specified host. Note:
212 * There is no real "connection" established because NTP solely works
215 ****************************************************************************
217 * hostname char* Hostname/IP of the host running ntpd
218 * fam int Address Family (AF_INET, AF_INET6, or 0)
221 * int 1 if the host connection could be set up, i.e.
222 * name resolution was succesful and/or IP address
225 * 0 (zero) if a failure occured
226 ****************************************************************************/
234 if ( openhost(hostname, fam) )
246 /*****************************************************************************
250 * Cleans up a connection by closing the used socket. Should be called
251 * when no further queries are required for the currently used host.
253 ****************************************************************************
258 * int 0 (zero) if no host has been opened before
260 * the resultcode from the closesocket function call
261 ****************************************************************************/
263 int ntpq_closehost(void)
266 return closesocket(sockfd);
272 /*****************************************************************************
274 * ntpq_read_associations
276 * This function queries the ntp host for its associations and returns the
277 * number of associations found.
279 * It takes an u_short array as its first parameter, this array holds the
280 * IDs of the associations,
281 * the function will not write more entries than specified with the
282 * max_entries parameter.
284 * However, if more than max_entries associations were found, the return
285 * value of this function will reflect the real number, even if not all
286 * associations have been stored in the array.
288 ****************************************************************************
290 * resultbuf u_short*Array that should hold the list of
292 * maxentries int maximum number of association IDs that can
293 * be stored in resultbuf
296 * int number of association IDs stored in resultbuf
298 * 0 (zero) if a failure occured or no association has
300 ****************************************************************************/
302 int ntpq_read_associations ( u_short resultbuf[], int max_entries )
306 if (ntpq_dogetassoc()) {
308 if(numassoc < max_entries)
309 max_entries = numassoc;
311 for (i=0;i<max_entries;i++)
312 resultbuf[i] = assoc_cache[i].assid;
323 /*****************************************************************************
327 * This function reads the associations of a previously selected (with
328 * ntpq_openhost) NTP host into its own (global) array and returns the
329 * number of associations found.
331 * The obtained association IDs can be read by using the ntpq_get_assoc_id
334 ****************************************************************************
339 * int number of association IDs stored in resultbuf
341 * 0 (zero) if a failure occured or no association has
343 ****************************************************************************/
345 int ntpq_get_assocs ( void )
347 return ntpq_read_associations( ntpq_associations, MAXASSOC );
351 /*****************************************************************************
353 * ntpq_get_assoc_number
355 * This function returns for a given Association ID the association number
356 * in the internal association array, which is filled by the ntpq_get_assocs
359 ****************************************************************************
361 * associd int requested associaton ID
364 * int the number of the association array element that is
365 * representing the given association ID
367 * -1 if a failure occured or no matching association
369 ****************************************************************************/
371 int ntpq_get_assoc_number ( associd_t associd )
375 for (i=0;i<numassoc;i++) {
376 if (assoc_cache[i].assid == associd)
385 /*****************************************************************************
387 * ntpq_read_assoc_peervars
389 * This function reads the peervars variable-set of a specified association
390 * from a NTP host and writes it to the result buffer specified, honoring
393 * It returns the number of bytes written or 0 when the variable-set is
394 * empty or failed to read.
396 ****************************************************************************
398 * associd int requested associaton ID
399 * resultbuf char* character buffer where the variable set
401 * maxsize int the maximum number of bytes that can be
402 * written to resultbuf
405 * int number of chars that have been copied to
408 * 0 (zero) if an error occured
409 ****************************************************************************/
412 ntpq_read_assoc_peervars(
423 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
429 fprintf(stderr, "server=%s ", currenthost);
431 "***No information returned for association %d\n",
438 memcpy(resultbuf, datap, dsize);
446 /*****************************************************************************
450 * This function reads the sysvars variable-set from a NTP host and writes it
451 * to the result buffer specified, honoring the maxsize limit.
453 * It returns the number of bytes written or 0 when the variable-set is empty
454 * or could not be read.
456 ****************************************************************************
458 * resultbuf char* character buffer where the variable set
460 * maxsize int the maximum number of bytes that can be
461 * written to resultbuf
464 * int number of chars that have been copied to
467 * 0 (zero) if an error occured
468 ****************************************************************************/
481 res = doquery(CTL_OP_READVAR, 0, 0, 0, NULL, &rstatus,
489 fprintf(stderr, "server=%s ", currenthost);
490 fprintf(stderr, "***No sysvar information returned\n");
494 dsize = max(0, i_dsize);
495 dsize = min(dsize, maxsize);
496 memcpy(resultbuf, datap, dsize);
503 /*****************************************************************************
504 * ntpq_get_assoc_allvars
506 * With this function all association variables for the specified association
507 * ID can be requested from a NTP host. They are stored internally and can be
508 * read by using the ntpq_get_peervar or ntpq_get_clockvar functions.
510 * Basically this is only a combination of the ntpq_get_assoc_peervars and
511 * ntpq_get_assoc_clockvars functions.
513 * It returns 1 if both variable-sets (peervars and clockvars) were
514 * received successfully. If one variable-set or both of them weren't
517 ****************************************************************************
519 * associd int requested associaton ID
522 * int nonzero if at least one variable set could be read
524 * 0 (zero) if an error occured and both variable sets
526 ****************************************************************************/
527 int ntpq_get_assoc_allvars( associd_t associd )
529 return ntpq_get_assoc_peervars ( associd ) &
530 ntpq_get_assoc_clockvars( associd );
536 /*****************************************************************************
540 * The system variables of a NTP host can be requested by using this function
541 * and afterwards using ntpq_get_sysvar to read the single variable values.
543 ****************************************************************************
548 * int nonzero if the variable set could be read
550 * 0 (zero) if an error occured and the sysvars
552 ****************************************************************************/
554 ntpq_get_sysvars(void)
556 sysvarlen = ntpq_read_sysvars(sysvars, sizeof(sysvars));
564 /*****************************************************************************
568 * This function uses the variable-set which was read by using
569 * ntp_get_peervars and searches for a variable specified with varname. If
570 * such a variable exists, it writes its value into
571 * varvalue (maxlen specifies the size of this target buffer).
573 ****************************************************************************
575 * varname char* requested variable name
576 * varvalue char* the buffer where the value should go into
577 * maxlen int maximum number of bytes that can be copied to
581 * int number of bytes copied to varvalue
583 * 0 (zero) if an error occured or the variable could
585 ****************************************************************************/
586 int ntpq_get_peervar( const char *varname, char *varvalue, int maxlen)
588 return ( ntpq_getvar(peervars,peervarlen,varname,varvalue,maxlen) );
593 /*****************************************************************************
595 * ntpq_get_assoc_peervars
597 * This function requests the peer variables of the specified association
598 * from a NTP host. In order to access the variable values, the function
599 * ntpq_get_peervar must be used.
601 ****************************************************************************
603 * associd int requested associaton ID
606 * int 1 (one) if the peervars have been read
608 * 0 (zero) if an error occured and the variable set
610 ****************************************************************************/
612 ntpq_get_assoc_peervars(
616 peervarlen = ntpq_read_assoc_peervars(associd, peervars,
618 if (peervarlen <= 0) {
623 peervar_assoc = associd;
629 /*****************************************************************************
631 * ntp_read_assoc_clockvars
633 * This function reads the clockvars variable-set of a specified association
634 * from a NTP host and writes it to the result buffer specified, honoring
637 * It returns the number of bytes written or 0 when the variable-set is
638 * empty or failed to read.
640 ****************************************************************************
642 * associd int requested associaton ID
643 * resultbuf char* character buffer where the variable set
645 * maxsize int the maximum number of bytes that can be
646 * written to resultbuf
649 * int number of chars that have been copied to
652 * 0 (zero) if an error occured
653 ****************************************************************************/
656 ntpq_read_assoc_clockvars(
667 res = ntpq_doquerylist(ntpq_varlist, CTL_OP_READCLOCK, associd,
668 0, &rstatus, &dsize, &datap);
673 if (numhosts > 1) /* no information returned from server */
678 memcpy(resultbuf, datap, dsize);
686 /*****************************************************************************
688 * ntpq_get_assoc_clocktype
690 * This function returns a clocktype value for a given association number
693 * NTP_CLOCKTYPE_UNKNOWN Unknown clock type
694 * NTP_CLOCKTYPE_BROADCAST Broadcast server
695 * NTP_CLOCKTYPE_LOCAL Local clock
696 * NTP_CLOCKTYPE_UNICAST Unicast server
697 * NTP_CLOCKTYPE_MULTICAST Multicast server
699 ****************************************************************************/
701 ntpq_get_assoc_clocktype(
708 sockaddr_u dum_store;
709 char dstadr[LENHOSTNAME];
710 char resultbuf[NTPQ_BUFLEN];
712 if (assoc_index < 0 || assoc_index >= numassoc)
715 associd = assoc_cache[assoc_index].assid;
716 if (associd == peervar_assoc) {
717 rc = ntpq_get_peervar("dstadr", dstadr, sizeof(dstadr));
719 i = ntpq_read_assoc_peervars(associd, resultbuf,
723 rc = ntpq_getvar(resultbuf, i, "dstadr", dstadr,
727 if (0 != rc && decodenetnum(dstadr, &dum_store))
728 return ntpq_decodeaddrtype(&dum_store);
735 /*****************************************************************************
737 * ntpq_get_assoc_clockvars
739 * With this function the clock variables of the specified association are
740 * requested from a NTP host. This makes only sense for associations with
741 * the type 'l' (Local Clock) and you should check this with
742 * ntpq_get_assoc_clocktype for each association, before you use this function
745 ****************************************************************************
747 * associd int requested associaton ID
750 * int 1 (one) if the clockvars have been read
752 * 0 (zero) if an error occured and the variable set
754 ****************************************************************************/
755 int ntpq_get_assoc_clockvars( associd_t associd )
757 if (NTP_CLOCKTYPE_LOCAL != ntpq_get_assoc_clocktype(
758 ntpq_get_assoc_number(associd)))
760 clockvarlen = ntpq_read_assoc_clockvars( associd, clockvars,
762 if ( clockvarlen <= 0 ) {
766 clockvar_assoc = associd;