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