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