2 * ntpq-subs.c - subroutines which are called to perform ntpq commands.
11 #include "ntpq-opts.h"
13 extern char currenthost[];
14 extern int currenthostisnum;
18 * Declarations for command handlers in here
20 static associd_t checkassocid (u_int32);
21 static struct varlist *findlistvar (struct varlist *, char *);
22 static void doaddvlist (struct varlist *, const char *);
23 static void dormvlist (struct varlist *, const char *);
24 static void doclearvlist (struct varlist *);
25 static void makequerydata (struct varlist *, size_t *, char *);
26 static int doquerylist (struct varlist *, int, associd_t, int,
27 u_short *, size_t *, const char **);
28 static void doprintvlist (struct varlist *, FILE *);
29 static void addvars (struct parse *, FILE *);
30 static void rmvars (struct parse *, FILE *);
31 static void clearvars (struct parse *, FILE *);
32 static void showvars (struct parse *, FILE *);
33 static int dolist (struct varlist *, associd_t, int, int,
35 static void readlist (struct parse *, FILE *);
36 static void writelist (struct parse *, FILE *);
37 static void readvar (struct parse *, FILE *);
38 static void writevar (struct parse *, FILE *);
39 static void clocklist (struct parse *, FILE *);
40 static void clockvar (struct parse *, FILE *);
41 static int findassidrange (u_int32, u_int32, int *, int *,
43 static void mreadlist (struct parse *, FILE *);
44 static void mreadvar (struct parse *, FILE *);
45 static void printassoc (int, FILE *);
46 static void associations (struct parse *, FILE *);
47 static void lassociations (struct parse *, FILE *);
48 static void passociations (struct parse *, FILE *);
49 static void lpassociations (struct parse *, FILE *);
52 static void radiostatus (struct parse *, FILE *);
55 static void authinfo (struct parse *, FILE *);
56 static void pstats (struct parse *, FILE *);
57 static long when (l_fp *, l_fp *, l_fp *);
58 static char * prettyinterval (char *, size_t, long);
59 static int doprintpeers (struct varlist *, int, int, size_t, const char *, FILE *, int);
60 static int dogetpeers (struct varlist *, associd_t, FILE *, int);
61 static void dopeers (int, FILE *, int);
62 static void peers (struct parse *, FILE *);
63 static void doapeers (int, FILE *, int);
64 static void apeers (struct parse *, FILE *);
65 static void lpeers (struct parse *, FILE *);
66 static void doopeers (int, FILE *, int);
67 static void opeers (struct parse *, FILE *);
68 static void lopeers (struct parse *, FILE *);
69 static void config (struct parse *, FILE *);
70 static void saveconfig (struct parse *, FILE *);
71 static void config_from_file(struct parse *, FILE *);
72 static void mrulist (struct parse *, FILE *);
73 static void ifstats (struct parse *, FILE *);
74 static void reslist (struct parse *, FILE *);
75 static void sysstats (struct parse *, FILE *);
76 static void sysinfo (struct parse *, FILE *);
77 static void kerninfo (struct parse *, FILE *);
78 static void monstats (struct parse *, FILE *);
79 static void iostats (struct parse *, FILE *);
80 static void timerstats (struct parse *, FILE *);
83 * Commands we understand. Ntpdc imports this.
85 struct xcmd opcmds[] = {
86 { "saveconfig", saveconfig, { NTP_STR, NO, NO, NO },
87 { "filename", "", "", ""},
88 "save ntpd configuration to file, . for current config file"},
89 { "associations", associations, { NO, NO, NO, NO },
91 "print list of association ID's and statuses for the server's peers" },
92 { "passociations", passociations, { NO, NO, NO, NO },
94 "print list of associations returned by last associations command" },
95 { "lassociations", lassociations, { NO, NO, NO, NO },
97 "print list of associations including all client information" },
98 { "lpassociations", lpassociations, { NO, NO, NO, NO },
100 "print last obtained list of associations, including client information" },
101 { "addvars", addvars, { NTP_STR, NO, NO, NO },
102 { "name[=value][,...]", "", "", "" },
103 "add variables to the variable list or change their values" },
104 { "rmvars", rmvars, { NTP_STR, NO, NO, NO },
105 { "name[,...]", "", "", "" },
106 "remove variables from the variable list" },
107 { "clearvars", clearvars, { NO, NO, NO, NO },
109 "remove all variables from the variable list" },
110 { "showvars", showvars, { NO, NO, NO, NO },
112 "print variables on the variable list" },
113 { "readlist", readlist, { OPT|NTP_UINT, NO, NO, NO },
114 { "assocID", "", "", "" },
115 "read the system or peer variables included in the variable list" },
116 { "rl", readlist, { OPT|NTP_UINT, NO, NO, NO },
117 { "assocID", "", "", "" },
118 "read the system or peer variables included in the variable list" },
119 { "writelist", writelist, { OPT|NTP_UINT, NO, NO, NO },
120 { "assocID", "", "", "" },
121 "write the system or peer variables included in the variable list" },
122 { "readvar", readvar, { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
123 { "assocID", "varname1", "varname2", "varname3" },
124 "read system or peer variables" },
125 { "rv", readvar, { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
126 { "assocID", "varname1", "varname2", "varname3" },
127 "read system or peer variables" },
128 { "writevar", writevar, { NTP_UINT, NTP_STR, NO, NO },
129 { "assocID", "name=value,[...]", "", "" },
130 "write system or peer variables" },
131 { "mreadlist", mreadlist, { NTP_UINT, NTP_UINT, NO, NO },
132 { "assocIDlow", "assocIDhigh", "", "" },
133 "read the peer variables in the variable list for multiple peers" },
134 { "mrl", mreadlist, { NTP_UINT, NTP_UINT, NO, NO },
135 { "assocIDlow", "assocIDhigh", "", "" },
136 "read the peer variables in the variable list for multiple peers" },
137 { "mreadvar", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
138 { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
139 "read peer variables from multiple peers" },
140 { "mrv", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
141 { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
142 "read peer variables from multiple peers" },
143 { "clocklist", clocklist, { OPT|NTP_UINT, NO, NO, NO },
144 { "assocID", "", "", "" },
145 "read the clock variables included in the variable list" },
146 { "cl", clocklist, { OPT|NTP_UINT, NO, NO, NO },
147 { "assocID", "", "", "" },
148 "read the clock variables included in the variable list" },
149 { "clockvar", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
150 { "assocID", "name=value[,...]", "", "" },
151 "read clock variables" },
152 { "cv", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
153 { "assocID", "name=value[,...]", "", "" },
154 "read clock variables" },
155 { "pstats", pstats, { NTP_UINT, NO, NO, NO },
156 { "assocID", "", "", "" },
157 "show statistics for a peer" },
158 { "peers", peers, { OPT|IP_VERSION, NO, NO, NO },
159 { "-4|-6", "", "", "" },
160 "obtain and print a list of the server's peers [IP version]" },
161 { "apeers", apeers, { OPT|IP_VERSION, NO, NO, NO },
162 { "-4|-6", "", "", "" },
163 "obtain and print a list of the server's peers and their assocIDs [IP version]" },
164 { "lpeers", lpeers, { OPT|IP_VERSION, NO, NO, NO },
165 { "-4|-6", "", "", "" },
166 "obtain and print a list of all peers and clients [IP version]" },
167 { "opeers", opeers, { OPT|IP_VERSION, NO, NO, NO },
168 { "-4|-6", "", "", "" },
169 "print peer list the old way, with dstadr shown rather than refid [IP version]" },
170 { "lopeers", lopeers, { OPT|IP_VERSION, NO, NO, NO },
171 { "-4|-6", "", "", "" },
172 "obtain and print a list of all peers and clients showing dstadr [IP version]" },
173 { ":config", config, { NTP_STR, NO, NO, NO },
174 { "<configuration command line>", "", "", "" },
175 "send a remote configuration command to ntpd" },
176 { "config-from-file", config_from_file, { NTP_STR, NO, NO, NO },
177 { "<configuration filename>", "", "", "" },
178 "configure ntpd using the configuration filename" },
179 { "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
180 { "tag=value", "tag=value", "tag=value", "tag=value" },
181 "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." },
182 { "ifstats", ifstats, { NO, NO, NO, NO },
184 "show statistics for each local address ntpd is using" },
185 { "reslist", reslist, { NO, NO, NO, NO },
187 "show ntpd access control list" },
188 { "sysinfo", sysinfo, { NO, NO, NO, NO },
190 "display system summary" },
191 { "kerninfo", kerninfo, { NO, NO, NO, NO },
193 "display kernel loop and PPS statistics" },
194 { "sysstats", sysstats, { NO, NO, NO, NO },
196 "display system uptime and packet counts" },
197 { "monstats", monstats, { NO, NO, NO, NO },
199 "display monitor (mrulist) counters and limits" },
200 { "authinfo", authinfo, { NO, NO, NO, NO },
202 "display symmetric authentication counters" },
203 { "iostats", iostats, { NO, NO, NO, NO },
205 "display network input and output counters" },
206 { "timerstats", timerstats, { NO, NO, NO, NO },
208 "display interval timer counters" },
209 { 0, 0, { NO, NO, NO, NO },
210 { "-4|-6", "", "", "" }, "" }
215 * Variable list data space
217 #define MAXLINE 512 /* maximum length of a line */
218 #define MAXLIST 128 /* maximum variables in list */
219 #define LENHOSTNAME 256 /* host name limit */
221 #define MRU_GOT_COUNT 0x1
222 #define MRU_GOT_LAST 0x2
223 #define MRU_GOT_FIRST 0x4
224 #define MRU_GOT_MV 0x8
225 #define MRU_GOT_RS 0x10
226 #define MRU_GOT_ADDR 0x20
227 #define MRU_GOT_ALL (MRU_GOT_COUNT | MRU_GOT_LAST | MRU_GOT_FIRST \
228 | MRU_GOT_MV | MRU_GOT_RS | MRU_GOT_ADDR)
231 * mrulist() depends on MRUSORT_DEF and MRUSORT_RDEF being the first two
233 typedef enum mru_sort_order_tag {
234 MRUSORT_DEF = 0, /* lstint ascending */
235 MRUSORT_R_DEF, /* lstint descending */
236 MRUSORT_AVGINT, /* avgint ascending */
237 MRUSORT_R_AVGINT, /* avgint descending */
238 MRUSORT_ADDR, /* IPv4 asc. then IPv6 asc. */
239 MRUSORT_R_ADDR, /* IPv6 desc. then IPv4 desc. */
240 MRUSORT_COUNT, /* hit count ascending */
241 MRUSORT_R_COUNT, /* hit count descending */
242 MRUSORT_MAX, /* special: count of this enum */
245 const char * const mru_sort_keywords[MRUSORT_MAX] = {
246 "lstint", /* MRUSORT_DEF */
247 "-lstint", /* MRUSORT_R_DEF */
248 "avgint", /* MRUSORT_AVGINT */
249 "-avgint", /* MRUSORT_R_AVGINT */
250 "addr", /* MRUSORT_ADDR */
251 "-addr", /* MRUSORT_R_ADDR */
252 "count", /* MRUSORT_COUNT */
253 "-count", /* MRUSORT_R_COUNT */
256 typedef int (*qsort_cmp)(const void *, const void *);
259 * Old CTL_PST defines for version 2.
261 #define OLD_CTL_PST_CONFIG 0x80
262 #define OLD_CTL_PST_AUTHENABLE 0x40
263 #define OLD_CTL_PST_AUTHENTIC 0x20
264 #define OLD_CTL_PST_REACH 0x10
265 #define OLD_CTL_PST_SANE 0x08
266 #define OLD_CTL_PST_DISP 0x04
268 #define OLD_CTL_PST_SEL_REJECT 0
269 #define OLD_CTL_PST_SEL_SELCAND 1
270 #define OLD_CTL_PST_SEL_SYNCCAND 2
271 #define OLD_CTL_PST_SEL_SYSPEER 3
273 char flash2[] = " .+* "; /* flash decode for version 2 */
274 char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
279 } g_varlist[MAXLIST] = { { 0, 0 } };
282 * Imported from ntpq.c
284 extern int showhostnames;
285 extern int wideremote;
287 extern struct servent *server_entry;
288 extern struct association *assoc_cache;
289 extern u_char pktversion;
291 typedef struct mru_tag mru;
293 mru * hlink; /* next in hash table bucket */
294 DECL_DLIST_LINK(mru, mlink);
304 typedef struct ifstats_row_tag {
320 typedef struct reslist_row_tag {
328 typedef struct var_display_collection_tag {
329 const char * const tag; /* system variable */
330 const char * const display; /* descriptive text */
331 u_char type; /* NTP_STR, etc */
334 sockaddr_u sau; /* NTP_ADD */
335 l_fp lfp; /* NTP_LFP */
336 } v; /* retrieved value */
338 #if !defined(MISSING_C99_STRUCT_INIT)
339 # define VDC_INIT(a, b, c) { .tag = a, .display = b, .type = c }
341 # define VDC_INIT(a, b, c) { a, b, c }
344 * other local function prototypes
346 static int mrulist_ctrl_c_hook(void);
347 static mru * add_mru(mru *);
348 static int collect_mru_list(const char *, l_fp *);
349 static int fetch_nonce(char *, size_t);
350 static int qcmp_mru_avgint(const void *, const void *);
351 static int qcmp_mru_r_avgint(const void *, const void *);
352 static int qcmp_mru_addr(const void *, const void *);
353 static int qcmp_mru_r_addr(const void *, const void *);
354 static int qcmp_mru_count(const void *, const void *);
355 static int qcmp_mru_r_count(const void *, const void *);
356 static void validate_ifnum(FILE *, u_int, int *, ifstats_row *);
357 static void another_ifstats_field(int *, ifstats_row *, FILE *);
358 static void collect_display_vdc(associd_t as, vdc *table,
359 int decodestatus, FILE *fp);
361 static int xprintf(FILE *, char const *, ...) NTP_PRINTF(2, 3);
362 static int xputs(char const *, FILE *);
363 static int xputc(int, FILE *);
368 static u_int mru_count;
369 static u_int mru_dupes;
370 volatile int mrulist_interrupted;
371 static mru mru_list; /* listhead */
372 static mru ** hash_table;
375 * qsort comparison function table for mrulist(). The first two
376 * entries are NULL because they are handled without qsort().
378 static const qsort_cmp mru_qcmp_table[MRUSORT_MAX] = {
379 NULL, /* MRUSORT_DEF unused */
380 NULL, /* MRUSORT_R_DEF unused */
381 &qcmp_mru_avgint, /* MRUSORT_AVGINT */
382 &qcmp_mru_r_avgint, /* MRUSORT_R_AVGINT */
383 &qcmp_mru_addr, /* MRUSORT_ADDR */
384 &qcmp_mru_r_addr, /* MRUSORT_R_ADDR */
385 &qcmp_mru_count, /* MRUSORT_COUNT */
386 &qcmp_mru_r_count, /* MRUSORT_R_COUNT */
390 * NULL-pointer safe FILE I/O: use stderr if no file supplied.
403 rc = vfprintf((ofp ? ofp : stderr), fmt, va);
414 return fputs(str, (ofp ? ofp : stderr));
423 return fputc(ch, (ofp ? ofp : stderr));
427 * checkassocid - return the association ID, checking to see if it is valid
437 associd = (associd_t)value;
438 if (0 == associd || value != associd) {
441 "***Invalid association ID %lu specified\n",
451 * findlistvar - Look for the named variable in a varlist. If found,
452 * return a pointer to it. Otherwise, if the list has
453 * slots available, return the pointer to the first free
454 * slot, or NULL if it's full.
456 static struct varlist *
458 struct varlist *list,
464 for (vl = list; vl < list + MAXLIST && vl->name != NULL; vl++)
465 if (!strcmp(name, vl->name))
467 if (vl < list + MAXLIST)
475 * doaddvlist - add variable(s) to the variable list
479 struct varlist *vlist,
489 while (nextvar(&len, &vars, &name, &value)) {
490 INSIST(name && value);
491 vl = findlistvar(vlist, name);
493 xprintf(stderr, "Variable list full\n");
497 if (NULL == vl->name) {
498 vl->name = estrdup(name);
499 } else if (vl->value != NULL) {
505 vl->value = estrdup(value);
511 * dormvlist - remove variable(s) from the variable list
515 struct varlist *vlist,
525 while (nextvar(&len, &vars, &name, &value)) {
526 INSIST(name && value);
527 vl = findlistvar(vlist, name);
528 if (vl == 0 || vl->name == 0) {
529 (void) xprintf(stderr, "Variable `%s' not found\n",
532 free((void *)(intptr_t)vl->name);
535 for ( ; (vl+1) < (g_varlist + MAXLIST)
536 && (vl+1)->name != 0; vl++) {
537 vl->name = (vl+1)->name;
538 vl->value = (vl+1)->value;
540 vl->name = vl->value = 0;
547 * doclearvlist - clear a variable list
551 struct varlist *vlist
554 register struct varlist *vl;
556 for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
557 free((void *)(intptr_t)vl->name);
559 if (vl->value != 0) {
568 * makequerydata - form a data buffer to be included with a query
572 struct varlist *vlist,
577 register struct varlist *vl;
578 register char *cp, *cpend;
579 register size_t namelen, valuelen;
580 register size_t totallen;
583 cpend = data + *datalen;
585 for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
586 namelen = strlen(vl->name);
590 valuelen = strlen(vl->value);
591 totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
592 if (cp + totallen > cpend) {
594 "***Ignoring variables starting with `%s'\n",
601 memcpy(cp, vl->name, (size_t)namelen);
605 memcpy(cp, vl->value, (size_t)valuelen);
609 *datalen = (size_t)(cp - data);
614 * doquerylist - send a message including variables in a list
618 struct varlist *vlist,
627 char data[CTL_MAX_DATA_LEN];
630 datalen = sizeof(data);
631 makequerydata(vlist, &datalen, data);
633 return doquery(op, associd, auth, datalen, data, rstatus, dsize,
639 * doprintvlist - print the variables on a list
643 struct varlist *vlist,
649 if (NULL == vlist->name) {
650 xprintf(fp, "No variables on list\n");
653 for (n = 0; n < MAXLIST && vlist[n].name != NULL; n++) {
654 if (NULL == vlist[n].value)
655 xprintf(fp, "%s\n", vlist[n].name);
657 xprintf(fp, "%s=%s\n", vlist[n].name,
663 * addvars - add variables to the variable list
672 doaddvlist(g_varlist, pcmd->argval[0].string);
677 * rmvars - remove variables from the variable list
686 dormvlist(g_varlist, pcmd->argval[0].string);
691 * clearvars - clear the variable list
700 doclearvlist(g_varlist);
705 * showvars - show variables on the variable list
714 doprintvlist(g_varlist, fp);
719 * dolist - send a request with the given list of variables
723 struct varlist *vlist,
737 * if we're asking for specific variables don't include the
738 * status header line in the output.
743 quiet = (vlist->name != NULL);
745 res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
751 xprintf(fp, "server=%s ", currenthost);
754 xprintf(fp, "No system%s variables returned\n",
755 (type == TYPE_CLOCK) ? " clock" : "");
758 "No information returned for%s association %u\n",
759 (type == TYPE_CLOCK) ? " clock" : "",
765 xprintf(fp, "associd=%u ", associd);
766 printvars(dsize, datap, (int)rstatus, type, quiet, fp);
772 * readlist - send a read variables request with the variables on the list
783 if (pcmd->nargs == 0) {
786 /* HMS: I think we want the u_int32 target here, not the u_long */
787 if (pcmd->argval[0].uval == 0)
789 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
793 type = (0 == associd)
796 dolist(g_varlist, associd, CTL_OP_READVAR, type, fp);
801 * writelist - send a write variables request with the variables on the list
815 if (pcmd->nargs == 0) {
818 /* HMS: Do we really want uval here? */
819 if (pcmd->argval[0].uval == 0)
821 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
825 res = doquerylist(g_varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
832 (void) xprintf(fp, "server=%s ", currenthost);
834 (void) xprintf(fp, "done! (no data returned)\n");
836 (void) xprintf(fp,"associd=%u ", associd);
837 printvars(dsize, datap, (int)rstatus,
838 (associd != 0) ? TYPE_PEER : TYPE_SYS, 0, fp);
845 * readvar - send a read variables request with the specified variables
857 struct varlist tmplist[MAXLIST];
861 if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
863 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
867 if (pcmd->nargs > 1) {
868 tmpcount = pcmd->nargs - 1;
869 for (u = 0; u < tmpcount; u++)
870 doaddvlist(tmplist, pcmd->argval[1 + u].string);
873 type = (0 == associd)
876 dolist(tmplist, associd, CTL_OP_READVAR, type, fp);
878 doclearvlist(tmplist);
883 * writevar - send a write variables request with the specified variables
897 struct varlist tmplist[MAXLIST];
900 if (pcmd->argval[0].uval == 0)
902 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
906 doaddvlist(tmplist, pcmd->argval[1].string);
908 res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
911 doclearvlist(tmplist);
917 xprintf(fp, "server=%s ", currenthost);
919 xprintf(fp, "done! (no data returned)\n");
921 xprintf(fp,"associd=%u ", associd);
922 type = (0 == associd)
925 printvars(dsize, datap, (int)rstatus, type, 0, fp);
932 * clocklist - send a clock variables request with the variables on the list
943 if (pcmd->nargs == 0) {
946 if (pcmd->argval[0].uval == 0)
948 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
952 dolist(g_varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
957 * clockvar - send a clock variables request with the specified variables
966 struct varlist tmplist[MAXLIST];
969 if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
971 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
975 if (pcmd->nargs >= 2)
976 doaddvlist(tmplist, pcmd->argval[1].string);
978 dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
980 doclearvlist(tmplist);
985 * findassidrange - verify a range of association ID's
997 int ind[COUNTOF(assids)];
1005 assids[0] = checkassocid(assid1);
1008 assids[1] = checkassocid(assid2);
1012 for (a = 0; a < COUNTOF(assids); a++) {
1014 for (i = 0; i < numassoc; i++)
1015 if (assoc_cache[i].assid == assids[a])
1018 for (a = 0; a < COUNTOF(assids); a++)
1021 "***Association ID %u not found in list\n",
1026 if (ind[0] < ind[1]) {
1039 * mreadlist - send a read variables request for multiple associations
1051 if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1055 for (i = from; i <= to; i++) {
1058 if (!dolist(g_varlist, assoc_cache[i].assid,
1059 CTL_OP_READVAR, TYPE_PEER, fp))
1067 * mreadvar - send a read variables request for multiple associations
1078 struct varlist tmplist[MAXLIST];
1079 struct varlist *pvars;
1081 if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1086 if (pcmd->nargs >= 3) {
1087 doaddvlist(tmplist, pcmd->argval[2].string);
1093 for (i = from; i <= to; i++) {
1094 if (!dolist(pvars, assoc_cache[i].assid, CTL_OP_READVAR,
1099 if (pvars == tmplist)
1100 doclearvlist(tmplist);
1107 * dogetassoc - query the host for its list of associations
1120 res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
1128 xprintf(fp, "server=%s ", currenthost);
1129 xprintf(fp, "No association ID's returned\n");
1135 xprintf(stderr, "server=%s ", currenthost);
1137 "***Server returned %zu octets, should be multiple of 4\n",
1145 if (numassoc >= assoc_cache_slots) {
1148 pus = (const void *)datap;
1149 assoc_cache[numassoc].assid = ntohs(*pus);
1150 datap += sizeof(*pus);
1151 pus = (const void *)datap;
1152 assoc_cache[numassoc].status = ntohs(*pus);
1153 datap += sizeof(*pus);
1154 dsize -= 2 * sizeof(*pus);
1156 xprintf(stderr, "[%u] ",
1157 assoc_cache[numassoc].assid);
1162 xprintf(stderr, "\n%d associations total\n", numassoc);
1170 * printassoc - print the current list of associations
1186 const char *condition = "";
1187 const char *last_event;
1190 if (numassoc == 0) {
1191 (void) xprintf(fp, "No association ID's in list\n");
1199 "ind assid status conf reach auth condition last_event cnt\n");
1201 "===========================================================\n");
1202 for (i = 0; i < numassoc; i++) {
1203 statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status);
1204 if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
1206 event = CTL_PEER_EVENT(assoc_cache[i].status);
1207 event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
1208 if (statval & CTL_PST_CONFIG)
1212 if (statval & CTL_PST_BCAST) {
1214 if (statval & CTL_PST_AUTHENABLE)
1219 if (statval & CTL_PST_REACH)
1223 if (statval & CTL_PST_AUTHENABLE) {
1224 if (statval & CTL_PST_AUTHENTIC)
1232 if (pktversion > NTP_OLDVERSION) {
1233 switch (statval & 0x7) {
1235 case CTL_PST_SEL_REJECT:
1236 condition = "reject";
1239 case CTL_PST_SEL_SANE:
1240 condition = "falsetick";
1243 case CTL_PST_SEL_CORRECT:
1244 condition = "excess";
1247 case CTL_PST_SEL_SELCAND:
1248 condition = "outlier";
1251 case CTL_PST_SEL_SYNCCAND:
1252 condition = "candidate";
1255 case CTL_PST_SEL_EXCESS:
1256 condition = "backup";
1259 case CTL_PST_SEL_SYSPEER:
1260 condition = "sys.peer";
1263 case CTL_PST_SEL_PPS:
1264 condition = "pps.peer";
1268 switch (statval & 0x3) {
1270 case OLD_CTL_PST_SEL_REJECT:
1271 if (!(statval & OLD_CTL_PST_SANE))
1272 condition = "insane";
1273 else if (!(statval & OLD_CTL_PST_DISP))
1274 condition = "hi_disp";
1279 case OLD_CTL_PST_SEL_SELCAND:
1280 condition = "sel_cand";
1283 case OLD_CTL_PST_SEL_SYNCCAND:
1284 condition = "sync_cand";
1287 case OLD_CTL_PST_SEL_SYSPEER:
1288 condition = "sys_peer";
1292 switch (PEER_EVENT|event) {
1295 last_event = "mobilize";
1299 last_event = "demobilize";
1303 last_event = "reachable";
1307 last_event = "unreachable";
1311 last_event = "restart";
1315 last_event = "no_reply";
1319 last_event = "rate_exceeded";
1323 last_event = "access_denied";
1327 last_event = "leap_armed";
1331 last_event = "sys_peer";
1335 last_event = "clock_alarm";
1342 snprintf(buf, sizeof(buf),
1343 "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2lu",
1344 i + 1, assoc_cache[i].assid,
1345 assoc_cache[i].status, conf, reach, auth,
1346 condition, last_event, event_count);
1347 bp = buf + strlen(buf);
1348 while (bp > buf && ' ' == bp[-1])
1351 xprintf(fp, "%s\n", buf);
1357 * associations - get, record and print a list of associations
1372 * lassociations - get, record and print a long list of associations
1387 * passociations - print the association list
1401 * lpassociations - print the long association list
1415 * saveconfig - dump ntp server configuration to server file
1428 if (0 == pcmd->nargs)
1431 res = doquery(CTL_OP_SAVECONFIG, 0, 1,
1432 strlen(pcmd->argval[0].string),
1433 pcmd->argval[0].string, &rstatus, &dsize,
1440 xprintf(fp, "(no response message, curiously)");
1442 xprintf(fp, "%.*s", (int)dsize, datap); /* cast is wobbly */
1448 * radiostatus - print the radio status returned by the server
1462 res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
1469 (void) xprintf(fp, "server=%s ", currenthost);
1471 (void) xprintf(fp, "No radio status string returned\n");
1475 asciize(dsize, datap, fp);
1480 * when - print how long its been since his last packet arrived
1493 else if (reftime->l_ui != 0)
1498 if (ts->l_ui < lasttime->l_ui)
1500 return (ts->l_ui - lasttime->l_ui);
1505 * Pretty-print an interval into the given buffer, in a human-friendly format.
1521 snprintf(buf, cb, "%u", (unsigned int)diff);
1525 diff = (diff + 29) / 60;
1527 snprintf(buf, cb, "%um", (unsigned int)diff);
1531 diff = (diff + 29) / 60;
1533 snprintf(buf, cb, "%uh", (unsigned int)diff);
1537 diff = (diff + 11) / 24;
1539 snprintf(buf, cb, "%ud", (unsigned int)diff);
1543 /* years are only approximated... */
1544 diff = (long)floor(diff / 365.25 + 0.5);
1546 snprintf(buf, cb, "%uy", (unsigned int)diff);
1549 /* Ok, this amounts to infinity... */
1550 strlcpy(buf, "INF", cb);
1564 dummy = SRCADR(sock);
1565 ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' :
1566 ((dummy&0x000000ff)==0x000000ff) ? 'b' :
1567 ((dummy&0xffffffff)==0x7f000001) ? 'l' :
1568 ((dummy&0xffffffe0)==0x00000000) ? '-' :
1572 if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock)))
1585 * A list of variables required by the peers command
1587 struct varlist opeervarlist[] = {
1588 { "srcadr", 0 }, /* 0 */
1589 { "dstadr", 0 }, /* 1 */
1590 { "stratum", 0 }, /* 2 */
1591 { "hpoll", 0 }, /* 3 */
1592 { "ppoll", 0 }, /* 4 */
1593 { "reach", 0 }, /* 5 */
1594 { "delay", 0 }, /* 6 */
1595 { "offset", 0 }, /* 7 */
1596 { "jitter", 0 }, /* 8 */
1597 { "dispersion", 0 }, /* 9 */
1598 { "rec", 0 }, /* 10 */
1599 { "reftime", 0 }, /* 11 */
1600 { "srcport", 0 }, /* 12 */
1601 { "hmode", 0 }, /* 13 */
1605 struct varlist peervarlist[] = {
1606 { "srcadr", 0 }, /* 0 */
1607 { "refid", 0 }, /* 1 */
1608 { "stratum", 0 }, /* 2 */
1609 { "hpoll", 0 }, /* 3 */
1610 { "ppoll", 0 }, /* 4 */
1611 { "reach", 0 }, /* 5 */
1612 { "delay", 0 }, /* 6 */
1613 { "offset", 0 }, /* 7 */
1614 { "jitter", 0 }, /* 8 */
1615 { "dispersion", 0 }, /* 9 */
1616 { "rec", 0 }, /* 10 */
1617 { "reftime", 0 }, /* 11 */
1618 { "srcport", 0 }, /* 12 */
1619 { "hmode", 0 }, /* 13 */
1620 { "srchost", 0 }, /* 14 */
1624 struct varlist apeervarlist[] = {
1625 { "srcadr", 0 }, /* 0 */
1626 { "refid", 0 }, /* 1 */
1627 { "assid", 0 }, /* 2 */
1628 { "stratum", 0 }, /* 3 */
1629 { "hpoll", 0 }, /* 4 */
1630 { "ppoll", 0 }, /* 5 */
1631 { "reach", 0 }, /* 6 */
1632 { "delay", 0 }, /* 7 */
1633 { "offset", 0 }, /* 8 */
1634 { "jitter", 0 }, /* 9 */
1635 { "dispersion", 0 }, /* 10 */
1636 { "rec", 0 }, /* 11 */
1637 { "reftime", 0 }, /* 12 */
1638 { "srcport", 0 }, /* 13 */
1639 { "hmode", 0 }, /* 14 */
1640 { "srchost", 0 }, /* 15 */
1646 * Decode an incoming data buffer and print a line in the peer list
1650 struct varlist *pvl,
1669 sockaddr_u dum_store;
1670 sockaddr_u refidadr;
1674 const char *dstadr_refid = "0.0.0.0";
1675 const char *serverlocal;
1691 char clock_name[LENHOSTNAME];
1692 char whenbuf[12], pollbuf[12];
1693 /* [Bug 3482] formally whenbuf & pollbuf should be able to hold
1694 * a full signed int. Not that we would use that much string
1699 have_srchost = FALSE;
1700 have_dstadr = FALSE;
1701 have_da_rid = FALSE;
1702 have_jitter = FALSE;
1705 clock_name[0] = '\0';
1711 while (nextvar(&datalen, &data, &name, &value)) {
1712 INSIST(name && value);
1713 if (!strcmp("srcadr", name) ||
1714 !strcmp("peeradr", name)) {
1715 if (!decodenetnum(value, &srcadr))
1716 xprintf(stderr, "malformed %s=%s\n",
1718 } else if (!strcmp("srchost", name)) {
1719 if (pvl == peervarlist || pvl == apeervarlist) {
1720 len = strlen(value);
1722 (size_t)len < sizeof(clock_name)) {
1726 memcpy(clock_name, value, len);
1727 clock_name[len] = '\0';
1728 have_srchost = TRUE;
1731 } else if (!strcmp("dstadr", name)) {
1732 if (decodenetnum(value, &dum_store)) {
1733 type = decodeaddrtype(&dum_store);
1736 if (pvl == opeervarlist) {
1738 dstadr_refid = trunc_left(stoa(&dstadr), 15);
1741 } else if (!strcmp("hmode", name)) {
1742 decodeint(value, &hmode);
1743 } else if (!strcmp("refid", name)) {
1744 if ( (pvl == peervarlist)
1745 && (drefid == REFID_IPV4)) {
1747 drlen = strlen(value);
1750 } else if (drlen <= 4) {
1752 memcpy(&u32, value, drlen);
1753 dstadr_refid = refid_str(u32, 1);
1754 } else if (decodenetnum(value, &refidadr)) {
1755 if (SOCK_UNSPEC(&refidadr))
1756 dstadr_refid = "0.0.0.0";
1757 else if (ISREFCLOCKADR(&refidadr))
1759 refnumtoa(&refidadr);
1764 have_da_rid = FALSE;
1766 } else if ( (pvl == apeervarlist)
1767 || (pvl == peervarlist)) {
1768 /* no need to check drefid == REFID_HASH */
1770 drlen = strlen(value);
1773 } else if (drlen <= 4) {
1775 memcpy(&u32, value, drlen);
1776 dstadr_refid = refid_str(u32, 1);
1777 //xprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value);
1778 } else if (decodenetnum(value, &refidadr)) {
1779 if (SOCK_UNSPEC(&refidadr))
1780 dstadr_refid = "0.0.0.0";
1781 else if (ISREFCLOCKADR(&refidadr))
1783 refnumtoa(&refidadr);
1785 char *buf = emalloc(10);
1786 int i = ntohl(refidadr.sa4.sin_addr.s_addr);
1791 //xprintf(stderr, "apeervarlist refid: value=<%x>\n", i);
1793 //xprintf(stderr, "apeervarlist refid: value=<%s>\n", value);
1795 have_da_rid = FALSE;
1798 } else if (!strcmp("stratum", name)) {
1799 decodeuint(value, &stratum);
1800 } else if (!strcmp("hpoll", name)) {
1801 if (decodeint(value, &hpoll) && hpoll < 0)
1802 hpoll = NTP_MINPOLL;
1803 } else if (!strcmp("ppoll", name)) {
1804 if (decodeint(value, &ppoll) && ppoll < 0)
1805 ppoll = NTP_MINPOLL;
1806 } else if (!strcmp("reach", name)) {
1807 decodeuint(value, &reach);
1808 } else if (!strcmp("delay", name)) {
1809 decodetime(value, &estdelay);
1810 } else if (!strcmp("offset", name)) {
1811 decodetime(value, &estoffset);
1812 } else if (!strcmp("jitter", name)) {
1813 if ((pvl == peervarlist || pvl == apeervarlist)
1814 && decodetime(value, &estjitter))
1816 } else if (!strcmp("rootdisp", name) ||
1817 !strcmp("dispersion", name)) {
1818 decodetime(value, &estdisp);
1819 } else if (!strcmp("rec", name)) {
1820 decodets(value, &rec);
1821 } else if (!strcmp("srcport", name) ||
1822 !strcmp("peerport", name)) {
1823 decodeuint(value, &srcport);
1824 } else if (!strcmp("reftime", name)) {
1825 if (!decodets(value, &reftime))
1827 } else if (!strcmp("flash", name)) {
1828 decodeuint(value, &flash);
1830 // xprintf(stderr, "UNRECOGNIZED name=%s ", name);
1835 * hmode gives the best guidance for the t column. If the response
1836 * did not include hmode we'll use the old decodeaddrtype() result.
1841 /* broadcastclient or multicastclient */
1845 case MODE_BROADCAST:
1846 /* broadcast or multicast server */
1847 if (IS_MCAST(&srcadr))
1854 if (ISREFCLOCKADR(&srcadr))
1855 type = 'l'; /* local refclock*/
1856 else if (SOCK_UNSPEC(&srcadr))
1857 type = 'p'; /* pool */
1858 else if (IS_MCAST(&srcadr))
1859 type = 'a'; /* manycastclient */
1861 type = 'u'; /* unicast */
1865 type = 's'; /* symmetric active */
1866 break; /* configured */
1869 type = 'S'; /* symmetric passive */
1870 break; /* ephemeral */
1874 * Got everything, format the line
1876 poll_sec = 1 << min(ppoll, hpoll);
1877 if (pktversion > NTP_OLDVERSION)
1878 c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
1880 c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
1882 if ((pvl == peervarlist || pvl == apeervarlist)
1884 serverlocal = nntohost_col(&dstadr,
1885 (size_t)min(LIB_BUFLENGTH - 1, maxhostlen),
1888 if (currenthostisnum)
1889 serverlocal = trunc_left(currenthost,
1892 serverlocal = currenthost;
1894 xprintf(fp, "%-*s ", (int)maxhostlen, serverlocal);
1896 if (AF_UNSPEC == af || AF(&srcadr) == af) {
1898 strlcpy(clock_name, nntohost(&srcadr),
1899 sizeof(clock_name));
1900 /* wide and long source - space over on next line */
1901 /* allow for host + sp if > 1 and regular tally + source + sp */
1902 if (wideremote && 15 < strlen(clock_name))
1903 xprintf(fp, "%c%s\n%*s", c, clock_name,
1904 ((numhosts > 1) ? (int)maxhostlen + 1 : 0)
1907 xprintf(fp, "%c%-15.15s ", c, clock_name);
1908 if ((flash & TEST12) && (pvl != opeervarlist)) {
1909 drlen = xprintf(fp, "(loop)");
1910 } else if (!have_da_rid) {
1913 drlen = strlen(dstadr_refid);
1914 makeascii(drlen, dstadr_refid, fp);
1916 if (pvl == apeervarlist) {
1919 xprintf(fp, "%-6d", associd);
1921 while (drlen++ < 15)
1925 " %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n",
1927 prettyinterval(whenbuf, sizeof(whenbuf),
1928 when(&ts, &rec, &reftime)),
1929 prettyinterval(pollbuf, sizeof(pollbuf),
1931 reach, ulfptoms(&estdelay, 3),
1932 lfptoms(&estoffset, 3),
1934 ? ulfptoms(&estjitter, 3)
1935 : ulfptoms(&estdisp, 3));
1944 * dogetpeers - given an association ID, read and print the spreadsheet
1949 struct varlist *pvl,
1961 res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
1967 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
1976 xprintf(stderr, "server=%s ", currenthost);
1978 "***No information returned for association %u\n",
1983 return doprintpeers(pvl, associd, (int)rstatus, dsize, datap,
1989 * peers - print a peer spreadsheet
1999 char fullname[LENHOSTNAME];
2001 const char * name_or_num;
2004 if (!dogetassoc(fp))
2007 for (u = 0; u < numhosts; u++) {
2008 if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
2009 name_or_num = nntohost(&netnum);
2010 sl = strlen(name_or_num);
2011 maxhostlen = max(maxhostlen, sl);
2015 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2018 " remote refid st t when poll reach delay offset jitter\n");
2020 for (u = 0; u <= maxhostlen; u++)
2023 "==============================================================================\n");
2025 for (u = 0; u < numassoc; u++) {
2027 !(CTL_PEER_STATVAL(assoc_cache[u].status)
2028 & (CTL_PST_CONFIG|CTL_PST_REACH))) {
2030 xprintf(stderr, "eliding [%d]\n",
2031 (int)assoc_cache[u].assid);
2034 if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid,
2043 * doapeers - print a peer spreadsheet with assocIDs
2053 char fullname[LENHOSTNAME];
2055 const char * name_or_num;
2058 if (!dogetassoc(fp))
2061 for (u = 0; u < numhosts; u++) {
2062 if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
2063 name_or_num = nntohost(&netnum);
2064 sl = strlen(name_or_num);
2065 maxhostlen = max(maxhostlen, sl);
2069 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2072 " remote refid assid st t when poll reach delay offset jitter\n");
2074 for (u = 0; u <= maxhostlen; u++)
2077 "==============================================================================\n");
2079 for (u = 0; u < numassoc; u++) {
2081 !(CTL_PEER_STATVAL(assoc_cache[u].status)
2082 & (CTL_PST_CONFIG|CTL_PST_REACH))) {
2084 xprintf(stderr, "eliding [%d]\n",
2085 (int)assoc_cache[u].assid);
2088 if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid,
2097 * peers - print a peer spreadsheet
2106 if (drefid == REFID_HASH) {
2111 if (pcmd->nargs == 1) {
2112 if (pcmd->argval->ival == 6)
2123 * apeers - print a peer spreadsheet, with assocIDs
2134 if (pcmd->nargs == 1) {
2135 if (pcmd->argval->ival == 6)
2140 doapeers(0, fp, af);
2145 * lpeers - print a peer spreadsheet including all fuzzball peers
2156 if (pcmd->nargs == 1) {
2157 if (pcmd->argval->ival == 6)
2167 * opeers - print a peer spreadsheet
2177 char fullname[LENHOSTNAME];
2180 if (!dogetassoc(fp))
2183 for (i = 0; i < numhosts; ++i) {
2184 if (getnetnum(chosts[i].name, &netnum, fullname, af))
2185 if (strlen(fullname) > maxhostlen)
2186 maxhostlen = strlen(fullname);
2189 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2192 " remote local st t when poll reach delay offset disp\n");
2194 for (i = 0; i <= maxhostlen; ++i)
2197 "==============================================================================\n");
2199 for (i = 0; i < numassoc; i++) {
2201 !(CTL_PEER_STATVAL(assoc_cache[i].status) &
2202 (CTL_PST_CONFIG | CTL_PST_REACH)))
2204 if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af))
2212 * opeers - print a peer spreadsheet the old way
2223 if (pcmd->nargs == 1) {
2224 if (pcmd->argval->ival == 6)
2229 doopeers(0, fp, af);
2234 * lopeers - print a peer spreadsheet including all fuzzball peers
2245 if (pcmd->nargs == 1) {
2246 if (pcmd->argval->ival == 6)
2251 doopeers(1, fp, af);
2256 * config - send a configuration command to a remote host
2273 cfgcmd = pcmd->argval[0].string;
2279 "Command = %s\n", pcmd->keyword, cfgcmd);
2281 res = doquery(CTL_OP_CONFIGURE, 0, 1,
2282 strlen(cfgcmd), cfgcmd,
2283 &rstatus, &rsize, &rdata);
2288 if (rsize > 0 && '\n' == rdata[rsize - 1])
2291 resp = emalloc(rsize + 1);
2292 memcpy(resp, rdata, rsize);
2296 if (1 == sscanf(resp, "column %d syntax error", &col)
2297 && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) {
2299 xputs(" *", stdout); /* "ntpq> :config " */
2301 printf("%s\n", cfgcmd);
2302 for (i = 0; i < col; i++)
2304 xputs("^\n", stdout);
2306 printf("%s\n", resp);
2312 * config_from_file - remotely configure an ntpd daemon using the
2313 * specified configuration file
2314 * SK: This function is a kludge at best and is full of bad design
2316 * 1. ntpq uses UDP, which means that there is no guarantee of in-order,
2317 * error-free delivery.
2318 * 2. The maximum length of a packet is constrained, and as a result, the
2319 * maximum length of a line in a configuration file is constrained.
2320 * Longer lines will lead to unpredictable results.
2321 * 3. Since this function is sending a line at a time, we can't update
2322 * the control key through the configuration file (YUCK!!)
2324 * Pearly: There are a few places where 'size_t' is cast to 'int' based
2325 * on the assumption that 'int' can hold the size of the involved
2326 * buffers without overflow.
2340 char config_cmd[MAXLINE];
2349 "Filename = %s\n", pcmd->keyword,
2350 pcmd->argval[0].string);
2352 config_fd = fopen(pcmd->argval[0].string, "r");
2353 if (NULL == config_fd) {
2354 printf("ERROR!! Couldn't open file: %s\n",
2355 pcmd->argval[0].string);
2359 printf("Sending configuration file, one line at a time.\n");
2361 while (fgets(config_cmd, MAXLINE, config_fd) != NULL) {
2362 /* Eliminate comments first. */
2363 cp = strchr(config_cmd, '#');
2364 config_len = (NULL != cp)
2365 ? (size_t)(cp - config_cmd)
2366 : strlen(config_cmd);
2368 /* [Bug 3015] make sure there's no trailing whitespace;
2369 * the fix for [Bug 2853] on the server side forbids
2370 * those. And don't transmit empty lines, as this would
2373 while (config_len != 0 &&
2374 (u_char)config_cmd[config_len-1] <= ' ')
2376 config_cmd[config_len] = '\0';
2379 if (0 == config_len)
2384 res = doquery(CTL_OP_CONFIGURE, 0, 1,
2385 config_len, config_cmd,
2386 &rstatus, &rsize, &rdata);
2387 while (res != 0 && retry_limit--);
2389 printf("Line No: %d query failed: %.*s\n"
2390 "Subsequent lines not sent.\n",
2391 i, (int)config_len, config_cmd);
2396 /* Right-strip the result code string, then output the
2397 * last line executed, with result code. */
2398 while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ')
2400 printf("Line No: %d %.*s: %.*s\n", i,
2402 (int)config_len, config_cmd);
2404 printf("Done sending file\n");
2415 const char nonce_eq[] = "nonce=";
2423 * Retrieve a nonce specific to this client to demonstrate to
2424 * ntpd that we're capable of receiving responses to our source
2425 * IP address, and thereby unlikely to be forging the source.
2427 qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus,
2430 xprintf(stderr, "nonce request failed\n");
2434 if ((size_t)rsize <= sizeof(nonce_eq) - 1 ||
2435 strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) {
2436 xprintf(stderr, "unexpected nonce response format: %.*s\n",
2437 (int)rsize, rdata); /* cast is wobbly */
2440 chars = rsize - (sizeof(nonce_eq) - 1);
2441 if (chars >= cb_nonce)
2443 memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars);
2444 nonce[chars] = '\0';
2446 ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) {
2448 nonce[chars] = '\0';
2456 * add_mru Add and entry to mru list, hash table, and allocate
2457 * and return a replacement.
2458 * This is a helper for collect_mru_list().
2470 hash = NTP_HASH_ADDR(&add->addr);
2471 /* see if we have it among previously received entries */
2472 for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink)
2473 if (SOCK_EQ(&mon->addr, &add->addr))
2476 if (!L_ISGEQ(&add->first, &mon->first)) {
2478 "add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n",
2479 sptoa(&add->addr), add->last.l_ui,
2480 add->last.l_uf, mon->last.l_ui,
2484 UNLINK_DLIST(mon, mlink);
2485 UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru);
2486 INSIST(unlinked == mon);
2488 TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui,
2491 LINK_DLIST(mru_list, add, mlink);
2492 LINK_SLIST(hash_table[hash], add, hlink);
2493 TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
2494 add->last.l_ui, add->last.l_uf, add->count,
2495 (int)add->mode, (int)add->ver, (u_int)add->rs,
2496 add->first.l_ui, add->first.l_uf, sptoa(&add->addr)));
2497 /* if we didn't update an existing entry, alloc replacement */
2499 mon = emalloc(sizeof(*mon));
2508 /* MGOT macro is specific to collect_mru_list() */
2512 if (MRU_GOT_ALL == got) { \
2514 mon = add_mru(mon); \
2521 mrulist_ctrl_c_hook(void)
2523 mrulist_interrupted = TRUE;
2534 const u_int sleep_msecs = 5;
2535 static int ntpd_row_limit = MRU_ROW_LIMIT;
2536 int c_mru_l_rc; /* this function's return code */
2537 u_char got; /* MRU_GOT_* bits */
2546 char req_buf[CTL_MAX_DATA_LEN];
2559 int si; /* server index in response */
2560 int ci; /* client (our) index for validation */
2561 int ri; /* request index (.# suffix) */
2565 sockaddr_u addr_older;
2567 int have_addr_older;
2568 int have_last_older;
2569 u_int restarted_count;
2574 if (!fetch_nonce(nonce, sizeof(nonce)))
2578 restarted_count = 0;
2580 INIT_DLIST(mru_list, mlink);
2581 cb = NTP_HASH_SIZE * sizeof(*hash_table);
2582 INSIST(NULL == hash_table);
2583 hash_table = emalloc_zero(cb);
2586 list_complete = FALSE;
2592 mon = emalloc_zero(cb);
2595 next_report = time(NULL) + MRU_REPORT_SECS;
2597 limit = min(3 * MAXFRAGS, ntpd_row_limit);
2599 snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s",
2600 nonce, frags, parms);
2605 xprintf(stderr, "READ_MRU parms: %s\n", req_buf);
2607 qres = doqueryex(CTL_OP_READ_MRU, 0, 0,
2608 strlen(req_buf), req_buf,
2609 &rstatus, &rsize, &rdata, TRUE);
2611 if (CERR_UNKNOWNVAR == qres && ri > 0) {
2613 * None of the supplied prior entries match, so
2614 * toss them from our list and try again.
2618 "no overlap between %d prior entries and server MRU list\n",
2621 recent = HEAD_DLIST(mru_list, mlink);
2622 INSIST(recent != NULL);
2625 "tossing prior entry %s to resync\n",
2626 sptoa(&recent->addr));
2627 UNLINK_DLIST(recent, mlink);
2628 hash = NTP_HASH_ADDR(&recent->addr);
2629 UNLINK_SLIST(unlinked, hash_table[hash],
2630 recent, hlink, mru);
2631 INSIST(unlinked == recent);
2635 if (NULL == HEAD_DLIST(mru_list, mlink)) {
2637 if (restarted_count > 8) {
2639 "Giving up after 8 restarts from the beginning.\n"
2640 "With high-traffic NTP servers, this can occur if the\n"
2641 "MRU list is limited to less than about 16 seconds' of\n"
2642 "entries. See the 'mru' ntp.conf directive to adjust.\n");
2643 goto cleanup_return;
2647 "---> Restarting from the beginning, retry #%u\n",
2650 } else if (CERR_UNKNOWNVAR == qres) {
2652 "CERR_UNKNOWNVAR from ntpd but no priors given.\n");
2653 goto cleanup_return;
2654 } else if (CERR_BADVALUE == qres) {
2659 "Reverted to row limit from fragments limit.\n");
2661 /* ntpd has lower cap on row limit */
2663 limit = min(limit, ntpd_row_limit);
2666 "Row limit reduced to %d following CERR_BADVALUE.\n",
2669 } else if (ERR_INCOMPLETE == qres ||
2670 ERR_TIMEOUT == qres) {
2672 * Reduce the number of rows/frags requested by
2673 * half to recover from lost response fragments.
2676 frags = max(2, frags / 2);
2679 "Frag limit reduced to %d following incomplete response.\n",
2682 limit = max(2, limit / 2);
2685 "Row limit reduced to %d following incomplete response.\n",
2689 show_error_msg(qres, 0);
2690 goto cleanup_return;
2693 * This is a cheap cop-out implementation of rawmode
2694 * output for mrulist. A better approach would be to
2695 * dump similar output after the list is collected by
2696 * ntpq with a continuous sequence of indexes. This
2697 * cheap approach has indexes resetting to zero for
2698 * each query/response, and duplicates are not
2701 if (!qres && rawmode)
2702 printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout);
2704 have_addr_older = FALSE;
2705 have_last_older = FALSE;
2706 while (!qres && nextvar(&rsize, &rdata, &tag, &val)) {
2709 xprintf(stderr, "nextvar gave: %s = %s\n",
2714 if (!strcmp(tag, "addr.older")) {
2715 if (!have_last_older) {
2717 "addr.older %s before last.older\n",
2719 goto cleanup_return;
2721 if (!decodenetnum(val, &addr_older)) {
2723 "addr.older %s garbled\n",
2725 goto cleanup_return;
2727 hash = NTP_HASH_ADDR(&addr_older);
2728 for (recent = hash_table[hash];
2730 recent = recent->hlink)
2735 if (NULL == recent) {
2737 "addr.older %s not in hash table\n",
2739 goto cleanup_return;
2741 if (!L_ISEQU(&last_older,
2744 "last.older %08x.%08x mismatches %08x.%08x expected.\n",
2749 goto cleanup_return;
2751 have_addr_older = TRUE;
2752 } else if (1 != sscanf(tag, "addr.%d", &si)
2755 else if (decodenetnum(val, &mon->addr))
2760 if (!strcmp(tag, "last.older")) {
2761 if ('0' != val[0] ||
2763 !hextolfp(val + 2, &last_older)) {
2765 "last.older %s garbled\n",
2767 goto cleanup_return;
2769 have_last_older = TRUE;
2770 } else if (!strcmp(tag, "last.newest")) {
2773 "last.newest %s before complete row, got = 0x%x\n",
2775 goto cleanup_return;
2779 "last.newest %s before now=\n",
2781 goto cleanup_return;
2783 head = HEAD_DLIST(mru_list, mlink);
2785 if ('0' != val[0] ||
2787 !hextolfp(val + 2, &newest) ||
2791 "last.newest %s mismatches %08x.%08x",
2795 goto cleanup_return;
2798 list_complete = TRUE;
2799 } else if (1 != sscanf(tag, "last.%d", &si) ||
2800 si != ci || '0' != val[0] ||
2802 !hextolfp(val + 2, &mon->last)) {
2807 * allow interrupted retrieval,
2808 * using most recent retrieved
2809 * entry's last seen timestamp
2810 * as the end of operation.
2817 if (1 != sscanf(tag, "first.%d", &si) ||
2818 si != ci || '0' != val[0] ||
2820 !hextolfp(val + 2, &mon->first))
2822 MGOT(MRU_GOT_FIRST);
2826 if (!strcmp(tag, "nonce")) {
2827 strlcpy(nonce, val, sizeof(nonce));
2830 } else if (strcmp(tag, "now") ||
2833 !hextolfp(val + 2, pnow))
2839 if (1 != sscanf(tag, "ct.%d", &si) ||
2841 1 != sscanf(val, "%d", &mon->count)
2844 MGOT(MRU_GOT_COUNT);
2848 if (1 != sscanf(tag, "mv.%d", &si) ||
2850 1 != sscanf(val, "%d", &mv))
2852 mon->mode = PKT_MODE(mv);
2853 mon->ver = PKT_VERSION(mv);
2858 if (1 != sscanf(tag, "rs.%d", &si) ||
2860 1 != sscanf(val, "0x%hx", &mon->rs))
2868 /* ignore unknown tags */
2872 list_complete = TRUE;
2873 if (list_complete) {
2874 INSIST(0 == ri || have_addr_older);
2876 if (mrulist_interrupted) {
2877 printf("mrulist retrieval interrupted by operator.\n"
2878 "Displaying partial client list.\n");
2881 if (list_complete || mrulist_interrupted) {
2883 "\rRetrieved %u unique MRU entries and %u updates.\n",
2884 mru_count, mru_dupes);
2888 if (time(NULL) >= next_report) {
2889 next_report += MRU_REPORT_SECS;
2890 xprintf(stderr, "\r%u (%u updates) ", mru_count,
2896 * Snooze for a bit between queries to let ntpd catch
2897 * up with other duties.
2901 #elif !defined(HAVE_NANOSLEEP)
2902 sleep((sleep_msecs / 1000) + 1);
2905 struct timespec interv = { 0,
2906 1000 * sleep_msecs };
2907 nanosleep(&interv, NULL);
2911 * If there were no errors, increase the number of rows
2912 * to a maximum of 3 * MAXFRAGS (the most packets ntpq
2913 * can handle in one response), on the assumption that
2914 * no less than 3 rows fit in each packet, capped at
2915 * our best guess at the server's row limit.
2919 frags = min(MAXFRAGS, frags + 1);
2921 limit = min3(3 * MAXFRAGS,
2928 * prepare next query with as many address and last-seen
2929 * timestamps as will fit in a single packet.
2932 req_end = req_buf + sizeof(req_buf);
2933 #define REQ_ROOM (req_end - req)
2934 snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce,
2944 if (nonce_uses >= 4) {
2945 if (!fetch_nonce(nonce, sizeof(nonce)))
2946 goto cleanup_return;
2951 for (ri = 0, recent = HEAD_DLIST(mru_list, mlink);
2953 ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) {
2955 snprintf(buf, sizeof(buf),
2956 ", addr.%d=%s, last.%d=0x%08x.%08x",
2957 ri, sptoa(&recent->addr), ri,
2958 recent->last.l_ui, recent->last.l_uf);
2959 chars = strlen(buf);
2960 if ((size_t)REQ_ROOM <= chars)
2962 memcpy(req, buf, chars + 1);
2968 goto retain_hash_table;
2983 * qcmp_mru_addr - sort MRU entries by remote address.
2985 * All IPv4 addresses sort before any IPv6, addresses are sorted by
2986 * value within address family.
2994 const mru * const * ppm1 = v1;
2995 const mru * const * ppm2 = v2;
3006 af1 = AF(&pm1->addr);
3007 af2 = AF(&pm2->addr);
3010 return (AF_INET == af1)
3014 cmplen = SIZEOF_INADDR(af1);
3015 addr_off = (AF_INET == af1)
3016 ? offsetof(struct sockaddr_in, sin_addr)
3017 : offsetof(struct sockaddr_in6, sin6_addr);
3019 return memcmp((const char *)&pm1->addr + addr_off,
3020 (const char *)&pm2->addr + addr_off,
3031 return -qcmp_mru_addr(v1, v2);
3036 * qcmp_mru_count - sort MRU entries by times seen (hit count).
3044 const mru * const * ppm1 = v1;
3045 const mru * const * ppm2 = v2;
3052 return (pm1->count < pm2->count)
3054 : ((pm1->count == pm2->count)
3066 return -qcmp_mru_count(v1, v2);
3071 * qcmp_mru_avgint - sort MRU entries by average interval.
3079 const mru * const * ppm1 = v1;
3080 const mru * const * ppm2 = v2;
3090 interval = pm1->last;
3091 L_SUB(&interval, &pm1->first);
3092 LFPTOD(&interval, avg1);
3095 interval = pm2->last;
3096 L_SUB(&interval, &pm2->first);
3097 LFPTOD(&interval, avg2);
3102 else if (avg1 > avg2)
3105 /* secondary sort on lstint - rarely tested */
3106 if (L_ISEQU(&pm1->last, &pm2->last))
3108 else if (L_ISGEQ(&pm1->last, &pm2->last))
3121 return -qcmp_mru_avgint(v1, v2);
3126 * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most
3127 * Recently Used (seen) remote address list from ntpd.
3129 * Similar to ntpdc's monlist command, but not limited to a single
3130 * request/response, and thereby not limited to a few hundred remote
3133 * See ntpd/ntp_control.c read_mru_list() for comments on the way
3134 * CTL_OP_READ_MRU is designed to be used.
3136 * mrulist intentionally differs from monlist in the way the avgint
3137 * column is calculated. monlist includes the time after the last
3138 * packet from the client until the monlist query time in the average,
3139 * while mrulist excludes it. That is, monlist's average interval grows
3140 * over time for remote addresses not heard from in some time, while it
3141 * remains unchanged in mrulist. This also affects the avgint value for
3142 * entries representing a single packet, with identical first and last
3143 * timestamps. mrulist shows 0 avgint, monlist shows a value identical
3148 struct parse * pcmd,
3152 const char mincount_eq[] = "mincount=";
3153 const char resall_eq[] = "resall=";
3154 const char resany_eq[] = "resany=";
3155 const char maxlstint_eq[] = "maxlstint=";
3156 const char laddr_eq[] = "laddr=";
3157 const char sort_eq[] = "sort=";
3158 mru_sort_order order;
3160 char parms_buf[128];
3176 mrulist_interrupted = FALSE;
3177 push_ctrl_c_handler(&mrulist_ctrl_c_hook);
3179 "Ctrl-C will stop MRU retrieval and display partial results.\n");
3182 order = MRUSORT_DEF;
3183 parms_buf[0] = '\0';
3185 for (i = 0; i < pcmd->nargs; i++) {
3186 arg = pcmd->argval[i].string;
3188 cb = strlen(arg) + 1;
3189 if ((!strncmp(resall_eq, arg, sizeof(resall_eq)
3190 - 1) || !strncmp(resany_eq, arg,
3191 sizeof(resany_eq) - 1) || !strncmp(
3192 mincount_eq, arg, sizeof(mincount_eq) - 1)
3193 || !strncmp(laddr_eq, arg, sizeof(laddr_eq)
3194 - 1) || !strncmp(maxlstint_eq, arg,
3195 sizeof(laddr_eq) - 1)) && parms + cb + 2 <=
3196 parms_buf + sizeof(parms_buf)) {
3197 /* these are passed intact to ntpd */
3198 memcpy(parms, ", ", 2);
3200 memcpy(parms, arg, cb);
3202 } else if (!strncmp(sort_eq, arg,
3203 sizeof(sort_eq) - 1)) {
3204 arg += sizeof(sort_eq) - 1;
3206 n < COUNTOF(mru_sort_keywords);
3208 if (!strcmp(mru_sort_keywords[n],
3211 if (n < COUNTOF(mru_sort_keywords))
3213 } else if (!strcmp("limited", arg) ||
3214 !strcmp("kod", arg)) {
3215 /* transform to resany=... */
3216 snprintf(buf, sizeof(buf),
3221 cb = 1 + strlen(buf);
3223 parms_buf + sizeof(parms_buf)) {
3224 memcpy(parms, buf, cb);
3229 "ignoring unrecognized mrulist parameter: %s\n",
3235 if (!collect_mru_list(parms, &now))
3238 /* display the results */
3240 goto cleanup_return;
3242 /* construct an array of entry pointers in default order */
3243 sorted = eallocarray(mru_count, sizeof(*sorted));
3245 if (MRUSORT_R_DEF != order) {
3246 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3247 INSIST(ppentry < sorted + mru_count);
3252 REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3253 INSIST(ppentry < sorted + mru_count);
3256 REV_ITER_DLIST_END()
3259 if (ppentry - sorted != (int)mru_count) {
3261 "mru_count %u should match MRU list depth %ld.\n",
3262 mru_count, (long)(ppentry - sorted));
3264 goto cleanup_return;
3267 /* re-sort sorted[] if not default or reverse default */
3268 if (MRUSORT_R_DEF < order)
3269 qsort(sorted, mru_count, sizeof(sorted[0]),
3270 mru_qcmp_table[order]);
3272 mrulist_interrupted = FALSE;
3273 printf( "lstint avgint rstr r m v count rport remote address\n"
3274 "==============================================================================\n");
3276 for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) {
3279 L_SUB(&interval, &recent->last);
3280 LFPTOD(&interval, flstint);
3281 lstint = (int)(flstint + 0.5);
3282 interval = recent->last;
3283 L_SUB(&interval, &recent->first);
3284 LFPTOD(&interval, favgint);
3285 favgint /= recent->count;
3286 avgint = (int)(favgint + 0.5);
3287 xprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n",
3288 lstint, avgint, recent->rs,
3289 (RES_KOD & recent->rs)
3291 : (RES_LIMITED & recent->rs)
3294 (int)recent->mode, (int)recent->ver,
3295 recent->count, SRCPORT(&recent->addr),
3296 nntohost(&recent->addr));
3299 if (mrulist_interrupted) {
3300 xputs("\n --interrupted--\n", fp);
3308 "--- completed, freeing sorted[] pointers\n");
3315 xprintf(stderr, "... freeing MRU entries\n");
3318 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3322 xprintf(stderr, "... freeing hash_table[]\n");
3327 INIT_DLIST(mru_list, mlink);
3329 pop_ctrl_c_handler(&mrulist_ctrl_c_hook);
3334 * validate_ifnum - helper for ifstats()
3336 * Ensures rows are received in order and complete.
3346 if (prow->ifnum == ifnum)
3348 if (prow->ifnum + 1 <= ifnum) {
3349 if (*pfields < IFSTATS_FIELDS)
3350 xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n",
3351 *pfields, IFSTATS_FIELDS);
3353 prow->ifnum = ifnum;
3357 "received if index %u, have %d of %d fields for index %u, aborting.\n",
3358 ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum);
3364 * another_ifstats_field - helper for ifstats()
3366 * If all fields for the row have been received, print it.
3369 another_ifstats_field(
3378 /* we understand 12 tags */
3379 if (IFSTATS_FIELDS > *pfields)
3382 " interface name send\n"
3383 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
3384 "==============================================================================\n");
3387 "%3u %-24.24s %c %4x %3u %2u %6u %6u %6u %5u %8d\n"
3389 prow->ifnum, prow->name,
3393 prow->flags, prow->ttl, prow->mcast_count,
3394 prow->received, prow->sent, prow->send_errors,
3395 prow->peer_count, prow->uptime, sptoa(&prow->addr));
3396 if (!SOCK_UNSPEC(&prow->bcast))
3397 xprintf(fp, " %s\n", sptoa(&prow->bcast));
3398 ifnum = prow->ifnum;
3400 prow->ifnum = ifnum;
3405 * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats.
3409 struct parse * pcmd,
3413 const char addr_fmt[] = "addr.%u";
3414 const char bcast_fmt[] = "bcast.%u";
3415 const char en_fmt[] = "en.%u"; /* enabled */
3416 const char flags_fmt[] = "flags.%u";
3417 const char mc_fmt[] = "mc.%u"; /* mcast count */
3418 const char name_fmt[] = "name.%u";
3419 const char pc_fmt[] = "pc.%u"; /* peer count */
3420 const char rx_fmt[] = "rx.%u";
3421 const char tl_fmt[] = "tl.%u"; /* ttl */
3422 const char tx_fmt[] = "tx.%u";
3423 const char txerr_fmt[] = "txerr.%u";
3424 const char up_fmt[] = "up.%u"; /* uptime */
3437 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus,
3439 if (qres) /* message already displayed */
3443 " interface name send\n"
3444 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
3445 "==============================================================================\n");
3451 while (nextvar(&dsize, &datap, &tag, &val)) {
3454 xprintf(stderr, "nextvar gave: %s = %s\n", tag, val);
3459 if (1 == sscanf(tag, addr_fmt, &ui) &&
3460 decodenetnum(val, &row.addr))
3465 if (1 == sscanf(tag, bcast_fmt, &ui) &&
3467 decodenetnum(val, &row.bcast)))
3472 if (1 == sscanf(tag, en_fmt, &ui) &&
3473 1 == sscanf(val, "%d", &row.enabled))
3478 if (1 == sscanf(tag, flags_fmt, &ui) &&
3479 1 == sscanf(val, "0x%x", &row.flags))
3484 if (1 == sscanf(tag, mc_fmt, &ui) &&
3485 1 == sscanf(val, "%u", &row.mcast_count))
3490 if (1 == sscanf(tag, name_fmt, &ui)) {
3494 len - 2 < sizeof(row.name)) {
3496 memcpy(row.name, val + 1, len);
3497 row.name[len] = '\0';
3504 if (1 == sscanf(tag, pc_fmt, &ui) &&
3505 1 == sscanf(val, "%u", &row.peer_count))
3510 if (1 == sscanf(tag, rx_fmt, &ui) &&
3511 1 == sscanf(val, "%u", &row.received))
3516 if (1 == sscanf(tag, tl_fmt, &ui) &&
3517 1 == sscanf(val, "%u", &row.ttl))
3519 else if (1 == sscanf(tag, tx_fmt, &ui) &&
3520 1 == sscanf(val, "%u", &row.sent))
3522 else if (1 == sscanf(tag, txerr_fmt, &ui) &&
3523 1 == sscanf(val, "%u", &row.send_errors))
3528 if (1 == sscanf(tag, up_fmt, &ui) &&
3529 1 == sscanf(val, "%u", &row.uptime))
3535 /* error out if rows out of order */
3536 validate_ifnum(fp, ui, &fields, &row);
3537 /* if the row is complete, print it */
3538 another_ifstats_field(&fields, &row, fp);
3541 if (fields != IFSTATS_FIELDS)
3542 xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n",
3543 fields, IFSTATS_FIELDS);
3550 * validate_reslist_idx - helper for reslist()
3552 * Ensures rows are received in order and complete.
3555 validate_reslist_idx(
3562 if (prow->idx == idx)
3564 if (prow->idx + 1 == idx) {
3565 if (*pfields < RESLIST_FIELDS)
3566 xprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3567 *pfields, RESLIST_FIELDS);
3573 "received reslist index %u, have %d of %d fields for index %u, aborting.\n",
3574 idx, *pfields, RESLIST_FIELDS, prow->idx);
3580 * another_reslist_field - helper for reslist()
3582 * If all fields for the row have been received, print it.
3585 another_reslist_field(
3591 char addrmaskstr[128];
3592 int prefix; /* subnet mask as prefix bits count */
3596 /* we understand 4 tags */
3597 if (RESLIST_FIELDS > *pfields)
3600 prefix = sockaddr_masktoprefixlen(&prow->mask);
3602 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d",
3603 stoa(&prow->addr), prefix);
3605 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s",
3606 stoa(&prow->addr), stoa(&prow->mask));
3609 " hits addr/prefix or addr mask\n"
3611 "==============================================================================\n");
3616 prow->hits, addrmaskstr, prow->flagstr);
3624 * reslist - ntpq -c reslist modeled on ntpdc -c reslist.
3628 struct parse * pcmd,
3632 const char addr_fmtu[] = "addr.%u";
3633 const char mask_fmtu[] = "mask.%u";
3634 const char hits_fmt[] = "hits.%u";
3635 const char flags_fmt[] = "flags.%u";
3636 const char qdata[] = "addr_restrictions";
3637 const int qdata_chars = COUNTOF(qdata) - 1;
3650 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars,
3651 qdata, &rstatus, &dsize, &datap);
3652 if (qres) /* message already displayed */
3656 " hits addr/prefix or addr mask\n"
3658 "==============================================================================\n");
3664 while (nextvar(&dsize, &datap, &tag, &val)) {
3667 xprintf(stderr, "nextvar gave: %s = %s\n", tag, val);
3672 if (1 == sscanf(tag, addr_fmtu, &ui) &&
3673 decodenetnum(val, &row.addr))
3678 if (1 == sscanf(tag, flags_fmt, &ui)) {
3680 row.flagstr[0] = '\0';
3682 } else if ((len = strlen(val)) < sizeof(row.flagstr)) {
3683 memcpy(row.flagstr, val, len);
3684 row.flagstr[len] = '\0';
3687 /* no flags, and still !comprende */
3688 row.flagstr[0] = '\0';
3694 if (1 == sscanf(tag, hits_fmt, &ui) &&
3695 1 == sscanf(val, "%lu", &row.hits))
3700 if (1 == sscanf(tag, mask_fmtu, &ui) &&
3701 decodenetnum(val, &row.mask))
3707 /* error out if rows out of order */
3708 validate_reslist_idx(fp, ui, &fields, &row);
3709 /* if the row is complete, print it */
3710 another_reslist_field(&fields, &row, fp);
3713 if (fields != RESLIST_FIELDS)
3714 xprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3715 fields, RESLIST_FIELDS);
3722 * collect_display_vdc
3725 collect_display_vdc(
3732 static const char * const suf[2] = { "adr", "port" };
3733 static const char * const leapbits[4] = { "00", "01",
3735 struct varlist vl[MAXLIST];
3752 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3754 if (NTP_ADD != pvdc->type) {
3755 doaddvlist(vl, pvdc->tag);
3757 for (n = 0; n < COUNTOF(suf); n++) {
3758 snprintf(tagbuf, sizeof(tagbuf), "%s%s",
3760 doaddvlist(vl, tagbuf);
3764 qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize,
3768 return; /* error msg already displayed */
3771 * iterate over the response variables filling vdc_table with
3772 * the retrieved values.
3774 while (nextvar(&rsize, &rdata, &tag, &val)) {
3777 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3778 len = strlen(pvdc->tag);
3779 if (strncmp(tag, pvdc->tag, len))
3781 if (NTP_ADD != pvdc->type) {
3782 if ('\0' != tag[len])
3787 for (n = 0; n < COUNTOF(suf); n++) {
3788 if (strcmp(tag + len, suf[n]))
3796 if (NULL == pvdc->tag)
3798 switch (pvdc->type) {
3801 /* strip surrounding double quotes */
3802 if ('"' == val[0]) {
3804 if (len > 0 && '"' == val[len - 1]) {
3805 val[len - 1] = '\0';
3810 case NTP_REFID: /* fallthru */
3811 case NTP_MODE: /* fallthru */
3813 pvdc->v.str = estrdup(val);
3817 decodets(val, &pvdc->v.lfp);
3821 if (!decodenetnum(val, &pvdc->v.sau))
3822 xprintf(stderr, "malformed %s=%s\n",
3827 if (0 == n) { /* adr */
3828 if (!decodenetnum(val, &pvdc->v.sau))
3830 "malformed %s=%s\n",
3833 if (atouint(val, &ul))
3834 SET_PORT(&pvdc->v.sau,
3846 xprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus,
3847 statustoa(vtype, rstatus));
3850 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3851 switch (pvdc->type) {
3854 if (pvdc->v.str != NULL) {
3855 xprintf(fp, "%s %s\n", pvdc->display,
3862 case NTP_ADD: /* fallthru */
3864 xprintf(fp, "%s %s\n", pvdc->display,
3865 nntohostp(&pvdc->v.sau));
3869 xprintf(fp, "%s %s\n", pvdc->display,
3870 prettydate(&pvdc->v.lfp));
3874 atouint(pvdc->v.str, &ul);
3875 xprintf(fp, "%s %s\n", pvdc->display,
3882 atouint(pvdc->v.str, &ul);
3883 xprintf(fp, "%s %s\n", pvdc->display,
3884 leapbits[ul & 0x3]);
3890 if (!decodenetnum(pvdc->v.str, &sau)) {
3891 fprintf(fp, "%s %s\n", pvdc->display, /* Text fmt */
3893 } else if (drefid == REFID_IPV4) {
3894 fprintf(fp, "%s %s\n", pvdc->display, /* IPv4 fmt */
3897 fprintf (fp, "%s 0x%08x\n", pvdc->display, /* Hex / hash */
3898 ntohl(addr2refid(&sau)));
3905 xprintf(stderr, "unexpected vdc type %d for %s\n",
3906 pvdc->type, pvdc->tag);
3914 * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats
3922 static vdc sysstats_vdc[] = {
3923 VDC_INIT("ss_uptime", "uptime: ", NTP_STR),
3924 VDC_INIT("ss_reset", "sysstats reset: ", NTP_STR),
3925 VDC_INIT("ss_received", "packets received: ", NTP_STR),
3926 VDC_INIT("ss_thisver", "current version: ", NTP_STR),
3927 VDC_INIT("ss_oldver", "older version: ", NTP_STR),
3928 VDC_INIT("ss_badformat", "bad length or format: ", NTP_STR),
3929 VDC_INIT("ss_badauth", "authentication failed:", NTP_STR),
3930 VDC_INIT("ss_declined", "declined: ", NTP_STR),
3931 VDC_INIT("ss_restricted", "restricted: ", NTP_STR),
3932 VDC_INIT("ss_limited", "rate limited: ", NTP_STR),
3933 VDC_INIT("ss_kodsent", "KoD responses: ", NTP_STR),
3934 VDC_INIT("ss_processed", "processed for time: ", NTP_STR),
3936 VDC_INIT("ss_lamport", "Lamport violations: ", NTP_STR),
3937 VDC_INIT("ss_tsrounding", "bad timestamp rounding:", NTP_STR),
3939 VDC_INIT(NULL, NULL, 0)
3942 collect_display_vdc(0, sysstats_vdc, FALSE, fp);
3947 * sysinfo - modeled on ntpdc's sysinfo
3955 static vdc sysinfo_vdc[] = {
3956 VDC_INIT("peeradr", "system peer: ", NTP_ADP),
3957 VDC_INIT("peermode", "system peer mode: ", NTP_MODE),
3958 VDC_INIT("leap", "leap indicator: ", NTP_2BIT),
3959 VDC_INIT("stratum", "stratum: ", NTP_STR),
3960 VDC_INIT("precision", "log2 precision: ", NTP_STR),
3961 VDC_INIT("rootdelay", "root delay: ", NTP_STR),
3962 VDC_INIT("rootdisp", "root dispersion: ", NTP_STR),
3963 VDC_INIT("refid", "reference ID: ", NTP_REFID),
3964 VDC_INIT("reftime", "reference time: ", NTP_LFP),
3965 VDC_INIT("sys_jitter", "system jitter: ", NTP_STR),
3966 VDC_INIT("clk_jitter", "clock jitter: ", NTP_STR),
3967 VDC_INIT("clk_wander", "clock wander: ", NTP_STR),
3968 VDC_INIT("bcastdelay", "broadcast delay: ", NTP_STR),
3969 VDC_INIT("authdelay", "symm. auth. delay:", NTP_STR),
3970 VDC_INIT(NULL, NULL, 0)
3973 collect_display_vdc(0, sysinfo_vdc, TRUE, fp);
3978 * kerninfo - modeled on ntpdc's kerninfo
3986 static vdc kerninfo_vdc[] = {
3987 VDC_INIT("koffset", "pll offset: ", NTP_STR),
3988 VDC_INIT("kfreq", "pll frequency: ", NTP_STR),
3989 VDC_INIT("kmaxerr", "maximum error: ", NTP_STR),
3990 VDC_INIT("kesterr", "estimated error: ", NTP_STR),
3991 VDC_INIT("kstflags", "kernel status: ", NTP_STR),
3992 VDC_INIT("ktimeconst", "pll time constant: ", NTP_STR),
3993 VDC_INIT("kprecis", "precision: ", NTP_STR),
3994 VDC_INIT("kfreqtol", "frequency tolerance: ", NTP_STR),
3995 VDC_INIT("kppsfreq", "pps frequency: ", NTP_STR),
3996 VDC_INIT("kppsstab", "pps stability: ", NTP_STR),
3997 VDC_INIT("kppsjitter", "pps jitter: ", NTP_STR),
3998 VDC_INIT("kppscalibdur", "calibration interval ", NTP_STR),
3999 VDC_INIT("kppscalibs", "calibration cycles: ", NTP_STR),
4000 VDC_INIT("kppsjitexc", "jitter exceeded: ", NTP_STR),
4001 VDC_INIT("kppsstbexc", "stability exceeded: ", NTP_STR),
4002 VDC_INIT("kppscaliberrs", "calibration errors: ", NTP_STR),
4003 VDC_INIT(NULL, NULL, 0)
4006 collect_display_vdc(0, kerninfo_vdc, TRUE, fp);
4011 * monstats - implements ntpq -c monstats
4019 static vdc monstats_vdc[] = {
4020 VDC_INIT("mru_enabled", "enabled: ", NTP_STR),
4021 VDC_INIT("mru_depth", "addresses: ", NTP_STR),
4022 VDC_INIT("mru_deepest", "peak addresses: ", NTP_STR),
4023 VDC_INIT("mru_maxdepth", "maximum addresses: ", NTP_STR),
4024 VDC_INIT("mru_mindepth", "reclaim above count:", NTP_STR),
4025 VDC_INIT("mru_maxage", "reclaim older than: ", NTP_STR),
4026 VDC_INIT("mru_mem", "kilobytes: ", NTP_STR),
4027 VDC_INIT("mru_maxmem", "maximum kilobytes: ", NTP_STR),
4028 VDC_INIT(NULL, NULL, 0)
4031 collect_display_vdc(0, monstats_vdc, FALSE, fp);
4036 * iostats - ntpq -c iostats - network input and output counters
4044 static vdc iostats_vdc[] = {
4045 VDC_INIT("iostats_reset", "time since reset: ", NTP_STR),
4046 VDC_INIT("total_rbuf", "receive buffers: ", NTP_STR),
4047 VDC_INIT("free_rbuf", "free receive buffers: ", NTP_STR),
4048 VDC_INIT("used_rbuf", "used receive buffers: ", NTP_STR),
4049 VDC_INIT("rbuf_lowater", "low water refills: ", NTP_STR),
4050 VDC_INIT("io_dropped", "dropped packets: ", NTP_STR),
4051 VDC_INIT("io_ignored", "ignored packets: ", NTP_STR),
4052 VDC_INIT("io_received", "received packets: ", NTP_STR),
4053 VDC_INIT("io_sent", "packets sent: ", NTP_STR),
4054 VDC_INIT("io_sendfailed", "packet send failures: ", NTP_STR),
4055 VDC_INIT("io_wakeups", "input wakeups: ", NTP_STR),
4056 VDC_INIT("io_goodwakeups", "useful input wakeups: ", NTP_STR),
4057 VDC_INIT(NULL, NULL, 0)
4060 collect_display_vdc(0, iostats_vdc, FALSE, fp);
4065 * timerstats - ntpq -c timerstats - interval timer counters
4073 static vdc timerstats_vdc[] = {
4074 VDC_INIT("timerstats_reset", "time since reset: ", NTP_STR),
4075 VDC_INIT("timer_overruns", "timer overruns: ", NTP_STR),
4076 VDC_INIT("timer_xmts", "calls to transmit: ", NTP_STR),
4077 VDC_INIT(NULL, NULL, 0)
4080 collect_display_vdc(0, timerstats_vdc, FALSE, fp);
4085 * authinfo - implements ntpq -c authinfo
4093 static vdc authinfo_vdc[] = {
4094 VDC_INIT("authreset", "time since reset:", NTP_STR),
4095 VDC_INIT("authkeys", "stored keys: ", NTP_STR),
4096 VDC_INIT("authfreek", "free keys: ", NTP_STR),
4097 VDC_INIT("authklookups", "key lookups: ", NTP_STR),
4098 VDC_INIT("authknotfound", "keys not found: ", NTP_STR),
4099 VDC_INIT("authkuncached", "uncached keys: ", NTP_STR),
4100 VDC_INIT("authkexpired", "expired keys: ", NTP_STR),
4101 VDC_INIT("authencrypts", "encryptions: ", NTP_STR),
4102 VDC_INIT("authdecrypts", "decryptions: ", NTP_STR),
4103 VDC_INIT(NULL, NULL, 0)
4106 collect_display_vdc(0, authinfo_vdc, FALSE, fp);
4111 * pstats - show statistics for a peer
4119 static vdc pstats_vdc[] = {
4120 VDC_INIT("src", "remote host: ", NTP_ADD),
4121 VDC_INIT("dst", "local address: ", NTP_ADD),
4122 VDC_INIT("timerec", "time last received: ", NTP_STR),
4123 VDC_INIT("timer", "time until next send:", NTP_STR),
4124 VDC_INIT("timereach", "reachability change: ", NTP_STR),
4125 VDC_INIT("sent", "packets sent: ", NTP_STR),
4126 VDC_INIT("received", "packets received: ", NTP_STR),
4127 VDC_INIT("badauth", "bad authentication: ", NTP_STR),
4128 VDC_INIT("bogusorg", "bogus origin: ", NTP_STR),
4129 VDC_INIT("oldpkt", "duplicate: ", NTP_STR),
4130 VDC_INIT("seldisp", "bad dispersion: ", NTP_STR),
4131 VDC_INIT("selbroken", "bad reference time: ", NTP_STR),
4132 VDC_INIT("candidate", "candidate order: ", NTP_STR),
4133 VDC_INIT(NULL, NULL, 0)
4137 associd = checkassocid(pcmd->argval[0].uval);
4141 collect_display_vdc(associd, pstats_vdc, TRUE, fp);