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