ntpq-subs.c revision 1.1.1.4 1 /* $NetBSD: ntpq-subs.c,v 1.1.1.4 2014/12/19 20:37:42 christos Exp $ */
2
3 /*
4 * ntpq-subs.c - subroutines which are called to perform ntpq commands.
5 */
6 #include <config.h>
7 #include <stdio.h>
8 #include <ctype.h>
9 #include <sys/types.h>
10 #include <sys/time.h>
11
12 #include "ntpq.h"
13 #include "ntpq-opts.h"
14
15 extern char currenthost[];
16 extern int currenthostisnum;
17 size_t maxhostlen;
18
19 /*
20 * Declarations for command handlers in here
21 */
22 static associd_t checkassocid (u_int32);
23 static struct varlist *findlistvar (struct varlist *, char *);
24 static void doaddvlist (struct varlist *, const char *);
25 static void dormvlist (struct varlist *, const char *);
26 static void doclearvlist (struct varlist *);
27 static void makequerydata (struct varlist *, int *, char *);
28 static int doquerylist (struct varlist *, int, associd_t, int,
29 u_short *, int *, const char **);
30 static void doprintvlist (struct varlist *, FILE *);
31 static void addvars (struct parse *, FILE *);
32 static void rmvars (struct parse *, FILE *);
33 static void clearvars (struct parse *, FILE *);
34 static void showvars (struct parse *, FILE *);
35 static int dolist (struct varlist *, associd_t, int, int,
36 FILE *);
37 static void readlist (struct parse *, FILE *);
38 static void writelist (struct parse *, FILE *);
39 static void readvar (struct parse *, FILE *);
40 static void writevar (struct parse *, FILE *);
41 static void clocklist (struct parse *, FILE *);
42 static void clockvar (struct parse *, FILE *);
43 static int findassidrange (u_int32, u_int32, int *, int *,
44 FILE *);
45 static void mreadlist (struct parse *, FILE *);
46 static void mreadvar (struct parse *, FILE *);
47 static void printassoc (int, FILE *);
48 static void associations (struct parse *, FILE *);
49 static void lassociations (struct parse *, FILE *);
50 static void passociations (struct parse *, FILE *);
51 static void lpassociations (struct parse *, FILE *);
52
53 #ifdef UNUSED
54 static void radiostatus (struct parse *, FILE *);
55 #endif /* UNUSED */
56
57 static void authinfo (struct parse *, FILE *);
58 static void pstats (struct parse *, FILE *);
59 static long when (l_fp *, l_fp *, l_fp *);
60 static char * prettyinterval (char *, size_t, long);
61 static int doprintpeers (struct varlist *, int, int, int, const char *, FILE *, int);
62 static int dogetpeers (struct varlist *, associd_t, FILE *, int);
63 static void dopeers (int, FILE *, int);
64 static void peers (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 *);
81
82 /*
83 * Commands we understand. Ntpdc imports this.
84 */
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 },
90 { "", "", "", "" },
91 "print list of association ID's and statuses for the server's peers" },
92 { "passociations", passociations, { NO, NO, NO, NO },
93 { "", "", "", "" },
94 "print list of associations returned by last associations command" },
95 { "lassociations", lassociations, { NO, NO, NO, NO },
96 { "", "", "", "" },
97 "print list of associations including all client information" },
98 { "lpassociations", lpassociations, { NO, NO, NO, NO },
99 { "", "", "", "" },
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 },
108 { "", "", "", "" },
109 "remove all variables from the variable list" },
110 { "showvars", showvars, { NO, NO, NO, NO },
111 { "", "", "", "" },
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 { "lpeers", lpeers, { OPT|IP_VERSION, NO, NO, NO },
162 { "-4|-6", "", "", "" },
163 "obtain and print a list of all peers and clients [IP version]" },
164 { "opeers", opeers, { OPT|IP_VERSION, NO, NO, NO },
165 { "-4|-6", "", "", "" },
166 "print peer list the old way, with dstadr shown rather than refid [IP version]" },
167 { "lopeers", lopeers, { OPT|IP_VERSION, NO, NO, NO },
168 { "-4|-6", "", "", "" },
169 "obtain and print a list of all peers and clients showing dstadr [IP version]" },
170 { ":config", config, { NTP_STR, NO, NO, NO },
171 { "<configuration command line>", "", "", "" },
172 "send a remote configuration command to ntpd" },
173 { "config-from-file", config_from_file, { NTP_STR, NO, NO, NO },
174 { "<configuration filename>", "", "", "" },
175 "configure ntpd using the configuration filename" },
176 { "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
177 { "tag=value", "tag=value", "tag=value", "tag=value" },
178 "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." },
179 { "ifstats", ifstats, { NO, NO, NO, NO },
180 { "", "", "", "" },
181 "show statistics for each local address ntpd is using" },
182 { "reslist", reslist, { NO, NO, NO, NO },
183 { "", "", "", "" },
184 "show ntpd access control list" },
185 { "sysinfo", sysinfo, { NO, NO, NO, NO },
186 { "", "", "", "" },
187 "display system summary" },
188 { "kerninfo", kerninfo, { NO, NO, NO, NO },
189 { "", "", "", "" },
190 "display kernel loop and PPS statistics" },
191 { "sysstats", sysstats, { NO, NO, NO, NO },
192 { "", "", "", "" },
193 "display system uptime and packet counts" },
194 { "monstats", monstats, { NO, NO, NO, NO },
195 { "", "", "", "" },
196 "display monitor (mrulist) counters and limits" },
197 { "authinfo", authinfo, { NO, NO, NO, NO },
198 { "", "", "", "" },
199 "display symmetric authentication counters" },
200 { "iostats", iostats, { NO, NO, NO, NO },
201 { "", "", "", "" },
202 "display network input and output counters" },
203 { "timerstats", timerstats, { NO, NO, NO, NO },
204 { "", "", "", "" },
205 "display interval timer counters" },
206 { 0, 0, { NO, NO, NO, NO },
207 { "-4|-6", "", "", "" }, "" }
208 };
209
210
211 /*
212 * Variable list data space
213 */
214 #define MAXLINE 512 /* maximum length of a line */
215 #define MAXLIST 128 /* maximum variables in list */
216 #define LENHOSTNAME 256 /* host name limit */
217
218 #define MRU_GOT_COUNT 0x1
219 #define MRU_GOT_LAST 0x2
220 #define MRU_GOT_FIRST 0x4
221 #define MRU_GOT_MV 0x8
222 #define MRU_GOT_RS 0x10
223 #define MRU_GOT_ADDR 0x20
224 #define MRU_GOT_ALL (MRU_GOT_COUNT | MRU_GOT_LAST | MRU_GOT_FIRST \
225 | MRU_GOT_MV | MRU_GOT_RS | MRU_GOT_ADDR)
226
227 /*
228 * mrulist() depends on MRUSORT_DEF and MRUSORT_RDEF being the first two
229 */
230 typedef enum mru_sort_order_tag {
231 MRUSORT_DEF = 0, /* lstint ascending */
232 MRUSORT_R_DEF, /* lstint descending */
233 MRUSORT_AVGINT, /* avgint ascending */
234 MRUSORT_R_AVGINT, /* avgint descending */
235 MRUSORT_ADDR, /* IPv4 asc. then IPv6 asc. */
236 MRUSORT_R_ADDR, /* IPv6 desc. then IPv4 desc. */
237 MRUSORT_COUNT, /* hit count ascending */
238 MRUSORT_R_COUNT, /* hit count descending */
239 MRUSORT_MAX, /* special: count of this enum */
240 } mru_sort_order;
241
242 const char * const mru_sort_keywords[MRUSORT_MAX] = {
243 "lstint", /* MRUSORT_DEF */
244 "-lstint", /* MRUSORT_R_DEF */
245 "avgint", /* MRUSORT_AVGINT */
246 "-avgint", /* MRUSORT_R_AVGINT */
247 "addr", /* MRUSORT_ADDR */
248 "-addr", /* MRUSORT_R_ADDR */
249 "count", /* MRUSORT_COUNT */
250 "-count", /* MRUSORT_R_COUNT */
251 };
252
253 typedef int (*qsort_cmp)(const void *, const void *);
254
255 /*
256 * Old CTL_PST defines for version 2.
257 */
258 #define OLD_CTL_PST_CONFIG 0x80
259 #define OLD_CTL_PST_AUTHENABLE 0x40
260 #define OLD_CTL_PST_AUTHENTIC 0x20
261 #define OLD_CTL_PST_REACH 0x10
262 #define OLD_CTL_PST_SANE 0x08
263 #define OLD_CTL_PST_DISP 0x04
264
265 #define OLD_CTL_PST_SEL_REJECT 0
266 #define OLD_CTL_PST_SEL_SELCAND 1
267 #define OLD_CTL_PST_SEL_SYNCCAND 2
268 #define OLD_CTL_PST_SEL_SYSPEER 3
269
270 char flash2[] = " .+* "; /* flash decode for version 2 */
271 char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
272
273 struct varlist {
274 char *name;
275 char *value;
276 } g_varlist[MAXLIST] = { { 0, 0 } };
277
278 /*
279 * Imported from ntpq.c
280 */
281 extern int showhostnames;
282 extern int wideremote;
283 extern int rawmode;
284 extern struct servent *server_entry;
285 extern struct association *assoc_cache;
286 extern u_char pktversion;
287
288 typedef struct mru_tag mru;
289 struct mru_tag {
290 mru * hlink; /* next in hash table bucket */
291 DECL_DLIST_LINK(mru, mlink);
292 int count;
293 l_fp last;
294 l_fp first;
295 u_char mode;
296 u_char ver;
297 u_short rs;
298 sockaddr_u addr;
299 };
300
301 typedef struct ifstats_row_tag {
302 u_int ifnum;
303 sockaddr_u addr;
304 sockaddr_u bcast;
305 int enabled;
306 u_int flags;
307 int mcast_count;
308 char name[32];
309 int peer_count;
310 int received;
311 int sent;
312 int send_errors;
313 u_int ttl;
314 u_int uptime;
315 } ifstats_row;
316
317 typedef struct reslist_row_tag {
318 u_int idx;
319 sockaddr_u addr;
320 sockaddr_u mask;
321 u_long hits;
322 char flagstr[128];
323 } reslist_row;
324
325 typedef struct var_display_collection_tag {
326 const char * const tag; /* system variable */
327 const char * const display; /* descriptive text */
328 u_char type; /* NTP_STR, etc */
329 union {
330 char * str;
331 sockaddr_u sau; /* NTP_ADD */
332 l_fp lfp; /* NTP_LFP */
333 } v; /* retrieved value */
334 } vdc;
335
336 /*
337 * other local function prototypes
338 */
339 void mrulist_ctrl_c_hook(void);
340 static mru * add_mru(mru *);
341 static int collect_mru_list(const char *, l_fp *);
342 static int fetch_nonce(char *, size_t);
343 static int qcmp_mru_avgint(const void *, const void *);
344 static int qcmp_mru_r_avgint(const void *, const void *);
345 static int qcmp_mru_addr(const void *, const void *);
346 static int qcmp_mru_r_addr(const void *, const void *);
347 static int qcmp_mru_count(const void *, const void *);
348 static int qcmp_mru_r_count(const void *, const void *);
349 static void validate_ifnum(FILE *, u_int, int *, ifstats_row *);
350 static void another_ifstats_field(int *, ifstats_row *, FILE *);
351 static void collect_display_vdc(associd_t as, vdc *table,
352 int decodestatus, FILE *fp);
353
354 /*
355 * static globals
356 */
357 static u_int mru_count;
358 static u_int mru_dupes;
359 volatile int mrulist_interrupted;
360 static mru mru_list; /* listhead */
361 static mru ** hash_table;
362
363 /*
364 * qsort comparison function table for mrulist(). The first two
365 * entries are NULL because they are handled without qsort().
366 */
367 const static qsort_cmp mru_qcmp_table[MRUSORT_MAX] = {
368 NULL, /* MRUSORT_DEF unused */
369 NULL, /* MRUSORT_R_DEF unused */
370 &qcmp_mru_avgint, /* MRUSORT_AVGINT */
371 &qcmp_mru_r_avgint, /* MRUSORT_R_AVGINT */
372 &qcmp_mru_addr, /* MRUSORT_ADDR */
373 &qcmp_mru_r_addr, /* MRUSORT_R_ADDR */
374 &qcmp_mru_count, /* MRUSORT_COUNT */
375 &qcmp_mru_r_count, /* MRUSORT_R_COUNT */
376 };
377
378 /*
379 * checkassocid - return the association ID, checking to see if it is valid
380 */
381 static associd_t
382 checkassocid(
383 u_int32 value
384 )
385 {
386 associd_t associd;
387 u_long ulvalue;
388
389 associd = (associd_t)value;
390 if (0 == associd || value != associd) {
391 ulvalue = value;
392 fprintf(stderr,
393 "***Invalid association ID %lu specified\n",
394 ulvalue);
395 return 0;
396 }
397
398 return associd;
399 }
400
401
402 /*
403 * findlistvar - Look for the named variable in a varlist. If found,
404 * return a pointer to it. Otherwise, if the list has
405 * slots available, return the pointer to the first free
406 * slot, or NULL if it's full.
407 */
408 static struct varlist *
409 findlistvar(
410 struct varlist *list,
411 char *name
412 )
413 {
414 struct varlist *vl;
415
416 for (vl = list; vl < list + MAXLIST && vl->name != NULL; vl++)
417 if (!strcmp(name, vl->name))
418 return vl;
419 if (vl < list + MAXLIST)
420 return vl;
421
422 return NULL;
423 }
424
425
426 /*
427 * doaddvlist - add variable(s) to the variable list
428 */
429 static void
430 doaddvlist(
431 struct varlist *vlist,
432 const char *vars
433 )
434 {
435 struct varlist *vl;
436 int len;
437 char *name;
438 char *value;
439
440 len = strlen(vars);
441 while (nextvar(&len, &vars, &name, &value)) {
442 vl = findlistvar(vlist, name);
443 if (NULL == vl) {
444 fprintf(stderr, "Variable list full\n");
445 return;
446 }
447
448 if (NULL == vl->name) {
449 vl->name = estrdup(name);
450 } else if (vl->value != NULL) {
451 free(vl->value);
452 vl->value = NULL;
453 }
454
455 if (value != NULL)
456 vl->value = estrdup(value);
457 }
458 }
459
460
461 /*
462 * dormvlist - remove variable(s) from the variable list
463 */
464 static void
465 dormvlist(
466 struct varlist *vlist,
467 const char *vars
468 )
469 {
470 struct varlist *vl;
471 int len;
472 char *name;
473 char *value;
474
475 len = strlen(vars);
476 while (nextvar(&len, &vars, &name, &value)) {
477 vl = findlistvar(vlist, name);
478 if (vl == 0 || vl->name == 0) {
479 (void) fprintf(stderr, "Variable `%s' not found\n",
480 name);
481 } else {
482 free((void *)vl->name);
483 if (vl->value != 0)
484 free(vl->value);
485 for ( ; (vl+1) < (g_varlist + MAXLIST)
486 && (vl+1)->name != 0; vl++) {
487 vl->name = (vl+1)->name;
488 vl->value = (vl+1)->value;
489 }
490 vl->name = vl->value = 0;
491 }
492 }
493 }
494
495
496 /*
497 * doclearvlist - clear a variable list
498 */
499 static void
500 doclearvlist(
501 struct varlist *vlist
502 )
503 {
504 register struct varlist *vl;
505
506 for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
507 free((void *)vl->name);
508 vl->name = 0;
509 if (vl->value != 0) {
510 free(vl->value);
511 vl->value = 0;
512 }
513 }
514 }
515
516
517 /*
518 * makequerydata - form a data buffer to be included with a query
519 */
520 static void
521 makequerydata(
522 struct varlist *vlist,
523 int *datalen,
524 char *data
525 )
526 {
527 register struct varlist *vl;
528 register char *cp, *cpend;
529 register int namelen, valuelen;
530 register int totallen;
531
532 cp = data;
533 cpend = data + *datalen;
534
535 for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
536 namelen = strlen(vl->name);
537 if (vl->value == 0)
538 valuelen = 0;
539 else
540 valuelen = strlen(vl->value);
541 totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
542 if (cp + totallen > cpend) {
543 fprintf(stderr,
544 "***Ignoring variables starting with `%s'\n",
545 vl->name);
546 break;
547 }
548
549 if (cp != data)
550 *cp++ = ',';
551 memcpy(cp, vl->name, (size_t)namelen);
552 cp += namelen;
553 if (valuelen != 0) {
554 *cp++ = '=';
555 memcpy(cp, vl->value, (size_t)valuelen);
556 cp += valuelen;
557 }
558 }
559 *datalen = cp - data;
560 }
561
562
563 /*
564 * doquerylist - send a message including variables in a list
565 */
566 static int
567 doquerylist(
568 struct varlist *vlist,
569 int op,
570 associd_t associd,
571 int auth,
572 u_short *rstatus,
573 int *dsize,
574 const char **datap
575 )
576 {
577 char data[CTL_MAX_DATA_LEN];
578 int datalen;
579
580 datalen = sizeof(data);
581 makequerydata(vlist, &datalen, data);
582
583 return doquery(op, associd, auth, datalen, data, rstatus, dsize,
584 datap);
585 }
586
587
588 /*
589 * doprintvlist - print the variables on a list
590 */
591 static void
592 doprintvlist(
593 struct varlist *vlist,
594 FILE *fp
595 )
596 {
597 size_t n;
598
599 if (NULL == vlist->name) {
600 fprintf(fp, "No variables on list\n");
601 return;
602 }
603 for (n = 0; n < MAXLIST && vlist[n].name != NULL; n++) {
604 if (NULL == vlist[n].value)
605 fprintf(fp, "%s\n", vlist[n].name);
606 else
607 fprintf(fp, "%s=%s\n", vlist[n].name,
608 vlist[n].value);
609 }
610 }
611
612 /*
613 * addvars - add variables to the variable list
614 */
615 /*ARGSUSED*/
616 static void
617 addvars(
618 struct parse *pcmd,
619 FILE *fp
620 )
621 {
622 doaddvlist(g_varlist, pcmd->argval[0].string);
623 }
624
625
626 /*
627 * rmvars - remove variables from the variable list
628 */
629 /*ARGSUSED*/
630 static void
631 rmvars(
632 struct parse *pcmd,
633 FILE *fp
634 )
635 {
636 dormvlist(g_varlist, pcmd->argval[0].string);
637 }
638
639
640 /*
641 * clearvars - clear the variable list
642 */
643 /*ARGSUSED*/
644 static void
645 clearvars(
646 struct parse *pcmd,
647 FILE *fp
648 )
649 {
650 doclearvlist(g_varlist);
651 }
652
653
654 /*
655 * showvars - show variables on the variable list
656 */
657 /*ARGSUSED*/
658 static void
659 showvars(
660 struct parse *pcmd,
661 FILE *fp
662 )
663 {
664 doprintvlist(g_varlist, fp);
665 }
666
667
668 /*
669 * dolist - send a request with the given list of variables
670 */
671 static int
672 dolist(
673 struct varlist *vlist,
674 associd_t associd,
675 int op,
676 int type,
677 FILE *fp
678 )
679 {
680 const char *datap;
681 int res;
682 int dsize;
683 u_short rstatus;
684 int quiet;
685
686 /*
687 * if we're asking for specific variables don't include the
688 * status header line in the output.
689 */
690 if (old_rv)
691 quiet = 0;
692 else
693 quiet = (vlist->name != NULL);
694
695 res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
696
697 if (res != 0)
698 return 0;
699
700 if (numhosts > 1)
701 fprintf(fp, "server=%s ", currenthost);
702 if (dsize == 0) {
703 if (associd == 0)
704 fprintf(fp, "No system%s variables returned\n",
705 (type == TYPE_CLOCK) ? " clock" : "");
706 else
707 fprintf(fp,
708 "No information returned for%s association %u\n",
709 (type == TYPE_CLOCK) ? " clock" : "",
710 associd);
711 return 1;
712 }
713
714 if (!quiet)
715 fprintf(fp, "associd=%u ", associd);
716 printvars(dsize, datap, (int)rstatus, type, quiet, fp);
717 return 1;
718 }
719
720
721 /*
722 * readlist - send a read variables request with the variables on the list
723 */
724 static void
725 readlist(
726 struct parse *pcmd,
727 FILE *fp
728 )
729 {
730 associd_t associd;
731 int type;
732
733 if (pcmd->nargs == 0) {
734 associd = 0;
735 } else {
736 /* HMS: I think we want the u_int32 target here, not the u_long */
737 if (pcmd->argval[0].uval == 0)
738 associd = 0;
739 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
740 return;
741 }
742
743 type = (0 == associd)
744 ? TYPE_SYS
745 : TYPE_PEER;
746 dolist(g_varlist, associd, CTL_OP_READVAR, type, fp);
747 }
748
749
750 /*
751 * writelist - send a write variables request with the variables on the list
752 */
753 static void
754 writelist(
755 struct parse *pcmd,
756 FILE *fp
757 )
758 {
759 const char *datap;
760 int res;
761 associd_t associd;
762 int dsize;
763 u_short rstatus;
764
765 if (pcmd->nargs == 0) {
766 associd = 0;
767 } else {
768 /* HMS: Do we really want uval here? */
769 if (pcmd->argval[0].uval == 0)
770 associd = 0;
771 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
772 return;
773 }
774
775 res = doquerylist(g_varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
776 &dsize, &datap);
777
778 if (res != 0)
779 return;
780
781 if (numhosts > 1)
782 (void) fprintf(fp, "server=%s ", currenthost);
783 if (dsize == 0)
784 (void) fprintf(fp, "done! (no data returned)\n");
785 else {
786 (void) fprintf(fp,"associd=%u ", associd);
787 printvars(dsize, datap, (int)rstatus,
788 (associd != 0) ? TYPE_PEER : TYPE_SYS, 0, fp);
789 }
790 return;
791 }
792
793
794 /*
795 * readvar - send a read variables request with the specified variables
796 */
797 static void
798 readvar(
799 struct parse *pcmd,
800 FILE *fp
801 )
802 {
803 associd_t associd;
804 u_int tmpcount;
805 u_int u;
806 int type;
807 struct varlist tmplist[MAXLIST];
808
809
810 /* HMS: uval? */
811 if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
812 associd = 0;
813 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
814 return;
815
816 ZERO(tmplist);
817 if (pcmd->nargs > 1) {
818 tmpcount = pcmd->nargs - 1;
819 for (u = 0; u < tmpcount; u++)
820 doaddvlist(tmplist, pcmd->argval[1 + u].string);
821 }
822
823 type = (0 == associd)
824 ? TYPE_SYS
825 : TYPE_PEER;
826 dolist(tmplist, associd, CTL_OP_READVAR, type, fp);
827
828 doclearvlist(tmplist);
829 }
830
831
832 /*
833 * writevar - send a write variables request with the specified variables
834 */
835 static void
836 writevar(
837 struct parse *pcmd,
838 FILE *fp
839 )
840 {
841 const char *datap;
842 int res;
843 associd_t associd;
844 int type;
845 int dsize;
846 u_short rstatus;
847 struct varlist tmplist[MAXLIST];
848
849 /* HMS: uval? */
850 if (pcmd->argval[0].uval == 0)
851 associd = 0;
852 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
853 return;
854
855 ZERO(tmplist);
856 doaddvlist(tmplist, pcmd->argval[1].string);
857
858 res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
859 &dsize, &datap);
860
861 doclearvlist(tmplist);
862
863 if (res != 0)
864 return;
865
866 if (numhosts > 1)
867 fprintf(fp, "server=%s ", currenthost);
868 if (dsize == 0)
869 fprintf(fp, "done! (no data returned)\n");
870 else {
871 fprintf(fp,"associd=%u ", associd);
872 type = (0 == associd)
873 ? TYPE_SYS
874 : TYPE_PEER;
875 printvars(dsize, datap, (int)rstatus, type, 0, fp);
876 }
877 return;
878 }
879
880
881 /*
882 * clocklist - send a clock variables request with the variables on the list
883 */
884 static void
885 clocklist(
886 struct parse *pcmd,
887 FILE *fp
888 )
889 {
890 associd_t associd;
891
892 /* HMS: uval? */
893 if (pcmd->nargs == 0) {
894 associd = 0;
895 } else {
896 if (pcmd->argval[0].uval == 0)
897 associd = 0;
898 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
899 return;
900 }
901
902 dolist(g_varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
903 }
904
905
906 /*
907 * clockvar - send a clock variables request with the specified variables
908 */
909 static void
910 clockvar(
911 struct parse *pcmd,
912 FILE *fp
913 )
914 {
915 associd_t associd;
916 struct varlist tmplist[MAXLIST];
917
918 /* HMS: uval? */
919 if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
920 associd = 0;
921 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
922 return;
923
924 ZERO(tmplist);
925 if (pcmd->nargs >= 2)
926 doaddvlist(tmplist, pcmd->argval[1].string);
927
928 dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
929
930 doclearvlist(tmplist);
931 }
932
933
934 /*
935 * findassidrange - verify a range of association ID's
936 */
937 static int
938 findassidrange(
939 u_int32 assid1,
940 u_int32 assid2,
941 int * from,
942 int * to,
943 FILE * fp
944 )
945 {
946 associd_t assids[2];
947 int ind[COUNTOF(assids)];
948 u_int i;
949 size_t a;
950
951
952 if (0 == numassoc)
953 dogetassoc(fp);
954
955 assids[0] = checkassocid(assid1);
956 if (0 == assids[0])
957 return 0;
958 assids[1] = checkassocid(assid2);
959 if (0 == assids[1])
960 return 0;
961
962 for (a = 0; a < COUNTOF(assids); a++) {
963 ind[a] = -1;
964 for (i = 0; i < numassoc; i++)
965 if (assoc_cache[i].assid == assids[a])
966 ind[a] = i;
967 }
968 for (a = 0; a < COUNTOF(assids); a++)
969 if (-1 == ind[a]) {
970 fprintf(stderr,
971 "***Association ID %u not found in list\n",
972 assids[a]);
973 return 0;
974 }
975
976 if (ind[0] < ind[1]) {
977 *from = ind[0];
978 *to = ind[1];
979 } else {
980 *to = ind[0];
981 *from = ind[1];
982 }
983 return 1;
984 }
985
986
987
988 /*
989 * mreadlist - send a read variables request for multiple associations
990 */
991 static void
992 mreadlist(
993 struct parse *pcmd,
994 FILE *fp
995 )
996 {
997 int i;
998 int from;
999 int to;
1000
1001 if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1002 &from, &to, fp))
1003 return;
1004
1005 for (i = from; i <= to; i++) {
1006 if (i != from)
1007 fprintf(fp, "\n");
1008 if (!dolist(g_varlist, assoc_cache[i].assid,
1009 CTL_OP_READVAR, TYPE_PEER, fp))
1010 return;
1011 }
1012 return;
1013 }
1014
1015
1016 /*
1017 * mreadvar - send a read variables request for multiple associations
1018 */
1019 static void
1020 mreadvar(
1021 struct parse *pcmd,
1022 FILE *fp
1023 )
1024 {
1025 int i;
1026 int from;
1027 int to;
1028 struct varlist tmplist[MAXLIST];
1029 struct varlist *pvars;
1030
1031 if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1032 &from, &to, fp))
1033 return;
1034
1035 ZERO(tmplist);
1036 if (pcmd->nargs >= 3) {
1037 doaddvlist(tmplist, pcmd->argval[2].string);
1038 pvars = tmplist;
1039 } else {
1040 pvars = g_varlist;
1041 }
1042
1043 for (i = from; i <= to; i++) {
1044 if (!dolist(pvars, assoc_cache[i].assid, CTL_OP_READVAR,
1045 TYPE_PEER, fp))
1046 break;
1047 }
1048
1049 if (pvars == tmplist)
1050 doclearvlist(tmplist);
1051
1052 return;
1053 }
1054
1055
1056 /*
1057 * dogetassoc - query the host for its list of associations
1058 */
1059 int
1060 dogetassoc(
1061 FILE *fp
1062 )
1063 {
1064 const char *datap;
1065 const u_short *pus;
1066 int res;
1067 int dsize;
1068 u_short rstatus;
1069
1070 res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
1071 &dsize, &datap);
1072
1073 if (res != 0)
1074 return 0;
1075
1076 if (dsize == 0) {
1077 if (numhosts > 1)
1078 fprintf(fp, "server=%s ", currenthost);
1079 fprintf(fp, "No association ID's returned\n");
1080 return 0;
1081 }
1082
1083 if (dsize & 0x3) {
1084 if (numhosts > 1)
1085 fprintf(stderr, "server=%s ", currenthost);
1086 fprintf(stderr,
1087 "***Server returned %d octets, should be multiple of 4\n",
1088 dsize);
1089 return 0;
1090 }
1091
1092 numassoc = 0;
1093
1094 while (dsize > 0) {
1095 if (numassoc >= assoc_cache_slots) {
1096 grow_assoc_cache();
1097 }
1098 pus = (const void *)datap;
1099 assoc_cache[numassoc].assid = ntohs(*pus);
1100 datap += sizeof(*pus);
1101 pus = (const void *)datap;
1102 assoc_cache[numassoc].status = ntohs(*pus);
1103 datap += sizeof(*pus);
1104 dsize -= 2 * sizeof(*pus);
1105 if (debug) {
1106 fprintf(stderr, "[%u] ",
1107 assoc_cache[numassoc].assid);
1108 }
1109 numassoc++;
1110 }
1111 if (debug) {
1112 fprintf(stderr, "\n%d associations total\n", numassoc);
1113 }
1114 sortassoc();
1115 return 1;
1116 }
1117
1118
1119 /*
1120 * printassoc - print the current list of associations
1121 */
1122 static void
1123 printassoc(
1124 int showall,
1125 FILE *fp
1126 )
1127 {
1128 register char *bp;
1129 u_int i;
1130 u_char statval;
1131 int event;
1132 u_long event_count;
1133 const char *conf;
1134 const char *reach;
1135 const char *auth;
1136 const char *condition = "";
1137 const char *last_event;
1138 char buf[128];
1139
1140 if (numassoc == 0) {
1141 (void) fprintf(fp, "No association ID's in list\n");
1142 return;
1143 }
1144
1145 /*
1146 * Output a header
1147 */
1148 (void) fprintf(fp,
1149 "\nind assid status conf reach auth condition last_event cnt\n");
1150 (void) fprintf(fp,
1151 "===========================================================\n");
1152 for (i = 0; i < numassoc; i++) {
1153 statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status);
1154 if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
1155 continue;
1156 event = CTL_PEER_EVENT(assoc_cache[i].status);
1157 event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
1158 if (statval & CTL_PST_CONFIG)
1159 conf = "yes";
1160 else
1161 conf = "no";
1162 if (statval & CTL_PST_BCAST) {
1163 reach = "none";
1164 if (statval & CTL_PST_AUTHENABLE)
1165 auth = "yes";
1166 else
1167 auth = "none";
1168 } else {
1169 if (statval & CTL_PST_REACH)
1170 reach = "yes";
1171 else
1172 reach = "no";
1173 if (statval & CTL_PST_AUTHENABLE) {
1174 if (statval & CTL_PST_AUTHENTIC)
1175 auth = "ok ";
1176 else
1177 auth = "bad";
1178 } else {
1179 auth = "none";
1180 }
1181 }
1182 if (pktversion > NTP_OLDVERSION) {
1183 switch (statval & 0x7) {
1184
1185 case CTL_PST_SEL_REJECT:
1186 condition = "reject";
1187 break;
1188
1189 case CTL_PST_SEL_SANE:
1190 condition = "falsetick";
1191 break;
1192
1193 case CTL_PST_SEL_CORRECT:
1194 condition = "excess";
1195 break;
1196
1197 case CTL_PST_SEL_SELCAND:
1198 condition = "outlyer";
1199 break;
1200
1201 case CTL_PST_SEL_SYNCCAND:
1202 condition = "candidate";
1203 break;
1204
1205 case CTL_PST_SEL_EXCESS:
1206 condition = "backup";
1207 break;
1208
1209 case CTL_PST_SEL_SYSPEER:
1210 condition = "sys.peer";
1211 break;
1212
1213 case CTL_PST_SEL_PPS:
1214 condition = "pps.peer";
1215 break;
1216 }
1217 } else {
1218 switch (statval & 0x3) {
1219
1220 case OLD_CTL_PST_SEL_REJECT:
1221 if (!(statval & OLD_CTL_PST_SANE))
1222 condition = "insane";
1223 else if (!(statval & OLD_CTL_PST_DISP))
1224 condition = "hi_disp";
1225 else
1226 condition = "";
1227 break;
1228
1229 case OLD_CTL_PST_SEL_SELCAND:
1230 condition = "sel_cand";
1231 break;
1232
1233 case OLD_CTL_PST_SEL_SYNCCAND:
1234 condition = "sync_cand";
1235 break;
1236
1237 case OLD_CTL_PST_SEL_SYSPEER:
1238 condition = "sys_peer";
1239 break;
1240 }
1241 }
1242 switch (PEER_EVENT|event) {
1243
1244 case PEVNT_MOBIL:
1245 last_event = "mobilize";
1246 break;
1247
1248 case PEVNT_DEMOBIL:
1249 last_event = "demobilize";
1250 break;
1251
1252 case PEVNT_REACH:
1253 last_event = "reachable";
1254 break;
1255
1256 case PEVNT_UNREACH:
1257 last_event = "unreachable";
1258 break;
1259
1260 case PEVNT_RESTART:
1261 last_event = "restart";
1262 break;
1263
1264 case PEVNT_REPLY:
1265 last_event = "no_reply";
1266 break;
1267
1268 case PEVNT_RATE:
1269 last_event = "rate_exceeded";
1270 break;
1271
1272 case PEVNT_DENY:
1273 last_event = "access_denied";
1274 break;
1275
1276 case PEVNT_ARMED:
1277 last_event = "leap_armed";
1278 break;
1279
1280 case PEVNT_NEWPEER:
1281 last_event = "sys_peer";
1282 break;
1283
1284 case PEVNT_CLOCK:
1285 last_event = "clock_alarm";
1286 break;
1287
1288 default:
1289 last_event = "";
1290 break;
1291 }
1292 snprintf(buf, sizeof(buf),
1293 "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2lu",
1294 i + 1, assoc_cache[i].assid,
1295 assoc_cache[i].status, conf, reach, auth,
1296 condition, last_event, event_count);
1297 bp = buf + strlen(buf);
1298 while (bp > buf && ' ' == bp[-1])
1299 --bp;
1300 bp[0] = '\0';
1301 fprintf(fp, "%s\n", buf);
1302 }
1303 }
1304
1305
1306 /*
1307 * associations - get, record and print a list of associations
1308 */
1309 /*ARGSUSED*/
1310 static void
1311 associations(
1312 struct parse *pcmd,
1313 FILE *fp
1314 )
1315 {
1316 if (dogetassoc(fp))
1317 printassoc(0, fp);
1318 }
1319
1320
1321 /*
1322 * lassociations - get, record and print a long list of associations
1323 */
1324 /*ARGSUSED*/
1325 static void
1326 lassociations(
1327 struct parse *pcmd,
1328 FILE *fp
1329 )
1330 {
1331 if (dogetassoc(fp))
1332 printassoc(1, fp);
1333 }
1334
1335
1336 /*
1337 * passociations - print the association list
1338 */
1339 /*ARGSUSED*/
1340 static void
1341 passociations(
1342 struct parse *pcmd,
1343 FILE *fp
1344 )
1345 {
1346 printassoc(0, fp);
1347 }
1348
1349
1350 /*
1351 * lpassociations - print the long association list
1352 */
1353 /*ARGSUSED*/
1354 static void
1355 lpassociations(
1356 struct parse *pcmd,
1357 FILE *fp
1358 )
1359 {
1360 printassoc(1, fp);
1361 }
1362
1363
1364 /*
1365 * saveconfig - dump ntp server configuration to server file
1366 */
1367 static void
1368 saveconfig(
1369 struct parse *pcmd,
1370 FILE *fp
1371 )
1372 {
1373 const char *datap;
1374 int res;
1375 int dsize;
1376 u_short rstatus;
1377
1378 if (0 == pcmd->nargs)
1379 return;
1380
1381 res = doquery(CTL_OP_SAVECONFIG, 0, 1,
1382 strlen(pcmd->argval[0].string),
1383 pcmd->argval[0].string, &rstatus, &dsize,
1384 &datap);
1385
1386 if (res != 0)
1387 return;
1388
1389 if (0 == dsize)
1390 fprintf(fp, "(no response message, curiously)");
1391 else
1392 fprintf(fp, "%.*s", dsize, datap);
1393 }
1394
1395
1396 #ifdef UNUSED
1397 /*
1398 * radiostatus - print the radio status returned by the server
1399 */
1400 /*ARGSUSED*/
1401 static void
1402 radiostatus(
1403 struct parse *pcmd,
1404 FILE *fp
1405 )
1406 {
1407 char *datap;
1408 int res;
1409 int dsize;
1410 u_short rstatus;
1411
1412 res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
1413 &dsize, &datap);
1414
1415 if (res != 0)
1416 return;
1417
1418 if (numhosts > 1)
1419 (void) fprintf(fp, "server=%s ", currenthost);
1420 if (dsize == 0) {
1421 (void) fprintf(fp, "No radio status string returned\n");
1422 return;
1423 }
1424
1425 asciize(dsize, datap, fp);
1426 }
1427 #endif /* UNUSED */
1428
1429 /*
1430 * when - print how long its been since his last packet arrived
1431 */
1432 static long
1433 when(
1434 l_fp *ts,
1435 l_fp *rec,
1436 l_fp *reftime
1437 )
1438 {
1439 l_fp *lasttime;
1440
1441 if (rec->l_ui != 0)
1442 lasttime = rec;
1443 else if (reftime->l_ui != 0)
1444 lasttime = reftime;
1445 else
1446 return 0;
1447
1448 return (ts->l_ui - lasttime->l_ui);
1449 }
1450
1451
1452 /*
1453 * Pretty-print an interval into the given buffer, in a human-friendly format.
1454 */
1455 static char *
1456 prettyinterval(
1457 char *buf,
1458 size_t cb,
1459 long diff
1460 )
1461 {
1462 if (diff <= 0) {
1463 buf[0] = '-';
1464 buf[1] = 0;
1465 return buf;
1466 }
1467
1468 if (diff <= 2048) {
1469 snprintf(buf, cb, "%ld", diff);
1470 return buf;
1471 }
1472
1473 diff = (diff + 29) / 60;
1474 if (diff <= 300) {
1475 snprintf(buf, cb, "%ldm", diff);
1476 return buf;
1477 }
1478
1479 diff = (diff + 29) / 60;
1480 if (diff <= 96) {
1481 snprintf(buf, cb, "%ldh", diff);
1482 return buf;
1483 }
1484
1485 diff = (diff + 11) / 24;
1486 snprintf(buf, cb, "%ldd", diff);
1487 return buf;
1488 }
1489
1490 static char
1491 decodeaddrtype(
1492 sockaddr_u *sock
1493 )
1494 {
1495 char ch = '-';
1496 u_int32 dummy;
1497
1498 switch(AF(sock)) {
1499 case AF_INET:
1500 dummy = SRCADR(sock);
1501 ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' :
1502 ((dummy&0x000000ff)==0x000000ff) ? 'b' :
1503 ((dummy&0xffffffff)==0x7f000001) ? 'l' :
1504 ((dummy&0xffffffe0)==0x00000000) ? '-' :
1505 'u');
1506 break;
1507 case AF_INET6:
1508 if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock)))
1509 ch = 'm';
1510 else
1511 ch = 'u';
1512 break;
1513 default:
1514 ch = '-';
1515 break;
1516 }
1517 return ch;
1518 }
1519
1520 /*
1521 * A list of variables required by the peers command
1522 */
1523 struct varlist opeervarlist[] = {
1524 { "srcadr", 0 }, /* 0 */
1525 { "dstadr", 0 }, /* 1 */
1526 { "stratum", 0 }, /* 2 */
1527 { "hpoll", 0 }, /* 3 */
1528 { "ppoll", 0 }, /* 4 */
1529 { "reach", 0 }, /* 5 */
1530 { "delay", 0 }, /* 6 */
1531 { "offset", 0 }, /* 7 */
1532 { "jitter", 0 }, /* 8 */
1533 { "dispersion", 0 }, /* 9 */
1534 { "rec", 0 }, /* 10 */
1535 { "reftime", 0 }, /* 11 */
1536 { "srcport", 0 }, /* 12 */
1537 { "hmode", 0 }, /* 13 */
1538 { 0, 0 }
1539 };
1540
1541 struct varlist peervarlist[] = {
1542 { "srcadr", 0 }, /* 0 */
1543 { "refid", 0 }, /* 1 */
1544 { "stratum", 0 }, /* 2 */
1545 { "hpoll", 0 }, /* 3 */
1546 { "ppoll", 0 }, /* 4 */
1547 { "reach", 0 }, /* 5 */
1548 { "delay", 0 }, /* 6 */
1549 { "offset", 0 }, /* 7 */
1550 { "jitter", 0 }, /* 8 */
1551 { "dispersion", 0 }, /* 9 */
1552 { "rec", 0 }, /* 10 */
1553 { "reftime", 0 }, /* 11 */
1554 { "srcport", 0 }, /* 12 */
1555 { "hmode", 0 }, /* 13 */
1556 { "srchost", 0 }, /* 14 */
1557 { 0, 0 }
1558 };
1559
1560
1561 /*
1562 * Decode an incoming data buffer and print a line in the peer list
1563 */
1564 static int
1565 doprintpeers(
1566 struct varlist *pvl,
1567 int associd,
1568 int rstatus,
1569 int datalen,
1570 const char *data,
1571 FILE *fp,
1572 int af
1573 )
1574 {
1575 char *name;
1576 char *value = NULL;
1577 int c;
1578 int len;
1579 int have_srchost;
1580 int have_dstadr;
1581 int have_da_rid;
1582 int have_jitter;
1583 sockaddr_u srcadr;
1584 sockaddr_u dstadr;
1585 sockaddr_u dum_store;
1586 sockaddr_u refidadr;
1587 long hmode = 0;
1588 u_long srcport = 0;
1589 u_int32 u32;
1590 const char *dstadr_refid = "0.0.0.0";
1591 const char *serverlocal;
1592 size_t drlen;
1593 u_long stratum = 0;
1594 long ppoll = 0;
1595 long hpoll = 0;
1596 u_long reach = 0;
1597 l_fp estoffset;
1598 l_fp estdelay;
1599 l_fp estjitter;
1600 l_fp estdisp;
1601 l_fp reftime;
1602 l_fp rec;
1603 l_fp ts;
1604 u_long poll_sec;
1605 char type = '?';
1606 char whenbuf[8], pollbuf[8];
1607 char clock_name[LENHOSTNAME];
1608
1609 get_systime(&ts);
1610
1611 have_srchost = FALSE;
1612 have_dstadr = FALSE;
1613 have_da_rid = FALSE;
1614 have_jitter = FALSE;
1615 ZERO_SOCK(&srcadr);
1616 ZERO_SOCK(&dstadr);
1617 clock_name[0] = '\0';
1618 ZERO(estoffset);
1619 ZERO(estdelay);
1620 ZERO(estjitter);
1621 ZERO(estdisp);
1622
1623 while (nextvar(&datalen, &data, &name, &value)) {
1624 if (!strcmp("srcadr", name) ||
1625 !strcmp("peeradr", name)) {
1626 if (!decodenetnum(value, &srcadr))
1627 fprintf(stderr, "malformed %s=%s\n",
1628 name, value);
1629 } else if (!strcmp("srchost", name)) {
1630 if (pvl == peervarlist) {
1631 len = strlen(value);
1632 if (2 < len &&
1633 (size_t)len < sizeof(clock_name)) {
1634 /* strip quotes */
1635 value++;
1636 len -= 2;
1637 memcpy(clock_name, value, len);
1638 clock_name[len] = '\0';
1639 have_srchost = TRUE;
1640 }
1641 }
1642 } else if (!strcmp("dstadr", name)) {
1643 if (decodenetnum(value, &dum_store)) {
1644 type = decodeaddrtype(&dum_store);
1645 have_dstadr = TRUE;
1646 dstadr = dum_store;
1647 if (pvl == opeervarlist) {
1648 have_da_rid = TRUE;
1649 dstadr_refid = trunc_left(stoa(&dstadr), 15);
1650 }
1651 }
1652 } else if (!strcmp("hmode", name)) {
1653 decodeint(value, &hmode);
1654 } else if (!strcmp("refid", name)) {
1655 if (pvl == peervarlist) {
1656 have_da_rid = TRUE;
1657 drlen = strlen(value);
1658 if (0 == drlen) {
1659 dstadr_refid = "";
1660 } else if (drlen <= 4) {
1661 ZERO(u32);
1662 memcpy(&u32, value, drlen);
1663 dstadr_refid = refid_str(u32, 1);
1664 } else if (decodenetnum(value, &refidadr)) {
1665 if (SOCK_UNSPEC(&refidadr))
1666 dstadr_refid = "0.0.0.0";
1667 else if (ISREFCLOCKADR(&refidadr))
1668 dstadr_refid =
1669 refnumtoa(&refidadr);
1670 else
1671 dstadr_refid =
1672 stoa(&refidadr);
1673 } else {
1674 have_da_rid = FALSE;
1675 }
1676 }
1677 } else if (!strcmp("stratum", name)) {
1678 decodeuint(value, &stratum);
1679 } else if (!strcmp("hpoll", name)) {
1680 if (decodeint(value, &hpoll) && hpoll < 0)
1681 hpoll = NTP_MINPOLL;
1682 } else if (!strcmp("ppoll", name)) {
1683 if (decodeint(value, &ppoll) && ppoll < 0)
1684 ppoll = NTP_MINPOLL;
1685 } else if (!strcmp("reach", name)) {
1686 decodeuint(value, &reach);
1687 } else if (!strcmp("delay", name)) {
1688 decodetime(value, &estdelay);
1689 } else if (!strcmp("offset", name)) {
1690 decodetime(value, &estoffset);
1691 } else if (!strcmp("jitter", name)) {
1692 if (pvl == peervarlist &&
1693 decodetime(value, &estjitter))
1694 have_jitter = 1;
1695 } else if (!strcmp("rootdisp", name) ||
1696 !strcmp("dispersion", name)) {
1697 decodetime(value, &estdisp);
1698 } else if (!strcmp("rec", name)) {
1699 decodets(value, &rec);
1700 } else if (!strcmp("srcport", name) ||
1701 !strcmp("peerport", name)) {
1702 decodeuint(value, &srcport);
1703 } else if (!strcmp("reftime", name)) {
1704 if (!decodets(value, &reftime))
1705 L_CLR(&reftime);
1706 }
1707 }
1708
1709 /*
1710 * hmode gives the best guidance for the t column. If the response
1711 * did not include hmode we'll use the old decodeaddrtype() result.
1712 */
1713 switch (hmode) {
1714
1715 case MODE_BCLIENT:
1716 /* broadcastclient or multicastclient */
1717 type = 'b';
1718 break;
1719
1720 case MODE_BROADCAST:
1721 /* broadcast or multicast server */
1722 if (IS_MCAST(&srcadr))
1723 type = 'M';
1724 else
1725 type = 'B';
1726 break;
1727
1728 case MODE_CLIENT:
1729 if (ISREFCLOCKADR(&srcadr))
1730 type = 'l'; /* local refclock*/
1731 else if (SOCK_UNSPEC(&srcadr))
1732 type = 'p'; /* pool */
1733 else if (IS_MCAST(&srcadr))
1734 type = 'a'; /* manycastclient */
1735 else
1736 type = 'u'; /* unicast */
1737 break;
1738
1739 case MODE_ACTIVE:
1740 type = 's'; /* symmetric active */
1741 break; /* configured */
1742
1743 case MODE_PASSIVE:
1744 type = 'S'; /* symmetric passive */
1745 break; /* ephemeral */
1746 }
1747
1748 /*
1749 * Got everything, format the line
1750 */
1751 poll_sec = 1 << min(ppoll, hpoll);
1752 if (pktversion > NTP_OLDVERSION)
1753 c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
1754 else
1755 c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
1756 if (numhosts > 1) {
1757 if (peervarlist == pvl && have_dstadr) {
1758 serverlocal = nntohost_col(&dstadr,
1759 (size_t)min(LIB_BUFLENGTH - 1, maxhostlen),
1760 TRUE);
1761 } else {
1762 if (currenthostisnum)
1763 serverlocal = trunc_left(currenthost,
1764 maxhostlen);
1765 else
1766 serverlocal = currenthost;
1767 }
1768 fprintf(fp, "%-*s ", (int)maxhostlen, serverlocal);
1769 }
1770 if (AF_UNSPEC == af || AF(&srcadr) == af) {
1771 if (!have_srchost)
1772 strlcpy(clock_name, nntohost(&srcadr),
1773 sizeof(clock_name));
1774 if (wideremote && 15 < strlen(clock_name))
1775 fprintf(fp, "%c%s\n ", c, clock_name);
1776 else
1777 fprintf(fp, "%c%-15.15s ", c, clock_name);
1778 if (!have_da_rid) {
1779 drlen = 0;
1780 } else {
1781 drlen = strlen(dstadr_refid);
1782 makeascii(drlen, dstadr_refid, fp);
1783 }
1784 while (drlen++ < 15)
1785 fputc(' ', fp);
1786 fprintf(fp,
1787 " %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n",
1788 stratum, type,
1789 prettyinterval(whenbuf, sizeof(whenbuf),
1790 when(&ts, &rec, &reftime)),
1791 prettyinterval(pollbuf, sizeof(pollbuf),
1792 (int)poll_sec),
1793 reach, lfptoms(&estdelay, 3),
1794 lfptoms(&estoffset, 3),
1795 (have_jitter)
1796 ? lfptoms(&estjitter, 3)
1797 : lfptoms(&estdisp, 3));
1798 return (1);
1799 }
1800 else
1801 return(1);
1802 }
1803
1804
1805 /*
1806 * dogetpeers - given an association ID, read and print the spreadsheet
1807 * peer variables.
1808 */
1809 static int
1810 dogetpeers(
1811 struct varlist *pvl,
1812 associd_t associd,
1813 FILE *fp,
1814 int af
1815 )
1816 {
1817 const char *datap;
1818 int res;
1819 int dsize;
1820 u_short rstatus;
1821
1822 #ifdef notdef
1823 res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
1824 &dsize, &datap);
1825 #else
1826 /*
1827 * Damn fuzzballs
1828 */
1829 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
1830 &dsize, &datap);
1831 #endif
1832
1833 if (res != 0)
1834 return 0;
1835
1836 if (dsize == 0) {
1837 if (numhosts > 1)
1838 fprintf(stderr, "server=%s ", currenthost);
1839 fprintf(stderr,
1840 "***No information returned for association %u\n",
1841 associd);
1842 return 0;
1843 }
1844
1845 return doprintpeers(pvl, associd, (int)rstatus, dsize, datap,
1846 fp, af);
1847 }
1848
1849
1850 /*
1851 * peers - print a peer spreadsheet
1852 */
1853 static void
1854 dopeers(
1855 int showall,
1856 FILE *fp,
1857 int af
1858 )
1859 {
1860 u_int u;
1861 char fullname[LENHOSTNAME];
1862 sockaddr_u netnum;
1863 const char * name_or_num;
1864 size_t sl;
1865
1866 if (!dogetassoc(fp))
1867 return;
1868
1869 for (u = 0; u < numhosts; u++) {
1870 if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
1871 name_or_num = nntohost(&netnum);
1872 sl = strlen(name_or_num);
1873 maxhostlen = max(maxhostlen, sl);
1874 }
1875 }
1876 if (numhosts > 1)
1877 fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
1878 "server (local)");
1879 fprintf(fp,
1880 " remote refid st t when poll reach delay offset jitter\n");
1881 if (numhosts > 1)
1882 for (u = 0; u <= maxhostlen; u++)
1883 fprintf(fp, "=");
1884 fprintf(fp,
1885 "==============================================================================\n");
1886
1887 for (u = 0; u < numassoc; u++) {
1888 if (!showall &&
1889 !(CTL_PEER_STATVAL(assoc_cache[u].status)
1890 & (CTL_PST_CONFIG|CTL_PST_REACH))) {
1891 if (debug)
1892 fprintf(stderr, "eliding [%d]\n",
1893 (int)assoc_cache[u].assid);
1894 continue;
1895 }
1896 if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid,
1897 fp, af))
1898 return;
1899 }
1900 return;
1901 }
1902
1903
1904 /*
1905 * peers - print a peer spreadsheet
1906 */
1907 /*ARGSUSED*/
1908 static void
1909 peers(
1910 struct parse *pcmd,
1911 FILE *fp
1912 )
1913 {
1914 int af = 0;
1915
1916 if (pcmd->nargs == 1) {
1917 if (pcmd->argval->ival == 6)
1918 af = AF_INET6;
1919 else
1920 af = AF_INET;
1921 }
1922 dopeers(0, fp, af);
1923 }
1924
1925
1926 /*
1927 * lpeers - print a peer spreadsheet including all fuzzball peers
1928 */
1929 /*ARGSUSED*/
1930 static void
1931 lpeers(
1932 struct parse *pcmd,
1933 FILE *fp
1934 )
1935 {
1936 int af = 0;
1937
1938 if (pcmd->nargs == 1) {
1939 if (pcmd->argval->ival == 6)
1940 af = AF_INET6;
1941 else
1942 af = AF_INET;
1943 }
1944 dopeers(1, fp, af);
1945 }
1946
1947
1948 /*
1949 * opeers - print a peer spreadsheet
1950 */
1951 static void
1952 doopeers(
1953 int showall,
1954 FILE *fp,
1955 int af
1956 )
1957 {
1958 u_int i;
1959 char fullname[LENHOSTNAME];
1960 sockaddr_u netnum;
1961
1962 if (!dogetassoc(fp))
1963 return;
1964
1965 for (i = 0; i < numhosts; ++i) {
1966 if (getnetnum(chosts[i].name, &netnum, fullname, af))
1967 if (strlen(fullname) > maxhostlen)
1968 maxhostlen = strlen(fullname);
1969 }
1970 if (numhosts > 1)
1971 fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
1972 "server");
1973 fprintf(fp,
1974 " remote local st t when poll reach delay offset disp\n");
1975 if (numhosts > 1)
1976 for (i = 0; i <= maxhostlen; ++i)
1977 fprintf(fp, "=");
1978 fprintf(fp,
1979 "==============================================================================\n");
1980
1981 for (i = 0; i < numassoc; i++) {
1982 if (!showall &&
1983 !(CTL_PEER_STATVAL(assoc_cache[i].status) &
1984 (CTL_PST_CONFIG | CTL_PST_REACH)))
1985 continue;
1986 if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af))
1987 return;
1988 }
1989 return;
1990 }
1991
1992
1993 /*
1994 * opeers - print a peer spreadsheet the old way
1995 */
1996 /*ARGSUSED*/
1997 static void
1998 opeers(
1999 struct parse *pcmd,
2000 FILE *fp
2001 )
2002 {
2003 int af = 0;
2004
2005 if (pcmd->nargs == 1) {
2006 if (pcmd->argval->ival == 6)
2007 af = AF_INET6;
2008 else
2009 af = AF_INET;
2010 }
2011 doopeers(0, fp, af);
2012 }
2013
2014
2015 /*
2016 * lopeers - print a peer spreadsheet including all fuzzball peers
2017 */
2018 /*ARGSUSED*/
2019 static void
2020 lopeers(
2021 struct parse *pcmd,
2022 FILE *fp
2023 )
2024 {
2025 int af = 0;
2026
2027 if (pcmd->nargs == 1) {
2028 if (pcmd->argval->ival == 6)
2029 af = AF_INET6;
2030 else
2031 af = AF_INET;
2032 }
2033 doopeers(1, fp, af);
2034 }
2035
2036
2037 /*
2038 * config - send a configuration command to a remote host
2039 */
2040 static void
2041 config (
2042 struct parse *pcmd,
2043 FILE *fp
2044 )
2045 {
2046 const char *cfgcmd;
2047 u_short rstatus;
2048 int rsize;
2049 const char *rdata;
2050 char *resp;
2051 int res;
2052 int col;
2053 int i;
2054
2055 cfgcmd = pcmd->argval[0].string;
2056
2057 if (debug > 2)
2058 fprintf(stderr,
2059 "In Config\n"
2060 "Keyword = %s\n"
2061 "Command = %s\n", pcmd->keyword, cfgcmd);
2062
2063 res = doquery(CTL_OP_CONFIGURE, 0, 1, strlen(cfgcmd), cfgcmd,
2064 &rstatus, &rsize, &rdata);
2065
2066 if (res != 0)
2067 return;
2068
2069 if (rsize > 0 && '\n' == rdata[rsize - 1])
2070 rsize--;
2071
2072 resp = emalloc(rsize + 1);
2073 memcpy(resp, rdata, rsize);
2074 resp[rsize] = '\0';
2075
2076 col = -1;
2077 if (1 == sscanf(resp, "column %d syntax error", &col)
2078 && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) {
2079 if (interactive) {
2080 printf("______"); /* "ntpq> " */
2081 printf("________"); /* ":config " */
2082 } else
2083 printf("%s\n", cfgcmd);
2084 for (i = 1; i < col; i++)
2085 putchar('_');
2086 printf("^\n");
2087 }
2088 printf("%s\n", resp);
2089 free(resp);
2090 }
2091
2092
2093 /*
2094 * config_from_file - remotely configure an ntpd daemon using the
2095 * specified configuration file
2096 * SK: This function is a kludge at best and is full of bad design
2097 * bugs:
2098 * 1. ntpq uses UDP, which means that there is no guarantee of in-order,
2099 * error-free delivery.
2100 * 2. The maximum length of a packet is constrained, and as a result, the
2101 * maximum length of a line in a configuration file is constrained.
2102 * Longer lines will lead to unpredictable results.
2103 * 3. Since this function is sending a line at a time, we can't update
2104 * the control key through the configuration file (YUCK!!)
2105 */
2106 static void
2107 config_from_file (
2108 struct parse *pcmd,
2109 FILE *fp
2110 )
2111 {
2112 u_short rstatus;
2113 int rsize;
2114 const char *rdata;
2115 int res;
2116 FILE *config_fd;
2117 char config_cmd[MAXLINE];
2118 size_t config_len;
2119 int i;
2120 int retry_limit;
2121
2122 if (debug > 2)
2123 fprintf(stderr,
2124 "In Config\n"
2125 "Keyword = %s\n"
2126 "Filename = %s\n", pcmd->keyword,
2127 pcmd->argval[0].string);
2128
2129 config_fd = fopen(pcmd->argval[0].string, "r");
2130 if (NULL == config_fd) {
2131 printf("ERROR!! Couldn't open file: %s\n",
2132 pcmd->argval[0].string);
2133 return;
2134 }
2135
2136 printf("Sending configuration file, one line at a time.\n");
2137 i = 0;
2138 while (fgets(config_cmd, MAXLINE, config_fd) != NULL) {
2139 config_len = strlen(config_cmd);
2140 /* ensure even the last line has newline, if possible */
2141 if (config_len > 0 &&
2142 config_len + 2 < sizeof(config_cmd) &&
2143 '\n' != config_cmd[config_len - 1])
2144 config_cmd[config_len++] = '\n';
2145 ++i;
2146 retry_limit = 2;
2147 do
2148 res = doquery(CTL_OP_CONFIGURE, 0, 1,
2149 strlen(config_cmd), config_cmd,
2150 &rstatus, &rsize, &rdata);
2151 while (res != 0 && retry_limit--);
2152 if (res != 0) {
2153 printf("Line No: %d query failed: %s", i,
2154 config_cmd);
2155 printf("Subsequent lines not sent.\n");
2156 fclose(config_fd);
2157 return;
2158 }
2159
2160 if (rsize > 0 && '\n' == rdata[rsize - 1])
2161 rsize--;
2162 if (rsize > 0 && '\r' == rdata[rsize - 1])
2163 rsize--;
2164 printf("Line No: %d %.*s: %s", i, rsize, rdata,
2165 config_cmd);
2166 }
2167 printf("Done sending file\n");
2168 fclose(config_fd);
2169 }
2170
2171
2172 static int
2173 fetch_nonce(
2174 char * nonce,
2175 size_t cb_nonce
2176 )
2177 {
2178 const char nonce_eq[] = "nonce=";
2179 int qres;
2180 u_short rstatus;
2181 int rsize;
2182 const char * rdata;
2183 int chars;
2184
2185 /*
2186 * Retrieve a nonce specific to this client to demonstrate to
2187 * ntpd that we're capable of receiving responses to our source
2188 * IP address, and thereby unlikely to be forging the source.
2189 */
2190 qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus,
2191 &rsize, &rdata);
2192 if (qres) {
2193 fprintf(stderr, "nonce request failed\n");
2194 return FALSE;
2195 }
2196
2197 if (rsize <= sizeof(nonce_eq) - 1 ||
2198 strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) {
2199 fprintf(stderr, "unexpected nonce response format: %.*s\n",
2200 rsize, rdata);
2201 return FALSE;
2202 }
2203 chars = rsize - (sizeof(nonce_eq) - 1);
2204 if (chars >= (int)cb_nonce)
2205 return FALSE;
2206 memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars);
2207 nonce[chars] = '\0';
2208 while (chars > 0 &&
2209 ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) {
2210 chars--;
2211 nonce[chars] = '\0';
2212 }
2213
2214 return TRUE;
2215 }
2216
2217
2218 /*
2219 * add_mru Add and entry to mru list, hash table, and allocate
2220 * and return a replacement.
2221 * This is a helper for collect_mru_list().
2222 */
2223 static mru *
2224 add_mru(
2225 mru *add
2226 )
2227 {
2228 u_short hash;
2229 mru *mon;
2230 mru *unlinked;
2231
2232
2233 hash = NTP_HASH_ADDR(&add->addr);
2234 /* see if we have it among previously received entries */
2235 for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink)
2236 if (SOCK_EQ(&mon->addr, &add->addr))
2237 break;
2238 if (mon != NULL) {
2239 if (!L_ISGEQ(&add->first, &mon->first)) {
2240 fprintf(stderr,
2241 "add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n",
2242 sptoa(&add->addr), add->last.l_ui,
2243 add->last.l_uf, mon->last.l_ui,
2244 mon->last.l_uf);
2245 exit(1);
2246 }
2247 UNLINK_DLIST(mon, mlink);
2248 UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru);
2249 NTP_INSIST(unlinked == mon);
2250 mru_dupes++;
2251 TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui,
2252 mon->last.l_uf));
2253 }
2254 LINK_DLIST(mru_list, add, mlink);
2255 LINK_SLIST(hash_table[hash], add, hlink);
2256 TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
2257 add->last.l_ui, add->last.l_uf, add->count,
2258 (int)add->mode, (int)add->ver, (u_int)add->rs,
2259 add->first.l_ui, add->first.l_uf, sptoa(&add->addr)));
2260 /* if we didn't update an existing entry, alloc replacement */
2261 if (NULL == mon) {
2262 mon = emalloc(sizeof(*mon));
2263 mru_count++;
2264 }
2265 ZERO(*mon);
2266
2267 return mon;
2268 }
2269
2270
2271 /* MGOT macro is specific to collect_mru_list() */
2272 #define MGOT(bit) \
2273 do { \
2274 got |= (bit); \
2275 if (MRU_GOT_ALL == got) { \
2276 got = 0; \
2277 mon = add_mru(mon); \
2278 ci++; \
2279 } \
2280 } while (0)
2281
2282
2283 void
2284 mrulist_ctrl_c_hook(void)
2285 {
2286 mrulist_interrupted = TRUE;
2287 }
2288
2289
2290 static int
2291 collect_mru_list(
2292 const char * parms,
2293 l_fp * pnow
2294 )
2295 {
2296 const u_int sleep_msecs = 5;
2297 static int ntpd_row_limit = MRU_ROW_LIMIT;
2298 int c_mru_l_rc; /* this function's return code */
2299 u_char got; /* MRU_GOT_* bits */
2300 time_t next_report;
2301 size_t cb;
2302 mru *mon;
2303 mru *head;
2304 mru *recent;
2305 int list_complete;
2306 char nonce[128];
2307 char buf[128];
2308 char req_buf[CTL_MAX_DATA_LEN];
2309 char *req;
2310 char *req_end;
2311 int chars;
2312 int qres;
2313 u_short rstatus;
2314 int rsize;
2315 const char *rdata;
2316 int limit;
2317 int frags;
2318 int cap_frags;
2319 char *tag;
2320 char *val;
2321 int si; /* server index in response */
2322 int ci; /* client (our) index for validation */
2323 int ri; /* request index (.# suffix) */
2324 int mv;
2325 l_fp newest;
2326 l_fp last_older;
2327 sockaddr_u addr_older;
2328 int have_now;
2329 int have_addr_older;
2330 int have_last_older;
2331 u_int restarted_count;
2332 u_int nonce_uses;
2333 u_short hash;
2334 mru *unlinked;
2335
2336 if (!fetch_nonce(nonce, sizeof(nonce)))
2337 return FALSE;
2338
2339 nonce_uses = 0;
2340 restarted_count = 0;
2341 mru_count = 0;
2342 INIT_DLIST(mru_list, mlink);
2343 cb = NTP_HASH_SIZE * sizeof(*hash_table);
2344 NTP_INSIST(NULL == hash_table);
2345 hash_table = emalloc_zero(cb);
2346
2347 c_mru_l_rc = FALSE;
2348 list_complete = FALSE;
2349 have_now = FALSE;
2350 cap_frags = TRUE;
2351 got = 0;
2352 ri = 0;
2353 cb = sizeof(*mon);
2354 mon = emalloc_zero(cb);
2355 ZERO(*pnow);
2356 ZERO(last_older);
2357 mrulist_interrupted = FALSE;
2358 set_ctrl_c_hook(&mrulist_ctrl_c_hook);
2359 fprintf(stderr,
2360 "Ctrl-C will stop MRU retrieval and display partial results.\n");
2361 fflush(stderr);
2362 next_report = time(NULL) + MRU_REPORT_SECS;
2363
2364 limit = min(3 * MAXFRAGS, ntpd_row_limit);
2365 frags = MAXFRAGS;
2366 snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s",
2367 nonce, frags, parms);
2368 nonce_uses++;
2369
2370 while (TRUE) {
2371 if (debug)
2372 fprintf(stderr, "READ_MRU parms: %s\n", req_buf);
2373
2374 qres = doqueryex(CTL_OP_READ_MRU, 0, 0, strlen(req_buf),
2375 req_buf, &rstatus, &rsize, &rdata, TRUE);
2376
2377 if (CERR_UNKNOWNVAR == qres && ri > 0) {
2378 /*
2379 * None of the supplied prior entries match, so
2380 * toss them from our list and try again.
2381 */
2382 if (debug)
2383 fprintf(stderr,
2384 "no overlap between %d prior entries and server MRU list\n",
2385 ri);
2386 while (ri--) {
2387 recent = HEAD_DLIST(mru_list, mlink);
2388 NTP_INSIST(recent != NULL);
2389 if (debug)
2390 fprintf(stderr,
2391 "tossing prior entry %s to resync\n",
2392 sptoa(&recent->addr));
2393 UNLINK_DLIST(recent, mlink);
2394 hash = NTP_HASH_ADDR(&recent->addr);
2395 UNLINK_SLIST(unlinked, hash_table[hash],
2396 recent, hlink, mru);
2397 NTP_INSIST(unlinked == recent);
2398 free(recent);
2399 mru_count--;
2400 }
2401 if (NULL == HEAD_DLIST(mru_list, mlink)) {
2402 restarted_count++;
2403 if (restarted_count > 8) {
2404 fprintf(stderr,
2405 "Giving up after 8 restarts from the beginning.\n"
2406 "With high-traffic NTP servers, this can occur if the\n"
2407 "MRU list is limited to less than about 16 seconds' of\n"
2408 "entries. See the 'mru' ntp.conf directive to adjust.\n");
2409 goto cleanup_return;
2410 }
2411 if (debug)
2412 fprintf(stderr,
2413 "---> Restarting from the beginning, retry #%u\n",
2414 restarted_count);
2415 }
2416 } else if (CERR_UNKNOWNVAR == qres) {
2417 fprintf(stderr,
2418 "CERR_UNKNOWNVAR from ntpd but no priors given.\n");
2419 goto cleanup_return;
2420 } else if (CERR_BADVALUE == qres) {
2421 if (cap_frags) {
2422 cap_frags = FALSE;
2423 if (debug)
2424 fprintf(stderr,
2425 "Reverted to row limit from fragments limit.\n");
2426 } else {
2427 /* ntpd has lower cap on row limit */
2428 ntpd_row_limit--;
2429 limit = min(limit, ntpd_row_limit);
2430 if (debug)
2431 fprintf(stderr,
2432 "Row limit reduced to %d following CERR_BADVALUE.\n",
2433 limit);
2434 }
2435 } else if (ERR_INCOMPLETE == qres ||
2436 ERR_TIMEOUT == qres) {
2437 /*
2438 * Reduce the number of rows/frags requested by
2439 * half to recover from lost response fragments.
2440 */
2441 if (cap_frags) {
2442 frags = max(2, frags / 2);
2443 if (debug)
2444 fprintf(stderr,
2445 "Frag limit reduced to %d following incomplete response.\n",
2446 frags);
2447 } else {
2448 limit = max(2, limit / 2);
2449 if (debug)
2450 fprintf(stderr,
2451 "Row limit reduced to %d following incomplete response.\n",
2452 limit);
2453 }
2454 } else if (qres) {
2455 show_error_msg(qres, 0);
2456 goto cleanup_return;
2457 }
2458 /*
2459 * This is a cheap cop-out implementation of rawmode
2460 * output for mrulist. A better approach would be to
2461 * dump similar output after the list is collected by
2462 * ntpq with a continuous sequence of indexes. This
2463 * cheap approach has indexes resetting to zero for
2464 * each query/response, and duplicates are not
2465 * coalesced.
2466 */
2467 if (!qres && rawmode)
2468 printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout);
2469 ci = 0;
2470 have_addr_older = FALSE;
2471 have_last_older = FALSE;
2472 while (!qres && nextvar(&rsize, &rdata, &tag, &val)) {
2473 if (debug > 1)
2474 fprintf(stderr, "nextvar gave: %s = %s\n",
2475 tag, val);
2476 switch(tag[0]) {
2477
2478 case 'a':
2479 if (!strcmp(tag, "addr.older")) {
2480 if (!have_last_older) {
2481 fprintf(stderr,
2482 "addr.older %s before last.older\n",
2483 val);
2484 goto cleanup_return;
2485 }
2486 if (!decodenetnum(val, &addr_older)) {
2487 fprintf(stderr,
2488 "addr.older %s garbled\n",
2489 val);
2490 goto cleanup_return;
2491 }
2492 hash = NTP_HASH_ADDR(&addr_older);
2493 for (recent = hash_table[hash];
2494 recent != NULL;
2495 recent = recent->hlink)
2496 if (ADDR_PORT_EQ(
2497 &addr_older,
2498 &recent->addr))
2499 break;
2500 if (NULL == recent) {
2501 fprintf(stderr,
2502 "addr.older %s not in hash table\n",
2503 val);
2504 goto cleanup_return;
2505 }
2506 if (!L_ISEQU(&last_older,
2507 &recent->last)) {
2508 fprintf(stderr,
2509 "last.older %08x.%08x mismatches %08x.%08x expected.\n",
2510 last_older.l_ui,
2511 last_older.l_uf,
2512 recent->last.l_ui,
2513 recent->last.l_uf);
2514 goto cleanup_return;
2515 }
2516 have_addr_older = TRUE;
2517 } else if (1 != sscanf(tag, "addr.%d", &si)
2518 || si != ci)
2519 goto nomatch;
2520 else if (decodenetnum(val, &mon->addr))
2521 MGOT(MRU_GOT_ADDR);
2522 break;
2523
2524 case 'l':
2525 if (!strcmp(tag, "last.older")) {
2526 if ('0' != val[0] ||
2527 'x' != val[1] ||
2528 !hextolfp(val + 2, &last_older)) {
2529 fprintf(stderr,
2530 "last.older %s garbled\n",
2531 val);
2532 goto cleanup_return;
2533 }
2534 have_last_older = TRUE;
2535 } else if (!strcmp(tag, "last.newest")) {
2536 if (0 != got) {
2537 fprintf(stderr,
2538 "last.newest %s before complete row, got = 0x%x\n",
2539 val, (u_int)got);
2540 goto cleanup_return;
2541 }
2542 if (!have_now) {
2543 fprintf(stderr,
2544 "last.newest %s before now=\n",
2545 val);
2546 goto cleanup_return;
2547 }
2548 head = HEAD_DLIST(mru_list, mlink);
2549 if (NULL != head) {
2550 if ('0' != val[0] ||
2551 'x' != val[1] ||
2552 !hextolfp(val + 2, &newest) ||
2553 !L_ISEQU(&newest,
2554 &head->last)) {
2555 fprintf(stderr,
2556 "last.newest %s mismatches %08x.%08x",
2557 val,
2558 head->last.l_ui,
2559 head->last.l_uf);
2560 goto cleanup_return;
2561 }
2562 }
2563 list_complete = TRUE;
2564 } else if (1 != sscanf(tag, "last.%d", &si) ||
2565 si != ci || '0' != val[0] ||
2566 'x' != val[1] ||
2567 !hextolfp(val + 2, &mon->last)) {
2568 goto nomatch;
2569 } else {
2570 MGOT(MRU_GOT_LAST);
2571 /*
2572 * allow interrupted retrieval,
2573 * using most recent retrieved
2574 * entry's last seen timestamp
2575 * as the end of operation.
2576 */
2577 *pnow = mon->last;
2578 }
2579 break;
2580
2581 case 'f':
2582 if (1 != sscanf(tag, "first.%d", &si) ||
2583 si != ci || '0' != val[0] ||
2584 'x' != val[1] ||
2585 !hextolfp(val + 2, &mon->first))
2586 goto nomatch;
2587 MGOT(MRU_GOT_FIRST);
2588 break;
2589
2590 case 'n':
2591 if (!strcmp(tag, "nonce")) {
2592 strlcpy(nonce, val, sizeof(nonce));
2593 nonce_uses = 0;
2594 break; /* case */
2595 } else if (strcmp(tag, "now") ||
2596 '0' != val[0] ||
2597 'x' != val[1] ||
2598 !hextolfp(val + 2, pnow))
2599 goto nomatch;
2600 have_now = TRUE;
2601 break;
2602
2603 case 'c':
2604 if (1 != sscanf(tag, "ct.%d", &si) ||
2605 si != ci ||
2606 1 != sscanf(val, "%d", &mon->count)
2607 || mon->count < 1)
2608 goto nomatch;
2609 MGOT(MRU_GOT_COUNT);
2610 break;
2611
2612 case 'm':
2613 if (1 != sscanf(tag, "mv.%d", &si) ||
2614 si != ci ||
2615 1 != sscanf(val, "%d", &mv))
2616 goto nomatch;
2617 mon->mode = PKT_MODE(mv);
2618 mon->ver = PKT_VERSION(mv);
2619 MGOT(MRU_GOT_MV);
2620 break;
2621
2622 case 'r':
2623 if (1 != sscanf(tag, "rs.%d", &si) ||
2624 si != ci ||
2625 1 != sscanf(val, "0x%hx", &mon->rs))
2626 goto nomatch;
2627 MGOT(MRU_GOT_RS);
2628 break;
2629
2630 default:
2631 nomatch:
2632 /* empty stmt */ ;
2633 /* ignore unknown tags */
2634 }
2635 }
2636 if (have_now)
2637 list_complete = TRUE;
2638 if (list_complete) {
2639 NTP_INSIST(0 == ri || have_addr_older);
2640 }
2641 if (mrulist_interrupted) {
2642 printf("mrulist retrieval interrupted by operator.\n"
2643 "Displaying partial client list.\n");
2644 fflush(stdout);
2645 }
2646 if (list_complete || mrulist_interrupted) {
2647 fprintf(stderr,
2648 "\rRetrieved %u unique MRU entries and %u updates.\n",
2649 mru_count, mru_dupes);
2650 fflush(stderr);
2651 break;
2652 }
2653 if (time(NULL) >= next_report) {
2654 next_report += MRU_REPORT_SECS;
2655 fprintf(stderr, "\r%u (%u updates) ", mru_count,
2656 mru_dupes);
2657 fflush(stderr);
2658 }
2659
2660 /*
2661 * Snooze for a bit between queries to let ntpd catch
2662 * up with other duties.
2663 */
2664 #ifdef SYS_WINNT
2665 Sleep(sleep_msecs);
2666 #elif !defined(HAVE_NANOSLEEP)
2667 sleep((sleep_msecs / 1000) + 1);
2668 #else
2669 {
2670 struct timespec interv = { 0,
2671 1000 * sleep_msecs };
2672 nanosleep(&interv, NULL);
2673 }
2674 #endif
2675 /*
2676 * If there were no errors, increase the number of rows
2677 * to a maximum of 3 * MAXFRAGS (the most packets ntpq
2678 * can handle in one response), on the assumption that
2679 * no less than 3 rows fit in each packet, capped at
2680 * our best guess at the server's row limit.
2681 */
2682 if (!qres) {
2683 if (cap_frags) {
2684 frags = min(MAXFRAGS, frags + 1);
2685 } else {
2686 limit = min3(3 * MAXFRAGS,
2687 ntpd_row_limit,
2688 max(limit + 1,
2689 limit * 33 / 32));
2690 }
2691 }
2692 /*
2693 * prepare next query with as many address and last-seen
2694 * timestamps as will fit in a single packet.
2695 */
2696 req = req_buf;
2697 req_end = req_buf + sizeof(req_buf);
2698 #define REQ_ROOM (req_end - req)
2699 snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce,
2700 (cap_frags)
2701 ? "frags"
2702 : "limit",
2703 (cap_frags)
2704 ? frags
2705 : limit,
2706 parms);
2707 req += strlen(req);
2708 nonce_uses++;
2709 if (nonce_uses >= 4) {
2710 if (!fetch_nonce(nonce, sizeof(nonce)))
2711 goto cleanup_return;
2712 nonce_uses = 0;
2713 }
2714
2715
2716 for (ri = 0, recent = HEAD_DLIST(mru_list, mlink);
2717 recent != NULL;
2718 ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) {
2719
2720 snprintf(buf, sizeof(buf),
2721 ", addr.%d=%s, last.%d=0x%08x.%08x",
2722 ri, sptoa(&recent->addr), ri,
2723 recent->last.l_ui, recent->last.l_uf);
2724 chars = strlen(buf);
2725 if (REQ_ROOM - chars < 1)
2726 break;
2727 memcpy(req, buf, chars + 1);
2728 req += chars;
2729 }
2730 }
2731
2732 set_ctrl_c_hook(NULL);
2733 c_mru_l_rc = TRUE;
2734 goto retain_hash_table;
2735
2736 cleanup_return:
2737 free(hash_table);
2738 hash_table = NULL;
2739
2740 retain_hash_table:
2741 if (mon != NULL)
2742 free(mon);
2743
2744 return c_mru_l_rc;
2745 }
2746
2747
2748 /*
2749 * qcmp_mru_addr - sort MRU entries by remote address.
2750 *
2751 * All IPv4 addresses sort before any IPv6, addresses are sorted by
2752 * value within address family.
2753 */
2754 static int
2755 qcmp_mru_addr(
2756 const void *v1,
2757 const void *v2
2758 )
2759 {
2760 const mru * const * ppm1 = v1;
2761 const mru * const * ppm2 = v2;
2762 const mru * pm1;
2763 const mru * pm2;
2764 u_short af1;
2765 u_short af2;
2766 size_t cmplen;
2767 size_t addr_off;
2768
2769 pm1 = *ppm1;
2770 pm2 = *ppm2;
2771
2772 af1 = AF(&pm1->addr);
2773 af2 = AF(&pm2->addr);
2774
2775 if (af1 != af2)
2776 return (AF_INET == af1)
2777 ? -1
2778 : 1;
2779
2780 cmplen = SIZEOF_INADDR(af1);
2781 addr_off = (AF_INET == af1)
2782 ? offsetof(struct sockaddr_in, sin_addr)
2783 : offsetof(struct sockaddr_in6, sin6_addr);
2784
2785 return memcmp((const char *)&pm1->addr + addr_off,
2786 (const char *)&pm2->addr + addr_off,
2787 cmplen);
2788 }
2789
2790
2791 static int
2792 qcmp_mru_r_addr(
2793 const void *v1,
2794 const void *v2
2795 )
2796 {
2797 return -qcmp_mru_addr(v1, v2);
2798 }
2799
2800
2801 /*
2802 * qcmp_mru_count - sort MRU entries by times seen (hit count).
2803 */
2804 static int
2805 qcmp_mru_count(
2806 const void *v1,
2807 const void *v2
2808 )
2809 {
2810 const mru * const * ppm1 = v1;
2811 const mru * const * ppm2 = v2;
2812 const mru * pm1;
2813 const mru * pm2;
2814
2815 pm1 = *ppm1;
2816 pm2 = *ppm2;
2817
2818 return (pm1->count < pm2->count)
2819 ? -1
2820 : ((pm1->count == pm2->count)
2821 ? 0
2822 : 1);
2823 }
2824
2825
2826 static int
2827 qcmp_mru_r_count(
2828 const void *v1,
2829 const void *v2
2830 )
2831 {
2832 return -qcmp_mru_count(v1, v2);
2833 }
2834
2835
2836 /*
2837 * qcmp_mru_avgint - sort MRU entries by average interval.
2838 */
2839 static int
2840 qcmp_mru_avgint(
2841 const void *v1,
2842 const void *v2
2843 )
2844 {
2845 const mru * const * ppm1 = v1;
2846 const mru * const * ppm2 = v2;
2847 const mru * pm1;
2848 const mru * pm2;
2849 l_fp interval;
2850 double avg1;
2851 double avg2;
2852
2853 pm1 = *ppm1;
2854 pm2 = *ppm2;
2855
2856 interval = pm1->last;
2857 L_SUB(&interval, &pm1->first);
2858 LFPTOD(&interval, avg1);
2859 avg1 /= pm1->count;
2860
2861 interval = pm2->last;
2862 L_SUB(&interval, &pm2->first);
2863 LFPTOD(&interval, avg2);
2864 avg2 /= pm2->count;
2865
2866 if (avg1 < avg2)
2867 return -1;
2868 else if (avg1 > avg2)
2869 return 1;
2870
2871 /* secondary sort on lstint - rarely tested */
2872 if (L_ISEQU(&pm1->last, &pm2->last))
2873 return 0;
2874 else if (L_ISGEQ(&pm1->last, &pm2->last))
2875 return -1;
2876 else
2877 return 1;
2878 }
2879
2880
2881 static int
2882 qcmp_mru_r_avgint(
2883 const void *v1,
2884 const void *v2
2885 )
2886 {
2887 return -qcmp_mru_avgint(v1, v2);
2888 }
2889
2890
2891 /*
2892 * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most
2893 * Recently Used (seen) remote address list from ntpd.
2894 *
2895 * Similar to ntpdc's monlist command, but not limited to a single
2896 * request/response, and thereby not limited to a few hundred remote
2897 * addresses.
2898 *
2899 * See ntpd/ntp_control.c read_mru_list() for comments on the way
2900 * CTL_OP_READ_MRU is designed to be used.
2901 *
2902 * mrulist intentionally differs from monlist in the way the avgint
2903 * column is calculated. monlist includes the time after the last
2904 * packet from the client until the monlist query time in the average,
2905 * while mrulist excludes it. That is, monlist's average interval grows
2906 * over time for remote addresses not heard from in some time, while it
2907 * remains unchanged in mrulist. This also affects the avgint value for
2908 * entries representing a single packet, with identical first and last
2909 * timestamps. mrulist shows 0 avgint, monlist shows a value identical
2910 * to lstint.
2911 */
2912 static void
2913 mrulist(
2914 struct parse * pcmd,
2915 FILE * fp
2916 )
2917 {
2918 const char mincount_eq[] = "mincount=";
2919 const char resall_eq[] = "resall=";
2920 const char resany_eq[] = "resany=";
2921 const char maxlstint_eq[] = "maxlstint=";
2922 const char laddr_eq[] = "laddr=";
2923 const char sort_eq[] = "sort=";
2924 mru_sort_order order;
2925 size_t n;
2926 char parms_buf[128];
2927 char buf[24];
2928 char *parms;
2929 const char *arg;
2930 size_t cb;
2931 mru **sorted;
2932 mru **ppentry;
2933 mru *recent;
2934 l_fp now;
2935 l_fp interval;
2936 double favgint;
2937 double flstint;
2938 int avgint;
2939 int lstint;
2940 int i;
2941
2942 order = MRUSORT_DEF;
2943 parms_buf[0] = '\0';
2944 parms = parms_buf;
2945 for (i = 0; i < pcmd->nargs; i++) {
2946 arg = pcmd->argval[i].string;
2947 if (arg != NULL) {
2948 cb = strlen(arg) + 1;
2949 if ((!strncmp(resall_eq, arg, sizeof(resall_eq)
2950 - 1) || !strncmp(resany_eq, arg,
2951 sizeof(resany_eq) - 1) || !strncmp(
2952 mincount_eq, arg, sizeof(mincount_eq) - 1)
2953 || !strncmp(laddr_eq, arg, sizeof(laddr_eq)
2954 - 1) || !strncmp(maxlstint_eq, arg,
2955 sizeof(laddr_eq) - 1)) && parms + cb + 2 <=
2956 parms_buf + sizeof(parms_buf)) {
2957 /* these are passed intact to ntpd */
2958 memcpy(parms, ", ", 2);
2959 parms += 2;
2960 memcpy(parms, arg, cb);
2961 parms += cb - 1;
2962 } else if (!strncmp(sort_eq, arg,
2963 sizeof(sort_eq) - 1)) {
2964 arg += sizeof(sort_eq) - 1;
2965 for (n = 0;
2966 n < COUNTOF(mru_sort_keywords);
2967 n++)
2968 if (!strcmp(mru_sort_keywords[n],
2969 arg))
2970 break;
2971 if (n < COUNTOF(mru_sort_keywords))
2972 order = n;
2973 } else if (!strcmp("limited", arg) ||
2974 !strcmp("kod", arg)) {
2975 /* transform to resany=... */
2976 snprintf(buf, sizeof(buf),
2977 ", resany=0x%x",
2978 ('k' == arg[0])
2979 ? RES_KOD
2980 : RES_LIMITED);
2981 cb = 1 + strlen(buf);
2982 if (parms + cb <
2983 parms_buf + sizeof(parms_buf)) {
2984 memcpy(parms, buf, cb);
2985 parms += cb - 1;
2986 }
2987 } else
2988 fprintf(stderr,
2989 "ignoring unrecognized mrulist parameter: %s\n",
2990 arg);
2991 }
2992 }
2993 parms = parms_buf;
2994
2995 if (!collect_mru_list(parms, &now))
2996 return;
2997
2998 /* display the results */
2999 if (rawmode)
3000 goto cleanup_return;
3001
3002 /* construct an array of entry pointers in default order */
3003 sorted = emalloc(mru_count * sizeof(*sorted));
3004 ppentry = sorted;
3005 if (MRUSORT_R_DEF != order) {
3006 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3007 NTP_INSIST(ppentry < sorted + mru_count);
3008 *ppentry = recent;
3009 ppentry++;
3010 ITER_DLIST_END()
3011 } else {
3012 REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3013 NTP_INSIST(ppentry < sorted + mru_count);
3014 *ppentry = recent;
3015 ppentry++;
3016 REV_ITER_DLIST_END()
3017 }
3018
3019 if (ppentry - sorted != (int)mru_count) {
3020 fprintf(stderr,
3021 "mru_count %u should match MRU list depth %ld.\n",
3022 mru_count, (long)(ppentry - sorted));
3023 free(sorted);
3024 goto cleanup_return;
3025 }
3026
3027 /* re-sort sorted[] if not default or reverse default */
3028 if (MRUSORT_R_DEF < order)
3029 qsort(sorted, mru_count, sizeof(sorted[0]),
3030 mru_qcmp_table[order]);
3031
3032 printf( "lstint avgint rstr r m v count rport remote address\n"
3033 "==============================================================================\n");
3034 /* '=' x 78 */
3035 for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) {
3036 recent = *ppentry;
3037 interval = now;
3038 L_SUB(&interval, &recent->last);
3039 LFPTOD(&interval, flstint);
3040 lstint = (int)(flstint + 0.5);
3041 interval = recent->last;
3042 L_SUB(&interval, &recent->first);
3043 LFPTOD(&interval, favgint);
3044 favgint /= recent->count;
3045 avgint = (int)(favgint + 0.5);
3046 fprintf(fp, "%6d %6d %4hx %c %d %d %6d %5hu %s\n",
3047 lstint, avgint, recent->rs,
3048 (RES_KOD & recent->rs)
3049 ? 'K'
3050 : (RES_LIMITED & recent->rs)
3051 ? 'L'
3052 : '.',
3053 (int)recent->mode, (int)recent->ver,
3054 recent->count, SRCPORT(&recent->addr),
3055 nntohost(&recent->addr));
3056 if (showhostnames)
3057 fflush(fp);
3058 }
3059 fflush(fp);
3060 if (debug) {
3061 fprintf(stderr,
3062 "--- completed, freeing sorted[] pointers\n");
3063 fflush(stderr);
3064 }
3065 free(sorted);
3066
3067 cleanup_return:
3068 if (debug) {
3069 fprintf(stderr, "... freeing MRU entries\n");
3070 fflush(stderr);
3071 }
3072 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3073 free(recent);
3074 ITER_DLIST_END()
3075 if (debug) {
3076 fprintf(stderr, "... freeing hash_table[]\n");
3077 fflush(stderr);
3078 }
3079 free(hash_table);
3080 hash_table = NULL;
3081 INIT_DLIST(mru_list, mlink);
3082 }
3083
3084
3085 /*
3086 * validate_ifnum - helper for ifstats()
3087 *
3088 * Ensures rows are received in order and complete.
3089 */
3090 static void
3091 validate_ifnum(
3092 FILE * fp,
3093 u_int ifnum,
3094 int * pfields,
3095 ifstats_row * prow
3096 )
3097 {
3098 if (prow->ifnum == ifnum)
3099 return;
3100 if (prow->ifnum + 1 == ifnum) {
3101 if (*pfields < IFSTATS_FIELDS)
3102 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3103 *pfields, IFSTATS_FIELDS);
3104 *pfields = 0;
3105 prow->ifnum = ifnum;
3106 return;
3107 }
3108 fprintf(stderr,
3109 "received if index %u, have %d of %d fields for index %u, aborting.\n",
3110 ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum);
3111 exit(1);
3112 }
3113
3114
3115 /*
3116 * another_ifstats_field - helper for ifstats()
3117 *
3118 * If all fields for the row have been received, print it.
3119 */
3120 static void
3121 another_ifstats_field(
3122 int * pfields,
3123 ifstats_row * prow,
3124 FILE * fp
3125 )
3126 {
3127 u_int ifnum;
3128
3129 (*pfields)++;
3130 /* we understand 12 tags */
3131 if (IFSTATS_FIELDS > *pfields)
3132 return;
3133 /*
3134 " interface name send\n"
3135 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
3136 "==============================================================================\n");
3137 */
3138 fprintf(fp,
3139 "%3u %-24.24s %c %4x %3d %2d %6d %6d %6d %5d %8d\n"
3140 " %s\n",
3141 prow->ifnum, prow->name,
3142 (prow->enabled)
3143 ? '.'
3144 : 'D',
3145 prow->flags, prow->ttl, prow->mcast_count,
3146 prow->received, prow->sent, prow->send_errors,
3147 prow->peer_count, prow->uptime, sptoa(&prow->addr));
3148 if (!SOCK_UNSPEC(&prow->bcast))
3149 fprintf(fp, " %s\n", sptoa(&prow->bcast));
3150 ifnum = prow->ifnum;
3151 ZERO(*prow);
3152 prow->ifnum = ifnum;
3153 }
3154
3155
3156 /*
3157 * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats.
3158 */
3159 static void
3160 ifstats(
3161 struct parse * pcmd,
3162 FILE * fp
3163 )
3164 {
3165 const char addr_fmt[] = "addr.%u";
3166 const char bcast_fmt[] = "bcast.%u";
3167 const char en_fmt[] = "en.%u"; /* enabled */
3168 const char flags_fmt[] = "flags.%u";
3169 const char mc_fmt[] = "mc.%u"; /* mcast count */
3170 const char name_fmt[] = "name.%u";
3171 const char pc_fmt[] = "pc.%u"; /* peer count */
3172 const char rx_fmt[] = "rx.%u";
3173 const char tl_fmt[] = "tl.%u"; /* ttl */
3174 const char tx_fmt[] = "tx.%u";
3175 const char txerr_fmt[] = "txerr.%u";
3176 const char up_fmt[] = "up.%u"; /* uptime */
3177 const char * datap;
3178 int qres;
3179 int dsize;
3180 u_short rstatus;
3181 char * tag;
3182 char * val;
3183 int fields;
3184 u_int ifnum;
3185 u_int ui;
3186 ifstats_row row;
3187 int comprende;
3188 size_t len;
3189
3190 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus,
3191 &dsize, &datap);
3192 if (qres) /* message already displayed */
3193 return;
3194
3195 fprintf(fp,
3196 " interface name send\n"
3197 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
3198 "==============================================================================\n");
3199 /* '=' x 78 */
3200
3201 ZERO(row);
3202 fields = 0;
3203 ifnum = 0;
3204 ui = 0;
3205 while (nextvar(&dsize, &datap, &tag, &val)) {
3206 if (debug > 1)
3207 fprintf(stderr, "nextvar gave: %s = %s\n", tag,
3208 (NULL == val)
3209 ? ""
3210 : val);
3211 comprende = FALSE;
3212 switch(tag[0]) {
3213
3214 case 'a':
3215 if (1 == sscanf(tag, addr_fmt, &ui) &&
3216 decodenetnum(val, &row.addr))
3217 comprende = TRUE;
3218 break;
3219
3220 case 'b':
3221 if (1 == sscanf(tag, bcast_fmt, &ui) &&
3222 (NULL == val ||
3223 decodenetnum(val, &row.bcast)))
3224 comprende = TRUE;
3225 break;
3226
3227 case 'e':
3228 if (1 == sscanf(tag, en_fmt, &ui) &&
3229 1 == sscanf(val, "%d", &row.enabled))
3230 comprende = TRUE;
3231 break;
3232
3233 case 'f':
3234 if (1 == sscanf(tag, flags_fmt, &ui) &&
3235 1 == sscanf(val, "0x%x", &row.flags))
3236 comprende = TRUE;
3237 break;
3238
3239 case 'm':
3240 if (1 == sscanf(tag, mc_fmt, &ui) &&
3241 1 == sscanf(val, "%d", &row.mcast_count))
3242 comprende = TRUE;
3243 break;
3244
3245 case 'n':
3246 if (1 == sscanf(tag, name_fmt, &ui)) {
3247 /* strip quotes */
3248 len = strlen(val);
3249 if (len >= 2 &&
3250 len - 2 < sizeof(row.name)) {
3251 len -= 2;
3252 memcpy(row.name, val + 1, len);
3253 row.name[len] = '\0';
3254 comprende = TRUE;
3255 }
3256 }
3257 break;
3258
3259 case 'p':
3260 if (1 == sscanf(tag, pc_fmt, &ui) &&
3261 1 == sscanf(val, "%d", &row.peer_count))
3262 comprende = TRUE;
3263 break;
3264
3265 case 'r':
3266 if (1 == sscanf(tag, rx_fmt, &ui) &&
3267 1 == sscanf(val, "%d", &row.received))
3268 comprende = TRUE;
3269 break;
3270
3271 case 't':
3272 if (1 == sscanf(tag, tl_fmt, &ui) &&
3273 1 == sscanf(val, "%d", &row.ttl))
3274 comprende = TRUE;
3275 else if (1 == sscanf(tag, tx_fmt, &ui) &&
3276 1 == sscanf(val, "%d", &row.sent))
3277 comprende = TRUE;
3278 else if (1 == sscanf(tag, txerr_fmt, &ui) &&
3279 1 == sscanf(val, "%d", &row.send_errors))
3280 comprende = TRUE;
3281 break;
3282
3283 case 'u':
3284 if (1 == sscanf(tag, up_fmt, &ui) &&
3285 1 == sscanf(val, "%d", &row.uptime))
3286 comprende = TRUE;
3287 break;
3288 }
3289
3290 if (comprende) {
3291 /* error out if rows out of order */
3292 validate_ifnum(fp, ui, &fields, &row);
3293 /* if the row is complete, print it */
3294 another_ifstats_field(&fields, &row, fp);
3295 }
3296 }
3297 if (fields != IFSTATS_FIELDS)
3298 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3299 fields, IFSTATS_FIELDS);
3300
3301 fflush(fp);
3302 }
3303
3304
3305 /*
3306 * validate_reslist_idx - helper for reslist()
3307 *
3308 * Ensures rows are received in order and complete.
3309 */
3310 static void
3311 validate_reslist_idx(
3312 FILE * fp,
3313 u_int idx,
3314 int * pfields,
3315 reslist_row * prow
3316 )
3317 {
3318 if (prow->idx == idx)
3319 return;
3320 if (prow->idx + 1 == idx) {
3321 if (*pfields < RESLIST_FIELDS)
3322 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3323 *pfields, RESLIST_FIELDS);
3324 *pfields = 0;
3325 prow->idx = idx;
3326 return;
3327 }
3328 fprintf(stderr,
3329 "received reslist index %u, have %d of %d fields for index %u, aborting.\n",
3330 idx, *pfields, RESLIST_FIELDS, prow->idx);
3331 exit(1);
3332 }
3333
3334
3335 /*
3336 * another_reslist_field - helper for reslist()
3337 *
3338 * If all fields for the row have been received, print it.
3339 */
3340 static void
3341 another_reslist_field(
3342 int * pfields,
3343 reslist_row * prow,
3344 FILE * fp
3345 )
3346 {
3347 char addrmaskstr[128];
3348 int prefix; /* subnet mask as prefix bits count */
3349 u_int idx;
3350
3351 (*pfields)++;
3352 /* we understand 4 tags */
3353 if (RESLIST_FIELDS > *pfields)
3354 return;
3355
3356 prefix = sockaddr_masktoprefixlen(&prow->mask);
3357 if (prefix >= 0)
3358 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d",
3359 stoa(&prow->addr), prefix);
3360 else
3361 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s",
3362 stoa(&prow->addr), stoa(&prow->mask));
3363
3364 /*
3365 " hits addr/prefix or addr mask\n"
3366 " restrictions\n"
3367 "==============================================================================\n");
3368 */
3369 fprintf(fp,
3370 "%10lu %s\n"
3371 " %s\n",
3372 prow->hits, addrmaskstr, prow->flagstr);
3373 idx = prow->idx;
3374 ZERO(*prow);
3375 prow->idx = idx;
3376 }
3377
3378
3379 /*
3380 * reslist - ntpq -c reslist modeled on ntpdc -c reslist.
3381 */
3382 static void
3383 reslist(
3384 struct parse * pcmd,
3385 FILE * fp
3386 )
3387 {
3388 const char addr_fmtu[] = "addr.%u";
3389 const char mask_fmtu[] = "mask.%u";
3390 const char hits_fmt[] = "hits.%u";
3391 const char flags_fmt[] = "flags.%u";
3392 const char qdata[] = "addr_restrictions";
3393 const int qdata_chars = COUNTOF(qdata) - 1;
3394 const char * datap;
3395 int qres;
3396 int dsize;
3397 u_short rstatus;
3398 char * tag;
3399 char * val;
3400 int fields;
3401 u_int idx;
3402 u_int ui;
3403 reslist_row row;
3404 int comprende;
3405 size_t len;
3406
3407 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars,
3408 qdata, &rstatus, &dsize, &datap);
3409 if (qres) /* message already displayed */
3410 return;
3411
3412 fprintf(fp,
3413 " hits addr/prefix or addr mask\n"
3414 " restrictions\n"
3415 "==============================================================================\n");
3416 /* '=' x 78 */
3417
3418 ZERO(row);
3419 fields = 0;
3420 idx = 0;
3421 ui = 0;
3422 while (nextvar(&dsize, &datap, &tag, &val)) {
3423 if (debug > 1)
3424 fprintf(stderr, "nextvar gave: %s = %s\n", tag,
3425 (NULL == val)
3426 ? ""
3427 : val);
3428 comprende = FALSE;
3429 switch(tag[0]) {
3430
3431 case 'a':
3432 if (1 == sscanf(tag, addr_fmtu, &ui) &&
3433 decodenetnum(val, &row.addr))
3434 comprende = TRUE;
3435 break;
3436
3437 case 'f':
3438 if (1 == sscanf(tag, flags_fmt, &ui)) {
3439 if (NULL == val) {
3440 row.flagstr[0] = '\0';
3441 comprende = TRUE;
3442 } else {
3443 len = strlen(val);
3444 memcpy(row.flagstr, val, len);
3445 row.flagstr[len] = '\0';
3446 comprende = TRUE;
3447 }
3448 }
3449 break;
3450
3451 case 'h':
3452 if (1 == sscanf(tag, hits_fmt, &ui) &&
3453 1 == sscanf(val, "%lu", &row.hits))
3454 comprende = TRUE;
3455 break;
3456
3457 case 'm':
3458 if (1 == sscanf(tag, mask_fmtu, &ui) &&
3459 decodenetnum(val, &row.mask))
3460 comprende = TRUE;
3461 break;
3462 }
3463
3464 if (comprende) {
3465 /* error out if rows out of order */
3466 validate_reslist_idx(fp, ui, &fields, &row);
3467 /* if the row is complete, print it */
3468 another_reslist_field(&fields, &row, fp);
3469 }
3470 }
3471 if (fields != RESLIST_FIELDS)
3472 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3473 fields, RESLIST_FIELDS);
3474
3475 fflush(fp);
3476 }
3477
3478
3479 /*
3480 * collect_display_vdc
3481 */
3482 static void
3483 collect_display_vdc(
3484 associd_t as,
3485 vdc * table,
3486 int decodestatus,
3487 FILE * fp
3488 )
3489 {
3490 static const char * const suf[2] = { "adr", "port" };
3491 static const char * const leapbits[4] = { "00", "01",
3492 "10", "11" };
3493 struct varlist vl[MAXLIST];
3494 char tagbuf[32];
3495 vdc *pvdc;
3496 u_short rstatus;
3497 int rsize;
3498 const char *rdata;
3499 int qres;
3500 char *tag;
3501 char *val;
3502 u_int n;
3503 size_t len;
3504 int match;
3505 u_long ul;
3506 int vtype;
3507
3508 ZERO(vl);
3509 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3510 ZERO(pvdc->v);
3511 if (NTP_ADD != pvdc->type) {
3512 doaddvlist(vl, pvdc->tag);
3513 } else {
3514 for (n = 0; n < COUNTOF(suf); n++) {
3515 snprintf(tagbuf, sizeof(tagbuf), "%s%s",
3516 pvdc->tag, suf[n]);
3517 doaddvlist(vl, tagbuf);
3518 }
3519 }
3520 }
3521 qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize,
3522 &rdata);
3523 doclearvlist(vl);
3524 if (qres)
3525 return; /* error msg already displayed */
3526
3527 /*
3528 * iterate over the response variables filling vdc_table with
3529 * the retrieved values.
3530 */
3531 while (nextvar(&rsize, &rdata, &tag, &val)) {
3532 if (NULL == val)
3533 continue;
3534 n = 0;
3535 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3536 len = strlen(pvdc->tag);
3537 if (strncmp(tag, pvdc->tag, len))
3538 continue;
3539 if (NTP_ADD != pvdc->type) {
3540 if ('\0' != tag[len])
3541 continue;
3542 break;
3543 }
3544 match = FALSE;
3545 for (n = 0; n < COUNTOF(suf); n++) {
3546 if (strcmp(tag + len, suf[n]))
3547 continue;
3548 match = TRUE;
3549 break;
3550 }
3551 if (match)
3552 break;
3553 }
3554 if (NULL == pvdc->tag)
3555 continue;
3556 switch (pvdc->type) {
3557
3558 case NTP_STR:
3559 /* strip surrounding double quotes */
3560 if ('"' == val[0]) {
3561 len = strlen(val);
3562 if (len > 0 && '"' == val[len - 1]) {
3563 val[len - 1] = '\0';
3564 val++;
3565 }
3566 }
3567 /* fallthru */
3568 case NTP_MODE: /* fallthru */
3569 case NTP_2BIT:
3570 pvdc->v.str = estrdup(val);
3571 break;
3572
3573 case NTP_LFP:
3574 decodets(val, &pvdc->v.lfp);
3575 break;
3576
3577 case NTP_ADP:
3578 if (!decodenetnum(val, &pvdc->v.sau))
3579 fprintf(stderr, "malformed %s=%s\n",
3580 pvdc->tag, val);
3581 break;
3582
3583 case NTP_ADD:
3584 if (0 == n) { /* adr */
3585 if (!decodenetnum(val, &pvdc->v.sau))
3586 fprintf(stderr,
3587 "malformed %s=%s\n",
3588 pvdc->tag, val);
3589 } else { /* port */
3590 if (atouint(val, &ul))
3591 SET_PORT(&pvdc->v.sau,
3592 (u_short)ul);
3593 }
3594 break;
3595 }
3596 }
3597
3598 /* and display */
3599 if (decodestatus) {
3600 vtype = (0 == as)
3601 ? TYPE_SYS
3602 : TYPE_PEER;
3603 fprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus,
3604 statustoa(vtype, rstatus));
3605 }
3606
3607 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3608 switch (pvdc->type) {
3609
3610 case NTP_STR:
3611 if (pvdc->v.str != NULL) {
3612 fprintf(fp, "%s %s\n", pvdc->display,
3613 pvdc->v.str);
3614 free(pvdc->v.str);
3615 pvdc->v.str = NULL;
3616 }
3617 break;
3618
3619 case NTP_ADD: /* fallthru */
3620 case NTP_ADP:
3621 fprintf(fp, "%s %s\n", pvdc->display,
3622 nntohostp(&pvdc->v.sau));
3623 break;
3624
3625 case NTP_LFP:
3626 fprintf(fp, "%s %s\n", pvdc->display,
3627 prettydate(&pvdc->v.lfp));
3628 break;
3629
3630 case NTP_MODE:
3631 atouint(pvdc->v.str, &ul);
3632 fprintf(fp, "%s %s\n", pvdc->display,
3633 modetoa((int)ul));
3634 break;
3635
3636 case NTP_2BIT:
3637 atouint(pvdc->v.str, &ul);
3638 fprintf(fp, "%s %s\n", pvdc->display,
3639 leapbits[ul & 0x3]);
3640 break;
3641
3642 default:
3643 fprintf(stderr, "unexpected vdc type %d for %s\n",
3644 pvdc->type, pvdc->tag);
3645 break;
3646 }
3647 }
3648 }
3649
3650
3651 /*
3652 * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats
3653 */
3654 static void
3655 sysstats(
3656 struct parse *pcmd,
3657 FILE *fp
3658 )
3659 {
3660 static vdc sysstats_vdc[] = {
3661 { "ss_uptime", "uptime: ", NTP_STR },
3662 { "ss_reset", "sysstats reset: ", NTP_STR },
3663 { "ss_received", "packets received: ", NTP_STR },
3664 { "ss_thisver", "current version: ", NTP_STR },
3665 { "ss_oldver", "older version: ", NTP_STR },
3666 { "ss_badformat", "bad length or format: ", NTP_STR },
3667 { "ss_badauth", "authentication failed:", NTP_STR },
3668 { "ss_declined", "declined: ", NTP_STR },
3669 { "ss_restricted", "restricted: ", NTP_STR },
3670 { "ss_limited", "rate limited: ", NTP_STR },
3671 { "ss_kodsent", "KoD responses: ", NTP_STR },
3672 { "ss_processed", "processed for time: ", NTP_STR },
3673 { NULL, NULL, 0 }
3674 };
3675
3676 collect_display_vdc(0, sysstats_vdc, FALSE, fp);
3677 }
3678
3679
3680 /*
3681 * sysinfo - modeled on ntpdc's sysinfo
3682 */
3683 static void
3684 sysinfo(
3685 struct parse *pcmd,
3686 FILE *fp
3687 )
3688 {
3689 static vdc sysinfo_vdc[] = {
3690 { "peeradr", "system peer: ", NTP_ADP },
3691 { "peermode", "system peer mode: ", NTP_MODE },
3692 { "leap", "leap indicator: ", NTP_2BIT },
3693 { "stratum", "stratum: ", NTP_STR },
3694 { "precision", "log2 precision: ", NTP_STR },
3695 { "rootdelay", "root delay: ", NTP_STR },
3696 { "rootdisp", "root dispersion: ", NTP_STR },
3697 { "refid", "reference ID: ", NTP_STR },
3698 { "reftime", "reference time: ", NTP_LFP },
3699 { "sys_jitter", "system jitter: ", NTP_STR },
3700 { "clk_jitter", "clock jitter: ", NTP_STR },
3701 { "clk_wander", "clock wander: ", NTP_STR },
3702 { "bcastdelay", "broadcast delay: ", NTP_STR },
3703 { "authdelay", "symm. auth. delay:", NTP_STR },
3704 { NULL, NULL, 0 }
3705 };
3706
3707 collect_display_vdc(0, sysinfo_vdc, TRUE, fp);
3708 }
3709
3710
3711 /*
3712 * kerninfo - modeled on ntpdc's kerninfo
3713 */
3714 static void
3715 kerninfo(
3716 struct parse *pcmd,
3717 FILE *fp
3718 )
3719 {
3720 static vdc kerninfo_vdc[] = {
3721 { "koffset", "pll offset: ", NTP_STR },
3722 { "kfreq", "pll frequency: ", NTP_STR },
3723 { "kmaxerr", "maximum error: ", NTP_STR },
3724 { "kesterr", "estimated error: ", NTP_STR },
3725 { "kstflags", "kernel status: ", NTP_STR },
3726 { "ktimeconst", "pll time constant: ", NTP_STR },
3727 { "kprecis", "precision: ", NTP_STR },
3728 { "kfreqtol", "frequency tolerance: ", NTP_STR },
3729 { "kppsfreq", "pps frequency: ", NTP_STR },
3730 { "kppsstab", "pps stability: ", NTP_STR },
3731 { "kppsjitter", "pps jitter: ", NTP_STR },
3732 { "kppscalibdur", "calibration interval ", NTP_STR },
3733 { "kppscalibs", "calibration cycles: ", NTP_STR },
3734 { "kppsjitexc", "jitter exceeded: ", NTP_STR },
3735 { "kppsstbexc", "stability exceeded: ", NTP_STR },
3736 { "kppscaliberrs", "calibration errors: ", NTP_STR },
3737 { NULL, NULL, 0 }
3738 };
3739
3740 collect_display_vdc(0, kerninfo_vdc, TRUE, fp);
3741 }
3742
3743
3744 /*
3745 * monstats - implements ntpq -c monstats
3746 */
3747 static void
3748 monstats(
3749 struct parse *pcmd,
3750 FILE *fp
3751 )
3752 {
3753 static vdc monstats_vdc[] = {
3754 { "mru_enabled", "enabled: ", NTP_STR },
3755 { "mru_depth", "addresses: ", NTP_STR },
3756 { "mru_deepest", "peak addresses: ", NTP_STR },
3757 { "mru_maxdepth", "maximum addresses: ", NTP_STR },
3758 { "mru_mindepth", "reclaim above count:", NTP_STR },
3759 { "mru_maxage", "reclaim older than: ", NTP_STR },
3760 { "mru_mem", "kilobytes: ", NTP_STR },
3761 { "mru_maxmem", "maximum kilobytes: ", NTP_STR },
3762 { NULL, NULL, 0 }
3763 };
3764
3765 collect_display_vdc(0, monstats_vdc, FALSE, fp);
3766 }
3767
3768
3769 /*
3770 * iostats - ntpq -c iostats - network input and output counters
3771 */
3772 static void
3773 iostats(
3774 struct parse *pcmd,
3775 FILE *fp
3776 )
3777 {
3778 static vdc iostats_vdc[] = {
3779 { "iostats_reset", "time since reset: ", NTP_STR },
3780 { "total_rbuf", "receive buffers: ", NTP_STR },
3781 { "free_rbuf", "free receive buffers: ", NTP_STR },
3782 { "used_rbuf", "used receive buffers: ", NTP_STR },
3783 { "rbuf_lowater", "low water refills: ", NTP_STR },
3784 { "io_dropped", "dropped packets: ", NTP_STR },
3785 { "io_ignored", "ignored packets: ", NTP_STR },
3786 { "io_received", "received packets: ", NTP_STR },
3787 { "io_sent", "packets sent: ", NTP_STR },
3788 { "io_sendfailed", "packet send failures: ", NTP_STR },
3789 { "io_wakeups", "input wakeups: ", NTP_STR },
3790 { "io_goodwakeups", "useful input wakeups: ", NTP_STR },
3791 { NULL, NULL, 0 }
3792 };
3793
3794 collect_display_vdc(0, iostats_vdc, FALSE, fp);
3795 }
3796
3797
3798 /*
3799 * timerstats - ntpq -c timerstats - interval timer counters
3800 */
3801 static void
3802 timerstats(
3803 struct parse *pcmd,
3804 FILE *fp
3805 )
3806 {
3807 static vdc timerstats_vdc[] = {
3808 { "timerstats_reset", "time since reset: ", NTP_STR },
3809 { "timer_overruns", "timer overruns: ", NTP_STR },
3810 { "timer_xmts", "calls to transmit: ", NTP_STR },
3811 { NULL, NULL, 0 }
3812 };
3813
3814 collect_display_vdc(0, timerstats_vdc, FALSE, fp);
3815 }
3816
3817
3818 /*
3819 * authinfo - implements ntpq -c authinfo
3820 */
3821 static void
3822 authinfo(
3823 struct parse *pcmd,
3824 FILE *fp
3825 )
3826 {
3827 static vdc authinfo_vdc[] = {
3828 { "authreset", "time since reset:", NTP_STR },
3829 { "authkeys", "stored keys: ", NTP_STR },
3830 { "authfreek", "free keys: ", NTP_STR },
3831 { "authklookups", "key lookups: ", NTP_STR },
3832 { "authknotfound", "keys not found: ", NTP_STR },
3833 { "authkuncached", "uncached keys: ", NTP_STR },
3834 { "authkexpired", "expired keys: ", NTP_STR },
3835 { "authencrypts", "encryptions: ", NTP_STR },
3836 { "authdecrypts", "decryptions: ", NTP_STR },
3837 { NULL, NULL, 0 }
3838 };
3839
3840 collect_display_vdc(0, authinfo_vdc, FALSE, fp);
3841 }
3842
3843
3844 /*
3845 * pstats - show statistics for a peer
3846 */
3847 static void
3848 pstats(
3849 struct parse *pcmd,
3850 FILE *fp
3851 )
3852 {
3853 static vdc pstats_vdc[] = {
3854 { "src", "remote host: ", NTP_ADD },
3855 { "dst", "local address: ", NTP_ADD },
3856 { "timerec", "time last received: ", NTP_STR },
3857 { "timer", "time until next send:", NTP_STR },
3858 { "timereach", "reachability change: ", NTP_STR },
3859 { "sent", "packets sent: ", NTP_STR },
3860 { "received", "packets received: ", NTP_STR },
3861 { "badauth", "bad authentication: ", NTP_STR },
3862 { "bogusorg", "bogus origin: ", NTP_STR },
3863 { "oldpkt", "duplicate: ", NTP_STR },
3864 { "seldisp", "bad dispersion: ", NTP_STR },
3865 { "selbroken", "bad reference time: ", NTP_STR },
3866 { "candidate", "candidate order: ", NTP_STR },
3867 { NULL, NULL, 0 }
3868 };
3869 associd_t associd;
3870
3871 associd = checkassocid(pcmd->argval[0].uval);
3872 if (0 == associd)
3873 return;
3874
3875 collect_display_vdc(associd, pstats_vdc, TRUE, fp);
3876 }
3877
3878