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