1181834Sroberto/*
2290000Sglebius * ntpq-subs.c - subroutines which are called to perform ntpq commands.
3181834Sroberto */
4290000Sglebius#include <config.h>
5181834Sroberto#include <stdio.h>
6181834Sroberto#include <ctype.h>
7181834Sroberto#include <sys/types.h>
8181834Sroberto#include <sys/time.h>
9181834Sroberto
10181834Sroberto#include "ntpq.h"
11290000Sglebius#include "ntpq-opts.h"
12181834Sroberto
13290000Sglebiusextern char	currenthost[];
14290000Sglebiusextern int	currenthostisnum;
15290000Sglebiussize_t		maxhostlen;
16181834Sroberto
17181834Sroberto/*
18181834Sroberto * Declarations for command handlers in here
19181834Sroberto */
20290000Sglebiusstatic	associd_t checkassocid	(u_int32);
21290000Sglebiusstatic	struct varlist *findlistvar (struct varlist *, char *);
22290000Sglebiusstatic	void	doaddvlist	(struct varlist *, const char *);
23290000Sglebiusstatic	void	dormvlist	(struct varlist *, const char *);
24290000Sglebiusstatic	void	doclearvlist	(struct varlist *);
25293894Sglebiusstatic	void	makequerydata	(struct varlist *, size_t *, char *);
26290000Sglebiusstatic	int	doquerylist	(struct varlist *, int, associd_t, int,
27293894Sglebius				 u_short *, size_t *, const char **);
28290000Sglebiusstatic	void	doprintvlist	(struct varlist *, FILE *);
29290000Sglebiusstatic	void	addvars 	(struct parse *, FILE *);
30290000Sglebiusstatic	void	rmvars		(struct parse *, FILE *);
31290000Sglebiusstatic	void	clearvars	(struct parse *, FILE *);
32290000Sglebiusstatic	void	showvars	(struct parse *, FILE *);
33290000Sglebiusstatic	int	dolist		(struct varlist *, associd_t, int, int,
34290000Sglebius				 FILE *);
35290000Sglebiusstatic	void	readlist	(struct parse *, FILE *);
36290000Sglebiusstatic	void	writelist	(struct parse *, FILE *);
37290000Sglebiusstatic	void	readvar 	(struct parse *, FILE *);
38290000Sglebiusstatic	void	writevar	(struct parse *, FILE *);
39290000Sglebiusstatic	void	clocklist	(struct parse *, FILE *);
40290000Sglebiusstatic	void	clockvar	(struct parse *, FILE *);
41290000Sglebiusstatic	int	findassidrange	(u_int32, u_int32, int *, int *,
42290000Sglebius				 FILE *);
43290000Sglebiusstatic	void	mreadlist	(struct parse *, FILE *);
44290000Sglebiusstatic	void	mreadvar	(struct parse *, FILE *);
45290000Sglebiusstatic	void	printassoc	(int, FILE *);
46290000Sglebiusstatic	void	associations	(struct parse *, FILE *);
47290000Sglebiusstatic	void	lassociations	(struct parse *, FILE *);
48290000Sglebiusstatic	void	passociations	(struct parse *, FILE *);
49290000Sglebiusstatic	void	lpassociations	(struct parse *, FILE *);
50181834Sroberto
51181834Sroberto#ifdef	UNUSED
52290000Sglebiusstatic	void	radiostatus (struct parse *, FILE *);
53181834Sroberto#endif	/* UNUSED */
54181834Sroberto
55290000Sglebiusstatic	void	authinfo	(struct parse *, FILE *);
56290000Sglebiusstatic	void	pstats	 	(struct parse *, FILE *);
57290000Sglebiusstatic	long	when		(l_fp *, l_fp *, l_fp *);
58290000Sglebiusstatic	char *	prettyinterval	(char *, size_t, long);
59293894Sglebiusstatic	int	doprintpeers	(struct varlist *, int, int, size_t, const char *, FILE *, int);
60290000Sglebiusstatic	int	dogetpeers	(struct varlist *, associd_t, FILE *, int);
61290000Sglebiusstatic	void	dopeers 	(int, FILE *, int);
62290000Sglebiusstatic	void	peers		(struct parse *, FILE *);
63290000Sglebiusstatic	void	doapeers 	(int, FILE *, int);
64290000Sglebiusstatic	void	apeers		(struct parse *, FILE *);
65290000Sglebiusstatic	void	lpeers		(struct parse *, FILE *);
66290000Sglebiusstatic	void	doopeers	(int, FILE *, int);
67290000Sglebiusstatic	void	opeers		(struct parse *, FILE *);
68290000Sglebiusstatic	void	lopeers 	(struct parse *, FILE *);
69290000Sglebiusstatic	void	config		(struct parse *, FILE *);
70290000Sglebiusstatic	void	saveconfig	(struct parse *, FILE *);
71290000Sglebiusstatic	void	config_from_file(struct parse *, FILE *);
72290000Sglebiusstatic	void	mrulist		(struct parse *, FILE *);
73290000Sglebiusstatic	void	ifstats		(struct parse *, FILE *);
74290000Sglebiusstatic	void	reslist		(struct parse *, FILE *);
75290000Sglebiusstatic	void	sysstats	(struct parse *, FILE *);
76290000Sglebiusstatic	void	sysinfo		(struct parse *, FILE *);
77290000Sglebiusstatic	void	kerninfo	(struct parse *, FILE *);
78290000Sglebiusstatic	void	monstats	(struct parse *, FILE *);
79290000Sglebiusstatic	void	iostats		(struct parse *, FILE *);
80290000Sglebiusstatic	void	timerstats	(struct parse *, FILE *);
81181834Sroberto
82181834Sroberto/*
83181834Sroberto * Commands we understand.	Ntpdc imports this.
84181834Sroberto */
85181834Srobertostruct xcmd opcmds[] = {
86290000Sglebius	{ "saveconfig", saveconfig, { NTP_STR, NO, NO, NO },
87290000Sglebius		{ "filename", "", "", ""},
88290000Sglebius		"save ntpd configuration to file, . for current config file"},
89181834Sroberto	{ "associations", associations, {  NO, NO, NO, NO },
90181834Sroberto	  { "", "", "", "" },
91181834Sroberto	  "print list of association ID's and statuses for the server's peers" },
92181834Sroberto	{ "passociations", passociations,   {  NO, NO, NO, NO },
93181834Sroberto	  { "", "", "", "" },
94181834Sroberto	  "print list of associations returned by last associations command" },
95181834Sroberto	{ "lassociations", lassociations,   {  NO, NO, NO, NO },
96181834Sroberto	  { "", "", "", "" },
97181834Sroberto	  "print list of associations including all client information" },
98181834Sroberto	{ "lpassociations", lpassociations, {  NO, NO, NO, NO },
99181834Sroberto	  { "", "", "", "" },
100181834Sroberto	  "print last obtained list of associations, including client information" },
101181834Sroberto	{ "addvars",    addvars,    { NTP_STR, NO, NO, NO },
102181834Sroberto	  { "name[=value][,...]", "", "", "" },
103181834Sroberto	  "add variables to the variable list or change their values" },
104181834Sroberto	{ "rmvars", rmvars,     { NTP_STR, NO, NO, NO },
105181834Sroberto	  { "name[,...]", "", "", "" },
106181834Sroberto	  "remove variables from the variable list" },
107181834Sroberto	{ "clearvars",  clearvars,  { NO, NO, NO, NO },
108181834Sroberto	  { "", "", "", "" },
109181834Sroberto	  "remove all variables from the variable list" },
110181834Sroberto	{ "showvars",   showvars,   { NO, NO, NO, NO },
111181834Sroberto	  { "", "", "", "" },
112181834Sroberto	  "print variables on the variable list" },
113181834Sroberto	{ "readlist",   readlist,   { OPT|NTP_UINT, NO, NO, NO },
114181834Sroberto	  { "assocID", "", "", "" },
115181834Sroberto	  "read the system or peer variables included in the variable list" },
116181834Sroberto	{ "rl",     readlist,   { OPT|NTP_UINT, NO, NO, NO },
117181834Sroberto	  { "assocID", "", "", "" },
118181834Sroberto	  "read the system or peer variables included in the variable list" },
119181834Sroberto	{ "writelist",  writelist,  { OPT|NTP_UINT, NO, NO, NO },
120181834Sroberto	  { "assocID", "", "", "" },
121181834Sroberto	  "write the system or peer variables included in the variable list" },
122290000Sglebius	{ "readvar", readvar,    { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
123290000Sglebius	  { "assocID", "varname1", "varname2", "varname3" },
124181834Sroberto	  "read system or peer variables" },
125290000Sglebius	{ "rv",      readvar,    { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
126290000Sglebius	  { "assocID", "varname1", "varname2", "varname3" },
127181834Sroberto	  "read system or peer variables" },
128181834Sroberto	{ "writevar",   writevar,   { NTP_UINT, NTP_STR, NO, NO },
129181834Sroberto	  { "assocID", "name=value,[...]", "", "" },
130181834Sroberto	  "write system or peer variables" },
131181834Sroberto	{ "mreadlist",  mreadlist,  { NTP_UINT, NTP_UINT, NO, NO },
132290000Sglebius	  { "assocIDlow", "assocIDhigh", "", "" },
133181834Sroberto	  "read the peer variables in the variable list for multiple peers" },
134181834Sroberto	{ "mrl",    mreadlist,  { NTP_UINT, NTP_UINT, NO, NO },
135290000Sglebius	  { "assocIDlow", "assocIDhigh", "", "" },
136181834Sroberto	  "read the peer variables in the variable list for multiple peers" },
137181834Sroberto	{ "mreadvar",   mreadvar,   { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
138290000Sglebius	  { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
139181834Sroberto	  "read peer variables from multiple peers" },
140181834Sroberto	{ "mrv",    mreadvar,   { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
141290000Sglebius	  { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
142181834Sroberto	  "read peer variables from multiple peers" },
143181834Sroberto	{ "clocklist",  clocklist,  { OPT|NTP_UINT, NO, NO, NO },
144181834Sroberto	  { "assocID", "", "", "" },
145181834Sroberto	  "read the clock variables included in the variable list" },
146181834Sroberto	{ "cl",     clocklist,  { OPT|NTP_UINT, NO, NO, NO },
147181834Sroberto	  { "assocID", "", "", "" },
148181834Sroberto	  "read the clock variables included in the variable list" },
149181834Sroberto	{ "clockvar",   clockvar,   { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
150181834Sroberto	  { "assocID", "name=value[,...]", "", "" },
151181834Sroberto	  "read clock variables" },
152181834Sroberto	{ "cv",     clockvar,   { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
153181834Sroberto	  { "assocID", "name=value[,...]", "", "" },
154181834Sroberto	  "read clock variables" },
155290000Sglebius	{ "pstats",    pstats,    { NTP_UINT, NO, NO, NO },
156181834Sroberto	  { "assocID", "", "", "" },
157290000Sglebius	  "show statistics for a peer" },
158181834Sroberto	{ "peers",  peers,      { OPT|IP_VERSION, NO, NO, NO },
159181834Sroberto	  { "-4|-6", "", "", "" },
160181834Sroberto	  "obtain and print a list of the server's peers [IP version]" },
161290000Sglebius	{ "apeers",  apeers,      { OPT|IP_VERSION, NO, NO, NO },
162290000Sglebius	  { "-4|-6", "", "", "" },
163290000Sglebius	  "obtain and print a list of the server's peers and their assocIDs [IP version]" },
164181834Sroberto	{ "lpeers", lpeers,     { OPT|IP_VERSION, NO, NO, NO },
165181834Sroberto	  { "-4|-6", "", "", "" },
166181834Sroberto	  "obtain and print a list of all peers and clients [IP version]" },
167181834Sroberto	{ "opeers", opeers,     { OPT|IP_VERSION, NO, NO, NO },
168181834Sroberto	  { "-4|-6", "", "", "" },
169181834Sroberto	  "print peer list the old way, with dstadr shown rather than refid [IP version]" },
170181834Sroberto	{ "lopeers", lopeers,   { OPT|IP_VERSION, NO, NO, NO },
171181834Sroberto	  { "-4|-6", "", "", "" },
172181834Sroberto	  "obtain and print a list of all peers and clients showing dstadr [IP version]" },
173290000Sglebius	{ ":config", config,   { NTP_STR, NO, NO, NO },
174290000Sglebius	  { "<configuration command line>", "", "", "" },
175290000Sglebius	  "send a remote configuration command to ntpd" },
176290000Sglebius	{ "config-from-file", config_from_file, { NTP_STR, NO, NO, NO },
177290000Sglebius	  { "<configuration filename>", "", "", "" },
178290000Sglebius	  "configure ntpd using the configuration filename" },
179290000Sglebius	{ "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
180290000Sglebius	  { "tag=value", "tag=value", "tag=value", "tag=value" },
181290000Sglebius	  "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." },
182290000Sglebius	{ "ifstats", ifstats, { NO, NO, NO, NO },
183290000Sglebius	  { "", "", "", "" },
184290000Sglebius	  "show statistics for each local address ntpd is using" },
185290000Sglebius	{ "reslist", reslist, { NO, NO, NO, NO },
186290000Sglebius	  { "", "", "", "" },
187290000Sglebius	  "show ntpd access control list" },
188290000Sglebius	{ "sysinfo", sysinfo, { NO, NO, NO, NO },
189290000Sglebius	  { "", "", "", "" },
190290000Sglebius	  "display system summary" },
191290000Sglebius	{ "kerninfo", kerninfo, { NO, NO, NO, NO },
192290000Sglebius	  { "", "", "", "" },
193290000Sglebius	  "display kernel loop and PPS statistics" },
194290000Sglebius	{ "sysstats", sysstats, { NO, NO, NO, NO },
195290000Sglebius	  { "", "", "", "" },
196290000Sglebius	  "display system uptime and packet counts" },
197290000Sglebius	{ "monstats", monstats, { NO, NO, NO, NO },
198290000Sglebius	  { "", "", "", "" },
199290000Sglebius	  "display monitor (mrulist) counters and limits" },
200290000Sglebius	{ "authinfo", authinfo, { NO, NO, NO, NO },
201290000Sglebius	  { "", "", "", "" },
202290000Sglebius	  "display symmetric authentication counters" },
203290000Sglebius	{ "iostats", iostats, { NO, NO, NO, NO },
204290000Sglebius	  { "", "", "", "" },
205290000Sglebius	  "display network input and output counters" },
206290000Sglebius	{ "timerstats", timerstats, { NO, NO, NO, NO },
207290000Sglebius	  { "", "", "", "" },
208290000Sglebius	  "display interval timer counters" },
209181834Sroberto	{ 0,		0,		{ NO, NO, NO, NO },
210181834Sroberto	  { "-4|-6", "", "", "" }, "" }
211181834Sroberto};
212181834Sroberto
213181834Sroberto
214181834Sroberto/*
215181834Sroberto * Variable list data space
216181834Sroberto */
217290000Sglebius#define MAXLINE		512	/* maximum length of a line */
218290000Sglebius#define MAXLIST		128	/* maximum variables in list */
219290000Sglebius#define LENHOSTNAME	256	/* host name limit */
220290000Sglebius
221290000Sglebius#define MRU_GOT_COUNT	0x1
222290000Sglebius#define MRU_GOT_LAST	0x2
223290000Sglebius#define MRU_GOT_FIRST	0x4
224290000Sglebius#define MRU_GOT_MV	0x8
225290000Sglebius#define MRU_GOT_RS	0x10
226290000Sglebius#define MRU_GOT_ADDR	0x20
227290000Sglebius#define MRU_GOT_ALL	(MRU_GOT_COUNT | MRU_GOT_LAST | MRU_GOT_FIRST \
228290000Sglebius			 | MRU_GOT_MV | MRU_GOT_RS | MRU_GOT_ADDR)
229290000Sglebius
230181834Sroberto/*
231290000Sglebius * mrulist() depends on MRUSORT_DEF and MRUSORT_RDEF being the first two
232290000Sglebius */
233290000Sglebiustypedef enum mru_sort_order_tag {
234290000Sglebius	MRUSORT_DEF = 0,	/* lstint ascending */
235290000Sglebius	MRUSORT_R_DEF,		/* lstint descending */
236290000Sglebius	MRUSORT_AVGINT,		/* avgint ascending */
237290000Sglebius	MRUSORT_R_AVGINT,	/* avgint descending */
238290000Sglebius	MRUSORT_ADDR,		/* IPv4 asc. then IPv6 asc. */
239290000Sglebius	MRUSORT_R_ADDR,		/* IPv6 desc. then IPv4 desc. */
240290000Sglebius	MRUSORT_COUNT,		/* hit count ascending */
241290000Sglebius	MRUSORT_R_COUNT,	/* hit count descending */
242290000Sglebius	MRUSORT_MAX,		/* special: count of this enum */
243290000Sglebius} mru_sort_order;
244290000Sglebius
245290000Sglebiusconst char * const mru_sort_keywords[MRUSORT_MAX] = {
246290000Sglebius	"lstint",		/* MRUSORT_DEF */
247290000Sglebius	"-lstint",		/* MRUSORT_R_DEF */
248290000Sglebius	"avgint",		/* MRUSORT_AVGINT */
249290000Sglebius	"-avgint",		/* MRUSORT_R_AVGINT */
250290000Sglebius	"addr",			/* MRUSORT_ADDR */
251290000Sglebius	"-addr",		/* MRUSORT_R_ADDR */
252290000Sglebius	"count",		/* MRUSORT_COUNT */
253290000Sglebius	"-count",		/* MRUSORT_R_COUNT */
254290000Sglebius};
255290000Sglebius
256290000Sglebiustypedef int (*qsort_cmp)(const void *, const void *);
257290000Sglebius
258290000Sglebius/*
259181834Sroberto * Old CTL_PST defines for version 2.
260181834Sroberto */
261290000Sglebius#define OLD_CTL_PST_CONFIG		0x80
262181834Sroberto#define OLD_CTL_PST_AUTHENABLE		0x40
263181834Sroberto#define OLD_CTL_PST_AUTHENTIC		0x20
264290000Sglebius#define OLD_CTL_PST_REACH		0x10
265290000Sglebius#define OLD_CTL_PST_SANE		0x08
266290000Sglebius#define OLD_CTL_PST_DISP		0x04
267290000Sglebius
268181834Sroberto#define OLD_CTL_PST_SEL_REJECT		0
269181834Sroberto#define OLD_CTL_PST_SEL_SELCAND 	1
270181834Sroberto#define OLD_CTL_PST_SEL_SYNCCAND	2
271181834Sroberto#define OLD_CTL_PST_SEL_SYSPEER 	3
272181834Sroberto
273181834Srobertochar flash2[] = " .+*    "; /* flash decode for version 2 */
274181834Srobertochar flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
275181834Sroberto
276181834Srobertostruct varlist {
277290000Sglebius	const char *name;
278181834Sroberto	char *value;
279290000Sglebius} g_varlist[MAXLIST] = { { 0, 0 } };
280181834Sroberto
281181834Sroberto/*
282181834Sroberto * Imported from ntpq.c
283181834Sroberto */
284181834Srobertoextern int showhostnames;
285290000Sglebiusextern int wideremote;
286181834Srobertoextern int rawmode;
287181834Srobertoextern struct servent *server_entry;
288290000Sglebiusextern struct association *assoc_cache;
289181834Srobertoextern u_char pktversion;
290181834Sroberto
291290000Sglebiustypedef struct mru_tag mru;
292290000Sglebiusstruct mru_tag {
293290000Sglebius	mru *		hlink;	/* next in hash table bucket */
294290000Sglebius	DECL_DLIST_LINK(mru, mlink);
295290000Sglebius	int		count;
296290000Sglebius	l_fp		last;
297290000Sglebius	l_fp		first;
298290000Sglebius	u_char		mode;
299290000Sglebius	u_char		ver;
300290000Sglebius	u_short		rs;
301290000Sglebius	sockaddr_u	addr;
302290000Sglebius};
303290000Sglebius
304290000Sglebiustypedef struct ifstats_row_tag {
305290000Sglebius	u_int		ifnum;
306290000Sglebius	sockaddr_u	addr;
307290000Sglebius	sockaddr_u	bcast;
308290000Sglebius	int		enabled;
309290000Sglebius	u_int		flags;
310290000Sglebius	int		mcast_count;
311290000Sglebius	char		name[32];
312290000Sglebius	int		peer_count;
313290000Sglebius	int		received;
314290000Sglebius	int		sent;
315290000Sglebius	int		send_errors;
316290000Sglebius	u_int		ttl;
317290000Sglebius	u_int		uptime;
318290000Sglebius} ifstats_row;
319290000Sglebius
320290000Sglebiustypedef struct reslist_row_tag {
321290000Sglebius	u_int		idx;
322290000Sglebius	sockaddr_u	addr;
323290000Sglebius	sockaddr_u	mask;
324290000Sglebius	u_long		hits;
325290000Sglebius	char		flagstr[128];
326290000Sglebius} reslist_row;
327290000Sglebius
328290000Sglebiustypedef struct var_display_collection_tag {
329290000Sglebius	const char * const tag;		/* system variable */
330290000Sglebius	const char * const display;	/* descriptive text */
331290000Sglebius	u_char type;			/* NTP_STR, etc */
332290000Sglebius	union {
333290000Sglebius		char *		str;
334290000Sglebius		sockaddr_u	sau;	/* NTP_ADD */
335290000Sglebius		l_fp		lfp;	/* NTP_LFP */
336290000Sglebius	} v;				/* retrieved value */
337290000Sglebius} vdc;
338290000Sglebius#if !defined(MISSING_C99_STRUCT_INIT)
339290000Sglebius# define VDC_INIT(a, b, c) { .tag = a, .display = b, .type = c }
340290000Sglebius#else
341290000Sglebius# define VDC_INIT(a, b, c) { a, b, c }
342290000Sglebius#endif
343181834Sroberto/*
344290000Sglebius * other local function prototypes
345181834Sroberto */
346293894Sglebiusstatic int	mrulist_ctrl_c_hook(void);
347290000Sglebiusstatic mru *	add_mru(mru *);
348290000Sglebiusstatic int	collect_mru_list(const char *, l_fp *);
349290000Sglebiusstatic int	fetch_nonce(char *, size_t);
350290000Sglebiusstatic int	qcmp_mru_avgint(const void *, const void *);
351290000Sglebiusstatic int	qcmp_mru_r_avgint(const void *, const void *);
352290000Sglebiusstatic int	qcmp_mru_addr(const void *, const void *);
353290000Sglebiusstatic int	qcmp_mru_r_addr(const void *, const void *);
354290000Sglebiusstatic int	qcmp_mru_count(const void *, const void *);
355290000Sglebiusstatic int	qcmp_mru_r_count(const void *, const void *);
356290000Sglebiusstatic void	validate_ifnum(FILE *, u_int, int *, ifstats_row *);
357290000Sglebiusstatic void	another_ifstats_field(int *, ifstats_row *, FILE *);
358290000Sglebiusstatic void	collect_display_vdc(associd_t as, vdc *table,
359290000Sglebius				    int decodestatus, FILE *fp);
360181834Sroberto
361290000Sglebius/*
362290000Sglebius * static globals
363290000Sglebius */
364290000Sglebiusstatic u_int	mru_count;
365290000Sglebiusstatic u_int	mru_dupes;
366290000Sglebiusvolatile int	mrulist_interrupted;
367290000Sglebiusstatic mru	mru_list;		/* listhead */
368290000Sglebiusstatic mru **	hash_table;
369181834Sroberto
370181834Sroberto/*
371290000Sglebius * qsort comparison function table for mrulist().  The first two
372290000Sglebius * entries are NULL because they are handled without qsort().
373290000Sglebius */
374290000Sglebiusstatic const qsort_cmp mru_qcmp_table[MRUSORT_MAX] = {
375290000Sglebius	NULL,			/* MRUSORT_DEF unused */
376290000Sglebius	NULL,			/* MRUSORT_R_DEF unused */
377290000Sglebius	&qcmp_mru_avgint,	/* MRUSORT_AVGINT */
378290000Sglebius	&qcmp_mru_r_avgint,	/* MRUSORT_R_AVGINT */
379290000Sglebius	&qcmp_mru_addr,		/* MRUSORT_ADDR */
380290000Sglebius	&qcmp_mru_r_addr,	/* MRUSORT_R_ADDR */
381290000Sglebius	&qcmp_mru_count,	/* MRUSORT_COUNT */
382290000Sglebius	&qcmp_mru_r_count,	/* MRUSORT_R_COUNT */
383290000Sglebius};
384290000Sglebius
385290000Sglebius/*
386181834Sroberto * checkassocid - return the association ID, checking to see if it is valid
387181834Sroberto */
388290000Sglebiusstatic associd_t
389181834Srobertocheckassocid(
390181834Sroberto	u_int32 value
391181834Sroberto	)
392181834Sroberto{
393290000Sglebius	associd_t	associd;
394290000Sglebius	u_long		ulvalue;
395290000Sglebius
396290000Sglebius	associd = (associd_t)value;
397290000Sglebius	if (0 == associd || value != associd) {
398290000Sglebius		ulvalue = value;
399290000Sglebius		fprintf(stderr,
400290000Sglebius			"***Invalid association ID %lu specified\n",
401290000Sglebius			ulvalue);
402181834Sroberto		return 0;
403181834Sroberto	}
404181834Sroberto
405290000Sglebius	return associd;
406181834Sroberto}
407181834Sroberto
408181834Sroberto
409181834Sroberto/*
410290000Sglebius * findlistvar - Look for the named variable in a varlist.  If found,
411290000Sglebius *		 return a pointer to it.  Otherwise, if the list has
412290000Sglebius *		 slots available, return the pointer to the first free
413290000Sglebius *		 slot, or NULL if it's full.
414181834Sroberto */
415181834Srobertostatic struct varlist *
416181834Srobertofindlistvar(
417181834Sroberto	struct varlist *list,
418181834Sroberto	char *name
419181834Sroberto	)
420181834Sroberto{
421290000Sglebius	struct varlist *vl;
422181834Sroberto
423290000Sglebius	for (vl = list; vl < list + MAXLIST && vl->name != NULL; vl++)
424290000Sglebius		if (!strcmp(name, vl->name))
425290000Sglebius			return vl;
426181834Sroberto	if (vl < list + MAXLIST)
427181834Sroberto		return vl;
428290000Sglebius
429290000Sglebius	return NULL;
430181834Sroberto}
431181834Sroberto
432181834Sroberto
433181834Sroberto/*
434181834Sroberto * doaddvlist - add variable(s) to the variable list
435181834Sroberto */
436181834Srobertostatic void
437181834Srobertodoaddvlist(
438181834Sroberto	struct varlist *vlist,
439290000Sglebius	const char *vars
440181834Sroberto	)
441181834Sroberto{
442290000Sglebius	struct varlist *vl;
443293894Sglebius	size_t len;
444181834Sroberto	char *name;
445181834Sroberto	char *value;
446181834Sroberto
447181834Sroberto	len = strlen(vars);
448181834Sroberto	while (nextvar(&len, &vars, &name, &value)) {
449181834Sroberto		vl = findlistvar(vlist, name);
450290000Sglebius		if (NULL == vl) {
451290000Sglebius			fprintf(stderr, "Variable list full\n");
452181834Sroberto			return;
453181834Sroberto		}
454181834Sroberto
455290000Sglebius		if (NULL == vl->name) {
456290000Sglebius			vl->name = estrdup(name);
457290000Sglebius		} else if (vl->value != NULL) {
458181834Sroberto			free(vl->value);
459290000Sglebius			vl->value = NULL;
460181834Sroberto		}
461181834Sroberto
462290000Sglebius		if (value != NULL)
463290000Sglebius			vl->value = estrdup(value);
464181834Sroberto	}
465181834Sroberto}
466181834Sroberto
467181834Sroberto
468181834Sroberto/*
469181834Sroberto * dormvlist - remove variable(s) from the variable list
470181834Sroberto */
471181834Srobertostatic void
472181834Srobertodormvlist(
473181834Sroberto	struct varlist *vlist,
474290000Sglebius	const char *vars
475181834Sroberto	)
476181834Sroberto{
477290000Sglebius	struct varlist *vl;
478293894Sglebius	size_t len;
479181834Sroberto	char *name;
480181834Sroberto	char *value;
481181834Sroberto
482181834Sroberto	len = strlen(vars);
483181834Sroberto	while (nextvar(&len, &vars, &name, &value)) {
484181834Sroberto		vl = findlistvar(vlist, name);
485181834Sroberto		if (vl == 0 || vl->name == 0) {
486181834Sroberto			(void) fprintf(stderr, "Variable `%s' not found\n",
487181834Sroberto				       name);
488181834Sroberto		} else {
489290000Sglebius			free((void *)(intptr_t)vl->name);
490181834Sroberto			if (vl->value != 0)
491181834Sroberto			    free(vl->value);
492290000Sglebius			for ( ; (vl+1) < (g_varlist + MAXLIST)
493181834Sroberto				      && (vl+1)->name != 0; vl++) {
494181834Sroberto				vl->name = (vl+1)->name;
495181834Sroberto				vl->value = (vl+1)->value;
496181834Sroberto			}
497181834Sroberto			vl->name = vl->value = 0;
498181834Sroberto		}
499181834Sroberto	}
500181834Sroberto}
501181834Sroberto
502181834Sroberto
503181834Sroberto/*
504181834Sroberto * doclearvlist - clear a variable list
505181834Sroberto */
506181834Srobertostatic void
507181834Srobertodoclearvlist(
508181834Sroberto	struct varlist *vlist
509181834Sroberto	)
510181834Sroberto{
511181834Sroberto	register struct varlist *vl;
512181834Sroberto
513181834Sroberto	for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
514290000Sglebius		free((void *)(intptr_t)vl->name);
515181834Sroberto		vl->name = 0;
516181834Sroberto		if (vl->value != 0) {
517181834Sroberto			free(vl->value);
518181834Sroberto			vl->value = 0;
519181834Sroberto		}
520181834Sroberto	}
521181834Sroberto}
522181834Sroberto
523181834Sroberto
524181834Sroberto/*
525181834Sroberto * makequerydata - form a data buffer to be included with a query
526181834Sroberto */
527181834Srobertostatic void
528181834Srobertomakequerydata(
529181834Sroberto	struct varlist *vlist,
530293894Sglebius	size_t *datalen,
531181834Sroberto	char *data
532181834Sroberto	)
533181834Sroberto{
534181834Sroberto	register struct varlist *vl;
535181834Sroberto	register char *cp, *cpend;
536293894Sglebius	register size_t namelen, valuelen;
537293894Sglebius	register size_t totallen;
538181834Sroberto
539181834Sroberto	cp = data;
540181834Sroberto	cpend = data + *datalen;
541181834Sroberto
542181834Sroberto	for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
543181834Sroberto		namelen = strlen(vl->name);
544181834Sroberto		if (vl->value == 0)
545181834Sroberto			valuelen = 0;
546181834Sroberto		else
547181834Sroberto			valuelen = strlen(vl->value);
548181834Sroberto		totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
549290000Sglebius		if (cp + totallen > cpend) {
550290000Sglebius		    fprintf(stderr,
551290000Sglebius			    "***Ignoring variables starting with `%s'\n",
552290000Sglebius			    vl->name);
553290000Sglebius		    break;
554290000Sglebius		}
555181834Sroberto
556181834Sroberto		if (cp != data)
557181834Sroberto			*cp++ = ',';
558290000Sglebius		memcpy(cp, vl->name, (size_t)namelen);
559181834Sroberto		cp += namelen;
560181834Sroberto		if (valuelen != 0) {
561181834Sroberto			*cp++ = '=';
562290000Sglebius			memcpy(cp, vl->value, (size_t)valuelen);
563181834Sroberto			cp += valuelen;
564181834Sroberto		}
565181834Sroberto	}
566293894Sglebius	*datalen = (size_t)(cp - data);
567181834Sroberto}
568181834Sroberto
569181834Sroberto
570181834Sroberto/*
571181834Sroberto * doquerylist - send a message including variables in a list
572181834Sroberto */
573181834Srobertostatic int
574181834Srobertodoquerylist(
575181834Sroberto	struct varlist *vlist,
576181834Sroberto	int op,
577290000Sglebius	associd_t associd,
578181834Sroberto	int auth,
579181834Sroberto	u_short *rstatus,
580293894Sglebius	size_t *dsize,
581290000Sglebius	const char **datap
582181834Sroberto	)
583181834Sroberto{
584181834Sroberto	char data[CTL_MAX_DATA_LEN];
585293894Sglebius	size_t datalen;
586181834Sroberto
587181834Sroberto	datalen = sizeof(data);
588181834Sroberto	makequerydata(vlist, &datalen, data);
589181834Sroberto
590290000Sglebius	return doquery(op, associd, auth, datalen, data, rstatus, dsize,
591290000Sglebius		       datap);
592181834Sroberto}
593181834Sroberto
594181834Sroberto
595181834Sroberto/*
596181834Sroberto * doprintvlist - print the variables on a list
597181834Sroberto */
598181834Srobertostatic void
599181834Srobertodoprintvlist(
600181834Sroberto	struct varlist *vlist,
601181834Sroberto	FILE *fp
602181834Sroberto	)
603181834Sroberto{
604290000Sglebius	size_t n;
605181834Sroberto
606290000Sglebius	if (NULL == vlist->name) {
607290000Sglebius		fprintf(fp, "No variables on list\n");
608290000Sglebius		return;
609181834Sroberto	}
610290000Sglebius	for (n = 0; n < MAXLIST && vlist[n].name != NULL; n++) {
611290000Sglebius		if (NULL == vlist[n].value)
612290000Sglebius			fprintf(fp, "%s\n", vlist[n].name);
613290000Sglebius		else
614290000Sglebius			fprintf(fp, "%s=%s\n", vlist[n].name,
615290000Sglebius				vlist[n].value);
616290000Sglebius	}
617181834Sroberto}
618181834Sroberto
619181834Sroberto/*
620181834Sroberto * addvars - add variables to the variable list
621181834Sroberto */
622181834Sroberto/*ARGSUSED*/
623181834Srobertostatic void
624181834Srobertoaddvars(
625181834Sroberto	struct parse *pcmd,
626181834Sroberto	FILE *fp
627181834Sroberto	)
628181834Sroberto{
629290000Sglebius	doaddvlist(g_varlist, pcmd->argval[0].string);
630181834Sroberto}
631181834Sroberto
632181834Sroberto
633181834Sroberto/*
634181834Sroberto * rmvars - remove variables from the variable list
635181834Sroberto */
636181834Sroberto/*ARGSUSED*/
637181834Srobertostatic void
638181834Srobertormvars(
639181834Sroberto	struct parse *pcmd,
640181834Sroberto	FILE *fp
641181834Sroberto	)
642181834Sroberto{
643290000Sglebius	dormvlist(g_varlist, pcmd->argval[0].string);
644181834Sroberto}
645181834Sroberto
646181834Sroberto
647181834Sroberto/*
648181834Sroberto * clearvars - clear the variable list
649181834Sroberto */
650181834Sroberto/*ARGSUSED*/
651181834Srobertostatic void
652181834Srobertoclearvars(
653181834Sroberto	struct parse *pcmd,
654181834Sroberto	FILE *fp
655181834Sroberto	)
656181834Sroberto{
657290000Sglebius	doclearvlist(g_varlist);
658181834Sroberto}
659181834Sroberto
660181834Sroberto
661181834Sroberto/*
662181834Sroberto * showvars - show variables on the variable list
663181834Sroberto */
664181834Sroberto/*ARGSUSED*/
665181834Srobertostatic void
666181834Srobertoshowvars(
667181834Sroberto	struct parse *pcmd,
668181834Sroberto	FILE *fp
669181834Sroberto	)
670181834Sroberto{
671290000Sglebius	doprintvlist(g_varlist, fp);
672181834Sroberto}
673181834Sroberto
674181834Sroberto
675181834Sroberto/*
676181834Sroberto * dolist - send a request with the given list of variables
677181834Sroberto */
678181834Srobertostatic int
679181834Srobertodolist(
680181834Sroberto	struct varlist *vlist,
681290000Sglebius	associd_t associd,
682181834Sroberto	int op,
683181834Sroberto	int type,
684181834Sroberto	FILE *fp
685181834Sroberto	)
686181834Sroberto{
687290000Sglebius	const char *datap;
688181834Sroberto	int res;
689293894Sglebius	size_t dsize;
690181834Sroberto	u_short rstatus;
691290000Sglebius	int quiet;
692181834Sroberto
693290000Sglebius	/*
694290000Sglebius	 * if we're asking for specific variables don't include the
695290000Sglebius	 * status header line in the output.
696290000Sglebius	 */
697290000Sglebius	if (old_rv)
698290000Sglebius		quiet = 0;
699290000Sglebius	else
700290000Sglebius		quiet = (vlist->name != NULL);
701290000Sglebius
702181834Sroberto	res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
703181834Sroberto
704181834Sroberto	if (res != 0)
705181834Sroberto		return 0;
706181834Sroberto
707181834Sroberto	if (numhosts > 1)
708290000Sglebius		fprintf(fp, "server=%s ", currenthost);
709181834Sroberto	if (dsize == 0) {
710181834Sroberto		if (associd == 0)
711290000Sglebius			fprintf(fp, "No system%s variables returned\n",
712290000Sglebius				(type == TYPE_CLOCK) ? " clock" : "");
713181834Sroberto		else
714290000Sglebius			fprintf(fp,
715290000Sglebius				"No information returned for%s association %u\n",
716290000Sglebius				(type == TYPE_CLOCK) ? " clock" : "",
717290000Sglebius				associd);
718181834Sroberto		return 1;
719181834Sroberto	}
720181834Sroberto
721290000Sglebius	if (!quiet)
722290000Sglebius		fprintf(fp, "associd=%u ", associd);
723290000Sglebius	printvars(dsize, datap, (int)rstatus, type, quiet, fp);
724181834Sroberto	return 1;
725181834Sroberto}
726181834Sroberto
727181834Sroberto
728181834Sroberto/*
729181834Sroberto * readlist - send a read variables request with the variables on the list
730181834Sroberto */
731181834Srobertostatic void
732181834Srobertoreadlist(
733181834Sroberto	struct parse *pcmd,
734181834Sroberto	FILE *fp
735181834Sroberto	)
736181834Sroberto{
737290000Sglebius	associd_t	associd;
738290000Sglebius	int		type;
739181834Sroberto
740181834Sroberto	if (pcmd->nargs == 0) {
741181834Sroberto		associd = 0;
742181834Sroberto	} else {
743181834Sroberto	  /* HMS: I think we want the u_int32 target here, not the u_long */
744181834Sroberto		if (pcmd->argval[0].uval == 0)
745181834Sroberto			associd = 0;
746181834Sroberto		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
747181834Sroberto			return;
748181834Sroberto	}
749181834Sroberto
750290000Sglebius	type = (0 == associd)
751290000Sglebius		   ? TYPE_SYS
752290000Sglebius		   : TYPE_PEER;
753290000Sglebius	dolist(g_varlist, associd, CTL_OP_READVAR, type, fp);
754181834Sroberto}
755181834Sroberto
756181834Sroberto
757181834Sroberto/*
758181834Sroberto * writelist - send a write variables request with the variables on the list
759181834Sroberto */
760181834Srobertostatic void
761181834Srobertowritelist(
762181834Sroberto	struct parse *pcmd,
763181834Sroberto	FILE *fp
764181834Sroberto	)
765181834Sroberto{
766290000Sglebius	const char *datap;
767181834Sroberto	int res;
768290000Sglebius	associd_t associd;
769293894Sglebius	size_t dsize;
770181834Sroberto	u_short rstatus;
771181834Sroberto
772181834Sroberto	if (pcmd->nargs == 0) {
773181834Sroberto		associd = 0;
774181834Sroberto	} else {
775181834Sroberto		/* HMS: Do we really want uval here? */
776181834Sroberto		if (pcmd->argval[0].uval == 0)
777181834Sroberto			associd = 0;
778181834Sroberto		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
779181834Sroberto			return;
780181834Sroberto	}
781181834Sroberto
782290000Sglebius	res = doquerylist(g_varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
783181834Sroberto			  &dsize, &datap);
784181834Sroberto
785181834Sroberto	if (res != 0)
786181834Sroberto		return;
787181834Sroberto
788181834Sroberto	if (numhosts > 1)
789181834Sroberto		(void) fprintf(fp, "server=%s ", currenthost);
790181834Sroberto	if (dsize == 0)
791181834Sroberto		(void) fprintf(fp, "done! (no data returned)\n");
792181834Sroberto	else {
793290000Sglebius		(void) fprintf(fp,"associd=%u ", associd);
794181834Sroberto		printvars(dsize, datap, (int)rstatus,
795290000Sglebius			  (associd != 0) ? TYPE_PEER : TYPE_SYS, 0, fp);
796181834Sroberto	}
797181834Sroberto	return;
798181834Sroberto}
799181834Sroberto
800181834Sroberto
801181834Sroberto/*
802181834Sroberto * readvar - send a read variables request with the specified variables
803181834Sroberto */
804181834Srobertostatic void
805181834Srobertoreadvar(
806181834Sroberto	struct parse *pcmd,
807181834Sroberto	FILE *fp
808181834Sroberto	)
809181834Sroberto{
810290000Sglebius	associd_t	associd;
811293894Sglebius	size_t		tmpcount;
812293894Sglebius	size_t		u;
813290000Sglebius	int		type;
814290000Sglebius	struct varlist	tmplist[MAXLIST];
815181834Sroberto
816290000Sglebius
817181834Sroberto	/* HMS: uval? */
818181834Sroberto	if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
819181834Sroberto		associd = 0;
820181834Sroberto	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
821181834Sroberto		return;
822181834Sroberto
823290000Sglebius	ZERO(tmplist);
824290000Sglebius	if (pcmd->nargs > 1) {
825290000Sglebius		tmpcount = pcmd->nargs - 1;
826290000Sglebius		for (u = 0; u < tmpcount; u++)
827290000Sglebius			doaddvlist(tmplist, pcmd->argval[1 + u].string);
828290000Sglebius	}
829181834Sroberto
830290000Sglebius	type = (0 == associd)
831290000Sglebius		   ? TYPE_SYS
832290000Sglebius		   : TYPE_PEER;
833290000Sglebius	dolist(tmplist, associd, CTL_OP_READVAR, type, fp);
834181834Sroberto
835181834Sroberto	doclearvlist(tmplist);
836181834Sroberto}
837181834Sroberto
838181834Sroberto
839181834Sroberto/*
840181834Sroberto * writevar - send a write variables request with the specified variables
841181834Sroberto */
842181834Srobertostatic void
843181834Srobertowritevar(
844181834Sroberto	struct parse *pcmd,
845181834Sroberto	FILE *fp
846181834Sroberto	)
847181834Sroberto{
848290000Sglebius	const char *datap;
849181834Sroberto	int res;
850290000Sglebius	associd_t associd;
851290000Sglebius	int type;
852293894Sglebius	size_t dsize;
853181834Sroberto	u_short rstatus;
854181834Sroberto	struct varlist tmplist[MAXLIST];
855181834Sroberto
856181834Sroberto	/* HMS: uval? */
857181834Sroberto	if (pcmd->argval[0].uval == 0)
858181834Sroberto		associd = 0;
859181834Sroberto	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
860181834Sroberto		return;
861181834Sroberto
862290000Sglebius	ZERO(tmplist);
863181834Sroberto	doaddvlist(tmplist, pcmd->argval[1].string);
864181834Sroberto
865181834Sroberto	res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
866181834Sroberto			  &dsize, &datap);
867181834Sroberto
868181834Sroberto	doclearvlist(tmplist);
869181834Sroberto
870181834Sroberto	if (res != 0)
871181834Sroberto		return;
872181834Sroberto
873181834Sroberto	if (numhosts > 1)
874290000Sglebius		fprintf(fp, "server=%s ", currenthost);
875181834Sroberto	if (dsize == 0)
876290000Sglebius		fprintf(fp, "done! (no data returned)\n");
877181834Sroberto	else {
878290000Sglebius		fprintf(fp,"associd=%u ", associd);
879290000Sglebius		type = (0 == associd)
880290000Sglebius			   ? TYPE_SYS
881290000Sglebius			   : TYPE_PEER;
882290000Sglebius		printvars(dsize, datap, (int)rstatus, type, 0, fp);
883181834Sroberto	}
884181834Sroberto	return;
885181834Sroberto}
886181834Sroberto
887181834Sroberto
888181834Sroberto/*
889181834Sroberto * clocklist - send a clock variables request with the variables on the list
890181834Sroberto */
891181834Srobertostatic void
892181834Srobertoclocklist(
893181834Sroberto	struct parse *pcmd,
894181834Sroberto	FILE *fp
895181834Sroberto	)
896181834Sroberto{
897290000Sglebius	associd_t associd;
898181834Sroberto
899181834Sroberto	/* HMS: uval? */
900181834Sroberto	if (pcmd->nargs == 0) {
901181834Sroberto		associd = 0;
902181834Sroberto	} else {
903181834Sroberto		if (pcmd->argval[0].uval == 0)
904181834Sroberto			associd = 0;
905181834Sroberto		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
906181834Sroberto			return;
907181834Sroberto	}
908181834Sroberto
909290000Sglebius	dolist(g_varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
910181834Sroberto}
911181834Sroberto
912181834Sroberto
913181834Sroberto/*
914181834Sroberto * clockvar - send a clock variables request with the specified variables
915181834Sroberto */
916181834Srobertostatic void
917181834Srobertoclockvar(
918181834Sroberto	struct parse *pcmd,
919181834Sroberto	FILE *fp
920181834Sroberto	)
921181834Sroberto{
922290000Sglebius	associd_t associd;
923181834Sroberto	struct varlist tmplist[MAXLIST];
924181834Sroberto
925181834Sroberto	/* HMS: uval? */
926181834Sroberto	if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
927181834Sroberto		associd = 0;
928181834Sroberto	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
929181834Sroberto		return;
930181834Sroberto
931290000Sglebius	ZERO(tmplist);
932181834Sroberto	if (pcmd->nargs >= 2)
933181834Sroberto		doaddvlist(tmplist, pcmd->argval[1].string);
934181834Sroberto
935290000Sglebius	dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
936181834Sroberto
937181834Sroberto	doclearvlist(tmplist);
938181834Sroberto}
939181834Sroberto
940181834Sroberto
941181834Sroberto/*
942181834Sroberto * findassidrange - verify a range of association ID's
943181834Sroberto */
944181834Srobertostatic int
945181834Srobertofindassidrange(
946290000Sglebius	u_int32	assid1,
947290000Sglebius	u_int32	assid2,
948290000Sglebius	int *	from,
949290000Sglebius	int *	to,
950290000Sglebius	FILE *	fp
951181834Sroberto	)
952181834Sroberto{
953290000Sglebius	associd_t	assids[2];
954290000Sglebius	int		ind[COUNTOF(assids)];
955290000Sglebius	u_int		i;
956290000Sglebius	size_t		a;
957181834Sroberto
958290000Sglebius
959290000Sglebius	if (0 == numassoc)
960290000Sglebius		dogetassoc(fp);
961290000Sglebius
962290000Sglebius	assids[0] = checkassocid(assid1);
963290000Sglebius	if (0 == assids[0])
964181834Sroberto		return 0;
965290000Sglebius	assids[1] = checkassocid(assid2);
966290000Sglebius	if (0 == assids[1])
967290000Sglebius		return 0;
968181834Sroberto
969290000Sglebius	for (a = 0; a < COUNTOF(assids); a++) {
970290000Sglebius		ind[a] = -1;
971290000Sglebius		for (i = 0; i < numassoc; i++)
972290000Sglebius			if (assoc_cache[i].assid == assids[a])
973290000Sglebius				ind[a] = i;
974181834Sroberto	}
975290000Sglebius	for (a = 0; a < COUNTOF(assids); a++)
976290000Sglebius		if (-1 == ind[a]) {
977290000Sglebius			fprintf(stderr,
978290000Sglebius				"***Association ID %u not found in list\n",
979290000Sglebius				assids[a]);
980290000Sglebius			return 0;
981181834Sroberto		}
982181834Sroberto
983290000Sglebius	if (ind[0] < ind[1]) {
984290000Sglebius		*from = ind[0];
985290000Sglebius		*to = ind[1];
986181834Sroberto	} else {
987290000Sglebius		*to = ind[0];
988290000Sglebius		*from = ind[1];
989181834Sroberto	}
990181834Sroberto	return 1;
991181834Sroberto}
992181834Sroberto
993181834Sroberto
994181834Sroberto
995181834Sroberto/*
996181834Sroberto * mreadlist - send a read variables request for multiple associations
997181834Sroberto */
998181834Srobertostatic void
999181834Srobertomreadlist(
1000181834Sroberto	struct parse *pcmd,
1001181834Sroberto	FILE *fp
1002181834Sroberto	)
1003181834Sroberto{
1004181834Sroberto	int i;
1005181834Sroberto	int from;
1006181834Sroberto	int to;
1007181834Sroberto
1008181834Sroberto	if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1009290000Sglebius			    &from, &to, fp))
1010181834Sroberto		return;
1011181834Sroberto
1012181834Sroberto	for (i = from; i <= to; i++) {
1013181834Sroberto		if (i != from)
1014290000Sglebius			fprintf(fp, "\n");
1015290000Sglebius		if (!dolist(g_varlist, assoc_cache[i].assid,
1016290000Sglebius			    CTL_OP_READVAR, TYPE_PEER, fp))
1017181834Sroberto			return;
1018181834Sroberto	}
1019181834Sroberto	return;
1020181834Sroberto}
1021181834Sroberto
1022181834Sroberto
1023181834Sroberto/*
1024181834Sroberto * mreadvar - send a read variables request for multiple associations
1025181834Sroberto */
1026181834Srobertostatic void
1027181834Srobertomreadvar(
1028181834Sroberto	struct parse *pcmd,
1029181834Sroberto	FILE *fp
1030181834Sroberto	)
1031181834Sroberto{
1032181834Sroberto	int i;
1033181834Sroberto	int from;
1034181834Sroberto	int to;
1035181834Sroberto	struct varlist tmplist[MAXLIST];
1036290000Sglebius	struct varlist *pvars;
1037181834Sroberto
1038181834Sroberto	if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1039290000Sglebius				&from, &to, fp))
1040181834Sroberto		return;
1041181834Sroberto
1042290000Sglebius	ZERO(tmplist);
1043290000Sglebius	if (pcmd->nargs >= 3) {
1044181834Sroberto		doaddvlist(tmplist, pcmd->argval[2].string);
1045290000Sglebius		pvars = tmplist;
1046290000Sglebius	} else {
1047290000Sglebius		pvars = g_varlist;
1048290000Sglebius	}
1049181834Sroberto
1050181834Sroberto	for (i = from; i <= to; i++) {
1051290000Sglebius		if (!dolist(pvars, assoc_cache[i].assid, CTL_OP_READVAR,
1052290000Sglebius			    TYPE_PEER, fp))
1053181834Sroberto			break;
1054181834Sroberto	}
1055290000Sglebius
1056290000Sglebius	if (pvars == tmplist)
1057290000Sglebius		doclearvlist(tmplist);
1058290000Sglebius
1059181834Sroberto	return;
1060181834Sroberto}
1061181834Sroberto
1062181834Sroberto
1063181834Sroberto/*
1064181834Sroberto * dogetassoc - query the host for its list of associations
1065181834Sroberto */
1066290000Sglebiusint
1067181834Srobertodogetassoc(
1068181834Sroberto	FILE *fp
1069181834Sroberto	)
1070181834Sroberto{
1071290000Sglebius	const char *datap;
1072290000Sglebius	const u_short *pus;
1073181834Sroberto	int res;
1074293894Sglebius	size_t dsize;
1075181834Sroberto	u_short rstatus;
1076181834Sroberto
1077181834Sroberto	res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
1078181834Sroberto			  &dsize, &datap);
1079181834Sroberto
1080181834Sroberto	if (res != 0)
1081181834Sroberto		return 0;
1082181834Sroberto
1083181834Sroberto	if (dsize == 0) {
1084181834Sroberto		if (numhosts > 1)
1085290000Sglebius			fprintf(fp, "server=%s ", currenthost);
1086290000Sglebius		fprintf(fp, "No association ID's returned\n");
1087181834Sroberto		return 0;
1088181834Sroberto	}
1089181834Sroberto
1090181834Sroberto	if (dsize & 0x3) {
1091181834Sroberto		if (numhosts > 1)
1092290000Sglebius			fprintf(stderr, "server=%s ", currenthost);
1093290000Sglebius		fprintf(stderr,
1094293894Sglebius			"***Server returned %zu octets, should be multiple of 4\n",
1095290000Sglebius			dsize);
1096181834Sroberto		return 0;
1097181834Sroberto	}
1098181834Sroberto
1099181834Sroberto	numassoc = 0;
1100290000Sglebius
1101181834Sroberto	while (dsize > 0) {
1102290000Sglebius		if (numassoc >= assoc_cache_slots) {
1103290000Sglebius			grow_assoc_cache();
1104290000Sglebius		}
1105290000Sglebius		pus = (const void *)datap;
1106290000Sglebius		assoc_cache[numassoc].assid = ntohs(*pus);
1107290000Sglebius		datap += sizeof(*pus);
1108290000Sglebius		pus = (const void *)datap;
1109290000Sglebius		assoc_cache[numassoc].status = ntohs(*pus);
1110290000Sglebius		datap += sizeof(*pus);
1111290000Sglebius		dsize -= 2 * sizeof(*pus);
1112290000Sglebius		if (debug) {
1113290000Sglebius			fprintf(stderr, "[%u] ",
1114290000Sglebius				assoc_cache[numassoc].assid);
1115290000Sglebius		}
1116290000Sglebius		numassoc++;
1117181834Sroberto	}
1118290000Sglebius	if (debug) {
1119290000Sglebius		fprintf(stderr, "\n%d associations total\n", numassoc);
1120290000Sglebius	}
1121181834Sroberto	sortassoc();
1122181834Sroberto	return 1;
1123181834Sroberto}
1124181834Sroberto
1125181834Sroberto
1126181834Sroberto/*
1127181834Sroberto * printassoc - print the current list of associations
1128181834Sroberto */
1129181834Srobertostatic void
1130181834Srobertoprintassoc(
1131181834Sroberto	int showall,
1132181834Sroberto	FILE *fp
1133181834Sroberto	)
1134181834Sroberto{
1135181834Sroberto	register char *bp;
1136290000Sglebius	u_int i;
1137181834Sroberto	u_char statval;
1138181834Sroberto	int event;
1139181834Sroberto	u_long event_count;
1140181834Sroberto	const char *conf;
1141181834Sroberto	const char *reach;
1142181834Sroberto	const char *auth;
1143181834Sroberto	const char *condition = "";
1144181834Sroberto	const char *last_event;
1145181834Sroberto	char buf[128];
1146181834Sroberto
1147181834Sroberto	if (numassoc == 0) {
1148181834Sroberto		(void) fprintf(fp, "No association ID's in list\n");
1149181834Sroberto		return;
1150181834Sroberto	}
1151181834Sroberto
1152181834Sroberto	/*
1153181834Sroberto	 * Output a header
1154181834Sroberto	 */
1155181834Sroberto	(void) fprintf(fp,
1156290000Sglebius			   "\nind assid status  conf reach auth condition  last_event cnt\n");
1157181834Sroberto	(void) fprintf(fp,
1158181834Sroberto			   "===========================================================\n");
1159181834Sroberto	for (i = 0; i < numassoc; i++) {
1160181834Sroberto		statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status);
1161181834Sroberto		if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
1162181834Sroberto			continue;
1163181834Sroberto		event = CTL_PEER_EVENT(assoc_cache[i].status);
1164181834Sroberto		event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
1165181834Sroberto		if (statval & CTL_PST_CONFIG)
1166181834Sroberto			conf = "yes";
1167181834Sroberto		else
1168181834Sroberto			conf = "no";
1169290000Sglebius		if (statval & CTL_PST_BCAST) {
1170290000Sglebius			reach = "none";
1171290000Sglebius			if (statval & CTL_PST_AUTHENABLE)
1172290000Sglebius				auth = "yes";
1173290000Sglebius			else
1174290000Sglebius				auth = "none";
1175290000Sglebius		} else {
1176290000Sglebius			if (statval & CTL_PST_REACH)
1177290000Sglebius				reach = "yes";
1178290000Sglebius			else
1179290000Sglebius				reach = "no";
1180181834Sroberto			if (statval & CTL_PST_AUTHENABLE) {
1181181834Sroberto				if (statval & CTL_PST_AUTHENTIC)
1182181834Sroberto					auth = "ok ";
1183181834Sroberto				else
1184181834Sroberto					auth = "bad";
1185290000Sglebius			} else {
1186181834Sroberto				auth = "none";
1187290000Sglebius			}
1188290000Sglebius		}
1189290000Sglebius		if (pktversion > NTP_OLDVERSION) {
1190290000Sglebius			switch (statval & 0x7) {
1191181834Sroberto
1192290000Sglebius			case CTL_PST_SEL_REJECT:
1193290000Sglebius				condition = "reject";
1194290000Sglebius				break;
1195290000Sglebius
1196290000Sglebius			case CTL_PST_SEL_SANE:
1197290000Sglebius				condition = "falsetick";
1198290000Sglebius				break;
1199290000Sglebius
1200290000Sglebius			case CTL_PST_SEL_CORRECT:
1201290000Sglebius				condition = "excess";
1202290000Sglebius				break;
1203290000Sglebius
1204290000Sglebius			case CTL_PST_SEL_SELCAND:
1205290000Sglebius				condition = "outlier";
1206290000Sglebius				break;
1207290000Sglebius
1208290000Sglebius			case CTL_PST_SEL_SYNCCAND:
1209290000Sglebius				condition = "candidate";
1210290000Sglebius				break;
1211290000Sglebius
1212290000Sglebius			case CTL_PST_SEL_EXCESS:
1213290000Sglebius				condition = "backup";
1214290000Sglebius				break;
1215290000Sglebius
1216290000Sglebius			case CTL_PST_SEL_SYSPEER:
1217290000Sglebius				condition = "sys.peer";
1218290000Sglebius				break;
1219290000Sglebius
1220290000Sglebius			case CTL_PST_SEL_PPS:
1221290000Sglebius				condition = "pps.peer";
1222290000Sglebius				break;
1223290000Sglebius			}
1224290000Sglebius		} else {
1225290000Sglebius			switch (statval & 0x3) {
1226290000Sglebius
1227290000Sglebius			case OLD_CTL_PST_SEL_REJECT:
1228290000Sglebius				if (!(statval & OLD_CTL_PST_SANE))
1229181834Sroberto					condition = "insane";
1230290000Sglebius				else if (!(statval & OLD_CTL_PST_DISP))
1231181834Sroberto					condition = "hi_disp";
1232290000Sglebius				else
1233181834Sroberto					condition = "";
1234290000Sglebius				break;
1235181834Sroberto
1236290000Sglebius			case OLD_CTL_PST_SEL_SELCAND:
1237290000Sglebius				condition = "sel_cand";
1238290000Sglebius				break;
1239290000Sglebius
1240290000Sglebius			case OLD_CTL_PST_SEL_SYNCCAND:
1241290000Sglebius				condition = "sync_cand";
1242290000Sglebius				break;
1243290000Sglebius
1244290000Sglebius			case OLD_CTL_PST_SEL_SYSPEER:
1245290000Sglebius				condition = "sys_peer";
1246290000Sglebius				break;
1247290000Sglebius			}
1248181834Sroberto		}
1249290000Sglebius		switch (PEER_EVENT|event) {
1250181834Sroberto
1251290000Sglebius		case PEVNT_MOBIL:
1252290000Sglebius			last_event = "mobilize";
1253181834Sroberto			break;
1254290000Sglebius
1255290000Sglebius		case PEVNT_DEMOBIL:
1256290000Sglebius			last_event = "demobilize";
1257181834Sroberto			break;
1258290000Sglebius
1259290000Sglebius		case PEVNT_REACH:
1260181834Sroberto			last_event = "reachable";
1261181834Sroberto			break;
1262290000Sglebius
1263290000Sglebius		case PEVNT_UNREACH:
1264290000Sglebius			last_event = "unreachable";
1265181834Sroberto			break;
1266290000Sglebius
1267290000Sglebius		case PEVNT_RESTART:
1268290000Sglebius			last_event = "restart";
1269181834Sroberto			break;
1270290000Sglebius
1271290000Sglebius		case PEVNT_REPLY:
1272290000Sglebius			last_event = "no_reply";
1273290000Sglebius			break;
1274290000Sglebius
1275290000Sglebius		case PEVNT_RATE:
1276290000Sglebius			last_event = "rate_exceeded";
1277290000Sglebius			break;
1278290000Sglebius
1279290000Sglebius		case PEVNT_DENY:
1280290000Sglebius			last_event = "access_denied";
1281290000Sglebius			break;
1282290000Sglebius
1283290000Sglebius		case PEVNT_ARMED:
1284290000Sglebius			last_event = "leap_armed";
1285290000Sglebius			break;
1286290000Sglebius
1287290000Sglebius		case PEVNT_NEWPEER:
1288290000Sglebius			last_event = "sys_peer";
1289290000Sglebius			break;
1290290000Sglebius
1291290000Sglebius		case PEVNT_CLOCK:
1292290000Sglebius			last_event = "clock_alarm";
1293290000Sglebius			break;
1294290000Sglebius
1295290000Sglebius		default:
1296181834Sroberto			last_event = "";
1297181834Sroberto			break;
1298181834Sroberto		}
1299290000Sglebius		snprintf(buf, sizeof(buf),
1300290000Sglebius			 "%3d %5u  %04x   %3.3s  %4s  %4.4s %9.9s %11s %2lu",
1301290000Sglebius			 i + 1, assoc_cache[i].assid,
1302290000Sglebius			 assoc_cache[i].status, conf, reach, auth,
1303290000Sglebius			 condition, last_event, event_count);
1304290000Sglebius		bp = buf + strlen(buf);
1305290000Sglebius		while (bp > buf && ' ' == bp[-1])
1306290000Sglebius			--bp;
1307290000Sglebius		bp[0] = '\0';
1308290000Sglebius		fprintf(fp, "%s\n", buf);
1309181834Sroberto	}
1310181834Sroberto}
1311181834Sroberto
1312181834Sroberto
1313181834Sroberto/*
1314181834Sroberto * associations - get, record and print a list of associations
1315181834Sroberto */
1316181834Sroberto/*ARGSUSED*/
1317181834Srobertostatic void
1318181834Srobertoassociations(
1319181834Sroberto	struct parse *pcmd,
1320181834Sroberto	FILE *fp
1321181834Sroberto	)
1322181834Sroberto{
1323181834Sroberto	if (dogetassoc(fp))
1324181834Sroberto		printassoc(0, fp);
1325181834Sroberto}
1326181834Sroberto
1327181834Sroberto
1328181834Sroberto/*
1329181834Sroberto * lassociations - get, record and print a long list of associations
1330181834Sroberto */
1331181834Sroberto/*ARGSUSED*/
1332181834Srobertostatic void
1333181834Srobertolassociations(
1334181834Sroberto	struct parse *pcmd,
1335181834Sroberto	FILE *fp
1336181834Sroberto	)
1337181834Sroberto{
1338181834Sroberto	if (dogetassoc(fp))
1339181834Sroberto		printassoc(1, fp);
1340181834Sroberto}
1341181834Sroberto
1342181834Sroberto
1343181834Sroberto/*
1344181834Sroberto * passociations - print the association list
1345181834Sroberto */
1346181834Sroberto/*ARGSUSED*/
1347181834Srobertostatic void
1348181834Srobertopassociations(
1349181834Sroberto	struct parse *pcmd,
1350181834Sroberto	FILE *fp
1351181834Sroberto	)
1352181834Sroberto{
1353181834Sroberto	printassoc(0, fp);
1354181834Sroberto}
1355181834Sroberto
1356181834Sroberto
1357181834Sroberto/*
1358181834Sroberto * lpassociations - print the long association list
1359181834Sroberto */
1360181834Sroberto/*ARGSUSED*/
1361181834Srobertostatic void
1362181834Srobertolpassociations(
1363181834Sroberto	struct parse *pcmd,
1364181834Sroberto	FILE *fp
1365181834Sroberto	)
1366181834Sroberto{
1367181834Sroberto	printassoc(1, fp);
1368181834Sroberto}
1369181834Sroberto
1370181834Sroberto
1371181834Sroberto/*
1372290000Sglebius *  saveconfig - dump ntp server configuration to server file
1373181834Sroberto */
1374181834Srobertostatic void
1375290000Sglebiussaveconfig(
1376181834Sroberto	struct parse *pcmd,
1377181834Sroberto	FILE *fp
1378181834Sroberto	)
1379181834Sroberto{
1380290000Sglebius	const char *datap;
1381181834Sroberto	int res;
1382293894Sglebius	size_t dsize;
1383181834Sroberto	u_short rstatus;
1384181834Sroberto
1385290000Sglebius	if (0 == pcmd->nargs)
1386290000Sglebius		return;
1387290000Sglebius
1388290000Sglebius	res = doquery(CTL_OP_SAVECONFIG, 0, 1,
1389290000Sglebius		      strlen(pcmd->argval[0].string),
1390290000Sglebius		      pcmd->argval[0].string, &rstatus, &dsize,
1391290000Sglebius		      &datap);
1392181834Sroberto
1393181834Sroberto	if (res != 0)
1394181834Sroberto		return;
1395181834Sroberto
1396290000Sglebius	if (0 == dsize)
1397290000Sglebius		fprintf(fp, "(no response message, curiously)");
1398290000Sglebius	else
1399293894Sglebius		fprintf(fp, "%.*s", (int)dsize, datap); /* cast is wobbly */
1400181834Sroberto}
1401181834Sroberto
1402290000Sglebius
1403290000Sglebius#ifdef	UNUSED
1404181834Sroberto/*
1405290000Sglebius * radiostatus - print the radio status returned by the server
1406181834Sroberto */
1407290000Sglebius/*ARGSUSED*/
1408181834Srobertostatic void
1409290000Sglebiusradiostatus(
1410181834Sroberto	struct parse *pcmd,
1411181834Sroberto	FILE *fp
1412181834Sroberto	)
1413181834Sroberto{
1414181834Sroberto	char *datap;
1415181834Sroberto	int res;
1416181834Sroberto	int dsize;
1417181834Sroberto	u_short rstatus;
1418181834Sroberto
1419290000Sglebius	res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
1420181834Sroberto			  &dsize, &datap);
1421181834Sroberto
1422181834Sroberto	if (res != 0)
1423181834Sroberto		return;
1424181834Sroberto
1425181834Sroberto	if (numhosts > 1)
1426181834Sroberto		(void) fprintf(fp, "server=%s ", currenthost);
1427181834Sroberto	if (dsize == 0) {
1428290000Sglebius		(void) fprintf(fp, "No radio status string returned\n");
1429181834Sroberto		return;
1430181834Sroberto	}
1431181834Sroberto
1432290000Sglebius	asciize(dsize, datap, fp);
1433181834Sroberto}
1434290000Sglebius#endif	/* UNUSED */
1435181834Sroberto
1436181834Sroberto/*
1437181834Sroberto * when - print how long its been since his last packet arrived
1438181834Sroberto */
1439181834Srobertostatic long
1440181834Srobertowhen(
1441181834Sroberto	l_fp *ts,
1442181834Sroberto	l_fp *rec,
1443181834Sroberto	l_fp *reftime
1444181834Sroberto	)
1445181834Sroberto{
1446181834Sroberto	l_fp *lasttime;
1447181834Sroberto
1448181834Sroberto	if (rec->l_ui != 0)
1449181834Sroberto		lasttime = rec;
1450181834Sroberto	else if (reftime->l_ui != 0)
1451181834Sroberto		lasttime = reftime;
1452181834Sroberto	else
1453181834Sroberto		return 0;
1454181834Sroberto
1455181834Sroberto	return (ts->l_ui - lasttime->l_ui);
1456181834Sroberto}
1457181834Sroberto
1458181834Sroberto
1459181834Sroberto/*
1460181834Sroberto * Pretty-print an interval into the given buffer, in a human-friendly format.
1461181834Sroberto */
1462181834Srobertostatic char *
1463181834Srobertoprettyinterval(
1464181834Sroberto	char *buf,
1465290000Sglebius	size_t cb,
1466181834Sroberto	long diff
1467181834Sroberto	)
1468181834Sroberto{
1469181834Sroberto	if (diff <= 0) {
1470181834Sroberto		buf[0] = '-';
1471181834Sroberto		buf[1] = 0;
1472181834Sroberto		return buf;
1473181834Sroberto	}
1474181834Sroberto
1475181834Sroberto	if (diff <= 2048) {
1476290000Sglebius		snprintf(buf, cb, "%ld", diff);
1477181834Sroberto		return buf;
1478181834Sroberto	}
1479181834Sroberto
1480181834Sroberto	diff = (diff + 29) / 60;
1481181834Sroberto	if (diff <= 300) {
1482290000Sglebius		snprintf(buf, cb, "%ldm", diff);
1483181834Sroberto		return buf;
1484181834Sroberto	}
1485181834Sroberto
1486181834Sroberto	diff = (diff + 29) / 60;
1487181834Sroberto	if (diff <= 96) {
1488290000Sglebius		snprintf(buf, cb, "%ldh", diff);
1489181834Sroberto		return buf;
1490181834Sroberto	}
1491181834Sroberto
1492181834Sroberto	diff = (diff + 11) / 24;
1493290000Sglebius	snprintf(buf, cb, "%ldd", diff);
1494181834Sroberto	return buf;
1495181834Sroberto}
1496181834Sroberto
1497181834Srobertostatic char
1498181834Srobertodecodeaddrtype(
1499290000Sglebius	sockaddr_u *sock
1500181834Sroberto	)
1501181834Sroberto{
1502181834Sroberto	char ch = '-';
1503181834Sroberto	u_int32 dummy;
1504181834Sroberto
1505290000Sglebius	switch(AF(sock)) {
1506181834Sroberto	case AF_INET:
1507290000Sglebius		dummy = SRCADR(sock);
1508181834Sroberto		ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' :
1509181834Sroberto			((dummy&0x000000ff)==0x000000ff) ? 'b' :
1510181834Sroberto			((dummy&0xffffffff)==0x7f000001) ? 'l' :
1511181834Sroberto			((dummy&0xffffffe0)==0x00000000) ? '-' :
1512181834Sroberto			'u');
1513181834Sroberto		break;
1514181834Sroberto	case AF_INET6:
1515290000Sglebius		if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock)))
1516181834Sroberto			ch = 'm';
1517181834Sroberto		else
1518181834Sroberto			ch = 'u';
1519181834Sroberto		break;
1520181834Sroberto	default:
1521181834Sroberto		ch = '-';
1522181834Sroberto		break;
1523181834Sroberto	}
1524181834Sroberto	return ch;
1525181834Sroberto}
1526181834Sroberto
1527181834Sroberto/*
1528181834Sroberto * A list of variables required by the peers command
1529181834Sroberto */
1530181834Srobertostruct varlist opeervarlist[] = {
1531290000Sglebius	{ "srcadr",	0 },	/* 0 */
1532290000Sglebius	{ "dstadr",	0 },	/* 1 */
1533290000Sglebius	{ "stratum",	0 },	/* 2 */
1534290000Sglebius	{ "hpoll",	0 },	/* 3 */
1535290000Sglebius	{ "ppoll",	0 },	/* 4 */
1536290000Sglebius	{ "reach",	0 },	/* 5 */
1537290000Sglebius	{ "delay",	0 },	/* 6 */
1538290000Sglebius	{ "offset",	0 },	/* 7 */
1539290000Sglebius	{ "jitter",	0 },	/* 8 */
1540290000Sglebius	{ "dispersion", 0 },	/* 9 */
1541290000Sglebius	{ "rec",	0 },	/* 10 */
1542290000Sglebius	{ "reftime",	0 },	/* 11 */
1543290000Sglebius	{ "srcport",	0 },	/* 12 */
1544290000Sglebius	{ "hmode",	0 },	/* 13 */
1545181834Sroberto	{ 0,		0 }
1546181834Sroberto};
1547181834Sroberto
1548181834Srobertostruct varlist peervarlist[] = {
1549290000Sglebius	{ "srcadr",	0 },	/* 0 */
1550290000Sglebius	{ "refid",	0 },	/* 1 */
1551290000Sglebius	{ "stratum",	0 },	/* 2 */
1552290000Sglebius	{ "hpoll",	0 },	/* 3 */
1553290000Sglebius	{ "ppoll",	0 },	/* 4 */
1554290000Sglebius	{ "reach",	0 },	/* 5 */
1555290000Sglebius	{ "delay",	0 },	/* 6 */
1556290000Sglebius	{ "offset",	0 },	/* 7 */
1557290000Sglebius	{ "jitter",	0 },	/* 8 */
1558290000Sglebius	{ "dispersion", 0 },	/* 9 */
1559290000Sglebius	{ "rec",	0 },	/* 10 */
1560290000Sglebius	{ "reftime",	0 },	/* 11 */
1561290000Sglebius	{ "srcport",	0 },	/* 12 */
1562290000Sglebius	{ "hmode",	0 },	/* 13 */
1563290000Sglebius	{ "srchost",	0 },	/* 14 */
1564181834Sroberto	{ 0,		0 }
1565181834Sroberto};
1566181834Sroberto
1567290000Sglebiusstruct varlist apeervarlist[] = {
1568290000Sglebius	{ "srcadr",	0 },	/* 0 */
1569290000Sglebius	{ "refid",	0 },	/* 1 */
1570290000Sglebius	{ "assid",	0 },	/* 2 */
1571290000Sglebius	{ "stratum",	0 },	/* 3 */
1572290000Sglebius	{ "hpoll",	0 },	/* 4 */
1573290000Sglebius	{ "ppoll",	0 },	/* 5 */
1574290000Sglebius	{ "reach",	0 },	/* 6 */
1575290000Sglebius	{ "delay",	0 },	/* 7 */
1576290000Sglebius	{ "offset",	0 },	/* 8 */
1577290000Sglebius	{ "jitter",	0 },	/* 9 */
1578290000Sglebius	{ "dispersion", 0 },	/* 10 */
1579290000Sglebius	{ "rec",	0 },	/* 11 */
1580290000Sglebius	{ "reftime",	0 },	/* 12 */
1581290000Sglebius	{ "srcport",	0 },	/* 13 */
1582290000Sglebius	{ "hmode",	0 },	/* 14 */
1583290000Sglebius	{ "srchost",	0 },	/* 15 */
1584290000Sglebius	{ 0,		0 }
1585290000Sglebius};
1586181834Sroberto
1587290000Sglebius
1588181834Sroberto/*
1589181834Sroberto * Decode an incoming data buffer and print a line in the peer list
1590181834Sroberto */
1591181834Srobertostatic int
1592181834Srobertodoprintpeers(
1593181834Sroberto	struct varlist *pvl,
1594181834Sroberto	int associd,
1595181834Sroberto	int rstatus,
1596293894Sglebius	size_t datalen,
1597290000Sglebius	const char *data,
1598181834Sroberto	FILE *fp,
1599181834Sroberto	int af
1600181834Sroberto	)
1601181834Sroberto{
1602181834Sroberto	char *name;
1603181834Sroberto	char *value = NULL;
1604181834Sroberto	int c;
1605293894Sglebius	size_t len;
1606290000Sglebius	int have_srchost;
1607290000Sglebius	int have_dstadr;
1608290000Sglebius	int have_da_rid;
1609290000Sglebius	int have_jitter;
1610290000Sglebius	sockaddr_u srcadr;
1611290000Sglebius	sockaddr_u dstadr;
1612290000Sglebius	sockaddr_u dum_store;
1613290000Sglebius	sockaddr_u refidadr;
1614290000Sglebius	long hmode = 0;
1615181834Sroberto	u_long srcport = 0;
1616290000Sglebius	u_int32 u32;
1617290000Sglebius	const char *dstadr_refid = "0.0.0.0";
1618290000Sglebius	const char *serverlocal;
1619290000Sglebius	size_t drlen;
1620181834Sroberto	u_long stratum = 0;
1621181834Sroberto	long ppoll = 0;
1622181834Sroberto	long hpoll = 0;
1623181834Sroberto	u_long reach = 0;
1624181834Sroberto	l_fp estoffset;
1625181834Sroberto	l_fp estdelay;
1626181834Sroberto	l_fp estjitter;
1627181834Sroberto	l_fp estdisp;
1628181834Sroberto	l_fp reftime;
1629181834Sroberto	l_fp rec;
1630181834Sroberto	l_fp ts;
1631181834Sroberto	u_long poll_sec;
1632181834Sroberto	char type = '?';
1633181834Sroberto	char whenbuf[8], pollbuf[8];
1634181834Sroberto	char clock_name[LENHOSTNAME];
1635181834Sroberto
1636181834Sroberto	get_systime(&ts);
1637181834Sroberto
1638290000Sglebius	have_srchost = FALSE;
1639290000Sglebius	have_dstadr = FALSE;
1640290000Sglebius	have_da_rid = FALSE;
1641290000Sglebius	have_jitter = FALSE;
1642290000Sglebius	ZERO_SOCK(&srcadr);
1643290000Sglebius	ZERO_SOCK(&dstadr);
1644290000Sglebius	clock_name[0] = '\0';
1645290000Sglebius	ZERO(estoffset);
1646290000Sglebius	ZERO(estdelay);
1647290000Sglebius	ZERO(estjitter);
1648290000Sglebius	ZERO(estdisp);
1649181834Sroberto
1650181834Sroberto	while (nextvar(&datalen, &data, &name, &value)) {
1651290000Sglebius		if (!strcmp("srcadr", name) ||
1652290000Sglebius		    !strcmp("peeradr", name)) {
1653290000Sglebius			if (!decodenetnum(value, &srcadr))
1654290000Sglebius				fprintf(stderr, "malformed %s=%s\n",
1655290000Sglebius					name, value);
1656290000Sglebius		} else if (!strcmp("srchost", name)) {
1657290000Sglebius			if (pvl == peervarlist || pvl == apeervarlist) {
1658290000Sglebius				len = strlen(value);
1659290000Sglebius				if (2 < len &&
1660290000Sglebius				    (size_t)len < sizeof(clock_name)) {
1661290000Sglebius					/* strip quotes */
1662290000Sglebius					value++;
1663290000Sglebius					len -= 2;
1664290000Sglebius					memcpy(clock_name, value, len);
1665290000Sglebius					clock_name[len] = '\0';
1666290000Sglebius					have_srchost = TRUE;
1667290000Sglebius				}
1668290000Sglebius			}
1669290000Sglebius		} else if (!strcmp("dstadr", name)) {
1670290000Sglebius			if (decodenetnum(value, &dum_store)) {
1671181834Sroberto				type = decodeaddrtype(&dum_store);
1672290000Sglebius				have_dstadr = TRUE;
1673290000Sglebius				dstadr = dum_store;
1674290000Sglebius				if (pvl == opeervarlist) {
1675290000Sglebius					have_da_rid = TRUE;
1676290000Sglebius					dstadr_refid = trunc_left(stoa(&dstadr), 15);
1677181834Sroberto				}
1678181834Sroberto			}
1679290000Sglebius		} else if (!strcmp("hmode", name)) {
1680290000Sglebius			decodeint(value, &hmode);
1681290000Sglebius		} else if (!strcmp("refid", name)) {
1682298770Sdelphij			if (   (pvl == peervarlist)
1683298770Sdelphij			    && (drefid == REFID_IPV4)) {
1684290000Sglebius				have_da_rid = TRUE;
1685290000Sglebius				drlen = strlen(value);
1686290000Sglebius				if (0 == drlen) {
1687290000Sglebius					dstadr_refid = "";
1688290000Sglebius				} else if (drlen <= 4) {
1689290000Sglebius					ZERO(u32);
1690290000Sglebius					memcpy(&u32, value, drlen);
1691290000Sglebius					dstadr_refid = refid_str(u32, 1);
1692290000Sglebius				} else if (decodenetnum(value, &refidadr)) {
1693290000Sglebius					if (SOCK_UNSPEC(&refidadr))
1694181834Sroberto						dstadr_refid = "0.0.0.0";
1695290000Sglebius					else if (ISREFCLOCKADR(&refidadr))
1696290000Sglebius						dstadr_refid =
1697290000Sglebius						    refnumtoa(&refidadr);
1698181834Sroberto					else
1699181834Sroberto						dstadr_refid =
1700290000Sglebius						    stoa(&refidadr);
1701181834Sroberto				} else {
1702290000Sglebius					have_da_rid = FALSE;
1703181834Sroberto				}
1704298770Sdelphij			} else if (   (pvl == apeervarlist)
1705298770Sdelphij				   || (pvl == peervarlist)) {
1706298770Sdelphij				/* no need to check drefid == REFID_HASH */
1707290000Sglebius				have_da_rid = TRUE;
1708290000Sglebius				drlen = strlen(value);
1709290000Sglebius				if (0 == drlen) {
1710290000Sglebius					dstadr_refid = "";
1711290000Sglebius				} else if (drlen <= 4) {
1712290000Sglebius					ZERO(u32);
1713290000Sglebius					memcpy(&u32, value, drlen);
1714290000Sglebius					dstadr_refid = refid_str(u32, 1);
1715290000Sglebius					//fprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value);
1716290000Sglebius				} else if (decodenetnum(value, &refidadr)) {
1717290000Sglebius					if (SOCK_UNSPEC(&refidadr))
1718290000Sglebius						dstadr_refid = "0.0.0.0";
1719290000Sglebius					else if (ISREFCLOCKADR(&refidadr))
1720290000Sglebius						dstadr_refid =
1721290000Sglebius						    refnumtoa(&refidadr);
1722290000Sglebius					else {
1723290000Sglebius						char *buf = emalloc(10);
1724290000Sglebius						int i = ntohl(refidadr.sa4.sin_addr.s_addr);
1725290000Sglebius
1726290000Sglebius						snprintf(buf, 10,
1727290000Sglebius							"%0x", i);
1728290000Sglebius						dstadr_refid = buf;
1729290000Sglebius					//fprintf(stderr, "apeervarlist refid: value=<%x>\n", i);
1730290000Sglebius					}
1731290000Sglebius					//fprintf(stderr, "apeervarlist refid: value=<%s>\n", value);
1732290000Sglebius				} else {
1733290000Sglebius					have_da_rid = FALSE;
1734290000Sglebius				}
1735181834Sroberto			}
1736290000Sglebius		} else if (!strcmp("stratum", name)) {
1737290000Sglebius			decodeuint(value, &stratum);
1738290000Sglebius		} else if (!strcmp("hpoll", name)) {
1739290000Sglebius			if (decodeint(value, &hpoll) && hpoll < 0)
1740290000Sglebius				hpoll = NTP_MINPOLL;
1741290000Sglebius		} else if (!strcmp("ppoll", name)) {
1742290000Sglebius			if (decodeint(value, &ppoll) && ppoll < 0)
1743290000Sglebius				ppoll = NTP_MINPOLL;
1744290000Sglebius		} else if (!strcmp("reach", name)) {
1745290000Sglebius			decodeuint(value, &reach);
1746290000Sglebius		} else if (!strcmp("delay", name)) {
1747290000Sglebius			decodetime(value, &estdelay);
1748290000Sglebius		} else if (!strcmp("offset", name)) {
1749290000Sglebius			decodetime(value, &estoffset);
1750290000Sglebius		} else if (!strcmp("jitter", name)) {
1751290000Sglebius			if ((pvl == peervarlist || pvl == apeervarlist)
1752290000Sglebius			    && decodetime(value, &estjitter))
1753290000Sglebius				have_jitter = 1;
1754290000Sglebius		} else if (!strcmp("rootdisp", name) ||
1755290000Sglebius			   !strcmp("dispersion", name)) {
1756290000Sglebius			decodetime(value, &estdisp);
1757290000Sglebius		} else if (!strcmp("rec", name)) {
1758290000Sglebius			decodets(value, &rec);
1759290000Sglebius		} else if (!strcmp("srcport", name) ||
1760290000Sglebius			   !strcmp("peerport", name)) {
1761290000Sglebius			decodeuint(value, &srcport);
1762290000Sglebius		} else if (!strcmp("reftime", name)) {
1763181834Sroberto			if (!decodets(value, &reftime))
1764181834Sroberto				L_CLR(&reftime);
1765290000Sglebius		} else {
1766290000Sglebius			// fprintf(stderr, "UNRECOGNIZED name=%s ", name);
1767181834Sroberto		}
1768181834Sroberto	}
1769181834Sroberto
1770181834Sroberto	/*
1771290000Sglebius	 * hmode gives the best guidance for the t column.  If the response
1772290000Sglebius	 * did not include hmode we'll use the old decodeaddrtype() result.
1773181834Sroberto	 */
1774290000Sglebius	switch (hmode) {
1775181834Sroberto
1776290000Sglebius	case MODE_BCLIENT:
1777290000Sglebius		/* broadcastclient or multicastclient */
1778290000Sglebius		type = 'b';
1779290000Sglebius		break;
1780290000Sglebius
1781290000Sglebius	case MODE_BROADCAST:
1782290000Sglebius		/* broadcast or multicast server */
1783290000Sglebius		if (IS_MCAST(&srcadr))
1784290000Sglebius			type = 'M';
1785290000Sglebius		else
1786290000Sglebius			type = 'B';
1787290000Sglebius		break;
1788290000Sglebius
1789290000Sglebius	case MODE_CLIENT:
1790290000Sglebius		if (ISREFCLOCKADR(&srcadr))
1791290000Sglebius			type = 'l';	/* local refclock*/
1792290000Sglebius		else if (SOCK_UNSPEC(&srcadr))
1793290000Sglebius			type = 'p';	/* pool */
1794290000Sglebius		else if (IS_MCAST(&srcadr))
1795290000Sglebius			type = 'a';	/* manycastclient */
1796290000Sglebius		else
1797290000Sglebius			type = 'u';	/* unicast */
1798290000Sglebius		break;
1799290000Sglebius
1800290000Sglebius	case MODE_ACTIVE:
1801290000Sglebius		type = 's';		/* symmetric active */
1802290000Sglebius		break;			/* configured */
1803290000Sglebius
1804290000Sglebius	case MODE_PASSIVE:
1805290000Sglebius		type = 'S';		/* symmetric passive */
1806290000Sglebius		break;			/* ephemeral */
1807290000Sglebius	}
1808290000Sglebius
1809181834Sroberto	/*
1810181834Sroberto	 * Got everything, format the line
1811181834Sroberto	 */
1812290000Sglebius	poll_sec = 1 << min(ppoll, hpoll);
1813181834Sroberto	if (pktversion > NTP_OLDVERSION)
1814181834Sroberto		c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
1815181834Sroberto	else
1816181834Sroberto		c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
1817290000Sglebius	if (numhosts > 1) {
1818290000Sglebius		if ((pvl == peervarlist || pvl == apeervarlist)
1819290000Sglebius		    && have_dstadr) {
1820290000Sglebius			serverlocal = nntohost_col(&dstadr,
1821290000Sglebius			    (size_t)min(LIB_BUFLENGTH - 1, maxhostlen),
1822290000Sglebius			    TRUE);
1823290000Sglebius		} else {
1824290000Sglebius			if (currenthostisnum)
1825290000Sglebius				serverlocal = trunc_left(currenthost,
1826290000Sglebius							 maxhostlen);
1827290000Sglebius			else
1828290000Sglebius				serverlocal = currenthost;
1829290000Sglebius		}
1830290000Sglebius		fprintf(fp, "%-*s ", (int)maxhostlen, serverlocal);
1831290000Sglebius	}
1832290000Sglebius	if (AF_UNSPEC == af || AF(&srcadr) == af) {
1833290000Sglebius		if (!have_srchost)
1834290000Sglebius			strlcpy(clock_name, nntohost(&srcadr),
1835290000Sglebius				sizeof(clock_name));
1836290000Sglebius		if (wideremote && 15 < strlen(clock_name))
1837290000Sglebius			fprintf(fp, "%c%s\n                 ", c, clock_name);
1838290000Sglebius		else
1839290000Sglebius			fprintf(fp, "%c%-15.15s ", c, clock_name);
1840290000Sglebius		if (!have_da_rid) {
1841290000Sglebius			drlen = 0;
1842290000Sglebius		} else {
1843290000Sglebius			drlen = strlen(dstadr_refid);
1844290000Sglebius			makeascii(drlen, dstadr_refid, fp);
1845290000Sglebius		}
1846290000Sglebius		if (pvl == apeervarlist) {
1847290000Sglebius			while (drlen++ < 9)
1848290000Sglebius				fputc(' ', fp);
1849290000Sglebius			fprintf(fp, "%-6d", associd);
1850290000Sglebius		} else {
1851290000Sglebius			while (drlen++ < 15)
1852290000Sglebius				fputc(' ', fp);
1853290000Sglebius		}
1854290000Sglebius		fprintf(fp,
1855290000Sglebius			" %2ld %c %4.4s %4.4s  %3lo  %7.7s %8.7s %7.7s\n",
1856290000Sglebius			stratum, type,
1857290000Sglebius			prettyinterval(whenbuf, sizeof(whenbuf),
1858290000Sglebius				       when(&ts, &rec, &reftime)),
1859290000Sglebius			prettyinterval(pollbuf, sizeof(pollbuf),
1860290000Sglebius				       (int)poll_sec),
1861290000Sglebius			reach, lfptoms(&estdelay, 3),
1862290000Sglebius			lfptoms(&estoffset, 3),
1863290000Sglebius			(have_jitter)
1864290000Sglebius			    ? lfptoms(&estjitter, 3)
1865290000Sglebius			    : lfptoms(&estdisp, 3));
1866181834Sroberto		return (1);
1867181834Sroberto	}
1868181834Sroberto	else
1869181834Sroberto		return(1);
1870181834Sroberto}
1871181834Sroberto
1872181834Sroberto
1873181834Sroberto/*
1874181834Sroberto * dogetpeers - given an association ID, read and print the spreadsheet
1875181834Sroberto *		peer variables.
1876181834Sroberto */
1877181834Srobertostatic int
1878181834Srobertodogetpeers(
1879181834Sroberto	struct varlist *pvl,
1880290000Sglebius	associd_t associd,
1881181834Sroberto	FILE *fp,
1882181834Sroberto	int af
1883181834Sroberto	)
1884181834Sroberto{
1885290000Sglebius	const char *datap;
1886181834Sroberto	int res;
1887293894Sglebius	size_t dsize;
1888181834Sroberto	u_short rstatus;
1889181834Sroberto
1890181834Sroberto#ifdef notdef
1891181834Sroberto	res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
1892181834Sroberto			  &dsize, &datap);
1893181834Sroberto#else
1894181834Sroberto	/*
1895181834Sroberto	 * Damn fuzzballs
1896181834Sroberto	 */
1897290000Sglebius	res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
1898181834Sroberto			  &dsize, &datap);
1899181834Sroberto#endif
1900181834Sroberto
1901181834Sroberto	if (res != 0)
1902181834Sroberto		return 0;
1903181834Sroberto
1904181834Sroberto	if (dsize == 0) {
1905181834Sroberto		if (numhosts > 1)
1906290000Sglebius			fprintf(stderr, "server=%s ", currenthost);
1907290000Sglebius		fprintf(stderr,
1908290000Sglebius			"***No information returned for association %u\n",
1909290000Sglebius			associd);
1910181834Sroberto		return 0;
1911181834Sroberto	}
1912181834Sroberto
1913290000Sglebius	return doprintpeers(pvl, associd, (int)rstatus, dsize, datap,
1914290000Sglebius			    fp, af);
1915181834Sroberto}
1916181834Sroberto
1917181834Sroberto
1918181834Sroberto/*
1919181834Sroberto * peers - print a peer spreadsheet
1920181834Sroberto */
1921181834Srobertostatic void
1922181834Srobertodopeers(
1923181834Sroberto	int showall,
1924181834Sroberto	FILE *fp,
1925181834Sroberto	int af
1926181834Sroberto	)
1927181834Sroberto{
1928290000Sglebius	u_int		u;
1929290000Sglebius	char		fullname[LENHOSTNAME];
1930290000Sglebius	sockaddr_u	netnum;
1931290000Sglebius	const char *	name_or_num;
1932290000Sglebius	size_t		sl;
1933181834Sroberto
1934181834Sroberto	if (!dogetassoc(fp))
1935181834Sroberto		return;
1936181834Sroberto
1937290000Sglebius	for (u = 0; u < numhosts; u++) {
1938290000Sglebius		if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
1939290000Sglebius			name_or_num = nntohost(&netnum);
1940290000Sglebius			sl = strlen(name_or_num);
1941290000Sglebius			maxhostlen = max(maxhostlen, sl);
1942290000Sglebius		}
1943181834Sroberto	}
1944181834Sroberto	if (numhosts > 1)
1945290000Sglebius		fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
1946290000Sglebius			"server (local)");
1947290000Sglebius	fprintf(fp,
1948290000Sglebius		"     remote           refid      st t when poll reach   delay   offset  jitter\n");
1949181834Sroberto	if (numhosts > 1)
1950290000Sglebius		for (u = 0; u <= maxhostlen; u++)
1951290000Sglebius			fprintf(fp, "=");
1952290000Sglebius	fprintf(fp,
1953290000Sglebius		"==============================================================================\n");
1954181834Sroberto
1955290000Sglebius	for (u = 0; u < numassoc; u++) {
1956181834Sroberto		if (!showall &&
1957290000Sglebius		    !(CTL_PEER_STATVAL(assoc_cache[u].status)
1958290000Sglebius		      & (CTL_PST_CONFIG|CTL_PST_REACH))) {
1959290000Sglebius			if (debug)
1960290000Sglebius				fprintf(stderr, "eliding [%d]\n",
1961290000Sglebius					(int)assoc_cache[u].assid);
1962181834Sroberto			continue;
1963290000Sglebius		}
1964290000Sglebius		if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid,
1965290000Sglebius				fp, af))
1966181834Sroberto			return;
1967290000Sglebius	}
1968290000Sglebius	return;
1969290000Sglebius}
1970290000Sglebius
1971290000Sglebius
1972290000Sglebius/*
1973290000Sglebius * doapeers - print a peer spreadsheet with assocIDs
1974290000Sglebius */
1975290000Sglebiusstatic void
1976290000Sglebiusdoapeers(
1977290000Sglebius	int showall,
1978290000Sglebius	FILE *fp,
1979290000Sglebius	int af
1980290000Sglebius	)
1981290000Sglebius{
1982290000Sglebius	u_int		u;
1983290000Sglebius	char		fullname[LENHOSTNAME];
1984290000Sglebius	sockaddr_u	netnum;
1985290000Sglebius	const char *	name_or_num;
1986290000Sglebius	size_t		sl;
1987290000Sglebius
1988290000Sglebius	if (!dogetassoc(fp))
1989290000Sglebius		return;
1990290000Sglebius
1991290000Sglebius	for (u = 0; u < numhosts; u++) {
1992290000Sglebius		if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
1993290000Sglebius			name_or_num = nntohost(&netnum);
1994290000Sglebius			sl = strlen(name_or_num);
1995290000Sglebius			maxhostlen = max(maxhostlen, sl);
1996181834Sroberto		}
1997181834Sroberto	}
1998290000Sglebius	if (numhosts > 1)
1999290000Sglebius		fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2000290000Sglebius			"server (local)");
2001290000Sglebius	fprintf(fp,
2002290000Sglebius		"     remote       refid   assid  st t when poll reach   delay   offset  jitter\n");
2003290000Sglebius	if (numhosts > 1)
2004290000Sglebius		for (u = 0; u <= maxhostlen; u++)
2005290000Sglebius			fprintf(fp, "=");
2006290000Sglebius	fprintf(fp,
2007290000Sglebius		"==============================================================================\n");
2008290000Sglebius
2009290000Sglebius	for (u = 0; u < numassoc; u++) {
2010290000Sglebius		if (!showall &&
2011290000Sglebius		    !(CTL_PEER_STATVAL(assoc_cache[u].status)
2012290000Sglebius		      & (CTL_PST_CONFIG|CTL_PST_REACH))) {
2013290000Sglebius			if (debug)
2014290000Sglebius				fprintf(stderr, "eliding [%d]\n",
2015290000Sglebius					(int)assoc_cache[u].assid);
2016290000Sglebius			continue;
2017290000Sglebius		}
2018290000Sglebius		if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid,
2019290000Sglebius				fp, af))
2020290000Sglebius			return;
2021290000Sglebius	}
2022181834Sroberto	return;
2023181834Sroberto}
2024181834Sroberto
2025181834Sroberto
2026181834Sroberto/*
2027181834Sroberto * peers - print a peer spreadsheet
2028181834Sroberto */
2029181834Sroberto/*ARGSUSED*/
2030181834Srobertostatic void
2031181834Srobertopeers(
2032181834Sroberto	struct parse *pcmd,
2033181834Sroberto	FILE *fp
2034181834Sroberto	)
2035181834Sroberto{
2036298770Sdelphij	if (drefid == REFID_HASH) {
2037298770Sdelphij		apeers(pcmd, fp);
2038298770Sdelphij	} else {
2039298770Sdelphij		int af = 0;
2040181834Sroberto
2041298770Sdelphij		if (pcmd->nargs == 1) {
2042298770Sdelphij			if (pcmd->argval->ival == 6)
2043298770Sdelphij				af = AF_INET6;
2044298770Sdelphij			else
2045298770Sdelphij				af = AF_INET;
2046298770Sdelphij		}
2047298770Sdelphij		dopeers(0, fp, af);
2048181834Sroberto	}
2049181834Sroberto}
2050181834Sroberto
2051181834Sroberto
2052181834Sroberto/*
2053290000Sglebius * apeers - print a peer spreadsheet, with assocIDs
2054290000Sglebius */
2055290000Sglebius/*ARGSUSED*/
2056290000Sglebiusstatic void
2057290000Sglebiusapeers(
2058290000Sglebius	struct parse *pcmd,
2059290000Sglebius	FILE *fp
2060290000Sglebius	)
2061290000Sglebius{
2062290000Sglebius	int af = 0;
2063290000Sglebius
2064290000Sglebius	if (pcmd->nargs == 1) {
2065290000Sglebius		if (pcmd->argval->ival == 6)
2066290000Sglebius			af = AF_INET6;
2067290000Sglebius		else
2068290000Sglebius			af = AF_INET;
2069290000Sglebius	}
2070290000Sglebius	doapeers(0, fp, af);
2071290000Sglebius}
2072290000Sglebius
2073290000Sglebius
2074290000Sglebius/*
2075181834Sroberto * lpeers - print a peer spreadsheet including all fuzzball peers
2076181834Sroberto */
2077181834Sroberto/*ARGSUSED*/
2078181834Srobertostatic void
2079181834Srobertolpeers(
2080181834Sroberto	struct parse *pcmd,
2081181834Sroberto	FILE *fp
2082181834Sroberto	)
2083181834Sroberto{
2084181834Sroberto	int af = 0;
2085181834Sroberto
2086181834Sroberto	if (pcmd->nargs == 1) {
2087181834Sroberto		if (pcmd->argval->ival == 6)
2088181834Sroberto			af = AF_INET6;
2089181834Sroberto		else
2090181834Sroberto			af = AF_INET;
2091181834Sroberto	}
2092181834Sroberto	dopeers(1, fp, af);
2093181834Sroberto}
2094181834Sroberto
2095181834Sroberto
2096181834Sroberto/*
2097181834Sroberto * opeers - print a peer spreadsheet
2098181834Sroberto */
2099181834Srobertostatic void
2100181834Srobertodoopeers(
2101181834Sroberto	int showall,
2102181834Sroberto	FILE *fp,
2103181834Sroberto	int af
2104181834Sroberto	)
2105181834Sroberto{
2106290000Sglebius	u_int i;
2107181834Sroberto	char fullname[LENHOSTNAME];
2108290000Sglebius	sockaddr_u netnum;
2109181834Sroberto
2110181834Sroberto	if (!dogetassoc(fp))
2111181834Sroberto		return;
2112181834Sroberto
2113181834Sroberto	for (i = 0; i < numhosts; ++i) {
2114290000Sglebius		if (getnetnum(chosts[i].name, &netnum, fullname, af))
2115290000Sglebius			if (strlen(fullname) > maxhostlen)
2116181834Sroberto				maxhostlen = strlen(fullname);
2117181834Sroberto	}
2118181834Sroberto	if (numhosts > 1)
2119290000Sglebius		fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2120290000Sglebius			"server");
2121290000Sglebius	fprintf(fp,
2122290000Sglebius	    "     remote           local      st t when poll reach   delay   offset    disp\n");
2123181834Sroberto	if (numhosts > 1)
2124181834Sroberto		for (i = 0; i <= maxhostlen; ++i)
2125290000Sglebius			fprintf(fp, "=");
2126290000Sglebius	fprintf(fp,
2127290000Sglebius	    "==============================================================================\n");
2128181834Sroberto
2129181834Sroberto	for (i = 0; i < numassoc; i++) {
2130181834Sroberto		if (!showall &&
2131290000Sglebius		    !(CTL_PEER_STATVAL(assoc_cache[i].status) &
2132290000Sglebius		      (CTL_PST_CONFIG | CTL_PST_REACH)))
2133181834Sroberto			continue;
2134290000Sglebius		if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af))
2135181834Sroberto			return;
2136181834Sroberto	}
2137181834Sroberto	return;
2138181834Sroberto}
2139181834Sroberto
2140181834Sroberto
2141181834Sroberto/*
2142181834Sroberto * opeers - print a peer spreadsheet the old way
2143181834Sroberto */
2144181834Sroberto/*ARGSUSED*/
2145181834Srobertostatic void
2146181834Srobertoopeers(
2147181834Sroberto	struct parse *pcmd,
2148181834Sroberto	FILE *fp
2149181834Sroberto	)
2150181834Sroberto{
2151181834Sroberto	int af = 0;
2152181834Sroberto
2153181834Sroberto	if (pcmd->nargs == 1) {
2154181834Sroberto		if (pcmd->argval->ival == 6)
2155181834Sroberto			af = AF_INET6;
2156181834Sroberto		else
2157181834Sroberto			af = AF_INET;
2158181834Sroberto	}
2159181834Sroberto	doopeers(0, fp, af);
2160181834Sroberto}
2161181834Sroberto
2162181834Sroberto
2163181834Sroberto/*
2164181834Sroberto * lopeers - print a peer spreadsheet including all fuzzball peers
2165181834Sroberto */
2166181834Sroberto/*ARGSUSED*/
2167181834Srobertostatic void
2168181834Srobertolopeers(
2169181834Sroberto	struct parse *pcmd,
2170181834Sroberto	FILE *fp
2171181834Sroberto	)
2172181834Sroberto{
2173181834Sroberto	int af = 0;
2174181834Sroberto
2175181834Sroberto	if (pcmd->nargs == 1) {
2176181834Sroberto		if (pcmd->argval->ival == 6)
2177181834Sroberto			af = AF_INET6;
2178181834Sroberto		else
2179181834Sroberto			af = AF_INET;
2180181834Sroberto	}
2181181834Sroberto	doopeers(1, fp, af);
2182181834Sroberto}
2183290000Sglebius
2184290000Sglebius
2185290000Sglebius/*
2186290000Sglebius * config - send a configuration command to a remote host
2187290000Sglebius */
2188290000Sglebiusstatic void
2189290000Sglebiusconfig (
2190290000Sglebius	struct parse *pcmd,
2191290000Sglebius	FILE *fp
2192290000Sglebius	)
2193290000Sglebius{
2194290000Sglebius	const char *cfgcmd;
2195290000Sglebius	u_short rstatus;
2196293894Sglebius	size_t rsize;
2197290000Sglebius	const char *rdata;
2198290000Sglebius	char *resp;
2199290000Sglebius	int res;
2200290000Sglebius	int col;
2201290000Sglebius	int i;
2202290000Sglebius
2203290000Sglebius	cfgcmd = pcmd->argval[0].string;
2204290000Sglebius
2205290000Sglebius	if (debug > 2)
2206290000Sglebius		fprintf(stderr,
2207290000Sglebius			"In Config\n"
2208290000Sglebius			"Keyword = %s\n"
2209290000Sglebius			"Command = %s\n", pcmd->keyword, cfgcmd);
2210290000Sglebius
2211293894Sglebius	res = doquery(CTL_OP_CONFIGURE, 0, 1,
2212293894Sglebius		      strlen(cfgcmd), cfgcmd,
2213290000Sglebius		      &rstatus, &rsize, &rdata);
2214290000Sglebius
2215290000Sglebius	if (res != 0)
2216290000Sglebius		return;
2217290000Sglebius
2218290000Sglebius	if (rsize > 0 && '\n' == rdata[rsize - 1])
2219290000Sglebius		rsize--;
2220290000Sglebius
2221290000Sglebius	resp = emalloc(rsize + 1);
2222290000Sglebius	memcpy(resp, rdata, rsize);
2223290000Sglebius	resp[rsize] = '\0';
2224290000Sglebius
2225290000Sglebius	col = -1;
2226290000Sglebius	if (1 == sscanf(resp, "column %d syntax error", &col)
2227290000Sglebius	    && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) {
2228290000Sglebius		if (interactive) {
2229290000Sglebius			printf("______");	/* "ntpq> " */
2230290000Sglebius			printf("________");	/* ":config " */
2231290000Sglebius		} else
2232290000Sglebius			printf("%s\n", cfgcmd);
2233290000Sglebius		for (i = 1; i < col; i++)
2234290000Sglebius			putchar('_');
2235290000Sglebius		printf("^\n");
2236290000Sglebius	}
2237290000Sglebius	printf("%s\n", resp);
2238290000Sglebius	free(resp);
2239290000Sglebius}
2240290000Sglebius
2241290000Sglebius
2242290000Sglebius/*
2243290000Sglebius * config_from_file - remotely configure an ntpd daemon using the
2244290000Sglebius * specified configuration file
2245290000Sglebius * SK: This function is a kludge at best and is full of bad design
2246290000Sglebius * bugs:
2247290000Sglebius * 1. ntpq uses UDP, which means that there is no guarantee of in-order,
2248290000Sglebius *    error-free delivery.
2249290000Sglebius * 2. The maximum length of a packet is constrained, and as a result, the
2250290000Sglebius *    maximum length of a line in a configuration file is constrained.
2251290000Sglebius *    Longer lines will lead to unpredictable results.
2252290000Sglebius * 3. Since this function is sending a line at a time, we can't update
2253290000Sglebius *    the control key through the configuration file (YUCK!!)
2254298770Sdelphij *
2255298770Sdelphij * Pearly: There are a few places where 'size_t' is cast to 'int' based
2256298770Sdelphij * on the assumption that 'int' can hold the size of the involved
2257298770Sdelphij * buffers without overflow.
2258290000Sglebius */
2259290000Sglebiusstatic void
2260290000Sglebiusconfig_from_file (
2261290000Sglebius	struct parse *pcmd,
2262290000Sglebius	FILE *fp
2263290000Sglebius	)
2264290000Sglebius{
2265290000Sglebius	u_short rstatus;
2266293894Sglebius	size_t rsize;
2267290000Sglebius	const char *rdata;
2268298770Sdelphij	char * cp;
2269290000Sglebius	int res;
2270290000Sglebius	FILE *config_fd;
2271290000Sglebius	char config_cmd[MAXLINE];
2272290000Sglebius	size_t config_len;
2273290000Sglebius	int i;
2274290000Sglebius	int retry_limit;
2275290000Sglebius
2276290000Sglebius	if (debug > 2)
2277290000Sglebius		fprintf(stderr,
2278290000Sglebius			"In Config\n"
2279290000Sglebius			"Keyword = %s\n"
2280290000Sglebius			"Filename = %s\n", pcmd->keyword,
2281290000Sglebius			pcmd->argval[0].string);
2282290000Sglebius
2283290000Sglebius	config_fd = fopen(pcmd->argval[0].string, "r");
2284290000Sglebius	if (NULL == config_fd) {
2285290000Sglebius		printf("ERROR!! Couldn't open file: %s\n",
2286290000Sglebius		       pcmd->argval[0].string);
2287290000Sglebius		return;
2288290000Sglebius	}
2289290000Sglebius
2290290000Sglebius	printf("Sending configuration file, one line at a time.\n");
2291290000Sglebius	i = 0;
2292290000Sglebius	while (fgets(config_cmd, MAXLINE, config_fd) != NULL) {
2293298770Sdelphij		/* Eliminate comments first. */
2294298770Sdelphij		cp = strchr(config_cmd, '#');
2295298770Sdelphij		config_len = (NULL != cp)
2296298770Sdelphij		    ? (size_t)(cp - config_cmd)
2297298770Sdelphij		    : strlen(config_cmd);
2298298770Sdelphij
2299298770Sdelphij		/* [Bug 3015] make sure there's no trailing whitespace;
2300298770Sdelphij		 * the fix for [Bug 2853] on the server side forbids
2301298770Sdelphij		 * those. And don't transmit empty lines, as this would
2302298770Sdelphij		 * just be waste.
2303298770Sdelphij		 */
2304298770Sdelphij		while (config_len != 0 &&
2305298770Sdelphij		       (u_char)config_cmd[config_len-1] <= ' ')
2306298770Sdelphij			--config_len;
2307298770Sdelphij		config_cmd[config_len] = '\0';
2308298770Sdelphij
2309290000Sglebius		++i;
2310298770Sdelphij		if (0 == config_len)
2311298770Sdelphij			continue;
2312298770Sdelphij
2313290000Sglebius		retry_limit = 2;
2314290000Sglebius		do
2315290000Sglebius			res = doquery(CTL_OP_CONFIGURE, 0, 1,
2316298770Sdelphij				      config_len, config_cmd,
2317290000Sglebius				      &rstatus, &rsize, &rdata);
2318290000Sglebius		while (res != 0 && retry_limit--);
2319290000Sglebius		if (res != 0) {
2320298770Sdelphij			printf("Line No: %d query failed: %.*s\n"
2321298770Sdelphij			       "Subsequent lines not sent.\n",
2322298770Sdelphij			       i, (int)config_len, config_cmd);
2323290000Sglebius			fclose(config_fd);
2324290000Sglebius			return;
2325290000Sglebius		}
2326290000Sglebius
2327298770Sdelphij		/* Right-strip the result code string, then output the
2328298770Sdelphij		 * last line executed, with result code. */
2329298770Sdelphij		while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ')
2330298770Sdelphij			--rsize;
2331298770Sdelphij		printf("Line No: %d %.*s: %.*s\n", i,
2332298770Sdelphij		       (int)rsize, rdata,
2333298770Sdelphij		       (int)config_len, config_cmd);
2334290000Sglebius	}
2335290000Sglebius	printf("Done sending file\n");
2336290000Sglebius	fclose(config_fd);
2337290000Sglebius}
2338290000Sglebius
2339290000Sglebius
2340290000Sglebiusstatic int
2341290000Sglebiusfetch_nonce(
2342290000Sglebius	char *	nonce,
2343290000Sglebius	size_t	cb_nonce
2344290000Sglebius	)
2345290000Sglebius{
2346290000Sglebius	const char	nonce_eq[] = "nonce=";
2347290000Sglebius	int		qres;
2348290000Sglebius	u_short		rstatus;
2349293894Sglebius	size_t		rsize;
2350290000Sglebius	const char *	rdata;
2351293894Sglebius	size_t		chars;
2352290000Sglebius
2353290000Sglebius	/*
2354290000Sglebius	 * Retrieve a nonce specific to this client to demonstrate to
2355290000Sglebius	 * ntpd that we're capable of receiving responses to our source
2356290000Sglebius	 * IP address, and thereby unlikely to be forging the source.
2357290000Sglebius	 */
2358290000Sglebius	qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus,
2359290000Sglebius		       &rsize, &rdata);
2360290000Sglebius	if (qres) {
2361290000Sglebius		fprintf(stderr, "nonce request failed\n");
2362290000Sglebius		return FALSE;
2363290000Sglebius	}
2364290000Sglebius
2365290000Sglebius	if ((size_t)rsize <= sizeof(nonce_eq) - 1 ||
2366290000Sglebius	    strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) {
2367290000Sglebius		fprintf(stderr, "unexpected nonce response format: %.*s\n",
2368293894Sglebius			(int)rsize, rdata); /* cast is wobbly */
2369290000Sglebius		return FALSE;
2370290000Sglebius	}
2371290000Sglebius	chars = rsize - (sizeof(nonce_eq) - 1);
2372290000Sglebius	if (chars >= (int)cb_nonce)
2373290000Sglebius		return FALSE;
2374290000Sglebius	memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars);
2375290000Sglebius	nonce[chars] = '\0';
2376290000Sglebius	while (chars > 0 &&
2377290000Sglebius	       ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) {
2378290000Sglebius		chars--;
2379290000Sglebius		nonce[chars] = '\0';
2380290000Sglebius	}
2381290000Sglebius
2382290000Sglebius	return TRUE;
2383290000Sglebius}
2384290000Sglebius
2385290000Sglebius
2386290000Sglebius/*
2387290000Sglebius * add_mru	Add and entry to mru list, hash table, and allocate
2388290000Sglebius *		and return a replacement.
2389290000Sglebius *		This is a helper for collect_mru_list().
2390290000Sglebius */
2391290000Sglebiusstatic mru *
2392290000Sglebiusadd_mru(
2393290000Sglebius	mru *add
2394290000Sglebius	)
2395290000Sglebius{
2396290000Sglebius	u_short hash;
2397290000Sglebius	mru *mon;
2398290000Sglebius	mru *unlinked;
2399290000Sglebius
2400290000Sglebius
2401290000Sglebius	hash = NTP_HASH_ADDR(&add->addr);
2402290000Sglebius	/* see if we have it among previously received entries */
2403290000Sglebius	for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink)
2404290000Sglebius		if (SOCK_EQ(&mon->addr, &add->addr))
2405290000Sglebius			break;
2406290000Sglebius	if (mon != NULL) {
2407290000Sglebius		if (!L_ISGEQ(&add->first, &mon->first)) {
2408290000Sglebius			fprintf(stderr,
2409290000Sglebius				"add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n",
2410290000Sglebius				sptoa(&add->addr), add->last.l_ui,
2411290000Sglebius				add->last.l_uf, mon->last.l_ui,
2412290000Sglebius				mon->last.l_uf);
2413290000Sglebius			exit(1);
2414290000Sglebius		}
2415290000Sglebius		UNLINK_DLIST(mon, mlink);
2416290000Sglebius		UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru);
2417290000Sglebius		INSIST(unlinked == mon);
2418290000Sglebius		mru_dupes++;
2419290000Sglebius		TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui,
2420290000Sglebius		      mon->last.l_uf));
2421290000Sglebius	}
2422290000Sglebius	LINK_DLIST(mru_list, add, mlink);
2423290000Sglebius	LINK_SLIST(hash_table[hash], add, hlink);
2424290000Sglebius	TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
2425290000Sglebius	      add->last.l_ui, add->last.l_uf, add->count,
2426290000Sglebius	      (int)add->mode, (int)add->ver, (u_int)add->rs,
2427290000Sglebius	      add->first.l_ui, add->first.l_uf, sptoa(&add->addr)));
2428290000Sglebius	/* if we didn't update an existing entry, alloc replacement */
2429290000Sglebius	if (NULL == mon) {
2430290000Sglebius		mon = emalloc(sizeof(*mon));
2431290000Sglebius		mru_count++;
2432290000Sglebius	}
2433290000Sglebius	ZERO(*mon);
2434290000Sglebius
2435290000Sglebius	return mon;
2436290000Sglebius}
2437290000Sglebius
2438290000Sglebius
2439290000Sglebius/* MGOT macro is specific to collect_mru_list() */
2440290000Sglebius#define MGOT(bit)				\
2441290000Sglebius	do {					\
2442290000Sglebius		got |= (bit);			\
2443290000Sglebius		if (MRU_GOT_ALL == got) {	\
2444290000Sglebius			got = 0;		\
2445290000Sglebius			mon = add_mru(mon);	\
2446290000Sglebius			ci++;			\
2447290000Sglebius		}				\
2448290000Sglebius	} while (0)
2449290000Sglebius
2450290000Sglebius
2451293894Sglebiusint
2452290000Sglebiusmrulist_ctrl_c_hook(void)
2453290000Sglebius{
2454290000Sglebius	mrulist_interrupted = TRUE;
2455293894Sglebius	return TRUE;
2456290000Sglebius}
2457290000Sglebius
2458290000Sglebius
2459290000Sglebiusstatic int
2460290000Sglebiuscollect_mru_list(
2461290000Sglebius	const char *	parms,
2462290000Sglebius	l_fp *		pnow
2463290000Sglebius	)
2464290000Sglebius{
2465290000Sglebius	const u_int sleep_msecs = 5;
2466290000Sglebius	static int ntpd_row_limit = MRU_ROW_LIMIT;
2467290000Sglebius	int c_mru_l_rc;		/* this function's return code */
2468290000Sglebius	u_char got;		/* MRU_GOT_* bits */
2469290000Sglebius	time_t next_report;
2470290000Sglebius	size_t cb;
2471290000Sglebius	mru *mon;
2472290000Sglebius	mru *head;
2473290000Sglebius	mru *recent;
2474290000Sglebius	int list_complete;
2475290000Sglebius	char nonce[128];
2476290000Sglebius	char buf[128];
2477290000Sglebius	char req_buf[CTL_MAX_DATA_LEN];
2478290000Sglebius	char *req;
2479290000Sglebius	char *req_end;
2480293894Sglebius	size_t chars;
2481290000Sglebius	int qres;
2482290000Sglebius	u_short rstatus;
2483293894Sglebius	size_t rsize;
2484290000Sglebius	const char *rdata;
2485290000Sglebius	int limit;
2486290000Sglebius	int frags;
2487290000Sglebius	int cap_frags;
2488290000Sglebius	char *tag;
2489290000Sglebius	char *val;
2490290000Sglebius	int si;		/* server index in response */
2491290000Sglebius	int ci;		/* client (our) index for validation */
2492290000Sglebius	int ri;		/* request index (.# suffix) */
2493290000Sglebius	int mv;
2494290000Sglebius	l_fp newest;
2495290000Sglebius	l_fp last_older;
2496290000Sglebius	sockaddr_u addr_older;
2497290000Sglebius	int have_now;
2498290000Sglebius	int have_addr_older;
2499290000Sglebius	int have_last_older;
2500290000Sglebius	u_int restarted_count;
2501290000Sglebius	u_int nonce_uses;
2502290000Sglebius	u_short hash;
2503290000Sglebius	mru *unlinked;
2504290000Sglebius
2505290000Sglebius	if (!fetch_nonce(nonce, sizeof(nonce)))
2506290000Sglebius		return FALSE;
2507290000Sglebius
2508290000Sglebius	nonce_uses = 0;
2509290000Sglebius	restarted_count = 0;
2510290000Sglebius	mru_count = 0;
2511290000Sglebius	INIT_DLIST(mru_list, mlink);
2512290000Sglebius	cb = NTP_HASH_SIZE * sizeof(*hash_table);
2513290000Sglebius	INSIST(NULL == hash_table);
2514290000Sglebius	hash_table = emalloc_zero(cb);
2515290000Sglebius
2516290000Sglebius	c_mru_l_rc = FALSE;
2517290000Sglebius	list_complete = FALSE;
2518290000Sglebius	have_now = FALSE;
2519290000Sglebius	cap_frags = TRUE;
2520290000Sglebius	got = 0;
2521290000Sglebius	ri = 0;
2522290000Sglebius	cb = sizeof(*mon);
2523290000Sglebius	mon = emalloc_zero(cb);
2524290000Sglebius	ZERO(*pnow);
2525290000Sglebius	ZERO(last_older);
2526290000Sglebius	next_report = time(NULL) + MRU_REPORT_SECS;
2527290000Sglebius
2528290000Sglebius	limit = min(3 * MAXFRAGS, ntpd_row_limit);
2529290000Sglebius	frags = MAXFRAGS;
2530290000Sglebius	snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s",
2531290000Sglebius		 nonce, frags, parms);
2532290000Sglebius	nonce_uses++;
2533290000Sglebius
2534290000Sglebius	while (TRUE) {
2535290000Sglebius		if (debug)
2536290000Sglebius			fprintf(stderr, "READ_MRU parms: %s\n", req_buf);
2537290000Sglebius
2538293894Sglebius		qres = doqueryex(CTL_OP_READ_MRU, 0, 0,
2539293894Sglebius				 strlen(req_buf), req_buf,
2540293894Sglebius				 &rstatus, &rsize, &rdata, TRUE);
2541290000Sglebius
2542290000Sglebius		if (CERR_UNKNOWNVAR == qres && ri > 0) {
2543290000Sglebius			/*
2544290000Sglebius			 * None of the supplied prior entries match, so
2545290000Sglebius			 * toss them from our list and try again.
2546290000Sglebius			 */
2547290000Sglebius			if (debug)
2548290000Sglebius				fprintf(stderr,
2549290000Sglebius					"no overlap between %d prior entries and server MRU list\n",
2550290000Sglebius					ri);
2551290000Sglebius			while (ri--) {
2552290000Sglebius				recent = HEAD_DLIST(mru_list, mlink);
2553290000Sglebius				INSIST(recent != NULL);
2554290000Sglebius				if (debug)
2555290000Sglebius					fprintf(stderr,
2556290000Sglebius						"tossing prior entry %s to resync\n",
2557290000Sglebius						sptoa(&recent->addr));
2558290000Sglebius				UNLINK_DLIST(recent, mlink);
2559290000Sglebius				hash = NTP_HASH_ADDR(&recent->addr);
2560290000Sglebius				UNLINK_SLIST(unlinked, hash_table[hash],
2561290000Sglebius					     recent, hlink, mru);
2562290000Sglebius				INSIST(unlinked == recent);
2563290000Sglebius				free(recent);
2564290000Sglebius				mru_count--;
2565290000Sglebius			}
2566290000Sglebius			if (NULL == HEAD_DLIST(mru_list, mlink)) {
2567290000Sglebius				restarted_count++;
2568290000Sglebius				if (restarted_count > 8) {
2569290000Sglebius					fprintf(stderr,
2570290000Sglebius						"Giving up after 8 restarts from the beginning.\n"
2571290000Sglebius						"With high-traffic NTP servers, this can occur if the\n"
2572290000Sglebius						"MRU list is limited to less than about 16 seconds' of\n"
2573290000Sglebius						"entries.  See the 'mru' ntp.conf directive to adjust.\n");
2574290000Sglebius					goto cleanup_return;
2575290000Sglebius				}
2576290000Sglebius				if (debug)
2577290000Sglebius					fprintf(stderr,
2578290000Sglebius						"--->   Restarting from the beginning, retry #%u\n",
2579290000Sglebius						restarted_count);
2580290000Sglebius			}
2581290000Sglebius		} else if (CERR_UNKNOWNVAR == qres) {
2582290000Sglebius			fprintf(stderr,
2583290000Sglebius				"CERR_UNKNOWNVAR from ntpd but no priors given.\n");
2584290000Sglebius			goto cleanup_return;
2585290000Sglebius		} else if (CERR_BADVALUE == qres) {
2586290000Sglebius			if (cap_frags) {
2587290000Sglebius				cap_frags = FALSE;
2588290000Sglebius				if (debug)
2589290000Sglebius					fprintf(stderr,
2590290000Sglebius						"Reverted to row limit from fragments limit.\n");
2591290000Sglebius			} else {
2592290000Sglebius				/* ntpd has lower cap on row limit */
2593290000Sglebius				ntpd_row_limit--;
2594290000Sglebius				limit = min(limit, ntpd_row_limit);
2595290000Sglebius				if (debug)
2596290000Sglebius					fprintf(stderr,
2597290000Sglebius						"Row limit reduced to %d following CERR_BADVALUE.\n",
2598290000Sglebius						limit);
2599290000Sglebius			}
2600290000Sglebius		} else if (ERR_INCOMPLETE == qres ||
2601290000Sglebius			   ERR_TIMEOUT == qres) {
2602290000Sglebius			/*
2603290000Sglebius			 * Reduce the number of rows/frags requested by
2604290000Sglebius			 * half to recover from lost response fragments.
2605290000Sglebius			 */
2606290000Sglebius			if (cap_frags) {
2607290000Sglebius				frags = max(2, frags / 2);
2608290000Sglebius				if (debug)
2609290000Sglebius					fprintf(stderr,
2610290000Sglebius						"Frag limit reduced to %d following incomplete response.\n",
2611290000Sglebius						frags);
2612290000Sglebius			} else {
2613290000Sglebius				limit = max(2, limit / 2);
2614290000Sglebius				if (debug)
2615290000Sglebius					fprintf(stderr,
2616290000Sglebius						"Row limit reduced to %d following incomplete response.\n",
2617290000Sglebius						limit);
2618290000Sglebius			}
2619290000Sglebius		} else if (qres) {
2620290000Sglebius			show_error_msg(qres, 0);
2621290000Sglebius			goto cleanup_return;
2622290000Sglebius		}
2623290000Sglebius		/*
2624290000Sglebius		 * This is a cheap cop-out implementation of rawmode
2625290000Sglebius		 * output for mrulist.  A better approach would be to
2626290000Sglebius		 * dump similar output after the list is collected by
2627290000Sglebius		 * ntpq with a continuous sequence of indexes.  This
2628290000Sglebius		 * cheap approach has indexes resetting to zero for
2629290000Sglebius		 * each query/response, and duplicates are not
2630290000Sglebius		 * coalesced.
2631290000Sglebius		 */
2632290000Sglebius		if (!qres && rawmode)
2633290000Sglebius			printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout);
2634290000Sglebius		ci = 0;
2635290000Sglebius		have_addr_older = FALSE;
2636290000Sglebius		have_last_older = FALSE;
2637290000Sglebius		while (!qres && nextvar(&rsize, &rdata, &tag, &val)) {
2638290000Sglebius			if (debug > 1)
2639290000Sglebius				fprintf(stderr, "nextvar gave: %s = %s\n",
2640290000Sglebius					tag, val);
2641290000Sglebius			switch(tag[0]) {
2642290000Sglebius
2643290000Sglebius			case 'a':
2644290000Sglebius				if (!strcmp(tag, "addr.older")) {
2645290000Sglebius					if (!have_last_older) {
2646290000Sglebius						fprintf(stderr,
2647290000Sglebius							"addr.older %s before last.older\n",
2648290000Sglebius							val);
2649290000Sglebius						goto cleanup_return;
2650290000Sglebius					}
2651290000Sglebius					if (!decodenetnum(val, &addr_older)) {
2652290000Sglebius						fprintf(stderr,
2653290000Sglebius							"addr.older %s garbled\n",
2654290000Sglebius							val);
2655290000Sglebius						goto cleanup_return;
2656290000Sglebius					}
2657290000Sglebius					hash = NTP_HASH_ADDR(&addr_older);
2658290000Sglebius					for (recent = hash_table[hash];
2659290000Sglebius					     recent != NULL;
2660290000Sglebius					     recent = recent->hlink)
2661290000Sglebius						if (ADDR_PORT_EQ(
2662290000Sglebius						      &addr_older,
2663290000Sglebius						      &recent->addr))
2664290000Sglebius							break;
2665290000Sglebius					if (NULL == recent) {
2666290000Sglebius						fprintf(stderr,
2667290000Sglebius							"addr.older %s not in hash table\n",
2668290000Sglebius							val);
2669290000Sglebius						goto cleanup_return;
2670290000Sglebius					}
2671290000Sglebius					if (!L_ISEQU(&last_older,
2672290000Sglebius						     &recent->last)) {
2673290000Sglebius						fprintf(stderr,
2674290000Sglebius							"last.older %08x.%08x mismatches %08x.%08x expected.\n",
2675290000Sglebius							last_older.l_ui,
2676290000Sglebius							last_older.l_uf,
2677290000Sglebius							recent->last.l_ui,
2678290000Sglebius							recent->last.l_uf);
2679290000Sglebius						goto cleanup_return;
2680290000Sglebius					}
2681290000Sglebius					have_addr_older = TRUE;
2682290000Sglebius				} else if (1 != sscanf(tag, "addr.%d", &si)
2683290000Sglebius					   || si != ci)
2684290000Sglebius					goto nomatch;
2685290000Sglebius				else if (decodenetnum(val, &mon->addr))
2686290000Sglebius					MGOT(MRU_GOT_ADDR);
2687290000Sglebius				break;
2688290000Sglebius
2689290000Sglebius			case 'l':
2690290000Sglebius				if (!strcmp(tag, "last.older")) {
2691290000Sglebius					if ('0' != val[0] ||
2692290000Sglebius					    'x' != val[1] ||
2693290000Sglebius					    !hextolfp(val + 2, &last_older)) {
2694290000Sglebius						fprintf(stderr,
2695290000Sglebius							"last.older %s garbled\n",
2696290000Sglebius							val);
2697290000Sglebius						goto cleanup_return;
2698290000Sglebius					}
2699290000Sglebius					have_last_older = TRUE;
2700290000Sglebius				} else if (!strcmp(tag, "last.newest")) {
2701290000Sglebius					if (0 != got) {
2702290000Sglebius						fprintf(stderr,
2703290000Sglebius							"last.newest %s before complete row, got = 0x%x\n",
2704290000Sglebius							val, (u_int)got);
2705290000Sglebius						goto cleanup_return;
2706290000Sglebius					}
2707290000Sglebius					if (!have_now) {
2708290000Sglebius						fprintf(stderr,
2709290000Sglebius							"last.newest %s before now=\n",
2710290000Sglebius							val);
2711290000Sglebius						goto cleanup_return;
2712290000Sglebius					}
2713290000Sglebius					head = HEAD_DLIST(mru_list, mlink);
2714290000Sglebius					if (NULL != head) {
2715290000Sglebius						if ('0' != val[0] ||
2716290000Sglebius						    'x' != val[1] ||
2717290000Sglebius						    !hextolfp(val + 2, &newest) ||
2718290000Sglebius						    !L_ISEQU(&newest,
2719290000Sglebius							     &head->last)) {
2720290000Sglebius							fprintf(stderr,
2721290000Sglebius								"last.newest %s mismatches %08x.%08x",
2722290000Sglebius								val,
2723290000Sglebius								head->last.l_ui,
2724290000Sglebius								head->last.l_uf);
2725290000Sglebius							goto cleanup_return;
2726290000Sglebius						}
2727290000Sglebius					}
2728290000Sglebius					list_complete = TRUE;
2729290000Sglebius				} else if (1 != sscanf(tag, "last.%d", &si) ||
2730290000Sglebius					   si != ci || '0' != val[0] ||
2731290000Sglebius					   'x' != val[1] ||
2732290000Sglebius					   !hextolfp(val + 2, &mon->last)) {
2733290000Sglebius					goto nomatch;
2734290000Sglebius				} else {
2735290000Sglebius					MGOT(MRU_GOT_LAST);
2736290000Sglebius					/*
2737290000Sglebius					 * allow interrupted retrieval,
2738290000Sglebius					 * using most recent retrieved
2739290000Sglebius					 * entry's last seen timestamp
2740290000Sglebius					 * as the end of operation.
2741290000Sglebius					 */
2742290000Sglebius					*pnow = mon->last;
2743290000Sglebius				}
2744290000Sglebius				break;
2745290000Sglebius
2746290000Sglebius			case 'f':
2747290000Sglebius				if (1 != sscanf(tag, "first.%d", &si) ||
2748290000Sglebius				    si != ci || '0' != val[0] ||
2749290000Sglebius				    'x' != val[1] ||
2750290000Sglebius				    !hextolfp(val + 2, &mon->first))
2751290000Sglebius					goto nomatch;
2752290000Sglebius				MGOT(MRU_GOT_FIRST);
2753290000Sglebius				break;
2754290000Sglebius
2755290000Sglebius			case 'n':
2756290000Sglebius				if (!strcmp(tag, "nonce")) {
2757290000Sglebius					strlcpy(nonce, val, sizeof(nonce));
2758290000Sglebius					nonce_uses = 0;
2759290000Sglebius					break; /* case */
2760290000Sglebius				} else if (strcmp(tag, "now") ||
2761290000Sglebius					   '0' != val[0] ||
2762290000Sglebius					   'x' != val[1] ||
2763290000Sglebius					    !hextolfp(val + 2, pnow))
2764290000Sglebius					goto nomatch;
2765290000Sglebius				have_now = TRUE;
2766290000Sglebius				break;
2767290000Sglebius
2768290000Sglebius			case 'c':
2769290000Sglebius				if (1 != sscanf(tag, "ct.%d", &si) ||
2770290000Sglebius				    si != ci ||
2771290000Sglebius				    1 != sscanf(val, "%d", &mon->count)
2772290000Sglebius				    || mon->count < 1)
2773290000Sglebius					goto nomatch;
2774290000Sglebius				MGOT(MRU_GOT_COUNT);
2775290000Sglebius				break;
2776290000Sglebius
2777290000Sglebius			case 'm':
2778290000Sglebius				if (1 != sscanf(tag, "mv.%d", &si) ||
2779290000Sglebius				    si != ci ||
2780290000Sglebius				    1 != sscanf(val, "%d", &mv))
2781290000Sglebius					goto nomatch;
2782290000Sglebius				mon->mode = PKT_MODE(mv);
2783290000Sglebius				mon->ver = PKT_VERSION(mv);
2784290000Sglebius				MGOT(MRU_GOT_MV);
2785290000Sglebius				break;
2786290000Sglebius
2787290000Sglebius			case 'r':
2788290000Sglebius				if (1 != sscanf(tag, "rs.%d", &si) ||
2789290000Sglebius				    si != ci ||
2790290000Sglebius				    1 != sscanf(val, "0x%hx", &mon->rs))
2791290000Sglebius					goto nomatch;
2792290000Sglebius				MGOT(MRU_GOT_RS);
2793290000Sglebius				break;
2794290000Sglebius
2795290000Sglebius			default:
2796290000Sglebius			nomatch:
2797290000Sglebius				/* empty stmt */ ;
2798290000Sglebius				/* ignore unknown tags */
2799290000Sglebius			}
2800290000Sglebius		}
2801290000Sglebius		if (have_now)
2802290000Sglebius			list_complete = TRUE;
2803290000Sglebius		if (list_complete) {
2804290000Sglebius			INSIST(0 == ri || have_addr_older);
2805290000Sglebius		}
2806290000Sglebius		if (mrulist_interrupted) {
2807290000Sglebius			printf("mrulist retrieval interrupted by operator.\n"
2808290000Sglebius			       "Displaying partial client list.\n");
2809290000Sglebius			fflush(stdout);
2810290000Sglebius		}
2811290000Sglebius		if (list_complete || mrulist_interrupted) {
2812290000Sglebius			fprintf(stderr,
2813290000Sglebius				"\rRetrieved %u unique MRU entries and %u updates.\n",
2814290000Sglebius				mru_count, mru_dupes);
2815290000Sglebius			fflush(stderr);
2816290000Sglebius			break;
2817290000Sglebius		}
2818290000Sglebius		if (time(NULL) >= next_report) {
2819290000Sglebius			next_report += MRU_REPORT_SECS;
2820290000Sglebius			fprintf(stderr, "\r%u (%u updates) ", mru_count,
2821290000Sglebius				mru_dupes);
2822290000Sglebius			fflush(stderr);
2823290000Sglebius		}
2824290000Sglebius
2825290000Sglebius		/*
2826290000Sglebius		 * Snooze for a bit between queries to let ntpd catch
2827290000Sglebius		 * up with other duties.
2828290000Sglebius		 */
2829290000Sglebius#ifdef SYS_WINNT
2830290000Sglebius		Sleep(sleep_msecs);
2831290000Sglebius#elif !defined(HAVE_NANOSLEEP)
2832290000Sglebius		sleep((sleep_msecs / 1000) + 1);
2833290000Sglebius#else
2834290000Sglebius		{
2835290000Sglebius			struct timespec interv = { 0,
2836290000Sglebius						   1000 * sleep_msecs };
2837290000Sglebius			nanosleep(&interv, NULL);
2838290000Sglebius		}
2839290000Sglebius#endif
2840290000Sglebius		/*
2841290000Sglebius		 * If there were no errors, increase the number of rows
2842290000Sglebius		 * to a maximum of 3 * MAXFRAGS (the most packets ntpq
2843290000Sglebius		 * can handle in one response), on the assumption that
2844290000Sglebius		 * no less than 3 rows fit in each packet, capped at
2845290000Sglebius		 * our best guess at the server's row limit.
2846290000Sglebius		 */
2847290000Sglebius		if (!qres) {
2848290000Sglebius			if (cap_frags) {
2849290000Sglebius				frags = min(MAXFRAGS, frags + 1);
2850290000Sglebius			} else {
2851290000Sglebius				limit = min3(3 * MAXFRAGS,
2852290000Sglebius					     ntpd_row_limit,
2853290000Sglebius					     max(limit + 1,
2854290000Sglebius					         limit * 33 / 32));
2855290000Sglebius			}
2856290000Sglebius		}
2857290000Sglebius		/*
2858290000Sglebius		 * prepare next query with as many address and last-seen
2859290000Sglebius		 * timestamps as will fit in a single packet.
2860290000Sglebius		 */
2861290000Sglebius		req = req_buf;
2862290000Sglebius		req_end = req_buf + sizeof(req_buf);
2863290000Sglebius#define REQ_ROOM	(req_end - req)
2864290000Sglebius		snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce,
2865290000Sglebius			 (cap_frags)
2866290000Sglebius			     ? "frags"
2867290000Sglebius			     : "limit",
2868290000Sglebius			 (cap_frags)
2869290000Sglebius			     ? frags
2870290000Sglebius			     : limit,
2871290000Sglebius			 parms);
2872290000Sglebius		req += strlen(req);
2873290000Sglebius		nonce_uses++;
2874290000Sglebius		if (nonce_uses >= 4) {
2875290000Sglebius			if (!fetch_nonce(nonce, sizeof(nonce)))
2876290000Sglebius				goto cleanup_return;
2877290000Sglebius			nonce_uses = 0;
2878290000Sglebius		}
2879290000Sglebius
2880290000Sglebius
2881290000Sglebius		for (ri = 0, recent = HEAD_DLIST(mru_list, mlink);
2882290000Sglebius		     recent != NULL;
2883290000Sglebius		     ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) {
2884290000Sglebius
2885290000Sglebius			snprintf(buf, sizeof(buf),
2886290000Sglebius				 ", addr.%d=%s, last.%d=0x%08x.%08x",
2887290000Sglebius				 ri, sptoa(&recent->addr), ri,
2888290000Sglebius				 recent->last.l_ui, recent->last.l_uf);
2889290000Sglebius			chars = strlen(buf);
2890294904Sdelphij			if ((size_t)REQ_ROOM <= chars)
2891290000Sglebius				break;
2892290000Sglebius			memcpy(req, buf, chars + 1);
2893290000Sglebius			req += chars;
2894290000Sglebius		}
2895290000Sglebius	}
2896290000Sglebius
2897290000Sglebius	c_mru_l_rc = TRUE;
2898290000Sglebius	goto retain_hash_table;
2899290000Sglebius
2900290000Sglebiuscleanup_return:
2901290000Sglebius	free(hash_table);
2902290000Sglebius	hash_table = NULL;
2903290000Sglebius
2904290000Sglebiusretain_hash_table:
2905290000Sglebius	if (mon != NULL)
2906290000Sglebius		free(mon);
2907290000Sglebius
2908290000Sglebius	return c_mru_l_rc;
2909290000Sglebius}
2910290000Sglebius
2911290000Sglebius
2912290000Sglebius/*
2913290000Sglebius * qcmp_mru_addr - sort MRU entries by remote address.
2914290000Sglebius *
2915290000Sglebius * All IPv4 addresses sort before any IPv6, addresses are sorted by
2916290000Sglebius * value within address family.
2917290000Sglebius */
2918290000Sglebiusstatic int
2919290000Sglebiusqcmp_mru_addr(
2920290000Sglebius	const void *v1,
2921290000Sglebius	const void *v2
2922290000Sglebius	)
2923290000Sglebius{
2924290000Sglebius	const mru * const *	ppm1 = v1;
2925290000Sglebius	const mru * const *	ppm2 = v2;
2926290000Sglebius	const mru *		pm1;
2927290000Sglebius	const mru *		pm2;
2928290000Sglebius	u_short			af1;
2929290000Sglebius	u_short			af2;
2930290000Sglebius	size_t			cmplen;
2931290000Sglebius	size_t			addr_off;
2932290000Sglebius
2933290000Sglebius	pm1 = *ppm1;
2934290000Sglebius	pm2 = *ppm2;
2935290000Sglebius
2936290000Sglebius	af1 = AF(&pm1->addr);
2937290000Sglebius	af2 = AF(&pm2->addr);
2938290000Sglebius
2939290000Sglebius	if (af1 != af2)
2940290000Sglebius		return (AF_INET == af1)
2941290000Sglebius			   ? -1
2942290000Sglebius			   : 1;
2943290000Sglebius
2944290000Sglebius	cmplen = SIZEOF_INADDR(af1);
2945290000Sglebius	addr_off = (AF_INET == af1)
2946290000Sglebius		      ? offsetof(struct sockaddr_in, sin_addr)
2947290000Sglebius		      : offsetof(struct sockaddr_in6, sin6_addr);
2948290000Sglebius
2949290000Sglebius	return memcmp((const char *)&pm1->addr + addr_off,
2950290000Sglebius		      (const char *)&pm2->addr + addr_off,
2951290000Sglebius		      cmplen);
2952290000Sglebius}
2953290000Sglebius
2954290000Sglebius
2955290000Sglebiusstatic int
2956290000Sglebiusqcmp_mru_r_addr(
2957290000Sglebius	const void *v1,
2958290000Sglebius	const void *v2
2959290000Sglebius	)
2960290000Sglebius{
2961290000Sglebius	return -qcmp_mru_addr(v1, v2);
2962290000Sglebius}
2963290000Sglebius
2964290000Sglebius
2965290000Sglebius/*
2966290000Sglebius * qcmp_mru_count - sort MRU entries by times seen (hit count).
2967290000Sglebius */
2968290000Sglebiusstatic int
2969290000Sglebiusqcmp_mru_count(
2970290000Sglebius	const void *v1,
2971290000Sglebius	const void *v2
2972290000Sglebius	)
2973290000Sglebius{
2974290000Sglebius	const mru * const *	ppm1 = v1;
2975290000Sglebius	const mru * const *	ppm2 = v2;
2976290000Sglebius	const mru *		pm1;
2977290000Sglebius	const mru *		pm2;
2978290000Sglebius
2979290000Sglebius	pm1 = *ppm1;
2980290000Sglebius	pm2 = *ppm2;
2981290000Sglebius
2982290000Sglebius	return (pm1->count < pm2->count)
2983290000Sglebius		   ? -1
2984290000Sglebius		   : ((pm1->count == pm2->count)
2985290000Sglebius			  ? 0
2986290000Sglebius			  : 1);
2987290000Sglebius}
2988290000Sglebius
2989290000Sglebius
2990290000Sglebiusstatic int
2991290000Sglebiusqcmp_mru_r_count(
2992290000Sglebius	const void *v1,
2993290000Sglebius	const void *v2
2994290000Sglebius	)
2995290000Sglebius{
2996290000Sglebius	return -qcmp_mru_count(v1, v2);
2997290000Sglebius}
2998290000Sglebius
2999290000Sglebius
3000290000Sglebius/*
3001290000Sglebius * qcmp_mru_avgint - sort MRU entries by average interval.
3002290000Sglebius */
3003290000Sglebiusstatic int
3004290000Sglebiusqcmp_mru_avgint(
3005290000Sglebius	const void *v1,
3006290000Sglebius	const void *v2
3007290000Sglebius	)
3008290000Sglebius{
3009290000Sglebius	const mru * const *	ppm1 = v1;
3010290000Sglebius	const mru * const *	ppm2 = v2;
3011290000Sglebius	const mru *		pm1;
3012290000Sglebius	const mru *		pm2;
3013290000Sglebius	l_fp			interval;
3014290000Sglebius	double			avg1;
3015290000Sglebius	double			avg2;
3016290000Sglebius
3017290000Sglebius	pm1 = *ppm1;
3018290000Sglebius	pm2 = *ppm2;
3019290000Sglebius
3020290000Sglebius	interval = pm1->last;
3021290000Sglebius	L_SUB(&interval, &pm1->first);
3022290000Sglebius	LFPTOD(&interval, avg1);
3023290000Sglebius	avg1 /= pm1->count;
3024290000Sglebius
3025290000Sglebius	interval = pm2->last;
3026290000Sglebius	L_SUB(&interval, &pm2->first);
3027290000Sglebius	LFPTOD(&interval, avg2);
3028290000Sglebius	avg2 /= pm2->count;
3029290000Sglebius
3030290000Sglebius	if (avg1 < avg2)
3031290000Sglebius		return -1;
3032290000Sglebius	else if (avg1 > avg2)
3033290000Sglebius		return 1;
3034290000Sglebius
3035290000Sglebius	/* secondary sort on lstint - rarely tested */
3036290000Sglebius	if (L_ISEQU(&pm1->last, &pm2->last))
3037290000Sglebius		return 0;
3038290000Sglebius	else if (L_ISGEQ(&pm1->last, &pm2->last))
3039290000Sglebius		return -1;
3040290000Sglebius	else
3041290000Sglebius		return 1;
3042290000Sglebius}
3043290000Sglebius
3044290000Sglebius
3045290000Sglebiusstatic int
3046290000Sglebiusqcmp_mru_r_avgint(
3047290000Sglebius	const void *v1,
3048290000Sglebius	const void *v2
3049290000Sglebius	)
3050290000Sglebius{
3051290000Sglebius	return -qcmp_mru_avgint(v1, v2);
3052290000Sglebius}
3053290000Sglebius
3054290000Sglebius
3055290000Sglebius/*
3056290000Sglebius * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most
3057290000Sglebius *	     Recently Used (seen) remote address list from ntpd.
3058290000Sglebius *
3059290000Sglebius * Similar to ntpdc's monlist command, but not limited to a single
3060290000Sglebius * request/response, and thereby not limited to a few hundred remote
3061290000Sglebius * addresses.
3062290000Sglebius *
3063290000Sglebius * See ntpd/ntp_control.c read_mru_list() for comments on the way
3064290000Sglebius * CTL_OP_READ_MRU is designed to be used.
3065290000Sglebius *
3066290000Sglebius * mrulist intentionally differs from monlist in the way the avgint
3067290000Sglebius * column is calculated.  monlist includes the time after the last
3068290000Sglebius * packet from the client until the monlist query time in the average,
3069290000Sglebius * while mrulist excludes it.  That is, monlist's average interval grows
3070290000Sglebius * over time for remote addresses not heard from in some time, while it
3071290000Sglebius * remains unchanged in mrulist.  This also affects the avgint value for
3072290000Sglebius * entries representing a single packet, with identical first and last
3073290000Sglebius * timestamps.  mrulist shows 0 avgint, monlist shows a value identical
3074290000Sglebius * to lstint.
3075290000Sglebius */
3076290000Sglebiusstatic void
3077290000Sglebiusmrulist(
3078290000Sglebius	struct parse *	pcmd,
3079290000Sglebius	FILE *		fp
3080290000Sglebius	)
3081290000Sglebius{
3082290000Sglebius	const char mincount_eq[] =	"mincount=";
3083290000Sglebius	const char resall_eq[] =	"resall=";
3084290000Sglebius	const char resany_eq[] =	"resany=";
3085290000Sglebius	const char maxlstint_eq[] =	"maxlstint=";
3086290000Sglebius	const char laddr_eq[] =		"laddr=";
3087290000Sglebius	const char sort_eq[] =		"sort=";
3088290000Sglebius	mru_sort_order order;
3089290000Sglebius	size_t n;
3090290000Sglebius	char parms_buf[128];
3091290000Sglebius	char buf[24];
3092290000Sglebius	char *parms;
3093290000Sglebius	const char *arg;
3094290000Sglebius	size_t cb;
3095290000Sglebius	mru **sorted;
3096290000Sglebius	mru **ppentry;
3097290000Sglebius	mru *recent;
3098290000Sglebius	l_fp now;
3099290000Sglebius	l_fp interval;
3100290000Sglebius	double favgint;
3101290000Sglebius	double flstint;
3102290000Sglebius	int avgint;
3103290000Sglebius	int lstint;
3104290000Sglebius	size_t i;
3105290000Sglebius
3106293894Sglebius	mrulist_interrupted = FALSE;
3107293894Sglebius	push_ctrl_c_handler(&mrulist_ctrl_c_hook);
3108293894Sglebius	fprintf(stderr,
3109293894Sglebius		"Ctrl-C will stop MRU retrieval and display partial results.\n");
3110293894Sglebius	fflush(stderr);
3111293894Sglebius
3112290000Sglebius	order = MRUSORT_DEF;
3113290000Sglebius	parms_buf[0] = '\0';
3114290000Sglebius	parms = parms_buf;
3115290000Sglebius	for (i = 0; i < pcmd->nargs; i++) {
3116290000Sglebius		arg = pcmd->argval[i].string;
3117290000Sglebius		if (arg != NULL) {
3118290000Sglebius			cb = strlen(arg) + 1;
3119290000Sglebius			if ((!strncmp(resall_eq, arg, sizeof(resall_eq)
3120290000Sglebius			    - 1) || !strncmp(resany_eq, arg,
3121290000Sglebius			    sizeof(resany_eq) - 1) || !strncmp(
3122290000Sglebius			    mincount_eq, arg, sizeof(mincount_eq) - 1)
3123290000Sglebius			    || !strncmp(laddr_eq, arg, sizeof(laddr_eq)
3124290000Sglebius			    - 1) || !strncmp(maxlstint_eq, arg,
3125290000Sglebius			    sizeof(laddr_eq) - 1)) && parms + cb + 2 <=
3126290000Sglebius			    parms_buf + sizeof(parms_buf)) {
3127290000Sglebius				/* these are passed intact to ntpd */
3128290000Sglebius				memcpy(parms, ", ", 2);
3129290000Sglebius				parms += 2;
3130290000Sglebius				memcpy(parms, arg, cb);
3131290000Sglebius				parms += cb - 1;
3132290000Sglebius			} else if (!strncmp(sort_eq, arg,
3133290000Sglebius					    sizeof(sort_eq) - 1)) {
3134290000Sglebius				arg += sizeof(sort_eq) - 1;
3135290000Sglebius				for (n = 0;
3136290000Sglebius				     n < COUNTOF(mru_sort_keywords);
3137290000Sglebius				     n++)
3138290000Sglebius					if (!strcmp(mru_sort_keywords[n],
3139290000Sglebius						    arg))
3140290000Sglebius						break;
3141290000Sglebius				if (n < COUNTOF(mru_sort_keywords))
3142290000Sglebius					order = n;
3143290000Sglebius			} else if (!strcmp("limited", arg) ||
3144290000Sglebius				   !strcmp("kod", arg)) {
3145290000Sglebius				/* transform to resany=... */
3146290000Sglebius				snprintf(buf, sizeof(buf),
3147290000Sglebius					 ", resany=0x%x",
3148290000Sglebius					 ('k' == arg[0])
3149290000Sglebius					     ? RES_KOD
3150290000Sglebius					     : RES_LIMITED);
3151290000Sglebius				cb = 1 + strlen(buf);
3152290000Sglebius				if (parms + cb <
3153290000Sglebius					parms_buf + sizeof(parms_buf)) {
3154290000Sglebius					memcpy(parms, buf, cb);
3155290000Sglebius					parms += cb - 1;
3156290000Sglebius				}
3157290000Sglebius			} else
3158290000Sglebius				fprintf(stderr,
3159290000Sglebius					"ignoring unrecognized mrulist parameter: %s\n",
3160290000Sglebius					arg);
3161290000Sglebius		}
3162290000Sglebius	}
3163290000Sglebius	parms = parms_buf;
3164290000Sglebius
3165290000Sglebius	if (!collect_mru_list(parms, &now))
3166290000Sglebius		return;
3167290000Sglebius
3168290000Sglebius	/* display the results */
3169290000Sglebius	if (rawmode)
3170290000Sglebius		goto cleanup_return;
3171290000Sglebius
3172290000Sglebius	/* construct an array of entry pointers in default order */
3173290000Sglebius	sorted = eallocarray(mru_count, sizeof(*sorted));
3174290000Sglebius	ppentry = sorted;
3175290000Sglebius	if (MRUSORT_R_DEF != order) {
3176290000Sglebius		ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3177290000Sglebius			INSIST(ppentry < sorted + mru_count);
3178290000Sglebius			*ppentry = recent;
3179290000Sglebius			ppentry++;
3180290000Sglebius		ITER_DLIST_END()
3181290000Sglebius	} else {
3182290000Sglebius		REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3183290000Sglebius			INSIST(ppentry < sorted + mru_count);
3184290000Sglebius			*ppentry = recent;
3185290000Sglebius			ppentry++;
3186290000Sglebius		REV_ITER_DLIST_END()
3187290000Sglebius	}
3188290000Sglebius
3189290000Sglebius	if (ppentry - sorted != (int)mru_count) {
3190290000Sglebius		fprintf(stderr,
3191290000Sglebius			"mru_count %u should match MRU list depth %ld.\n",
3192290000Sglebius			mru_count, (long)(ppentry - sorted));
3193290000Sglebius		free(sorted);
3194290000Sglebius		goto cleanup_return;
3195290000Sglebius	}
3196290000Sglebius
3197290000Sglebius	/* re-sort sorted[] if not default or reverse default */
3198290000Sglebius	if (MRUSORT_R_DEF < order)
3199290000Sglebius		qsort(sorted, mru_count, sizeof(sorted[0]),
3200290000Sglebius		      mru_qcmp_table[order]);
3201290000Sglebius
3202294904Sdelphij	mrulist_interrupted = FALSE;
3203290000Sglebius	printf(	"lstint avgint rstr r m v  count rport remote address\n"
3204290000Sglebius		"==============================================================================\n");
3205290000Sglebius		/* '=' x 78 */
3206290000Sglebius	for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) {
3207290000Sglebius		recent = *ppentry;
3208290000Sglebius		interval = now;
3209290000Sglebius		L_SUB(&interval, &recent->last);
3210290000Sglebius		LFPTOD(&interval, flstint);
3211290000Sglebius		lstint = (int)(flstint + 0.5);
3212290000Sglebius		interval = recent->last;
3213290000Sglebius		L_SUB(&interval, &recent->first);
3214290000Sglebius		LFPTOD(&interval, favgint);
3215290000Sglebius		favgint /= recent->count;
3216290000Sglebius		avgint = (int)(favgint + 0.5);
3217290000Sglebius		fprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n",
3218290000Sglebius			lstint, avgint, recent->rs,
3219290000Sglebius			(RES_KOD & recent->rs)
3220290000Sglebius			    ? 'K'
3221290000Sglebius			    : (RES_LIMITED & recent->rs)
3222290000Sglebius				  ? 'L'
3223290000Sglebius				  : '.',
3224290000Sglebius			(int)recent->mode, (int)recent->ver,
3225290000Sglebius			recent->count, SRCPORT(&recent->addr),
3226290000Sglebius			nntohost(&recent->addr));
3227290000Sglebius		if (showhostnames)
3228290000Sglebius			fflush(fp);
3229294904Sdelphij		if (mrulist_interrupted) {
3230294904Sdelphij			fputs("\n --interrupted--\n", fp);
3231294904Sdelphij			fflush(fp);
3232294904Sdelphij			break;
3233294904Sdelphij		}
3234290000Sglebius	}
3235290000Sglebius	fflush(fp);
3236290000Sglebius	if (debug) {
3237290000Sglebius		fprintf(stderr,
3238290000Sglebius			"--- completed, freeing sorted[] pointers\n");
3239290000Sglebius		fflush(stderr);
3240290000Sglebius	}
3241290000Sglebius	free(sorted);
3242290000Sglebius
3243290000Sglebiuscleanup_return:
3244290000Sglebius	if (debug) {
3245290000Sglebius		fprintf(stderr, "... freeing MRU entries\n");
3246290000Sglebius		fflush(stderr);
3247290000Sglebius	}
3248290000Sglebius	ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3249290000Sglebius		free(recent);
3250290000Sglebius	ITER_DLIST_END()
3251290000Sglebius	if (debug) {
3252290000Sglebius		fprintf(stderr, "... freeing hash_table[]\n");
3253290000Sglebius		fflush(stderr);
3254290000Sglebius	}
3255290000Sglebius	free(hash_table);
3256290000Sglebius	hash_table = NULL;
3257290000Sglebius	INIT_DLIST(mru_list, mlink);
3258293894Sglebius
3259293894Sglebius	pop_ctrl_c_handler(&mrulist_ctrl_c_hook);
3260290000Sglebius}
3261290000Sglebius
3262290000Sglebius
3263290000Sglebius/*
3264290000Sglebius * validate_ifnum - helper for ifstats()
3265290000Sglebius *
3266290000Sglebius * Ensures rows are received in order and complete.
3267290000Sglebius */
3268290000Sglebiusstatic void
3269290000Sglebiusvalidate_ifnum(
3270290000Sglebius	FILE *		fp,
3271290000Sglebius	u_int		ifnum,
3272290000Sglebius	int *		pfields,
3273290000Sglebius	ifstats_row *	prow
3274290000Sglebius	)
3275290000Sglebius{
3276290000Sglebius	if (prow->ifnum == ifnum)
3277290000Sglebius		return;
3278290000Sglebius	if (prow->ifnum + 1 <= ifnum) {
3279290000Sglebius		if (*pfields < IFSTATS_FIELDS)
3280290000Sglebius			fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3281290000Sglebius				*pfields, IFSTATS_FIELDS);
3282290000Sglebius		*pfields = 0;
3283290000Sglebius		prow->ifnum = ifnum;
3284290000Sglebius		return;
3285290000Sglebius	}
3286290000Sglebius	fprintf(stderr,
3287290000Sglebius		"received if index %u, have %d of %d fields for index %u, aborting.\n",
3288290000Sglebius		ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum);
3289290000Sglebius	exit(1);
3290290000Sglebius}
3291290000Sglebius
3292290000Sglebius
3293290000Sglebius/*
3294290000Sglebius * another_ifstats_field - helper for ifstats()
3295290000Sglebius *
3296290000Sglebius * If all fields for the row have been received, print it.
3297290000Sglebius */
3298290000Sglebiusstatic void
3299290000Sglebiusanother_ifstats_field(
3300290000Sglebius	int *		pfields,
3301290000Sglebius	ifstats_row *	prow,
3302290000Sglebius	FILE *		fp
3303290000Sglebius	)
3304290000Sglebius{
3305290000Sglebius	u_int ifnum;
3306290000Sglebius
3307290000Sglebius	(*pfields)++;
3308290000Sglebius	/* we understand 12 tags */
3309290000Sglebius	if (IFSTATS_FIELDS > *pfields)
3310290000Sglebius		return;
3311290000Sglebius	/*
3312290000Sglebius	"    interface name                                        send\n"
3313290000Sglebius	" #  address/broadcast     drop flag ttl mc received sent failed peers   uptime\n"
3314290000Sglebius	"==============================================================================\n");
3315290000Sglebius	 */
3316290000Sglebius	fprintf(fp,
3317290000Sglebius		"%3u %-24.24s %c %4x %3d %2d %6d %6d %6d %5d %8d\n"
3318290000Sglebius		"    %s\n",
3319290000Sglebius		prow->ifnum, prow->name,
3320290000Sglebius		(prow->enabled)
3321290000Sglebius		    ? '.'
3322290000Sglebius		    : 'D',
3323290000Sglebius		prow->flags, prow->ttl, prow->mcast_count,
3324290000Sglebius		prow->received, prow->sent, prow->send_errors,
3325290000Sglebius		prow->peer_count, prow->uptime, sptoa(&prow->addr));
3326290000Sglebius	if (!SOCK_UNSPEC(&prow->bcast))
3327290000Sglebius		fprintf(fp, "    %s\n", sptoa(&prow->bcast));
3328290000Sglebius	ifnum = prow->ifnum;
3329290000Sglebius	ZERO(*prow);
3330290000Sglebius	prow->ifnum = ifnum;
3331290000Sglebius}
3332290000Sglebius
3333290000Sglebius
3334290000Sglebius/*
3335290000Sglebius * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats.
3336290000Sglebius */
3337290000Sglebiusstatic void
3338290000Sglebiusifstats(
3339290000Sglebius	struct parse *	pcmd,
3340290000Sglebius	FILE *		fp
3341290000Sglebius	)
3342290000Sglebius{
3343290000Sglebius	const char	addr_fmt[] =	"addr.%u";
3344290000Sglebius	const char	bcast_fmt[] =	"bcast.%u";
3345290000Sglebius	const char	en_fmt[] =	"en.%u";	/* enabled */
3346290000Sglebius	const char	flags_fmt[] =	"flags.%u";
3347290000Sglebius	const char	mc_fmt[] =	"mc.%u";	/* mcast count */
3348290000Sglebius	const char	name_fmt[] =	"name.%u";
3349290000Sglebius	const char	pc_fmt[] =	"pc.%u";	/* peer count */
3350290000Sglebius	const char	rx_fmt[] =	"rx.%u";
3351290000Sglebius	const char	tl_fmt[] =	"tl.%u";	/* ttl */
3352290000Sglebius	const char	tx_fmt[] =	"tx.%u";
3353290000Sglebius	const char	txerr_fmt[] =	"txerr.%u";
3354290000Sglebius	const char	up_fmt[] =	"up.%u";	/* uptime */
3355290000Sglebius	const char *	datap;
3356290000Sglebius	int		qres;
3357293894Sglebius	size_t		dsize;
3358290000Sglebius	u_short		rstatus;
3359290000Sglebius	char *		tag;
3360290000Sglebius	char *		val;
3361290000Sglebius	int		fields;
3362290000Sglebius	u_int		ui;
3363290000Sglebius	ifstats_row	row;
3364290000Sglebius	int		comprende;
3365290000Sglebius	size_t		len;
3366290000Sglebius
3367290000Sglebius	qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus,
3368290000Sglebius		       &dsize, &datap);
3369290000Sglebius	if (qres)	/* message already displayed */
3370290000Sglebius		return;
3371290000Sglebius
3372290000Sglebius	fprintf(fp,
3373290000Sglebius		"    interface name                                        send\n"
3374290000Sglebius		" #  address/broadcast     drop flag ttl mc received sent failed peers   uptime\n"
3375290000Sglebius		"==============================================================================\n");
3376290000Sglebius		/* '=' x 78 */
3377290000Sglebius
3378290000Sglebius	ZERO(row);
3379290000Sglebius	fields = 0;
3380290000Sglebius	ui = 0;
3381290000Sglebius	while (nextvar(&dsize, &datap, &tag, &val)) {
3382290000Sglebius		if (debug > 1)
3383290000Sglebius			fprintf(stderr, "nextvar gave: %s = %s\n", tag,
3384290000Sglebius				(NULL == val)
3385290000Sglebius				    ? ""
3386290000Sglebius				    : val);
3387290000Sglebius		comprende = FALSE;
3388290000Sglebius		switch(tag[0]) {
3389290000Sglebius
3390290000Sglebius		case 'a':
3391290000Sglebius			if (1 == sscanf(tag, addr_fmt, &ui) &&
3392290000Sglebius			    decodenetnum(val, &row.addr))
3393290000Sglebius				comprende = TRUE;
3394290000Sglebius			break;
3395290000Sglebius
3396290000Sglebius		case 'b':
3397290000Sglebius			if (1 == sscanf(tag, bcast_fmt, &ui) &&
3398290000Sglebius			    (NULL == val ||
3399290000Sglebius			     decodenetnum(val, &row.bcast)))
3400290000Sglebius				comprende = TRUE;
3401290000Sglebius			break;
3402290000Sglebius
3403290000Sglebius		case 'e':
3404290000Sglebius			if (1 == sscanf(tag, en_fmt, &ui) &&
3405290000Sglebius			    1 == sscanf(val, "%d", &row.enabled))
3406290000Sglebius				comprende = TRUE;
3407290000Sglebius			break;
3408290000Sglebius
3409290000Sglebius		case 'f':
3410290000Sglebius			if (1 == sscanf(tag, flags_fmt, &ui) &&
3411290000Sglebius			    1 == sscanf(val, "0x%x", &row.flags))
3412290000Sglebius				comprende = TRUE;
3413290000Sglebius			break;
3414290000Sglebius
3415290000Sglebius		case 'm':
3416290000Sglebius			if (1 == sscanf(tag, mc_fmt, &ui) &&
3417290000Sglebius			    1 == sscanf(val, "%d", &row.mcast_count))
3418290000Sglebius				comprende = TRUE;
3419290000Sglebius			break;
3420290000Sglebius
3421290000Sglebius		case 'n':
3422290000Sglebius			if (1 == sscanf(tag, name_fmt, &ui)) {
3423290000Sglebius				/* strip quotes */
3424290000Sglebius				INSIST(val);
3425290000Sglebius				len = strlen(val);
3426290000Sglebius				if (len >= 2 &&
3427290000Sglebius				    len - 2 < sizeof(row.name)) {
3428290000Sglebius					len -= 2;
3429290000Sglebius					memcpy(row.name, val + 1, len);
3430290000Sglebius					row.name[len] = '\0';
3431290000Sglebius					comprende = TRUE;
3432290000Sglebius				}
3433290000Sglebius			}
3434290000Sglebius			break;
3435290000Sglebius
3436290000Sglebius		case 'p':
3437290000Sglebius			if (1 == sscanf(tag, pc_fmt, &ui) &&
3438290000Sglebius			    1 == sscanf(val, "%d", &row.peer_count))
3439290000Sglebius				comprende = TRUE;
3440290000Sglebius			break;
3441290000Sglebius
3442290000Sglebius		case 'r':
3443290000Sglebius			if (1 == sscanf(tag, rx_fmt, &ui) &&
3444290000Sglebius			    1 == sscanf(val, "%d", &row.received))
3445290000Sglebius				comprende = TRUE;
3446290000Sglebius			break;
3447290000Sglebius
3448290000Sglebius		case 't':
3449290000Sglebius			if (1 == sscanf(tag, tl_fmt, &ui) &&
3450290000Sglebius			    1 == sscanf(val, "%d", &row.ttl))
3451290000Sglebius				comprende = TRUE;
3452290000Sglebius			else if (1 == sscanf(tag, tx_fmt, &ui) &&
3453290000Sglebius				 1 == sscanf(val, "%d", &row.sent))
3454290000Sglebius				comprende = TRUE;
3455290000Sglebius			else if (1 == sscanf(tag, txerr_fmt, &ui) &&
3456290000Sglebius				 1 == sscanf(val, "%d", &row.send_errors))
3457290000Sglebius				comprende = TRUE;
3458290000Sglebius			break;
3459290000Sglebius
3460290000Sglebius		case 'u':
3461290000Sglebius			if (1 == sscanf(tag, up_fmt, &ui) &&
3462290000Sglebius			    1 == sscanf(val, "%d", &row.uptime))
3463290000Sglebius				comprende = TRUE;
3464290000Sglebius			break;
3465290000Sglebius		}
3466290000Sglebius
3467290000Sglebius		if (comprende) {
3468290000Sglebius			/* error out if rows out of order */
3469290000Sglebius			validate_ifnum(fp, ui, &fields, &row);
3470290000Sglebius			/* if the row is complete, print it */
3471290000Sglebius			another_ifstats_field(&fields, &row, fp);
3472290000Sglebius		}
3473290000Sglebius	}
3474290000Sglebius	if (fields != IFSTATS_FIELDS)
3475290000Sglebius		fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3476290000Sglebius			fields, IFSTATS_FIELDS);
3477290000Sglebius
3478290000Sglebius	fflush(fp);
3479290000Sglebius}
3480290000Sglebius
3481290000Sglebius
3482290000Sglebius/*
3483290000Sglebius * validate_reslist_idx - helper for reslist()
3484290000Sglebius *
3485290000Sglebius * Ensures rows are received in order and complete.
3486290000Sglebius */
3487290000Sglebiusstatic void
3488290000Sglebiusvalidate_reslist_idx(
3489290000Sglebius	FILE *		fp,
3490290000Sglebius	u_int		idx,
3491290000Sglebius	int *		pfields,
3492290000Sglebius	reslist_row *	prow
3493290000Sglebius	)
3494290000Sglebius{
3495290000Sglebius	if (prow->idx == idx)
3496290000Sglebius		return;
3497290000Sglebius	if (prow->idx + 1 == idx) {
3498290000Sglebius		if (*pfields < RESLIST_FIELDS)
3499290000Sglebius			fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3500290000Sglebius				*pfields, RESLIST_FIELDS);
3501290000Sglebius		*pfields = 0;
3502290000Sglebius		prow->idx = idx;
3503290000Sglebius		return;
3504290000Sglebius	}
3505290000Sglebius	fprintf(stderr,
3506290000Sglebius		"received reslist index %u, have %d of %d fields for index %u, aborting.\n",
3507290000Sglebius		idx, *pfields, RESLIST_FIELDS, prow->idx);
3508290000Sglebius	exit(1);
3509290000Sglebius}
3510290000Sglebius
3511290000Sglebius
3512290000Sglebius/*
3513290000Sglebius * another_reslist_field - helper for reslist()
3514290000Sglebius *
3515290000Sglebius * If all fields for the row have been received, print it.
3516290000Sglebius */
3517290000Sglebiusstatic void
3518290000Sglebiusanother_reslist_field(
3519290000Sglebius	int *		pfields,
3520290000Sglebius	reslist_row *	prow,
3521290000Sglebius	FILE *		fp
3522290000Sglebius	)
3523290000Sglebius{
3524290000Sglebius	char	addrmaskstr[128];
3525290000Sglebius	int	prefix;	/* subnet mask as prefix bits count */
3526290000Sglebius	u_int	idx;
3527290000Sglebius
3528290000Sglebius	(*pfields)++;
3529290000Sglebius	/* we understand 4 tags */
3530290000Sglebius	if (RESLIST_FIELDS > *pfields)
3531290000Sglebius		return;
3532290000Sglebius
3533290000Sglebius	prefix = sockaddr_masktoprefixlen(&prow->mask);
3534290000Sglebius	if (prefix >= 0)
3535290000Sglebius		snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d",
3536290000Sglebius			 stoa(&prow->addr), prefix);
3537290000Sglebius	else
3538290000Sglebius		snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s",
3539290000Sglebius			 stoa(&prow->addr), stoa(&prow->mask));
3540290000Sglebius
3541290000Sglebius	/*
3542290000Sglebius	"   hits    addr/prefix or addr mask\n"
3543290000Sglebius	"           restrictions\n"
3544290000Sglebius	"==============================================================================\n");
3545290000Sglebius	 */
3546290000Sglebius	fprintf(fp,
3547290000Sglebius		"%10lu %s\n"
3548290000Sglebius		"           %s\n",
3549290000Sglebius		prow->hits, addrmaskstr, prow->flagstr);
3550290000Sglebius	idx = prow->idx;
3551290000Sglebius	ZERO(*prow);
3552290000Sglebius	prow->idx = idx;
3553290000Sglebius}
3554290000Sglebius
3555290000Sglebius
3556290000Sglebius/*
3557290000Sglebius * reslist - ntpq -c reslist modeled on ntpdc -c reslist.
3558290000Sglebius */
3559290000Sglebiusstatic void
3560290000Sglebiusreslist(
3561290000Sglebius	struct parse *	pcmd,
3562290000Sglebius	FILE *		fp
3563290000Sglebius	)
3564290000Sglebius{
3565290000Sglebius	const char addr_fmtu[] =	"addr.%u";
3566290000Sglebius	const char mask_fmtu[] =	"mask.%u";
3567290000Sglebius	const char hits_fmt[] =		"hits.%u";
3568290000Sglebius	const char flags_fmt[] =	"flags.%u";
3569290000Sglebius	const char qdata[] =		"addr_restrictions";
3570290000Sglebius	const int qdata_chars =		COUNTOF(qdata) - 1;
3571290000Sglebius	const char *	datap;
3572290000Sglebius	int		qres;
3573293894Sglebius	size_t		dsize;
3574290000Sglebius	u_short		rstatus;
3575290000Sglebius	char *		tag;
3576290000Sglebius	char *		val;
3577290000Sglebius	int		fields;
3578290000Sglebius	u_int		ui;
3579290000Sglebius	reslist_row	row;
3580290000Sglebius	int		comprende;
3581290000Sglebius	size_t		len;
3582290000Sglebius
3583290000Sglebius	qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars,
3584290000Sglebius		       qdata, &rstatus, &dsize, &datap);
3585290000Sglebius	if (qres)	/* message already displayed */
3586290000Sglebius		return;
3587290000Sglebius
3588290000Sglebius	fprintf(fp,
3589290000Sglebius		"   hits    addr/prefix or addr mask\n"
3590290000Sglebius		"           restrictions\n"
3591290000Sglebius		"==============================================================================\n");
3592290000Sglebius		/* '=' x 78 */
3593290000Sglebius
3594290000Sglebius	ZERO(row);
3595290000Sglebius	fields = 0;
3596290000Sglebius	ui = 0;
3597290000Sglebius	while (nextvar(&dsize, &datap, &tag, &val)) {
3598290000Sglebius		if (debug > 1)
3599290000Sglebius			fprintf(stderr, "nextvar gave: %s = %s\n", tag,
3600290000Sglebius				(NULL == val)
3601290000Sglebius				    ? ""
3602290000Sglebius				    : val);
3603290000Sglebius		comprende = FALSE;
3604290000Sglebius		switch(tag[0]) {
3605290000Sglebius
3606290000Sglebius		case 'a':
3607290000Sglebius			if (1 == sscanf(tag, addr_fmtu, &ui) &&
3608290000Sglebius			    decodenetnum(val, &row.addr))
3609290000Sglebius				comprende = TRUE;
3610290000Sglebius			break;
3611290000Sglebius
3612290000Sglebius		case 'f':
3613290000Sglebius			if (1 == sscanf(tag, flags_fmt, &ui)) {
3614290000Sglebius				if (NULL == val) {
3615290000Sglebius					row.flagstr[0] = '\0';
3616290000Sglebius					comprende = TRUE;
3617290000Sglebius				} else {
3618290000Sglebius					len = strlen(val);
3619290000Sglebius					memcpy(row.flagstr, val, len);
3620290000Sglebius					row.flagstr[len] = '\0';
3621290000Sglebius					comprende = TRUE;
3622290000Sglebius				}
3623290000Sglebius			}
3624290000Sglebius			break;
3625290000Sglebius
3626290000Sglebius		case 'h':
3627290000Sglebius			if (1 == sscanf(tag, hits_fmt, &ui) &&
3628290000Sglebius			    1 == sscanf(val, "%lu", &row.hits))
3629290000Sglebius				comprende = TRUE;
3630290000Sglebius			break;
3631290000Sglebius
3632290000Sglebius		case 'm':
3633290000Sglebius			if (1 == sscanf(tag, mask_fmtu, &ui) &&
3634290000Sglebius			    decodenetnum(val, &row.mask))
3635290000Sglebius				comprende = TRUE;
3636290000Sglebius			break;
3637290000Sglebius		}
3638290000Sglebius
3639290000Sglebius		if (comprende) {
3640290000Sglebius			/* error out if rows out of order */
3641290000Sglebius			validate_reslist_idx(fp, ui, &fields, &row);
3642290000Sglebius			/* if the row is complete, print it */
3643290000Sglebius			another_reslist_field(&fields, &row, fp);
3644290000Sglebius		}
3645290000Sglebius	}
3646290000Sglebius	if (fields != RESLIST_FIELDS)
3647290000Sglebius		fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3648290000Sglebius			fields, RESLIST_FIELDS);
3649290000Sglebius
3650290000Sglebius	fflush(fp);
3651290000Sglebius}
3652290000Sglebius
3653290000Sglebius
3654290000Sglebius/*
3655290000Sglebius * collect_display_vdc
3656290000Sglebius */
3657290000Sglebiusstatic void
3658290000Sglebiuscollect_display_vdc(
3659290000Sglebius	associd_t	as,
3660290000Sglebius	vdc *		table,
3661290000Sglebius	int		decodestatus,
3662290000Sglebius	FILE *		fp
3663290000Sglebius	)
3664290000Sglebius{
3665290000Sglebius	static const char * const suf[2] = { "adr", "port" };
3666290000Sglebius	static const char * const leapbits[4] = { "00", "01",
3667290000Sglebius						  "10", "11" };
3668290000Sglebius	struct varlist vl[MAXLIST];
3669290000Sglebius	char tagbuf[32];
3670290000Sglebius	vdc *pvdc;
3671290000Sglebius	u_short rstatus;
3672293894Sglebius	size_t rsize;
3673290000Sglebius	const char *rdata;
3674290000Sglebius	int qres;
3675290000Sglebius	char *tag;
3676290000Sglebius	char *val;
3677290000Sglebius	u_int n;
3678290000Sglebius	size_t len;
3679290000Sglebius	int match;
3680290000Sglebius	u_long ul;
3681290000Sglebius	int vtype;
3682290000Sglebius
3683290000Sglebius	ZERO(vl);
3684290000Sglebius	for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3685290000Sglebius		ZERO(pvdc->v);
3686290000Sglebius		if (NTP_ADD != pvdc->type) {
3687290000Sglebius			doaddvlist(vl, pvdc->tag);
3688290000Sglebius		} else {
3689290000Sglebius			for (n = 0; n < COUNTOF(suf); n++) {
3690290000Sglebius				snprintf(tagbuf, sizeof(tagbuf), "%s%s",
3691290000Sglebius					 pvdc->tag, suf[n]);
3692290000Sglebius				doaddvlist(vl, tagbuf);
3693290000Sglebius			}
3694290000Sglebius		}
3695290000Sglebius	}
3696290000Sglebius	qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize,
3697290000Sglebius			   &rdata);
3698290000Sglebius	doclearvlist(vl);
3699290000Sglebius	if (qres)
3700290000Sglebius		return;		/* error msg already displayed */
3701290000Sglebius
3702290000Sglebius	/*
3703290000Sglebius	 * iterate over the response variables filling vdc_table with
3704290000Sglebius	 * the retrieved values.
3705290000Sglebius	 */
3706290000Sglebius	while (nextvar(&rsize, &rdata, &tag, &val)) {
3707290000Sglebius		if (NULL == val)
3708290000Sglebius			continue;
3709290000Sglebius		n = 0;
3710290000Sglebius		for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3711290000Sglebius			len = strlen(pvdc->tag);
3712290000Sglebius			if (strncmp(tag, pvdc->tag, len))
3713290000Sglebius				continue;
3714290000Sglebius			if (NTP_ADD != pvdc->type) {
3715290000Sglebius				if ('\0' != tag[len])
3716290000Sglebius					continue;
3717290000Sglebius				break;
3718290000Sglebius			}
3719290000Sglebius			match = FALSE;
3720290000Sglebius			for (n = 0; n < COUNTOF(suf); n++) {
3721290000Sglebius				if (strcmp(tag + len, suf[n]))
3722290000Sglebius					continue;
3723290000Sglebius				match = TRUE;
3724290000Sglebius				break;
3725290000Sglebius			}
3726290000Sglebius			if (match)
3727290000Sglebius				break;
3728290000Sglebius		}
3729290000Sglebius		if (NULL == pvdc->tag)
3730290000Sglebius			continue;
3731290000Sglebius		switch (pvdc->type) {
3732290000Sglebius
3733290000Sglebius		case NTP_STR:
3734290000Sglebius			/* strip surrounding double quotes */
3735290000Sglebius			if ('"' == val[0]) {
3736290000Sglebius				len = strlen(val);
3737290000Sglebius				if (len > 0 && '"' == val[len - 1]) {
3738290000Sglebius					val[len - 1] = '\0';
3739290000Sglebius					val++;
3740290000Sglebius				}
3741290000Sglebius			}
3742290000Sglebius			/* fallthru */
3743290000Sglebius		case NTP_MODE:	/* fallthru */
3744290000Sglebius		case NTP_2BIT:
3745290000Sglebius			pvdc->v.str = estrdup(val);
3746290000Sglebius			break;
3747290000Sglebius
3748290000Sglebius		case NTP_LFP:
3749290000Sglebius			decodets(val, &pvdc->v.lfp);
3750290000Sglebius			break;
3751290000Sglebius
3752290000Sglebius		case NTP_ADP:
3753290000Sglebius			if (!decodenetnum(val, &pvdc->v.sau))
3754290000Sglebius				fprintf(stderr, "malformed %s=%s\n",
3755290000Sglebius					pvdc->tag, val);
3756290000Sglebius			break;
3757290000Sglebius
3758290000Sglebius		case NTP_ADD:
3759290000Sglebius			if (0 == n) {	/* adr */
3760290000Sglebius				if (!decodenetnum(val, &pvdc->v.sau))
3761290000Sglebius					fprintf(stderr,
3762290000Sglebius						"malformed %s=%s\n",
3763290000Sglebius						pvdc->tag, val);
3764290000Sglebius			} else {	/* port */
3765290000Sglebius				if (atouint(val, &ul))
3766290000Sglebius					SET_PORT(&pvdc->v.sau,
3767290000Sglebius						 (u_short)ul);
3768290000Sglebius			}
3769290000Sglebius			break;
3770290000Sglebius		}
3771290000Sglebius	}
3772290000Sglebius
3773290000Sglebius	/* and display */
3774290000Sglebius	if (decodestatus) {
3775290000Sglebius		vtype = (0 == as)
3776290000Sglebius			    ? TYPE_SYS
3777290000Sglebius			    : TYPE_PEER;
3778290000Sglebius		fprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus,
3779290000Sglebius			statustoa(vtype, rstatus));
3780290000Sglebius	}
3781290000Sglebius
3782290000Sglebius	for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3783290000Sglebius		switch (pvdc->type) {
3784290000Sglebius
3785290000Sglebius		case NTP_STR:
3786290000Sglebius			if (pvdc->v.str != NULL) {
3787290000Sglebius				fprintf(fp, "%s  %s\n", pvdc->display,
3788290000Sglebius					pvdc->v.str);
3789290000Sglebius				free(pvdc->v.str);
3790290000Sglebius				pvdc->v.str = NULL;
3791290000Sglebius			}
3792290000Sglebius			break;
3793290000Sglebius
3794290000Sglebius		case NTP_ADD:	/* fallthru */
3795290000Sglebius		case NTP_ADP:
3796290000Sglebius			fprintf(fp, "%s  %s\n", pvdc->display,
3797290000Sglebius				nntohostp(&pvdc->v.sau));
3798290000Sglebius			break;
3799290000Sglebius
3800290000Sglebius		case NTP_LFP:
3801290000Sglebius			fprintf(fp, "%s  %s\n", pvdc->display,
3802290000Sglebius				prettydate(&pvdc->v.lfp));
3803290000Sglebius			break;
3804290000Sglebius
3805290000Sglebius		case NTP_MODE:
3806290000Sglebius			atouint(pvdc->v.str, &ul);
3807290000Sglebius			fprintf(fp, "%s  %s\n", pvdc->display,
3808290000Sglebius				modetoa((int)ul));
3809290000Sglebius			break;
3810290000Sglebius
3811290000Sglebius		case NTP_2BIT:
3812290000Sglebius			atouint(pvdc->v.str, &ul);
3813290000Sglebius			fprintf(fp, "%s  %s\n", pvdc->display,
3814290000Sglebius				leapbits[ul & 0x3]);
3815290000Sglebius			break;
3816290000Sglebius
3817290000Sglebius		default:
3818290000Sglebius			fprintf(stderr, "unexpected vdc type %d for %s\n",
3819290000Sglebius				pvdc->type, pvdc->tag);
3820290000Sglebius			break;
3821290000Sglebius		}
3822290000Sglebius	}
3823290000Sglebius}
3824290000Sglebius
3825290000Sglebius
3826290000Sglebius/*
3827290000Sglebius * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats
3828290000Sglebius */
3829290000Sglebiusstatic void
3830290000Sglebiussysstats(
3831290000Sglebius	struct parse *pcmd,
3832290000Sglebius	FILE *fp
3833290000Sglebius	)
3834290000Sglebius{
3835290000Sglebius    static vdc sysstats_vdc[] = {
3836290000Sglebius	VDC_INIT("ss_uptime",		"uptime:               ", NTP_STR),
3837290000Sglebius	VDC_INIT("ss_reset",		"sysstats reset:       ", NTP_STR),
3838290000Sglebius	VDC_INIT("ss_received",		"packets received:     ", NTP_STR),
3839290000Sglebius	VDC_INIT("ss_thisver",		"current version:      ", NTP_STR),
3840290000Sglebius	VDC_INIT("ss_oldver",		"older version:        ", NTP_STR),
3841290000Sglebius	VDC_INIT("ss_badformat",	"bad length or format: ", NTP_STR),
3842290000Sglebius	VDC_INIT("ss_badauth",		"authentication failed:", NTP_STR),
3843290000Sglebius	VDC_INIT("ss_declined",		"declined:             ", NTP_STR),
3844290000Sglebius	VDC_INIT("ss_restricted",	"restricted:           ", NTP_STR),
3845290000Sglebius	VDC_INIT("ss_limited",		"rate limited:         ", NTP_STR),
3846290000Sglebius	VDC_INIT("ss_kodsent",		"KoD responses:        ", NTP_STR),
3847290000Sglebius	VDC_INIT("ss_processed",	"processed for time:   ", NTP_STR),
3848290000Sglebius	VDC_INIT(NULL,			NULL,			  0)
3849290000Sglebius    };
3850290000Sglebius
3851290000Sglebius	collect_display_vdc(0, sysstats_vdc, FALSE, fp);
3852290000Sglebius}
3853290000Sglebius
3854290000Sglebius
3855290000Sglebius/*
3856290000Sglebius * sysinfo - modeled on ntpdc's sysinfo
3857290000Sglebius */
3858290000Sglebiusstatic void
3859290000Sglebiussysinfo(
3860290000Sglebius	struct parse *pcmd,
3861290000Sglebius	FILE *fp
3862290000Sglebius	)
3863290000Sglebius{
3864290000Sglebius    static vdc sysinfo_vdc[] = {
3865290000Sglebius	VDC_INIT("peeradr",		"system peer:      ", NTP_ADP),
3866290000Sglebius	VDC_INIT("peermode",		"system peer mode: ", NTP_MODE),
3867290000Sglebius	VDC_INIT("leap",		"leap indicator:   ", NTP_2BIT),
3868290000Sglebius	VDC_INIT("stratum",		"stratum:          ", NTP_STR),
3869290000Sglebius	VDC_INIT("precision",		"log2 precision:   ", NTP_STR),
3870290000Sglebius	VDC_INIT("rootdelay",		"root delay:       ", NTP_STR),
3871290000Sglebius	VDC_INIT("rootdisp",		"root dispersion:  ", NTP_STR),
3872290000Sglebius	VDC_INIT("refid",		"reference ID:     ", NTP_STR),
3873290000Sglebius	VDC_INIT("reftime",		"reference time:   ", NTP_LFP),
3874290000Sglebius	VDC_INIT("sys_jitter",		"system jitter:    ", NTP_STR),
3875290000Sglebius	VDC_INIT("clk_jitter",		"clock jitter:     ", NTP_STR),
3876290000Sglebius	VDC_INIT("clk_wander",		"clock wander:     ", NTP_STR),
3877290000Sglebius	VDC_INIT("bcastdelay",		"broadcast delay:  ", NTP_STR),
3878290000Sglebius	VDC_INIT("authdelay",		"symm. auth. delay:", NTP_STR),
3879290000Sglebius	VDC_INIT(NULL,			NULL,		      0)
3880290000Sglebius    };
3881290000Sglebius
3882290000Sglebius	collect_display_vdc(0, sysinfo_vdc, TRUE, fp);
3883290000Sglebius}
3884290000Sglebius
3885290000Sglebius
3886290000Sglebius/*
3887290000Sglebius * kerninfo - modeled on ntpdc's kerninfo
3888290000Sglebius */
3889290000Sglebiusstatic void
3890290000Sglebiuskerninfo(
3891290000Sglebius	struct parse *pcmd,
3892290000Sglebius	FILE *fp
3893290000Sglebius	)
3894290000Sglebius{
3895290000Sglebius    static vdc kerninfo_vdc[] = {
3896290000Sglebius	VDC_INIT("koffset",		"pll offset:          ", NTP_STR),
3897290000Sglebius	VDC_INIT("kfreq",		"pll frequency:       ", NTP_STR),
3898290000Sglebius	VDC_INIT("kmaxerr",		"maximum error:       ", NTP_STR),
3899290000Sglebius	VDC_INIT("kesterr",		"estimated error:     ", NTP_STR),
3900290000Sglebius	VDC_INIT("kstflags",		"kernel status:       ", NTP_STR),
3901290000Sglebius	VDC_INIT("ktimeconst",		"pll time constant:   ", NTP_STR),
3902290000Sglebius	VDC_INIT("kprecis",		"precision:           ", NTP_STR),
3903290000Sglebius	VDC_INIT("kfreqtol",		"frequency tolerance: ", NTP_STR),
3904290000Sglebius	VDC_INIT("kppsfreq",		"pps frequency:       ", NTP_STR),
3905290000Sglebius	VDC_INIT("kppsstab",		"pps stability:       ", NTP_STR),
3906290000Sglebius	VDC_INIT("kppsjitter",		"pps jitter:          ", NTP_STR),
3907290000Sglebius	VDC_INIT("kppscalibdur",	"calibration interval ", NTP_STR),
3908290000Sglebius	VDC_INIT("kppscalibs",		"calibration cycles:  ", NTP_STR),
3909290000Sglebius	VDC_INIT("kppsjitexc",		"jitter exceeded:     ", NTP_STR),
3910290000Sglebius	VDC_INIT("kppsstbexc",		"stability exceeded:  ", NTP_STR),
3911290000Sglebius	VDC_INIT("kppscaliberrs",	"calibration errors:  ", NTP_STR),
3912290000Sglebius	VDC_INIT(NULL,			NULL,			 0)
3913290000Sglebius    };
3914290000Sglebius
3915290000Sglebius	collect_display_vdc(0, kerninfo_vdc, TRUE, fp);
3916290000Sglebius}
3917290000Sglebius
3918290000Sglebius
3919290000Sglebius/*
3920290000Sglebius * monstats - implements ntpq -c monstats
3921290000Sglebius */
3922290000Sglebiusstatic void
3923290000Sglebiusmonstats(
3924290000Sglebius	struct parse *pcmd,
3925290000Sglebius	FILE *fp
3926290000Sglebius	)
3927290000Sglebius{
3928290000Sglebius    static vdc monstats_vdc[] = {
3929290000Sglebius	VDC_INIT("mru_enabled",	"enabled:            ", NTP_STR),
3930290000Sglebius	VDC_INIT("mru_depth",		"addresses:          ", NTP_STR),
3931290000Sglebius	VDC_INIT("mru_deepest",	"peak addresses:     ", NTP_STR),
3932290000Sglebius	VDC_INIT("mru_maxdepth",	"maximum addresses:  ", NTP_STR),
3933290000Sglebius	VDC_INIT("mru_mindepth",	"reclaim above count:", NTP_STR),
3934290000Sglebius	VDC_INIT("mru_maxage",		"reclaim older than: ", NTP_STR),
3935290000Sglebius	VDC_INIT("mru_mem",		"kilobytes:          ", NTP_STR),
3936290000Sglebius	VDC_INIT("mru_maxmem",		"maximum kilobytes:  ", NTP_STR),
3937290000Sglebius	VDC_INIT(NULL,			NULL,			0)
3938290000Sglebius    };
3939290000Sglebius
3940290000Sglebius	collect_display_vdc(0, monstats_vdc, FALSE, fp);
3941290000Sglebius}
3942290000Sglebius
3943290000Sglebius
3944290000Sglebius/*
3945290000Sglebius * iostats - ntpq -c iostats - network input and output counters
3946290000Sglebius */
3947290000Sglebiusstatic void
3948290000Sglebiusiostats(
3949290000Sglebius	struct parse *pcmd,
3950290000Sglebius	FILE *fp
3951290000Sglebius	)
3952290000Sglebius{
3953290000Sglebius    static vdc iostats_vdc[] = {
3954290000Sglebius	VDC_INIT("iostats_reset",	"time since reset:     ", NTP_STR),
3955290000Sglebius	VDC_INIT("total_rbuf",		"receive buffers:      ", NTP_STR),
3956290000Sglebius	VDC_INIT("free_rbuf",		"free receive buffers: ", NTP_STR),
3957290000Sglebius	VDC_INIT("used_rbuf",		"used receive buffers: ", NTP_STR),
3958290000Sglebius	VDC_INIT("rbuf_lowater",	"low water refills:    ", NTP_STR),
3959290000Sglebius	VDC_INIT("io_dropped",		"dropped packets:      ", NTP_STR),
3960290000Sglebius	VDC_INIT("io_ignored",		"ignored packets:      ", NTP_STR),
3961290000Sglebius	VDC_INIT("io_received",		"received packets:     ", NTP_STR),
3962290000Sglebius	VDC_INIT("io_sent",		"packets sent:         ", NTP_STR),
3963290000Sglebius	VDC_INIT("io_sendfailed",	"packet send failures: ", NTP_STR),
3964290000Sglebius	VDC_INIT("io_wakeups",		"input wakeups:        ", NTP_STR),
3965290000Sglebius	VDC_INIT("io_goodwakeups",	"useful input wakeups: ", NTP_STR),
3966290000Sglebius	VDC_INIT(NULL,			NULL,			  0)
3967290000Sglebius    };
3968290000Sglebius
3969290000Sglebius	collect_display_vdc(0, iostats_vdc, FALSE, fp);
3970290000Sglebius}
3971290000Sglebius
3972290000Sglebius
3973290000Sglebius/*
3974290000Sglebius * timerstats - ntpq -c timerstats - interval timer counters
3975290000Sglebius */
3976290000Sglebiusstatic void
3977290000Sglebiustimerstats(
3978290000Sglebius	struct parse *pcmd,
3979290000Sglebius	FILE *fp
3980290000Sglebius	)
3981290000Sglebius{
3982290000Sglebius    static vdc timerstats_vdc[] = {
3983290000Sglebius	VDC_INIT("timerstats_reset",	"time since reset:  ", NTP_STR),
3984290000Sglebius	VDC_INIT("timer_overruns",	"timer overruns:    ", NTP_STR),
3985290000Sglebius	VDC_INIT("timer_xmts",		"calls to transmit: ", NTP_STR),
3986290000Sglebius	VDC_INIT(NULL,			NULL,		       0)
3987290000Sglebius    };
3988290000Sglebius
3989290000Sglebius	collect_display_vdc(0, timerstats_vdc, FALSE, fp);
3990290000Sglebius}
3991290000Sglebius
3992290000Sglebius
3993290000Sglebius/*
3994290000Sglebius * authinfo - implements ntpq -c authinfo
3995290000Sglebius */
3996290000Sglebiusstatic void
3997290000Sglebiusauthinfo(
3998290000Sglebius	struct parse *pcmd,
3999290000Sglebius	FILE *fp
4000290000Sglebius	)
4001290000Sglebius{
4002290000Sglebius    static vdc authinfo_vdc[] = {
4003290000Sglebius	VDC_INIT("authreset",		"time since reset:", NTP_STR),
4004290000Sglebius	VDC_INIT("authkeys",		"stored keys:     ", NTP_STR),
4005290000Sglebius	VDC_INIT("authfreek",		"free keys:       ", NTP_STR),
4006290000Sglebius	VDC_INIT("authklookups",	"key lookups:     ", NTP_STR),
4007290000Sglebius	VDC_INIT("authknotfound",	"keys not found:  ", NTP_STR),
4008290000Sglebius	VDC_INIT("authkuncached",	"uncached keys:   ", NTP_STR),
4009290000Sglebius	VDC_INIT("authkexpired",	"expired keys:    ", NTP_STR),
4010290000Sglebius	VDC_INIT("authencrypts",	"encryptions:     ", NTP_STR),
4011290000Sglebius	VDC_INIT("authdecrypts",	"decryptions:     ", NTP_STR),
4012290000Sglebius	VDC_INIT(NULL,			NULL,		     0)
4013290000Sglebius    };
4014290000Sglebius
4015290000Sglebius	collect_display_vdc(0, authinfo_vdc, FALSE, fp);
4016290000Sglebius}
4017290000Sglebius
4018290000Sglebius
4019290000Sglebius/*
4020290000Sglebius * pstats - show statistics for a peer
4021290000Sglebius */
4022290000Sglebiusstatic void
4023290000Sglebiuspstats(
4024290000Sglebius	struct parse *pcmd,
4025290000Sglebius	FILE *fp
4026290000Sglebius	)
4027290000Sglebius{
4028290000Sglebius    static vdc pstats_vdc[] = {
4029290000Sglebius	VDC_INIT("src",		"remote host:         ", NTP_ADD),
4030290000Sglebius	VDC_INIT("dst",		"local address:       ", NTP_ADD),
4031290000Sglebius	VDC_INIT("timerec",	"time last received:  ", NTP_STR),
4032290000Sglebius	VDC_INIT("timer",	"time until next send:", NTP_STR),
4033290000Sglebius	VDC_INIT("timereach",	"reachability change: ", NTP_STR),
4034290000Sglebius	VDC_INIT("sent",	"packets sent:        ", NTP_STR),
4035290000Sglebius	VDC_INIT("received",	"packets received:    ", NTP_STR),
4036290000Sglebius	VDC_INIT("badauth",	"bad authentication:  ", NTP_STR),
4037290000Sglebius	VDC_INIT("bogusorg",	"bogus origin:        ", NTP_STR),
4038290000Sglebius	VDC_INIT("oldpkt",	"duplicate:           ", NTP_STR),
4039290000Sglebius	VDC_INIT("seldisp",	"bad dispersion:      ", NTP_STR),
4040290000Sglebius	VDC_INIT("selbroken",	"bad reference time:  ", NTP_STR),
4041290000Sglebius	VDC_INIT("candidate",	"candidate order:     ", NTP_STR),
4042290000Sglebius	VDC_INIT(NULL,		NULL,			 0)
4043290000Sglebius    };
4044290000Sglebius	associd_t associd;
4045290000Sglebius
4046290000Sglebius	associd = checkassocid(pcmd->argval[0].uval);
4047290000Sglebius	if (0 == associd)
4048290000Sglebius		return;
4049290000Sglebius
4050290000Sglebius	collect_display_vdc(associd, pstats_vdc, TRUE, fp);
4051290000Sglebius}
4052