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