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