ntpq-subs.c revision 1.11 1 /* $NetBSD: ntpq-subs.c,v 1.11 2015/07/10 14:20:33 christos Exp $ */
2
3 /*
4 * ntpq-subs.c - subroutines which are called to perform ntpq commands.
5 */
6 #include <config.h>
7 #include <stdio.h>
8 #include <ctype.h>
9 #include <sys/types.h>
10 #include <sys/time.h>
11
12 #include "ntpq.h"
13 #include "ntpq-opts.h"
14
15 extern char currenthost[];
16 extern int currenthostisnum;
17 size_t maxhostlen;
18
19 /*
20 * Declarations for command handlers in here
21 */
22 static associd_t checkassocid (u_int32);
23 static struct varlist *findlistvar (struct varlist *, char *);
24 static void doaddvlist (struct varlist *, const char *);
25 static void dormvlist (struct varlist *, const char *);
26 static void doclearvlist (struct varlist *);
27 static void makequerydata (struct varlist *, int *, char *);
28 static int doquerylist (struct varlist *, int, associd_t, int,
29 u_short *, int *, const char **);
30 static void doprintvlist (struct varlist *, FILE *);
31 static void addvars (struct parse *, FILE *);
32 static void rmvars (struct parse *, FILE *);
33 static void clearvars (struct parse *, FILE *);
34 static void showvars (struct parse *, FILE *);
35 static int dolist (struct varlist *, associd_t, int, int,
36 FILE *);
37 static void readlist (struct parse *, FILE *);
38 static void writelist (struct parse *, FILE *);
39 static void readvar (struct parse *, FILE *);
40 static void writevar (struct parse *, FILE *);
41 static void clocklist (struct parse *, FILE *);
42 static void clockvar (struct parse *, FILE *);
43 static int findassidrange (u_int32, u_int32, int *, int *,
44 FILE *);
45 static void mreadlist (struct parse *, FILE *);
46 static void mreadvar (struct parse *, FILE *);
47 static void printassoc (int, FILE *);
48 static void associations (struct parse *, FILE *);
49 static void lassociations (struct parse *, FILE *);
50 static void passociations (struct parse *, FILE *);
51 static void lpassociations (struct parse *, FILE *);
52
53 #ifdef UNUSED
54 static void radiostatus (struct parse *, FILE *);
55 #endif /* UNUSED */
56
57 static void authinfo (struct parse *, FILE *);
58 static void pstats (struct parse *, FILE *);
59 static long when (l_fp *, l_fp *, l_fp *);
60 static char * prettyinterval (char *, size_t, long);
61 static int doprintpeers (struct varlist *, int, int, int, const char *, FILE *, int);
62 static int dogetpeers (struct varlist *, associd_t, FILE *, int);
63 static void dopeers (int, FILE *, int);
64 static void peers (struct parse *, FILE *);
65 static void 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 void 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 int 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 int 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 int *datalen,
533 char *data
534 )
535 {
536 register struct varlist *vl;
537 register char *cp, *cpend;
538 register int namelen, valuelen;
539 register int 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 = 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 int *dsize,
583 const char **datap
584 )
585 {
586 char data[CTL_MAX_DATA_LEN];
587 int 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 int 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 int 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 u_int tmpcount;
814 u_int 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 int 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 int 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 %d 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 = "outlyer";
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 int 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", dsize, datap);
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 int datalen,
1599 const char *data,
1600 FILE *fp,
1601 int af
1602 )
1603 {
1604 char *name;
1605 char *value = NULL;
1606 int c;
1607 int 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 int 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 int 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, strlen(cfgcmd), cfgcmd,
2207 &rstatus, &rsize, &rdata);
2208
2209 if (res != 0)
2210 return;
2211
2212 if (rsize > 0 && '\n' == rdata[rsize - 1])
2213 rsize--;
2214
2215 resp = emalloc(rsize + 1);
2216 memcpy(resp, rdata, rsize);
2217 resp[rsize] = '\0';
2218
2219 col = -1;
2220 if (1 == sscanf(resp, "column %d syntax error", &col)
2221 && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) {
2222 if (interactive) {
2223 printf("______"); /* "ntpq> " */
2224 printf("________"); /* ":config " */
2225 } else
2226 printf("%s\n", cfgcmd);
2227 for (i = 1; i < col; i++)
2228 putchar('_');
2229 printf("^\n");
2230 }
2231 printf("%s\n", resp);
2232 free(resp);
2233 }
2234
2235
2236 /*
2237 * config_from_file - remotely configure an ntpd daemon using the
2238 * specified configuration file
2239 * SK: This function is a kludge at best and is full of bad design
2240 * bugs:
2241 * 1. ntpq uses UDP, which means that there is no guarantee of in-order,
2242 * error-free delivery.
2243 * 2. The maximum length of a packet is constrained, and as a result, the
2244 * maximum length of a line in a configuration file is constrained.
2245 * Longer lines will lead to unpredictable results.
2246 * 3. Since this function is sending a line at a time, we can't update
2247 * the control key through the configuration file (YUCK!!)
2248 */
2249 static void
2250 config_from_file (
2251 struct parse *pcmd,
2252 FILE *fp
2253 )
2254 {
2255 u_short rstatus;
2256 int rsize;
2257 const char *rdata;
2258 int res;
2259 FILE *config_fd;
2260 char config_cmd[MAXLINE];
2261 size_t config_len;
2262 int i;
2263 int retry_limit;
2264
2265 if (debug > 2)
2266 fprintf(stderr,
2267 "In Config\n"
2268 "Keyword = %s\n"
2269 "Filename = %s\n", pcmd->keyword,
2270 pcmd->argval[0].string);
2271
2272 config_fd = fopen(pcmd->argval[0].string, "r");
2273 if (NULL == config_fd) {
2274 printf("ERROR!! Couldn't open file: %s\n",
2275 pcmd->argval[0].string);
2276 return;
2277 }
2278
2279 printf("Sending configuration file, one line at a time.\n");
2280 i = 0;
2281 while (fgets(config_cmd, MAXLINE, config_fd) != NULL) {
2282 config_len = strlen(config_cmd);
2283 /* ensure even the last line has newline, if possible */
2284 if (config_len > 0 &&
2285 config_len + 2 < sizeof(config_cmd) &&
2286 '\n' != config_cmd[config_len - 1])
2287 config_cmd[config_len++] = '\n';
2288 ++i;
2289 retry_limit = 2;
2290 do
2291 res = doquery(CTL_OP_CONFIGURE, 0, 1,
2292 strlen(config_cmd), config_cmd,
2293 &rstatus, &rsize, &rdata);
2294 while (res != 0 && retry_limit--);
2295 if (res != 0) {
2296 printf("Line No: %d query failed: %s", i,
2297 config_cmd);
2298 printf("Subsequent lines not sent.\n");
2299 fclose(config_fd);
2300 return;
2301 }
2302
2303 if (rsize > 0 && '\n' == rdata[rsize - 1])
2304 rsize--;
2305 if (rsize > 0 && '\r' == rdata[rsize - 1])
2306 rsize--;
2307 printf("Line No: %d %.*s: %s", i, rsize, rdata,
2308 config_cmd);
2309 }
2310 printf("Done sending file\n");
2311 fclose(config_fd);
2312 }
2313
2314
2315 static int
2316 fetch_nonce(
2317 char * nonce,
2318 size_t cb_nonce
2319 )
2320 {
2321 const char nonce_eq[] = "nonce=";
2322 int qres;
2323 u_short rstatus;
2324 int rsize;
2325 const char * rdata;
2326 int chars;
2327
2328 /*
2329 * Retrieve a nonce specific to this client to demonstrate to
2330 * ntpd that we're capable of receiving responses to our source
2331 * IP address, and thereby unlikely to be forging the source.
2332 */
2333 qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus,
2334 &rsize, &rdata);
2335 if (qres) {
2336 fprintf(stderr, "nonce request failed\n");
2337 return FALSE;
2338 }
2339
2340 if ((size_t)rsize <= sizeof(nonce_eq) - 1 ||
2341 strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) {
2342 fprintf(stderr, "unexpected nonce response format: %.*s\n",
2343 rsize, rdata);
2344 return FALSE;
2345 }
2346 chars = rsize - (sizeof(nonce_eq) - 1);
2347 if (chars >= (int)cb_nonce)
2348 return FALSE;
2349 memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars);
2350 nonce[chars] = '\0';
2351 while (chars > 0 &&
2352 ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) {
2353 chars--;
2354 nonce[chars] = '\0';
2355 }
2356
2357 return TRUE;
2358 }
2359
2360
2361 /*
2362 * add_mru Add and entry to mru list, hash table, and allocate
2363 * and return a replacement.
2364 * This is a helper for collect_mru_list().
2365 */
2366 static mru *
2367 add_mru(
2368 mru *add
2369 )
2370 {
2371 u_short hash;
2372 mru *mon;
2373 mru *unlinked;
2374
2375
2376 hash = NTP_HASH_ADDR(&add->addr);
2377 /* see if we have it among previously received entries */
2378 for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink)
2379 if (SOCK_EQ(&mon->addr, &add->addr))
2380 break;
2381 if (mon != NULL) {
2382 if (!L_ISGEQ(&add->first, &mon->first)) {
2383 fprintf(stderr,
2384 "add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n",
2385 sptoa(&add->addr), add->last.l_ui,
2386 add->last.l_uf, mon->last.l_ui,
2387 mon->last.l_uf);
2388 exit(1);
2389 }
2390 UNLINK_DLIST(mon, mlink);
2391 UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru);
2392 NTP_INSIST(unlinked == mon);
2393 mru_dupes++;
2394 TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui,
2395 mon->last.l_uf));
2396 }
2397 LINK_DLIST(mru_list, add, mlink);
2398 LINK_SLIST(hash_table[hash], add, hlink);
2399 TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
2400 add->last.l_ui, add->last.l_uf, add->count,
2401 (int)add->mode, (int)add->ver, (u_int)add->rs,
2402 add->first.l_ui, add->first.l_uf, sptoa(&add->addr)));
2403 /* if we didn't update an existing entry, alloc replacement */
2404 if (NULL == mon) {
2405 mon = emalloc(sizeof(*mon));
2406 mru_count++;
2407 }
2408 ZERO(*mon);
2409
2410 return mon;
2411 }
2412
2413
2414 /* MGOT macro is specific to collect_mru_list() */
2415 #define MGOT(bit) \
2416 do { \
2417 got |= (bit); \
2418 if (MRU_GOT_ALL == got) { \
2419 got = 0; \
2420 mon = add_mru(mon); \
2421 ci++; \
2422 } \
2423 } while (0)
2424
2425
2426 void
2427 mrulist_ctrl_c_hook(void)
2428 {
2429 mrulist_interrupted = TRUE;
2430 }
2431
2432
2433 static int
2434 collect_mru_list(
2435 const char * parms,
2436 l_fp * pnow
2437 )
2438 {
2439 const u_int sleep_msecs = 5;
2440 static int ntpd_row_limit = MRU_ROW_LIMIT;
2441 int c_mru_l_rc; /* this function's return code */
2442 u_char got; /* MRU_GOT_* bits */
2443 time_t next_report;
2444 size_t cb;
2445 mru *mon;
2446 mru *head;
2447 mru *recent;
2448 int list_complete;
2449 char nonce[128];
2450 char buf[128];
2451 char req_buf[CTL_MAX_DATA_LEN];
2452 char *req;
2453 char *req_end;
2454 int chars;
2455 int qres;
2456 u_short rstatus;
2457 int rsize;
2458 const char *rdata;
2459 int limit;
2460 int frags;
2461 int cap_frags;
2462 char *tag;
2463 char *val;
2464 int si; /* server index in response */
2465 int ci; /* client (our) index for validation */
2466 int ri; /* request index (.# suffix) */
2467 int mv;
2468 l_fp newest;
2469 l_fp last_older;
2470 sockaddr_u addr_older;
2471 int have_now;
2472 int have_addr_older;
2473 int have_last_older;
2474 u_int restarted_count;
2475 u_int nonce_uses;
2476 u_short hash;
2477 mru *unlinked;
2478
2479 if (!fetch_nonce(nonce, sizeof(nonce)))
2480 return FALSE;
2481
2482 nonce_uses = 0;
2483 restarted_count = 0;
2484 mru_count = 0;
2485 INIT_DLIST(mru_list, mlink);
2486 cb = NTP_HASH_SIZE * sizeof(*hash_table);
2487 NTP_INSIST(NULL == hash_table);
2488 hash_table = emalloc_zero(cb);
2489
2490 c_mru_l_rc = FALSE;
2491 list_complete = FALSE;
2492 have_now = FALSE;
2493 cap_frags = TRUE;
2494 got = 0;
2495 ri = 0;
2496 cb = sizeof(*mon);
2497 mon = emalloc_zero(cb);
2498 ZERO(*pnow);
2499 ZERO(last_older);
2500 mrulist_interrupted = FALSE;
2501 set_ctrl_c_hook(&mrulist_ctrl_c_hook);
2502 fprintf(stderr,
2503 "Ctrl-C will stop MRU retrieval and display partial results.\n");
2504 fflush(stderr);
2505 next_report = time(NULL) + MRU_REPORT_SECS;
2506
2507 limit = min(3 * MAXFRAGS, ntpd_row_limit);
2508 frags = MAXFRAGS;
2509 snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s",
2510 nonce, frags, parms);
2511 nonce_uses++;
2512
2513 while (TRUE) {
2514 if (debug)
2515 fprintf(stderr, "READ_MRU parms: %s\n", req_buf);
2516
2517 qres = doqueryex(CTL_OP_READ_MRU, 0, 0, strlen(req_buf),
2518 req_buf, &rstatus, &rsize, &rdata, TRUE);
2519
2520 if (CERR_UNKNOWNVAR == qres && ri > 0) {
2521 /*
2522 * None of the supplied prior entries match, so
2523 * toss them from our list and try again.
2524 */
2525 if (debug)
2526 fprintf(stderr,
2527 "no overlap between %d prior entries and server MRU list\n",
2528 ri);
2529 while (ri--) {
2530 recent = HEAD_DLIST(mru_list, mlink);
2531 NTP_INSIST(recent != NULL);
2532 if (debug)
2533 fprintf(stderr,
2534 "tossing prior entry %s to resync\n",
2535 sptoa(&recent->addr));
2536 UNLINK_DLIST(recent, mlink);
2537 hash = NTP_HASH_ADDR(&recent->addr);
2538 UNLINK_SLIST(unlinked, hash_table[hash],
2539 recent, hlink, mru);
2540 NTP_INSIST(unlinked == recent);
2541 free(recent);
2542 mru_count--;
2543 }
2544 if (NULL == HEAD_DLIST(mru_list, mlink)) {
2545 restarted_count++;
2546 if (restarted_count > 8) {
2547 fprintf(stderr,
2548 "Giving up after 8 restarts from the beginning.\n"
2549 "With high-traffic NTP servers, this can occur if the\n"
2550 "MRU list is limited to less than about 16 seconds' of\n"
2551 "entries. See the 'mru' ntp.conf directive to adjust.\n");
2552 goto cleanup_return;
2553 }
2554 if (debug)
2555 fprintf(stderr,
2556 "---> Restarting from the beginning, retry #%u\n",
2557 restarted_count);
2558 }
2559 } else if (CERR_UNKNOWNVAR == qres) {
2560 fprintf(stderr,
2561 "CERR_UNKNOWNVAR from ntpd but no priors given.\n");
2562 goto cleanup_return;
2563 } else if (CERR_BADVALUE == qres) {
2564 if (cap_frags) {
2565 cap_frags = FALSE;
2566 if (debug)
2567 fprintf(stderr,
2568 "Reverted to row limit from fragments limit.\n");
2569 } else {
2570 /* ntpd has lower cap on row limit */
2571 ntpd_row_limit--;
2572 limit = min(limit, ntpd_row_limit);
2573 if (debug)
2574 fprintf(stderr,
2575 "Row limit reduced to %d following CERR_BADVALUE.\n",
2576 limit);
2577 }
2578 } else if (ERR_INCOMPLETE == qres ||
2579 ERR_TIMEOUT == qres) {
2580 /*
2581 * Reduce the number of rows/frags requested by
2582 * half to recover from lost response fragments.
2583 */
2584 if (cap_frags) {
2585 frags = max(2, frags / 2);
2586 if (debug)
2587 fprintf(stderr,
2588 "Frag limit reduced to %d following incomplete response.\n",
2589 frags);
2590 } else {
2591 limit = max(2, limit / 2);
2592 if (debug)
2593 fprintf(stderr,
2594 "Row limit reduced to %d following incomplete response.\n",
2595 limit);
2596 }
2597 } else if (qres) {
2598 show_error_msg(qres, 0);
2599 goto cleanup_return;
2600 }
2601 /*
2602 * This is a cheap cop-out implementation of rawmode
2603 * output for mrulist. A better approach would be to
2604 * dump similar output after the list is collected by
2605 * ntpq with a continuous sequence of indexes. This
2606 * cheap approach has indexes resetting to zero for
2607 * each query/response, and duplicates are not
2608 * coalesced.
2609 */
2610 if (!qres && rawmode)
2611 printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout);
2612 ci = 0;
2613 have_addr_older = FALSE;
2614 have_last_older = FALSE;
2615 while (!qres && nextvar(&rsize, &rdata, &tag, &val)) {
2616 if (debug > 1)
2617 fprintf(stderr, "nextvar gave: %s = %s\n",
2618 tag, val);
2619 switch(tag[0]) {
2620
2621 case 'a':
2622 if (!strcmp(tag, "addr.older")) {
2623 if (!have_last_older) {
2624 fprintf(stderr,
2625 "addr.older %s before last.older\n",
2626 val);
2627 goto cleanup_return;
2628 }
2629 if (!decodenetnum(val, &addr_older)) {
2630 fprintf(stderr,
2631 "addr.older %s garbled\n",
2632 val);
2633 goto cleanup_return;
2634 }
2635 hash = NTP_HASH_ADDR(&addr_older);
2636 for (recent = hash_table[hash];
2637 recent != NULL;
2638 recent = recent->hlink)
2639 if (ADDR_PORT_EQ(
2640 &addr_older,
2641 &recent->addr))
2642 break;
2643 if (NULL == recent) {
2644 fprintf(stderr,
2645 "addr.older %s not in hash table\n",
2646 val);
2647 goto cleanup_return;
2648 }
2649 if (!L_ISEQU(&last_older,
2650 &recent->last)) {
2651 fprintf(stderr,
2652 "last.older %08x.%08x mismatches %08x.%08x expected.\n",
2653 last_older.l_ui,
2654 last_older.l_uf,
2655 recent->last.l_ui,
2656 recent->last.l_uf);
2657 goto cleanup_return;
2658 }
2659 have_addr_older = TRUE;
2660 } else if (1 != sscanf(tag, "addr.%d", &si)
2661 || si != ci)
2662 goto nomatch;
2663 else if (decodenetnum(val, &mon->addr))
2664 MGOT(MRU_GOT_ADDR);
2665 break;
2666
2667 case 'l':
2668 if (!strcmp(tag, "last.older")) {
2669 if ('0' != val[0] ||
2670 'x' != val[1] ||
2671 !hextolfp(val + 2, &last_older)) {
2672 fprintf(stderr,
2673 "last.older %s garbled\n",
2674 val);
2675 goto cleanup_return;
2676 }
2677 have_last_older = TRUE;
2678 } else if (!strcmp(tag, "last.newest")) {
2679 if (0 != got) {
2680 fprintf(stderr,
2681 "last.newest %s before complete row, got = 0x%x\n",
2682 val, (u_int)got);
2683 goto cleanup_return;
2684 }
2685 if (!have_now) {
2686 fprintf(stderr,
2687 "last.newest %s before now=\n",
2688 val);
2689 goto cleanup_return;
2690 }
2691 head = HEAD_DLIST(mru_list, mlink);
2692 if (NULL != head) {
2693 if ('0' != val[0] ||
2694 'x' != val[1] ||
2695 !hextolfp(val + 2, &newest) ||
2696 !L_ISEQU(&newest,
2697 &head->last)) {
2698 fprintf(stderr,
2699 "last.newest %s mismatches %08x.%08x",
2700 val,
2701 head->last.l_ui,
2702 head->last.l_uf);
2703 goto cleanup_return;
2704 }
2705 }
2706 list_complete = TRUE;
2707 } else if (1 != sscanf(tag, "last.%d", &si) ||
2708 si != ci || '0' != val[0] ||
2709 'x' != val[1] ||
2710 !hextolfp(val + 2, &mon->last)) {
2711 goto nomatch;
2712 } else {
2713 MGOT(MRU_GOT_LAST);
2714 /*
2715 * allow interrupted retrieval,
2716 * using most recent retrieved
2717 * entry's last seen timestamp
2718 * as the end of operation.
2719 */
2720 *pnow = mon->last;
2721 }
2722 break;
2723
2724 case 'f':
2725 if (1 != sscanf(tag, "first.%d", &si) ||
2726 si != ci || '0' != val[0] ||
2727 'x' != val[1] ||
2728 !hextolfp(val + 2, &mon->first))
2729 goto nomatch;
2730 MGOT(MRU_GOT_FIRST);
2731 break;
2732
2733 case 'n':
2734 if (!strcmp(tag, "nonce")) {
2735 strlcpy(nonce, val, sizeof(nonce));
2736 nonce_uses = 0;
2737 break; /* case */
2738 } else if (strcmp(tag, "now") ||
2739 '0' != val[0] ||
2740 'x' != val[1] ||
2741 !hextolfp(val + 2, pnow))
2742 goto nomatch;
2743 have_now = TRUE;
2744 break;
2745
2746 case 'c':
2747 if (1 != sscanf(tag, "ct.%d", &si) ||
2748 si != ci ||
2749 1 != sscanf(val, "%d", &mon->count)
2750 || mon->count < 1)
2751 goto nomatch;
2752 MGOT(MRU_GOT_COUNT);
2753 break;
2754
2755 case 'm':
2756 if (1 != sscanf(tag, "mv.%d", &si) ||
2757 si != ci ||
2758 1 != sscanf(val, "%d", &mv))
2759 goto nomatch;
2760 mon->mode = PKT_MODE(mv);
2761 mon->ver = PKT_VERSION(mv);
2762 MGOT(MRU_GOT_MV);
2763 break;
2764
2765 case 'r':
2766 if (1 != sscanf(tag, "rs.%d", &si) ||
2767 si != ci ||
2768 1 != sscanf(val, "0x%hx", &mon->rs))
2769 goto nomatch;
2770 MGOT(MRU_GOT_RS);
2771 break;
2772
2773 default:
2774 nomatch:
2775 /* empty stmt */ ;
2776 /* ignore unknown tags */
2777 }
2778 }
2779 if (have_now)
2780 list_complete = TRUE;
2781 if (list_complete) {
2782 NTP_INSIST(0 == ri || have_addr_older);
2783 }
2784 if (mrulist_interrupted) {
2785 printf("mrulist retrieval interrupted by operator.\n"
2786 "Displaying partial client list.\n");
2787 fflush(stdout);
2788 }
2789 if (list_complete || mrulist_interrupted) {
2790 fprintf(stderr,
2791 "\rRetrieved %u unique MRU entries and %u updates.\n",
2792 mru_count, mru_dupes);
2793 fflush(stderr);
2794 break;
2795 }
2796 if (time(NULL) >= next_report) {
2797 next_report += MRU_REPORT_SECS;
2798 fprintf(stderr, "\r%u (%u updates) ", mru_count,
2799 mru_dupes);
2800 fflush(stderr);
2801 }
2802
2803 /*
2804 * Snooze for a bit between queries to let ntpd catch
2805 * up with other duties.
2806 */
2807 #ifdef SYS_WINNT
2808 Sleep(sleep_msecs);
2809 #elif !defined(HAVE_NANOSLEEP)
2810 sleep((sleep_msecs / 1000) + 1);
2811 #else
2812 {
2813 struct timespec interv = { 0,
2814 1000 * sleep_msecs };
2815 nanosleep(&interv, NULL);
2816 }
2817 #endif
2818 /*
2819 * If there were no errors, increase the number of rows
2820 * to a maximum of 3 * MAXFRAGS (the most packets ntpq
2821 * can handle in one response), on the assumption that
2822 * no less than 3 rows fit in each packet, capped at
2823 * our best guess at the server's row limit.
2824 */
2825 if (!qres) {
2826 if (cap_frags) {
2827 frags = min(MAXFRAGS, frags + 1);
2828 } else {
2829 limit = min3(3 * MAXFRAGS,
2830 ntpd_row_limit,
2831 max(limit + 1,
2832 limit * 33 / 32));
2833 }
2834 }
2835 /*
2836 * prepare next query with as many address and last-seen
2837 * timestamps as will fit in a single packet.
2838 */
2839 req = req_buf;
2840 req_end = req_buf + sizeof(req_buf);
2841 #define REQ_ROOM (req_end - req)
2842 snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce,
2843 (cap_frags)
2844 ? "frags"
2845 : "limit",
2846 (cap_frags)
2847 ? frags
2848 : limit,
2849 parms);
2850 req += strlen(req);
2851 nonce_uses++;
2852 if (nonce_uses >= 4) {
2853 if (!fetch_nonce(nonce, sizeof(nonce)))
2854 goto cleanup_return;
2855 nonce_uses = 0;
2856 }
2857
2858
2859 for (ri = 0, recent = HEAD_DLIST(mru_list, mlink);
2860 recent != NULL;
2861 ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) {
2862
2863 snprintf(buf, sizeof(buf),
2864 ", addr.%d=%s, last.%d=0x%08x.%08x",
2865 ri, sptoa(&recent->addr), ri,
2866 recent->last.l_ui, recent->last.l_uf);
2867 chars = strlen(buf);
2868 if (REQ_ROOM - chars < 1)
2869 break;
2870 memcpy(req, buf, chars + 1);
2871 req += chars;
2872 }
2873 }
2874
2875 set_ctrl_c_hook(NULL);
2876 c_mru_l_rc = TRUE;
2877 goto retain_hash_table;
2878
2879 cleanup_return:
2880 free(hash_table);
2881 hash_table = NULL;
2882
2883 retain_hash_table:
2884 if (mon != NULL)
2885 free(mon);
2886
2887 return c_mru_l_rc;
2888 }
2889
2890
2891 /*
2892 * qcmp_mru_addr - sort MRU entries by remote address.
2893 *
2894 * All IPv4 addresses sort before any IPv6, addresses are sorted by
2895 * value within address family.
2896 */
2897 static int
2898 qcmp_mru_addr(
2899 const void *v1,
2900 const void *v2
2901 )
2902 {
2903 const mru * const * ppm1 = v1;
2904 const mru * const * ppm2 = v2;
2905 const mru * pm1;
2906 const mru * pm2;
2907 u_short af1;
2908 u_short af2;
2909 size_t cmplen;
2910 size_t addr_off;
2911
2912 pm1 = *ppm1;
2913 pm2 = *ppm2;
2914
2915 af1 = AF(&pm1->addr);
2916 af2 = AF(&pm2->addr);
2917
2918 if (af1 != af2)
2919 return (AF_INET == af1)
2920 ? -1
2921 : 1;
2922
2923 cmplen = SIZEOF_INADDR(af1);
2924 addr_off = (AF_INET == af1)
2925 ? offsetof(struct sockaddr_in, sin_addr)
2926 : offsetof(struct sockaddr_in6, sin6_addr);
2927
2928 return memcmp((const char *)&pm1->addr + addr_off,
2929 (const char *)&pm2->addr + addr_off,
2930 cmplen);
2931 }
2932
2933
2934 static int
2935 qcmp_mru_r_addr(
2936 const void *v1,
2937 const void *v2
2938 )
2939 {
2940 return -qcmp_mru_addr(v1, v2);
2941 }
2942
2943
2944 /*
2945 * qcmp_mru_count - sort MRU entries by times seen (hit count).
2946 */
2947 static int
2948 qcmp_mru_count(
2949 const void *v1,
2950 const void *v2
2951 )
2952 {
2953 const mru * const * ppm1 = v1;
2954 const mru * const * ppm2 = v2;
2955 const mru * pm1;
2956 const mru * pm2;
2957
2958 pm1 = *ppm1;
2959 pm2 = *ppm2;
2960
2961 return (pm1->count < pm2->count)
2962 ? -1
2963 : ((pm1->count == pm2->count)
2964 ? 0
2965 : 1);
2966 }
2967
2968
2969 static int
2970 qcmp_mru_r_count(
2971 const void *v1,
2972 const void *v2
2973 )
2974 {
2975 return -qcmp_mru_count(v1, v2);
2976 }
2977
2978
2979 /*
2980 * qcmp_mru_avgint - sort MRU entries by average interval.
2981 */
2982 static int
2983 qcmp_mru_avgint(
2984 const void *v1,
2985 const void *v2
2986 )
2987 {
2988 const mru * const * ppm1 = v1;
2989 const mru * const * ppm2 = v2;
2990 const mru * pm1;
2991 const mru * pm2;
2992 l_fp interval;
2993 double avg1;
2994 double avg2;
2995
2996 pm1 = *ppm1;
2997 pm2 = *ppm2;
2998
2999 interval = pm1->last;
3000 L_SUB(&interval, &pm1->first);
3001 LFPTOD(&interval, avg1);
3002 avg1 /= pm1->count;
3003
3004 interval = pm2->last;
3005 L_SUB(&interval, &pm2->first);
3006 LFPTOD(&interval, avg2);
3007 avg2 /= pm2->count;
3008
3009 if (avg1 < avg2)
3010 return -1;
3011 else if (avg1 > avg2)
3012 return 1;
3013
3014 /* secondary sort on lstint - rarely tested */
3015 if (L_ISEQU(&pm1->last, &pm2->last))
3016 return 0;
3017 else if (L_ISGEQ(&pm1->last, &pm2->last))
3018 return -1;
3019 else
3020 return 1;
3021 }
3022
3023
3024 static int
3025 qcmp_mru_r_avgint(
3026 const void *v1,
3027 const void *v2
3028 )
3029 {
3030 return -qcmp_mru_avgint(v1, v2);
3031 }
3032
3033
3034 /*
3035 * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most
3036 * Recently Used (seen) remote address list from ntpd.
3037 *
3038 * Similar to ntpdc's monlist command, but not limited to a single
3039 * request/response, and thereby not limited to a few hundred remote
3040 * addresses.
3041 *
3042 * See ntpd/ntp_control.c read_mru_list() for comments on the way
3043 * CTL_OP_READ_MRU is designed to be used.
3044 *
3045 * mrulist intentionally differs from monlist in the way the avgint
3046 * column is calculated. monlist includes the time after the last
3047 * packet from the client until the monlist query time in the average,
3048 * while mrulist excludes it. That is, monlist's average interval grows
3049 * over time for remote addresses not heard from in some time, while it
3050 * remains unchanged in mrulist. This also affects the avgint value for
3051 * entries representing a single packet, with identical first and last
3052 * timestamps. mrulist shows 0 avgint, monlist shows a value identical
3053 * to lstint.
3054 */
3055 static void
3056 mrulist(
3057 struct parse * pcmd,
3058 FILE * fp
3059 )
3060 {
3061 const char mincount_eq[] = "mincount=";
3062 const char resall_eq[] = "resall=";
3063 const char resany_eq[] = "resany=";
3064 const char maxlstint_eq[] = "maxlstint=";
3065 const char laddr_eq[] = "laddr=";
3066 const char sort_eq[] = "sort=";
3067 mru_sort_order order;
3068 size_t n;
3069 char parms_buf[128];
3070 char buf[24];
3071 char *parms;
3072 const char *arg;
3073 size_t cb;
3074 mru **sorted;
3075 mru **ppentry;
3076 mru *recent;
3077 l_fp now;
3078 l_fp interval;
3079 double favgint;
3080 double flstint;
3081 int avgint;
3082 int lstint;
3083 size_t i;
3084
3085 order = MRUSORT_DEF;
3086 parms_buf[0] = '\0';
3087 parms = parms_buf;
3088 for (i = 0; i < pcmd->nargs; i++) {
3089 arg = pcmd->argval[i].string;
3090 if (arg != NULL) {
3091 cb = strlen(arg) + 1;
3092 if ((!strncmp(resall_eq, arg, sizeof(resall_eq)
3093 - 1) || !strncmp(resany_eq, arg,
3094 sizeof(resany_eq) - 1) || !strncmp(
3095 mincount_eq, arg, sizeof(mincount_eq) - 1)
3096 || !strncmp(laddr_eq, arg, sizeof(laddr_eq)
3097 - 1) || !strncmp(maxlstint_eq, arg,
3098 sizeof(laddr_eq) - 1)) && parms + cb + 2 <=
3099 parms_buf + sizeof(parms_buf)) {
3100 /* these are passed intact to ntpd */
3101 memcpy(parms, ", ", 2);
3102 parms += 2;
3103 memcpy(parms, arg, cb);
3104 parms += cb - 1;
3105 } else if (!strncmp(sort_eq, arg,
3106 sizeof(sort_eq) - 1)) {
3107 arg += sizeof(sort_eq) - 1;
3108 for (n = 0;
3109 n < COUNTOF(mru_sort_keywords);
3110 n++)
3111 if (!strcmp(mru_sort_keywords[n],
3112 arg))
3113 break;
3114 if (n < COUNTOF(mru_sort_keywords))
3115 order = n;
3116 } else if (!strcmp("limited", arg) ||
3117 !strcmp("kod", arg)) {
3118 /* transform to resany=... */
3119 snprintf(buf, sizeof(buf),
3120 ", resany=0x%x",
3121 ('k' == arg[0])
3122 ? RES_KOD
3123 : RES_LIMITED);
3124 cb = 1 + strlen(buf);
3125 if (parms + cb <
3126 parms_buf + sizeof(parms_buf)) {
3127 memcpy(parms, buf, cb);
3128 parms += cb - 1;
3129 }
3130 } else
3131 fprintf(stderr,
3132 "ignoring unrecognized mrulist parameter: %s\n",
3133 arg);
3134 }
3135 }
3136 parms = parms_buf;
3137
3138 if (!collect_mru_list(parms, &now))
3139 return;
3140
3141 /* display the results */
3142 if (rawmode)
3143 goto cleanup_return;
3144
3145 /* construct an array of entry pointers in default order */
3146 sorted = eallocarray(mru_count, sizeof(*sorted));
3147 ppentry = sorted;
3148 if (MRUSORT_R_DEF != order) {
3149 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3150 NTP_INSIST(ppentry < sorted + mru_count);
3151 *ppentry = recent;
3152 ppentry++;
3153 ITER_DLIST_END()
3154 } else {
3155 REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3156 NTP_INSIST(ppentry < sorted + mru_count);
3157 *ppentry = recent;
3158 ppentry++;
3159 REV_ITER_DLIST_END()
3160 }
3161
3162 if (ppentry - sorted != (int)mru_count) {
3163 fprintf(stderr,
3164 "mru_count %u should match MRU list depth %ld.\n",
3165 mru_count, (long)(ppentry - sorted));
3166 free(sorted);
3167 goto cleanup_return;
3168 }
3169
3170 /* re-sort sorted[] if not default or reverse default */
3171 if (MRUSORT_R_DEF < order)
3172 qsort(sorted, mru_count, sizeof(sorted[0]),
3173 mru_qcmp_table[order]);
3174
3175 printf( "lstint avgint rstr r m v count rport remote address\n"
3176 "==============================================================================\n");
3177 /* '=' x 78 */
3178 for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) {
3179 recent = *ppentry;
3180 interval = now;
3181 L_SUB(&interval, &recent->last);
3182 LFPTOD(&interval, flstint);
3183 lstint = (int)(flstint + 0.5);
3184 interval = recent->last;
3185 L_SUB(&interval, &recent->first);
3186 LFPTOD(&interval, favgint);
3187 favgint /= recent->count;
3188 avgint = (int)(favgint + 0.5);
3189 fprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n",
3190 lstint, avgint, recent->rs,
3191 (RES_KOD & recent->rs)
3192 ? 'K'
3193 : (RES_LIMITED & recent->rs)
3194 ? 'L'
3195 : '.',
3196 (int)recent->mode, (int)recent->ver,
3197 recent->count, SRCPORT(&recent->addr),
3198 nntohost(&recent->addr));
3199 if (showhostnames)
3200 fflush(fp);
3201 }
3202 fflush(fp);
3203 if (debug) {
3204 fprintf(stderr,
3205 "--- completed, freeing sorted[] pointers\n");
3206 fflush(stderr);
3207 }
3208 free(sorted);
3209
3210 cleanup_return:
3211 if (debug) {
3212 fprintf(stderr, "... freeing MRU entries\n");
3213 fflush(stderr);
3214 }
3215 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3216 free(recent);
3217 ITER_DLIST_END()
3218 if (debug) {
3219 fprintf(stderr, "... freeing hash_table[]\n");
3220 fflush(stderr);
3221 }
3222 free(hash_table);
3223 hash_table = NULL;
3224 INIT_DLIST(mru_list, mlink);
3225 }
3226
3227
3228 /*
3229 * validate_ifnum - helper for ifstats()
3230 *
3231 * Ensures rows are received in order and complete.
3232 */
3233 static void
3234 validate_ifnum(
3235 FILE * fp,
3236 u_int ifnum,
3237 int * pfields,
3238 ifstats_row * prow
3239 )
3240 {
3241 if (prow->ifnum == ifnum)
3242 return;
3243 if (prow->ifnum + 1 <= ifnum) {
3244 if (*pfields < IFSTATS_FIELDS)
3245 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3246 *pfields, IFSTATS_FIELDS);
3247 *pfields = 0;
3248 prow->ifnum = ifnum;
3249 return;
3250 }
3251 fprintf(stderr,
3252 "received if index %u, have %d of %d fields for index %u, aborting.\n",
3253 ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum);
3254 exit(1);
3255 }
3256
3257
3258 /*
3259 * another_ifstats_field - helper for ifstats()
3260 *
3261 * If all fields for the row have been received, print it.
3262 */
3263 static void
3264 another_ifstats_field(
3265 int * pfields,
3266 ifstats_row * prow,
3267 FILE * fp
3268 )
3269 {
3270 u_int ifnum;
3271
3272 (*pfields)++;
3273 /* we understand 12 tags */
3274 if (IFSTATS_FIELDS > *pfields)
3275 return;
3276 /*
3277 " interface name send\n"
3278 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
3279 "==============================================================================\n");
3280 */
3281 fprintf(fp,
3282 "%3u %-24.24s %c %4x %3d %2d %6d %6d %6d %5d %8d\n"
3283 " %s\n",
3284 prow->ifnum, prow->name,
3285 (prow->enabled)
3286 ? '.'
3287 : 'D',
3288 prow->flags, prow->ttl, prow->mcast_count,
3289 prow->received, prow->sent, prow->send_errors,
3290 prow->peer_count, prow->uptime, sptoa(&prow->addr));
3291 if (!SOCK_UNSPEC(&prow->bcast))
3292 fprintf(fp, " %s\n", sptoa(&prow->bcast));
3293 ifnum = prow->ifnum;
3294 ZERO(*prow);
3295 prow->ifnum = ifnum;
3296 }
3297
3298
3299 /*
3300 * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats.
3301 */
3302 static void
3303 ifstats(
3304 struct parse * pcmd,
3305 FILE * fp
3306 )
3307 {
3308 const char addr_fmt[] = "addr.%u";
3309 const char bcast_fmt[] = "bcast.%u";
3310 const char en_fmt[] = "en.%u"; /* enabled */
3311 const char flags_fmt[] = "flags.%u";
3312 const char mc_fmt[] = "mc.%u"; /* mcast count */
3313 const char name_fmt[] = "name.%u";
3314 const char pc_fmt[] = "pc.%u"; /* peer count */
3315 const char rx_fmt[] = "rx.%u";
3316 const char tl_fmt[] = "tl.%u"; /* ttl */
3317 const char tx_fmt[] = "tx.%u";
3318 const char txerr_fmt[] = "txerr.%u";
3319 const char up_fmt[] = "up.%u"; /* uptime */
3320 const char * datap;
3321 int qres;
3322 int dsize;
3323 u_short rstatus;
3324 char * tag;
3325 char * val;
3326 int fields;
3327 u_int ui;
3328 ifstats_row row;
3329 int comprende;
3330 size_t len;
3331
3332 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus,
3333 &dsize, &datap);
3334 if (qres) /* message already displayed */
3335 return;
3336
3337 fprintf(fp,
3338 " interface name send\n"
3339 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
3340 "==============================================================================\n");
3341 /* '=' x 78 */
3342
3343 ZERO(row);
3344 fields = 0;
3345 ui = 0;
3346 while (nextvar(&dsize, &datap, &tag, &val)) {
3347 if (debug > 1)
3348 fprintf(stderr, "nextvar gave: %s = %s\n", tag,
3349 (NULL == val)
3350 ? ""
3351 : val);
3352 comprende = FALSE;
3353 switch(tag[0]) {
3354
3355 case 'a':
3356 if (1 == sscanf(tag, addr_fmt, &ui) &&
3357 decodenetnum(val, &row.addr))
3358 comprende = TRUE;
3359 break;
3360
3361 case 'b':
3362 if (1 == sscanf(tag, bcast_fmt, &ui) &&
3363 (NULL == val ||
3364 decodenetnum(val, &row.bcast)))
3365 comprende = TRUE;
3366 break;
3367
3368 case 'e':
3369 if (1 == sscanf(tag, en_fmt, &ui) &&
3370 1 == sscanf(val, "%d", &row.enabled))
3371 comprende = TRUE;
3372 break;
3373
3374 case 'f':
3375 if (1 == sscanf(tag, flags_fmt, &ui) &&
3376 1 == sscanf(val, "0x%x", &row.flags))
3377 comprende = TRUE;
3378 break;
3379
3380 case 'm':
3381 if (1 == sscanf(tag, mc_fmt, &ui) &&
3382 1 == sscanf(val, "%d", &row.mcast_count))
3383 comprende = TRUE;
3384 break;
3385
3386 case 'n':
3387 if (1 == sscanf(tag, name_fmt, &ui)) {
3388 /* strip quotes */
3389 INSIST(val);
3390 len = strlen(val);
3391 if (len >= 2 &&
3392 len - 2 < sizeof(row.name)) {
3393 len -= 2;
3394 memcpy(row.name, val + 1, len);
3395 row.name[len] = '\0';
3396 comprende = TRUE;
3397 }
3398 }
3399 break;
3400
3401 case 'p':
3402 if (1 == sscanf(tag, pc_fmt, &ui) &&
3403 1 == sscanf(val, "%d", &row.peer_count))
3404 comprende = TRUE;
3405 break;
3406
3407 case 'r':
3408 if (1 == sscanf(tag, rx_fmt, &ui) &&
3409 1 == sscanf(val, "%d", &row.received))
3410 comprende = TRUE;
3411 break;
3412
3413 case 't':
3414 if (1 == sscanf(tag, tl_fmt, &ui) &&
3415 1 == sscanf(val, "%d", &row.ttl))
3416 comprende = TRUE;
3417 else if (1 == sscanf(tag, tx_fmt, &ui) &&
3418 1 == sscanf(val, "%d", &row.sent))
3419 comprende = TRUE;
3420 else if (1 == sscanf(tag, txerr_fmt, &ui) &&
3421 1 == sscanf(val, "%d", &row.send_errors))
3422 comprende = TRUE;
3423 break;
3424
3425 case 'u':
3426 if (1 == sscanf(tag, up_fmt, &ui) &&
3427 1 == sscanf(val, "%d", &row.uptime))
3428 comprende = TRUE;
3429 break;
3430 }
3431
3432 if (comprende) {
3433 /* error out if rows out of order */
3434 validate_ifnum(fp, ui, &fields, &row);
3435 /* if the row is complete, print it */
3436 another_ifstats_field(&fields, &row, fp);
3437 }
3438 }
3439 if (fields != IFSTATS_FIELDS)
3440 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3441 fields, IFSTATS_FIELDS);
3442
3443 fflush(fp);
3444 }
3445
3446
3447 /*
3448 * validate_reslist_idx - helper for reslist()
3449 *
3450 * Ensures rows are received in order and complete.
3451 */
3452 static void
3453 validate_reslist_idx(
3454 FILE * fp,
3455 u_int idx,
3456 int * pfields,
3457 reslist_row * prow
3458 )
3459 {
3460 if (prow->idx == idx)
3461 return;
3462 if (prow->idx + 1 == idx) {
3463 if (*pfields < RESLIST_FIELDS)
3464 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3465 *pfields, RESLIST_FIELDS);
3466 *pfields = 0;
3467 prow->idx = idx;
3468 return;
3469 }
3470 fprintf(stderr,
3471 "received reslist index %u, have %d of %d fields for index %u, aborting.\n",
3472 idx, *pfields, RESLIST_FIELDS, prow->idx);
3473 exit(1);
3474 }
3475
3476
3477 /*
3478 * another_reslist_field - helper for reslist()
3479 *
3480 * If all fields for the row have been received, print it.
3481 */
3482 static void
3483 another_reslist_field(
3484 int * pfields,
3485 reslist_row * prow,
3486 FILE * fp
3487 )
3488 {
3489 char addrmaskstr[128];
3490 int prefix; /* subnet mask as prefix bits count */
3491 u_int idx;
3492
3493 (*pfields)++;
3494 /* we understand 4 tags */
3495 if (RESLIST_FIELDS > *pfields)
3496 return;
3497
3498 prefix = sockaddr_masktoprefixlen(&prow->mask);
3499 if (prefix >= 0)
3500 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d",
3501 stoa(&prow->addr), prefix);
3502 else
3503 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s",
3504 stoa(&prow->addr), stoa(&prow->mask));
3505
3506 /*
3507 " hits addr/prefix or addr mask\n"
3508 " restrictions\n"
3509 "==============================================================================\n");
3510 */
3511 fprintf(fp,
3512 "%10lu %s\n"
3513 " %s\n",
3514 prow->hits, addrmaskstr, prow->flagstr);
3515 idx = prow->idx;
3516 ZERO(*prow);
3517 prow->idx = idx;
3518 }
3519
3520
3521 /*
3522 * reslist - ntpq -c reslist modeled on ntpdc -c reslist.
3523 */
3524 static void
3525 reslist(
3526 struct parse * pcmd,
3527 FILE * fp
3528 )
3529 {
3530 const char addr_fmtu[] = "addr.%u";
3531 const char mask_fmtu[] = "mask.%u";
3532 const char hits_fmt[] = "hits.%u";
3533 const char flags_fmt[] = "flags.%u";
3534 const char qdata[] = "addr_restrictions";
3535 const int qdata_chars = COUNTOF(qdata) - 1;
3536 const char * datap;
3537 int qres;
3538 int dsize;
3539 u_short rstatus;
3540 char * tag;
3541 char * val;
3542 int fields;
3543 u_int ui;
3544 reslist_row row;
3545 int comprende;
3546 size_t len;
3547
3548 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars,
3549 qdata, &rstatus, &dsize, &datap);
3550 if (qres) /* message already displayed */
3551 return;
3552
3553 fprintf(fp,
3554 " hits addr/prefix or addr mask\n"
3555 " restrictions\n"
3556 "==============================================================================\n");
3557 /* '=' x 78 */
3558
3559 ZERO(row);
3560 fields = 0;
3561 ui = 0;
3562 while (nextvar(&dsize, &datap, &tag, &val)) {
3563 if (debug > 1)
3564 fprintf(stderr, "nextvar gave: %s = %s\n", tag,
3565 (NULL == val)
3566 ? ""
3567 : val);
3568 comprende = FALSE;
3569 switch(tag[0]) {
3570
3571 case 'a':
3572 if (1 == sscanf(tag, addr_fmtu, &ui) &&
3573 decodenetnum(val, &row.addr))
3574 comprende = TRUE;
3575 break;
3576
3577 case 'f':
3578 if (1 == sscanf(tag, flags_fmt, &ui)) {
3579 if (NULL == val) {
3580 row.flagstr[0] = '\0';
3581 comprende = TRUE;
3582 } else {
3583 len = strlen(val);
3584 memcpy(row.flagstr, val, len);
3585 row.flagstr[len] = '\0';
3586 comprende = TRUE;
3587 }
3588 }
3589 break;
3590
3591 case 'h':
3592 if (1 == sscanf(tag, hits_fmt, &ui) &&
3593 1 == sscanf(val, "%lu", &row.hits))
3594 comprende = TRUE;
3595 break;
3596
3597 case 'm':
3598 if (1 == sscanf(tag, mask_fmtu, &ui) &&
3599 decodenetnum(val, &row.mask))
3600 comprende = TRUE;
3601 break;
3602 }
3603
3604 if (comprende) {
3605 /* error out if rows out of order */
3606 validate_reslist_idx(fp, ui, &fields, &row);
3607 /* if the row is complete, print it */
3608 another_reslist_field(&fields, &row, fp);
3609 }
3610 }
3611 if (fields != RESLIST_FIELDS)
3612 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3613 fields, RESLIST_FIELDS);
3614
3615 fflush(fp);
3616 }
3617
3618
3619 /*
3620 * collect_display_vdc
3621 */
3622 static void
3623 collect_display_vdc(
3624 associd_t as,
3625 vdc * table,
3626 int decodestatus,
3627 FILE * fp
3628 )
3629 {
3630 static const char * const suf[2] = { "adr", "port" };
3631 static const char * const leapbits[4] = { "00", "01",
3632 "10", "11" };
3633 struct varlist vl[MAXLIST];
3634 char tagbuf[32];
3635 vdc *pvdc;
3636 u_short rstatus;
3637 int rsize;
3638 const char *rdata;
3639 int qres;
3640 char *tag;
3641 char *val;
3642 u_int n;
3643 size_t len;
3644 int match;
3645 u_long ul;
3646 int vtype;
3647
3648 ZERO(vl);
3649 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3650 ZERO(pvdc->v);
3651 if (NTP_ADD != pvdc->type) {
3652 doaddvlist(vl, pvdc->tag);
3653 } else {
3654 for (n = 0; n < COUNTOF(suf); n++) {
3655 snprintf(tagbuf, sizeof(tagbuf), "%s%s",
3656 pvdc->tag, suf[n]);
3657 doaddvlist(vl, tagbuf);
3658 }
3659 }
3660 }
3661 qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize,
3662 &rdata);
3663 doclearvlist(vl);
3664 if (qres)
3665 return; /* error msg already displayed */
3666
3667 /*
3668 * iterate over the response variables filling vdc_table with
3669 * the retrieved values.
3670 */
3671 while (nextvar(&rsize, &rdata, &tag, &val)) {
3672 if (NULL == val)
3673 continue;
3674 n = 0;
3675 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3676 len = strlen(pvdc->tag);
3677 if (strncmp(tag, pvdc->tag, len))
3678 continue;
3679 if (NTP_ADD != pvdc->type) {
3680 if ('\0' != tag[len])
3681 continue;
3682 break;
3683 }
3684 match = FALSE;
3685 for (n = 0; n < COUNTOF(suf); n++) {
3686 if (strcmp(tag + len, suf[n]))
3687 continue;
3688 match = TRUE;
3689 break;
3690 }
3691 if (match)
3692 break;
3693 }
3694 if (NULL == pvdc->tag)
3695 continue;
3696 switch (pvdc->type) {
3697
3698 case NTP_STR:
3699 /* strip surrounding double quotes */
3700 if ('"' == val[0]) {
3701 len = strlen(val);
3702 if (len > 0 && '"' == val[len - 1]) {
3703 val[len - 1] = '\0';
3704 val++;
3705 }
3706 }
3707 /* fallthru */
3708 case NTP_MODE: /* fallthru */
3709 case NTP_2BIT:
3710 pvdc->v.str = estrdup(val);
3711 break;
3712
3713 case NTP_LFP:
3714 decodets(val, &pvdc->v.lfp);
3715 break;
3716
3717 case NTP_ADP:
3718 if (!decodenetnum(val, &pvdc->v.sau))
3719 fprintf(stderr, "malformed %s=%s\n",
3720 pvdc->tag, val);
3721 break;
3722
3723 case NTP_ADD:
3724 if (0 == n) { /* adr */
3725 if (!decodenetnum(val, &pvdc->v.sau))
3726 fprintf(stderr,
3727 "malformed %s=%s\n",
3728 pvdc->tag, val);
3729 } else { /* port */
3730 if (atouint(val, &ul))
3731 SET_PORT(&pvdc->v.sau,
3732 (u_short)ul);
3733 }
3734 break;
3735 }
3736 }
3737
3738 /* and display */
3739 if (decodestatus) {
3740 vtype = (0 == as)
3741 ? TYPE_SYS
3742 : TYPE_PEER;
3743 fprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus,
3744 statustoa(vtype, rstatus));
3745 }
3746
3747 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3748 switch (pvdc->type) {
3749
3750 case NTP_STR:
3751 if (pvdc->v.str != NULL) {
3752 fprintf(fp, "%s %s\n", pvdc->display,
3753 pvdc->v.str);
3754 free(pvdc->v.str);
3755 pvdc->v.str = NULL;
3756 }
3757 break;
3758
3759 case NTP_ADD: /* fallthru */
3760 case NTP_ADP:
3761 fprintf(fp, "%s %s\n", pvdc->display,
3762 nntohostp(&pvdc->v.sau));
3763 break;
3764
3765 case NTP_LFP:
3766 fprintf(fp, "%s %s\n", pvdc->display,
3767 prettydate(&pvdc->v.lfp));
3768 break;
3769
3770 case NTP_MODE:
3771 atouint(pvdc->v.str, &ul);
3772 fprintf(fp, "%s %s\n", pvdc->display,
3773 modetoa((int)ul));
3774 break;
3775
3776 case NTP_2BIT:
3777 atouint(pvdc->v.str, &ul);
3778 fprintf(fp, "%s %s\n", pvdc->display,
3779 leapbits[ul & 0x3]);
3780 break;
3781
3782 default:
3783 fprintf(stderr, "unexpected vdc type %d for %s\n",
3784 pvdc->type, pvdc->tag);
3785 break;
3786 }
3787 }
3788 }
3789
3790
3791 /*
3792 * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats
3793 */
3794 static void
3795 sysstats(
3796 struct parse *pcmd,
3797 FILE *fp
3798 )
3799 {
3800 static vdc sysstats_vdc[] = {
3801 VDC_INIT("ss_uptime", "uptime: ", NTP_STR),
3802 VDC_INIT("ss_reset", "sysstats reset: ", NTP_STR),
3803 VDC_INIT("ss_received", "packets received: ", NTP_STR),
3804 VDC_INIT("ss_thisver", "current version: ", NTP_STR),
3805 VDC_INIT("ss_oldver", "older version: ", NTP_STR),
3806 VDC_INIT("ss_badformat", "bad length or format: ", NTP_STR),
3807 VDC_INIT("ss_badauth", "authentication failed:", NTP_STR),
3808 VDC_INIT("ss_declined", "declined: ", NTP_STR),
3809 VDC_INIT("ss_restricted", "restricted: ", NTP_STR),
3810 VDC_INIT("ss_limited", "rate limited: ", NTP_STR),
3811 VDC_INIT("ss_kodsent", "KoD responses: ", NTP_STR),
3812 VDC_INIT("ss_processed", "processed for time: ", NTP_STR),
3813 VDC_INIT(NULL, NULL, 0)
3814 };
3815
3816 collect_display_vdc(0, sysstats_vdc, FALSE, fp);
3817 }
3818
3819
3820 /*
3821 * sysinfo - modeled on ntpdc's sysinfo
3822 */
3823 static void
3824 sysinfo(
3825 struct parse *pcmd,
3826 FILE *fp
3827 )
3828 {
3829 static vdc sysinfo_vdc[] = {
3830 VDC_INIT("peeradr", "system peer: ", NTP_ADP),
3831 VDC_INIT("peermode", "system peer mode: ", NTP_MODE),
3832 VDC_INIT("leap", "leap indicator: ", NTP_2BIT),
3833 VDC_INIT("stratum", "stratum: ", NTP_STR),
3834 VDC_INIT("precision", "log2 precision: ", NTP_STR),
3835 VDC_INIT("rootdelay", "root delay: ", NTP_STR),
3836 VDC_INIT("rootdisp", "root dispersion: ", NTP_STR),
3837 VDC_INIT("refid", "reference ID: ", NTP_STR),
3838 VDC_INIT("reftime", "reference time: ", NTP_LFP),
3839 VDC_INIT("sys_jitter", "system jitter: ", NTP_STR),
3840 VDC_INIT("clk_jitter", "clock jitter: ", NTP_STR),
3841 VDC_INIT("clk_wander", "clock wander: ", NTP_STR),
3842 VDC_INIT("bcastdelay", "broadcast delay: ", NTP_STR),
3843 VDC_INIT("authdelay", "symm. auth. delay:", NTP_STR),
3844 VDC_INIT(NULL, NULL, 0)
3845 };
3846
3847 collect_display_vdc(0, sysinfo_vdc, TRUE, fp);
3848 }
3849
3850
3851 /*
3852 * kerninfo - modeled on ntpdc's kerninfo
3853 */
3854 static void
3855 kerninfo(
3856 struct parse *pcmd,
3857 FILE *fp
3858 )
3859 {
3860 static vdc kerninfo_vdc[] = {
3861 VDC_INIT("koffset", "pll offset: ", NTP_STR),
3862 VDC_INIT("kfreq", "pll frequency: ", NTP_STR),
3863 VDC_INIT("kmaxerr", "maximum error: ", NTP_STR),
3864 VDC_INIT("kesterr", "estimated error: ", NTP_STR),
3865 VDC_INIT("kstflags", "kernel status: ", NTP_STR),
3866 VDC_INIT("ktimeconst", "pll time constant: ", NTP_STR),
3867 VDC_INIT("kprecis", "precision: ", NTP_STR),
3868 VDC_INIT("kfreqtol", "frequency tolerance: ", NTP_STR),
3869 VDC_INIT("kppsfreq", "pps frequency: ", NTP_STR),
3870 VDC_INIT("kppsstab", "pps stability: ", NTP_STR),
3871 VDC_INIT("kppsjitter", "pps jitter: ", NTP_STR),
3872 VDC_INIT("kppscalibdur", "calibration interval ", NTP_STR),
3873 VDC_INIT("kppscalibs", "calibration cycles: ", NTP_STR),
3874 VDC_INIT("kppsjitexc", "jitter exceeded: ", NTP_STR),
3875 VDC_INIT("kppsstbexc", "stability exceeded: ", NTP_STR),
3876 VDC_INIT("kppscaliberrs", "calibration errors: ", NTP_STR),
3877 VDC_INIT(NULL, NULL, 0)
3878 };
3879
3880 collect_display_vdc(0, kerninfo_vdc, TRUE, fp);
3881 }
3882
3883
3884 /*
3885 * monstats - implements ntpq -c monstats
3886 */
3887 static void
3888 monstats(
3889 struct parse *pcmd,
3890 FILE *fp
3891 )
3892 {
3893 static vdc monstats_vdc[] = {
3894 VDC_INIT("mru_enabled", "enabled: ", NTP_STR),
3895 VDC_INIT("mru_depth", "addresses: ", NTP_STR),
3896 VDC_INIT("mru_deepest", "peak addresses: ", NTP_STR),
3897 VDC_INIT("mru_maxdepth", "maximum addresses: ", NTP_STR),
3898 VDC_INIT("mru_mindepth", "reclaim above count:", NTP_STR),
3899 VDC_INIT("mru_maxage", "reclaim older than: ", NTP_STR),
3900 VDC_INIT("mru_mem", "kilobytes: ", NTP_STR),
3901 VDC_INIT("mru_maxmem", "maximum kilobytes: ", NTP_STR),
3902 VDC_INIT(NULL, NULL, 0)
3903 };
3904
3905 collect_display_vdc(0, monstats_vdc, FALSE, fp);
3906 }
3907
3908
3909 /*
3910 * iostats - ntpq -c iostats - network input and output counters
3911 */
3912 static void
3913 iostats(
3914 struct parse *pcmd,
3915 FILE *fp
3916 )
3917 {
3918 static vdc iostats_vdc[] = {
3919 VDC_INIT("iostats_reset", "time since reset: ", NTP_STR),
3920 VDC_INIT("total_rbuf", "receive buffers: ", NTP_STR),
3921 VDC_INIT("free_rbuf", "free receive buffers: ", NTP_STR),
3922 VDC_INIT("used_rbuf", "used receive buffers: ", NTP_STR),
3923 VDC_INIT("rbuf_lowater", "low water refills: ", NTP_STR),
3924 VDC_INIT("io_dropped", "dropped packets: ", NTP_STR),
3925 VDC_INIT("io_ignored", "ignored packets: ", NTP_STR),
3926 VDC_INIT("io_received", "received packets: ", NTP_STR),
3927 VDC_INIT("io_sent", "packets sent: ", NTP_STR),
3928 VDC_INIT("io_sendfailed", "packet send failures: ", NTP_STR),
3929 VDC_INIT("io_wakeups", "input wakeups: ", NTP_STR),
3930 VDC_INIT("io_goodwakeups", "useful input wakeups: ", NTP_STR),
3931 VDC_INIT(NULL, NULL, 0)
3932 };
3933
3934 collect_display_vdc(0, iostats_vdc, FALSE, fp);
3935 }
3936
3937
3938 /*
3939 * timerstats - ntpq -c timerstats - interval timer counters
3940 */
3941 static void
3942 timerstats(
3943 struct parse *pcmd,
3944 FILE *fp
3945 )
3946 {
3947 static vdc timerstats_vdc[] = {
3948 VDC_INIT("timerstats_reset", "time since reset: ", NTP_STR),
3949 VDC_INIT("timer_overruns", "timer overruns: ", NTP_STR),
3950 VDC_INIT("timer_xmts", "calls to transmit: ", NTP_STR),
3951 VDC_INIT(NULL, NULL, 0)
3952 };
3953
3954 collect_display_vdc(0, timerstats_vdc, FALSE, fp);
3955 }
3956
3957
3958 /*
3959 * authinfo - implements ntpq -c authinfo
3960 */
3961 static void
3962 authinfo(
3963 struct parse *pcmd,
3964 FILE *fp
3965 )
3966 {
3967 static vdc authinfo_vdc[] = {
3968 VDC_INIT("authreset", "time since reset:", NTP_STR),
3969 VDC_INIT("authkeys", "stored keys: ", NTP_STR),
3970 VDC_INIT("authfreek", "free keys: ", NTP_STR),
3971 VDC_INIT("authklookups", "key lookups: ", NTP_STR),
3972 VDC_INIT("authknotfound", "keys not found: ", NTP_STR),
3973 VDC_INIT("authkuncached", "uncached keys: ", NTP_STR),
3974 VDC_INIT("authkexpired", "expired keys: ", NTP_STR),
3975 VDC_INIT("authencrypts", "encryptions: ", NTP_STR),
3976 VDC_INIT("authdecrypts", "decryptions: ", NTP_STR),
3977 VDC_INIT(NULL, NULL, 0)
3978 };
3979
3980 collect_display_vdc(0, authinfo_vdc, FALSE, fp);
3981 }
3982
3983
3984 /*
3985 * pstats - show statistics for a peer
3986 */
3987 static void
3988 pstats(
3989 struct parse *pcmd,
3990 FILE *fp
3991 )
3992 {
3993 static vdc pstats_vdc[] = {
3994 VDC_INIT("src", "remote host: ", NTP_ADD),
3995 VDC_INIT("dst", "local address: ", NTP_ADD),
3996 VDC_INIT("timerec", "time last received: ", NTP_STR),
3997 VDC_INIT("timer", "time until next send:", NTP_STR),
3998 VDC_INIT("timereach", "reachability change: ", NTP_STR),
3999 VDC_INIT("sent", "packets sent: ", NTP_STR),
4000 VDC_INIT("received", "packets received: ", NTP_STR),
4001 VDC_INIT("badauth", "bad authentication: ", NTP_STR),
4002 VDC_INIT("bogusorg", "bogus origin: ", NTP_STR),
4003 VDC_INIT("oldpkt", "duplicate: ", NTP_STR),
4004 VDC_INIT("seldisp", "bad dispersion: ", NTP_STR),
4005 VDC_INIT("selbroken", "bad reference time: ", NTP_STR),
4006 VDC_INIT("candidate", "candidate order: ", NTP_STR),
4007 VDC_INIT(NULL, NULL, 0)
4008 };
4009 associd_t associd;
4010
4011 associd = checkassocid(pcmd->argval[0].uval);
4012 if (0 == associd)
4013 return;
4014
4015 collect_display_vdc(associd, pstats_vdc, TRUE, fp);
4016 }
4017