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