ntpq-subs.c revision 1.1.1.14 1 /*
2 * ntpq-subs.c - subroutines which are called to perform ntpq commands.
3 */
4 #include <config.h>
5 #include <stdio.h>
6 #include <ctype.h>
7 #include <sys/types.h>
8 #include <sys/time.h>
9
10 #include "ntpq.h"
11 #include "ntpq-opts.h"
12
13 extern char currenthost[];
14 extern int currenthostisnum;
15 size_t maxhostlen;
16
17 /*
18 * Declarations for command handlers in here
19 */
20 static associd_t checkassocid (u_int32);
21 static struct varlist *findlistvar (struct varlist *, char *);
22 static void doaddvlist (struct varlist *, const char *);
23 static void dormvlist (struct varlist *, const char *);
24 static void doclearvlist (struct varlist *);
25 static void makequerydata (struct varlist *, size_t *, char *);
26 static int doquerylist (struct varlist *, int, associd_t, int,
27 u_short *, size_t *, const char **);
28 static void doprintvlist (struct varlist *, FILE *);
29 static void addvars (struct parse *, FILE *);
30 static void rmvars (struct parse *, FILE *);
31 static void clearvars (struct parse *, FILE *);
32 static void showvars (struct parse *, FILE *);
33 static int dolist (struct varlist *, associd_t, int, int,
34 FILE *);
35 static void readlist (struct parse *, FILE *);
36 static void writelist (struct parse *, FILE *);
37 static void readvar (struct parse *, FILE *);
38 static void writevar (struct parse *, FILE *);
39 static void clocklist (struct parse *, FILE *);
40 static void clockvar (struct parse *, FILE *);
41 static int findassidrange (u_int32, u_int32, int *, int *,
42 FILE *);
43 static void mreadlist (struct parse *, FILE *);
44 static void mreadvar (struct parse *, FILE *);
45 static void printassoc (int, FILE *);
46 static void associations (struct parse *, FILE *);
47 static void lassociations (struct parse *, FILE *);
48 static void passociations (struct parse *, FILE *);
49 static void lpassociations (struct parse *, FILE *);
50
51 #ifdef UNUSED
52 static void radiostatus (struct parse *, FILE *);
53 #endif /* UNUSED */
54
55 static void authinfo (struct parse *, FILE *);
56 static void pstats (struct parse *, FILE *);
57 static long when (l_fp *, l_fp *, l_fp *);
58 static char * prettyinterval (char *, size_t, long);
59 static int doprintpeers (struct varlist *, int, int, size_t, const char *, FILE *, int);
60 static int dogetpeers (struct varlist *, associd_t, FILE *, int);
61 static void dopeers (int, FILE *, int);
62 static void peers (struct parse *, FILE *);
63 static void doapeers (int, FILE *, int);
64 static void apeers (struct parse *, FILE *);
65 static void lpeers (struct parse *, FILE *);
66 static void doopeers (int, FILE *, int);
67 static void opeers (struct parse *, FILE *);
68 static void lopeers (struct parse *, FILE *);
69 static void config (struct parse *, FILE *);
70 static void saveconfig (struct parse *, FILE *);
71 static void config_from_file(struct parse *, FILE *);
72 static void mrulist (struct parse *, FILE *);
73 static void ifstats (struct parse *, FILE *);
74 static void reslist (struct parse *, FILE *);
75 static void sysstats (struct parse *, FILE *);
76 static void sysinfo (struct parse *, FILE *);
77 static void kerninfo (struct parse *, FILE *);
78 static void monstats (struct parse *, FILE *);
79 static void iostats (struct parse *, FILE *);
80 static void timerstats (struct parse *, FILE *);
81
82 /*
83 * Commands we understand. Ntpdc imports this.
84 */
85 struct xcmd opcmds[] = {
86 { "saveconfig", saveconfig, { NTP_STR, NO, NO, NO },
87 { "filename", "", "", ""},
88 "save ntpd configuration to file, . for current config file"},
89 { "associations", associations, { NO, NO, NO, NO },
90 { "", "", "", "" },
91 "print list of association ID's and statuses for the server's peers" },
92 { "passociations", passociations, { NO, NO, NO, NO },
93 { "", "", "", "" },
94 "print list of associations returned by last associations command" },
95 { "lassociations", lassociations, { NO, NO, NO, NO },
96 { "", "", "", "" },
97 "print list of associations including all client information" },
98 { "lpassociations", lpassociations, { NO, NO, NO, NO },
99 { "", "", "", "" },
100 "print last obtained list of associations, including client information" },
101 { "addvars", addvars, { NTP_STR, NO, NO, NO },
102 { "name[=value][,...]", "", "", "" },
103 "add variables to the variable list or change their values" },
104 { "rmvars", rmvars, { NTP_STR, NO, NO, NO },
105 { "name[,...]", "", "", "" },
106 "remove variables from the variable list" },
107 { "clearvars", clearvars, { NO, NO, NO, NO },
108 { "", "", "", "" },
109 "remove all variables from the variable list" },
110 { "showvars", showvars, { NO, NO, NO, NO },
111 { "", "", "", "" },
112 "print variables on the variable list" },
113 { "readlist", readlist, { OPT|NTP_UINT, NO, NO, NO },
114 { "assocID", "", "", "" },
115 "read the system or peer variables included in the variable list" },
116 { "rl", readlist, { OPT|NTP_UINT, NO, NO, NO },
117 { "assocID", "", "", "" },
118 "read the system or peer variables included in the variable list" },
119 { "writelist", writelist, { OPT|NTP_UINT, NO, NO, NO },
120 { "assocID", "", "", "" },
121 "write the system or peer variables included in the variable list" },
122 { "readvar", readvar, { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
123 { "assocID", "varname1", "varname2", "varname3" },
124 "read system or peer variables" },
125 { "rv", readvar, { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
126 { "assocID", "varname1", "varname2", "varname3" },
127 "read system or peer variables" },
128 { "writevar", writevar, { NTP_UINT, NTP_STR, NO, NO },
129 { "assocID", "name=value,[...]", "", "" },
130 "write system or peer variables" },
131 { "mreadlist", mreadlist, { NTP_UINT, NTP_UINT, NO, NO },
132 { "assocIDlow", "assocIDhigh", "", "" },
133 "read the peer variables in the variable list for multiple peers" },
134 { "mrl", mreadlist, { NTP_UINT, NTP_UINT, NO, NO },
135 { "assocIDlow", "assocIDhigh", "", "" },
136 "read the peer variables in the variable list for multiple peers" },
137 { "mreadvar", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
138 { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
139 "read peer variables from multiple peers" },
140 { "mrv", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
141 { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
142 "read peer variables from multiple peers" },
143 { "clocklist", clocklist, { OPT|NTP_UINT, NO, NO, NO },
144 { "assocID", "", "", "" },
145 "read the clock variables included in the variable list" },
146 { "cl", clocklist, { OPT|NTP_UINT, NO, NO, NO },
147 { "assocID", "", "", "" },
148 "read the clock variables included in the variable list" },
149 { "clockvar", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
150 { "assocID", "name=value[,...]", "", "" },
151 "read clock variables" },
152 { "cv", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
153 { "assocID", "name=value[,...]", "", "" },
154 "read clock variables" },
155 { "pstats", pstats, { NTP_UINT, NO, NO, NO },
156 { "assocID", "", "", "" },
157 "show statistics for a peer" },
158 { "peers", peers, { OPT|IP_VERSION, NO, NO, NO },
159 { "-4|-6", "", "", "" },
160 "obtain and print a list of the server's peers [IP version]" },
161 { "apeers", apeers, { OPT|IP_VERSION, NO, NO, NO },
162 { "-4|-6", "", "", "" },
163 "obtain and print a list of the server's peers and their assocIDs [IP version]" },
164 { "lpeers", lpeers, { OPT|IP_VERSION, NO, NO, NO },
165 { "-4|-6", "", "", "" },
166 "obtain and print a list of all peers and clients [IP version]" },
167 { "opeers", opeers, { OPT|IP_VERSION, NO, NO, NO },
168 { "-4|-6", "", "", "" },
169 "print peer list the old way, with dstadr shown rather than refid [IP version]" },
170 { "lopeers", lopeers, { OPT|IP_VERSION, NO, NO, NO },
171 { "-4|-6", "", "", "" },
172 "obtain and print a list of all peers and clients showing dstadr [IP version]" },
173 { ":config", config, { NTP_STR, NO, NO, NO },
174 { "<configuration command line>", "", "", "" },
175 "send a remote configuration command to ntpd" },
176 { "config-from-file", config_from_file, { NTP_STR, NO, NO, NO },
177 { "<configuration filename>", "", "", "" },
178 "configure ntpd using the configuration filename" },
179 { "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
180 { "tag=value", "tag=value", "tag=value", "tag=value" },
181 "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." },
182 { "ifstats", ifstats, { NO, NO, NO, NO },
183 { "", "", "", "" },
184 "show statistics for each local address ntpd is using" },
185 { "reslist", reslist, { NO, NO, NO, NO },
186 { "", "", "", "" },
187 "show ntpd access control list" },
188 { "sysinfo", sysinfo, { NO, NO, NO, NO },
189 { "", "", "", "" },
190 "display system summary" },
191 { "kerninfo", kerninfo, { NO, NO, NO, NO },
192 { "", "", "", "" },
193 "display kernel loop and PPS statistics" },
194 { "sysstats", sysstats, { NO, NO, NO, NO },
195 { "", "", "", "" },
196 "display system uptime and packet counts" },
197 { "monstats", monstats, { NO, NO, NO, NO },
198 { "", "", "", "" },
199 "display monitor (mrulist) counters and limits" },
200 { "authinfo", authinfo, { NO, NO, NO, NO },
201 { "", "", "", "" },
202 "display symmetric authentication counters" },
203 { "iostats", iostats, { NO, NO, NO, NO },
204 { "", "", "", "" },
205 "display network input and output counters" },
206 { "timerstats", timerstats, { NO, NO, NO, NO },
207 { "", "", "", "" },
208 "display interval timer counters" },
209 { 0, 0, { NO, NO, NO, NO },
210 { "-4|-6", "", "", "" }, "" }
211 };
212
213
214 /*
215 * Variable list data space
216 */
217 #define MAXLINE 512 /* maximum length of a line */
218 #define MAXLIST 128 /* maximum variables in list */
219 #define LENHOSTNAME 256 /* host name limit */
220
221 #define MRU_GOT_COUNT 0x1
222 #define MRU_GOT_LAST 0x2
223 #define MRU_GOT_FIRST 0x4
224 #define MRU_GOT_MV 0x8
225 #define MRU_GOT_RS 0x10
226 #define MRU_GOT_ADDR 0x20
227 #define MRU_GOT_ALL (MRU_GOT_COUNT | MRU_GOT_LAST | MRU_GOT_FIRST \
228 | MRU_GOT_MV | MRU_GOT_RS | MRU_GOT_ADDR)
229
230 /*
231 * mrulist() depends on MRUSORT_DEF and MRUSORT_RDEF being the first two
232 */
233 typedef enum mru_sort_order_tag {
234 MRUSORT_DEF = 0, /* lstint ascending */
235 MRUSORT_R_DEF, /* lstint descending */
236 MRUSORT_AVGINT, /* avgint ascending */
237 MRUSORT_R_AVGINT, /* avgint descending */
238 MRUSORT_ADDR, /* IPv4 asc. then IPv6 asc. */
239 MRUSORT_R_ADDR, /* IPv6 desc. then IPv4 desc. */
240 MRUSORT_COUNT, /* hit count ascending */
241 MRUSORT_R_COUNT, /* hit count descending */
242 MRUSORT_MAX, /* special: count of this enum */
243 } mru_sort_order;
244
245 const char * const mru_sort_keywords[MRUSORT_MAX] = {
246 "lstint", /* MRUSORT_DEF */
247 "-lstint", /* MRUSORT_R_DEF */
248 "avgint", /* MRUSORT_AVGINT */
249 "-avgint", /* MRUSORT_R_AVGINT */
250 "addr", /* MRUSORT_ADDR */
251 "-addr", /* MRUSORT_R_ADDR */
252 "count", /* MRUSORT_COUNT */
253 "-count", /* MRUSORT_R_COUNT */
254 };
255
256 typedef int (*qsort_cmp)(const void *, const void *);
257
258 /*
259 * Old CTL_PST defines for version 2.
260 */
261 #define OLD_CTL_PST_CONFIG 0x80
262 #define OLD_CTL_PST_AUTHENABLE 0x40
263 #define OLD_CTL_PST_AUTHENTIC 0x20
264 #define OLD_CTL_PST_REACH 0x10
265 #define OLD_CTL_PST_SANE 0x08
266 #define OLD_CTL_PST_DISP 0x04
267
268 #define OLD_CTL_PST_SEL_REJECT 0
269 #define OLD_CTL_PST_SEL_SELCAND 1
270 #define OLD_CTL_PST_SEL_SYNCCAND 2
271 #define OLD_CTL_PST_SEL_SYSPEER 3
272
273 char flash2[] = " .+* "; /* flash decode for version 2 */
274 char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
275
276 struct varlist {
277 const char *name;
278 char *value;
279 } g_varlist[MAXLIST] = { { 0, 0 } };
280
281 /*
282 * Imported from ntpq.c
283 */
284 extern int showhostnames;
285 extern int wideremote;
286 extern int rawmode;
287 extern struct servent *server_entry;
288 extern struct association *assoc_cache;
289 extern u_char pktversion;
290
291 typedef struct mru_tag mru;
292 struct mru_tag {
293 mru * hlink; /* next in hash table bucket */
294 DECL_DLIST_LINK(mru, mlink);
295 int count;
296 l_fp last;
297 l_fp first;
298 u_char mode;
299 u_char ver;
300 u_short rs;
301 sockaddr_u addr;
302 };
303
304 typedef struct ifstats_row_tag {
305 u_int ifnum;
306 sockaddr_u addr;
307 sockaddr_u bcast;
308 int enabled;
309 u_int flags;
310 u_int mcast_count;
311 char name[32];
312 u_int peer_count;
313 u_int received;
314 u_int sent;
315 u_int send_errors;
316 u_int ttl;
317 u_int uptime;
318 } ifstats_row;
319
320 typedef struct reslist_row_tag {
321 u_int idx;
322 sockaddr_u addr;
323 sockaddr_u mask;
324 u_long hits;
325 char flagstr[128];
326 } reslist_row;
327
328 typedef struct var_display_collection_tag {
329 const char * const tag; /* system variable */
330 const char * const display; /* descriptive text */
331 u_char type; /* NTP_STR, etc */
332 union {
333 char * str;
334 sockaddr_u sau; /* NTP_ADD */
335 l_fp lfp; /* NTP_LFP */
336 } v; /* retrieved value */
337 } vdc;
338 #if !defined(MISSING_C99_STRUCT_INIT)
339 # define VDC_INIT(a, b, c) { .tag = a, .display = b, .type = c }
340 #else
341 # define VDC_INIT(a, b, c) { a, b, c }
342 #endif
343 /*
344 * other local function prototypes
345 */
346 static int mrulist_ctrl_c_hook(void);
347 static mru * add_mru(mru *);
348 static int collect_mru_list(const char *, l_fp *);
349 static int fetch_nonce(char *, size_t);
350 static int qcmp_mru_avgint(const void *, const void *);
351 static int qcmp_mru_r_avgint(const void *, const void *);
352 static int qcmp_mru_addr(const void *, const void *);
353 static int qcmp_mru_r_addr(const void *, const void *);
354 static int qcmp_mru_count(const void *, const void *);
355 static int qcmp_mru_r_count(const void *, const void *);
356 static void validate_ifnum(FILE *, u_int, int *, ifstats_row *);
357 static void another_ifstats_field(int *, ifstats_row *, FILE *);
358 static void collect_display_vdc(associd_t as, vdc *table,
359 int decodestatus, FILE *fp);
360
361 /*
362 * static globals
363 */
364 static u_int mru_count;
365 static u_int mru_dupes;
366 volatile int mrulist_interrupted;
367 static mru mru_list; /* listhead */
368 static mru ** hash_table;
369
370 /*
371 * qsort comparison function table for mrulist(). The first two
372 * entries are NULL because they are handled without qsort().
373 */
374 static const qsort_cmp mru_qcmp_table[MRUSORT_MAX] = {
375 NULL, /* MRUSORT_DEF unused */
376 NULL, /* MRUSORT_R_DEF unused */
377 &qcmp_mru_avgint, /* MRUSORT_AVGINT */
378 &qcmp_mru_r_avgint, /* MRUSORT_R_AVGINT */
379 &qcmp_mru_addr, /* MRUSORT_ADDR */
380 &qcmp_mru_r_addr, /* MRUSORT_R_ADDR */
381 &qcmp_mru_count, /* MRUSORT_COUNT */
382 &qcmp_mru_r_count, /* MRUSORT_R_COUNT */
383 };
384
385 /*
386 * checkassocid - return the association ID, checking to see if it is valid
387 */
388 static associd_t
389 checkassocid(
390 u_int32 value
391 )
392 {
393 associd_t associd;
394 u_long ulvalue;
395
396 associd = (associd_t)value;
397 if (0 == associd || value != associd) {
398 ulvalue = value;
399 fprintf(stderr,
400 "***Invalid association ID %lu specified\n",
401 ulvalue);
402 return 0;
403 }
404
405 return associd;
406 }
407
408
409 /*
410 * findlistvar - Look for the named variable in a varlist. If found,
411 * return a pointer to it. Otherwise, if the list has
412 * slots available, return the pointer to the first free
413 * slot, or NULL if it's full.
414 */
415 static struct varlist *
416 findlistvar(
417 struct varlist *list,
418 char *name
419 )
420 {
421 struct varlist *vl;
422
423 for (vl = list; vl < list + MAXLIST && vl->name != NULL; vl++)
424 if (!strcmp(name, vl->name))
425 return vl;
426 if (vl < list + MAXLIST)
427 return vl;
428
429 return NULL;
430 }
431
432
433 /*
434 * doaddvlist - add variable(s) to the variable list
435 */
436 static void
437 doaddvlist(
438 struct varlist *vlist,
439 const char *vars
440 )
441 {
442 struct varlist *vl;
443 size_t len;
444 char *name;
445 char *value;
446
447 len = strlen(vars);
448 while (nextvar(&len, &vars, &name, &value)) {
449 INSIST(name && value);
450 vl = findlistvar(vlist, name);
451 if (NULL == vl) {
452 fprintf(stderr, "Variable list full\n");
453 return;
454 }
455
456 if (NULL == vl->name) {
457 vl->name = estrdup(name);
458 } else if (vl->value != NULL) {
459 free(vl->value);
460 vl->value = NULL;
461 }
462
463 if (value != NULL)
464 vl->value = estrdup(value);
465 }
466 }
467
468
469 /*
470 * dormvlist - remove variable(s) from the variable list
471 */
472 static void
473 dormvlist(
474 struct varlist *vlist,
475 const char *vars
476 )
477 {
478 struct varlist *vl;
479 size_t len;
480 char *name;
481 char *value;
482
483 len = strlen(vars);
484 while (nextvar(&len, &vars, &name, &value)) {
485 INSIST(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 "ind 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 if (ts->l_ui < lasttime->l_ui)
1458 return -1;
1459 return (ts->l_ui - lasttime->l_ui);
1460 }
1461
1462
1463 /*
1464 * Pretty-print an interval into the given buffer, in a human-friendly format.
1465 */
1466 static char *
1467 prettyinterval(
1468 char *buf,
1469 size_t cb,
1470 long diff
1471 )
1472 {
1473 if (diff <= 0) {
1474 buf[0] = '-';
1475 buf[1] = 0;
1476 return buf;
1477 }
1478
1479 if (diff <= 2048) {
1480 snprintf(buf, cb, "%u", (unsigned int)diff);
1481 return buf;
1482 }
1483
1484 diff = (diff + 29) / 60;
1485 if (diff <= 300) {
1486 snprintf(buf, cb, "%um", (unsigned int)diff);
1487 return buf;
1488 }
1489
1490 diff = (diff + 29) / 60;
1491 if (diff <= 96) {
1492 snprintf(buf, cb, "%uh", (unsigned int)diff);
1493 return buf;
1494 }
1495
1496 diff = (diff + 11) / 24;
1497 if (diff <= 999) {
1498 snprintf(buf, cb, "%ud", (unsigned int)diff);
1499 return buf;
1500 }
1501
1502 /* years are only approximated... */
1503 diff = (long)floor(diff / 365.25 + 0.5);
1504 if (diff <= 999) {
1505 snprintf(buf, cb, "%uy", (unsigned int)diff);
1506 return buf;
1507 }
1508 /* Ok, this amounts to infinity... */
1509 strlcpy(buf, "INF", cb);
1510 return buf;
1511 }
1512
1513 static char
1514 decodeaddrtype(
1515 sockaddr_u *sock
1516 )
1517 {
1518 char ch = '-';
1519 u_int32 dummy;
1520
1521 switch(AF(sock)) {
1522 case AF_INET:
1523 dummy = SRCADR(sock);
1524 ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' :
1525 ((dummy&0x000000ff)==0x000000ff) ? 'b' :
1526 ((dummy&0xffffffff)==0x7f000001) ? 'l' :
1527 ((dummy&0xffffffe0)==0x00000000) ? '-' :
1528 'u');
1529 break;
1530 case AF_INET6:
1531 if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock)))
1532 ch = 'm';
1533 else
1534 ch = 'u';
1535 break;
1536 default:
1537 ch = '-';
1538 break;
1539 }
1540 return ch;
1541 }
1542
1543 /*
1544 * A list of variables required by the peers command
1545 */
1546 struct varlist opeervarlist[] = {
1547 { "srcadr", 0 }, /* 0 */
1548 { "dstadr", 0 }, /* 1 */
1549 { "stratum", 0 }, /* 2 */
1550 { "hpoll", 0 }, /* 3 */
1551 { "ppoll", 0 }, /* 4 */
1552 { "reach", 0 }, /* 5 */
1553 { "delay", 0 }, /* 6 */
1554 { "offset", 0 }, /* 7 */
1555 { "jitter", 0 }, /* 8 */
1556 { "dispersion", 0 }, /* 9 */
1557 { "rec", 0 }, /* 10 */
1558 { "reftime", 0 }, /* 11 */
1559 { "srcport", 0 }, /* 12 */
1560 { "hmode", 0 }, /* 13 */
1561 { 0, 0 }
1562 };
1563
1564 struct varlist peervarlist[] = {
1565 { "srcadr", 0 }, /* 0 */
1566 { "refid", 0 }, /* 1 */
1567 { "stratum", 0 }, /* 2 */
1568 { "hpoll", 0 }, /* 3 */
1569 { "ppoll", 0 }, /* 4 */
1570 { "reach", 0 }, /* 5 */
1571 { "delay", 0 }, /* 6 */
1572 { "offset", 0 }, /* 7 */
1573 { "jitter", 0 }, /* 8 */
1574 { "dispersion", 0 }, /* 9 */
1575 { "rec", 0 }, /* 10 */
1576 { "reftime", 0 }, /* 11 */
1577 { "srcport", 0 }, /* 12 */
1578 { "hmode", 0 }, /* 13 */
1579 { "srchost", 0 }, /* 14 */
1580 { 0, 0 }
1581 };
1582
1583 struct varlist apeervarlist[] = {
1584 { "srcadr", 0 }, /* 0 */
1585 { "refid", 0 }, /* 1 */
1586 { "assid", 0 }, /* 2 */
1587 { "stratum", 0 }, /* 3 */
1588 { "hpoll", 0 }, /* 4 */
1589 { "ppoll", 0 }, /* 5 */
1590 { "reach", 0 }, /* 6 */
1591 { "delay", 0 }, /* 7 */
1592 { "offset", 0 }, /* 8 */
1593 { "jitter", 0 }, /* 9 */
1594 { "dispersion", 0 }, /* 10 */
1595 { "rec", 0 }, /* 11 */
1596 { "reftime", 0 }, /* 12 */
1597 { "srcport", 0 }, /* 13 */
1598 { "hmode", 0 }, /* 14 */
1599 { "srchost", 0 }, /* 15 */
1600 { 0, 0 }
1601 };
1602
1603
1604 /*
1605 * Decode an incoming data buffer and print a line in the peer list
1606 */
1607 static int
1608 doprintpeers(
1609 struct varlist *pvl,
1610 int associd,
1611 int rstatus,
1612 size_t datalen,
1613 const char *data,
1614 FILE *fp,
1615 int af
1616 )
1617 {
1618 char *name;
1619 char *value = NULL;
1620 int c;
1621 size_t len;
1622 int have_srchost;
1623 int have_dstadr;
1624 int have_da_rid;
1625 int have_jitter;
1626 sockaddr_u srcadr;
1627 sockaddr_u dstadr;
1628 sockaddr_u dum_store;
1629 sockaddr_u refidadr;
1630 long hmode = 0;
1631 u_long srcport = 0;
1632 u_int32 u32;
1633 const char *dstadr_refid = "0.0.0.0";
1634 const char *serverlocal;
1635 size_t drlen;
1636 u_long stratum = 0;
1637 long ppoll = 0;
1638 long hpoll = 0;
1639 u_long reach = 0;
1640 l_fp estoffset;
1641 l_fp estdelay;
1642 l_fp estjitter;
1643 l_fp estdisp;
1644 l_fp reftime;
1645 l_fp rec;
1646 l_fp ts;
1647 u_long poll_sec;
1648 u_long flash = 0;
1649 char type = '?';
1650 char clock_name[LENHOSTNAME];
1651 char whenbuf[12], pollbuf[12];
1652 /* [Bug 3482] formally whenbuf & pollbuf should be able to hold
1653 * a full signed int. Not that we would use that much string
1654 * data for it...
1655 */
1656 get_systime(&ts);
1657
1658 have_srchost = FALSE;
1659 have_dstadr = FALSE;
1660 have_da_rid = FALSE;
1661 have_jitter = FALSE;
1662 ZERO_SOCK(&srcadr);
1663 ZERO_SOCK(&dstadr);
1664 clock_name[0] = '\0';
1665 ZERO(estoffset);
1666 ZERO(estdelay);
1667 ZERO(estjitter);
1668 ZERO(estdisp);
1669
1670 while (nextvar(&datalen, &data, &name, &value)) {
1671 INSIST(name && value);
1672 if (!strcmp("srcadr", name) ||
1673 !strcmp("peeradr", name)) {
1674 if (!decodenetnum(value, &srcadr))
1675 fprintf(stderr, "malformed %s=%s\n",
1676 name, value);
1677 } else if (!strcmp("srchost", name)) {
1678 if (pvl == peervarlist || pvl == apeervarlist) {
1679 len = strlen(value);
1680 if (2 < len &&
1681 (size_t)len < sizeof(clock_name)) {
1682 /* strip quotes */
1683 value++;
1684 len -= 2;
1685 memcpy(clock_name, value, len);
1686 clock_name[len] = '\0';
1687 have_srchost = TRUE;
1688 }
1689 }
1690 } else if (!strcmp("dstadr", name)) {
1691 if (decodenetnum(value, &dum_store)) {
1692 type = decodeaddrtype(&dum_store);
1693 have_dstadr = TRUE;
1694 dstadr = dum_store;
1695 if (pvl == opeervarlist) {
1696 have_da_rid = TRUE;
1697 dstadr_refid = trunc_left(stoa(&dstadr), 15);
1698 }
1699 }
1700 } else if (!strcmp("hmode", name)) {
1701 decodeint(value, &hmode);
1702 } else if (!strcmp("refid", name)) {
1703 if ( (pvl == peervarlist)
1704 && (drefid == REFID_IPV4)) {
1705 have_da_rid = TRUE;
1706 drlen = strlen(value);
1707 if (0 == drlen) {
1708 dstadr_refid = "";
1709 } else if (drlen <= 4) {
1710 ZERO(u32);
1711 memcpy(&u32, value, drlen);
1712 dstadr_refid = refid_str(u32, 1);
1713 } else if (decodenetnum(value, &refidadr)) {
1714 if (SOCK_UNSPEC(&refidadr))
1715 dstadr_refid = "0.0.0.0";
1716 else if (ISREFCLOCKADR(&refidadr))
1717 dstadr_refid =
1718 refnumtoa(&refidadr);
1719 else
1720 dstadr_refid =
1721 stoa(&refidadr);
1722 } else {
1723 have_da_rid = FALSE;
1724 }
1725 } else if ( (pvl == apeervarlist)
1726 || (pvl == peervarlist)) {
1727 /* no need to check drefid == REFID_HASH */
1728 have_da_rid = TRUE;
1729 drlen = strlen(value);
1730 if (0 == drlen) {
1731 dstadr_refid = "";
1732 } else if (drlen <= 4) {
1733 ZERO(u32);
1734 memcpy(&u32, value, drlen);
1735 dstadr_refid = refid_str(u32, 1);
1736 //fprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value);
1737 } else if (decodenetnum(value, &refidadr)) {
1738 if (SOCK_UNSPEC(&refidadr))
1739 dstadr_refid = "0.0.0.0";
1740 else if (ISREFCLOCKADR(&refidadr))
1741 dstadr_refid =
1742 refnumtoa(&refidadr);
1743 else {
1744 char *buf = emalloc(10);
1745 int i = ntohl(refidadr.sa4.sin_addr.s_addr);
1746
1747 snprintf(buf, 10,
1748 "%0x", i);
1749 dstadr_refid = buf;
1750 //fprintf(stderr, "apeervarlist refid: value=<%x>\n", i);
1751 }
1752 //fprintf(stderr, "apeervarlist refid: value=<%s>\n", value);
1753 } else {
1754 have_da_rid = FALSE;
1755 }
1756 }
1757 } else if (!strcmp("stratum", name)) {
1758 decodeuint(value, &stratum);
1759 } else if (!strcmp("hpoll", name)) {
1760 if (decodeint(value, &hpoll) && hpoll < 0)
1761 hpoll = NTP_MINPOLL;
1762 } else if (!strcmp("ppoll", name)) {
1763 if (decodeint(value, &ppoll) && ppoll < 0)
1764 ppoll = NTP_MINPOLL;
1765 } else if (!strcmp("reach", name)) {
1766 decodeuint(value, &reach);
1767 } else if (!strcmp("delay", name)) {
1768 decodetime(value, &estdelay);
1769 } else if (!strcmp("offset", name)) {
1770 decodetime(value, &estoffset);
1771 } else if (!strcmp("jitter", name)) {
1772 if ((pvl == peervarlist || pvl == apeervarlist)
1773 && decodetime(value, &estjitter))
1774 have_jitter = 1;
1775 } else if (!strcmp("rootdisp", name) ||
1776 !strcmp("dispersion", name)) {
1777 decodetime(value, &estdisp);
1778 } else if (!strcmp("rec", name)) {
1779 decodets(value, &rec);
1780 } else if (!strcmp("srcport", name) ||
1781 !strcmp("peerport", name)) {
1782 decodeuint(value, &srcport);
1783 } else if (!strcmp("reftime", name)) {
1784 if (!decodets(value, &reftime))
1785 L_CLR(&reftime);
1786 } else if (!strcmp("flash", name)) {
1787 decodeuint(value, &flash);
1788 } else {
1789 // fprintf(stderr, "UNRECOGNIZED name=%s ", name);
1790 }
1791 }
1792
1793 /*
1794 * hmode gives the best guidance for the t column. If the response
1795 * did not include hmode we'll use the old decodeaddrtype() result.
1796 */
1797 switch (hmode) {
1798
1799 case MODE_BCLIENT:
1800 /* broadcastclient or multicastclient */
1801 type = 'b';
1802 break;
1803
1804 case MODE_BROADCAST:
1805 /* broadcast or multicast server */
1806 if (IS_MCAST(&srcadr))
1807 type = 'M';
1808 else
1809 type = 'B';
1810 break;
1811
1812 case MODE_CLIENT:
1813 if (ISREFCLOCKADR(&srcadr))
1814 type = 'l'; /* local refclock*/
1815 else if (SOCK_UNSPEC(&srcadr))
1816 type = 'p'; /* pool */
1817 else if (IS_MCAST(&srcadr))
1818 type = 'a'; /* manycastclient */
1819 else
1820 type = 'u'; /* unicast */
1821 break;
1822
1823 case MODE_ACTIVE:
1824 type = 's'; /* symmetric active */
1825 break; /* configured */
1826
1827 case MODE_PASSIVE:
1828 type = 'S'; /* symmetric passive */
1829 break; /* ephemeral */
1830 }
1831
1832 /*
1833 * Got everything, format the line
1834 */
1835 poll_sec = 1 << min(ppoll, hpoll);
1836 if (pktversion > NTP_OLDVERSION)
1837 c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
1838 else
1839 c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
1840 if (numhosts > 1) {
1841 if ((pvl == peervarlist || pvl == apeervarlist)
1842 && have_dstadr) {
1843 serverlocal = nntohost_col(&dstadr,
1844 (size_t)min(LIB_BUFLENGTH - 1, maxhostlen),
1845 TRUE);
1846 } else {
1847 if (currenthostisnum)
1848 serverlocal = trunc_left(currenthost,
1849 maxhostlen);
1850 else
1851 serverlocal = currenthost;
1852 }
1853 fprintf(fp, "%-*s ", (int)maxhostlen, serverlocal);
1854 }
1855 if (AF_UNSPEC == af || AF(&srcadr) == af) {
1856 if (!have_srchost)
1857 strlcpy(clock_name, nntohost(&srcadr),
1858 sizeof(clock_name));
1859 /* wide and long source - space over on next line */
1860 /* allow for host + sp if > 1 and regular tally + source + sp */
1861 if (wideremote && 15 < strlen(clock_name))
1862 fprintf(fp, "%c%s\n%*s", c, clock_name,
1863 ((numhosts > 1) ? (int)maxhostlen + 1 : 0)
1864 + 1 + 15 + 1, "");
1865 else
1866 fprintf(fp, "%c%-15.15s ", c, clock_name);
1867 if ((flash & TEST12) && (pvl != opeervarlist)) {
1868 drlen = fprintf(fp, "(loop)");
1869 } else if (!have_da_rid) {
1870 drlen = 0;
1871 } else {
1872 drlen = strlen(dstadr_refid);
1873 makeascii(drlen, dstadr_refid, fp);
1874 }
1875 if (pvl == apeervarlist) {
1876 while (drlen++ < 9)
1877 fputc(' ', fp);
1878 fprintf(fp, "%-6d", associd);
1879 } else {
1880 while (drlen++ < 15)
1881 fputc(' ', fp);
1882 }
1883 fprintf(fp,
1884 " %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n",
1885 stratum, type,
1886 prettyinterval(whenbuf, sizeof(whenbuf),
1887 when(&ts, &rec, &reftime)),
1888 prettyinterval(pollbuf, sizeof(pollbuf),
1889 (int)poll_sec),
1890 reach, lfptoms(&estdelay, 3),
1891 lfptoms(&estoffset, 3),
1892 (have_jitter)
1893 ? lfptoms(&estjitter, 3)
1894 : lfptoms(&estdisp, 3));
1895 return (1);
1896 }
1897 else
1898 return(1);
1899 }
1900
1901
1902 /*
1903 * dogetpeers - given an association ID, read and print the spreadsheet
1904 * peer variables.
1905 */
1906 static int
1907 dogetpeers(
1908 struct varlist *pvl,
1909 associd_t associd,
1910 FILE *fp,
1911 int af
1912 )
1913 {
1914 const char *datap;
1915 int res;
1916 size_t dsize;
1917 u_short rstatus;
1918
1919 #ifdef notdef
1920 res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
1921 &dsize, &datap);
1922 #else
1923 /*
1924 * Damn fuzzballs
1925 */
1926 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
1927 &dsize, &datap);
1928 #endif
1929
1930 if (res != 0)
1931 return 0;
1932
1933 if (dsize == 0) {
1934 if (numhosts > 1)
1935 fprintf(stderr, "server=%s ", currenthost);
1936 fprintf(stderr,
1937 "***No information returned for association %u\n",
1938 associd);
1939 return 0;
1940 }
1941
1942 return doprintpeers(pvl, associd, (int)rstatus, dsize, datap,
1943 fp, af);
1944 }
1945
1946
1947 /*
1948 * peers - print a peer spreadsheet
1949 */
1950 static void
1951 dopeers(
1952 int showall,
1953 FILE *fp,
1954 int af
1955 )
1956 {
1957 u_int u;
1958 char fullname[LENHOSTNAME];
1959 sockaddr_u netnum;
1960 const char * name_or_num;
1961 size_t sl;
1962
1963 if (!dogetassoc(fp))
1964 return;
1965
1966 for (u = 0; u < numhosts; u++) {
1967 if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
1968 name_or_num = nntohost(&netnum);
1969 sl = strlen(name_or_num);
1970 maxhostlen = max(maxhostlen, sl);
1971 }
1972 }
1973 if (numhosts > 1)
1974 fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
1975 "server (local)");
1976 fprintf(fp,
1977 " remote refid st t when poll reach delay offset jitter\n");
1978 if (numhosts > 1)
1979 for (u = 0; u <= maxhostlen; u++)
1980 fprintf(fp, "=");
1981 fprintf(fp,
1982 "==============================================================================\n");
1983
1984 for (u = 0; u < numassoc; u++) {
1985 if (!showall &&
1986 !(CTL_PEER_STATVAL(assoc_cache[u].status)
1987 & (CTL_PST_CONFIG|CTL_PST_REACH))) {
1988 if (debug)
1989 fprintf(stderr, "eliding [%d]\n",
1990 (int)assoc_cache[u].assid);
1991 continue;
1992 }
1993 if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid,
1994 fp, af))
1995 return;
1996 }
1997 return;
1998 }
1999
2000
2001 /*
2002 * doapeers - print a peer spreadsheet with assocIDs
2003 */
2004 static void
2005 doapeers(
2006 int showall,
2007 FILE *fp,
2008 int af
2009 )
2010 {
2011 u_int u;
2012 char fullname[LENHOSTNAME];
2013 sockaddr_u netnum;
2014 const char * name_or_num;
2015 size_t sl;
2016
2017 if (!dogetassoc(fp))
2018 return;
2019
2020 for (u = 0; u < numhosts; u++) {
2021 if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
2022 name_or_num = nntohost(&netnum);
2023 sl = strlen(name_or_num);
2024 maxhostlen = max(maxhostlen, sl);
2025 }
2026 }
2027 if (numhosts > 1)
2028 fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2029 "server (local)");
2030 fprintf(fp,
2031 " remote refid assid st t when poll reach delay offset jitter\n");
2032 if (numhosts > 1)
2033 for (u = 0; u <= maxhostlen; u++)
2034 fprintf(fp, "=");
2035 fprintf(fp,
2036 "==============================================================================\n");
2037
2038 for (u = 0; u < numassoc; u++) {
2039 if (!showall &&
2040 !(CTL_PEER_STATVAL(assoc_cache[u].status)
2041 & (CTL_PST_CONFIG|CTL_PST_REACH))) {
2042 if (debug)
2043 fprintf(stderr, "eliding [%d]\n",
2044 (int)assoc_cache[u].assid);
2045 continue;
2046 }
2047 if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid,
2048 fp, af))
2049 return;
2050 }
2051 return;
2052 }
2053
2054
2055 /*
2056 * peers - print a peer spreadsheet
2057 */
2058 /*ARGSUSED*/
2059 static void
2060 peers(
2061 struct parse *pcmd,
2062 FILE *fp
2063 )
2064 {
2065 if (drefid == REFID_HASH) {
2066 apeers(pcmd, fp);
2067 } else {
2068 int af = 0;
2069
2070 if (pcmd->nargs == 1) {
2071 if (pcmd->argval->ival == 6)
2072 af = AF_INET6;
2073 else
2074 af = AF_INET;
2075 }
2076 dopeers(0, fp, af);
2077 }
2078 }
2079
2080
2081 /*
2082 * apeers - print a peer spreadsheet, with assocIDs
2083 */
2084 /*ARGSUSED*/
2085 static void
2086 apeers(
2087 struct parse *pcmd,
2088 FILE *fp
2089 )
2090 {
2091 int af = 0;
2092
2093 if (pcmd->nargs == 1) {
2094 if (pcmd->argval->ival == 6)
2095 af = AF_INET6;
2096 else
2097 af = AF_INET;
2098 }
2099 doapeers(0, fp, af);
2100 }
2101
2102
2103 /*
2104 * lpeers - print a peer spreadsheet including all fuzzball peers
2105 */
2106 /*ARGSUSED*/
2107 static void
2108 lpeers(
2109 struct parse *pcmd,
2110 FILE *fp
2111 )
2112 {
2113 int af = 0;
2114
2115 if (pcmd->nargs == 1) {
2116 if (pcmd->argval->ival == 6)
2117 af = AF_INET6;
2118 else
2119 af = AF_INET;
2120 }
2121 dopeers(1, fp, af);
2122 }
2123
2124
2125 /*
2126 * opeers - print a peer spreadsheet
2127 */
2128 static void
2129 doopeers(
2130 int showall,
2131 FILE *fp,
2132 int af
2133 )
2134 {
2135 u_int i;
2136 char fullname[LENHOSTNAME];
2137 sockaddr_u netnum;
2138
2139 if (!dogetassoc(fp))
2140 return;
2141
2142 for (i = 0; i < numhosts; ++i) {
2143 if (getnetnum(chosts[i].name, &netnum, fullname, af))
2144 if (strlen(fullname) > maxhostlen)
2145 maxhostlen = strlen(fullname);
2146 }
2147 if (numhosts > 1)
2148 fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2149 "server");
2150 fprintf(fp,
2151 " remote local st t when poll reach delay offset disp\n");
2152 if (numhosts > 1)
2153 for (i = 0; i <= maxhostlen; ++i)
2154 fprintf(fp, "=");
2155 fprintf(fp,
2156 "==============================================================================\n");
2157
2158 for (i = 0; i < numassoc; i++) {
2159 if (!showall &&
2160 !(CTL_PEER_STATVAL(assoc_cache[i].status) &
2161 (CTL_PST_CONFIG | CTL_PST_REACH)))
2162 continue;
2163 if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af))
2164 return;
2165 }
2166 return;
2167 }
2168
2169
2170 /*
2171 * opeers - print a peer spreadsheet the old way
2172 */
2173 /*ARGSUSED*/
2174 static void
2175 opeers(
2176 struct parse *pcmd,
2177 FILE *fp
2178 )
2179 {
2180 int af = 0;
2181
2182 if (pcmd->nargs == 1) {
2183 if (pcmd->argval->ival == 6)
2184 af = AF_INET6;
2185 else
2186 af = AF_INET;
2187 }
2188 doopeers(0, fp, af);
2189 }
2190
2191
2192 /*
2193 * lopeers - print a peer spreadsheet including all fuzzball peers
2194 */
2195 /*ARGSUSED*/
2196 static void
2197 lopeers(
2198 struct parse *pcmd,
2199 FILE *fp
2200 )
2201 {
2202 int af = 0;
2203
2204 if (pcmd->nargs == 1) {
2205 if (pcmd->argval->ival == 6)
2206 af = AF_INET6;
2207 else
2208 af = AF_INET;
2209 }
2210 doopeers(1, fp, af);
2211 }
2212
2213
2214 /*
2215 * config - send a configuration command to a remote host
2216 */
2217 static void
2218 config (
2219 struct parse *pcmd,
2220 FILE *fp
2221 )
2222 {
2223 const char *cfgcmd;
2224 u_short rstatus;
2225 size_t rsize;
2226 const char *rdata;
2227 char *resp;
2228 int res;
2229 int col;
2230 int i;
2231
2232 cfgcmd = pcmd->argval[0].string;
2233
2234 if (debug > 2)
2235 fprintf(stderr,
2236 "In Config\n"
2237 "Keyword = %s\n"
2238 "Command = %s\n", pcmd->keyword, cfgcmd);
2239
2240 res = doquery(CTL_OP_CONFIGURE, 0, 1,
2241 strlen(cfgcmd), cfgcmd,
2242 &rstatus, &rsize, &rdata);
2243
2244 if (res != 0)
2245 return;
2246
2247 if (rsize > 0 && '\n' == rdata[rsize - 1])
2248 rsize--;
2249
2250 resp = emalloc(rsize + 1);
2251 memcpy(resp, rdata, rsize);
2252 resp[rsize] = '\0';
2253
2254 col = -1;
2255 if (1 == sscanf(resp, "column %d syntax error", &col)
2256 && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) {
2257 if (interactive)
2258 fputs(" *", stdout); /* "ntpq> :config " */
2259 else
2260 printf("%s\n", cfgcmd);
2261 for (i = 0; i < col; i++)
2262 fputc('_', stdout);
2263 fputs("^\n", stdout);
2264 }
2265 printf("%s\n", resp);
2266 free(resp);
2267 }
2268
2269
2270 /*
2271 * config_from_file - remotely configure an ntpd daemon using the
2272 * specified configuration file
2273 * SK: This function is a kludge at best and is full of bad design
2274 * bugs:
2275 * 1. ntpq uses UDP, which means that there is no guarantee of in-order,
2276 * error-free delivery.
2277 * 2. The maximum length of a packet is constrained, and as a result, the
2278 * maximum length of a line in a configuration file is constrained.
2279 * Longer lines will lead to unpredictable results.
2280 * 3. Since this function is sending a line at a time, we can't update
2281 * the control key through the configuration file (YUCK!!)
2282 *
2283 * Pearly: There are a few places where 'size_t' is cast to 'int' based
2284 * on the assumption that 'int' can hold the size of the involved
2285 * buffers without overflow.
2286 */
2287 static void
2288 config_from_file (
2289 struct parse *pcmd,
2290 FILE *fp
2291 )
2292 {
2293 u_short rstatus;
2294 size_t rsize;
2295 const char *rdata;
2296 char * cp;
2297 int res;
2298 FILE *config_fd;
2299 char config_cmd[MAXLINE];
2300 size_t config_len;
2301 int i;
2302 int retry_limit;
2303
2304 if (debug > 2)
2305 fprintf(stderr,
2306 "In Config\n"
2307 "Keyword = %s\n"
2308 "Filename = %s\n", pcmd->keyword,
2309 pcmd->argval[0].string);
2310
2311 config_fd = fopen(pcmd->argval[0].string, "r");
2312 if (NULL == config_fd) {
2313 printf("ERROR!! Couldn't open file: %s\n",
2314 pcmd->argval[0].string);
2315 return;
2316 }
2317
2318 printf("Sending configuration file, one line at a time.\n");
2319 i = 0;
2320 while (fgets(config_cmd, MAXLINE, config_fd) != NULL) {
2321 /* Eliminate comments first. */
2322 cp = strchr(config_cmd, '#');
2323 config_len = (NULL != cp)
2324 ? (size_t)(cp - config_cmd)
2325 : strlen(config_cmd);
2326
2327 /* [Bug 3015] make sure there's no trailing whitespace;
2328 * the fix for [Bug 2853] on the server side forbids
2329 * those. And don't transmit empty lines, as this would
2330 * just be waste.
2331 */
2332 while (config_len != 0 &&
2333 (u_char)config_cmd[config_len-1] <= ' ')
2334 --config_len;
2335 config_cmd[config_len] = '\0';
2336
2337 ++i;
2338 if (0 == config_len)
2339 continue;
2340
2341 retry_limit = 2;
2342 do
2343 res = doquery(CTL_OP_CONFIGURE, 0, 1,
2344 config_len, config_cmd,
2345 &rstatus, &rsize, &rdata);
2346 while (res != 0 && retry_limit--);
2347 if (res != 0) {
2348 printf("Line No: %d query failed: %.*s\n"
2349 "Subsequent lines not sent.\n",
2350 i, (int)config_len, config_cmd);
2351 fclose(config_fd);
2352 return;
2353 }
2354
2355 /* Right-strip the result code string, then output the
2356 * last line executed, with result code. */
2357 while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ')
2358 --rsize;
2359 printf("Line No: %d %.*s: %.*s\n", i,
2360 (int)rsize, rdata,
2361 (int)config_len, config_cmd);
2362 }
2363 printf("Done sending file\n");
2364 fclose(config_fd);
2365 }
2366
2367
2368 static int
2369 fetch_nonce(
2370 char * nonce,
2371 size_t cb_nonce
2372 )
2373 {
2374 const char nonce_eq[] = "nonce=";
2375 int qres;
2376 u_short rstatus;
2377 size_t rsize;
2378 const char * rdata;
2379 size_t chars;
2380
2381 /*
2382 * Retrieve a nonce specific to this client to demonstrate to
2383 * ntpd that we're capable of receiving responses to our source
2384 * IP address, and thereby unlikely to be forging the source.
2385 */
2386 qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus,
2387 &rsize, &rdata);
2388 if (qres) {
2389 fprintf(stderr, "nonce request failed\n");
2390 return FALSE;
2391 }
2392
2393 if ((size_t)rsize <= sizeof(nonce_eq) - 1 ||
2394 strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) {
2395 fprintf(stderr, "unexpected nonce response format: %.*s\n",
2396 (int)rsize, rdata); /* cast is wobbly */
2397 return FALSE;
2398 }
2399 chars = rsize - (sizeof(nonce_eq) - 1);
2400 if (chars >= cb_nonce)
2401 return FALSE;
2402 memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars);
2403 nonce[chars] = '\0';
2404 while (chars > 0 &&
2405 ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) {
2406 chars--;
2407 nonce[chars] = '\0';
2408 }
2409
2410 return TRUE;
2411 }
2412
2413
2414 /*
2415 * add_mru Add and entry to mru list, hash table, and allocate
2416 * and return a replacement.
2417 * This is a helper for collect_mru_list().
2418 */
2419 static mru *
2420 add_mru(
2421 mru *add
2422 )
2423 {
2424 u_short hash;
2425 mru *mon;
2426 mru *unlinked;
2427
2428
2429 hash = NTP_HASH_ADDR(&add->addr);
2430 /* see if we have it among previously received entries */
2431 for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink)
2432 if (SOCK_EQ(&mon->addr, &add->addr))
2433 break;
2434 if (mon != NULL) {
2435 if (!L_ISGEQ(&add->first, &mon->first)) {
2436 fprintf(stderr,
2437 "add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n",
2438 sptoa(&add->addr), add->last.l_ui,
2439 add->last.l_uf, mon->last.l_ui,
2440 mon->last.l_uf);
2441 exit(1);
2442 }
2443 UNLINK_DLIST(mon, mlink);
2444 UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru);
2445 INSIST(unlinked == mon);
2446 mru_dupes++;
2447 TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui,
2448 mon->last.l_uf));
2449 }
2450 LINK_DLIST(mru_list, add, mlink);
2451 LINK_SLIST(hash_table[hash], add, hlink);
2452 TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
2453 add->last.l_ui, add->last.l_uf, add->count,
2454 (int)add->mode, (int)add->ver, (u_int)add->rs,
2455 add->first.l_ui, add->first.l_uf, sptoa(&add->addr)));
2456 /* if we didn't update an existing entry, alloc replacement */
2457 if (NULL == mon) {
2458 mon = emalloc(sizeof(*mon));
2459 mru_count++;
2460 }
2461 ZERO(*mon);
2462
2463 return mon;
2464 }
2465
2466
2467 /* MGOT macro is specific to collect_mru_list() */
2468 #define MGOT(bit) \
2469 do { \
2470 got |= (bit); \
2471 if (MRU_GOT_ALL == got) { \
2472 got = 0; \
2473 mon = add_mru(mon); \
2474 ci++; \
2475 } \
2476 } while (0)
2477
2478
2479 int
2480 mrulist_ctrl_c_hook(void)
2481 {
2482 mrulist_interrupted = TRUE;
2483 return TRUE;
2484 }
2485
2486
2487 static int
2488 collect_mru_list(
2489 const char * parms,
2490 l_fp * pnow
2491 )
2492 {
2493 const u_int sleep_msecs = 5;
2494 static int ntpd_row_limit = MRU_ROW_LIMIT;
2495 int c_mru_l_rc; /* this function's return code */
2496 u_char got; /* MRU_GOT_* bits */
2497 time_t next_report;
2498 size_t cb;
2499 mru *mon;
2500 mru *head;
2501 mru *recent;
2502 int list_complete;
2503 char nonce[128];
2504 char buf[128];
2505 char req_buf[CTL_MAX_DATA_LEN];
2506 char *req;
2507 char *req_end;
2508 size_t chars;
2509 int qres;
2510 u_short rstatus;
2511 size_t rsize;
2512 const char *rdata;
2513 int limit;
2514 int frags;
2515 int cap_frags;
2516 char *tag;
2517 char *val;
2518 int si; /* server index in response */
2519 int ci; /* client (our) index for validation */
2520 int ri; /* request index (.# suffix) */
2521 int mv;
2522 l_fp newest;
2523 l_fp last_older;
2524 sockaddr_u addr_older;
2525 int have_now;
2526 int have_addr_older;
2527 int have_last_older;
2528 u_int restarted_count;
2529 u_int nonce_uses;
2530 u_short hash;
2531 mru *unlinked;
2532
2533 if (!fetch_nonce(nonce, sizeof(nonce)))
2534 return FALSE;
2535
2536 nonce_uses = 0;
2537 restarted_count = 0;
2538 mru_count = 0;
2539 INIT_DLIST(mru_list, mlink);
2540 cb = NTP_HASH_SIZE * sizeof(*hash_table);
2541 INSIST(NULL == hash_table);
2542 hash_table = emalloc_zero(cb);
2543
2544 c_mru_l_rc = FALSE;
2545 list_complete = FALSE;
2546 have_now = FALSE;
2547 cap_frags = TRUE;
2548 got = 0;
2549 ri = 0;
2550 cb = sizeof(*mon);
2551 mon = emalloc_zero(cb);
2552 ZERO(*pnow);
2553 ZERO(last_older);
2554 next_report = time(NULL) + MRU_REPORT_SECS;
2555
2556 limit = min(3 * MAXFRAGS, ntpd_row_limit);
2557 frags = MAXFRAGS;
2558 snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s",
2559 nonce, frags, parms);
2560 nonce_uses++;
2561
2562 while (TRUE) {
2563 if (debug)
2564 fprintf(stderr, "READ_MRU parms: %s\n", req_buf);
2565
2566 qres = doqueryex(CTL_OP_READ_MRU, 0, 0,
2567 strlen(req_buf), req_buf,
2568 &rstatus, &rsize, &rdata, TRUE);
2569
2570 if (CERR_UNKNOWNVAR == qres && ri > 0) {
2571 /*
2572 * None of the supplied prior entries match, so
2573 * toss them from our list and try again.
2574 */
2575 if (debug)
2576 fprintf(stderr,
2577 "no overlap between %d prior entries and server MRU list\n",
2578 ri);
2579 while (ri--) {
2580 recent = HEAD_DLIST(mru_list, mlink);
2581 INSIST(recent != NULL);
2582 if (debug)
2583 fprintf(stderr,
2584 "tossing prior entry %s to resync\n",
2585 sptoa(&recent->addr));
2586 UNLINK_DLIST(recent, mlink);
2587 hash = NTP_HASH_ADDR(&recent->addr);
2588 UNLINK_SLIST(unlinked, hash_table[hash],
2589 recent, hlink, mru);
2590 INSIST(unlinked == recent);
2591 free(recent);
2592 mru_count--;
2593 }
2594 if (NULL == HEAD_DLIST(mru_list, mlink)) {
2595 restarted_count++;
2596 if (restarted_count > 8) {
2597 fprintf(stderr,
2598 "Giving up after 8 restarts from the beginning.\n"
2599 "With high-traffic NTP servers, this can occur if the\n"
2600 "MRU list is limited to less than about 16 seconds' of\n"
2601 "entries. See the 'mru' ntp.conf directive to adjust.\n");
2602 goto cleanup_return;
2603 }
2604 if (debug)
2605 fprintf(stderr,
2606 "---> Restarting from the beginning, retry #%u\n",
2607 restarted_count);
2608 }
2609 } else if (CERR_UNKNOWNVAR == qres) {
2610 fprintf(stderr,
2611 "CERR_UNKNOWNVAR from ntpd but no priors given.\n");
2612 goto cleanup_return;
2613 } else if (CERR_BADVALUE == qres) {
2614 if (cap_frags) {
2615 cap_frags = FALSE;
2616 if (debug)
2617 fprintf(stderr,
2618 "Reverted to row limit from fragments limit.\n");
2619 } else {
2620 /* ntpd has lower cap on row limit */
2621 ntpd_row_limit--;
2622 limit = min(limit, ntpd_row_limit);
2623 if (debug)
2624 fprintf(stderr,
2625 "Row limit reduced to %d following CERR_BADVALUE.\n",
2626 limit);
2627 }
2628 } else if (ERR_INCOMPLETE == qres ||
2629 ERR_TIMEOUT == qres) {
2630 /*
2631 * Reduce the number of rows/frags requested by
2632 * half to recover from lost response fragments.
2633 */
2634 if (cap_frags) {
2635 frags = max(2, frags / 2);
2636 if (debug)
2637 fprintf(stderr,
2638 "Frag limit reduced to %d following incomplete response.\n",
2639 frags);
2640 } else {
2641 limit = max(2, limit / 2);
2642 if (debug)
2643 fprintf(stderr,
2644 "Row limit reduced to %d following incomplete response.\n",
2645 limit);
2646 }
2647 } else if (qres) {
2648 show_error_msg(qres, 0);
2649 goto cleanup_return;
2650 }
2651 /*
2652 * This is a cheap cop-out implementation of rawmode
2653 * output for mrulist. A better approach would be to
2654 * dump similar output after the list is collected by
2655 * ntpq with a continuous sequence of indexes. This
2656 * cheap approach has indexes resetting to zero for
2657 * each query/response, and duplicates are not
2658 * coalesced.
2659 */
2660 if (!qres && rawmode)
2661 printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout);
2662 ci = 0;
2663 have_addr_older = FALSE;
2664 have_last_older = FALSE;
2665 while (!qres && nextvar(&rsize, &rdata, &tag, &val)) {
2666 INSIST(tag && val);
2667 if (debug > 1)
2668 fprintf(stderr, "nextvar gave: %s = %s\n",
2669 tag, val);
2670 switch(tag[0]) {
2671
2672 case 'a':
2673 if (!strcmp(tag, "addr.older")) {
2674 if (!have_last_older) {
2675 fprintf(stderr,
2676 "addr.older %s before last.older\n",
2677 val);
2678 goto cleanup_return;
2679 }
2680 if (!decodenetnum(val, &addr_older)) {
2681 fprintf(stderr,
2682 "addr.older %s garbled\n",
2683 val);
2684 goto cleanup_return;
2685 }
2686 hash = NTP_HASH_ADDR(&addr_older);
2687 for (recent = hash_table[hash];
2688 recent != NULL;
2689 recent = recent->hlink)
2690 if (ADDR_PORT_EQ(
2691 &addr_older,
2692 &recent->addr))
2693 break;
2694 if (NULL == recent) {
2695 fprintf(stderr,
2696 "addr.older %s not in hash table\n",
2697 val);
2698 goto cleanup_return;
2699 }
2700 if (!L_ISEQU(&last_older,
2701 &recent->last)) {
2702 fprintf(stderr,
2703 "last.older %08x.%08x mismatches %08x.%08x expected.\n",
2704 last_older.l_ui,
2705 last_older.l_uf,
2706 recent->last.l_ui,
2707 recent->last.l_uf);
2708 goto cleanup_return;
2709 }
2710 have_addr_older = TRUE;
2711 } else if (1 != sscanf(tag, "addr.%d", &si)
2712 || si != ci)
2713 goto nomatch;
2714 else if (decodenetnum(val, &mon->addr))
2715 MGOT(MRU_GOT_ADDR);
2716 break;
2717
2718 case 'l':
2719 if (!strcmp(tag, "last.older")) {
2720 if ('0' != val[0] ||
2721 'x' != val[1] ||
2722 !hextolfp(val + 2, &last_older)) {
2723 fprintf(stderr,
2724 "last.older %s garbled\n",
2725 val);
2726 goto cleanup_return;
2727 }
2728 have_last_older = TRUE;
2729 } else if (!strcmp(tag, "last.newest")) {
2730 if (0 != got) {
2731 fprintf(stderr,
2732 "last.newest %s before complete row, got = 0x%x\n",
2733 val, (u_int)got);
2734 goto cleanup_return;
2735 }
2736 if (!have_now) {
2737 fprintf(stderr,
2738 "last.newest %s before now=\n",
2739 val);
2740 goto cleanup_return;
2741 }
2742 head = HEAD_DLIST(mru_list, mlink);
2743 if (NULL != head) {
2744 if ('0' != val[0] ||
2745 'x' != val[1] ||
2746 !hextolfp(val + 2, &newest) ||
2747 !L_ISEQU(&newest,
2748 &head->last)) {
2749 fprintf(stderr,
2750 "last.newest %s mismatches %08x.%08x",
2751 val,
2752 head->last.l_ui,
2753 head->last.l_uf);
2754 goto cleanup_return;
2755 }
2756 }
2757 list_complete = TRUE;
2758 } else if (1 != sscanf(tag, "last.%d", &si) ||
2759 si != ci || '0' != val[0] ||
2760 'x' != val[1] ||
2761 !hextolfp(val + 2, &mon->last)) {
2762 goto nomatch;
2763 } else {
2764 MGOT(MRU_GOT_LAST);
2765 /*
2766 * allow interrupted retrieval,
2767 * using most recent retrieved
2768 * entry's last seen timestamp
2769 * as the end of operation.
2770 */
2771 *pnow = mon->last;
2772 }
2773 break;
2774
2775 case 'f':
2776 if (1 != sscanf(tag, "first.%d", &si) ||
2777 si != ci || '0' != val[0] ||
2778 'x' != val[1] ||
2779 !hextolfp(val + 2, &mon->first))
2780 goto nomatch;
2781 MGOT(MRU_GOT_FIRST);
2782 break;
2783
2784 case 'n':
2785 if (!strcmp(tag, "nonce")) {
2786 strlcpy(nonce, val, sizeof(nonce));
2787 nonce_uses = 0;
2788 break; /* case */
2789 } else if (strcmp(tag, "now") ||
2790 '0' != val[0] ||
2791 'x' != val[1] ||
2792 !hextolfp(val + 2, pnow))
2793 goto nomatch;
2794 have_now = TRUE;
2795 break;
2796
2797 case 'c':
2798 if (1 != sscanf(tag, "ct.%d", &si) ||
2799 si != ci ||
2800 1 != sscanf(val, "%d", &mon->count)
2801 || mon->count < 1)
2802 goto nomatch;
2803 MGOT(MRU_GOT_COUNT);
2804 break;
2805
2806 case 'm':
2807 if (1 != sscanf(tag, "mv.%d", &si) ||
2808 si != ci ||
2809 1 != sscanf(val, "%d", &mv))
2810 goto nomatch;
2811 mon->mode = PKT_MODE(mv);
2812 mon->ver = PKT_VERSION(mv);
2813 MGOT(MRU_GOT_MV);
2814 break;
2815
2816 case 'r':
2817 if (1 != sscanf(tag, "rs.%d", &si) ||
2818 si != ci ||
2819 1 != sscanf(val, "0x%hx", &mon->rs))
2820 goto nomatch;
2821 MGOT(MRU_GOT_RS);
2822 break;
2823
2824 default:
2825 nomatch:
2826 /* empty stmt */ ;
2827 /* ignore unknown tags */
2828 }
2829 }
2830 if (have_now)
2831 list_complete = TRUE;
2832 if (list_complete) {
2833 INSIST(0 == ri || have_addr_older);
2834 }
2835 if (mrulist_interrupted) {
2836 printf("mrulist retrieval interrupted by operator.\n"
2837 "Displaying partial client list.\n");
2838 fflush(stdout);
2839 }
2840 if (list_complete || mrulist_interrupted) {
2841 fprintf(stderr,
2842 "\rRetrieved %u unique MRU entries and %u updates.\n",
2843 mru_count, mru_dupes);
2844 fflush(stderr);
2845 break;
2846 }
2847 if (time(NULL) >= next_report) {
2848 next_report += MRU_REPORT_SECS;
2849 fprintf(stderr, "\r%u (%u updates) ", mru_count,
2850 mru_dupes);
2851 fflush(stderr);
2852 }
2853
2854 /*
2855 * Snooze for a bit between queries to let ntpd catch
2856 * up with other duties.
2857 */
2858 #ifdef SYS_WINNT
2859 Sleep(sleep_msecs);
2860 #elif !defined(HAVE_NANOSLEEP)
2861 sleep((sleep_msecs / 1000) + 1);
2862 #else
2863 {
2864 struct timespec interv = { 0,
2865 1000 * sleep_msecs };
2866 nanosleep(&interv, NULL);
2867 }
2868 #endif
2869 /*
2870 * If there were no errors, increase the number of rows
2871 * to a maximum of 3 * MAXFRAGS (the most packets ntpq
2872 * can handle in one response), on the assumption that
2873 * no less than 3 rows fit in each packet, capped at
2874 * our best guess at the server's row limit.
2875 */
2876 if (!qres) {
2877 if (cap_frags) {
2878 frags = min(MAXFRAGS, frags + 1);
2879 } else {
2880 limit = min3(3 * MAXFRAGS,
2881 ntpd_row_limit,
2882 max(limit + 1,
2883 limit * 33 / 32));
2884 }
2885 }
2886 /*
2887 * prepare next query with as many address and last-seen
2888 * timestamps as will fit in a single packet.
2889 */
2890 req = req_buf;
2891 req_end = req_buf + sizeof(req_buf);
2892 #define REQ_ROOM (req_end - req)
2893 snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce,
2894 (cap_frags)
2895 ? "frags"
2896 : "limit",
2897 (cap_frags)
2898 ? frags
2899 : limit,
2900 parms);
2901 req += strlen(req);
2902 nonce_uses++;
2903 if (nonce_uses >= 4) {
2904 if (!fetch_nonce(nonce, sizeof(nonce)))
2905 goto cleanup_return;
2906 nonce_uses = 0;
2907 }
2908
2909
2910 for (ri = 0, recent = HEAD_DLIST(mru_list, mlink);
2911 recent != NULL;
2912 ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) {
2913
2914 snprintf(buf, sizeof(buf),
2915 ", addr.%d=%s, last.%d=0x%08x.%08x",
2916 ri, sptoa(&recent->addr), ri,
2917 recent->last.l_ui, recent->last.l_uf);
2918 chars = strlen(buf);
2919 if ((size_t)REQ_ROOM <= chars)
2920 break;
2921 memcpy(req, buf, chars + 1);
2922 req += chars;
2923 }
2924 }
2925
2926 c_mru_l_rc = TRUE;
2927 goto retain_hash_table;
2928
2929 cleanup_return:
2930 free(hash_table);
2931 hash_table = NULL;
2932
2933 retain_hash_table:
2934 if (mon != NULL)
2935 free(mon);
2936
2937 return c_mru_l_rc;
2938 }
2939
2940
2941 /*
2942 * qcmp_mru_addr - sort MRU entries by remote address.
2943 *
2944 * All IPv4 addresses sort before any IPv6, addresses are sorted by
2945 * value within address family.
2946 */
2947 static int
2948 qcmp_mru_addr(
2949 const void *v1,
2950 const void *v2
2951 )
2952 {
2953 const mru * const * ppm1 = v1;
2954 const mru * const * ppm2 = v2;
2955 const mru * pm1;
2956 const mru * pm2;
2957 u_short af1;
2958 u_short af2;
2959 size_t cmplen;
2960 size_t addr_off;
2961
2962 pm1 = *ppm1;
2963 pm2 = *ppm2;
2964
2965 af1 = AF(&pm1->addr);
2966 af2 = AF(&pm2->addr);
2967
2968 if (af1 != af2)
2969 return (AF_INET == af1)
2970 ? -1
2971 : 1;
2972
2973 cmplen = SIZEOF_INADDR(af1);
2974 addr_off = (AF_INET == af1)
2975 ? offsetof(struct sockaddr_in, sin_addr)
2976 : offsetof(struct sockaddr_in6, sin6_addr);
2977
2978 return memcmp((const char *)&pm1->addr + addr_off,
2979 (const char *)&pm2->addr + addr_off,
2980 cmplen);
2981 }
2982
2983
2984 static int
2985 qcmp_mru_r_addr(
2986 const void *v1,
2987 const void *v2
2988 )
2989 {
2990 return -qcmp_mru_addr(v1, v2);
2991 }
2992
2993
2994 /*
2995 * qcmp_mru_count - sort MRU entries by times seen (hit count).
2996 */
2997 static int
2998 qcmp_mru_count(
2999 const void *v1,
3000 const void *v2
3001 )
3002 {
3003 const mru * const * ppm1 = v1;
3004 const mru * const * ppm2 = v2;
3005 const mru * pm1;
3006 const mru * pm2;
3007
3008 pm1 = *ppm1;
3009 pm2 = *ppm2;
3010
3011 return (pm1->count < pm2->count)
3012 ? -1
3013 : ((pm1->count == pm2->count)
3014 ? 0
3015 : 1);
3016 }
3017
3018
3019 static int
3020 qcmp_mru_r_count(
3021 const void *v1,
3022 const void *v2
3023 )
3024 {
3025 return -qcmp_mru_count(v1, v2);
3026 }
3027
3028
3029 /*
3030 * qcmp_mru_avgint - sort MRU entries by average interval.
3031 */
3032 static int
3033 qcmp_mru_avgint(
3034 const void *v1,
3035 const void *v2
3036 )
3037 {
3038 const mru * const * ppm1 = v1;
3039 const mru * const * ppm2 = v2;
3040 const mru * pm1;
3041 const mru * pm2;
3042 l_fp interval;
3043 double avg1;
3044 double avg2;
3045
3046 pm1 = *ppm1;
3047 pm2 = *ppm2;
3048
3049 interval = pm1->last;
3050 L_SUB(&interval, &pm1->first);
3051 LFPTOD(&interval, avg1);
3052 avg1 /= pm1->count;
3053
3054 interval = pm2->last;
3055 L_SUB(&interval, &pm2->first);
3056 LFPTOD(&interval, avg2);
3057 avg2 /= pm2->count;
3058
3059 if (avg1 < avg2)
3060 return -1;
3061 else if (avg1 > avg2)
3062 return 1;
3063
3064 /* secondary sort on lstint - rarely tested */
3065 if (L_ISEQU(&pm1->last, &pm2->last))
3066 return 0;
3067 else if (L_ISGEQ(&pm1->last, &pm2->last))
3068 return -1;
3069 else
3070 return 1;
3071 }
3072
3073
3074 static int
3075 qcmp_mru_r_avgint(
3076 const void *v1,
3077 const void *v2
3078 )
3079 {
3080 return -qcmp_mru_avgint(v1, v2);
3081 }
3082
3083
3084 /*
3085 * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most
3086 * Recently Used (seen) remote address list from ntpd.
3087 *
3088 * Similar to ntpdc's monlist command, but not limited to a single
3089 * request/response, and thereby not limited to a few hundred remote
3090 * addresses.
3091 *
3092 * See ntpd/ntp_control.c read_mru_list() for comments on the way
3093 * CTL_OP_READ_MRU is designed to be used.
3094 *
3095 * mrulist intentionally differs from monlist in the way the avgint
3096 * column is calculated. monlist includes the time after the last
3097 * packet from the client until the monlist query time in the average,
3098 * while mrulist excludes it. That is, monlist's average interval grows
3099 * over time for remote addresses not heard from in some time, while it
3100 * remains unchanged in mrulist. This also affects the avgint value for
3101 * entries representing a single packet, with identical first and last
3102 * timestamps. mrulist shows 0 avgint, monlist shows a value identical
3103 * to lstint.
3104 */
3105 static void
3106 mrulist(
3107 struct parse * pcmd,
3108 FILE * fp
3109 )
3110 {
3111 const char mincount_eq[] = "mincount=";
3112 const char resall_eq[] = "resall=";
3113 const char resany_eq[] = "resany=";
3114 const char maxlstint_eq[] = "maxlstint=";
3115 const char laddr_eq[] = "laddr=";
3116 const char sort_eq[] = "sort=";
3117 mru_sort_order order;
3118 size_t n;
3119 char parms_buf[128];
3120 char buf[24];
3121 char *parms;
3122 const char *arg;
3123 size_t cb;
3124 mru **sorted;
3125 mru **ppentry;
3126 mru *recent;
3127 l_fp now;
3128 l_fp interval;
3129 double favgint;
3130 double flstint;
3131 int avgint;
3132 int lstint;
3133 size_t i;
3134
3135 mrulist_interrupted = FALSE;
3136 push_ctrl_c_handler(&mrulist_ctrl_c_hook);
3137 fprintf(stderr,
3138 "Ctrl-C will stop MRU retrieval and display partial results.\n");
3139 fflush(stderr);
3140
3141 order = MRUSORT_DEF;
3142 parms_buf[0] = '\0';
3143 parms = parms_buf;
3144 for (i = 0; i < pcmd->nargs; i++) {
3145 arg = pcmd->argval[i].string;
3146 if (arg != NULL) {
3147 cb = strlen(arg) + 1;
3148 if ((!strncmp(resall_eq, arg, sizeof(resall_eq)
3149 - 1) || !strncmp(resany_eq, arg,
3150 sizeof(resany_eq) - 1) || !strncmp(
3151 mincount_eq, arg, sizeof(mincount_eq) - 1)
3152 || !strncmp(laddr_eq, arg, sizeof(laddr_eq)
3153 - 1) || !strncmp(maxlstint_eq, arg,
3154 sizeof(laddr_eq) - 1)) && parms + cb + 2 <=
3155 parms_buf + sizeof(parms_buf)) {
3156 /* these are passed intact to ntpd */
3157 memcpy(parms, ", ", 2);
3158 parms += 2;
3159 memcpy(parms, arg, cb);
3160 parms += cb - 1;
3161 } else if (!strncmp(sort_eq, arg,
3162 sizeof(sort_eq) - 1)) {
3163 arg += sizeof(sort_eq) - 1;
3164 for (n = 0;
3165 n < COUNTOF(mru_sort_keywords);
3166 n++)
3167 if (!strcmp(mru_sort_keywords[n],
3168 arg))
3169 break;
3170 if (n < COUNTOF(mru_sort_keywords))
3171 order = n;
3172 } else if (!strcmp("limited", arg) ||
3173 !strcmp("kod", arg)) {
3174 /* transform to resany=... */
3175 snprintf(buf, sizeof(buf),
3176 ", resany=0x%x",
3177 ('k' == arg[0])
3178 ? RES_KOD
3179 : RES_LIMITED);
3180 cb = 1 + strlen(buf);
3181 if (parms + cb <
3182 parms_buf + sizeof(parms_buf)) {
3183 memcpy(parms, buf, cb);
3184 parms += cb - 1;
3185 }
3186 } else
3187 fprintf(stderr,
3188 "ignoring unrecognized mrulist parameter: %s\n",
3189 arg);
3190 }
3191 }
3192 parms = parms_buf;
3193
3194 if (!collect_mru_list(parms, &now))
3195 return;
3196
3197 /* display the results */
3198 if (rawmode)
3199 goto cleanup_return;
3200
3201 /* construct an array of entry pointers in default order */
3202 sorted = eallocarray(mru_count, sizeof(*sorted));
3203 ppentry = sorted;
3204 if (MRUSORT_R_DEF != order) {
3205 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3206 INSIST(ppentry < sorted + mru_count);
3207 *ppentry = recent;
3208 ppentry++;
3209 ITER_DLIST_END()
3210 } else {
3211 REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3212 INSIST(ppentry < sorted + mru_count);
3213 *ppentry = recent;
3214 ppentry++;
3215 REV_ITER_DLIST_END()
3216 }
3217
3218 if (ppentry - sorted != (int)mru_count) {
3219 fprintf(stderr,
3220 "mru_count %u should match MRU list depth %ld.\n",
3221 mru_count, (long)(ppentry - sorted));
3222 free(sorted);
3223 goto cleanup_return;
3224 }
3225
3226 /* re-sort sorted[] if not default or reverse default */
3227 if (MRUSORT_R_DEF < order)
3228 qsort(sorted, mru_count, sizeof(sorted[0]),
3229 mru_qcmp_table[order]);
3230
3231 mrulist_interrupted = FALSE;
3232 printf( "lstint avgint rstr r m v count rport remote address\n"
3233 "==============================================================================\n");
3234 /* '=' x 78 */
3235 for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) {
3236 recent = *ppentry;
3237 interval = now;
3238 L_SUB(&interval, &recent->last);
3239 LFPTOD(&interval, flstint);
3240 lstint = (int)(flstint + 0.5);
3241 interval = recent->last;
3242 L_SUB(&interval, &recent->first);
3243 LFPTOD(&interval, favgint);
3244 favgint /= recent->count;
3245 avgint = (int)(favgint + 0.5);
3246 fprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n",
3247 lstint, avgint, recent->rs,
3248 (RES_KOD & recent->rs)
3249 ? 'K'
3250 : (RES_LIMITED & recent->rs)
3251 ? 'L'
3252 : '.',
3253 (int)recent->mode, (int)recent->ver,
3254 recent->count, SRCPORT(&recent->addr),
3255 nntohost(&recent->addr));
3256 if (showhostnames)
3257 fflush(fp);
3258 if (mrulist_interrupted) {
3259 fputs("\n --interrupted--\n", fp);
3260 fflush(fp);
3261 break;
3262 }
3263 }
3264 fflush(fp);
3265 if (debug) {
3266 fprintf(stderr,
3267 "--- completed, freeing sorted[] pointers\n");
3268 fflush(stderr);
3269 }
3270 free(sorted);
3271
3272 cleanup_return:
3273 if (debug) {
3274 fprintf(stderr, "... freeing MRU entries\n");
3275 fflush(stderr);
3276 }
3277 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3278 free(recent);
3279 ITER_DLIST_END()
3280 if (debug) {
3281 fprintf(stderr, "... freeing hash_table[]\n");
3282 fflush(stderr);
3283 }
3284 free(hash_table);
3285 hash_table = NULL;
3286 INIT_DLIST(mru_list, mlink);
3287
3288 pop_ctrl_c_handler(&mrulist_ctrl_c_hook);
3289 }
3290
3291
3292 /*
3293 * validate_ifnum - helper for ifstats()
3294 *
3295 * Ensures rows are received in order and complete.
3296 */
3297 static void
3298 validate_ifnum(
3299 FILE * fp,
3300 u_int ifnum,
3301 int * pfields,
3302 ifstats_row * prow
3303 )
3304 {
3305 if (prow->ifnum == ifnum)
3306 return;
3307 if (prow->ifnum + 1 <= ifnum) {
3308 if (*pfields < IFSTATS_FIELDS)
3309 fprintf(fp, "Warning: incomplete row with %d (of %d) fields\n",
3310 *pfields, IFSTATS_FIELDS);
3311 *pfields = 0;
3312 prow->ifnum = ifnum;
3313 return;
3314 }
3315 fprintf(stderr,
3316 "received if index %u, have %d of %d fields for index %u, aborting.\n",
3317 ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum);
3318 exit(1);
3319 }
3320
3321
3322 /*
3323 * another_ifstats_field - helper for ifstats()
3324 *
3325 * If all fields for the row have been received, print it.
3326 */
3327 static void
3328 another_ifstats_field(
3329 int * pfields,
3330 ifstats_row * prow,
3331 FILE * fp
3332 )
3333 {
3334 u_int ifnum;
3335
3336 (*pfields)++;
3337 /* we understand 12 tags */
3338 if (IFSTATS_FIELDS > *pfields)
3339 return;
3340 /*
3341 " interface name send\n"
3342 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
3343 "==============================================================================\n");
3344 */
3345 fprintf(fp,
3346 "%3u %-24.24s %c %4x %3u %2u %6u %6u %6u %5u %8d\n"
3347 " %s\n",
3348 prow->ifnum, prow->name,
3349 (prow->enabled)
3350 ? '.'
3351 : 'D',
3352 prow->flags, prow->ttl, prow->mcast_count,
3353 prow->received, prow->sent, prow->send_errors,
3354 prow->peer_count, prow->uptime, sptoa(&prow->addr));
3355 if (!SOCK_UNSPEC(&prow->bcast))
3356 fprintf(fp, " %s\n", sptoa(&prow->bcast));
3357 ifnum = prow->ifnum;
3358 ZERO(*prow);
3359 prow->ifnum = ifnum;
3360 }
3361
3362
3363 /*
3364 * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats.
3365 */
3366 static void
3367 ifstats(
3368 struct parse * pcmd,
3369 FILE * fp
3370 )
3371 {
3372 const char addr_fmt[] = "addr.%u";
3373 const char bcast_fmt[] = "bcast.%u";
3374 const char en_fmt[] = "en.%u"; /* enabled */
3375 const char flags_fmt[] = "flags.%u";
3376 const char mc_fmt[] = "mc.%u"; /* mcast count */
3377 const char name_fmt[] = "name.%u";
3378 const char pc_fmt[] = "pc.%u"; /* peer count */
3379 const char rx_fmt[] = "rx.%u";
3380 const char tl_fmt[] = "tl.%u"; /* ttl */
3381 const char tx_fmt[] = "tx.%u";
3382 const char txerr_fmt[] = "txerr.%u";
3383 const char up_fmt[] = "up.%u"; /* uptime */
3384 const char * datap;
3385 int qres;
3386 size_t dsize;
3387 u_short rstatus;
3388 char * tag;
3389 char * val;
3390 int fields;
3391 u_int ui;
3392 ifstats_row row;
3393 int comprende;
3394 size_t len;
3395
3396 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus,
3397 &dsize, &datap);
3398 if (qres) /* message already displayed */
3399 return;
3400
3401 fprintf(fp,
3402 " interface name send\n"
3403 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
3404 "==============================================================================\n");
3405 /* '=' x 78 */
3406
3407 ZERO(row);
3408 fields = 0;
3409 ui = 0;
3410 while (nextvar(&dsize, &datap, &tag, &val)) {
3411 INSIST(tag && val);
3412 if (debug > 1)
3413 fprintf(stderr, "nextvar gave: %s = %s\n", tag, val);
3414 comprende = FALSE;
3415 switch(tag[0]) {
3416
3417 case 'a':
3418 if (1 == sscanf(tag, addr_fmt, &ui) &&
3419 decodenetnum(val, &row.addr))
3420 comprende = TRUE;
3421 break;
3422
3423 case 'b':
3424 if (1 == sscanf(tag, bcast_fmt, &ui) &&
3425 ('\0' == *val ||
3426 decodenetnum(val, &row.bcast)))
3427 comprende = TRUE;
3428 break;
3429
3430 case 'e':
3431 if (1 == sscanf(tag, en_fmt, &ui) &&
3432 1 == sscanf(val, "%d", &row.enabled))
3433 comprende = TRUE;
3434 break;
3435
3436 case 'f':
3437 if (1 == sscanf(tag, flags_fmt, &ui) &&
3438 1 == sscanf(val, "0x%x", &row.flags))
3439 comprende = TRUE;
3440 break;
3441
3442 case 'm':
3443 if (1 == sscanf(tag, mc_fmt, &ui) &&
3444 1 == sscanf(val, "%u", &row.mcast_count))
3445 comprende = TRUE;
3446 break;
3447
3448 case 'n':
3449 if (1 == sscanf(tag, name_fmt, &ui)) {
3450 /* strip quotes */
3451 len = strlen(val);
3452 if (len >= 2 &&
3453 len - 2 < sizeof(row.name)) {
3454 len -= 2;
3455 memcpy(row.name, val + 1, len);
3456 row.name[len] = '\0';
3457 comprende = TRUE;
3458 }
3459 }
3460 break;
3461
3462 case 'p':
3463 if (1 == sscanf(tag, pc_fmt, &ui) &&
3464 1 == sscanf(val, "%u", &row.peer_count))
3465 comprende = TRUE;
3466 break;
3467
3468 case 'r':
3469 if (1 == sscanf(tag, rx_fmt, &ui) &&
3470 1 == sscanf(val, "%u", &row.received))
3471 comprende = TRUE;
3472 break;
3473
3474 case 't':
3475 if (1 == sscanf(tag, tl_fmt, &ui) &&
3476 1 == sscanf(val, "%u", &row.ttl))
3477 comprende = TRUE;
3478 else if (1 == sscanf(tag, tx_fmt, &ui) &&
3479 1 == sscanf(val, "%u", &row.sent))
3480 comprende = TRUE;
3481 else if (1 == sscanf(tag, txerr_fmt, &ui) &&
3482 1 == sscanf(val, "%u", &row.send_errors))
3483 comprende = TRUE;
3484 break;
3485
3486 case 'u':
3487 if (1 == sscanf(tag, up_fmt, &ui) &&
3488 1 == sscanf(val, "%u", &row.uptime))
3489 comprende = TRUE;
3490 break;
3491 }
3492
3493 if (comprende) {
3494 /* error out if rows out of order */
3495 validate_ifnum(fp, ui, &fields, &row);
3496 /* if the row is complete, print it */
3497 another_ifstats_field(&fields, &row, fp);
3498 }
3499 }
3500 if (fields != IFSTATS_FIELDS)
3501 fprintf(fp, "Warning: incomplete row with %d (of %d) fields\n",
3502 fields, IFSTATS_FIELDS);
3503
3504 fflush(fp);
3505 }
3506
3507
3508 /*
3509 * validate_reslist_idx - helper for reslist()
3510 *
3511 * Ensures rows are received in order and complete.
3512 */
3513 static void
3514 validate_reslist_idx(
3515 FILE * fp,
3516 u_int idx,
3517 int * pfields,
3518 reslist_row * prow
3519 )
3520 {
3521 if (prow->idx == idx)
3522 return;
3523 if (prow->idx + 1 == idx) {
3524 if (*pfields < RESLIST_FIELDS)
3525 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3526 *pfields, RESLIST_FIELDS);
3527 *pfields = 0;
3528 prow->idx = idx;
3529 return;
3530 }
3531 fprintf(stderr,
3532 "received reslist index %u, have %d of %d fields for index %u, aborting.\n",
3533 idx, *pfields, RESLIST_FIELDS, prow->idx);
3534 exit(1);
3535 }
3536
3537
3538 /*
3539 * another_reslist_field - helper for reslist()
3540 *
3541 * If all fields for the row have been received, print it.
3542 */
3543 static void
3544 another_reslist_field(
3545 int * pfields,
3546 reslist_row * prow,
3547 FILE * fp
3548 )
3549 {
3550 char addrmaskstr[128];
3551 int prefix; /* subnet mask as prefix bits count */
3552 u_int idx;
3553
3554 (*pfields)++;
3555 /* we understand 4 tags */
3556 if (RESLIST_FIELDS > *pfields)
3557 return;
3558
3559 prefix = sockaddr_masktoprefixlen(&prow->mask);
3560 if (prefix >= 0)
3561 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d",
3562 stoa(&prow->addr), prefix);
3563 else
3564 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s",
3565 stoa(&prow->addr), stoa(&prow->mask));
3566
3567 /*
3568 " hits addr/prefix or addr mask\n"
3569 " restrictions\n"
3570 "==============================================================================\n");
3571 */
3572 fprintf(fp,
3573 "%10lu %s\n"
3574 " %s\n",
3575 prow->hits, addrmaskstr, prow->flagstr);
3576 idx = prow->idx;
3577 ZERO(*prow);
3578 prow->idx = idx;
3579 }
3580
3581
3582 /*
3583 * reslist - ntpq -c reslist modeled on ntpdc -c reslist.
3584 */
3585 static void
3586 reslist(
3587 struct parse * pcmd,
3588 FILE * fp
3589 )
3590 {
3591 const char addr_fmtu[] = "addr.%u";
3592 const char mask_fmtu[] = "mask.%u";
3593 const char hits_fmt[] = "hits.%u";
3594 const char flags_fmt[] = "flags.%u";
3595 const char qdata[] = "addr_restrictions";
3596 const int qdata_chars = COUNTOF(qdata) - 1;
3597 const char * datap;
3598 int qres;
3599 size_t dsize;
3600 u_short rstatus;
3601 char * tag;
3602 char * val;
3603 int fields;
3604 u_int ui;
3605 reslist_row row;
3606 int comprende;
3607 size_t len;
3608
3609 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars,
3610 qdata, &rstatus, &dsize, &datap);
3611 if (qres) /* message already displayed */
3612 return;
3613
3614 fprintf(fp,
3615 " hits addr/prefix or addr mask\n"
3616 " restrictions\n"
3617 "==============================================================================\n");
3618 /* '=' x 78 */
3619
3620 ZERO(row);
3621 fields = 0;
3622 ui = 0;
3623 while (nextvar(&dsize, &datap, &tag, &val)) {
3624 INSIST(tag && val);
3625 if (debug > 1)
3626 fprintf(stderr, "nextvar gave: %s = %s\n", tag, val);
3627 comprende = FALSE;
3628 switch(tag[0]) {
3629
3630 case 'a':
3631 if (1 == sscanf(tag, addr_fmtu, &ui) &&
3632 decodenetnum(val, &row.addr))
3633 comprende = TRUE;
3634 break;
3635
3636 case 'f':
3637 if (1 == sscanf(tag, flags_fmt, &ui)) {
3638 if (NULL == val) {
3639 row.flagstr[0] = '\0';
3640 comprende = TRUE;
3641 } else if ((len = strlen(val)) < sizeof(row.flagstr)) {
3642 memcpy(row.flagstr, val, len);
3643 row.flagstr[len] = '\0';
3644 comprende = TRUE;
3645 } else {
3646 /* no flags, and still !comprende */
3647 row.flagstr[0] = '\0';
3648 }
3649 }
3650 break;
3651
3652 case 'h':
3653 if (1 == sscanf(tag, hits_fmt, &ui) &&
3654 1 == sscanf(val, "%lu", &row.hits))
3655 comprende = TRUE;
3656 break;
3657
3658 case 'm':
3659 if (1 == sscanf(tag, mask_fmtu, &ui) &&
3660 decodenetnum(val, &row.mask))
3661 comprende = TRUE;
3662 break;
3663 }
3664
3665 if (comprende) {
3666 /* error out if rows out of order */
3667 validate_reslist_idx(fp, ui, &fields, &row);
3668 /* if the row is complete, print it */
3669 another_reslist_field(&fields, &row, fp);
3670 }
3671 }
3672 if (fields != RESLIST_FIELDS)
3673 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3674 fields, RESLIST_FIELDS);
3675
3676 fflush(fp);
3677 }
3678
3679
3680 /*
3681 * collect_display_vdc
3682 */
3683 static void
3684 collect_display_vdc(
3685 associd_t as,
3686 vdc * table,
3687 int decodestatus,
3688 FILE * fp
3689 )
3690 {
3691 static const char * const suf[2] = { "adr", "port" };
3692 static const char * const leapbits[4] = { "00", "01",
3693 "10", "11" };
3694 struct varlist vl[MAXLIST];
3695 char tagbuf[32];
3696 vdc *pvdc;
3697 u_short rstatus;
3698 size_t rsize;
3699 const char *rdata;
3700 int qres;
3701 char *tag;
3702 char *val;
3703 u_int n;
3704 size_t len;
3705 int match;
3706 u_long ul;
3707 int vtype;
3708
3709 ZERO(vl);
3710 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3711 ZERO(pvdc->v);
3712 if (NTP_ADD != pvdc->type) {
3713 doaddvlist(vl, pvdc->tag);
3714 } else {
3715 for (n = 0; n < COUNTOF(suf); n++) {
3716 snprintf(tagbuf, sizeof(tagbuf), "%s%s",
3717 pvdc->tag, suf[n]);
3718 doaddvlist(vl, tagbuf);
3719 }
3720 }
3721 }
3722 qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize,
3723 &rdata);
3724 doclearvlist(vl);
3725 if (qres)
3726 return; /* error msg already displayed */
3727
3728 /*
3729 * iterate over the response variables filling vdc_table with
3730 * the retrieved values.
3731 */
3732 while (nextvar(&rsize, &rdata, &tag, &val)) {
3733 INSIST(tag && val);
3734 n = 0;
3735 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3736 len = strlen(pvdc->tag);
3737 if (strncmp(tag, pvdc->tag, len))
3738 continue;
3739 if (NTP_ADD != pvdc->type) {
3740 if ('\0' != tag[len])
3741 continue;
3742 break;
3743 }
3744 match = FALSE;
3745 for (n = 0; n < COUNTOF(suf); n++) {
3746 if (strcmp(tag + len, suf[n]))
3747 continue;
3748 match = TRUE;
3749 break;
3750 }
3751 if (match)
3752 break;
3753 }
3754 if (NULL == pvdc->tag)
3755 continue;
3756 switch (pvdc->type) {
3757
3758 case NTP_STR:
3759 /* strip surrounding double quotes */
3760 if ('"' == val[0]) {
3761 len = strlen(val);
3762 if (len > 0 && '"' == val[len - 1]) {
3763 val[len - 1] = '\0';
3764 val++;
3765 }
3766 }
3767 /* fallthru */
3768 case NTP_MODE: /* fallthru */
3769 case NTP_2BIT:
3770 pvdc->v.str = estrdup(val);
3771 break;
3772
3773 case NTP_LFP:
3774 decodets(val, &pvdc->v.lfp);
3775 break;
3776
3777 case NTP_ADP:
3778 if (!decodenetnum(val, &pvdc->v.sau))
3779 fprintf(stderr, "malformed %s=%s\n",
3780 pvdc->tag, val);
3781 break;
3782
3783 case NTP_ADD:
3784 if (0 == n) { /* adr */
3785 if (!decodenetnum(val, &pvdc->v.sau))
3786 fprintf(stderr,
3787 "malformed %s=%s\n",
3788 pvdc->tag, val);
3789 } else { /* port */
3790 if (atouint(val, &ul))
3791 SET_PORT(&pvdc->v.sau,
3792 (u_short)ul);
3793 }
3794 break;
3795 }
3796 }
3797
3798 /* and display */
3799 if (decodestatus) {
3800 vtype = (0 == as)
3801 ? TYPE_SYS
3802 : TYPE_PEER;
3803 fprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus,
3804 statustoa(vtype, rstatus));
3805 }
3806
3807 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3808 switch (pvdc->type) {
3809
3810 case NTP_STR:
3811 if (pvdc->v.str != NULL) {
3812 fprintf(fp, "%s %s\n", pvdc->display,
3813 pvdc->v.str);
3814 free(pvdc->v.str);
3815 pvdc->v.str = NULL;
3816 }
3817 break;
3818
3819 case NTP_ADD: /* fallthru */
3820 case NTP_ADP:
3821 fprintf(fp, "%s %s\n", pvdc->display,
3822 nntohostp(&pvdc->v.sau));
3823 break;
3824
3825 case NTP_LFP:
3826 fprintf(fp, "%s %s\n", pvdc->display,
3827 prettydate(&pvdc->v.lfp));
3828 break;
3829
3830 case NTP_MODE:
3831 atouint(pvdc->v.str, &ul);
3832 fprintf(fp, "%s %s\n", pvdc->display,
3833 modetoa((int)ul));
3834 break;
3835
3836 case NTP_2BIT:
3837 atouint(pvdc->v.str, &ul);
3838 fprintf(fp, "%s %s\n", pvdc->display,
3839 leapbits[ul & 0x3]);
3840 break;
3841
3842 default:
3843 fprintf(stderr, "unexpected vdc type %d for %s\n",
3844 pvdc->type, pvdc->tag);
3845 break;
3846 }
3847 }
3848 }
3849
3850
3851 /*
3852 * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats
3853 */
3854 static void
3855 sysstats(
3856 struct parse *pcmd,
3857 FILE *fp
3858 )
3859 {
3860 static vdc sysstats_vdc[] = {
3861 VDC_INIT("ss_uptime", "uptime: ", NTP_STR),
3862 VDC_INIT("ss_reset", "sysstats reset: ", NTP_STR),
3863 VDC_INIT("ss_received", "packets received: ", NTP_STR),
3864 VDC_INIT("ss_thisver", "current version: ", NTP_STR),
3865 VDC_INIT("ss_oldver", "older version: ", NTP_STR),
3866 VDC_INIT("ss_badformat", "bad length or format: ", NTP_STR),
3867 VDC_INIT("ss_badauth", "authentication failed:", NTP_STR),
3868 VDC_INIT("ss_declined", "declined: ", NTP_STR),
3869 VDC_INIT("ss_restricted", "restricted: ", NTP_STR),
3870 VDC_INIT("ss_limited", "rate limited: ", NTP_STR),
3871 VDC_INIT("ss_kodsent", "KoD responses: ", NTP_STR),
3872 VDC_INIT("ss_processed", "processed for time: ", NTP_STR),
3873 #if 0
3874 VDC_INIT("ss_lamport", "Lamport violations: ", NTP_STR),
3875 VDC_INIT("ss_tsrounding", "bad timestamp rounding:", NTP_STR),
3876 #endif
3877 VDC_INIT(NULL, NULL, 0)
3878 };
3879
3880 collect_display_vdc(0, sysstats_vdc, FALSE, fp);
3881 }
3882
3883
3884 /*
3885 * sysinfo - modeled on ntpdc's sysinfo
3886 */
3887 static void
3888 sysinfo(
3889 struct parse *pcmd,
3890 FILE *fp
3891 )
3892 {
3893 static vdc sysinfo_vdc[] = {
3894 VDC_INIT("peeradr", "system peer: ", NTP_ADP),
3895 VDC_INIT("peermode", "system peer mode: ", NTP_MODE),
3896 VDC_INIT("leap", "leap indicator: ", NTP_2BIT),
3897 VDC_INIT("stratum", "stratum: ", NTP_STR),
3898 VDC_INIT("precision", "log2 precision: ", NTP_STR),
3899 VDC_INIT("rootdelay", "root delay: ", NTP_STR),
3900 VDC_INIT("rootdisp", "root dispersion: ", NTP_STR),
3901 VDC_INIT("refid", "reference ID: ", NTP_STR),
3902 VDC_INIT("reftime", "reference time: ", NTP_LFP),
3903 VDC_INIT("sys_jitter", "system jitter: ", NTP_STR),
3904 VDC_INIT("clk_jitter", "clock jitter: ", NTP_STR),
3905 VDC_INIT("clk_wander", "clock wander: ", NTP_STR),
3906 VDC_INIT("bcastdelay", "broadcast delay: ", NTP_STR),
3907 VDC_INIT("authdelay", "symm. auth. delay:", NTP_STR),
3908 VDC_INIT(NULL, NULL, 0)
3909 };
3910
3911 collect_display_vdc(0, sysinfo_vdc, TRUE, fp);
3912 }
3913
3914
3915 /*
3916 * kerninfo - modeled on ntpdc's kerninfo
3917 */
3918 static void
3919 kerninfo(
3920 struct parse *pcmd,
3921 FILE *fp
3922 )
3923 {
3924 static vdc kerninfo_vdc[] = {
3925 VDC_INIT("koffset", "pll offset: ", NTP_STR),
3926 VDC_INIT("kfreq", "pll frequency: ", NTP_STR),
3927 VDC_INIT("kmaxerr", "maximum error: ", NTP_STR),
3928 VDC_INIT("kesterr", "estimated error: ", NTP_STR),
3929 VDC_INIT("kstflags", "kernel status: ", NTP_STR),
3930 VDC_INIT("ktimeconst", "pll time constant: ", NTP_STR),
3931 VDC_INIT("kprecis", "precision: ", NTP_STR),
3932 VDC_INIT("kfreqtol", "frequency tolerance: ", NTP_STR),
3933 VDC_INIT("kppsfreq", "pps frequency: ", NTP_STR),
3934 VDC_INIT("kppsstab", "pps stability: ", NTP_STR),
3935 VDC_INIT("kppsjitter", "pps jitter: ", NTP_STR),
3936 VDC_INIT("kppscalibdur", "calibration interval ", NTP_STR),
3937 VDC_INIT("kppscalibs", "calibration cycles: ", NTP_STR),
3938 VDC_INIT("kppsjitexc", "jitter exceeded: ", NTP_STR),
3939 VDC_INIT("kppsstbexc", "stability exceeded: ", NTP_STR),
3940 VDC_INIT("kppscaliberrs", "calibration errors: ", NTP_STR),
3941 VDC_INIT(NULL, NULL, 0)
3942 };
3943
3944 collect_display_vdc(0, kerninfo_vdc, TRUE, fp);
3945 }
3946
3947
3948 /*
3949 * monstats - implements ntpq -c monstats
3950 */
3951 static void
3952 monstats(
3953 struct parse *pcmd,
3954 FILE *fp
3955 )
3956 {
3957 static vdc monstats_vdc[] = {
3958 VDC_INIT("mru_enabled", "enabled: ", NTP_STR),
3959 VDC_INIT("mru_depth", "addresses: ", NTP_STR),
3960 VDC_INIT("mru_deepest", "peak addresses: ", NTP_STR),
3961 VDC_INIT("mru_maxdepth", "maximum addresses: ", NTP_STR),
3962 VDC_INIT("mru_mindepth", "reclaim above count:", NTP_STR),
3963 VDC_INIT("mru_maxage", "reclaim older than: ", NTP_STR),
3964 VDC_INIT("mru_mem", "kilobytes: ", NTP_STR),
3965 VDC_INIT("mru_maxmem", "maximum kilobytes: ", NTP_STR),
3966 VDC_INIT(NULL, NULL, 0)
3967 };
3968
3969 collect_display_vdc(0, monstats_vdc, FALSE, fp);
3970 }
3971
3972
3973 /*
3974 * iostats - ntpq -c iostats - network input and output counters
3975 */
3976 static void
3977 iostats(
3978 struct parse *pcmd,
3979 FILE *fp
3980 )
3981 {
3982 static vdc iostats_vdc[] = {
3983 VDC_INIT("iostats_reset", "time since reset: ", NTP_STR),
3984 VDC_INIT("total_rbuf", "receive buffers: ", NTP_STR),
3985 VDC_INIT("free_rbuf", "free receive buffers: ", NTP_STR),
3986 VDC_INIT("used_rbuf", "used receive buffers: ", NTP_STR),
3987 VDC_INIT("rbuf_lowater", "low water refills: ", NTP_STR),
3988 VDC_INIT("io_dropped", "dropped packets: ", NTP_STR),
3989 VDC_INIT("io_ignored", "ignored packets: ", NTP_STR),
3990 VDC_INIT("io_received", "received packets: ", NTP_STR),
3991 VDC_INIT("io_sent", "packets sent: ", NTP_STR),
3992 VDC_INIT("io_sendfailed", "packet send failures: ", NTP_STR),
3993 VDC_INIT("io_wakeups", "input wakeups: ", NTP_STR),
3994 VDC_INIT("io_goodwakeups", "useful input wakeups: ", NTP_STR),
3995 VDC_INIT(NULL, NULL, 0)
3996 };
3997
3998 collect_display_vdc(0, iostats_vdc, FALSE, fp);
3999 }
4000
4001
4002 /*
4003 * timerstats - ntpq -c timerstats - interval timer counters
4004 */
4005 static void
4006 timerstats(
4007 struct parse *pcmd,
4008 FILE *fp
4009 )
4010 {
4011 static vdc timerstats_vdc[] = {
4012 VDC_INIT("timerstats_reset", "time since reset: ", NTP_STR),
4013 VDC_INIT("timer_overruns", "timer overruns: ", NTP_STR),
4014 VDC_INIT("timer_xmts", "calls to transmit: ", NTP_STR),
4015 VDC_INIT(NULL, NULL, 0)
4016 };
4017
4018 collect_display_vdc(0, timerstats_vdc, FALSE, fp);
4019 }
4020
4021
4022 /*
4023 * authinfo - implements ntpq -c authinfo
4024 */
4025 static void
4026 authinfo(
4027 struct parse *pcmd,
4028 FILE *fp
4029 )
4030 {
4031 static vdc authinfo_vdc[] = {
4032 VDC_INIT("authreset", "time since reset:", NTP_STR),
4033 VDC_INIT("authkeys", "stored keys: ", NTP_STR),
4034 VDC_INIT("authfreek", "free keys: ", NTP_STR),
4035 VDC_INIT("authklookups", "key lookups: ", NTP_STR),
4036 VDC_INIT("authknotfound", "keys not found: ", NTP_STR),
4037 VDC_INIT("authkuncached", "uncached keys: ", NTP_STR),
4038 VDC_INIT("authkexpired", "expired keys: ", NTP_STR),
4039 VDC_INIT("authencrypts", "encryptions: ", NTP_STR),
4040 VDC_INIT("authdecrypts", "decryptions: ", NTP_STR),
4041 VDC_INIT(NULL, NULL, 0)
4042 };
4043
4044 collect_display_vdc(0, authinfo_vdc, FALSE, fp);
4045 }
4046
4047
4048 /*
4049 * pstats - show statistics for a peer
4050 */
4051 static void
4052 pstats(
4053 struct parse *pcmd,
4054 FILE *fp
4055 )
4056 {
4057 static vdc pstats_vdc[] = {
4058 VDC_INIT("src", "remote host: ", NTP_ADD),
4059 VDC_INIT("dst", "local address: ", NTP_ADD),
4060 VDC_INIT("timerec", "time last received: ", NTP_STR),
4061 VDC_INIT("timer", "time until next send:", NTP_STR),
4062 VDC_INIT("timereach", "reachability change: ", NTP_STR),
4063 VDC_INIT("sent", "packets sent: ", NTP_STR),
4064 VDC_INIT("received", "packets received: ", NTP_STR),
4065 VDC_INIT("badauth", "bad authentication: ", NTP_STR),
4066 VDC_INIT("bogusorg", "bogus origin: ", NTP_STR),
4067 VDC_INIT("oldpkt", "duplicate: ", NTP_STR),
4068 VDC_INIT("seldisp", "bad dispersion: ", NTP_STR),
4069 VDC_INIT("selbroken", "bad reference time: ", NTP_STR),
4070 VDC_INIT("candidate", "candidate order: ", NTP_STR),
4071 VDC_INIT(NULL, NULL, 0)
4072 };
4073 associd_t associd;
4074
4075 associd = checkassocid(pcmd->argval[0].uval);
4076 if (0 == associd)
4077 return;
4078
4079 collect_display_vdc(associd, pstats_vdc, TRUE, fp);
4080 }
4081