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