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