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