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