ntpq-subs.c revision 316722
1215976Sjmallett/*
2215976Sjmallett * ntpq-subs.c - subroutines which are called to perform ntpq commands.
3215976Sjmallett */
4215976Sjmallett#include <config.h>
5215976Sjmallett#include <stdio.h>
6215976Sjmallett#include <ctype.h>
7215976Sjmallett#include <sys/types.h>
8215976Sjmallett#include <sys/time.h>
9215976Sjmallett
10215976Sjmallett#include "ntpq.h"
11215976Sjmallett#include "ntpq-opts.h"
12215976Sjmallett
13215976Sjmallettextern char	currenthost[];
14215976Sjmallettextern int	currenthostisnum;
15215976Sjmallettsize_t		maxhostlen;
16215976Sjmallett
17215976Sjmallett/*
18215976Sjmallett * Declarations for command handlers in here
19215976Sjmallett */
20215976Sjmallettstatic	associd_t checkassocid	(u_int32);
21215976Sjmallettstatic	struct varlist *findlistvar (struct varlist *, char *);
22215976Sjmallettstatic	void	doaddvlist	(struct varlist *, const char *);
23215976Sjmallettstatic	void	dormvlist	(struct varlist *, const char *);
24215976Sjmallettstatic	void	doclearvlist	(struct varlist *);
25215976Sjmallettstatic	void	makequerydata	(struct varlist *, size_t *, char *);
26215976Sjmallettstatic	int	doquerylist	(struct varlist *, int, associd_t, int,
27215976Sjmallett				 u_short *, size_t *, const char **);
28215976Sjmallettstatic	void	doprintvlist	(struct varlist *, FILE *);
29215976Sjmallettstatic	void	addvars 	(struct parse *, FILE *);
30215976Sjmallettstatic	void	rmvars		(struct parse *, FILE *);
31215976Sjmallettstatic	void	clearvars	(struct parse *, FILE *);
32215976Sjmallettstatic	void	showvars	(struct parse *, FILE *);
33215976Sjmallettstatic	int	dolist		(struct varlist *, associd_t, int, int,
34215976Sjmallett				 FILE *);
35215976Sjmallettstatic	void	readlist	(struct parse *, FILE *);
36215976Sjmallettstatic	void	writelist	(struct parse *, FILE *);
37215976Sjmallettstatic	void	readvar 	(struct parse *, FILE *);
38215976Sjmallettstatic	void	writevar	(struct parse *, FILE *);
39215976Sjmallettstatic	void	clocklist	(struct parse *, FILE *);
40215976Sjmallettstatic	void	clockvar	(struct parse *, FILE *);
41215976Sjmallettstatic	int	findassidrange	(u_int32, u_int32, int *, int *,
42215976Sjmallett				 FILE *);
43215976Sjmallettstatic	void	mreadlist	(struct parse *, FILE *);
44215976Sjmallettstatic	void	mreadvar	(struct parse *, FILE *);
45215976Sjmallettstatic	void	printassoc	(int, FILE *);
46215976Sjmallettstatic	void	associations	(struct parse *, FILE *);
47215976Sjmallettstatic	void	lassociations	(struct parse *, FILE *);
48215976Sjmallettstatic	void	passociations	(struct parse *, FILE *);
49215976Sjmallettstatic	void	lpassociations	(struct parse *, FILE *);
50215976Sjmallett
51215976Sjmallett#ifdef	UNUSED
52215976Sjmallettstatic	void	radiostatus (struct parse *, FILE *);
53215976Sjmallett#endif	/* UNUSED */
54215976Sjmallett
55215976Sjmallettstatic	void	authinfo	(struct parse *, FILE *);
56215976Sjmallettstatic	void	pstats	 	(struct parse *, FILE *);
57215976Sjmallettstatic	long	when		(l_fp *, l_fp *, l_fp *);
58215976Sjmallettstatic	char *	prettyinterval	(char *, size_t, long);
59215976Sjmallettstatic	int	doprintpeers	(struct varlist *, int, int, size_t, const char *, FILE *, int);
60215976Sjmallettstatic	int	dogetpeers	(struct varlist *, associd_t, FILE *, int);
61215976Sjmallettstatic	void	dopeers 	(int, FILE *, int);
62215976Sjmallettstatic	void	peers		(struct parse *, FILE *);
63215976Sjmallettstatic	void	doapeers 	(int, FILE *, int);
64215976Sjmallettstatic	void	apeers		(struct parse *, FILE *);
65215976Sjmallettstatic	void	lpeers		(struct parse *, FILE *);
66215976Sjmallettstatic	void	doopeers	(int, FILE *, int);
67215976Sjmallettstatic	void	opeers		(struct parse *, FILE *);
68215976Sjmallettstatic	void	lopeers 	(struct parse *, FILE *);
69215976Sjmallettstatic	void	config		(struct parse *, FILE *);
70215976Sjmallettstatic	void	saveconfig	(struct parse *, FILE *);
71215976Sjmallettstatic	void	config_from_file(struct parse *, FILE *);
72215976Sjmallettstatic	void	mrulist		(struct parse *, FILE *);
73215976Sjmallettstatic	void	ifstats		(struct parse *, FILE *);
74215976Sjmallettstatic	void	reslist		(struct parse *, FILE *);
75215976Sjmallettstatic	void	sysstats	(struct parse *, FILE *);
76215976Sjmallettstatic	void	sysinfo		(struct parse *, FILE *);
77215976Sjmallettstatic	void	kerninfo	(struct parse *, FILE *);
78215976Sjmallettstatic	void	monstats	(struct parse *, FILE *);
79215976Sjmallettstatic	void	iostats		(struct parse *, FILE *);
80215976Sjmallettstatic	void	timerstats	(struct parse *, FILE *);
81215976Sjmallett
82215976Sjmallett/*
83215976Sjmallett * Commands we understand.	Ntpdc imports this.
84215976Sjmallett */
85215976Sjmallettstruct xcmd opcmds[] = {
86215976Sjmallett	{ "saveconfig", saveconfig, { NTP_STR, NO, NO, NO },
87215976Sjmallett		{ "filename", "", "", ""},
88215976Sjmallett		"save ntpd configuration to file, . for current config file"},
89215976Sjmallett	{ "associations", associations, {  NO, NO, NO, NO },
90215976Sjmallett	  { "", "", "", "" },
91215976Sjmallett	  "print list of association ID's and statuses for the server's peers" },
92215976Sjmallett	{ "passociations", passociations,   {  NO, NO, NO, NO },
93215976Sjmallett	  { "", "", "", "" },
94215976Sjmallett	  "print list of associations returned by last associations command" },
95215976Sjmallett	{ "lassociations", lassociations,   {  NO, NO, NO, NO },
96215976Sjmallett	  { "", "", "", "" },
97215976Sjmallett	  "print list of associations including all client information" },
98215976Sjmallett	{ "lpassociations", lpassociations, {  NO, NO, NO, NO },
99215976Sjmallett	  { "", "", "", "" },
100215976Sjmallett	  "print last obtained list of associations, including client information" },
101215976Sjmallett	{ "addvars",    addvars,    { NTP_STR, NO, NO, NO },
102215976Sjmallett	  { "name[=value][,...]", "", "", "" },
103215976Sjmallett	  "add variables to the variable list or change their values" },
104215976Sjmallett	{ "rmvars", rmvars,     { NTP_STR, NO, NO, NO },
105215976Sjmallett	  { "name[,...]", "", "", "" },
106215976Sjmallett	  "remove variables from the variable list" },
107215976Sjmallett	{ "clearvars",  clearvars,  { NO, NO, NO, NO },
108215976Sjmallett	  { "", "", "", "" },
109215976Sjmallett	  "remove all variables from the variable list" },
110215976Sjmallett	{ "showvars",   showvars,   { NO, NO, NO, NO },
111215976Sjmallett	  { "", "", "", "" },
112215976Sjmallett	  "print variables on the variable list" },
113215976Sjmallett	{ "readlist",   readlist,   { OPT|NTP_UINT, NO, NO, NO },
114215976Sjmallett	  { "assocID", "", "", "" },
115215976Sjmallett	  "read the system or peer variables included in the variable list" },
116215976Sjmallett	{ "rl",     readlist,   { OPT|NTP_UINT, NO, NO, NO },
117215976Sjmallett	  { "assocID", "", "", "" },
118215976Sjmallett	  "read the system or peer variables included in the variable list" },
119215976Sjmallett	{ "writelist",  writelist,  { OPT|NTP_UINT, NO, NO, NO },
120215976Sjmallett	  { "assocID", "", "", "" },
121215976Sjmallett	  "write the system or peer variables included in the variable list" },
122215976Sjmallett	{ "readvar", readvar,    { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
123215976Sjmallett	  { "assocID", "varname1", "varname2", "varname3" },
124215976Sjmallett	  "read system or peer variables" },
125215976Sjmallett	{ "rv",      readvar,    { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
126215976Sjmallett	  { "assocID", "varname1", "varname2", "varname3" },
127215976Sjmallett	  "read system or peer variables" },
128215976Sjmallett	{ "writevar",   writevar,   { NTP_UINT, NTP_STR, NO, NO },
129215976Sjmallett	  { "assocID", "name=value,[...]", "", "" },
130215976Sjmallett	  "write system or peer variables" },
131215976Sjmallett	{ "mreadlist",  mreadlist,  { NTP_UINT, NTP_UINT, NO, NO },
132215976Sjmallett	  { "assocIDlow", "assocIDhigh", "", "" },
133215976Sjmallett	  "read the peer variables in the variable list for multiple peers" },
134215976Sjmallett	{ "mrl",    mreadlist,  { NTP_UINT, NTP_UINT, NO, NO },
135215976Sjmallett	  { "assocIDlow", "assocIDhigh", "", "" },
136215976Sjmallett	  "read the peer variables in the variable list for multiple peers" },
137215976Sjmallett	{ "mreadvar",   mreadvar,   { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
138215976Sjmallett	  { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
139215976Sjmallett	  "read peer variables from multiple peers" },
140215976Sjmallett	{ "mrv",    mreadvar,   { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
141215976Sjmallett	  { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
142215976Sjmallett	  "read peer variables from multiple peers" },
143215976Sjmallett	{ "clocklist",  clocklist,  { OPT|NTP_UINT, NO, NO, NO },
144215976Sjmallett	  { "assocID", "", "", "" },
145215976Sjmallett	  "read the clock variables included in the variable list" },
146215976Sjmallett	{ "cl",     clocklist,  { OPT|NTP_UINT, NO, NO, NO },
147215976Sjmallett	  { "assocID", "", "", "" },
148215976Sjmallett	  "read the clock variables included in the variable list" },
149215976Sjmallett	{ "clockvar",   clockvar,   { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
150215976Sjmallett	  { "assocID", "name=value[,...]", "", "" },
151215976Sjmallett	  "read clock variables" },
152215976Sjmallett	{ "cv",     clockvar,   { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
153215976Sjmallett	  { "assocID", "name=value[,...]", "", "" },
154215976Sjmallett	  "read clock variables" },
155215976Sjmallett	{ "pstats",    pstats,    { NTP_UINT, NO, NO, NO },
156215976Sjmallett	  { "assocID", "", "", "" },
157215976Sjmallett	  "show statistics for a peer" },
158215990Sjmallett	{ "peers",  peers,      { OPT|IP_VERSION, NO, NO, NO },
159215990Sjmallett	  { "-4|-6", "", "", "" },
160215976Sjmallett	  "obtain and print a list of the server's peers [IP version]" },
161215976Sjmallett	{ "apeers",  apeers,      { OPT|IP_VERSION, NO, NO, NO },
162215976Sjmallett	  { "-4|-6", "", "", "" },
163215976Sjmallett	  "obtain and print a list of the server's peers and their assocIDs [IP version]" },
164215976Sjmallett	{ "lpeers", lpeers,     { OPT|IP_VERSION, NO, NO, NO },
165215976Sjmallett	  { "-4|-6", "", "", "" },
166215976Sjmallett	  "obtain and print a list of all peers and clients [IP version]" },
167215976Sjmallett	{ "opeers", opeers,     { OPT|IP_VERSION, NO, NO, NO },
168215976Sjmallett	  { "-4|-6", "", "", "" },
169215976Sjmallett	  "print peer list the old way, with dstadr shown rather than refid [IP version]" },
170215976Sjmallett	{ "lopeers", lopeers,   { OPT|IP_VERSION, NO, NO, NO },
171215976Sjmallett	  { "-4|-6", "", "", "" },
172215976Sjmallett	  "obtain and print a list of all peers and clients showing dstadr [IP version]" },
173215976Sjmallett	{ ":config", config,   { NTP_STR, NO, NO, NO },
174215976Sjmallett	  { "<configuration command line>", "", "", "" },
175215976Sjmallett	  "send a remote configuration command to ntpd" },
176215976Sjmallett	{ "config-from-file", config_from_file, { NTP_STR, NO, NO, NO },
177215976Sjmallett	  { "<configuration filename>", "", "", "" },
178215976Sjmallett	  "configure ntpd using the configuration filename" },
179215976Sjmallett	{ "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
180215976Sjmallett	  { "tag=value", "tag=value", "tag=value", "tag=value" },
181215976Sjmallett	  "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." },
182215976Sjmallett	{ "ifstats", ifstats, { NO, NO, NO, NO },
183215976Sjmallett	  { "", "", "", "" },
184215976Sjmallett	  "show statistics for each local address ntpd is using" },
185215976Sjmallett	{ "reslist", reslist, { NO, NO, NO, NO },
186215976Sjmallett	  { "", "", "", "" },
187215976Sjmallett	  "show ntpd access control list" },
188215976Sjmallett	{ "sysinfo", sysinfo, { NO, NO, NO, NO },
189215976Sjmallett	  { "", "", "", "" },
190215976Sjmallett	  "display system summary" },
191215976Sjmallett	{ "kerninfo", kerninfo, { NO, NO, NO, NO },
192215976Sjmallett	  { "", "", "", "" },
193215976Sjmallett	  "display kernel loop and PPS statistics" },
194215976Sjmallett	{ "sysstats", sysstats, { NO, NO, NO, NO },
195215976Sjmallett	  { "", "", "", "" },
196215976Sjmallett	  "display system uptime and packet counts" },
197215976Sjmallett	{ "monstats", monstats, { NO, NO, NO, NO },
198215976Sjmallett	  { "", "", "", "" },
199215976Sjmallett	  "display monitor (mrulist) counters and limits" },
200215976Sjmallett	{ "authinfo", authinfo, { NO, NO, NO, NO },
201215976Sjmallett	  { "", "", "", "" },
202215976Sjmallett	  "display symmetric authentication counters" },
203215976Sjmallett	{ "iostats", iostats, { NO, NO, NO, NO },
204215976Sjmallett	  { "", "", "", "" },
205215976Sjmallett	  "display network input and output counters" },
206215976Sjmallett	{ "timerstats", timerstats, { NO, NO, NO, NO },
207215976Sjmallett	  { "", "", "", "" },
208215976Sjmallett	  "display interval timer counters" },
209215976Sjmallett	{ 0,		0,		{ NO, NO, NO, NO },
210215976Sjmallett	  { "-4|-6", "", "", "" }, "" }
211215976Sjmallett};
212215976Sjmallett
213215976Sjmallett
214215976Sjmallett/*
215215976Sjmallett * Variable list data space
216215976Sjmallett */
217215976Sjmallett#define MAXLINE		512	/* maximum length of a line */
218215976Sjmallett#define MAXLIST		128	/* maximum variables in list */
219215976Sjmallett#define LENHOSTNAME	256	/* host name limit */
220215976Sjmallett
221215976Sjmallett#define MRU_GOT_COUNT	0x1
222215976Sjmallett#define MRU_GOT_LAST	0x2
223215976Sjmallett#define MRU_GOT_FIRST	0x4
224215976Sjmallett#define MRU_GOT_MV	0x8
225215976Sjmallett#define MRU_GOT_RS	0x10
226215976Sjmallett#define MRU_GOT_ADDR	0x20
227215976Sjmallett#define MRU_GOT_ALL	(MRU_GOT_COUNT | MRU_GOT_LAST | MRU_GOT_FIRST \
228215976Sjmallett			 | MRU_GOT_MV | MRU_GOT_RS | MRU_GOT_ADDR)
229215976Sjmallett
230215976Sjmallett/*
231215976Sjmallett * mrulist() depends on MRUSORT_DEF and MRUSORT_RDEF being the first two
232215976Sjmallett */
233215976Sjmalletttypedef enum mru_sort_order_tag {
234215976Sjmallett	MRUSORT_DEF = 0,	/* lstint ascending */
235215976Sjmallett	MRUSORT_R_DEF,		/* lstint descending */
236215976Sjmallett	MRUSORT_AVGINT,		/* avgint ascending */
237215976Sjmallett	MRUSORT_R_AVGINT,	/* avgint descending */
238215976Sjmallett	MRUSORT_ADDR,		/* IPv4 asc. then IPv6 asc. */
239215976Sjmallett	MRUSORT_R_ADDR,		/* IPv6 desc. then IPv4 desc. */
240215976Sjmallett	MRUSORT_COUNT,		/* hit count ascending */
241215976Sjmallett	MRUSORT_R_COUNT,	/* hit count descending */
242215976Sjmallett	MRUSORT_MAX,		/* special: count of this enum */
243215976Sjmallett} mru_sort_order;
244215976Sjmallett
245215976Sjmallettconst char * const mru_sort_keywords[MRUSORT_MAX] = {
246215976Sjmallett	"lstint",		/* MRUSORT_DEF */
247215976Sjmallett	"-lstint",		/* MRUSORT_R_DEF */
248215976Sjmallett	"avgint",		/* MRUSORT_AVGINT */
249215976Sjmallett	"-avgint",		/* MRUSORT_R_AVGINT */
250215976Sjmallett	"addr",			/* MRUSORT_ADDR */
251215976Sjmallett	"-addr",		/* MRUSORT_R_ADDR */
252215976Sjmallett	"count",		/* MRUSORT_COUNT */
253215976Sjmallett	"-count",		/* MRUSORT_R_COUNT */
254215976Sjmallett};
255215976Sjmallett
256215976Sjmalletttypedef int (*qsort_cmp)(const void *, const void *);
257215976Sjmallett
258215976Sjmallett/*
259215976Sjmallett * Old CTL_PST defines for version 2.
260215976Sjmallett */
261215976Sjmallett#define OLD_CTL_PST_CONFIG		0x80
262215976Sjmallett#define OLD_CTL_PST_AUTHENABLE		0x40
263215976Sjmallett#define OLD_CTL_PST_AUTHENTIC		0x20
264215976Sjmallett#define OLD_CTL_PST_REACH		0x10
265215976Sjmallett#define OLD_CTL_PST_SANE		0x08
266215976Sjmallett#define OLD_CTL_PST_DISP		0x04
267215976Sjmallett
268215976Sjmallett#define OLD_CTL_PST_SEL_REJECT		0
269215976Sjmallett#define OLD_CTL_PST_SEL_SELCAND 	1
270215976Sjmallett#define OLD_CTL_PST_SEL_SYNCCAND	2
271215976Sjmallett#define OLD_CTL_PST_SEL_SYSPEER 	3
272215976Sjmallett
273215976Sjmallettchar flash2[] = " .+*    "; /* flash decode for version 2 */
274215976Sjmallettchar flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
275215976Sjmallett
276215976Sjmallettstruct varlist {
277215976Sjmallett	const char *name;
278215976Sjmallett	char *value;
279215976Sjmallett} g_varlist[MAXLIST] = { { 0, 0 } };
280215976Sjmallett
281215976Sjmallett/*
282215976Sjmallett * Imported from ntpq.c
283215976Sjmallett */
284215976Sjmallettextern int showhostnames;
285215976Sjmallettextern int wideremote;
286215976Sjmallettextern int rawmode;
287215976Sjmallettextern struct servent *server_entry;
288215976Sjmallettextern struct association *assoc_cache;
289215976Sjmallettextern u_char pktversion;
290215976Sjmallett
291215976Sjmalletttypedef struct mru_tag mru;
292215976Sjmallettstruct mru_tag {
293215976Sjmallett	mru *		hlink;	/* next in hash table bucket */
294215976Sjmallett	DECL_DLIST_LINK(mru, mlink);
295215976Sjmallett	int		count;
296215976Sjmallett	l_fp		last;
297215976Sjmallett	l_fp		first;
298215976Sjmallett	u_char		mode;
299215976Sjmallett	u_char		ver;
300215976Sjmallett	u_short		rs;
301215976Sjmallett	sockaddr_u	addr;
302215976Sjmallett};
303215976Sjmallett
304215976Sjmalletttypedef struct ifstats_row_tag {
305215976Sjmallett	u_int		ifnum;
306215976Sjmallett	sockaddr_u	addr;
307215976Sjmallett	sockaddr_u	bcast;
308215976Sjmallett	int		enabled;
309215976Sjmallett	u_int		flags;
310215976Sjmallett	int		mcast_count;
311215976Sjmallett	char		name[32];
312215976Sjmallett	int		peer_count;
313215976Sjmallett	int		received;
314215976Sjmallett	int		sent;
315215976Sjmallett	int		send_errors;
316215976Sjmallett	u_int		ttl;
317215976Sjmallett	u_int		uptime;
318215976Sjmallett} ifstats_row;
319215976Sjmallett
320215976Sjmalletttypedef struct reslist_row_tag {
321215976Sjmallett	u_int		idx;
322215976Sjmallett	sockaddr_u	addr;
323215976Sjmallett	sockaddr_u	mask;
324215976Sjmallett	u_long		hits;
325215976Sjmallett	char		flagstr[128];
326215976Sjmallett} reslist_row;
327215976Sjmallett
328215976Sjmalletttypedef struct var_display_collection_tag {
329215976Sjmallett	const char * const tag;		/* system variable */
330215976Sjmallett	const char * const display;	/* descriptive text */
331215976Sjmallett	u_char type;			/* NTP_STR, etc */
332215976Sjmallett	union {
333215976Sjmallett		char *		str;
334215976Sjmallett		sockaddr_u	sau;	/* NTP_ADD */
335215976Sjmallett		l_fp		lfp;	/* NTP_LFP */
336215976Sjmallett	} v;				/* retrieved value */
337215976Sjmallett} vdc;
338215976Sjmallett#if !defined(MISSING_C99_STRUCT_INIT)
339215976Sjmallett# define VDC_INIT(a, b, c) { .tag = a, .display = b, .type = c }
340215976Sjmallett#else
341215976Sjmallett# define VDC_INIT(a, b, c) { a, b, c }
342215976Sjmallett#endif
343215976Sjmallett/*
344215976Sjmallett * other local function prototypes
345215976Sjmallett */
346215976Sjmallettstatic int	mrulist_ctrl_c_hook(void);
347215976Sjmallettstatic mru *	add_mru(mru *);
348215976Sjmallettstatic int	collect_mru_list(const char *, l_fp *);
349215976Sjmallettstatic int	fetch_nonce(char *, size_t);
350215976Sjmallettstatic int	qcmp_mru_avgint(const void *, const void *);
351215976Sjmallettstatic int	qcmp_mru_r_avgint(const void *, const void *);
352215976Sjmallettstatic int	qcmp_mru_addr(const void *, const void *);
353215976Sjmallettstatic int	qcmp_mru_r_addr(const void *, const void *);
354215976Sjmallettstatic int	qcmp_mru_count(const void *, const void *);
355215976Sjmallettstatic int	qcmp_mru_r_count(const void *, const void *);
356215976Sjmallettstatic void	validate_ifnum(FILE *, u_int, int *, ifstats_row *);
357215976Sjmallettstatic void	another_ifstats_field(int *, ifstats_row *, FILE *);
358215976Sjmallettstatic void	collect_display_vdc(associd_t as, vdc *table,
359215976Sjmallett				    int decodestatus, FILE *fp);
360215976Sjmallett
361215976Sjmallett/*
362215976Sjmallett * static globals
363215976Sjmallett */
364215976Sjmallettstatic u_int	mru_count;
365215976Sjmallettstatic u_int	mru_dupes;
366215976Sjmallettvolatile int	mrulist_interrupted;
367215976Sjmallettstatic mru	mru_list;		/* listhead */
368215976Sjmallettstatic mru **	hash_table;
369215976Sjmallett
370215976Sjmallett/*
371215976Sjmallett * qsort comparison function table for mrulist().  The first two
372215976Sjmallett * entries are NULL because they are handled without qsort().
373215976Sjmallett */
374215976Sjmallettstatic const qsort_cmp mru_qcmp_table[MRUSORT_MAX] = {
375215976Sjmallett	NULL,			/* MRUSORT_DEF unused */
376215976Sjmallett	NULL,			/* MRUSORT_R_DEF unused */
377215976Sjmallett	&qcmp_mru_avgint,	/* MRUSORT_AVGINT */
378215976Sjmallett	&qcmp_mru_r_avgint,	/* MRUSORT_R_AVGINT */
379215976Sjmallett	&qcmp_mru_addr,		/* MRUSORT_ADDR */
380215976Sjmallett	&qcmp_mru_r_addr,	/* MRUSORT_R_ADDR */
381215976Sjmallett	&qcmp_mru_count,	/* MRUSORT_COUNT */
382215976Sjmallett	&qcmp_mru_r_count,	/* MRUSORT_R_COUNT */
383215976Sjmallett};
384215976Sjmallett
385215976Sjmallett/*
386215976Sjmallett * checkassocid - return the association ID, checking to see if it is valid
387215976Sjmallett */
388215976Sjmallettstatic associd_t
389215976Sjmallettcheckassocid(
390215976Sjmallett	u_int32 value
391215976Sjmallett	)
392215976Sjmallett{
393215976Sjmallett	associd_t	associd;
394215976Sjmallett	u_long		ulvalue;
395215976Sjmallett
396215976Sjmallett	associd = (associd_t)value;
397215976Sjmallett	if (0 == associd || value != associd) {
398215976Sjmallett		ulvalue = value;
399215976Sjmallett		fprintf(stderr,
400215976Sjmallett			"***Invalid association ID %lu specified\n",
401215976Sjmallett			ulvalue);
402215976Sjmallett		return 0;
403215976Sjmallett	}
404215976Sjmallett
405215976Sjmallett	return associd;
406215976Sjmallett}
407215976Sjmallett
408215976Sjmallett
409215976Sjmallett/*
410215976Sjmallett * findlistvar - Look for the named variable in a varlist.  If found,
411215976Sjmallett *		 return a pointer to it.  Otherwise, if the list has
412215976Sjmallett *		 slots available, return the pointer to the first free
413215976Sjmallett *		 slot, or NULL if it's full.
414215976Sjmallett */
415215976Sjmallettstatic struct varlist *
416215976Sjmallettfindlistvar(
417215976Sjmallett	struct varlist *list,
418215976Sjmallett	char *name
419215976Sjmallett	)
420215976Sjmallett{
421215976Sjmallett	struct varlist *vl;
422215976Sjmallett
423215976Sjmallett	for (vl = list; vl < list + MAXLIST && vl->name != NULL; vl++)
424215976Sjmallett		if (!strcmp(name, vl->name))
425215976Sjmallett			return vl;
426215976Sjmallett	if (vl < list + MAXLIST)
427215976Sjmallett		return vl;
428215976Sjmallett
429215976Sjmallett	return NULL;
430215976Sjmallett}
431215976Sjmallett
432215976Sjmallett
433215976Sjmallett/*
434215976Sjmallett * doaddvlist - add variable(s) to the variable list
435215976Sjmallett */
436215976Sjmallettstatic void
437215976Sjmallettdoaddvlist(
438215976Sjmallett	struct varlist *vlist,
439215976Sjmallett	const char *vars
440215976Sjmallett	)
441215976Sjmallett{
442215976Sjmallett	struct varlist *vl;
443215976Sjmallett	size_t len;
444215976Sjmallett	char *name;
445215976Sjmallett	char *value;
446215976Sjmallett
447215976Sjmallett	len = strlen(vars);
448215976Sjmallett	while (nextvar(&len, &vars, &name, &value)) {
449215976Sjmallett		vl = findlistvar(vlist, name);
450215976Sjmallett		if (NULL == vl) {
451215976Sjmallett			fprintf(stderr, "Variable list full\n");
452215976Sjmallett			return;
453215976Sjmallett		}
454215976Sjmallett
455215976Sjmallett		if (NULL == vl->name) {
456215976Sjmallett			vl->name = estrdup(name);
457215976Sjmallett		} else if (vl->value != NULL) {
458215976Sjmallett			free(vl->value);
459215976Sjmallett			vl->value = NULL;
460215976Sjmallett		}
461215976Sjmallett
462215976Sjmallett		if (value != NULL)
463215976Sjmallett			vl->value = estrdup(value);
464215976Sjmallett	}
465215976Sjmallett}
466215976Sjmallett
467215976Sjmallett
468215976Sjmallett/*
469215976Sjmallett * dormvlist - remove variable(s) from the variable list
470215976Sjmallett */
471215976Sjmallettstatic void
472215976Sjmallettdormvlist(
473215976Sjmallett	struct varlist *vlist,
474215976Sjmallett	const char *vars
475215976Sjmallett	)
476215976Sjmallett{
477215976Sjmallett	struct varlist *vl;
478215976Sjmallett	size_t len;
479215976Sjmallett	char *name;
480215976Sjmallett	char *value;
481215976Sjmallett
482215976Sjmallett	len = strlen(vars);
483215976Sjmallett	while (nextvar(&len, &vars, &name, &value)) {
484215976Sjmallett		vl = findlistvar(vlist, name);
485215976Sjmallett		if (vl == 0 || vl->name == 0) {
486215976Sjmallett			(void) fprintf(stderr, "Variable `%s' not found\n",
487215976Sjmallett				       name);
488215976Sjmallett		} else {
489215976Sjmallett			free((void *)(intptr_t)vl->name);
490215976Sjmallett			if (vl->value != 0)
491215976Sjmallett			    free(vl->value);
492215976Sjmallett			for ( ; (vl+1) < (g_varlist + MAXLIST)
493215976Sjmallett				      && (vl+1)->name != 0; vl++) {
494215976Sjmallett				vl->name = (vl+1)->name;
495215976Sjmallett				vl->value = (vl+1)->value;
496215976Sjmallett			}
497215976Sjmallett			vl->name = vl->value = 0;
498215976Sjmallett		}
499215976Sjmallett	}
500215976Sjmallett}
501215976Sjmallett
502215976Sjmallett
503215976Sjmallett/*
504215976Sjmallett * doclearvlist - clear a variable list
505215976Sjmallett */
506215976Sjmallettstatic void
507215976Sjmallettdoclearvlist(
508215976Sjmallett	struct varlist *vlist
509215976Sjmallett	)
510215976Sjmallett{
511215976Sjmallett	register struct varlist *vl;
512215976Sjmallett
513215976Sjmallett	for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
514215976Sjmallett		free((void *)(intptr_t)vl->name);
515215976Sjmallett		vl->name = 0;
516215976Sjmallett		if (vl->value != 0) {
517215976Sjmallett			free(vl->value);
518215976Sjmallett			vl->value = 0;
519215976Sjmallett		}
520215976Sjmallett	}
521215976Sjmallett}
522215976Sjmallett
523215976Sjmallett
524215976Sjmallett/*
525215976Sjmallett * makequerydata - form a data buffer to be included with a query
526215976Sjmallett */
527215976Sjmallettstatic void
528215976Sjmallettmakequerydata(
529215976Sjmallett	struct varlist *vlist,
530215976Sjmallett	size_t *datalen,
531215976Sjmallett	char *data
532215976Sjmallett	)
533215976Sjmallett{
534215976Sjmallett	register struct varlist *vl;
535215976Sjmallett	register char *cp, *cpend;
536215976Sjmallett	register size_t namelen, valuelen;
537215976Sjmallett	register size_t totallen;
538215976Sjmallett
539215976Sjmallett	cp = data;
540215976Sjmallett	cpend = data + *datalen;
541215976Sjmallett
542215976Sjmallett	for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
543215976Sjmallett		namelen = strlen(vl->name);
544215976Sjmallett		if (vl->value == 0)
545215976Sjmallett			valuelen = 0;
546215976Sjmallett		else
547215976Sjmallett			valuelen = strlen(vl->value);
548215976Sjmallett		totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
549215976Sjmallett		if (cp + totallen > cpend) {
550215976Sjmallett		    fprintf(stderr,
551215976Sjmallett			    "***Ignoring variables starting with `%s'\n",
552215976Sjmallett			    vl->name);
553215976Sjmallett		    break;
554215976Sjmallett		}
555215976Sjmallett
556215976Sjmallett		if (cp != data)
557215976Sjmallett			*cp++ = ',';
558215976Sjmallett		memcpy(cp, vl->name, (size_t)namelen);
559215976Sjmallett		cp += namelen;
560215976Sjmallett		if (valuelen != 0) {
561215976Sjmallett			*cp++ = '=';
562215976Sjmallett			memcpy(cp, vl->value, (size_t)valuelen);
563215976Sjmallett			cp += valuelen;
564215976Sjmallett		}
565215976Sjmallett	}
566215976Sjmallett	*datalen = (size_t)(cp - data);
567215976Sjmallett}
568215976Sjmallett
569215976Sjmallett
570215976Sjmallett/*
571215976Sjmallett * doquerylist - send a message including variables in a list
572215976Sjmallett */
573215976Sjmallettstatic int
574215976Sjmallettdoquerylist(
575215976Sjmallett	struct varlist *vlist,
576215976Sjmallett	int op,
577215976Sjmallett	associd_t associd,
578215976Sjmallett	int auth,
579215976Sjmallett	u_short *rstatus,
580215976Sjmallett	size_t *dsize,
581215976Sjmallett	const char **datap
582215976Sjmallett	)
583215976Sjmallett{
584215976Sjmallett	char data[CTL_MAX_DATA_LEN];
585215976Sjmallett	size_t datalen;
586215976Sjmallett
587215976Sjmallett	datalen = sizeof(data);
588215976Sjmallett	makequerydata(vlist, &datalen, data);
589215976Sjmallett
590215976Sjmallett	return doquery(op, associd, auth, datalen, data, rstatus, dsize,
591215976Sjmallett		       datap);
592215976Sjmallett}
593215976Sjmallett
594215976Sjmallett
595215976Sjmallett/*
596215976Sjmallett * doprintvlist - print the variables on a list
597215976Sjmallett */
598215976Sjmallettstatic void
599215976Sjmallettdoprintvlist(
600215976Sjmallett	struct varlist *vlist,
601215976Sjmallett	FILE *fp
602215976Sjmallett	)
603215976Sjmallett{
604215976Sjmallett	size_t n;
605215976Sjmallett
606215976Sjmallett	if (NULL == vlist->name) {
607215976Sjmallett		fprintf(fp, "No variables on list\n");
608215976Sjmallett		return;
609215976Sjmallett	}
610215976Sjmallett	for (n = 0; n < MAXLIST && vlist[n].name != NULL; n++) {
611215976Sjmallett		if (NULL == vlist[n].value)
612215976Sjmallett			fprintf(fp, "%s\n", vlist[n].name);
613215976Sjmallett		else
614215976Sjmallett			fprintf(fp, "%s=%s\n", vlist[n].name,
615215976Sjmallett				vlist[n].value);
616215976Sjmallett	}
617215976Sjmallett}
618215976Sjmallett
619215976Sjmallett/*
620215976Sjmallett * addvars - add variables to the variable list
621215976Sjmallett */
622215976Sjmallett/*ARGSUSED*/
623215976Sjmallettstatic void
624215976Sjmallettaddvars(
625215976Sjmallett	struct parse *pcmd,
626215976Sjmallett	FILE *fp
627215976Sjmallett	)
628215976Sjmallett{
629215976Sjmallett	doaddvlist(g_varlist, pcmd->argval[0].string);
630215976Sjmallett}
631215976Sjmallett
632215976Sjmallett
633215976Sjmallett/*
634215976Sjmallett * rmvars - remove variables from the variable list
635215976Sjmallett */
636215976Sjmallett/*ARGSUSED*/
637215976Sjmallettstatic void
638215976Sjmallettrmvars(
639215976Sjmallett	struct parse *pcmd,
640215976Sjmallett	FILE *fp
641215976Sjmallett	)
642215976Sjmallett{
643215976Sjmallett	dormvlist(g_varlist, pcmd->argval[0].string);
644215976Sjmallett}
645215976Sjmallett
646215976Sjmallett
647215976Sjmallett/*
648215976Sjmallett * clearvars - clear the variable list
649215976Sjmallett */
650215976Sjmallett/*ARGSUSED*/
651215976Sjmallettstatic void
652215976Sjmallettclearvars(
653215976Sjmallett	struct parse *pcmd,
654215976Sjmallett	FILE *fp
655215976Sjmallett	)
656215976Sjmallett{
657215976Sjmallett	doclearvlist(g_varlist);
658215976Sjmallett}
659215976Sjmallett
660215976Sjmallett
661215976Sjmallett/*
662215976Sjmallett * showvars - show variables on the variable list
663215976Sjmallett */
664215976Sjmallett/*ARGSUSED*/
665215976Sjmallettstatic void
666215976Sjmallettshowvars(
667215976Sjmallett	struct parse *pcmd,
668215976Sjmallett	FILE *fp
669215976Sjmallett	)
670215976Sjmallett{
671215976Sjmallett	doprintvlist(g_varlist, fp);
672215976Sjmallett}
673215976Sjmallett
674215976Sjmallett
675215976Sjmallett/*
676215976Sjmallett * dolist - send a request with the given list of variables
677215976Sjmallett */
678215976Sjmallettstatic int
679215976Sjmallettdolist(
680215976Sjmallett	struct varlist *vlist,
681215976Sjmallett	associd_t associd,
682215976Sjmallett	int op,
683215976Sjmallett	int type,
684215976Sjmallett	FILE *fp
685215976Sjmallett	)
686215976Sjmallett{
687215976Sjmallett	const char *datap;
688215976Sjmallett	int res;
689215976Sjmallett	size_t dsize;
690215976Sjmallett	u_short rstatus;
691215976Sjmallett	int quiet;
692215976Sjmallett
693215976Sjmallett	/*
694215976Sjmallett	 * if we're asking for specific variables don't include the
695215976Sjmallett	 * status header line in the output.
696215976Sjmallett	 */
697215976Sjmallett	if (old_rv)
698215976Sjmallett		quiet = 0;
699215976Sjmallett	else
700215976Sjmallett		quiet = (vlist->name != NULL);
701215976Sjmallett
702215976Sjmallett	res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
703215976Sjmallett
704215976Sjmallett	if (res != 0)
705215976Sjmallett		return 0;
706215976Sjmallett
707215976Sjmallett	if (numhosts > 1)
708215976Sjmallett		fprintf(fp, "server=%s ", currenthost);
709215976Sjmallett	if (dsize == 0) {
710215976Sjmallett		if (associd == 0)
711215976Sjmallett			fprintf(fp, "No system%s variables returned\n",
712215976Sjmallett				(type == TYPE_CLOCK) ? " clock" : "");
713215976Sjmallett		else
714215976Sjmallett			fprintf(fp,
715215976Sjmallett				"No information returned for%s association %u\n",
716215976Sjmallett				(type == TYPE_CLOCK) ? " clock" : "",
717215976Sjmallett				associd);
718215976Sjmallett		return 1;
719215976Sjmallett	}
720215976Sjmallett
721215976Sjmallett	if (!quiet)
722215976Sjmallett		fprintf(fp, "associd=%u ", associd);
723215976Sjmallett	printvars(dsize, datap, (int)rstatus, type, quiet, fp);
724215976Sjmallett	return 1;
725215976Sjmallett}
726215976Sjmallett
727215976Sjmallett
728215976Sjmallett/*
729215976Sjmallett * readlist - send a read variables request with the variables on the list
730215976Sjmallett */
731215976Sjmallettstatic void
732215976Sjmallettreadlist(
733215976Sjmallett	struct parse *pcmd,
734215976Sjmallett	FILE *fp
735215976Sjmallett	)
736215976Sjmallett{
737215976Sjmallett	associd_t	associd;
738215976Sjmallett	int		type;
739215976Sjmallett
740215976Sjmallett	if (pcmd->nargs == 0) {
741215976Sjmallett		associd = 0;
742215976Sjmallett	} else {
743215976Sjmallett	  /* HMS: I think we want the u_int32 target here, not the u_long */
744215976Sjmallett		if (pcmd->argval[0].uval == 0)
745215976Sjmallett			associd = 0;
746215976Sjmallett		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
747215976Sjmallett			return;
748215976Sjmallett	}
749215976Sjmallett
750215976Sjmallett	type = (0 == associd)
751215976Sjmallett		   ? TYPE_SYS
752215976Sjmallett		   : TYPE_PEER;
753215976Sjmallett	dolist(g_varlist, associd, CTL_OP_READVAR, type, fp);
754215976Sjmallett}
755215976Sjmallett
756215976Sjmallett
757215976Sjmallett/*
758215976Sjmallett * writelist - send a write variables request with the variables on the list
759215976Sjmallett */
760215976Sjmallettstatic void
761215976Sjmallettwritelist(
762215976Sjmallett	struct parse *pcmd,
763215976Sjmallett	FILE *fp
764215976Sjmallett	)
765215976Sjmallett{
766215976Sjmallett	const char *datap;
767215976Sjmallett	int res;
768215976Sjmallett	associd_t associd;
769215976Sjmallett	size_t dsize;
770215976Sjmallett	u_short rstatus;
771215976Sjmallett
772215976Sjmallett	if (pcmd->nargs == 0) {
773215976Sjmallett		associd = 0;
774215976Sjmallett	} else {
775215976Sjmallett		/* HMS: Do we really want uval here? */
776215976Sjmallett		if (pcmd->argval[0].uval == 0)
777215976Sjmallett			associd = 0;
778215976Sjmallett		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
779215976Sjmallett			return;
780215976Sjmallett	}
781215976Sjmallett
782215976Sjmallett	res = doquerylist(g_varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
783215976Sjmallett			  &dsize, &datap);
784215976Sjmallett
785215976Sjmallett	if (res != 0)
786215976Sjmallett		return;
787215976Sjmallett
788215976Sjmallett	if (numhosts > 1)
789215976Sjmallett		(void) fprintf(fp, "server=%s ", currenthost);
790215976Sjmallett	if (dsize == 0)
791215976Sjmallett		(void) fprintf(fp, "done! (no data returned)\n");
792215976Sjmallett	else {
793215976Sjmallett		(void) fprintf(fp,"associd=%u ", associd);
794215976Sjmallett		printvars(dsize, datap, (int)rstatus,
795215976Sjmallett			  (associd != 0) ? TYPE_PEER : TYPE_SYS, 0, fp);
796215976Sjmallett	}
797215976Sjmallett	return;
798215976Sjmallett}
799215976Sjmallett
800215976Sjmallett
801215976Sjmallett/*
802215976Sjmallett * readvar - send a read variables request with the specified variables
803215976Sjmallett */
804215976Sjmallettstatic void
805215976Sjmallettreadvar(
806215976Sjmallett	struct parse *pcmd,
807215976Sjmallett	FILE *fp
808215976Sjmallett	)
809215976Sjmallett{
810215976Sjmallett	associd_t	associd;
811215976Sjmallett	size_t		tmpcount;
812215976Sjmallett	size_t		u;
813215976Sjmallett	int		type;
814215976Sjmallett	struct varlist	tmplist[MAXLIST];
815215976Sjmallett
816215976Sjmallett
817215976Sjmallett	/* HMS: uval? */
818215976Sjmallett	if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
819215976Sjmallett		associd = 0;
820215976Sjmallett	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
821215976Sjmallett		return;
822215976Sjmallett
823215976Sjmallett	ZERO(tmplist);
824215976Sjmallett	if (pcmd->nargs > 1) {
825215976Sjmallett		tmpcount = pcmd->nargs - 1;
826215976Sjmallett		for (u = 0; u < tmpcount; u++)
827215976Sjmallett			doaddvlist(tmplist, pcmd->argval[1 + u].string);
828215976Sjmallett	}
829215976Sjmallett
830215976Sjmallett	type = (0 == associd)
831215976Sjmallett		   ? TYPE_SYS
832215976Sjmallett		   : TYPE_PEER;
833215976Sjmallett	dolist(tmplist, associd, CTL_OP_READVAR, type, fp);
834215976Sjmallett
835215976Sjmallett	doclearvlist(tmplist);
836215976Sjmallett}
837215976Sjmallett
838215976Sjmallett
839215976Sjmallett/*
840215976Sjmallett * writevar - send a write variables request with the specified variables
841215976Sjmallett */
842215976Sjmallettstatic void
843215976Sjmallettwritevar(
844215976Sjmallett	struct parse *pcmd,
845215976Sjmallett	FILE *fp
846215976Sjmallett	)
847215976Sjmallett{
848215976Sjmallett	const char *datap;
849215976Sjmallett	int res;
850215976Sjmallett	associd_t associd;
851215976Sjmallett	int type;
852215976Sjmallett	size_t dsize;
853215976Sjmallett	u_short rstatus;
854215976Sjmallett	struct varlist tmplist[MAXLIST];
855215976Sjmallett
856215976Sjmallett	/* HMS: uval? */
857215976Sjmallett	if (pcmd->argval[0].uval == 0)
858215976Sjmallett		associd = 0;
859215976Sjmallett	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
860215976Sjmallett		return;
861215976Sjmallett
862215976Sjmallett	ZERO(tmplist);
863215976Sjmallett	doaddvlist(tmplist, pcmd->argval[1].string);
864215976Sjmallett
865215976Sjmallett	res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
866215976Sjmallett			  &dsize, &datap);
867215976Sjmallett
868215976Sjmallett	doclearvlist(tmplist);
869215976Sjmallett
870215976Sjmallett	if (res != 0)
871215976Sjmallett		return;
872215976Sjmallett
873215976Sjmallett	if (numhosts > 1)
874215976Sjmallett		fprintf(fp, "server=%s ", currenthost);
875215976Sjmallett	if (dsize == 0)
876215976Sjmallett		fprintf(fp, "done! (no data returned)\n");
877215976Sjmallett	else {
878215976Sjmallett		fprintf(fp,"associd=%u ", associd);
879215976Sjmallett		type = (0 == associd)
880215976Sjmallett			   ? TYPE_SYS
881215976Sjmallett			   : TYPE_PEER;
882215976Sjmallett		printvars(dsize, datap, (int)rstatus, type, 0, fp);
883215976Sjmallett	}
884215976Sjmallett	return;
885215976Sjmallett}
886215976Sjmallett
887215976Sjmallett
888215976Sjmallett/*
889215976Sjmallett * clocklist - send a clock variables request with the variables on the list
890215976Sjmallett */
891215976Sjmallettstatic void
892215976Sjmallettclocklist(
893215976Sjmallett	struct parse *pcmd,
894215976Sjmallett	FILE *fp
895215976Sjmallett	)
896215976Sjmallett{
897215976Sjmallett	associd_t associd;
898215976Sjmallett
899215976Sjmallett	/* HMS: uval? */
900215976Sjmallett	if (pcmd->nargs == 0) {
901215976Sjmallett		associd = 0;
902215976Sjmallett	} else {
903215976Sjmallett		if (pcmd->argval[0].uval == 0)
904215976Sjmallett			associd = 0;
905215976Sjmallett		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
906215976Sjmallett			return;
907215976Sjmallett	}
908215976Sjmallett
909215976Sjmallett	dolist(g_varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
910215976Sjmallett}
911215976Sjmallett
912215976Sjmallett
913215976Sjmallett/*
914215976Sjmallett * clockvar - send a clock variables request with the specified variables
915215976Sjmallett */
916215976Sjmallettstatic void
917215976Sjmallettclockvar(
918215976Sjmallett	struct parse *pcmd,
919215976Sjmallett	FILE *fp
920215976Sjmallett	)
921215976Sjmallett{
922215976Sjmallett	associd_t associd;
923215976Sjmallett	struct varlist tmplist[MAXLIST];
924215976Sjmallett
925215976Sjmallett	/* HMS: uval? */
926215976Sjmallett	if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
927215976Sjmallett		associd = 0;
928215976Sjmallett	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
929215976Sjmallett		return;
930215976Sjmallett
931215976Sjmallett	ZERO(tmplist);
932215976Sjmallett	if (pcmd->nargs >= 2)
933215976Sjmallett		doaddvlist(tmplist, pcmd->argval[1].string);
934215976Sjmallett
935215976Sjmallett	dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
936215976Sjmallett
937215976Sjmallett	doclearvlist(tmplist);
938215976Sjmallett}
939215976Sjmallett
940215976Sjmallett
941215976Sjmallett/*
942215976Sjmallett * findassidrange - verify a range of association ID's
943215976Sjmallett */
944215976Sjmallettstatic int
945215976Sjmallettfindassidrange(
946215976Sjmallett	u_int32	assid1,
947215976Sjmallett	u_int32	assid2,
948215976Sjmallett	int *	from,
949215976Sjmallett	int *	to,
950215976Sjmallett	FILE *	fp
951215976Sjmallett	)
952215976Sjmallett{
953215976Sjmallett	associd_t	assids[2];
954215976Sjmallett	int		ind[COUNTOF(assids)];
955215976Sjmallett	u_int		i;
956215976Sjmallett	size_t		a;
957215976Sjmallett
958215976Sjmallett
959215976Sjmallett	if (0 == numassoc)
960215976Sjmallett		dogetassoc(fp);
961215976Sjmallett
962215976Sjmallett	assids[0] = checkassocid(assid1);
963215976Sjmallett	if (0 == assids[0])
964215976Sjmallett		return 0;
965215976Sjmallett	assids[1] = checkassocid(assid2);
966215976Sjmallett	if (0 == assids[1])
967215976Sjmallett		return 0;
968215976Sjmallett
969215976Sjmallett	for (a = 0; a < COUNTOF(assids); a++) {
970215976Sjmallett		ind[a] = -1;
971215976Sjmallett		for (i = 0; i < numassoc; i++)
972215976Sjmallett			if (assoc_cache[i].assid == assids[a])
973215976Sjmallett				ind[a] = i;
974215976Sjmallett	}
975215976Sjmallett	for (a = 0; a < COUNTOF(assids); a++)
976215976Sjmallett		if (-1 == ind[a]) {
977215976Sjmallett			fprintf(stderr,
978215976Sjmallett				"***Association ID %u not found in list\n",
979215976Sjmallett				assids[a]);
980215976Sjmallett			return 0;
981215976Sjmallett		}
982215976Sjmallett
983215976Sjmallett	if (ind[0] < ind[1]) {
984215976Sjmallett		*from = ind[0];
985215976Sjmallett		*to = ind[1];
986215976Sjmallett	} else {
987215976Sjmallett		*to = ind[0];
988215976Sjmallett		*from = ind[1];
989215976Sjmallett	}
990215976Sjmallett	return 1;
991215976Sjmallett}
992215976Sjmallett
993215976Sjmallett
994215976Sjmallett
995215976Sjmallett/*
996215976Sjmallett * mreadlist - send a read variables request for multiple associations
997215976Sjmallett */
998215976Sjmallettstatic void
999215976Sjmallettmreadlist(
1000215976Sjmallett	struct parse *pcmd,
1001215976Sjmallett	FILE *fp
1002215976Sjmallett	)
1003215976Sjmallett{
1004215976Sjmallett	int i;
1005215976Sjmallett	int from;
1006215976Sjmallett	int to;
1007215976Sjmallett
1008215976Sjmallett	if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1009215976Sjmallett			    &from, &to, fp))
1010215976Sjmallett		return;
1011215976Sjmallett
1012215976Sjmallett	for (i = from; i <= to; i++) {
1013215976Sjmallett		if (i != from)
1014215976Sjmallett			fprintf(fp, "\n");
1015215976Sjmallett		if (!dolist(g_varlist, assoc_cache[i].assid,
1016215976Sjmallett			    CTL_OP_READVAR, TYPE_PEER, fp))
1017215976Sjmallett			return;
1018215976Sjmallett	}
1019215976Sjmallett	return;
1020215976Sjmallett}
1021215976Sjmallett
1022215976Sjmallett
1023215976Sjmallett/*
1024215976Sjmallett * mreadvar - send a read variables request for multiple associations
1025215976Sjmallett */
1026215976Sjmallettstatic void
1027215976Sjmallettmreadvar(
1028215976Sjmallett	struct parse *pcmd,
1029215976Sjmallett	FILE *fp
1030215976Sjmallett	)
1031215976Sjmallett{
1032215976Sjmallett	int i;
1033215976Sjmallett	int from;
1034215976Sjmallett	int to;
1035215976Sjmallett	struct varlist tmplist[MAXLIST];
1036215976Sjmallett	struct varlist *pvars;
1037215976Sjmallett
1038215976Sjmallett	if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1039215976Sjmallett				&from, &to, fp))
1040215976Sjmallett		return;
1041215976Sjmallett
1042215976Sjmallett	ZERO(tmplist);
1043215976Sjmallett	if (pcmd->nargs >= 3) {
1044215976Sjmallett		doaddvlist(tmplist, pcmd->argval[2].string);
1045215976Sjmallett		pvars = tmplist;
1046215976Sjmallett	} else {
1047215976Sjmallett		pvars = g_varlist;
1048215976Sjmallett	}
1049215976Sjmallett
1050215976Sjmallett	for (i = from; i <= to; i++) {
1051215976Sjmallett		if (!dolist(pvars, assoc_cache[i].assid, CTL_OP_READVAR,
1052215976Sjmallett			    TYPE_PEER, fp))
1053215976Sjmallett			break;
1054215976Sjmallett	}
1055215976Sjmallett
1056215976Sjmallett	if (pvars == tmplist)
1057215976Sjmallett		doclearvlist(tmplist);
1058215976Sjmallett
1059215976Sjmallett	return;
1060215976Sjmallett}
1061215976Sjmallett
1062215976Sjmallett
1063215976Sjmallett/*
1064215976Sjmallett * dogetassoc - query the host for its list of associations
1065215976Sjmallett */
1066215976Sjmallettint
1067215976Sjmallettdogetassoc(
1068215976Sjmallett	FILE *fp
1069215976Sjmallett	)
1070215976Sjmallett{
1071215976Sjmallett	const char *datap;
1072215976Sjmallett	const u_short *pus;
1073215976Sjmallett	int res;
1074215976Sjmallett	size_t dsize;
1075215976Sjmallett	u_short rstatus;
1076215976Sjmallett
1077215976Sjmallett	res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
1078215976Sjmallett			  &dsize, &datap);
1079215976Sjmallett
1080215976Sjmallett	if (res != 0)
1081215976Sjmallett		return 0;
1082215976Sjmallett
1083215976Sjmallett	if (dsize == 0) {
1084215976Sjmallett		if (numhosts > 1)
1085215976Sjmallett			fprintf(fp, "server=%s ", currenthost);
1086215976Sjmallett		fprintf(fp, "No association ID's returned\n");
1087215976Sjmallett		return 0;
1088215976Sjmallett	}
1089215976Sjmallett
1090215976Sjmallett	if (dsize & 0x3) {
1091215976Sjmallett		if (numhosts > 1)
1092215976Sjmallett			fprintf(stderr, "server=%s ", currenthost);
1093215976Sjmallett		fprintf(stderr,
1094215976Sjmallett			"***Server returned %zu octets, should be multiple of 4\n",
1095215976Sjmallett			dsize);
1096215976Sjmallett		return 0;
1097215976Sjmallett	}
1098215976Sjmallett
1099215976Sjmallett	numassoc = 0;
1100215976Sjmallett
1101215976Sjmallett	while (dsize > 0) {
1102215976Sjmallett		if (numassoc >= assoc_cache_slots) {
1103215976Sjmallett			grow_assoc_cache();
1104215976Sjmallett		}
1105215976Sjmallett		pus = (const void *)datap;
1106215976Sjmallett		assoc_cache[numassoc].assid = ntohs(*pus);
1107215976Sjmallett		datap += sizeof(*pus);
1108215976Sjmallett		pus = (const void *)datap;
1109215976Sjmallett		assoc_cache[numassoc].status = ntohs(*pus);
1110215976Sjmallett		datap += sizeof(*pus);
1111215976Sjmallett		dsize -= 2 * sizeof(*pus);
1112215976Sjmallett		if (debug) {
1113215976Sjmallett			fprintf(stderr, "[%u] ",
1114215976Sjmallett				assoc_cache[numassoc].assid);
1115215976Sjmallett		}
1116215976Sjmallett		numassoc++;
1117215976Sjmallett	}
1118215976Sjmallett	if (debug) {
1119215976Sjmallett		fprintf(stderr, "\n%d associations total\n", numassoc);
1120215976Sjmallett	}
1121215976Sjmallett	sortassoc();
1122215976Sjmallett	return 1;
1123215976Sjmallett}
1124215976Sjmallett
1125215976Sjmallett
1126215976Sjmallett/*
1127215976Sjmallett * printassoc - print the current list of associations
1128215976Sjmallett */
1129215976Sjmallettstatic void
1130215976Sjmallettprintassoc(
1131215976Sjmallett	int showall,
1132215976Sjmallett	FILE *fp
1133215976Sjmallett	)
1134215976Sjmallett{
1135215976Sjmallett	register char *bp;
1136215976Sjmallett	u_int i;
1137215976Sjmallett	u_char statval;
1138215976Sjmallett	int event;
1139215976Sjmallett	u_long event_count;
1140215976Sjmallett	const char *conf;
1141215976Sjmallett	const char *reach;
1142215976Sjmallett	const char *auth;
1143215976Sjmallett	const char *condition = "";
1144215976Sjmallett	const char *last_event;
1145215976Sjmallett	char buf[128];
1146215976Sjmallett
1147215976Sjmallett	if (numassoc == 0) {
1148215976Sjmallett		(void) fprintf(fp, "No association ID's in list\n");
1149215976Sjmallett		return;
1150215976Sjmallett	}
1151215976Sjmallett
1152215976Sjmallett	/*
1153215976Sjmallett	 * Output a header
1154215976Sjmallett	 */
1155215976Sjmallett	(void) fprintf(fp,
1156215976Sjmallett			   "\nind assid status  conf reach auth condition  last_event cnt\n");
1157215976Sjmallett	(void) fprintf(fp,
1158215976Sjmallett			   "===========================================================\n");
1159215976Sjmallett	for (i = 0; i < numassoc; i++) {
1160215976Sjmallett		statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status);
1161215976Sjmallett		if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
1162215976Sjmallett			continue;
1163215976Sjmallett		event = CTL_PEER_EVENT(assoc_cache[i].status);
1164215976Sjmallett		event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
1165215976Sjmallett		if (statval & CTL_PST_CONFIG)
1166215976Sjmallett			conf = "yes";
1167215976Sjmallett		else
1168215976Sjmallett			conf = "no";
1169215976Sjmallett		if (statval & CTL_PST_BCAST) {
1170215976Sjmallett			reach = "none";
1171215976Sjmallett			if (statval & CTL_PST_AUTHENABLE)
1172215976Sjmallett				auth = "yes";
1173215976Sjmallett			else
1174215976Sjmallett				auth = "none";
1175215976Sjmallett		} else {
1176215976Sjmallett			if (statval & CTL_PST_REACH)
1177215976Sjmallett				reach = "yes";
1178215976Sjmallett			else
1179215976Sjmallett				reach = "no";
1180215976Sjmallett			if (statval & CTL_PST_AUTHENABLE) {
1181215976Sjmallett				if (statval & CTL_PST_AUTHENTIC)
1182215976Sjmallett					auth = "ok ";
1183215976Sjmallett				else
1184215976Sjmallett					auth = "bad";
1185215976Sjmallett			} else {
1186215976Sjmallett				auth = "none";
1187215976Sjmallett			}
1188215976Sjmallett		}
1189215976Sjmallett		if (pktversion > NTP_OLDVERSION) {
1190215976Sjmallett			switch (statval & 0x7) {
1191215976Sjmallett
1192215976Sjmallett			case CTL_PST_SEL_REJECT:
1193215976Sjmallett				condition = "reject";
1194215976Sjmallett				break;
1195215976Sjmallett
1196215976Sjmallett			case CTL_PST_SEL_SANE:
1197215976Sjmallett				condition = "falsetick";
1198215976Sjmallett				break;
1199215976Sjmallett
1200215976Sjmallett			case CTL_PST_SEL_CORRECT:
1201215976Sjmallett				condition = "excess";
1202215976Sjmallett				break;
1203215976Sjmallett
1204215976Sjmallett			case CTL_PST_SEL_SELCAND:
1205215976Sjmallett				condition = "outlier";
1206215976Sjmallett				break;
1207215976Sjmallett
1208215976Sjmallett			case CTL_PST_SEL_SYNCCAND:
1209215976Sjmallett				condition = "candidate";
1210215976Sjmallett				break;
1211215976Sjmallett
1212215976Sjmallett			case CTL_PST_SEL_EXCESS:
1213215976Sjmallett				condition = "backup";
1214215976Sjmallett				break;
1215215976Sjmallett
1216215976Sjmallett			case CTL_PST_SEL_SYSPEER:
1217215976Sjmallett				condition = "sys.peer";
1218215976Sjmallett				break;
1219215976Sjmallett
1220215976Sjmallett			case CTL_PST_SEL_PPS:
1221215976Sjmallett				condition = "pps.peer";
1222215976Sjmallett				break;
1223215976Sjmallett			}
1224215976Sjmallett		} else {
1225215976Sjmallett			switch (statval & 0x3) {
1226215976Sjmallett
1227215976Sjmallett			case OLD_CTL_PST_SEL_REJECT:
1228215976Sjmallett				if (!(statval & OLD_CTL_PST_SANE))
1229215976Sjmallett					condition = "insane";
1230215976Sjmallett				else if (!(statval & OLD_CTL_PST_DISP))
1231215976Sjmallett					condition = "hi_disp";
1232215976Sjmallett				else
1233215976Sjmallett					condition = "";
1234215976Sjmallett				break;
1235215976Sjmallett
1236215976Sjmallett			case OLD_CTL_PST_SEL_SELCAND:
1237215976Sjmallett				condition = "sel_cand";
1238215976Sjmallett				break;
1239215976Sjmallett
1240215976Sjmallett			case OLD_CTL_PST_SEL_SYNCCAND:
1241215976Sjmallett				condition = "sync_cand";
1242215976Sjmallett				break;
1243215976Sjmallett
1244215976Sjmallett			case OLD_CTL_PST_SEL_SYSPEER:
1245215976Sjmallett				condition = "sys_peer";
1246215976Sjmallett				break;
1247215976Sjmallett			}
1248215976Sjmallett		}
1249215976Sjmallett		switch (PEER_EVENT|event) {
1250215976Sjmallett
1251215976Sjmallett		case PEVNT_MOBIL:
1252215976Sjmallett			last_event = "mobilize";
1253215976Sjmallett			break;
1254215976Sjmallett
1255215976Sjmallett		case PEVNT_DEMOBIL:
1256215976Sjmallett			last_event = "demobilize";
1257215976Sjmallett			break;
1258215976Sjmallett
1259215976Sjmallett		case PEVNT_REACH:
1260215976Sjmallett			last_event = "reachable";
1261215976Sjmallett			break;
1262215976Sjmallett
1263215976Sjmallett		case PEVNT_UNREACH:
1264215976Sjmallett			last_event = "unreachable";
1265215976Sjmallett			break;
1266215976Sjmallett
1267215976Sjmallett		case PEVNT_RESTART:
1268215976Sjmallett			last_event = "restart";
1269215976Sjmallett			break;
1270215976Sjmallett
1271215976Sjmallett		case PEVNT_REPLY:
1272215976Sjmallett			last_event = "no_reply";
1273215976Sjmallett			break;
1274215976Sjmallett
1275215976Sjmallett		case PEVNT_RATE:
1276215976Sjmallett			last_event = "rate_exceeded";
1277215976Sjmallett			break;
1278215976Sjmallett
1279215976Sjmallett		case PEVNT_DENY:
1280215976Sjmallett			last_event = "access_denied";
1281215976Sjmallett			break;
1282215976Sjmallett
1283215976Sjmallett		case PEVNT_ARMED:
1284215976Sjmallett			last_event = "leap_armed";
1285215976Sjmallett			break;
1286215976Sjmallett
1287215976Sjmallett		case PEVNT_NEWPEER:
1288215976Sjmallett			last_event = "sys_peer";
1289215976Sjmallett			break;
1290215976Sjmallett
1291215976Sjmallett		case PEVNT_CLOCK:
1292215976Sjmallett			last_event = "clock_alarm";
1293215976Sjmallett			break;
1294215976Sjmallett
1295215976Sjmallett		default:
1296215976Sjmallett			last_event = "";
1297215976Sjmallett			break;
1298215976Sjmallett		}
1299215976Sjmallett		snprintf(buf, sizeof(buf),
1300215976Sjmallett			 "%3d %5u  %04x   %3.3s  %4s  %4.4s %9.9s %11s %2lu",
1301215976Sjmallett			 i + 1, assoc_cache[i].assid,
1302215976Sjmallett			 assoc_cache[i].status, conf, reach, auth,
1303215976Sjmallett			 condition, last_event, event_count);
1304215976Sjmallett		bp = buf + strlen(buf);
1305215976Sjmallett		while (bp > buf && ' ' == bp[-1])
1306215976Sjmallett			--bp;
1307215976Sjmallett		bp[0] = '\0';
1308215976Sjmallett		fprintf(fp, "%s\n", buf);
1309215976Sjmallett	}
1310215976Sjmallett}
1311215976Sjmallett
1312215976Sjmallett
1313215976Sjmallett/*
1314215976Sjmallett * associations - get, record and print a list of associations
1315215976Sjmallett */
1316215976Sjmallett/*ARGSUSED*/
1317215976Sjmallettstatic void
1318215976Sjmallettassociations(
1319215976Sjmallett	struct parse *pcmd,
1320215976Sjmallett	FILE *fp
1321215976Sjmallett	)
1322215976Sjmallett{
1323215976Sjmallett	if (dogetassoc(fp))
1324215976Sjmallett		printassoc(0, fp);
1325215976Sjmallett}
1326215976Sjmallett
1327215976Sjmallett
1328215976Sjmallett/*
1329215976Sjmallett * lassociations - get, record and print a long list of associations
1330215976Sjmallett */
1331215976Sjmallett/*ARGSUSED*/
1332215976Sjmallettstatic void
1333215976Sjmallettlassociations(
1334215976Sjmallett	struct parse *pcmd,
1335215976Sjmallett	FILE *fp
1336215976Sjmallett	)
1337215976Sjmallett{
1338215976Sjmallett	if (dogetassoc(fp))
1339215976Sjmallett		printassoc(1, fp);
1340215976Sjmallett}
1341215976Sjmallett
1342215976Sjmallett
1343215976Sjmallett/*
1344215976Sjmallett * passociations - print the association list
1345215976Sjmallett */
1346215976Sjmallett/*ARGSUSED*/
1347215976Sjmallettstatic void
1348215976Sjmallettpassociations(
1349215976Sjmallett	struct parse *pcmd,
1350215976Sjmallett	FILE *fp
1351215976Sjmallett	)
1352215976Sjmallett{
1353215976Sjmallett	printassoc(0, fp);
1354215976Sjmallett}
1355215976Sjmallett
1356215976Sjmallett
1357215976Sjmallett/*
1358215976Sjmallett * lpassociations - print the long association list
1359215976Sjmallett */
1360215976Sjmallett/*ARGSUSED*/
1361215976Sjmallettstatic void
1362215976Sjmallettlpassociations(
1363215976Sjmallett	struct parse *pcmd,
1364215976Sjmallett	FILE *fp
1365215976Sjmallett	)
1366215976Sjmallett{
1367215976Sjmallett	printassoc(1, fp);
1368215976Sjmallett}
1369215976Sjmallett
1370215976Sjmallett
1371215976Sjmallett/*
1372215976Sjmallett *  saveconfig - dump ntp server configuration to server file
1373215976Sjmallett */
1374215976Sjmallettstatic void
1375215976Sjmallettsaveconfig(
1376215976Sjmallett	struct parse *pcmd,
1377215976Sjmallett	FILE *fp
1378215976Sjmallett	)
1379215976Sjmallett{
1380215976Sjmallett	const char *datap;
1381215976Sjmallett	int res;
1382215976Sjmallett	size_t dsize;
1383215976Sjmallett	u_short rstatus;
1384215976Sjmallett
1385215976Sjmallett	if (0 == pcmd->nargs)
1386215976Sjmallett		return;
1387215976Sjmallett
1388215976Sjmallett	res = doquery(CTL_OP_SAVECONFIG, 0, 1,
1389215976Sjmallett		      strlen(pcmd->argval[0].string),
1390215976Sjmallett		      pcmd->argval[0].string, &rstatus, &dsize,
1391215976Sjmallett		      &datap);
1392215976Sjmallett
1393215976Sjmallett	if (res != 0)
1394215976Sjmallett		return;
1395215976Sjmallett
1396215976Sjmallett	if (0 == dsize)
1397215976Sjmallett		fprintf(fp, "(no response message, curiously)");
1398215976Sjmallett	else
1399215976Sjmallett		fprintf(fp, "%.*s", (int)dsize, datap); /* cast is wobbly */
1400215976Sjmallett}
1401215976Sjmallett
1402215976Sjmallett
1403215976Sjmallett#ifdef	UNUSED
1404215976Sjmallett/*
1405215976Sjmallett * radiostatus - print the radio status returned by the server
1406215976Sjmallett */
1407215976Sjmallett/*ARGSUSED*/
1408215976Sjmallettstatic void
1409215976Sjmallettradiostatus(
1410215976Sjmallett	struct parse *pcmd,
1411215976Sjmallett	FILE *fp
1412215976Sjmallett	)
1413215976Sjmallett{
1414215976Sjmallett	char *datap;
1415215976Sjmallett	int res;
1416215976Sjmallett	int dsize;
1417215976Sjmallett	u_short rstatus;
1418215976Sjmallett
1419215976Sjmallett	res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
1420215976Sjmallett			  &dsize, &datap);
1421215976Sjmallett
1422215976Sjmallett	if (res != 0)
1423215976Sjmallett		return;
1424215976Sjmallett
1425215976Sjmallett	if (numhosts > 1)
1426215976Sjmallett		(void) fprintf(fp, "server=%s ", currenthost);
1427215976Sjmallett	if (dsize == 0) {
1428215976Sjmallett		(void) fprintf(fp, "No radio status string returned\n");
1429215976Sjmallett		return;
1430215976Sjmallett	}
1431215976Sjmallett
1432215976Sjmallett	asciize(dsize, datap, fp);
1433215976Sjmallett}
1434215976Sjmallett#endif	/* UNUSED */
1435215976Sjmallett
1436215976Sjmallett/*
1437215976Sjmallett * when - print how long its been since his last packet arrived
1438215976Sjmallett */
1439215976Sjmallettstatic long
1440215976Sjmallettwhen(
1441215976Sjmallett	l_fp *ts,
1442215976Sjmallett	l_fp *rec,
1443215976Sjmallett	l_fp *reftime
1444215976Sjmallett	)
1445215976Sjmallett{
1446215976Sjmallett	l_fp *lasttime;
1447215976Sjmallett
1448215976Sjmallett	if (rec->l_ui != 0)
1449215976Sjmallett		lasttime = rec;
1450215976Sjmallett	else if (reftime->l_ui != 0)
1451215976Sjmallett		lasttime = reftime;
1452215976Sjmallett	else
1453215976Sjmallett		return 0;
1454215976Sjmallett
1455215976Sjmallett	return (ts->l_ui - lasttime->l_ui);
1456215976Sjmallett}
1457215976Sjmallett
1458215976Sjmallett
1459215976Sjmallett/*
1460215976Sjmallett * Pretty-print an interval into the given buffer, in a human-friendly format.
1461215976Sjmallett */
1462215976Sjmallettstatic char *
1463215976Sjmallettprettyinterval(
1464215976Sjmallett	char *buf,
1465215976Sjmallett	size_t cb,
1466215976Sjmallett	long diff
1467215976Sjmallett	)
1468215976Sjmallett{
1469215976Sjmallett	if (diff <= 0) {
1470215976Sjmallett		buf[0] = '-';
1471215976Sjmallett		buf[1] = 0;
1472215976Sjmallett		return buf;
1473215976Sjmallett	}
1474215976Sjmallett
1475215976Sjmallett	if (diff <= 2048) {
1476215976Sjmallett		snprintf(buf, cb, "%ld", diff);
1477215976Sjmallett		return buf;
1478215976Sjmallett	}
1479215976Sjmallett
1480215976Sjmallett	diff = (diff + 29) / 60;
1481215976Sjmallett	if (diff <= 300) {
1482215976Sjmallett		snprintf(buf, cb, "%ldm", diff);
1483215976Sjmallett		return buf;
1484215976Sjmallett	}
1485215976Sjmallett
1486215976Sjmallett	diff = (diff + 29) / 60;
1487215976Sjmallett	if (diff <= 96) {
1488215976Sjmallett		snprintf(buf, cb, "%ldh", diff);
1489215976Sjmallett		return buf;
1490215976Sjmallett	}
1491215976Sjmallett
1492215976Sjmallett	diff = (diff + 11) / 24;
1493215976Sjmallett	snprintf(buf, cb, "%ldd", diff);
1494215976Sjmallett	return buf;
1495215976Sjmallett}
1496215976Sjmallett
1497215976Sjmallettstatic char
1498215976Sjmallettdecodeaddrtype(
1499215976Sjmallett	sockaddr_u *sock
1500215976Sjmallett	)
1501215976Sjmallett{
1502215976Sjmallett	char ch = '-';
1503215976Sjmallett	u_int32 dummy;
1504215976Sjmallett
1505215976Sjmallett	switch(AF(sock)) {
1506215976Sjmallett	case AF_INET:
1507215976Sjmallett		dummy = SRCADR(sock);
1508215976Sjmallett		ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' :
1509215976Sjmallett			((dummy&0x000000ff)==0x000000ff) ? 'b' :
1510215976Sjmallett			((dummy&0xffffffff)==0x7f000001) ? 'l' :
1511215976Sjmallett			((dummy&0xffffffe0)==0x00000000) ? '-' :
1512215976Sjmallett			'u');
1513215976Sjmallett		break;
1514215976Sjmallett	case AF_INET6:
1515215976Sjmallett		if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock)))
1516215976Sjmallett			ch = 'm';
1517215976Sjmallett		else
1518215976Sjmallett			ch = 'u';
1519215976Sjmallett		break;
1520215976Sjmallett	default:
1521215976Sjmallett		ch = '-';
1522215976Sjmallett		break;
1523215976Sjmallett	}
1524215976Sjmallett	return ch;
1525215976Sjmallett}
1526215976Sjmallett
1527215976Sjmallett/*
1528215976Sjmallett * A list of variables required by the peers command
1529215976Sjmallett */
1530215976Sjmallettstruct varlist opeervarlist[] = {
1531215976Sjmallett	{ "srcadr",	0 },	/* 0 */
1532215976Sjmallett	{ "dstadr",	0 },	/* 1 */
1533215976Sjmallett	{ "stratum",	0 },	/* 2 */
1534215976Sjmallett	{ "hpoll",	0 },	/* 3 */
1535215976Sjmallett	{ "ppoll",	0 },	/* 4 */
1536215976Sjmallett	{ "reach",	0 },	/* 5 */
1537215976Sjmallett	{ "delay",	0 },	/* 6 */
1538215976Sjmallett	{ "offset",	0 },	/* 7 */
1539215976Sjmallett	{ "jitter",	0 },	/* 8 */
1540215976Sjmallett	{ "dispersion", 0 },	/* 9 */
1541215976Sjmallett	{ "rec",	0 },	/* 10 */
1542215976Sjmallett	{ "reftime",	0 },	/* 11 */
1543215976Sjmallett	{ "srcport",	0 },	/* 12 */
1544215976Sjmallett	{ "hmode",	0 },	/* 13 */
1545215976Sjmallett	{ 0,		0 }
1546215976Sjmallett};
1547215976Sjmallett
1548215976Sjmallettstruct varlist peervarlist[] = {
1549215976Sjmallett	{ "srcadr",	0 },	/* 0 */
1550215976Sjmallett	{ "refid",	0 },	/* 1 */
1551215976Sjmallett	{ "stratum",	0 },	/* 2 */
1552215976Sjmallett	{ "hpoll",	0 },	/* 3 */
1553215976Sjmallett	{ "ppoll",	0 },	/* 4 */
1554215976Sjmallett	{ "reach",	0 },	/* 5 */
1555215976Sjmallett	{ "delay",	0 },	/* 6 */
1556215976Sjmallett	{ "offset",	0 },	/* 7 */
1557215976Sjmallett	{ "jitter",	0 },	/* 8 */
1558215976Sjmallett	{ "dispersion", 0 },	/* 9 */
1559215976Sjmallett	{ "rec",	0 },	/* 10 */
1560215976Sjmallett	{ "reftime",	0 },	/* 11 */
1561215976Sjmallett	{ "srcport",	0 },	/* 12 */
1562215976Sjmallett	{ "hmode",	0 },	/* 13 */
1563215976Sjmallett	{ "srchost",	0 },	/* 14 */
1564215976Sjmallett	{ 0,		0 }
1565215976Sjmallett};
1566215976Sjmallett
1567215976Sjmallettstruct varlist apeervarlist[] = {
1568215976Sjmallett	{ "srcadr",	0 },	/* 0 */
1569215976Sjmallett	{ "refid",	0 },	/* 1 */
1570215976Sjmallett	{ "assid",	0 },	/* 2 */
1571215976Sjmallett	{ "stratum",	0 },	/* 3 */
1572215976Sjmallett	{ "hpoll",	0 },	/* 4 */
1573215976Sjmallett	{ "ppoll",	0 },	/* 5 */
1574215976Sjmallett	{ "reach",	0 },	/* 6 */
1575215976Sjmallett	{ "delay",	0 },	/* 7 */
1576215976Sjmallett	{ "offset",	0 },	/* 8 */
1577215976Sjmallett	{ "jitter",	0 },	/* 9 */
1578215976Sjmallett	{ "dispersion", 0 },	/* 10 */
1579215976Sjmallett	{ "rec",	0 },	/* 11 */
1580215976Sjmallett	{ "reftime",	0 },	/* 12 */
1581215976Sjmallett	{ "srcport",	0 },	/* 13 */
1582215976Sjmallett	{ "hmode",	0 },	/* 14 */
1583215976Sjmallett	{ "srchost",	0 },	/* 15 */
1584215976Sjmallett	{ 0,		0 }
1585215976Sjmallett};
1586215976Sjmallett
1587215976Sjmallett
1588215976Sjmallett/*
1589215976Sjmallett * Decode an incoming data buffer and print a line in the peer list
1590215976Sjmallett */
1591215976Sjmallettstatic int
1592215976Sjmallettdoprintpeers(
1593215976Sjmallett	struct varlist *pvl,
1594215976Sjmallett	int associd,
1595215976Sjmallett	int rstatus,
1596215976Sjmallett	size_t datalen,
1597215976Sjmallett	const char *data,
1598215976Sjmallett	FILE *fp,
1599215976Sjmallett	int af
1600215976Sjmallett	)
1601215976Sjmallett{
1602215976Sjmallett	char *name;
1603215976Sjmallett	char *value = NULL;
1604215976Sjmallett	int c;
1605215976Sjmallett	size_t len;
1606215976Sjmallett	int have_srchost;
1607215976Sjmallett	int have_dstadr;
1608215976Sjmallett	int have_da_rid;
1609215976Sjmallett	int have_jitter;
1610215976Sjmallett	sockaddr_u srcadr;
1611215976Sjmallett	sockaddr_u dstadr;
1612215976Sjmallett	sockaddr_u dum_store;
1613215976Sjmallett	sockaddr_u refidadr;
1614215976Sjmallett	long hmode = 0;
1615215976Sjmallett	u_long srcport = 0;
1616215976Sjmallett	u_int32 u32;
1617215976Sjmallett	const char *dstadr_refid = "0.0.0.0";
1618215976Sjmallett	const char *serverlocal;
1619215976Sjmallett	size_t drlen;
1620215976Sjmallett	u_long stratum = 0;
1621215976Sjmallett	long ppoll = 0;
1622215976Sjmallett	long hpoll = 0;
1623215976Sjmallett	u_long reach = 0;
1624215976Sjmallett	l_fp estoffset;
1625215976Sjmallett	l_fp estdelay;
1626215976Sjmallett	l_fp estjitter;
1627215976Sjmallett	l_fp estdisp;
1628215976Sjmallett	l_fp reftime;
1629215976Sjmallett	l_fp rec;
1630215976Sjmallett	l_fp ts;
1631215976Sjmallett	u_long poll_sec;
1632215976Sjmallett	char type = '?';
1633215976Sjmallett	char whenbuf[8], pollbuf[8];
1634215976Sjmallett	char clock_name[LENHOSTNAME];
1635215976Sjmallett
1636215976Sjmallett	get_systime(&ts);
1637215976Sjmallett
1638215976Sjmallett	have_srchost = FALSE;
1639215976Sjmallett	have_dstadr = FALSE;
1640215976Sjmallett	have_da_rid = FALSE;
1641215976Sjmallett	have_jitter = FALSE;
1642215976Sjmallett	ZERO_SOCK(&srcadr);
1643215976Sjmallett	ZERO_SOCK(&dstadr);
1644215976Sjmallett	clock_name[0] = '\0';
1645215976Sjmallett	ZERO(estoffset);
1646215976Sjmallett	ZERO(estdelay);
1647215976Sjmallett	ZERO(estjitter);
1648215976Sjmallett	ZERO(estdisp);
1649215976Sjmallett
1650215976Sjmallett	while (nextvar(&datalen, &data, &name, &value)) {
1651215976Sjmallett		if (!strcmp("srcadr", name) ||
1652215976Sjmallett		    !strcmp("peeradr", name)) {
1653215976Sjmallett			if (!decodenetnum(value, &srcadr))
1654215976Sjmallett				fprintf(stderr, "malformed %s=%s\n",
1655215976Sjmallett					name, value);
1656215976Sjmallett		} else if (!strcmp("srchost", name)) {
1657215976Sjmallett			if (pvl == peervarlist || pvl == apeervarlist) {
1658215976Sjmallett				len = strlen(value);
1659215976Sjmallett				if (2 < len &&
1660215976Sjmallett				    (size_t)len < sizeof(clock_name)) {
1661215976Sjmallett					/* strip quotes */
1662215976Sjmallett					value++;
1663215976Sjmallett					len -= 2;
1664215976Sjmallett					memcpy(clock_name, value, len);
1665215976Sjmallett					clock_name[len] = '\0';
1666215976Sjmallett					have_srchost = TRUE;
1667215976Sjmallett				}
1668215976Sjmallett			}
1669215976Sjmallett		} else if (!strcmp("dstadr", name)) {
1670215976Sjmallett			if (decodenetnum(value, &dum_store)) {
1671215976Sjmallett				type = decodeaddrtype(&dum_store);
1672215976Sjmallett				have_dstadr = TRUE;
1673215976Sjmallett				dstadr = dum_store;
1674215976Sjmallett				if (pvl == opeervarlist) {
1675215976Sjmallett					have_da_rid = TRUE;
1676215976Sjmallett					dstadr_refid = trunc_left(stoa(&dstadr), 15);
1677215976Sjmallett				}
1678215976Sjmallett			}
1679215976Sjmallett		} else if (!strcmp("hmode", name)) {
1680215976Sjmallett			decodeint(value, &hmode);
1681215976Sjmallett		} else if (!strcmp("refid", name)) {
1682215976Sjmallett			if (   (pvl == peervarlist)
1683215976Sjmallett			    && (drefid == REFID_IPV4)) {
1684215976Sjmallett				have_da_rid = TRUE;
1685215976Sjmallett				drlen = strlen(value);
1686215976Sjmallett				if (0 == drlen) {
1687215976Sjmallett					dstadr_refid = "";
1688215976Sjmallett				} else if (drlen <= 4) {
1689215976Sjmallett					ZERO(u32);
1690215976Sjmallett					memcpy(&u32, value, drlen);
1691215976Sjmallett					dstadr_refid = refid_str(u32, 1);
1692215976Sjmallett				} else if (decodenetnum(value, &refidadr)) {
1693215976Sjmallett					if (SOCK_UNSPEC(&refidadr))
1694215976Sjmallett						dstadr_refid = "0.0.0.0";
1695215976Sjmallett					else if (ISREFCLOCKADR(&refidadr))
1696215976Sjmallett						dstadr_refid =
1697215976Sjmallett						    refnumtoa(&refidadr);
1698215976Sjmallett					else
1699215976Sjmallett						dstadr_refid =
1700215976Sjmallett						    stoa(&refidadr);
1701215976Sjmallett				} else {
1702215976Sjmallett					have_da_rid = FALSE;
1703215976Sjmallett				}
1704215976Sjmallett			} else if (   (pvl == apeervarlist)
1705215976Sjmallett				   || (pvl == peervarlist)) {
1706215976Sjmallett				/* no need to check drefid == REFID_HASH */
1707215976Sjmallett				have_da_rid = TRUE;
1708215976Sjmallett				drlen = strlen(value);
1709215976Sjmallett				if (0 == drlen) {
1710215976Sjmallett					dstadr_refid = "";
1711215976Sjmallett				} else if (drlen <= 4) {
1712215976Sjmallett					ZERO(u32);
1713215976Sjmallett					memcpy(&u32, value, drlen);
1714215976Sjmallett					dstadr_refid = refid_str(u32, 1);
1715215976Sjmallett					//fprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value);
1716215976Sjmallett				} else if (decodenetnum(value, &refidadr)) {
1717215976Sjmallett					if (SOCK_UNSPEC(&refidadr))
1718215976Sjmallett						dstadr_refid = "0.0.0.0";
1719215976Sjmallett					else if (ISREFCLOCKADR(&refidadr))
1720215976Sjmallett						dstadr_refid =
1721215976Sjmallett						    refnumtoa(&refidadr);
1722215976Sjmallett					else {
1723215976Sjmallett						char *buf = emalloc(10);
1724215976Sjmallett						int i = ntohl(refidadr.sa4.sin_addr.s_addr);
1725215976Sjmallett
1726215976Sjmallett						snprintf(buf, 10,
1727215976Sjmallett							"%0x", i);
1728215976Sjmallett						dstadr_refid = buf;
1729215976Sjmallett					//fprintf(stderr, "apeervarlist refid: value=<%x>\n", i);
1730215976Sjmallett					}
1731215976Sjmallett					//fprintf(stderr, "apeervarlist refid: value=<%s>\n", value);
1732215976Sjmallett				} else {
1733215976Sjmallett					have_da_rid = FALSE;
1734215976Sjmallett				}
1735215976Sjmallett			}
1736215976Sjmallett		} else if (!strcmp("stratum", name)) {
1737215976Sjmallett			decodeuint(value, &stratum);
1738215976Sjmallett		} else if (!strcmp("hpoll", name)) {
1739215976Sjmallett			if (decodeint(value, &hpoll) && hpoll < 0)
1740215976Sjmallett				hpoll = NTP_MINPOLL;
1741215976Sjmallett		} else if (!strcmp("ppoll", name)) {
1742215976Sjmallett			if (decodeint(value, &ppoll) && ppoll < 0)
1743215976Sjmallett				ppoll = NTP_MINPOLL;
1744215976Sjmallett		} else if (!strcmp("reach", name)) {
1745215976Sjmallett			decodeuint(value, &reach);
1746215976Sjmallett		} else if (!strcmp("delay", name)) {
1747215976Sjmallett			decodetime(value, &estdelay);
1748215976Sjmallett		} else if (!strcmp("offset", name)) {
1749215976Sjmallett			decodetime(value, &estoffset);
1750215976Sjmallett		} else if (!strcmp("jitter", name)) {
1751215976Sjmallett			if ((pvl == peervarlist || pvl == apeervarlist)
1752215976Sjmallett			    && decodetime(value, &estjitter))
1753215976Sjmallett				have_jitter = 1;
1754215976Sjmallett		} else if (!strcmp("rootdisp", name) ||
1755215976Sjmallett			   !strcmp("dispersion", name)) {
1756215976Sjmallett			decodetime(value, &estdisp);
1757215976Sjmallett		} else if (!strcmp("rec", name)) {
1758215976Sjmallett			decodets(value, &rec);
1759215976Sjmallett		} else if (!strcmp("srcport", name) ||
1760215976Sjmallett			   !strcmp("peerport", name)) {
1761215976Sjmallett			decodeuint(value, &srcport);
1762215976Sjmallett		} else if (!strcmp("reftime", name)) {
1763215976Sjmallett			if (!decodets(value, &reftime))
1764215976Sjmallett				L_CLR(&reftime);
1765215976Sjmallett		} else {
1766215976Sjmallett			// fprintf(stderr, "UNRECOGNIZED name=%s ", name);
1767215976Sjmallett		}
1768215976Sjmallett	}
1769215976Sjmallett
1770215976Sjmallett	/*
1771215976Sjmallett	 * hmode gives the best guidance for the t column.  If the response
1772215976Sjmallett	 * did not include hmode we'll use the old decodeaddrtype() result.
1773215976Sjmallett	 */
1774215976Sjmallett	switch (hmode) {
1775215976Sjmallett
1776215976Sjmallett	case MODE_BCLIENT:
1777215976Sjmallett		/* broadcastclient or multicastclient */
1778215976Sjmallett		type = 'b';
1779215976Sjmallett		break;
1780215976Sjmallett
1781215976Sjmallett	case MODE_BROADCAST:
1782215976Sjmallett		/* broadcast or multicast server */
1783215976Sjmallett		if (IS_MCAST(&srcadr))
1784215976Sjmallett			type = 'M';
1785215976Sjmallett		else
1786215976Sjmallett			type = 'B';
1787215976Sjmallett		break;
1788215976Sjmallett
1789215976Sjmallett	case MODE_CLIENT:
1790215976Sjmallett		if (ISREFCLOCKADR(&srcadr))
1791215976Sjmallett			type = 'l';	/* local refclock*/
1792215976Sjmallett		else if (SOCK_UNSPEC(&srcadr))
1793215976Sjmallett			type = 'p';	/* pool */
1794215976Sjmallett		else if (IS_MCAST(&srcadr))
1795215976Sjmallett			type = 'a';	/* manycastclient */
1796215976Sjmallett		else
1797215976Sjmallett			type = 'u';	/* unicast */
1798215976Sjmallett		break;
1799215976Sjmallett
1800215976Sjmallett	case MODE_ACTIVE:
1801215976Sjmallett		type = 's';		/* symmetric active */
1802215976Sjmallett		break;			/* configured */
1803215976Sjmallett
1804215976Sjmallett	case MODE_PASSIVE:
1805215976Sjmallett		type = 'S';		/* symmetric passive */
1806215976Sjmallett		break;			/* ephemeral */
1807215976Sjmallett	}
1808215976Sjmallett
1809215976Sjmallett	/*
1810215976Sjmallett	 * Got everything, format the line
1811215976Sjmallett	 */
1812215976Sjmallett	poll_sec = 1 << min(ppoll, hpoll);
1813215976Sjmallett	if (pktversion > NTP_OLDVERSION)
1814215976Sjmallett		c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
1815215976Sjmallett	else
1816215976Sjmallett		c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
1817215976Sjmallett	if (numhosts > 1) {
1818215976Sjmallett		if ((pvl == peervarlist || pvl == apeervarlist)
1819215976Sjmallett		    && have_dstadr) {
1820215976Sjmallett			serverlocal = nntohost_col(&dstadr,
1821215976Sjmallett			    (size_t)min(LIB_BUFLENGTH - 1, maxhostlen),
1822215976Sjmallett			    TRUE);
1823215976Sjmallett		} else {
1824215976Sjmallett			if (currenthostisnum)
1825215976Sjmallett				serverlocal = trunc_left(currenthost,
1826215976Sjmallett							 maxhostlen);
1827215976Sjmallett			else
1828215976Sjmallett				serverlocal = currenthost;
1829215976Sjmallett		}
1830215976Sjmallett		fprintf(fp, "%-*s ", (int)maxhostlen, serverlocal);
1831215976Sjmallett	}
1832215976Sjmallett	if (AF_UNSPEC == af || AF(&srcadr) == af) {
1833215976Sjmallett		if (!have_srchost)
1834215976Sjmallett			strlcpy(clock_name, nntohost(&srcadr),
1835215976Sjmallett				sizeof(clock_name));
1836215976Sjmallett		if (wideremote && 15 < strlen(clock_name))
1837215976Sjmallett			fprintf(fp, "%c%s\n                 ", c, clock_name);
1838215976Sjmallett		else
1839215976Sjmallett			fprintf(fp, "%c%-15.15s ", c, clock_name);
1840215976Sjmallett		if (!have_da_rid) {
1841215976Sjmallett			drlen = 0;
1842215976Sjmallett		} else {
1843215976Sjmallett			drlen = strlen(dstadr_refid);
1844215976Sjmallett			makeascii(drlen, dstadr_refid, fp);
1845215976Sjmallett		}
1846215976Sjmallett		if (pvl == apeervarlist) {
1847215976Sjmallett			while (drlen++ < 9)
1848215976Sjmallett				fputc(' ', fp);
1849215976Sjmallett			fprintf(fp, "%-6d", associd);
1850215976Sjmallett		} else {
1851215976Sjmallett			while (drlen++ < 15)
1852215976Sjmallett				fputc(' ', fp);
1853215976Sjmallett		}
1854215976Sjmallett		fprintf(fp,
1855215976Sjmallett			" %2ld %c %4.4s %4.4s  %3lo  %7.7s %8.7s %7.7s\n",
1856215976Sjmallett			stratum, type,
1857215976Sjmallett			prettyinterval(whenbuf, sizeof(whenbuf),
1858215976Sjmallett				       when(&ts, &rec, &reftime)),
1859215976Sjmallett			prettyinterval(pollbuf, sizeof(pollbuf),
1860215976Sjmallett				       (int)poll_sec),
1861215976Sjmallett			reach, lfptoms(&estdelay, 3),
1862215976Sjmallett			lfptoms(&estoffset, 3),
1863215976Sjmallett			(have_jitter)
1864215976Sjmallett			    ? lfptoms(&estjitter, 3)
1865215976Sjmallett			    : lfptoms(&estdisp, 3));
1866215976Sjmallett		return (1);
1867215976Sjmallett	}
1868215976Sjmallett	else
1869215976Sjmallett		return(1);
1870215976Sjmallett}
1871215976Sjmallett
1872215976Sjmallett
1873215976Sjmallett/*
1874215976Sjmallett * dogetpeers - given an association ID, read and print the spreadsheet
1875215976Sjmallett *		peer variables.
1876215976Sjmallett */
1877215976Sjmallettstatic int
1878215976Sjmallettdogetpeers(
1879215976Sjmallett	struct varlist *pvl,
1880215976Sjmallett	associd_t associd,
1881215976Sjmallett	FILE *fp,
1882215976Sjmallett	int af
1883215976Sjmallett	)
1884215976Sjmallett{
1885215976Sjmallett	const char *datap;
1886215976Sjmallett	int res;
1887215976Sjmallett	size_t dsize;
1888215976Sjmallett	u_short rstatus;
1889215976Sjmallett
1890215976Sjmallett#ifdef notdef
1891215976Sjmallett	res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
1892215976Sjmallett			  &dsize, &datap);
1893215976Sjmallett#else
1894215976Sjmallett	/*
1895215976Sjmallett	 * Damn fuzzballs
1896215976Sjmallett	 */
1897215976Sjmallett	res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
1898215976Sjmallett			  &dsize, &datap);
1899215976Sjmallett#endif
1900215976Sjmallett
1901215976Sjmallett	if (res != 0)
1902215976Sjmallett		return 0;
1903215976Sjmallett
1904215976Sjmallett	if (dsize == 0) {
1905215976Sjmallett		if (numhosts > 1)
1906215976Sjmallett			fprintf(stderr, "server=%s ", currenthost);
1907215976Sjmallett		fprintf(stderr,
1908215976Sjmallett			"***No information returned for association %u\n",
1909215976Sjmallett			associd);
1910215976Sjmallett		return 0;
1911215976Sjmallett	}
1912215976Sjmallett
1913215976Sjmallett	return doprintpeers(pvl, associd, (int)rstatus, dsize, datap,
1914215976Sjmallett			    fp, af);
1915215976Sjmallett}
1916215976Sjmallett
1917215976Sjmallett
1918215976Sjmallett/*
1919215976Sjmallett * peers - print a peer spreadsheet
1920215976Sjmallett */
1921215976Sjmallettstatic void
1922215976Sjmallettdopeers(
1923215976Sjmallett	int showall,
1924215976Sjmallett	FILE *fp,
1925215976Sjmallett	int af
1926215976Sjmallett	)
1927215976Sjmallett{
1928215976Sjmallett	u_int		u;
1929215976Sjmallett	char		fullname[LENHOSTNAME];
1930215976Sjmallett	sockaddr_u	netnum;
1931215976Sjmallett	const char *	name_or_num;
1932215976Sjmallett	size_t		sl;
1933215976Sjmallett
1934215976Sjmallett	if (!dogetassoc(fp))
1935215976Sjmallett		return;
1936215976Sjmallett
1937215976Sjmallett	for (u = 0; u < numhosts; u++) {
1938215976Sjmallett		if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
1939215976Sjmallett			name_or_num = nntohost(&netnum);
1940215976Sjmallett			sl = strlen(name_or_num);
1941215976Sjmallett			maxhostlen = max(maxhostlen, sl);
1942215976Sjmallett		}
1943215976Sjmallett	}
1944215976Sjmallett	if (numhosts > 1)
1945215976Sjmallett		fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
1946215976Sjmallett			"server (local)");
1947215976Sjmallett	fprintf(fp,
1948215976Sjmallett		"     remote           refid      st t when poll reach   delay   offset  jitter\n");
1949215976Sjmallett	if (numhosts > 1)
1950215976Sjmallett		for (u = 0; u <= maxhostlen; u++)
1951215976Sjmallett			fprintf(fp, "=");
1952215976Sjmallett	fprintf(fp,
1953215976Sjmallett		"==============================================================================\n");
1954215976Sjmallett
1955215976Sjmallett	for (u = 0; u < numassoc; u++) {
1956215976Sjmallett		if (!showall &&
1957215976Sjmallett		    !(CTL_PEER_STATVAL(assoc_cache[u].status)
1958215976Sjmallett		      & (CTL_PST_CONFIG|CTL_PST_REACH))) {
1959215976Sjmallett			if (debug)
1960215976Sjmallett				fprintf(stderr, "eliding [%d]\n",
1961215976Sjmallett					(int)assoc_cache[u].assid);
1962215976Sjmallett			continue;
1963215976Sjmallett		}
1964215976Sjmallett		if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid,
1965215976Sjmallett				fp, af))
1966215976Sjmallett			return;
1967215976Sjmallett	}
1968215976Sjmallett	return;
1969215976Sjmallett}
1970215976Sjmallett
1971215976Sjmallett
1972215976Sjmallett/*
1973215976Sjmallett * doapeers - print a peer spreadsheet with assocIDs
1974215976Sjmallett */
1975215976Sjmallettstatic void
1976215976Sjmallettdoapeers(
1977215976Sjmallett	int showall,
1978215976Sjmallett	FILE *fp,
1979215976Sjmallett	int af
1980215976Sjmallett	)
1981215976Sjmallett{
1982215976Sjmallett	u_int		u;
1983215976Sjmallett	char		fullname[LENHOSTNAME];
1984215976Sjmallett	sockaddr_u	netnum;
1985215976Sjmallett	const char *	name_or_num;
1986215976Sjmallett	size_t		sl;
1987215976Sjmallett
1988215976Sjmallett	if (!dogetassoc(fp))
1989215976Sjmallett		return;
1990215976Sjmallett
1991215976Sjmallett	for (u = 0; u < numhosts; u++) {
1992215976Sjmallett		if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
1993215976Sjmallett			name_or_num = nntohost(&netnum);
1994215976Sjmallett			sl = strlen(name_or_num);
1995215976Sjmallett			maxhostlen = max(maxhostlen, sl);
1996215976Sjmallett		}
1997215976Sjmallett	}
1998215976Sjmallett	if (numhosts > 1)
1999215976Sjmallett		fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2000215976Sjmallett			"server (local)");
2001215976Sjmallett	fprintf(fp,
2002215976Sjmallett		"     remote       refid   assid  st t when poll reach   delay   offset  jitter\n");
2003215976Sjmallett	if (numhosts > 1)
2004215976Sjmallett		for (u = 0; u <= maxhostlen; u++)
2005215976Sjmallett			fprintf(fp, "=");
2006215976Sjmallett	fprintf(fp,
2007215976Sjmallett		"==============================================================================\n");
2008215976Sjmallett
2009215976Sjmallett	for (u = 0; u < numassoc; u++) {
2010215976Sjmallett		if (!showall &&
2011215976Sjmallett		    !(CTL_PEER_STATVAL(assoc_cache[u].status)
2012215976Sjmallett		      & (CTL_PST_CONFIG|CTL_PST_REACH))) {
2013215976Sjmallett			if (debug)
2014215976Sjmallett				fprintf(stderr, "eliding [%d]\n",
2015215976Sjmallett					(int)assoc_cache[u].assid);
2016215976Sjmallett			continue;
2017215976Sjmallett		}
2018215976Sjmallett		if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid,
2019215976Sjmallett				fp, af))
2020215976Sjmallett			return;
2021215976Sjmallett	}
2022215976Sjmallett	return;
2023215976Sjmallett}
2024215976Sjmallett
2025215976Sjmallett
2026215976Sjmallett/*
2027215976Sjmallett * peers - print a peer spreadsheet
2028215976Sjmallett */
2029215976Sjmallett/*ARGSUSED*/
2030215976Sjmallettstatic void
2031215976Sjmallettpeers(
2032215976Sjmallett	struct parse *pcmd,
2033215976Sjmallett	FILE *fp
2034215976Sjmallett	)
2035215976Sjmallett{
2036215976Sjmallett	if (drefid == REFID_HASH) {
2037215976Sjmallett		apeers(pcmd, fp);
2038215976Sjmallett	} else {
2039215976Sjmallett		int af = 0;
2040215976Sjmallett
2041215976Sjmallett		if (pcmd->nargs == 1) {
2042215976Sjmallett			if (pcmd->argval->ival == 6)
2043215976Sjmallett				af = AF_INET6;
2044215976Sjmallett			else
2045215976Sjmallett				af = AF_INET;
2046215976Sjmallett		}
2047215976Sjmallett		dopeers(0, fp, af);
2048215976Sjmallett	}
2049215976Sjmallett}
2050215976Sjmallett
2051215976Sjmallett
2052215976Sjmallett/*
2053215976Sjmallett * apeers - print a peer spreadsheet, with assocIDs
2054215976Sjmallett */
2055215976Sjmallett/*ARGSUSED*/
2056215976Sjmallettstatic void
2057215976Sjmallettapeers(
2058215976Sjmallett	struct parse *pcmd,
2059215976Sjmallett	FILE *fp
2060215976Sjmallett	)
2061215976Sjmallett{
2062215976Sjmallett	int af = 0;
2063215976Sjmallett
2064215976Sjmallett	if (pcmd->nargs == 1) {
2065215976Sjmallett		if (pcmd->argval->ival == 6)
2066215976Sjmallett			af = AF_INET6;
2067215976Sjmallett		else
2068215976Sjmallett			af = AF_INET;
2069215976Sjmallett	}
2070215976Sjmallett	doapeers(0, fp, af);
2071215976Sjmallett}
2072215976Sjmallett
2073215976Sjmallett
2074215976Sjmallett/*
2075215976Sjmallett * lpeers - print a peer spreadsheet including all fuzzball peers
2076215976Sjmallett */
2077215976Sjmallett/*ARGSUSED*/
2078215976Sjmallettstatic void
2079215976Sjmallettlpeers(
2080215976Sjmallett	struct parse *pcmd,
2081215976Sjmallett	FILE *fp
2082215976Sjmallett	)
2083215976Sjmallett{
2084215976Sjmallett	int af = 0;
2085215976Sjmallett
2086215976Sjmallett	if (pcmd->nargs == 1) {
2087215976Sjmallett		if (pcmd->argval->ival == 6)
2088215976Sjmallett			af = AF_INET6;
2089215976Sjmallett		else
2090215976Sjmallett			af = AF_INET;
2091215976Sjmallett	}
2092215976Sjmallett	dopeers(1, fp, af);
2093215976Sjmallett}
2094215976Sjmallett
2095215976Sjmallett
2096215976Sjmallett/*
2097215976Sjmallett * opeers - print a peer spreadsheet
2098215976Sjmallett */
2099215976Sjmallettstatic void
2100215976Sjmallettdoopeers(
2101215976Sjmallett	int showall,
2102215976Sjmallett	FILE *fp,
2103215976Sjmallett	int af
2104215976Sjmallett	)
2105215976Sjmallett{
2106215976Sjmallett	u_int i;
2107215976Sjmallett	char fullname[LENHOSTNAME];
2108215976Sjmallett	sockaddr_u netnum;
2109215976Sjmallett
2110215976Sjmallett	if (!dogetassoc(fp))
2111215976Sjmallett		return;
2112215976Sjmallett
2113215976Sjmallett	for (i = 0; i < numhosts; ++i) {
2114215976Sjmallett		if (getnetnum(chosts[i].name, &netnum, fullname, af))
2115215976Sjmallett			if (strlen(fullname) > maxhostlen)
2116215976Sjmallett				maxhostlen = strlen(fullname);
2117215976Sjmallett	}
2118215976Sjmallett	if (numhosts > 1)
2119215976Sjmallett		fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2120215976Sjmallett			"server");
2121215976Sjmallett	fprintf(fp,
2122215976Sjmallett	    "     remote           local      st t when poll reach   delay   offset    disp\n");
2123215976Sjmallett	if (numhosts > 1)
2124215976Sjmallett		for (i = 0; i <= maxhostlen; ++i)
2125215976Sjmallett			fprintf(fp, "=");
2126215976Sjmallett	fprintf(fp,
2127215976Sjmallett	    "==============================================================================\n");
2128215976Sjmallett
2129215976Sjmallett	for (i = 0; i < numassoc; i++) {
2130215976Sjmallett		if (!showall &&
2131215976Sjmallett		    !(CTL_PEER_STATVAL(assoc_cache[i].status) &
2132215976Sjmallett		      (CTL_PST_CONFIG | CTL_PST_REACH)))
2133215976Sjmallett			continue;
2134215976Sjmallett		if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af))
2135215976Sjmallett			return;
2136215976Sjmallett	}
2137215976Sjmallett	return;
2138215976Sjmallett}
2139215976Sjmallett
2140215976Sjmallett
2141215976Sjmallett/*
2142215976Sjmallett * opeers - print a peer spreadsheet the old way
2143215976Sjmallett */
2144215976Sjmallett/*ARGSUSED*/
2145215976Sjmallettstatic void
2146215976Sjmallettopeers(
2147215976Sjmallett	struct parse *pcmd,
2148215976Sjmallett	FILE *fp
2149215976Sjmallett	)
2150215976Sjmallett{
2151215976Sjmallett	int af = 0;
2152215976Sjmallett
2153215976Sjmallett	if (pcmd->nargs == 1) {
2154215976Sjmallett		if (pcmd->argval->ival == 6)
2155215976Sjmallett			af = AF_INET6;
2156215976Sjmallett		else
2157215976Sjmallett			af = AF_INET;
2158215976Sjmallett	}
2159215976Sjmallett	doopeers(0, fp, af);
2160215976Sjmallett}
2161215976Sjmallett
2162215976Sjmallett
2163215976Sjmallett/*
2164215976Sjmallett * lopeers - print a peer spreadsheet including all fuzzball peers
2165215976Sjmallett */
2166215976Sjmallett/*ARGSUSED*/
2167215976Sjmallettstatic void
2168215976Sjmallettlopeers(
2169215976Sjmallett	struct parse *pcmd,
2170215976Sjmallett	FILE *fp
2171215976Sjmallett	)
2172215976Sjmallett{
2173215976Sjmallett	int af = 0;
2174215976Sjmallett
2175215976Sjmallett	if (pcmd->nargs == 1) {
2176215976Sjmallett		if (pcmd->argval->ival == 6)
2177215976Sjmallett			af = AF_INET6;
2178215976Sjmallett		else
2179215976Sjmallett			af = AF_INET;
2180215976Sjmallett	}
2181215976Sjmallett	doopeers(1, fp, af);
2182215976Sjmallett}
2183215976Sjmallett
2184215976Sjmallett
2185215976Sjmallett/*
2186215976Sjmallett * config - send a configuration command to a remote host
2187215976Sjmallett */
2188215976Sjmallettstatic void
2189215976Sjmallettconfig (
2190215976Sjmallett	struct parse *pcmd,
2191215976Sjmallett	FILE *fp
2192215976Sjmallett	)
2193215976Sjmallett{
2194215976Sjmallett	const char *cfgcmd;
2195215976Sjmallett	u_short rstatus;
2196215976Sjmallett	size_t rsize;
2197215976Sjmallett	const char *rdata;
2198215976Sjmallett	char *resp;
2199215976Sjmallett	int res;
2200215976Sjmallett	int col;
2201215976Sjmallett	int i;
2202215976Sjmallett
2203215976Sjmallett	cfgcmd = pcmd->argval[0].string;
2204215976Sjmallett
2205215976Sjmallett	if (debug > 2)
2206215976Sjmallett		fprintf(stderr,
2207215976Sjmallett			"In Config\n"
2208215976Sjmallett			"Keyword = %s\n"
2209215976Sjmallett			"Command = %s\n", pcmd->keyword, cfgcmd);
2210215976Sjmallett
2211215976Sjmallett	res = doquery(CTL_OP_CONFIGURE, 0, 1,
2212215976Sjmallett		      strlen(cfgcmd), cfgcmd,
2213215976Sjmallett		      &rstatus, &rsize, &rdata);
2214215976Sjmallett
2215215976Sjmallett	if (res != 0)
2216215976Sjmallett		return;
2217215976Sjmallett
2218215976Sjmallett	if (rsize > 0 && '\n' == rdata[rsize - 1])
2219215976Sjmallett		rsize--;
2220215976Sjmallett
2221215976Sjmallett	resp = emalloc(rsize + 1);
2222215976Sjmallett	memcpy(resp, rdata, rsize);
2223215976Sjmallett	resp[rsize] = '\0';
2224215976Sjmallett
2225215976Sjmallett	col = -1;
2226215976Sjmallett	if (1 == sscanf(resp, "column %d syntax error", &col)
2227215976Sjmallett	    && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) {
2228215976Sjmallett		if (interactive) {
2229215976Sjmallett			printf("______");	/* "ntpq> " */
2230215976Sjmallett			printf("________");	/* ":config " */
2231215976Sjmallett		} else
2232215976Sjmallett			printf("%s\n", cfgcmd);
2233215976Sjmallett		for (i = 1; i < col; i++)
2234215976Sjmallett			putchar('_');
2235215976Sjmallett		printf("^\n");
2236215976Sjmallett	}
2237215976Sjmallett	printf("%s\n", resp);
2238215976Sjmallett	free(resp);
2239215976Sjmallett}
2240215976Sjmallett
2241215976Sjmallett
2242215976Sjmallett/*
2243215976Sjmallett * config_from_file - remotely configure an ntpd daemon using the
2244215976Sjmallett * specified configuration file
2245215976Sjmallett * SK: This function is a kludge at best and is full of bad design
2246215976Sjmallett * bugs:
2247215976Sjmallett * 1. ntpq uses UDP, which means that there is no guarantee of in-order,
2248215976Sjmallett *    error-free delivery.
2249215976Sjmallett * 2. The maximum length of a packet is constrained, and as a result, the
2250215976Sjmallett *    maximum length of a line in a configuration file is constrained.
2251215976Sjmallett *    Longer lines will lead to unpredictable results.
2252215976Sjmallett * 3. Since this function is sending a line at a time, we can't update
2253215976Sjmallett *    the control key through the configuration file (YUCK!!)
2254215976Sjmallett *
2255215976Sjmallett * Pearly: There are a few places where 'size_t' is cast to 'int' based
2256215976Sjmallett * on the assumption that 'int' can hold the size of the involved
2257215976Sjmallett * buffers without overflow.
2258215976Sjmallett */
2259215976Sjmallettstatic void
2260215976Sjmallettconfig_from_file (
2261215976Sjmallett	struct parse *pcmd,
2262215976Sjmallett	FILE *fp
2263215976Sjmallett	)
2264215976Sjmallett{
2265215976Sjmallett	u_short rstatus;
2266215976Sjmallett	size_t rsize;
2267215976Sjmallett	const char *rdata;
2268215976Sjmallett	char * cp;
2269215976Sjmallett	int res;
2270215976Sjmallett	FILE *config_fd;
2271215976Sjmallett	char config_cmd[MAXLINE];
2272215976Sjmallett	size_t config_len;
2273215976Sjmallett	int i;
2274215976Sjmallett	int retry_limit;
2275215976Sjmallett
2276215976Sjmallett	if (debug > 2)
2277215976Sjmallett		fprintf(stderr,
2278215976Sjmallett			"In Config\n"
2279215976Sjmallett			"Keyword = %s\n"
2280215976Sjmallett			"Filename = %s\n", pcmd->keyword,
2281215976Sjmallett			pcmd->argval[0].string);
2282215976Sjmallett
2283215976Sjmallett	config_fd = fopen(pcmd->argval[0].string, "r");
2284215976Sjmallett	if (NULL == config_fd) {
2285215976Sjmallett		printf("ERROR!! Couldn't open file: %s\n",
2286215976Sjmallett		       pcmd->argval[0].string);
2287215976Sjmallett		return;
2288215976Sjmallett	}
2289215976Sjmallett
2290215976Sjmallett	printf("Sending configuration file, one line at a time.\n");
2291215976Sjmallett	i = 0;
2292215976Sjmallett	while (fgets(config_cmd, MAXLINE, config_fd) != NULL) {
2293215976Sjmallett		/* Eliminate comments first. */
2294215976Sjmallett		cp = strchr(config_cmd, '#');
2295215976Sjmallett		config_len = (NULL != cp)
2296215976Sjmallett		    ? (size_t)(cp - config_cmd)
2297215976Sjmallett		    : strlen(config_cmd);
2298215976Sjmallett
2299215976Sjmallett		/* [Bug 3015] make sure there's no trailing whitespace;
2300215976Sjmallett		 * the fix for [Bug 2853] on the server side forbids
2301215976Sjmallett		 * those. And don't transmit empty lines, as this would
2302215976Sjmallett		 * just be waste.
2303215976Sjmallett		 */
2304215976Sjmallett		while (config_len != 0 &&
2305215976Sjmallett		       (u_char)config_cmd[config_len-1] <= ' ')
2306215976Sjmallett			--config_len;
2307215976Sjmallett		config_cmd[config_len] = '\0';
2308215976Sjmallett
2309215976Sjmallett		++i;
2310215976Sjmallett		if (0 == config_len)
2311215976Sjmallett			continue;
2312215976Sjmallett
2313215976Sjmallett		retry_limit = 2;
2314215976Sjmallett		do
2315215976Sjmallett			res = doquery(CTL_OP_CONFIGURE, 0, 1,
2316215976Sjmallett				      config_len, config_cmd,
2317215976Sjmallett				      &rstatus, &rsize, &rdata);
2318215976Sjmallett		while (res != 0 && retry_limit--);
2319215976Sjmallett		if (res != 0) {
2320215976Sjmallett			printf("Line No: %d query failed: %.*s\n"
2321215976Sjmallett			       "Subsequent lines not sent.\n",
2322215976Sjmallett			       i, (int)config_len, config_cmd);
2323215976Sjmallett			fclose(config_fd);
2324215976Sjmallett			return;
2325215976Sjmallett		}
2326215976Sjmallett
2327215976Sjmallett		/* Right-strip the result code string, then output the
2328215976Sjmallett		 * last line executed, with result code. */
2329215976Sjmallett		while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ')
2330215976Sjmallett			--rsize;
2331215976Sjmallett		printf("Line No: %d %.*s: %.*s\n", i,
2332215976Sjmallett		       (int)rsize, rdata,
2333215976Sjmallett		       (int)config_len, config_cmd);
2334215976Sjmallett	}
2335215976Sjmallett	printf("Done sending file\n");
2336215976Sjmallett	fclose(config_fd);
2337215976Sjmallett}
2338215976Sjmallett
2339215976Sjmallett
2340215976Sjmallettstatic int
2341215976Sjmallettfetch_nonce(
2342215976Sjmallett	char *	nonce,
2343215976Sjmallett	size_t	cb_nonce
2344215976Sjmallett	)
2345215976Sjmallett{
2346215976Sjmallett	const char	nonce_eq[] = "nonce=";
2347215976Sjmallett	int		qres;
2348215976Sjmallett	u_short		rstatus;
2349215976Sjmallett	size_t		rsize;
2350215976Sjmallett	const char *	rdata;
2351215976Sjmallett	size_t		chars;
2352215976Sjmallett
2353215976Sjmallett	/*
2354215976Sjmallett	 * Retrieve a nonce specific to this client to demonstrate to
2355215976Sjmallett	 * ntpd that we're capable of receiving responses to our source
2356215976Sjmallett	 * IP address, and thereby unlikely to be forging the source.
2357215976Sjmallett	 */
2358215976Sjmallett	qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus,
2359215976Sjmallett		       &rsize, &rdata);
2360215976Sjmallett	if (qres) {
2361215976Sjmallett		fprintf(stderr, "nonce request failed\n");
2362215976Sjmallett		return FALSE;
2363215976Sjmallett	}
2364215976Sjmallett
2365215976Sjmallett	if ((size_t)rsize <= sizeof(nonce_eq) - 1 ||
2366215976Sjmallett	    strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) {
2367215976Sjmallett		fprintf(stderr, "unexpected nonce response format: %.*s\n",
2368215976Sjmallett			(int)rsize, rdata); /* cast is wobbly */
2369215976Sjmallett		return FALSE;
2370215976Sjmallett	}
2371215976Sjmallett	chars = rsize - (sizeof(nonce_eq) - 1);
2372215976Sjmallett	if (chars >= (int)cb_nonce)
2373215976Sjmallett		return FALSE;
2374215976Sjmallett	memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars);
2375215976Sjmallett	nonce[chars] = '\0';
2376215976Sjmallett	while (chars > 0 &&
2377215976Sjmallett	       ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) {
2378215976Sjmallett		chars--;
2379215976Sjmallett		nonce[chars] = '\0';
2380215976Sjmallett	}
2381215976Sjmallett
2382215976Sjmallett	return TRUE;
2383215976Sjmallett}
2384215976Sjmallett
2385215976Sjmallett
2386215976Sjmallett/*
2387215976Sjmallett * add_mru	Add and entry to mru list, hash table, and allocate
2388215976Sjmallett *		and return a replacement.
2389215976Sjmallett *		This is a helper for collect_mru_list().
2390215976Sjmallett */
2391215976Sjmallettstatic mru *
2392215976Sjmallettadd_mru(
2393215976Sjmallett	mru *add
2394215976Sjmallett	)
2395215976Sjmallett{
2396215976Sjmallett	u_short hash;
2397215976Sjmallett	mru *mon;
2398215976Sjmallett	mru *unlinked;
2399215976Sjmallett
2400215976Sjmallett
2401215976Sjmallett	hash = NTP_HASH_ADDR(&add->addr);
2402215976Sjmallett	/* see if we have it among previously received entries */
2403215976Sjmallett	for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink)
2404215976Sjmallett		if (SOCK_EQ(&mon->addr, &add->addr))
2405215976Sjmallett			break;
2406215976Sjmallett	if (mon != NULL) {
2407215976Sjmallett		if (!L_ISGEQ(&add->first, &mon->first)) {
2408215976Sjmallett			fprintf(stderr,
2409215976Sjmallett				"add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n",
2410215976Sjmallett				sptoa(&add->addr), add->last.l_ui,
2411215976Sjmallett				add->last.l_uf, mon->last.l_ui,
2412215976Sjmallett				mon->last.l_uf);
2413215976Sjmallett			exit(1);
2414215976Sjmallett		}
2415215976Sjmallett		UNLINK_DLIST(mon, mlink);
2416215976Sjmallett		UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru);
2417215976Sjmallett		INSIST(unlinked == mon);
2418215976Sjmallett		mru_dupes++;
2419215976Sjmallett		TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui,
2420215976Sjmallett		      mon->last.l_uf));
2421215976Sjmallett	}
2422215976Sjmallett	LINK_DLIST(mru_list, add, mlink);
2423215976Sjmallett	LINK_SLIST(hash_table[hash], add, hlink);
2424215976Sjmallett	TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
2425215976Sjmallett	      add->last.l_ui, add->last.l_uf, add->count,
2426215976Sjmallett	      (int)add->mode, (int)add->ver, (u_int)add->rs,
2427215976Sjmallett	      add->first.l_ui, add->first.l_uf, sptoa(&add->addr)));
2428215976Sjmallett	/* if we didn't update an existing entry, alloc replacement */
2429215976Sjmallett	if (NULL == mon) {
2430215976Sjmallett		mon = emalloc(sizeof(*mon));
2431215976Sjmallett		mru_count++;
2432215976Sjmallett	}
2433215976Sjmallett	ZERO(*mon);
2434215976Sjmallett
2435215976Sjmallett	return mon;
2436215976Sjmallett}
2437215976Sjmallett
2438215976Sjmallett
2439215976Sjmallett/* MGOT macro is specific to collect_mru_list() */
2440215976Sjmallett#define MGOT(bit)				\
2441215976Sjmallett	do {					\
2442215976Sjmallett		got |= (bit);			\
2443215976Sjmallett		if (MRU_GOT_ALL == got) {	\
2444215976Sjmallett			got = 0;		\
2445215976Sjmallett			mon = add_mru(mon);	\
2446215976Sjmallett			ci++;			\
2447215976Sjmallett		}				\
2448215976Sjmallett	} while (0)
2449215976Sjmallett
2450215976Sjmallett
2451215976Sjmallettint
2452215976Sjmallettmrulist_ctrl_c_hook(void)
2453215976Sjmallett{
2454215976Sjmallett	mrulist_interrupted = TRUE;
2455215976Sjmallett	return TRUE;
2456215976Sjmallett}
2457215976Sjmallett
2458215976Sjmallett
2459215976Sjmallettstatic int
2460215976Sjmallettcollect_mru_list(
2461215976Sjmallett	const char *	parms,
2462215976Sjmallett	l_fp *		pnow
2463215976Sjmallett	)
2464215976Sjmallett{
2465215976Sjmallett	const u_int sleep_msecs = 5;
2466215976Sjmallett	static int ntpd_row_limit = MRU_ROW_LIMIT;
2467215976Sjmallett	int c_mru_l_rc;		/* this function's return code */
2468215976Sjmallett	u_char got;		/* MRU_GOT_* bits */
2469215976Sjmallett	time_t next_report;
2470215976Sjmallett	size_t cb;
2471215976Sjmallett	mru *mon;
2472215976Sjmallett	mru *head;
2473215976Sjmallett	mru *recent;
2474215976Sjmallett	int list_complete;
2475215976Sjmallett	char nonce[128];
2476215976Sjmallett	char buf[128];
2477215976Sjmallett	char req_buf[CTL_MAX_DATA_LEN];
2478215976Sjmallett	char *req;
2479215976Sjmallett	char *req_end;
2480215976Sjmallett	size_t chars;
2481215976Sjmallett	int qres;
2482215976Sjmallett	u_short rstatus;
2483215976Sjmallett	size_t rsize;
2484215976Sjmallett	const char *rdata;
2485215976Sjmallett	int limit;
2486215976Sjmallett	int frags;
2487215976Sjmallett	int cap_frags;
2488215976Sjmallett	char *tag;
2489215976Sjmallett	char *val;
2490215976Sjmallett	int si;		/* server index in response */
2491215976Sjmallett	int ci;		/* client (our) index for validation */
2492215976Sjmallett	int ri;		/* request index (.# suffix) */
2493215976Sjmallett	int mv;
2494215976Sjmallett	l_fp newest;
2495215976Sjmallett	l_fp last_older;
2496215976Sjmallett	sockaddr_u addr_older;
2497215976Sjmallett	int have_now;
2498215976Sjmallett	int have_addr_older;
2499215976Sjmallett	int have_last_older;
2500215976Sjmallett	u_int restarted_count;
2501215976Sjmallett	u_int nonce_uses;
2502215976Sjmallett	u_short hash;
2503215976Sjmallett	mru *unlinked;
2504215976Sjmallett
2505215976Sjmallett	if (!fetch_nonce(nonce, sizeof(nonce)))
2506215976Sjmallett		return FALSE;
2507215976Sjmallett
2508215976Sjmallett	nonce_uses = 0;
2509215976Sjmallett	restarted_count = 0;
2510215976Sjmallett	mru_count = 0;
2511215976Sjmallett	INIT_DLIST(mru_list, mlink);
2512215976Sjmallett	cb = NTP_HASH_SIZE * sizeof(*hash_table);
2513215976Sjmallett	INSIST(NULL == hash_table);
2514215976Sjmallett	hash_table = emalloc_zero(cb);
2515215976Sjmallett
2516215976Sjmallett	c_mru_l_rc = FALSE;
2517215976Sjmallett	list_complete = FALSE;
2518215976Sjmallett	have_now = FALSE;
2519215976Sjmallett	cap_frags = TRUE;
2520215976Sjmallett	got = 0;
2521215976Sjmallett	ri = 0;
2522215976Sjmallett	cb = sizeof(*mon);
2523215976Sjmallett	mon = emalloc_zero(cb);
2524215976Sjmallett	ZERO(*pnow);
2525215976Sjmallett	ZERO(last_older);
2526215976Sjmallett	next_report = time(NULL) + MRU_REPORT_SECS;
2527215976Sjmallett
2528215976Sjmallett	limit = min(3 * MAXFRAGS, ntpd_row_limit);
2529215976Sjmallett	frags = MAXFRAGS;
2530215976Sjmallett	snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s",
2531215976Sjmallett		 nonce, frags, parms);
2532215976Sjmallett	nonce_uses++;
2533215976Sjmallett
2534215976Sjmallett	while (TRUE) {
2535215976Sjmallett		if (debug)
2536215976Sjmallett			fprintf(stderr, "READ_MRU parms: %s\n", req_buf);
2537215976Sjmallett
2538215976Sjmallett		qres = doqueryex(CTL_OP_READ_MRU, 0, 0,
2539215976Sjmallett				 strlen(req_buf), req_buf,
2540215976Sjmallett				 &rstatus, &rsize, &rdata, TRUE);
2541215976Sjmallett
2542215976Sjmallett		if (CERR_UNKNOWNVAR == qres && ri > 0) {
2543215976Sjmallett			/*
2544215976Sjmallett			 * None of the supplied prior entries match, so
2545215976Sjmallett			 * toss them from our list and try again.
2546215976Sjmallett			 */
2547215976Sjmallett			if (debug)
2548215976Sjmallett				fprintf(stderr,
2549215976Sjmallett					"no overlap between %d prior entries and server MRU list\n",
2550215976Sjmallett					ri);
2551215976Sjmallett			while (ri--) {
2552215976Sjmallett				recent = HEAD_DLIST(mru_list, mlink);
2553215976Sjmallett				INSIST(recent != NULL);
2554215976Sjmallett				if (debug)
2555215976Sjmallett					fprintf(stderr,
2556215976Sjmallett						"tossing prior entry %s to resync\n",
2557215976Sjmallett						sptoa(&recent->addr));
2558215976Sjmallett				UNLINK_DLIST(recent, mlink);
2559215976Sjmallett				hash = NTP_HASH_ADDR(&recent->addr);
2560215976Sjmallett				UNLINK_SLIST(unlinked, hash_table[hash],
2561215976Sjmallett					     recent, hlink, mru);
2562215976Sjmallett				INSIST(unlinked == recent);
2563215976Sjmallett				free(recent);
2564215976Sjmallett				mru_count--;
2565215976Sjmallett			}
2566215976Sjmallett			if (NULL == HEAD_DLIST(mru_list, mlink)) {
2567215976Sjmallett				restarted_count++;
2568215976Sjmallett				if (restarted_count > 8) {
2569215976Sjmallett					fprintf(stderr,
2570215976Sjmallett						"Giving up after 8 restarts from the beginning.\n"
2571215976Sjmallett						"With high-traffic NTP servers, this can occur if the\n"
2572215976Sjmallett						"MRU list is limited to less than about 16 seconds' of\n"
2573215976Sjmallett						"entries.  See the 'mru' ntp.conf directive to adjust.\n");
2574215976Sjmallett					goto cleanup_return;
2575215976Sjmallett				}
2576215976Sjmallett				if (debug)
2577215976Sjmallett					fprintf(stderr,
2578215976Sjmallett						"--->   Restarting from the beginning, retry #%u\n",
2579215976Sjmallett						restarted_count);
2580215976Sjmallett			}
2581215976Sjmallett		} else if (CERR_UNKNOWNVAR == qres) {
2582215976Sjmallett			fprintf(stderr,
2583215976Sjmallett				"CERR_UNKNOWNVAR from ntpd but no priors given.\n");
2584215976Sjmallett			goto cleanup_return;
2585215976Sjmallett		} else if (CERR_BADVALUE == qres) {
2586215976Sjmallett			if (cap_frags) {
2587215976Sjmallett				cap_frags = FALSE;
2588215976Sjmallett				if (debug)
2589215976Sjmallett					fprintf(stderr,
2590215976Sjmallett						"Reverted to row limit from fragments limit.\n");
2591215976Sjmallett			} else {
2592215976Sjmallett				/* ntpd has lower cap on row limit */
2593215976Sjmallett				ntpd_row_limit--;
2594215976Sjmallett				limit = min(limit, ntpd_row_limit);
2595215976Sjmallett				if (debug)
2596215976Sjmallett					fprintf(stderr,
2597215976Sjmallett						"Row limit reduced to %d following CERR_BADVALUE.\n",
2598215976Sjmallett						limit);
2599215976Sjmallett			}
2600215976Sjmallett		} else if (ERR_INCOMPLETE == qres ||
2601215976Sjmallett			   ERR_TIMEOUT == qres) {
2602215976Sjmallett			/*
2603215976Sjmallett			 * Reduce the number of rows/frags requested by
2604215976Sjmallett			 * half to recover from lost response fragments.
2605215976Sjmallett			 */
2606215976Sjmallett			if (cap_frags) {
2607215976Sjmallett				frags = max(2, frags / 2);
2608215976Sjmallett				if (debug)
2609215976Sjmallett					fprintf(stderr,
2610215976Sjmallett						"Frag limit reduced to %d following incomplete response.\n",
2611215976Sjmallett						frags);
2612215976Sjmallett			} else {
2613215976Sjmallett				limit = max(2, limit / 2);
2614215976Sjmallett				if (debug)
2615215976Sjmallett					fprintf(stderr,
2616215976Sjmallett						"Row limit reduced to %d following incomplete response.\n",
2617215976Sjmallett						limit);
2618215976Sjmallett			}
2619215976Sjmallett		} else if (qres) {
2620215976Sjmallett			show_error_msg(qres, 0);
2621215976Sjmallett			goto cleanup_return;
2622215976Sjmallett		}
2623215976Sjmallett		/*
2624215976Sjmallett		 * This is a cheap cop-out implementation of rawmode
2625215976Sjmallett		 * output for mrulist.  A better approach would be to
2626215976Sjmallett		 * dump similar output after the list is collected by
2627215976Sjmallett		 * ntpq with a continuous sequence of indexes.  This
2628215976Sjmallett		 * cheap approach has indexes resetting to zero for
2629215976Sjmallett		 * each query/response, and duplicates are not
2630215976Sjmallett		 * coalesced.
2631215976Sjmallett		 */
2632215976Sjmallett		if (!qres && rawmode)
2633215976Sjmallett			printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout);
2634215976Sjmallett		ci = 0;
2635215976Sjmallett		have_addr_older = FALSE;
2636215976Sjmallett		have_last_older = FALSE;
2637215976Sjmallett		while (!qres && nextvar(&rsize, &rdata, &tag, &val)) {
2638215976Sjmallett			if (debug > 1)
2639215976Sjmallett				fprintf(stderr, "nextvar gave: %s = %s\n",
2640215976Sjmallett					tag, val);
2641215976Sjmallett			switch(tag[0]) {
2642215976Sjmallett
2643215976Sjmallett			case 'a':
2644215976Sjmallett				if (!strcmp(tag, "addr.older")) {
2645215976Sjmallett					if (!have_last_older) {
2646215976Sjmallett						fprintf(stderr,
2647215976Sjmallett							"addr.older %s before last.older\n",
2648215976Sjmallett							val);
2649215976Sjmallett						goto cleanup_return;
2650215976Sjmallett					}
2651215976Sjmallett					if (!decodenetnum(val, &addr_older)) {
2652215976Sjmallett						fprintf(stderr,
2653215976Sjmallett							"addr.older %s garbled\n",
2654215976Sjmallett							val);
2655215976Sjmallett						goto cleanup_return;
2656215976Sjmallett					}
2657215976Sjmallett					hash = NTP_HASH_ADDR(&addr_older);
2658215976Sjmallett					for (recent = hash_table[hash];
2659215976Sjmallett					     recent != NULL;
2660215976Sjmallett					     recent = recent->hlink)
2661215976Sjmallett						if (ADDR_PORT_EQ(
2662215976Sjmallett						      &addr_older,
2663215976Sjmallett						      &recent->addr))
2664215976Sjmallett							break;
2665215976Sjmallett					if (NULL == recent) {
2666215976Sjmallett						fprintf(stderr,
2667215976Sjmallett							"addr.older %s not in hash table\n",
2668215976Sjmallett							val);
2669215976Sjmallett						goto cleanup_return;
2670215976Sjmallett					}
2671215976Sjmallett					if (!L_ISEQU(&last_older,
2672215976Sjmallett						     &recent->last)) {
2673215976Sjmallett						fprintf(stderr,
2674215976Sjmallett							"last.older %08x.%08x mismatches %08x.%08x expected.\n",
2675215976Sjmallett							last_older.l_ui,
2676215976Sjmallett							last_older.l_uf,
2677215976Sjmallett							recent->last.l_ui,
2678215976Sjmallett							recent->last.l_uf);
2679215976Sjmallett						goto cleanup_return;
2680215976Sjmallett					}
2681215976Sjmallett					have_addr_older = TRUE;
2682215976Sjmallett				} else if (1 != sscanf(tag, "addr.%d", &si)
2683215976Sjmallett					   || si != ci)
2684215976Sjmallett					goto nomatch;
2685215976Sjmallett				else if (decodenetnum(val, &mon->addr))
2686215976Sjmallett					MGOT(MRU_GOT_ADDR);
2687215976Sjmallett				break;
2688215976Sjmallett
2689215976Sjmallett			case 'l':
2690215976Sjmallett				if (!strcmp(tag, "last.older")) {
2691215976Sjmallett					if ('0' != val[0] ||
2692215976Sjmallett					    'x' != val[1] ||
2693215976Sjmallett					    !hextolfp(val + 2, &last_older)) {
2694215976Sjmallett						fprintf(stderr,
2695215976Sjmallett							"last.older %s garbled\n",
2696215976Sjmallett							val);
2697215976Sjmallett						goto cleanup_return;
2698215976Sjmallett					}
2699215976Sjmallett					have_last_older = TRUE;
2700215976Sjmallett				} else if (!strcmp(tag, "last.newest")) {
2701215976Sjmallett					if (0 != got) {
2702215976Sjmallett						fprintf(stderr,
2703215976Sjmallett							"last.newest %s before complete row, got = 0x%x\n",
2704215976Sjmallett							val, (u_int)got);
2705215976Sjmallett						goto cleanup_return;
2706215976Sjmallett					}
2707215976Sjmallett					if (!have_now) {
2708215976Sjmallett						fprintf(stderr,
2709215976Sjmallett							"last.newest %s before now=\n",
2710215976Sjmallett							val);
2711215976Sjmallett						goto cleanup_return;
2712215976Sjmallett					}
2713215976Sjmallett					head = HEAD_DLIST(mru_list, mlink);
2714215976Sjmallett					if (NULL != head) {
2715215976Sjmallett						if ('0' != val[0] ||
2716215976Sjmallett						    'x' != val[1] ||
2717215976Sjmallett						    !hextolfp(val + 2, &newest) ||
2718215976Sjmallett						    !L_ISEQU(&newest,
2719215976Sjmallett							     &head->last)) {
2720215976Sjmallett							fprintf(stderr,
2721215976Sjmallett								"last.newest %s mismatches %08x.%08x",
2722215976Sjmallett								val,
2723215976Sjmallett								head->last.l_ui,
2724215976Sjmallett								head->last.l_uf);
2725215976Sjmallett							goto cleanup_return;
2726215976Sjmallett						}
2727215976Sjmallett					}
2728215976Sjmallett					list_complete = TRUE;
2729215976Sjmallett				} else if (1 != sscanf(tag, "last.%d", &si) ||
2730215976Sjmallett					   si != ci || '0' != val[0] ||
2731215976Sjmallett					   'x' != val[1] ||
2732215976Sjmallett					   !hextolfp(val + 2, &mon->last)) {
2733215976Sjmallett					goto nomatch;
2734215976Sjmallett				} else {
2735215976Sjmallett					MGOT(MRU_GOT_LAST);
2736215976Sjmallett					/*
2737215976Sjmallett					 * allow interrupted retrieval,
2738215976Sjmallett					 * using most recent retrieved
2739215976Sjmallett					 * entry's last seen timestamp
2740215976Sjmallett					 * as the end of operation.
2741215976Sjmallett					 */
2742215976Sjmallett					*pnow = mon->last;
2743215976Sjmallett				}
2744215976Sjmallett				break;
2745215976Sjmallett
2746215976Sjmallett			case 'f':
2747215976Sjmallett				if (1 != sscanf(tag, "first.%d", &si) ||
2748215976Sjmallett				    si != ci || '0' != val[0] ||
2749215976Sjmallett				    'x' != val[1] ||
2750215976Sjmallett				    !hextolfp(val + 2, &mon->first))
2751215976Sjmallett					goto nomatch;
2752215976Sjmallett				MGOT(MRU_GOT_FIRST);
2753215976Sjmallett				break;
2754215976Sjmallett
2755215976Sjmallett			case 'n':
2756215976Sjmallett				if (!strcmp(tag, "nonce")) {
2757215976Sjmallett					strlcpy(nonce, val, sizeof(nonce));
2758215976Sjmallett					nonce_uses = 0;
2759215976Sjmallett					break; /* case */
2760215976Sjmallett				} else if (strcmp(tag, "now") ||
2761215976Sjmallett					   '0' != val[0] ||
2762215976Sjmallett					   'x' != val[1] ||
2763215976Sjmallett					    !hextolfp(val + 2, pnow))
2764215976Sjmallett					goto nomatch;
2765215976Sjmallett				have_now = TRUE;
2766215976Sjmallett				break;
2767215976Sjmallett
2768215976Sjmallett			case 'c':
2769215976Sjmallett				if (1 != sscanf(tag, "ct.%d", &si) ||
2770215976Sjmallett				    si != ci ||
2771215976Sjmallett				    1 != sscanf(val, "%d", &mon->count)
2772215976Sjmallett				    || mon->count < 1)
2773215976Sjmallett					goto nomatch;
2774215976Sjmallett				MGOT(MRU_GOT_COUNT);
2775215976Sjmallett				break;
2776215976Sjmallett
2777215976Sjmallett			case 'm':
2778215976Sjmallett				if (1 != sscanf(tag, "mv.%d", &si) ||
2779215976Sjmallett				    si != ci ||
2780215976Sjmallett				    1 != sscanf(val, "%d", &mv))
2781215976Sjmallett					goto nomatch;
2782215976Sjmallett				mon->mode = PKT_MODE(mv);
2783215976Sjmallett				mon->ver = PKT_VERSION(mv);
2784215976Sjmallett				MGOT(MRU_GOT_MV);
2785215976Sjmallett				break;
2786215976Sjmallett
2787215976Sjmallett			case 'r':
2788215976Sjmallett				if (1 != sscanf(tag, "rs.%d", &si) ||
2789215976Sjmallett				    si != ci ||
2790215976Sjmallett				    1 != sscanf(val, "0x%hx", &mon->rs))
2791215976Sjmallett					goto nomatch;
2792215976Sjmallett				MGOT(MRU_GOT_RS);
2793215976Sjmallett				break;
2794215976Sjmallett
2795215976Sjmallett			default:
2796215976Sjmallett			nomatch:
2797215976Sjmallett				/* empty stmt */ ;
2798215976Sjmallett				/* ignore unknown tags */
2799215976Sjmallett			}
2800215976Sjmallett		}
2801215976Sjmallett		if (have_now)
2802215976Sjmallett			list_complete = TRUE;
2803215976Sjmallett		if (list_complete) {
2804215976Sjmallett			INSIST(0 == ri || have_addr_older);
2805215976Sjmallett		}
2806215976Sjmallett		if (mrulist_interrupted) {
2807215976Sjmallett			printf("mrulist retrieval interrupted by operator.\n"
2808215976Sjmallett			       "Displaying partial client list.\n");
2809215976Sjmallett			fflush(stdout);
2810215976Sjmallett		}
2811215976Sjmallett		if (list_complete || mrulist_interrupted) {
2812215976Sjmallett			fprintf(stderr,
2813215976Sjmallett				"\rRetrieved %u unique MRU entries and %u updates.\n",
2814215976Sjmallett				mru_count, mru_dupes);
2815215976Sjmallett			fflush(stderr);
2816215976Sjmallett			break;
2817215976Sjmallett		}
2818215976Sjmallett		if (time(NULL) >= next_report) {
2819215976Sjmallett			next_report += MRU_REPORT_SECS;
2820215976Sjmallett			fprintf(stderr, "\r%u (%u updates) ", mru_count,
2821215976Sjmallett				mru_dupes);
2822215976Sjmallett			fflush(stderr);
2823215976Sjmallett		}
2824215976Sjmallett
2825215976Sjmallett		/*
2826215976Sjmallett		 * Snooze for a bit between queries to let ntpd catch
2827215976Sjmallett		 * up with other duties.
2828215976Sjmallett		 */
2829215976Sjmallett#ifdef SYS_WINNT
2830215976Sjmallett		Sleep(sleep_msecs);
2831215976Sjmallett#elif !defined(HAVE_NANOSLEEP)
2832215976Sjmallett		sleep((sleep_msecs / 1000) + 1);
2833215976Sjmallett#else
2834215976Sjmallett		{
2835215976Sjmallett			struct timespec interv = { 0,
2836215976Sjmallett						   1000 * sleep_msecs };
2837215976Sjmallett			nanosleep(&interv, NULL);
2838215976Sjmallett		}
2839215976Sjmallett#endif
2840215976Sjmallett		/*
2841215976Sjmallett		 * If there were no errors, increase the number of rows
2842215976Sjmallett		 * to a maximum of 3 * MAXFRAGS (the most packets ntpq
2843215976Sjmallett		 * can handle in one response), on the assumption that
2844215976Sjmallett		 * no less than 3 rows fit in each packet, capped at
2845215976Sjmallett		 * our best guess at the server's row limit.
2846215976Sjmallett		 */
2847215976Sjmallett		if (!qres) {
2848215976Sjmallett			if (cap_frags) {
2849215976Sjmallett				frags = min(MAXFRAGS, frags + 1);
2850215976Sjmallett			} else {
2851215976Sjmallett				limit = min3(3 * MAXFRAGS,
2852215976Sjmallett					     ntpd_row_limit,
2853215976Sjmallett					     max(limit + 1,
2854215976Sjmallett					         limit * 33 / 32));
2855215976Sjmallett			}
2856215976Sjmallett		}
2857215976Sjmallett		/*
2858215976Sjmallett		 * prepare next query with as many address and last-seen
2859215976Sjmallett		 * timestamps as will fit in a single packet.
2860215976Sjmallett		 */
2861215976Sjmallett		req = req_buf;
2862215976Sjmallett		req_end = req_buf + sizeof(req_buf);
2863215976Sjmallett#define REQ_ROOM	(req_end - req)
2864215976Sjmallett		snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce,
2865215976Sjmallett			 (cap_frags)
2866215976Sjmallett			     ? "frags"
2867215976Sjmallett			     : "limit",
2868215976Sjmallett			 (cap_frags)
2869215976Sjmallett			     ? frags
2870215976Sjmallett			     : limit,
2871215976Sjmallett			 parms);
2872215976Sjmallett		req += strlen(req);
2873215976Sjmallett		nonce_uses++;
2874215976Sjmallett		if (nonce_uses >= 4) {
2875215976Sjmallett			if (!fetch_nonce(nonce, sizeof(nonce)))
2876215976Sjmallett				goto cleanup_return;
2877215976Sjmallett			nonce_uses = 0;
2878215976Sjmallett		}
2879215976Sjmallett
2880215976Sjmallett
2881215976Sjmallett		for (ri = 0, recent = HEAD_DLIST(mru_list, mlink);
2882215976Sjmallett		     recent != NULL;
2883215976Sjmallett		     ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) {
2884215976Sjmallett
2885215976Sjmallett			snprintf(buf, sizeof(buf),
2886215976Sjmallett				 ", addr.%d=%s, last.%d=0x%08x.%08x",
2887215976Sjmallett				 ri, sptoa(&recent->addr), ri,
2888215976Sjmallett				 recent->last.l_ui, recent->last.l_uf);
2889215976Sjmallett			chars = strlen(buf);
2890215976Sjmallett			if ((size_t)REQ_ROOM <= chars)
2891215976Sjmallett				break;
2892215976Sjmallett			memcpy(req, buf, chars + 1);
2893215976Sjmallett			req += chars;
2894215976Sjmallett		}
2895215976Sjmallett	}
2896215976Sjmallett
2897215976Sjmallett	c_mru_l_rc = TRUE;
2898215976Sjmallett	goto retain_hash_table;
2899215976Sjmallett
2900215976Sjmallettcleanup_return:
2901215976Sjmallett	free(hash_table);
2902215976Sjmallett	hash_table = NULL;
2903215976Sjmallett
2904215976Sjmallettretain_hash_table:
2905215976Sjmallett	if (mon != NULL)
2906215976Sjmallett		free(mon);
2907215976Sjmallett
2908215976Sjmallett	return c_mru_l_rc;
2909215976Sjmallett}
2910215976Sjmallett
2911215976Sjmallett
2912215976Sjmallett/*
2913215976Sjmallett * qcmp_mru_addr - sort MRU entries by remote address.
2914215976Sjmallett *
2915215976Sjmallett * All IPv4 addresses sort before any IPv6, addresses are sorted by
2916215976Sjmallett * value within address family.
2917215976Sjmallett */
2918215976Sjmallettstatic int
2919215976Sjmallettqcmp_mru_addr(
2920215976Sjmallett	const void *v1,
2921215976Sjmallett	const void *v2
2922215976Sjmallett	)
2923215976Sjmallett{
2924215976Sjmallett	const mru * const *	ppm1 = v1;
2925215976Sjmallett	const mru * const *	ppm2 = v2;
2926215976Sjmallett	const mru *		pm1;
2927215976Sjmallett	const mru *		pm2;
2928215976Sjmallett	u_short			af1;
2929215976Sjmallett	u_short			af2;
2930215976Sjmallett	size_t			cmplen;
2931215976Sjmallett	size_t			addr_off;
2932215976Sjmallett
2933215976Sjmallett	pm1 = *ppm1;
2934215976Sjmallett	pm2 = *ppm2;
2935215976Sjmallett
2936215976Sjmallett	af1 = AF(&pm1->addr);
2937215976Sjmallett	af2 = AF(&pm2->addr);
2938215976Sjmallett
2939215976Sjmallett	if (af1 != af2)
2940215976Sjmallett		return (AF_INET == af1)
2941215976Sjmallett			   ? -1
2942215976Sjmallett			   : 1;
2943215976Sjmallett
2944215976Sjmallett	cmplen = SIZEOF_INADDR(af1);
2945215976Sjmallett	addr_off = (AF_INET == af1)
2946215976Sjmallett		      ? offsetof(struct sockaddr_in, sin_addr)
2947215976Sjmallett		      : offsetof(struct sockaddr_in6, sin6_addr);
2948215976Sjmallett
2949215976Sjmallett	return memcmp((const char *)&pm1->addr + addr_off,
2950215976Sjmallett		      (const char *)&pm2->addr + addr_off,
2951215976Sjmallett		      cmplen);
2952215976Sjmallett}
2953215976Sjmallett
2954215976Sjmallett
2955215976Sjmallettstatic int
2956215976Sjmallettqcmp_mru_r_addr(
2957215976Sjmallett	const void *v1,
2958215976Sjmallett	const void *v2
2959215976Sjmallett	)
2960215976Sjmallett{
2961215976Sjmallett	return -qcmp_mru_addr(v1, v2);
2962215976Sjmallett}
2963215976Sjmallett
2964215976Sjmallett
2965215976Sjmallett/*
2966215976Sjmallett * qcmp_mru_count - sort MRU entries by times seen (hit count).
2967215976Sjmallett */
2968215976Sjmallettstatic int
2969215976Sjmallettqcmp_mru_count(
2970215976Sjmallett	const void *v1,
2971215976Sjmallett	const void *v2
2972215976Sjmallett	)
2973215976Sjmallett{
2974215976Sjmallett	const mru * const *	ppm1 = v1;
2975215976Sjmallett	const mru * const *	ppm2 = v2;
2976215976Sjmallett	const mru *		pm1;
2977215976Sjmallett	const mru *		pm2;
2978215976Sjmallett
2979215976Sjmallett	pm1 = *ppm1;
2980215976Sjmallett	pm2 = *ppm2;
2981215976Sjmallett
2982215976Sjmallett	return (pm1->count < pm2->count)
2983215976Sjmallett		   ? -1
2984215976Sjmallett		   : ((pm1->count == pm2->count)
2985215976Sjmallett			  ? 0
2986215976Sjmallett			  : 1);
2987215976Sjmallett}
2988215976Sjmallett
2989215976Sjmallett
2990215976Sjmallettstatic int
2991215976Sjmallettqcmp_mru_r_count(
2992215976Sjmallett	const void *v1,
2993215976Sjmallett	const void *v2
2994215976Sjmallett	)
2995215976Sjmallett{
2996215976Sjmallett	return -qcmp_mru_count(v1, v2);
2997215976Sjmallett}
2998215976Sjmallett
2999215976Sjmallett
3000215976Sjmallett/*
3001215976Sjmallett * qcmp_mru_avgint - sort MRU entries by average interval.
3002215976Sjmallett */
3003215976Sjmallettstatic int
3004215976Sjmallettqcmp_mru_avgint(
3005215976Sjmallett	const void *v1,
3006215976Sjmallett	const void *v2
3007215976Sjmallett	)
3008215976Sjmallett{
3009215976Sjmallett	const mru * const *	ppm1 = v1;
3010215976Sjmallett	const mru * const *	ppm2 = v2;
3011215976Sjmallett	const mru *		pm1;
3012215976Sjmallett	const mru *		pm2;
3013215976Sjmallett	l_fp			interval;
3014215976Sjmallett	double			avg1;
3015215976Sjmallett	double			avg2;
3016215976Sjmallett
3017215976Sjmallett	pm1 = *ppm1;
3018215976Sjmallett	pm2 = *ppm2;
3019215976Sjmallett
3020215976Sjmallett	interval = pm1->last;
3021215976Sjmallett	L_SUB(&interval, &pm1->first);
3022215976Sjmallett	LFPTOD(&interval, avg1);
3023215976Sjmallett	avg1 /= pm1->count;
3024215976Sjmallett
3025215976Sjmallett	interval = pm2->last;
3026215976Sjmallett	L_SUB(&interval, &pm2->first);
3027215976Sjmallett	LFPTOD(&interval, avg2);
3028215976Sjmallett	avg2 /= pm2->count;
3029215976Sjmallett
3030215976Sjmallett	if (avg1 < avg2)
3031215976Sjmallett		return -1;
3032215976Sjmallett	else if (avg1 > avg2)
3033215976Sjmallett		return 1;
3034215976Sjmallett
3035215976Sjmallett	/* secondary sort on lstint - rarely tested */
3036215976Sjmallett	if (L_ISEQU(&pm1->last, &pm2->last))
3037215976Sjmallett		return 0;
3038215976Sjmallett	else if (L_ISGEQ(&pm1->last, &pm2->last))
3039215976Sjmallett		return -1;
3040215976Sjmallett	else
3041215976Sjmallett		return 1;
3042215976Sjmallett}
3043215976Sjmallett
3044215976Sjmallett
3045215976Sjmallettstatic int
3046215976Sjmallettqcmp_mru_r_avgint(
3047215976Sjmallett	const void *v1,
3048215976Sjmallett	const void *v2
3049215976Sjmallett	)
3050215976Sjmallett{
3051215976Sjmallett	return -qcmp_mru_avgint(v1, v2);
3052215976Sjmallett}
3053215976Sjmallett
3054215976Sjmallett
3055215976Sjmallett/*
3056215976Sjmallett * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most
3057215976Sjmallett *	     Recently Used (seen) remote address list from ntpd.
3058215976Sjmallett *
3059215976Sjmallett * Similar to ntpdc's monlist command, but not limited to a single
3060215976Sjmallett * request/response, and thereby not limited to a few hundred remote
3061215976Sjmallett * addresses.
3062215976Sjmallett *
3063215976Sjmallett * See ntpd/ntp_control.c read_mru_list() for comments on the way
3064215976Sjmallett * CTL_OP_READ_MRU is designed to be used.
3065215976Sjmallett *
3066215976Sjmallett * mrulist intentionally differs from monlist in the way the avgint
3067215976Sjmallett * column is calculated.  monlist includes the time after the last
3068215976Sjmallett * packet from the client until the monlist query time in the average,
3069215976Sjmallett * while mrulist excludes it.  That is, monlist's average interval grows
3070215976Sjmallett * over time for remote addresses not heard from in some time, while it
3071215976Sjmallett * remains unchanged in mrulist.  This also affects the avgint value for
3072215976Sjmallett * entries representing a single packet, with identical first and last
3073215976Sjmallett * timestamps.  mrulist shows 0 avgint, monlist shows a value identical
3074215976Sjmallett * to lstint.
3075215976Sjmallett */
3076215976Sjmallettstatic void
3077215976Sjmallettmrulist(
3078215976Sjmallett	struct parse *	pcmd,
3079215976Sjmallett	FILE *		fp
3080215976Sjmallett	)
3081215976Sjmallett{
3082215976Sjmallett	const char mincount_eq[] =	"mincount=";
3083215976Sjmallett	const char resall_eq[] =	"resall=";
3084215976Sjmallett	const char resany_eq[] =	"resany=";
3085215976Sjmallett	const char maxlstint_eq[] =	"maxlstint=";
3086215976Sjmallett	const char laddr_eq[] =		"laddr=";
3087215976Sjmallett	const char sort_eq[] =		"sort=";
3088215976Sjmallett	mru_sort_order order;
3089215976Sjmallett	size_t n;
3090215976Sjmallett	char parms_buf[128];
3091215976Sjmallett	char buf[24];
3092215976Sjmallett	char *parms;
3093215976Sjmallett	const char *arg;
3094215976Sjmallett	size_t cb;
3095215976Sjmallett	mru **sorted;
3096215976Sjmallett	mru **ppentry;
3097215976Sjmallett	mru *recent;
3098215976Sjmallett	l_fp now;
3099215976Sjmallett	l_fp interval;
3100215976Sjmallett	double favgint;
3101215976Sjmallett	double flstint;
3102215976Sjmallett	int avgint;
3103215976Sjmallett	int lstint;
3104215976Sjmallett	size_t i;
3105215976Sjmallett
3106215976Sjmallett	mrulist_interrupted = FALSE;
3107215976Sjmallett	push_ctrl_c_handler(&mrulist_ctrl_c_hook);
3108215976Sjmallett	fprintf(stderr,
3109215976Sjmallett		"Ctrl-C will stop MRU retrieval and display partial results.\n");
3110215976Sjmallett	fflush(stderr);
3111215976Sjmallett
3112215976Sjmallett	order = MRUSORT_DEF;
3113215976Sjmallett	parms_buf[0] = '\0';
3114215976Sjmallett	parms = parms_buf;
3115215976Sjmallett	for (i = 0; i < pcmd->nargs; i++) {
3116215976Sjmallett		arg = pcmd->argval[i].string;
3117215976Sjmallett		if (arg != NULL) {
3118215976Sjmallett			cb = strlen(arg) + 1;
3119215976Sjmallett			if ((!strncmp(resall_eq, arg, sizeof(resall_eq)
3120215976Sjmallett			    - 1) || !strncmp(resany_eq, arg,
3121215976Sjmallett			    sizeof(resany_eq) - 1) || !strncmp(
3122215976Sjmallett			    mincount_eq, arg, sizeof(mincount_eq) - 1)
3123215976Sjmallett			    || !strncmp(laddr_eq, arg, sizeof(laddr_eq)
3124215976Sjmallett			    - 1) || !strncmp(maxlstint_eq, arg,
3125215976Sjmallett			    sizeof(laddr_eq) - 1)) && parms + cb + 2 <=
3126215976Sjmallett			    parms_buf + sizeof(parms_buf)) {
3127215976Sjmallett				/* these are passed intact to ntpd */
3128215976Sjmallett				memcpy(parms, ", ", 2);
3129215976Sjmallett				parms += 2;
3130215976Sjmallett				memcpy(parms, arg, cb);
3131215976Sjmallett				parms += cb - 1;
3132215976Sjmallett			} else if (!strncmp(sort_eq, arg,
3133215976Sjmallett					    sizeof(sort_eq) - 1)) {
3134215976Sjmallett				arg += sizeof(sort_eq) - 1;
3135215976Sjmallett				for (n = 0;
3136215976Sjmallett				     n < COUNTOF(mru_sort_keywords);
3137215976Sjmallett				     n++)
3138215976Sjmallett					if (!strcmp(mru_sort_keywords[n],
3139215976Sjmallett						    arg))
3140215976Sjmallett						break;
3141215976Sjmallett				if (n < COUNTOF(mru_sort_keywords))
3142215976Sjmallett					order = n;
3143215976Sjmallett			} else if (!strcmp("limited", arg) ||
3144215976Sjmallett				   !strcmp("kod", arg)) {
3145215976Sjmallett				/* transform to resany=... */
3146215976Sjmallett				snprintf(buf, sizeof(buf),
3147215976Sjmallett					 ", resany=0x%x",
3148215976Sjmallett					 ('k' == arg[0])
3149215976Sjmallett					     ? RES_KOD
3150215976Sjmallett					     : RES_LIMITED);
3151215976Sjmallett				cb = 1 + strlen(buf);
3152215976Sjmallett				if (parms + cb <
3153215976Sjmallett					parms_buf + sizeof(parms_buf)) {
3154215976Sjmallett					memcpy(parms, buf, cb);
3155215976Sjmallett					parms += cb - 1;
3156215976Sjmallett				}
3157215976Sjmallett			} else
3158215976Sjmallett				fprintf(stderr,
3159215976Sjmallett					"ignoring unrecognized mrulist parameter: %s\n",
3160215976Sjmallett					arg);
3161215976Sjmallett		}
3162215976Sjmallett	}
3163215976Sjmallett	parms = parms_buf;
3164215976Sjmallett
3165215976Sjmallett	if (!collect_mru_list(parms, &now))
3166215976Sjmallett		return;
3167215976Sjmallett
3168215976Sjmallett	/* display the results */
3169215976Sjmallett	if (rawmode)
3170215976Sjmallett		goto cleanup_return;
3171215976Sjmallett
3172215976Sjmallett	/* construct an array of entry pointers in default order */
3173215976Sjmallett	sorted = eallocarray(mru_count, sizeof(*sorted));
3174215976Sjmallett	ppentry = sorted;
3175215976Sjmallett	if (MRUSORT_R_DEF != order) {
3176215976Sjmallett		ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3177215976Sjmallett			INSIST(ppentry < sorted + mru_count);
3178215976Sjmallett			*ppentry = recent;
3179215976Sjmallett			ppentry++;
3180215976Sjmallett		ITER_DLIST_END()
3181215976Sjmallett	} else {
3182215976Sjmallett		REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3183215976Sjmallett			INSIST(ppentry < sorted + mru_count);
3184215976Sjmallett			*ppentry = recent;
3185215976Sjmallett			ppentry++;
3186215976Sjmallett		REV_ITER_DLIST_END()
3187215976Sjmallett	}
3188215976Sjmallett
3189215976Sjmallett	if (ppentry - sorted != (int)mru_count) {
3190215976Sjmallett		fprintf(stderr,
3191215976Sjmallett			"mru_count %u should match MRU list depth %ld.\n",
3192215976Sjmallett			mru_count, (long)(ppentry - sorted));
3193215976Sjmallett		free(sorted);
3194215976Sjmallett		goto cleanup_return;
3195215976Sjmallett	}
3196215976Sjmallett
3197215976Sjmallett	/* re-sort sorted[] if not default or reverse default */
3198215976Sjmallett	if (MRUSORT_R_DEF < order)
3199215976Sjmallett		qsort(sorted, mru_count, sizeof(sorted[0]),
3200215976Sjmallett		      mru_qcmp_table[order]);
3201215976Sjmallett
3202215976Sjmallett	mrulist_interrupted = FALSE;
3203215976Sjmallett	printf(	"lstint avgint rstr r m v  count rport remote address\n"
3204215976Sjmallett		"==============================================================================\n");
3205215976Sjmallett		/* '=' x 78 */
3206215976Sjmallett	for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) {
3207215976Sjmallett		recent = *ppentry;
3208215976Sjmallett		interval = now;
3209215976Sjmallett		L_SUB(&interval, &recent->last);
3210215976Sjmallett		LFPTOD(&interval, flstint);
3211215976Sjmallett		lstint = (int)(flstint + 0.5);
3212215976Sjmallett		interval = recent->last;
3213215976Sjmallett		L_SUB(&interval, &recent->first);
3214215976Sjmallett		LFPTOD(&interval, favgint);
3215215976Sjmallett		favgint /= recent->count;
3216215976Sjmallett		avgint = (int)(favgint + 0.5);
3217215976Sjmallett		fprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n",
3218215976Sjmallett			lstint, avgint, recent->rs,
3219215976Sjmallett			(RES_KOD & recent->rs)
3220215976Sjmallett			    ? 'K'
3221215976Sjmallett			    : (RES_LIMITED & recent->rs)
3222215976Sjmallett				  ? 'L'
3223215976Sjmallett				  : '.',
3224215976Sjmallett			(int)recent->mode, (int)recent->ver,
3225215976Sjmallett			recent->count, SRCPORT(&recent->addr),
3226215976Sjmallett			nntohost(&recent->addr));
3227215976Sjmallett		if (showhostnames)
3228215976Sjmallett			fflush(fp);
3229215976Sjmallett		if (mrulist_interrupted) {
3230215976Sjmallett			fputs("\n --interrupted--\n", fp);
3231215976Sjmallett			fflush(fp);
3232215976Sjmallett			break;
3233215976Sjmallett		}
3234215976Sjmallett	}
3235215976Sjmallett	fflush(fp);
3236215976Sjmallett	if (debug) {
3237215976Sjmallett		fprintf(stderr,
3238215976Sjmallett			"--- completed, freeing sorted[] pointers\n");
3239215976Sjmallett		fflush(stderr);
3240215976Sjmallett	}
3241215976Sjmallett	free(sorted);
3242215976Sjmallett
3243215976Sjmallettcleanup_return:
3244215976Sjmallett	if (debug) {
3245215976Sjmallett		fprintf(stderr, "... freeing MRU entries\n");
3246215976Sjmallett		fflush(stderr);
3247215976Sjmallett	}
3248215976Sjmallett	ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3249215976Sjmallett		free(recent);
3250215976Sjmallett	ITER_DLIST_END()
3251215976Sjmallett	if (debug) {
3252215976Sjmallett		fprintf(stderr, "... freeing hash_table[]\n");
3253215976Sjmallett		fflush(stderr);
3254215976Sjmallett	}
3255215976Sjmallett	free(hash_table);
3256215976Sjmallett	hash_table = NULL;
3257215976Sjmallett	INIT_DLIST(mru_list, mlink);
3258215976Sjmallett
3259215976Sjmallett	pop_ctrl_c_handler(&mrulist_ctrl_c_hook);
3260215976Sjmallett}
3261215976Sjmallett
3262215976Sjmallett
3263215976Sjmallett/*
3264215976Sjmallett * validate_ifnum - helper for ifstats()
3265215976Sjmallett *
3266215976Sjmallett * Ensures rows are received in order and complete.
3267215976Sjmallett */
3268215976Sjmallettstatic void
3269215976Sjmallettvalidate_ifnum(
3270215976Sjmallett	FILE *		fp,
3271215976Sjmallett	u_int		ifnum,
3272215976Sjmallett	int *		pfields,
3273215976Sjmallett	ifstats_row *	prow
3274215976Sjmallett	)
3275215976Sjmallett{
3276215976Sjmallett	if (prow->ifnum == ifnum)
3277215976Sjmallett		return;
3278215976Sjmallett	if (prow->ifnum + 1 <= ifnum) {
3279215976Sjmallett		if (*pfields < IFSTATS_FIELDS)
3280215976Sjmallett			fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3281215976Sjmallett				*pfields, IFSTATS_FIELDS);
3282215976Sjmallett		*pfields = 0;
3283215976Sjmallett		prow->ifnum = ifnum;
3284215976Sjmallett		return;
3285215976Sjmallett	}
3286215976Sjmallett	fprintf(stderr,
3287215976Sjmallett		"received if index %u, have %d of %d fields for index %u, aborting.\n",
3288215976Sjmallett		ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum);
3289215976Sjmallett	exit(1);
3290215976Sjmallett}
3291215976Sjmallett
3292215976Sjmallett
3293215976Sjmallett/*
3294215976Sjmallett * another_ifstats_field - helper for ifstats()
3295215976Sjmallett *
3296215976Sjmallett * If all fields for the row have been received, print it.
3297215976Sjmallett */
3298215976Sjmallettstatic void
3299215976Sjmallettanother_ifstats_field(
3300215976Sjmallett	int *		pfields,
3301215976Sjmallett	ifstats_row *	prow,
3302215976Sjmallett	FILE *		fp
3303215976Sjmallett	)
3304215976Sjmallett{
3305215976Sjmallett	u_int ifnum;
3306215976Sjmallett
3307215976Sjmallett	(*pfields)++;
3308215976Sjmallett	/* we understand 12 tags */
3309215976Sjmallett	if (IFSTATS_FIELDS > *pfields)
3310215976Sjmallett		return;
3311215976Sjmallett	/*
3312215976Sjmallett	"    interface name                                        send\n"
3313215976Sjmallett	" #  address/broadcast     drop flag ttl mc received sent failed peers   uptime\n"
3314215976Sjmallett	"==============================================================================\n");
3315215976Sjmallett	 */
3316215976Sjmallett	fprintf(fp,
3317215976Sjmallett		"%3u %-24.24s %c %4x %3d %2d %6d %6d %6d %5d %8d\n"
3318215976Sjmallett		"    %s\n",
3319215976Sjmallett		prow->ifnum, prow->name,
3320215976Sjmallett		(prow->enabled)
3321215976Sjmallett		    ? '.'
3322215976Sjmallett		    : 'D',
3323215976Sjmallett		prow->flags, prow->ttl, prow->mcast_count,
3324215976Sjmallett		prow->received, prow->sent, prow->send_errors,
3325215976Sjmallett		prow->peer_count, prow->uptime, sptoa(&prow->addr));
3326215976Sjmallett	if (!SOCK_UNSPEC(&prow->bcast))
3327215976Sjmallett		fprintf(fp, "    %s\n", sptoa(&prow->bcast));
3328215976Sjmallett	ifnum = prow->ifnum;
3329215976Sjmallett	ZERO(*prow);
3330215976Sjmallett	prow->ifnum = ifnum;
3331215976Sjmallett}
3332215976Sjmallett
3333215976Sjmallett
3334215976Sjmallett/*
3335215976Sjmallett * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats.
3336215976Sjmallett */
3337215976Sjmallettstatic void
3338215976Sjmallettifstats(
3339215976Sjmallett	struct parse *	pcmd,
3340215976Sjmallett	FILE *		fp
3341215976Sjmallett	)
3342215976Sjmallett{
3343215976Sjmallett	const char	addr_fmt[] =	"addr.%u";
3344215976Sjmallett	const char	bcast_fmt[] =	"bcast.%u";
3345215976Sjmallett	const char	en_fmt[] =	"en.%u";	/* enabled */
3346215976Sjmallett	const char	flags_fmt[] =	"flags.%u";
3347215976Sjmallett	const char	mc_fmt[] =	"mc.%u";	/* mcast count */
3348215976Sjmallett	const char	name_fmt[] =	"name.%u";
3349215976Sjmallett	const char	pc_fmt[] =	"pc.%u";	/* peer count */
3350215976Sjmallett	const char	rx_fmt[] =	"rx.%u";
3351215976Sjmallett	const char	tl_fmt[] =	"tl.%u";	/* ttl */
3352215976Sjmallett	const char	tx_fmt[] =	"tx.%u";
3353215976Sjmallett	const char	txerr_fmt[] =	"txerr.%u";
3354215976Sjmallett	const char	up_fmt[] =	"up.%u";	/* uptime */
3355215976Sjmallett	const char *	datap;
3356215976Sjmallett	int		qres;
3357215976Sjmallett	size_t		dsize;
3358215976Sjmallett	u_short		rstatus;
3359215976Sjmallett	char *		tag;
3360215976Sjmallett	char *		val;
3361215976Sjmallett	int		fields;
3362215976Sjmallett	u_int		ui;
3363215976Sjmallett	ifstats_row	row;
3364215976Sjmallett	int		comprende;
3365215976Sjmallett	size_t		len;
3366215976Sjmallett
3367215976Sjmallett	qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus,
3368215976Sjmallett		       &dsize, &datap);
3369215976Sjmallett	if (qres)	/* message already displayed */
3370215976Sjmallett		return;
3371215976Sjmallett
3372215976Sjmallett	fprintf(fp,
3373215976Sjmallett		"    interface name                                        send\n"
3374215976Sjmallett		" #  address/broadcast     drop flag ttl mc received sent failed peers   uptime\n"
3375215976Sjmallett		"==============================================================================\n");
3376215976Sjmallett		/* '=' x 78 */
3377215976Sjmallett
3378215976Sjmallett	ZERO(row);
3379215976Sjmallett	fields = 0;
3380215976Sjmallett	ui = 0;
3381215976Sjmallett	while (nextvar(&dsize, &datap, &tag, &val)) {
3382215976Sjmallett		if (debug > 1)
3383215976Sjmallett			fprintf(stderr, "nextvar gave: %s = %s\n", tag,
3384215976Sjmallett				(NULL == val)
3385215976Sjmallett				    ? ""
3386215976Sjmallett				    : val);
3387215976Sjmallett		comprende = FALSE;
3388215976Sjmallett		switch(tag[0]) {
3389215976Sjmallett
3390215976Sjmallett		case 'a':
3391215976Sjmallett			if (1 == sscanf(tag, addr_fmt, &ui) &&
3392215976Sjmallett			    decodenetnum(val, &row.addr))
3393215976Sjmallett				comprende = TRUE;
3394215976Sjmallett			break;
3395215976Sjmallett
3396215976Sjmallett		case 'b':
3397215976Sjmallett			if (1 == sscanf(tag, bcast_fmt, &ui) &&
3398215976Sjmallett			    (NULL == val ||
3399215976Sjmallett			     decodenetnum(val, &row.bcast)))
3400215976Sjmallett				comprende = TRUE;
3401215976Sjmallett			break;
3402215976Sjmallett
3403215976Sjmallett		case 'e':
3404215976Sjmallett			if (1 == sscanf(tag, en_fmt, &ui) &&
3405215976Sjmallett			    1 == sscanf(val, "%d", &row.enabled))
3406215976Sjmallett				comprende = TRUE;
3407215976Sjmallett			break;
3408215976Sjmallett
3409215976Sjmallett		case 'f':
3410215976Sjmallett			if (1 == sscanf(tag, flags_fmt, &ui) &&
3411215976Sjmallett			    1 == sscanf(val, "0x%x", &row.flags))
3412215976Sjmallett				comprende = TRUE;
3413215976Sjmallett			break;
3414215976Sjmallett
3415215976Sjmallett		case 'm':
3416215976Sjmallett			if (1 == sscanf(tag, mc_fmt, &ui) &&
3417215976Sjmallett			    1 == sscanf(val, "%d", &row.mcast_count))
3418215976Sjmallett				comprende = TRUE;
3419215976Sjmallett			break;
3420215976Sjmallett
3421215976Sjmallett		case 'n':
3422215976Sjmallett			if (1 == sscanf(tag, name_fmt, &ui)) {
3423215976Sjmallett				/* strip quotes */
3424215976Sjmallett				INSIST(val);
3425215976Sjmallett				len = strlen(val);
3426215976Sjmallett				if (len >= 2 &&
3427215976Sjmallett				    len - 2 < sizeof(row.name)) {
3428215976Sjmallett					len -= 2;
3429215976Sjmallett					memcpy(row.name, val + 1, len);
3430215976Sjmallett					row.name[len] = '\0';
3431215976Sjmallett					comprende = TRUE;
3432215976Sjmallett				}
3433215976Sjmallett			}
3434215976Sjmallett			break;
3435215976Sjmallett
3436215976Sjmallett		case 'p':
3437215976Sjmallett			if (1 == sscanf(tag, pc_fmt, &ui) &&
3438215976Sjmallett			    1 == sscanf(val, "%d", &row.peer_count))
3439215976Sjmallett				comprende = TRUE;
3440215976Sjmallett			break;
3441215976Sjmallett
3442215976Sjmallett		case 'r':
3443215976Sjmallett			if (1 == sscanf(tag, rx_fmt, &ui) &&
3444215976Sjmallett			    1 == sscanf(val, "%d", &row.received))
3445215976Sjmallett				comprende = TRUE;
3446215976Sjmallett			break;
3447215976Sjmallett
3448215976Sjmallett		case 't':
3449215976Sjmallett			if (1 == sscanf(tag, tl_fmt, &ui) &&
3450215976Sjmallett			    1 == sscanf(val, "%d", &row.ttl))
3451215976Sjmallett				comprende = TRUE;
3452215976Sjmallett			else if (1 == sscanf(tag, tx_fmt, &ui) &&
3453215976Sjmallett				 1 == sscanf(val, "%d", &row.sent))
3454215976Sjmallett				comprende = TRUE;
3455215976Sjmallett			else if (1 == sscanf(tag, txerr_fmt, &ui) &&
3456215976Sjmallett				 1 == sscanf(val, "%d", &row.send_errors))
3457215976Sjmallett				comprende = TRUE;
3458215976Sjmallett			break;
3459215976Sjmallett
3460215976Sjmallett		case 'u':
3461215976Sjmallett			if (1 == sscanf(tag, up_fmt, &ui) &&
3462215976Sjmallett			    1 == sscanf(val, "%d", &row.uptime))
3463215976Sjmallett				comprende = TRUE;
3464215976Sjmallett			break;
3465215976Sjmallett		}
3466215976Sjmallett
3467215976Sjmallett		if (comprende) {
3468215976Sjmallett			/* error out if rows out of order */
3469215976Sjmallett			validate_ifnum(fp, ui, &fields, &row);
3470215976Sjmallett			/* if the row is complete, print it */
3471215976Sjmallett			another_ifstats_field(&fields, &row, fp);
3472215976Sjmallett		}
3473215976Sjmallett	}
3474215976Sjmallett	if (fields != IFSTATS_FIELDS)
3475215976Sjmallett		fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3476215976Sjmallett			fields, IFSTATS_FIELDS);
3477215976Sjmallett
3478215976Sjmallett	fflush(fp);
3479215976Sjmallett}
3480215976Sjmallett
3481215976Sjmallett
3482215976Sjmallett/*
3483215976Sjmallett * validate_reslist_idx - helper for reslist()
3484215976Sjmallett *
3485215976Sjmallett * Ensures rows are received in order and complete.
3486215976Sjmallett */
3487215976Sjmallettstatic void
3488215976Sjmallettvalidate_reslist_idx(
3489215976Sjmallett	FILE *		fp,
3490215976Sjmallett	u_int		idx,
3491215976Sjmallett	int *		pfields,
3492215976Sjmallett	reslist_row *	prow
3493215976Sjmallett	)
3494215976Sjmallett{
3495215976Sjmallett	if (prow->idx == idx)
3496215976Sjmallett		return;
3497215976Sjmallett	if (prow->idx + 1 == idx) {
3498215976Sjmallett		if (*pfields < RESLIST_FIELDS)
3499215976Sjmallett			fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3500215976Sjmallett				*pfields, RESLIST_FIELDS);
3501215976Sjmallett		*pfields = 0;
3502215976Sjmallett		prow->idx = idx;
3503215976Sjmallett		return;
3504215976Sjmallett	}
3505215976Sjmallett	fprintf(stderr,
3506215976Sjmallett		"received reslist index %u, have %d of %d fields for index %u, aborting.\n",
3507215976Sjmallett		idx, *pfields, RESLIST_FIELDS, prow->idx);
3508215976Sjmallett	exit(1);
3509215976Sjmallett}
3510215976Sjmallett
3511215976Sjmallett
3512215976Sjmallett/*
3513215976Sjmallett * another_reslist_field - helper for reslist()
3514215976Sjmallett *
3515215976Sjmallett * If all fields for the row have been received, print it.
3516215976Sjmallett */
3517215976Sjmallettstatic void
3518215976Sjmallettanother_reslist_field(
3519215976Sjmallett	int *		pfields,
3520215976Sjmallett	reslist_row *	prow,
3521215976Sjmallett	FILE *		fp
3522215976Sjmallett	)
3523215976Sjmallett{
3524215976Sjmallett	char	addrmaskstr[128];
3525215976Sjmallett	int	prefix;	/* subnet mask as prefix bits count */
3526215976Sjmallett	u_int	idx;
3527215976Sjmallett
3528215976Sjmallett	(*pfields)++;
3529215976Sjmallett	/* we understand 4 tags */
3530215976Sjmallett	if (RESLIST_FIELDS > *pfields)
3531215976Sjmallett		return;
3532215976Sjmallett
3533215976Sjmallett	prefix = sockaddr_masktoprefixlen(&prow->mask);
3534215976Sjmallett	if (prefix >= 0)
3535215976Sjmallett		snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d",
3536215976Sjmallett			 stoa(&prow->addr), prefix);
3537215976Sjmallett	else
3538215976Sjmallett		snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s",
3539215976Sjmallett			 stoa(&prow->addr), stoa(&prow->mask));
3540215976Sjmallett
3541215976Sjmallett	/*
3542215976Sjmallett	"   hits    addr/prefix or addr mask\n"
3543215976Sjmallett	"           restrictions\n"
3544215976Sjmallett	"==============================================================================\n");
3545215976Sjmallett	 */
3546215976Sjmallett	fprintf(fp,
3547215976Sjmallett		"%10lu %s\n"
3548215976Sjmallett		"           %s\n",
3549215976Sjmallett		prow->hits, addrmaskstr, prow->flagstr);
3550215976Sjmallett	idx = prow->idx;
3551215976Sjmallett	ZERO(*prow);
3552215976Sjmallett	prow->idx = idx;
3553215976Sjmallett}
3554215976Sjmallett
3555215976Sjmallett
3556215976Sjmallett/*
3557215976Sjmallett * reslist - ntpq -c reslist modeled on ntpdc -c reslist.
3558215976Sjmallett */
3559215976Sjmallettstatic void
3560215976Sjmallettreslist(
3561215976Sjmallett	struct parse *	pcmd,
3562215976Sjmallett	FILE *		fp
3563215976Sjmallett	)
3564215976Sjmallett{
3565215976Sjmallett	const char addr_fmtu[] =	"addr.%u";
3566215976Sjmallett	const char mask_fmtu[] =	"mask.%u";
3567215976Sjmallett	const char hits_fmt[] =		"hits.%u";
3568215976Sjmallett	const char flags_fmt[] =	"flags.%u";
3569215976Sjmallett	const char qdata[] =		"addr_restrictions";
3570215976Sjmallett	const int qdata_chars =		COUNTOF(qdata) - 1;
3571215976Sjmallett	const char *	datap;
3572215976Sjmallett	int		qres;
3573215976Sjmallett	size_t		dsize;
3574215976Sjmallett	u_short		rstatus;
3575215976Sjmallett	char *		tag;
3576215976Sjmallett	char *		val;
3577215976Sjmallett	int		fields;
3578215976Sjmallett	u_int		ui;
3579215976Sjmallett	reslist_row	row;
3580215976Sjmallett	int		comprende;
3581215976Sjmallett	size_t		len;
3582215976Sjmallett
3583215976Sjmallett	qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars,
3584215976Sjmallett		       qdata, &rstatus, &dsize, &datap);
3585215976Sjmallett	if (qres)	/* message already displayed */
3586215976Sjmallett		return;
3587215976Sjmallett
3588215976Sjmallett	fprintf(fp,
3589215976Sjmallett		"   hits    addr/prefix or addr mask\n"
3590215976Sjmallett		"           restrictions\n"
3591215976Sjmallett		"==============================================================================\n");
3592215976Sjmallett		/* '=' x 78 */
3593215976Sjmallett
3594215976Sjmallett	ZERO(row);
3595215976Sjmallett	fields = 0;
3596215976Sjmallett	ui = 0;
3597215976Sjmallett	while (nextvar(&dsize, &datap, &tag, &val)) {
3598215976Sjmallett		if (debug > 1)
3599215976Sjmallett			fprintf(stderr, "nextvar gave: %s = %s\n", tag,
3600215976Sjmallett				(NULL == val)
3601215976Sjmallett				    ? ""
3602215976Sjmallett				    : val);
3603215976Sjmallett		comprende = FALSE;
3604215976Sjmallett		switch(tag[0]) {
3605215976Sjmallett
3606215976Sjmallett		case 'a':
3607215976Sjmallett			if (1 == sscanf(tag, addr_fmtu, &ui) &&
3608215976Sjmallett			    decodenetnum(val, &row.addr))
3609215976Sjmallett				comprende = TRUE;
3610215976Sjmallett			break;
3611215976Sjmallett
3612215976Sjmallett		case 'f':
3613215976Sjmallett			if (1 == sscanf(tag, flags_fmt, &ui)) {
3614215976Sjmallett				if (NULL == val) {
3615215976Sjmallett					row.flagstr[0] = '\0';
3616215976Sjmallett					comprende = TRUE;
3617215976Sjmallett				} else if ((len = strlen(val)) < sizeof(row.flagstr)) {
3618215976Sjmallett					memcpy(row.flagstr, val, len);
3619215976Sjmallett					row.flagstr[len] = '\0';
3620215976Sjmallett					comprende = TRUE;
3621215976Sjmallett				} else {
3622215976Sjmallett					 /* no flags, and still !comprende */
3623215976Sjmallett					row.flagstr[0] = '\0';
3624215976Sjmallett				}
3625215976Sjmallett			}
3626215976Sjmallett			break;
3627215976Sjmallett
3628215976Sjmallett		case 'h':
3629215976Sjmallett			if (1 == sscanf(tag, hits_fmt, &ui) &&
3630215976Sjmallett			    1 == sscanf(val, "%lu", &row.hits))
3631215976Sjmallett				comprende = TRUE;
3632215976Sjmallett			break;
3633215976Sjmallett
3634215976Sjmallett		case 'm':
3635215976Sjmallett			if (1 == sscanf(tag, mask_fmtu, &ui) &&
3636215976Sjmallett			    decodenetnum(val, &row.mask))
3637215976Sjmallett				comprende = TRUE;
3638215976Sjmallett			break;
3639215976Sjmallett		}
3640215976Sjmallett
3641215976Sjmallett		if (comprende) {
3642215976Sjmallett			/* error out if rows out of order */
3643215976Sjmallett			validate_reslist_idx(fp, ui, &fields, &row);
3644215976Sjmallett			/* if the row is complete, print it */
3645215976Sjmallett			another_reslist_field(&fields, &row, fp);
3646215976Sjmallett		}
3647215976Sjmallett	}
3648215976Sjmallett	if (fields != RESLIST_FIELDS)
3649215976Sjmallett		fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3650215976Sjmallett			fields, RESLIST_FIELDS);
3651215976Sjmallett
3652215976Sjmallett	fflush(fp);
3653215976Sjmallett}
3654215976Sjmallett
3655215976Sjmallett
3656215976Sjmallett/*
3657215976Sjmallett * collect_display_vdc
3658215976Sjmallett */
3659215976Sjmallettstatic void
3660215976Sjmallettcollect_display_vdc(
3661215976Sjmallett	associd_t	as,
3662215976Sjmallett	vdc *		table,
3663215976Sjmallett	int		decodestatus,
3664215976Sjmallett	FILE *		fp
3665215976Sjmallett	)
3666215976Sjmallett{
3667215976Sjmallett	static const char * const suf[2] = { "adr", "port" };
3668215976Sjmallett	static const char * const leapbits[4] = { "00", "01",
3669215976Sjmallett						  "10", "11" };
3670215976Sjmallett	struct varlist vl[MAXLIST];
3671215976Sjmallett	char tagbuf[32];
3672215976Sjmallett	vdc *pvdc;
3673215976Sjmallett	u_short rstatus;
3674215976Sjmallett	size_t rsize;
3675215976Sjmallett	const char *rdata;
3676215976Sjmallett	int qres;
3677215976Sjmallett	char *tag;
3678215976Sjmallett	char *val;
3679215976Sjmallett	u_int n;
3680215976Sjmallett	size_t len;
3681215976Sjmallett	int match;
3682215976Sjmallett	u_long ul;
3683215976Sjmallett	int vtype;
3684215976Sjmallett
3685215976Sjmallett	ZERO(vl);
3686215976Sjmallett	for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3687215976Sjmallett		ZERO(pvdc->v);
3688215976Sjmallett		if (NTP_ADD != pvdc->type) {
3689215976Sjmallett			doaddvlist(vl, pvdc->tag);
3690215976Sjmallett		} else {
3691215976Sjmallett			for (n = 0; n < COUNTOF(suf); n++) {
3692215976Sjmallett				snprintf(tagbuf, sizeof(tagbuf), "%s%s",
3693215976Sjmallett					 pvdc->tag, suf[n]);
3694215976Sjmallett				doaddvlist(vl, tagbuf);
3695215976Sjmallett			}
3696215976Sjmallett		}
3697215976Sjmallett	}
3698215976Sjmallett	qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize,
3699215976Sjmallett			   &rdata);
3700215976Sjmallett	doclearvlist(vl);
3701215976Sjmallett	if (qres)
3702215976Sjmallett		return;		/* error msg already displayed */
3703215976Sjmallett
3704215976Sjmallett	/*
3705215976Sjmallett	 * iterate over the response variables filling vdc_table with
3706215976Sjmallett	 * the retrieved values.
3707215976Sjmallett	 */
3708215976Sjmallett	while (nextvar(&rsize, &rdata, &tag, &val)) {
3709215976Sjmallett		if (NULL == val)
3710215976Sjmallett			continue;
3711215976Sjmallett		n = 0;
3712215976Sjmallett		for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3713215976Sjmallett			len = strlen(pvdc->tag);
3714215976Sjmallett			if (strncmp(tag, pvdc->tag, len))
3715215976Sjmallett				continue;
3716215976Sjmallett			if (NTP_ADD != pvdc->type) {
3717215976Sjmallett				if ('\0' != tag[len])
3718215976Sjmallett					continue;
3719215976Sjmallett				break;
3720215976Sjmallett			}
3721215976Sjmallett			match = FALSE;
3722215976Sjmallett			for (n = 0; n < COUNTOF(suf); n++) {
3723215976Sjmallett				if (strcmp(tag + len, suf[n]))
3724215976Sjmallett					continue;
3725215976Sjmallett				match = TRUE;
3726215976Sjmallett				break;
3727215976Sjmallett			}
3728215976Sjmallett			if (match)
3729215976Sjmallett				break;
3730215976Sjmallett		}
3731215976Sjmallett		if (NULL == pvdc->tag)
3732215976Sjmallett			continue;
3733215976Sjmallett		switch (pvdc->type) {
3734215976Sjmallett
3735215976Sjmallett		case NTP_STR:
3736215976Sjmallett			/* strip surrounding double quotes */
3737215976Sjmallett			if ('"' == val[0]) {
3738215976Sjmallett				len = strlen(val);
3739215976Sjmallett				if (len > 0 && '"' == val[len - 1]) {
3740215976Sjmallett					val[len - 1] = '\0';
3741215976Sjmallett					val++;
3742215976Sjmallett				}
3743215976Sjmallett			}
3744215976Sjmallett			/* fallthru */
3745215976Sjmallett		case NTP_MODE:	/* fallthru */
3746215976Sjmallett		case NTP_2BIT:
3747215976Sjmallett			pvdc->v.str = estrdup(val);
3748215976Sjmallett			break;
3749215976Sjmallett
3750215976Sjmallett		case NTP_LFP:
3751215976Sjmallett			decodets(val, &pvdc->v.lfp);
3752215976Sjmallett			break;
3753215976Sjmallett
3754215976Sjmallett		case NTP_ADP:
3755215976Sjmallett			if (!decodenetnum(val, &pvdc->v.sau))
3756215976Sjmallett				fprintf(stderr, "malformed %s=%s\n",
3757215976Sjmallett					pvdc->tag, val);
3758215976Sjmallett			break;
3759215976Sjmallett
3760215976Sjmallett		case NTP_ADD:
3761215976Sjmallett			if (0 == n) {	/* adr */
3762215976Sjmallett				if (!decodenetnum(val, &pvdc->v.sau))
3763215976Sjmallett					fprintf(stderr,
3764215976Sjmallett						"malformed %s=%s\n",
3765215976Sjmallett						pvdc->tag, val);
3766215976Sjmallett			} else {	/* port */
3767215976Sjmallett				if (atouint(val, &ul))
3768215976Sjmallett					SET_PORT(&pvdc->v.sau,
3769215976Sjmallett						 (u_short)ul);
3770215976Sjmallett			}
3771215976Sjmallett			break;
3772215976Sjmallett		}
3773215976Sjmallett	}
3774215976Sjmallett
3775215976Sjmallett	/* and display */
3776215976Sjmallett	if (decodestatus) {
3777215976Sjmallett		vtype = (0 == as)
3778215976Sjmallett			    ? TYPE_SYS
3779215976Sjmallett			    : TYPE_PEER;
3780215976Sjmallett		fprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus,
3781215976Sjmallett			statustoa(vtype, rstatus));
3782215976Sjmallett	}
3783215976Sjmallett
3784215976Sjmallett	for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3785215976Sjmallett		switch (pvdc->type) {
3786215976Sjmallett
3787215976Sjmallett		case NTP_STR:
3788215976Sjmallett			if (pvdc->v.str != NULL) {
3789215976Sjmallett				fprintf(fp, "%s  %s\n", pvdc->display,
3790215976Sjmallett					pvdc->v.str);
3791215976Sjmallett				free(pvdc->v.str);
3792215976Sjmallett				pvdc->v.str = NULL;
3793215976Sjmallett			}
3794215976Sjmallett			break;
3795215976Sjmallett
3796215976Sjmallett		case NTP_ADD:	/* fallthru */
3797215976Sjmallett		case NTP_ADP:
3798215976Sjmallett			fprintf(fp, "%s  %s\n", pvdc->display,
3799215976Sjmallett				nntohostp(&pvdc->v.sau));
3800215976Sjmallett			break;
3801215976Sjmallett
3802215976Sjmallett		case NTP_LFP:
3803215976Sjmallett			fprintf(fp, "%s  %s\n", pvdc->display,
3804215976Sjmallett				prettydate(&pvdc->v.lfp));
3805215976Sjmallett			break;
3806215976Sjmallett
3807215976Sjmallett		case NTP_MODE:
3808215976Sjmallett			atouint(pvdc->v.str, &ul);
3809215976Sjmallett			fprintf(fp, "%s  %s\n", pvdc->display,
3810215976Sjmallett				modetoa((int)ul));
3811215976Sjmallett			break;
3812215976Sjmallett
3813215976Sjmallett		case NTP_2BIT:
3814215976Sjmallett			atouint(pvdc->v.str, &ul);
3815215976Sjmallett			fprintf(fp, "%s  %s\n", pvdc->display,
3816215976Sjmallett				leapbits[ul & 0x3]);
3817215976Sjmallett			break;
3818215976Sjmallett
3819215976Sjmallett		default:
3820215976Sjmallett			fprintf(stderr, "unexpected vdc type %d for %s\n",
3821215976Sjmallett				pvdc->type, pvdc->tag);
3822215976Sjmallett			break;
3823215976Sjmallett		}
3824215976Sjmallett	}
3825215976Sjmallett}
3826215976Sjmallett
3827215976Sjmallett
3828215976Sjmallett/*
3829215976Sjmallett * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats
3830215976Sjmallett */
3831215976Sjmallettstatic void
3832215976Sjmallettsysstats(
3833215976Sjmallett	struct parse *pcmd,
3834215976Sjmallett	FILE *fp
3835215976Sjmallett	)
3836215976Sjmallett{
3837215976Sjmallett    static vdc sysstats_vdc[] = {
3838215976Sjmallett	VDC_INIT("ss_uptime",		"uptime:               ", NTP_STR),
3839215976Sjmallett	VDC_INIT("ss_reset",		"sysstats reset:       ", NTP_STR),
3840215976Sjmallett	VDC_INIT("ss_received",		"packets received:     ", NTP_STR),
3841215976Sjmallett	VDC_INIT("ss_thisver",		"current version:      ", NTP_STR),
3842215976Sjmallett	VDC_INIT("ss_oldver",		"older version:        ", NTP_STR),
3843215976Sjmallett	VDC_INIT("ss_badformat",	"bad length or format: ", NTP_STR),
3844215976Sjmallett	VDC_INIT("ss_badauth",		"authentication failed:", NTP_STR),
3845215976Sjmallett	VDC_INIT("ss_declined",		"declined:             ", NTP_STR),
3846215976Sjmallett	VDC_INIT("ss_restricted",	"restricted:           ", NTP_STR),
3847215976Sjmallett	VDC_INIT("ss_limited",		"rate limited:         ", NTP_STR),
3848215976Sjmallett	VDC_INIT("ss_kodsent",		"KoD responses:        ", NTP_STR),
3849215976Sjmallett	VDC_INIT("ss_processed",	"processed for time:   ", NTP_STR),
3850215976Sjmallett	VDC_INIT(NULL,			NULL,			  0)
3851215976Sjmallett    };
3852215976Sjmallett
3853215976Sjmallett	collect_display_vdc(0, sysstats_vdc, FALSE, fp);
3854215976Sjmallett}
3855215976Sjmallett
3856215976Sjmallett
3857215976Sjmallett/*
3858215976Sjmallett * sysinfo - modeled on ntpdc's sysinfo
3859215976Sjmallett */
3860215976Sjmallettstatic void
3861215976Sjmallettsysinfo(
3862215976Sjmallett	struct parse *pcmd,
3863215976Sjmallett	FILE *fp
3864215976Sjmallett	)
3865215976Sjmallett{
3866215976Sjmallett    static vdc sysinfo_vdc[] = {
3867215976Sjmallett	VDC_INIT("peeradr",		"system peer:      ", NTP_ADP),
3868215976Sjmallett	VDC_INIT("peermode",		"system peer mode: ", NTP_MODE),
3869215976Sjmallett	VDC_INIT("leap",		"leap indicator:   ", NTP_2BIT),
3870215976Sjmallett	VDC_INIT("stratum",		"stratum:          ", NTP_STR),
3871215976Sjmallett	VDC_INIT("precision",		"log2 precision:   ", NTP_STR),
3872215976Sjmallett	VDC_INIT("rootdelay",		"root delay:       ", NTP_STR),
3873215976Sjmallett	VDC_INIT("rootdisp",		"root dispersion:  ", NTP_STR),
3874215976Sjmallett	VDC_INIT("refid",		"reference ID:     ", NTP_STR),
3875215976Sjmallett	VDC_INIT("reftime",		"reference time:   ", NTP_LFP),
3876215976Sjmallett	VDC_INIT("sys_jitter",		"system jitter:    ", NTP_STR),
3877215976Sjmallett	VDC_INIT("clk_jitter",		"clock jitter:     ", NTP_STR),
3878215976Sjmallett	VDC_INIT("clk_wander",		"clock wander:     ", NTP_STR),
3879215976Sjmallett	VDC_INIT("bcastdelay",		"broadcast delay:  ", NTP_STR),
3880215976Sjmallett	VDC_INIT("authdelay",		"symm. auth. delay:", NTP_STR),
3881215976Sjmallett	VDC_INIT(NULL,			NULL,		      0)
3882215976Sjmallett    };
3883215976Sjmallett
3884215976Sjmallett	collect_display_vdc(0, sysinfo_vdc, TRUE, fp);
3885215976Sjmallett}
3886215976Sjmallett
3887215976Sjmallett
3888215976Sjmallett/*
3889215976Sjmallett * kerninfo - modeled on ntpdc's kerninfo
3890215976Sjmallett */
3891215976Sjmallettstatic void
3892215976Sjmallettkerninfo(
3893215976Sjmallett	struct parse *pcmd,
3894215976Sjmallett	FILE *fp
3895215976Sjmallett	)
3896215976Sjmallett{
3897215976Sjmallett    static vdc kerninfo_vdc[] = {
3898215976Sjmallett	VDC_INIT("koffset",		"pll offset:          ", NTP_STR),
3899215976Sjmallett	VDC_INIT("kfreq",		"pll frequency:       ", NTP_STR),
3900215976Sjmallett	VDC_INIT("kmaxerr",		"maximum error:       ", NTP_STR),
3901215976Sjmallett	VDC_INIT("kesterr",		"estimated error:     ", NTP_STR),
3902215976Sjmallett	VDC_INIT("kstflags",		"kernel status:       ", NTP_STR),
3903215976Sjmallett	VDC_INIT("ktimeconst",		"pll time constant:   ", NTP_STR),
3904215976Sjmallett	VDC_INIT("kprecis",		"precision:           ", NTP_STR),
3905215976Sjmallett	VDC_INIT("kfreqtol",		"frequency tolerance: ", NTP_STR),
3906215976Sjmallett	VDC_INIT("kppsfreq",		"pps frequency:       ", NTP_STR),
3907215976Sjmallett	VDC_INIT("kppsstab",		"pps stability:       ", NTP_STR),
3908215976Sjmallett	VDC_INIT("kppsjitter",		"pps jitter:          ", NTP_STR),
3909215976Sjmallett	VDC_INIT("kppscalibdur",	"calibration interval ", NTP_STR),
3910215976Sjmallett	VDC_INIT("kppscalibs",		"calibration cycles:  ", NTP_STR),
3911215976Sjmallett	VDC_INIT("kppsjitexc",		"jitter exceeded:     ", NTP_STR),
3912215976Sjmallett	VDC_INIT("kppsstbexc",		"stability exceeded:  ", NTP_STR),
3913215976Sjmallett	VDC_INIT("kppscaliberrs",	"calibration errors:  ", NTP_STR),
3914215976Sjmallett	VDC_INIT(NULL,			NULL,			 0)
3915215976Sjmallett    };
3916215976Sjmallett
3917215976Sjmallett	collect_display_vdc(0, kerninfo_vdc, TRUE, fp);
3918215976Sjmallett}
3919215976Sjmallett
3920215976Sjmallett
3921215976Sjmallett/*
3922215976Sjmallett * monstats - implements ntpq -c monstats
3923215976Sjmallett */
3924215976Sjmallettstatic void
3925215976Sjmallettmonstats(
3926215976Sjmallett	struct parse *pcmd,
3927215976Sjmallett	FILE *fp
3928215976Sjmallett	)
3929215976Sjmallett{
3930215976Sjmallett    static vdc monstats_vdc[] = {
3931215976Sjmallett	VDC_INIT("mru_enabled",	"enabled:            ", NTP_STR),
3932215976Sjmallett	VDC_INIT("mru_depth",		"addresses:          ", NTP_STR),
3933215976Sjmallett	VDC_INIT("mru_deepest",	"peak addresses:     ", NTP_STR),
3934215976Sjmallett	VDC_INIT("mru_maxdepth",	"maximum addresses:  ", NTP_STR),
3935215976Sjmallett	VDC_INIT("mru_mindepth",	"reclaim above count:", NTP_STR),
3936215976Sjmallett	VDC_INIT("mru_maxage",		"reclaim older than: ", NTP_STR),
3937215976Sjmallett	VDC_INIT("mru_mem",		"kilobytes:          ", NTP_STR),
3938215976Sjmallett	VDC_INIT("mru_maxmem",		"maximum kilobytes:  ", NTP_STR),
3939215976Sjmallett	VDC_INIT(NULL,			NULL,			0)
3940215976Sjmallett    };
3941215976Sjmallett
3942215976Sjmallett	collect_display_vdc(0, monstats_vdc, FALSE, fp);
3943215976Sjmallett}
3944215976Sjmallett
3945215976Sjmallett
3946215976Sjmallett/*
3947215976Sjmallett * iostats - ntpq -c iostats - network input and output counters
3948215976Sjmallett */
3949215976Sjmallettstatic void
3950215976Sjmallettiostats(
3951215976Sjmallett	struct parse *pcmd,
3952215976Sjmallett	FILE *fp
3953215976Sjmallett	)
3954215976Sjmallett{
3955215976Sjmallett    static vdc iostats_vdc[] = {
3956215976Sjmallett	VDC_INIT("iostats_reset",	"time since reset:     ", NTP_STR),
3957215976Sjmallett	VDC_INIT("total_rbuf",		"receive buffers:      ", NTP_STR),
3958215976Sjmallett	VDC_INIT("free_rbuf",		"free receive buffers: ", NTP_STR),
3959215976Sjmallett	VDC_INIT("used_rbuf",		"used receive buffers: ", NTP_STR),
3960215976Sjmallett	VDC_INIT("rbuf_lowater",	"low water refills:    ", NTP_STR),
3961215976Sjmallett	VDC_INIT("io_dropped",		"dropped packets:      ", NTP_STR),
3962215976Sjmallett	VDC_INIT("io_ignored",		"ignored packets:      ", NTP_STR),
3963215976Sjmallett	VDC_INIT("io_received",		"received packets:     ", NTP_STR),
3964215976Sjmallett	VDC_INIT("io_sent",		"packets sent:         ", NTP_STR),
3965215976Sjmallett	VDC_INIT("io_sendfailed",	"packet send failures: ", NTP_STR),
3966215976Sjmallett	VDC_INIT("io_wakeups",		"input wakeups:        ", NTP_STR),
3967215976Sjmallett	VDC_INIT("io_goodwakeups",	"useful input wakeups: ", NTP_STR),
3968215976Sjmallett	VDC_INIT(NULL,			NULL,			  0)
3969215976Sjmallett    };
3970215976Sjmallett
3971215976Sjmallett	collect_display_vdc(0, iostats_vdc, FALSE, fp);
3972215976Sjmallett}
3973215976Sjmallett
3974215976Sjmallett
3975215976Sjmallett/*
3976215976Sjmallett * timerstats - ntpq -c timerstats - interval timer counters
3977215976Sjmallett */
3978215976Sjmallettstatic void
3979215976Sjmalletttimerstats(
3980215976Sjmallett	struct parse *pcmd,
3981215976Sjmallett	FILE *fp
3982215976Sjmallett	)
3983215976Sjmallett{
3984215976Sjmallett    static vdc timerstats_vdc[] = {
3985215976Sjmallett	VDC_INIT("timerstats_reset",	"time since reset:  ", NTP_STR),
3986215976Sjmallett	VDC_INIT("timer_overruns",	"timer overruns:    ", NTP_STR),
3987215976Sjmallett	VDC_INIT("timer_xmts",		"calls to transmit: ", NTP_STR),
3988215976Sjmallett	VDC_INIT(NULL,			NULL,		       0)
3989215976Sjmallett    };
3990215976Sjmallett
3991215976Sjmallett	collect_display_vdc(0, timerstats_vdc, FALSE, fp);
3992215976Sjmallett}
3993215976Sjmallett
3994215976Sjmallett
3995215976Sjmallett/*
3996215976Sjmallett * authinfo - implements ntpq -c authinfo
3997215976Sjmallett */
3998215976Sjmallettstatic void
3999215976Sjmallettauthinfo(
4000215976Sjmallett	struct parse *pcmd,
4001215976Sjmallett	FILE *fp
4002215976Sjmallett	)
4003215976Sjmallett{
4004215976Sjmallett    static vdc authinfo_vdc[] = {
4005215976Sjmallett	VDC_INIT("authreset",		"time since reset:", NTP_STR),
4006215976Sjmallett	VDC_INIT("authkeys",		"stored keys:     ", NTP_STR),
4007215976Sjmallett	VDC_INIT("authfreek",		"free keys:       ", NTP_STR),
4008215976Sjmallett	VDC_INIT("authklookups",	"key lookups:     ", NTP_STR),
4009215976Sjmallett	VDC_INIT("authknotfound",	"keys not found:  ", NTP_STR),
4010215976Sjmallett	VDC_INIT("authkuncached",	"uncached keys:   ", NTP_STR),
4011215976Sjmallett	VDC_INIT("authkexpired",	"expired keys:    ", NTP_STR),
4012215976Sjmallett	VDC_INIT("authencrypts",	"encryptions:     ", NTP_STR),
4013215976Sjmallett	VDC_INIT("authdecrypts",	"decryptions:     ", NTP_STR),
4014215976Sjmallett	VDC_INIT(NULL,			NULL,		     0)
4015215976Sjmallett    };
4016215976Sjmallett
4017215976Sjmallett	collect_display_vdc(0, authinfo_vdc, FALSE, fp);
4018215976Sjmallett}
4019215976Sjmallett
4020215976Sjmallett
4021215976Sjmallett/*
4022215976Sjmallett * pstats - show statistics for a peer
4023215976Sjmallett */
4024215976Sjmallettstatic void
4025215976Sjmallettpstats(
4026215976Sjmallett	struct parse *pcmd,
4027215976Sjmallett	FILE *fp
4028215976Sjmallett	)
4029215976Sjmallett{
4030215976Sjmallett    static vdc pstats_vdc[] = {
4031215976Sjmallett	VDC_INIT("src",		"remote host:         ", NTP_ADD),
4032215976Sjmallett	VDC_INIT("dst",		"local address:       ", NTP_ADD),
4033215976Sjmallett	VDC_INIT("timerec",	"time last received:  ", NTP_STR),
4034215976Sjmallett	VDC_INIT("timer",	"time until next send:", NTP_STR),
4035215976Sjmallett	VDC_INIT("timereach",	"reachability change: ", NTP_STR),
4036215976Sjmallett	VDC_INIT("sent",	"packets sent:        ", NTP_STR),
4037215976Sjmallett	VDC_INIT("received",	"packets received:    ", NTP_STR),
4038215976Sjmallett	VDC_INIT("badauth",	"bad authentication:  ", NTP_STR),
4039215976Sjmallett	VDC_INIT("bogusorg",	"bogus origin:        ", NTP_STR),
4040215976Sjmallett	VDC_INIT("oldpkt",	"duplicate:           ", NTP_STR),
4041215976Sjmallett	VDC_INIT("seldisp",	"bad dispersion:      ", NTP_STR),
4042215976Sjmallett	VDC_INIT("selbroken",	"bad reference time:  ", NTP_STR),
4043215976Sjmallett	VDC_INIT("candidate",	"candidate order:     ", NTP_STR),
4044215976Sjmallett	VDC_INIT(NULL,		NULL,			 0)
4045215976Sjmallett    };
4046215976Sjmallett	associd_t associd;
4047215976Sjmallett
4048215976Sjmallett	associd = checkassocid(pcmd->argval[0].uval);
4049215976Sjmallett	if (0 == associd)
4050215976Sjmallett		return;
4051215976Sjmallett
4052215976Sjmallett	collect_display_vdc(associd, pstats_vdc, TRUE, fp);
4053215976Sjmallett}
4054215976Sjmallett