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