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
13extern char	currenthost[];
14extern int	currenthostisnum;
15size_t		maxhostlen;
16
17/*
18 * Declarations for command handlers in here
19 */
20static	associd_t checkassocid	(u_int32);
21static	struct varlist *findlistvar (struct varlist *, char *);
22static	void	doaddvlist	(struct varlist *, const char *);
23static	void	dormvlist	(struct varlist *, const char *);
24static	void	doclearvlist	(struct varlist *);
25static	void	makequerydata	(struct varlist *, size_t *, char *);
26static	int	doquerylist	(struct varlist *, int, associd_t, int,
27				 u_short *, size_t *, const char **);
28static	void	doprintvlist	(struct varlist *, FILE *);
29static	void	addvars 	(struct parse *, FILE *);
30static	void	rmvars		(struct parse *, FILE *);
31static	void	clearvars	(struct parse *, FILE *);
32static	void	showvars	(struct parse *, FILE *);
33static	int	dolist		(struct varlist *, associd_t, int, int,
34				 FILE *);
35static	void	readlist	(struct parse *, FILE *);
36static	void	writelist	(struct parse *, FILE *);
37static	void	readvar 	(struct parse *, FILE *);
38static	void	writevar	(struct parse *, FILE *);
39static	void	clocklist	(struct parse *, FILE *);
40static	void	clockvar	(struct parse *, FILE *);
41static	int	findassidrange	(u_int32, u_int32, int *, int *,
42				 FILE *);
43static	void	mreadlist	(struct parse *, FILE *);
44static	void	mreadvar	(struct parse *, FILE *);
45static	void	printassoc	(int, FILE *);
46static	void	associations	(struct parse *, FILE *);
47static	void	lassociations	(struct parse *, FILE *);
48static	void	passociations	(struct parse *, FILE *);
49static	void	lpassociations	(struct parse *, FILE *);
50
51#ifdef	UNUSED
52static	void	radiostatus (struct parse *, FILE *);
53#endif	/* UNUSED */
54
55static	void	authinfo	(struct parse *, FILE *);
56static	void	pstats	 	(struct parse *, FILE *);
57static	long	when		(l_fp *, l_fp *, l_fp *);
58static	char *	prettyinterval	(char *, size_t, long);
59static	int	doprintpeers	(struct varlist *, int, int, size_t, const char *, FILE *, int);
60static	int	dogetpeers	(struct varlist *, associd_t, FILE *, int);
61static	void	dopeers 	(int, FILE *, int);
62static	void	peers		(struct parse *, FILE *);
63static	void	doapeers 	(int, FILE *, int);
64static	void	apeers		(struct parse *, FILE *);
65static	void	lpeers		(struct parse *, FILE *);
66static	void	doopeers	(int, FILE *, int);
67static	void	opeers		(struct parse *, FILE *);
68static	void	lopeers 	(struct parse *, FILE *);
69static	void	config		(struct parse *, FILE *);
70static	void	saveconfig	(struct parse *, FILE *);
71static	void	config_from_file(struct parse *, FILE *);
72static	void	mrulist		(struct parse *, FILE *);
73static	void	ifstats		(struct parse *, FILE *);
74static	void	reslist		(struct parse *, FILE *);
75static	void	sysstats	(struct parse *, FILE *);
76static	void	sysinfo		(struct parse *, FILE *);
77static	void	kerninfo	(struct parse *, FILE *);
78static	void	monstats	(struct parse *, FILE *);
79static	void	iostats		(struct parse *, FILE *);
80static	void	timerstats	(struct parse *, FILE *);
81
82/*
83 * Commands we understand.	Ntpdc imports this.
84 */
85struct 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 */
233typedef 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
245const 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
256typedef 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
273char flash2[] = " .+*    "; /* flash decode for version 2 */
274char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
275
276struct varlist {
277	const char *name;
278	char *value;
279} g_varlist[MAXLIST] = { { 0, 0 } };
280
281/*
282 * Imported from ntpq.c
283 */
284extern int showhostnames;
285extern int wideremote;
286extern int rawmode;
287extern struct servent *server_entry;
288extern struct association *assoc_cache;
289extern u_char pktversion;
290
291typedef struct mru_tag mru;
292struct 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
304typedef 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
320typedef 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
328typedef 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 */
346static int	mrulist_ctrl_c_hook(void);
347static mru *	add_mru(mru *);
348static int	collect_mru_list(const char *, l_fp *);
349static int	fetch_nonce(char *, size_t);
350static int	qcmp_mru_avgint(const void *, const void *);
351static int	qcmp_mru_r_avgint(const void *, const void *);
352static int	qcmp_mru_addr(const void *, const void *);
353static int	qcmp_mru_r_addr(const void *, const void *);
354static int	qcmp_mru_count(const void *, const void *);
355static int	qcmp_mru_r_count(const void *, const void *);
356static void	validate_ifnum(FILE *, u_int, int *, ifstats_row *);
357static void	another_ifstats_field(int *, ifstats_row *, FILE *);
358static void	collect_display_vdc(associd_t as, vdc *table,
359				    int decodestatus, FILE *fp);
360
361/*
362 * static globals
363 */
364static u_int	mru_count;
365static u_int	mru_dupes;
366volatile int	mrulist_interrupted;
367static mru	mru_list;		/* listhead */
368static 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 */
374static 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 */
388static associd_t
389checkassocid(
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 */
415static struct varlist *
416findlistvar(
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 */
436static void
437doaddvlist(
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 */
472static void
473dormvlist(
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 */
508static void
509doclearvlist(
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 */
529static void
530makequerydata(
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 */
575static int
576doquerylist(
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 */
600static void
601doprintvlist(
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*/
625static void
626addvars(
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*/
639static void
640rmvars(
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*/
653static void
654clearvars(
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*/
667static void
668showvars(
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 */
680static int
681dolist(
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 */
733static void
734readlist(
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 */
762static void
763writelist(
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 */
806static void
807readvar(
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 */
844static void
845writevar(
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 */
893static void
894clocklist(
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 */
918static void
919clockvar(
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 */
946static int
947findassidrange(
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 */
1000static void
1001mreadlist(
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 */
1028static void
1029mreadvar(
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 */
1068int
1069dogetassoc(
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 */
1131static void
1132printassoc(
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*/
1319static void
1320associations(
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*/
1334static void
1335lassociations(
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*/
1349static void
1350passociations(
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*/
1363static void
1364lpassociations(
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 */
1376static void
1377saveconfig(
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*/
1410static void
1411radiostatus(
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 */
1441static long
1442when(
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 */
1466static char *
1467prettyinterval(
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
1513static char
1514decodeaddrtype(
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 */
1546struct 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
1564struct 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
1583struct 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 */
1607static int
1608doprintpeers(
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 */
1906static int
1907dogetpeers(
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 */
1950static void
1951dopeers(
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 */
2004static void
2005doapeers(
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*/
2059static void
2060peers(
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*/
2085static void
2086apeers(
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*/
2107static void
2108lpeers(
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 */
2128static void
2129doopeers(
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*/
2174static void
2175opeers(
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*/
2196static void
2197lopeers(
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 */
2217static void
2218config (
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 */
2287static void
2288config_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
2368static int
2369fetch_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 */
2419static mru *
2420add_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
2479int
2480mrulist_ctrl_c_hook(void)
2481{
2482	mrulist_interrupted = TRUE;
2483	return TRUE;
2484}
2485
2486
2487static int
2488collect_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
2929cleanup_return:
2930	free(hash_table);
2931	hash_table = NULL;
2932
2933retain_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 */
2947static int
2948qcmp_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
2984static int
2985qcmp_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 */
2997static int
2998qcmp_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
3019static int
3020qcmp_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 */
3032static int
3033qcmp_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
3074static int
3075qcmp_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 */
3105static void
3106mrulist(
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
3272cleanup_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 */
3297static void
3298validate_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 */
3327static void
3328another_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 */
3366static void
3367ifstats(
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 */
3513static void
3514validate_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 */
3543static void
3544another_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 */
3585static void
3586reslist(
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 */
3683static void
3684collect_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 */
3854static void
3855sysstats(
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 */
3887static void
3888sysinfo(
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 */
3918static void
3919kerninfo(
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 */
3951static void
3952monstats(
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 */
3976static void
3977iostats(
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 */
4005static void
4006timerstats(
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 */
4025static void
4026authinfo(
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 */
4051static void
4052pstats(
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