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