1ef64b99roberto/*
2047f369cy * ntpq-subs.c - subroutines which are called to perform ntpq commands.
3ef64b99roberto */
4047f369cy#include <config.h>
5ef64b99roberto#include <stdio.h>
6118e757roberto#include <ctype.h>
7118e757roberto#include <sys/types.h>
8118e757roberto#include <sys/time.h>
9ef64b99roberto
10d54cfbdroberto#include "ntpq.h"
11d54cfbdroberto#include "ntpq-opts.h"
12ef64b99roberto
13047f369cyextern char	currenthost[];
14047f369cyextern int	currenthostisnum;
15047f369cysize_t		maxhostlen;
16ef64b99roberto
17ef64b99roberto/*
18ef64b99roberto * Declarations for command handlers in here
19ef64b99roberto */
20d54cfbdrobertostatic	associd_t checkassocid	(u_int32);
21d54cfbdrobertostatic	struct varlist *findlistvar (struct varlist *, char *);
22d54cfbdrobertostatic	void	doaddvlist	(struct varlist *, const char *);
23d54cfbdrobertostatic	void	dormvlist	(struct varlist *, const char *);
24d54cfbdrobertostatic	void	doclearvlist	(struct varlist *);
258518518delphijstatic	void	makequerydata	(struct varlist *, size_t *, char *);
26d54cfbdrobertostatic	int	doquerylist	(struct varlist *, int, associd_t, int,
278518518delphij				 u_short *, size_t *, const char **);
28d54cfbdrobertostatic	void	doprintvlist	(struct varlist *, FILE *);
29d54cfbdrobertostatic	void	addvars 	(struct parse *, FILE *);
30d54cfbdrobertostatic	void	rmvars		(struct parse *, FILE *);
31d54cfbdrobertostatic	void	clearvars	(struct parse *, FILE *);
32d54cfbdrobertostatic	void	showvars	(struct parse *, FILE *);
33d54cfbdrobertostatic	int	dolist		(struct varlist *, associd_t, int, int,
34d54cfbdroberto				 FILE *);
35d54cfbdrobertostatic	void	readlist	(struct parse *, FILE *);
36d54cfbdrobertostatic	void	writelist	(struct parse *, FILE *);
37d54cfbdrobertostatic	void	readvar 	(struct parse *, FILE *);
38d54cfbdrobertostatic	void	writevar	(struct parse *, FILE *);
39d54cfbdrobertostatic	void	clocklist	(struct parse *, FILE *);
40d54cfbdrobertostatic	void	clockvar	(struct parse *, FILE *);
41047f369cystatic	int	findassidrange	(u_int32, u_int32, int *, int *,
42047f369cy				 FILE *);
43d54cfbdrobertostatic	void	mreadlist	(struct parse *, FILE *);
44d54cfbdrobertostatic	void	mreadvar	(struct parse *, FILE *);
45d54cfbdrobertostatic	void	printassoc	(int, FILE *);
46d54cfbdrobertostatic	void	associations	(struct parse *, FILE *);
47d54cfbdrobertostatic	void	lassociations	(struct parse *, FILE *);
48d54cfbdrobertostatic	void	passociations	(struct parse *, FILE *);
49d54cfbdrobertostatic	void	lpassociations	(struct parse *, FILE *);
50ef64b99roberto
51ef64b99roberto#ifdef	UNUSED
52d54cfbdrobertostatic	void	radiostatus (struct parse *, FILE *);
53ef64b99roberto#endif	/* UNUSED */
54ef64b99roberto
55047f369cystatic	void	authinfo	(struct parse *, FILE *);
56047f369cystatic	void	pstats	 	(struct parse *, FILE *);
57d54cfbdrobertostatic	long	when		(l_fp *, l_fp *, l_fp *);
58d54cfbdrobertostatic	char *	prettyinterval	(char *, size_t, long);
598518518delphijstatic	int	doprintpeers	(struct varlist *, int, int, size_t, const char *, FILE *, int);
60d54cfbdrobertostatic	int	dogetpeers	(struct varlist *, associd_t, FILE *, int);
61d54cfbdrobertostatic	void	dopeers 	(int, FILE *, int);
62d54cfbdrobertostatic	void	peers		(struct parse *, FILE *);
635ef283fcystatic	void	doapeers 	(int, FILE *, int);
645ef283fcystatic	void	apeers		(struct parse *, FILE *);
65d54cfbdrobertostatic	void	lpeers		(struct parse *, FILE *);
66d54cfbdrobertostatic	void	doopeers	(int, FILE *, int);
67d54cfbdrobertostatic	void	opeers		(struct parse *, FILE *);
68d54cfbdrobertostatic	void	lopeers 	(struct parse *, FILE *);
69047f369cystatic	void	config		(struct parse *, FILE *);
70047f369cystatic	void	saveconfig	(struct parse *, FILE *);
71047f369cystatic	void	config_from_file(struct parse *, FILE *);
72047f369cystatic	void	mrulist		(struct parse *, FILE *);
73047f369cystatic	void	ifstats		(struct parse *, FILE *);
74047f369cystatic	void	reslist		(struct parse *, FILE *);
75047f369cystatic	void	sysstats	(struct parse *, FILE *);
76047f369cystatic	void	sysinfo		(struct parse *, FILE *);
77047f369cystatic	void	kerninfo	(struct parse *, FILE *);
78047f369cystatic	void	monstats	(struct parse *, FILE *);
79047f369cystatic	void	iostats		(struct parse *, FILE *);
80047f369cystatic	void	timerstats	(struct parse *, FILE *);
81ef64b99roberto
82ef64b99roberto/*
83ef64b99roberto * Commands we understand.	Ntpdc imports this.
84ef64b99roberto */
85ef64b99robertostruct xcmd opcmds[] = {
86d54cfbdroberto	{ "saveconfig", saveconfig, { NTP_STR, NO, NO, NO },
87837c91fcy		{ "filename", "", "", ""},
88d54cfbdroberto		"save ntpd configuration to file, . for current config file"},
89ef64b99roberto	{ "associations", associations, {  NO, NO, NO, NO },
90ef64b99roberto	  { "", "", "", "" },
91ef64b99roberto	  "print list of association ID's and statuses for the server's peers" },
92ef64b99roberto	{ "passociations", passociations,   {  NO, NO, NO, NO },
93ef64b99roberto	  { "", "", "", "" },
94ef64b99roberto	  "print list of associations returned by last associations command" },
95ef64b99roberto	{ "lassociations", lassociations,   {  NO, NO, NO, NO },
96ef64b99roberto	  { "", "", "", "" },
97ef64b99roberto	  "print list of associations including all client information" },
98ef64b99roberto	{ "lpassociations", lpassociations, {  NO, NO, NO, NO },
99ef64b99roberto	  { "", "", "", "" },
100ef64b99roberto	  "print last obtained list of associations, including client information" },
1017a6072eroberto	{ "addvars",    addvars,    { NTP_STR, NO, NO, NO },
102ef64b99roberto	  { "name[=value][,...]", "", "", "" },
103ef64b99roberto	  "add variables to the variable list or change their values" },
1047a6072eroberto	{ "rmvars", rmvars,     { NTP_STR, NO, NO, NO },
105ef64b99roberto	  { "name[,...]", "", "", "" },
106ef64b99roberto	  "remove variables from the variable list" },
107ef64b99roberto	{ "clearvars",  clearvars,  { NO, NO, NO, NO },
108ef64b99roberto	  { "", "", "", "" },
109ef64b99roberto	  "remove all variables from the variable list" },
110ef64b99roberto	{ "showvars",   showvars,   { NO, NO, NO, NO },
111ef64b99roberto	  { "", "", "", "" },
112ef64b99roberto	  "print variables on the variable list" },
1137a6072eroberto	{ "readlist",   readlist,   { OPT|NTP_UINT, NO, NO, NO },
114ef64b99roberto	  { "assocID", "", "", "" },
115ef64b99roberto	  "read the system or peer variables included in the variable list" },
1167a6072eroberto	{ "rl",     readlist,   { OPT|NTP_UINT, NO, NO, NO },
117ef64b99roberto	  { "assocID", "", "", "" },
118ef64b99roberto	  "read the system or peer variables included in the variable list" },
1197a6072eroberto	{ "writelist",  writelist,  { OPT|NTP_UINT, NO, NO, NO },
120ef64b99roberto	  { "assocID", "", "", "" },
121ef64b99roberto	  "write the system or peer variables included in the variable list" },
122047f369cy	{ "readvar", readvar,    { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
123047f369cy	  { "assocID", "varname1", "varname2", "varname3" },
124ef64b99roberto	  "read system or peer variables" },
125047f369cy	{ "rv",      readvar,    { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
126047f369cy	  { "assocID", "varname1", "varname2", "varname3" },
127ef64b99roberto	  "read system or peer variables" },
1287a6072eroberto	{ "writevar",   writevar,   { NTP_UINT, NTP_STR, NO, NO },
129ef64b99roberto	  { "assocID", "name=value,[...]", "", "" },
130ef64b99roberto	  "write system or peer variables" },
1317a6072eroberto	{ "mreadlist",  mreadlist,  { NTP_UINT, NTP_UINT, NO, NO },
132047f369cy	  { "assocIDlow", "assocIDhigh", "", "" },
133ef64b99roberto	  "read the peer variables in the variable list for multiple peers" },
1347a6072eroberto	{ "mrl",    mreadlist,  { NTP_UINT, NTP_UINT, NO, NO },
135047f369cy	  { "assocIDlow", "assocIDhigh", "", "" },
136ef64b99roberto	  "read the peer variables in the variable list for multiple peers" },
1377a6072eroberto	{ "mreadvar",   mreadvar,   { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
138047f369cy	  { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
139ef64b99roberto	  "read peer variables from multiple peers" },
1407a6072eroberto	{ "mrv",    mreadvar,   { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
141047f369cy	  { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
142ef64b99roberto	  "read peer variables from multiple peers" },
1437a6072eroberto	{ "clocklist",  clocklist,  { OPT|NTP_UINT, NO, NO, NO },
144ef64b99roberto	  { "assocID", "", "", "" },
145ef64b99roberto	  "read the clock variables included in the variable list" },
1467a6072eroberto	{ "cl",     clocklist,  { OPT|NTP_UINT, NO, NO, NO },
147ef64b99roberto	  { "assocID", "", "", "" },
148ef64b99roberto	  "read the clock variables included in the variable list" },
1497a6072eroberto	{ "clockvar",   clockvar,   { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
150ef64b99roberto	  { "assocID", "name=value[,...]", "", "" },
151ef64b99roberto	  "read clock variables" },
1527a6072eroberto	{ "cv",     clockvar,   { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
153ef64b99roberto	  { "assocID", "name=value[,...]", "", "" },
154ef64b99roberto	  "read clock variables" },
155047f369cy	{ "pstats",    pstats,    { NTP_UINT, NO, NO, NO },
156ef64b99roberto	  { "assocID", "", "", "" },
157047f369cy	  "show statistics for a peer" },
158118e757roberto	{ "peers",  peers,      { OPT|IP_VERSION, NO, NO, NO },
159118e757roberto	  { "-4|-6", "", "", "" },
160118e757roberto	  "obtain and print a list of the server's peers [IP version]" },
1615ef283fcy	{ "apeers",  apeers,      { OPT|IP_VERSION, NO, NO, NO },
1625ef283fcy	  { "-4|-6", "", "", "" },
1635ef283fcy	  "obtain and print a list of the server's peers and their assocIDs [IP version]" },
164118e757roberto	{ "lpeers", lpeers,     { OPT|IP_VERSION, NO, NO, NO },
165118e757roberto	  { "-4|-6", "", "", "" },
166118e757roberto	  "obtain and print a list of all peers and clients [IP version]" },
167118e757roberto	{ "opeers", opeers,     { OPT|IP_VERSION, NO, NO, NO },
168118e757roberto	  { "-4|-6", "", "", "" },
169118e757roberto	  "print peer list the old way, with dstadr shown rather than refid [IP version]" },
170118e757roberto	{ "lopeers", lopeers,   { OPT|IP_VERSION, NO, NO, NO },
171118e757roberto	  { "-4|-6", "", "", "" },
172118e757roberto	  "obtain and print a list of all peers and clients showing dstadr [IP version]" },
173d54cfbdroberto	{ ":config", config,   { NTP_STR, NO, NO, NO },
174d54cfbdroberto	  { "<configuration command line>", "", "", "" },
175d54cfbdroberto	  "send a remote configuration command to ntpd" },
176d54cfbdroberto	{ "config-from-file", config_from_file, { NTP_STR, NO, NO, NO },
177d54cfbdroberto	  { "<configuration filename>", "", "", "" },
178d54cfbdroberto	  "configure ntpd using the configuration filename" },
179047f369cy	{ "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
180047f369cy	  { "tag=value", "tag=value", "tag=value", "tag=value" },
181047f369cy	  "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." },
182047f369cy	{ "ifstats", ifstats, { NO, NO, NO, NO },
183047f369cy	  { "", "", "", "" },
184047f369cy	  "show statistics for each local address ntpd is using" },
185047f369cy	{ "reslist", reslist, { NO, NO, NO, NO },
186047f369cy	  { "", "", "", "" },
187047f369cy	  "show ntpd access control list" },
188047f369cy	{ "sysinfo", sysinfo, { NO, NO, NO, NO },
189047f369cy	  { "", "", "", "" },
190047f369cy	  "display system summary" },
191047f369cy	{ "kerninfo", kerninfo, { NO, NO, NO, NO },
192047f369cy	  { "", "", "", "" },
193047f369cy	  "display kernel loop and PPS statistics" },
194047f369cy	{ "sysstats", sysstats, { NO, NO, NO, NO },
195047f369cy	  { "", "", "", "" },
196047f369cy	  "display system uptime and packet counts" },
197047f369cy	{ "monstats", monstats, { NO, NO, NO, NO },
198047f369cy	  { "", "", "", "" },
199047f369cy	  "display monitor (mrulist) counters and limits" },
200047f369cy	{ "authinfo", authinfo, { NO, NO, NO, NO },
201047f369cy	  { "", "", "", "" },
202047f369cy	  "display symmetric authentication counters" },
203047f369cy	{ "iostats", iostats, { NO, NO, NO, NO },
204047f369cy	  { "", "", "", "" },
205047f369cy	  "display network input and output counters" },
206047f369cy	{ "timerstats", timerstats, { NO, NO, NO, NO },
207047f369cy	  { "", "", "", "" },
208047f369cy	  "display interval timer counters" },
209ef64b99roberto	{ 0,		0,		{ NO, NO, NO, NO },
210118e757roberto	  { "-4|-6", "", "", "" }, "" }
211ef64b99roberto};
212ef64b99roberto
213ef64b99roberto
214ef64b99roberto/*
215ef64b99roberto * Variable list data space
216ef64b99roberto */
217047f369cy#define MAXLINE		512	/* maximum length of a line */
218047f369cy#define MAXLIST		128	/* maximum variables in list */
219047f369cy#define LENHOSTNAME	256	/* host name limit */
220047f369cy
221047f369cy#define MRU_GOT_COUNT	0x1
222047f369cy#define MRU_GOT_LAST	0x2
223047f369cy#define MRU_GOT_FIRST	0x4
224047f369cy#define MRU_GOT_MV	0x8
225047f369cy#define MRU_GOT_RS	0x10
226047f369cy#define MRU_GOT_ADDR	0x20
227047f369cy#define MRU_GOT_ALL	(MRU_GOT_COUNT | MRU_GOT_LAST | MRU_GOT_FIRST \
228047f369cy			 | MRU_GOT_MV | MRU_GOT_RS | MRU_GOT_ADDR)
229047f369cy
230047f369cy/*
231047f369cy * mrulist() depends on MRUSORT_DEF and MRUSORT_RDEF being the first two
232047f369cy */
233047f369cytypedef enum mru_sort_order_tag {
234047f369cy	MRUSORT_DEF = 0,	/* lstint ascending */
235047f369cy	MRUSORT_R_DEF,		/* lstint descending */
236047f369cy	MRUSORT_AVGINT,		/* avgint ascending */
237047f369cy	MRUSORT_R_AVGINT,	/* avgint descending */
238047f369cy	MRUSORT_ADDR,		/* IPv4 asc. then IPv6 asc. */
239047f369cy	MRUSORT_R_ADDR,		/* IPv6 desc. then IPv4 desc. */
240047f369cy	MRUSORT_COUNT,		/* hit count ascending */
241047f369cy	MRUSORT_R_COUNT,	/* hit count descending */
242047f369cy	MRUSORT_MAX,		/* special: count of this enum */
243047f369cy} mru_sort_order;
244047f369cy
245047f369cyconst char * const mru_sort_keywords[MRUSORT_MAX] = {
246047f369cy	"lstint",		/* MRUSORT_DEF */
247047f369cy	"-lstint",		/* MRUSORT_R_DEF */
248047f369cy	"avgint",		/* MRUSORT_AVGINT */
249047f369cy	"-avgint",		/* MRUSORT_R_AVGINT */
250047f369cy	"addr",			/* MRUSORT_ADDR */
251047f369cy	"-addr",		/* MRUSORT_R_ADDR */
252047f369cy	"count",		/* MRUSORT_COUNT */
253047f369cy	"-count",		/* MRUSORT_R_COUNT */
254047f369cy};
255047f369cy
256047f369cytypedef int (*qsort_cmp)(const void *, const void *);
257047f369cy
258ef64b99roberto/*
259ef64b99roberto * Old CTL_PST defines for version 2.
260ef64b99roberto */
261d54cfbdroberto#define OLD_CTL_PST_CONFIG		0x80
262ef64b99roberto#define OLD_CTL_PST_AUTHENABLE		0x40
263ef64b99roberto#define OLD_CTL_PST_AUTHENTIC		0x20
264d54cfbdroberto#define OLD_CTL_PST_REACH		0x10
265d54cfbdroberto#define OLD_CTL_PST_SANE		0x08
266d54cfbdroberto#define OLD_CTL_PST_DISP		0x04
267d54cfbdroberto
268ef64b99roberto#define OLD_CTL_PST_SEL_REJECT		0
269ef64b99roberto#define OLD_CTL_PST_SEL_SELCAND 	1
270ef64b99roberto#define OLD_CTL_PST_SEL_SYNCCAND	2
271ef64b99roberto#define OLD_CTL_PST_SEL_SYSPEER 	3
272ef64b99roberto
273ef64b99robertochar flash2[] = " .+*    "; /* flash decode for version 2 */
274ef64b99robertochar flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
275ef64b99roberto
276ef64b99robertostruct varlist {
277bdc155dcy	const char *name;
278ef64b99roberto	char *value;
279d54cfbdroberto} g_varlist[MAXLIST] = { { 0, 0 } };
280ef64b99roberto
281ef64b99roberto/*
282ef64b99roberto * Imported from ntpq.c
283ef64b99roberto */
284ef64b99robertoextern int showhostnames;
285047f369cyextern int wideremote;
286ef64b99robertoextern int rawmode;
287ef64b99robertoextern struct servent *server_entry;
288047f369cyextern struct association *assoc_cache;
289ef64b99robertoextern u_char pktversion;
290047f369cy
291047f369cytypedef struct mru_tag mru;
292047f369cystruct mru_tag {
293047f369cy	mru *		hlink;	/* next in hash table bucket */
294047f369cy	DECL_DLIST_LINK(mru, mlink);
295047f369cy	int		count;
296047f369cy	l_fp		last;
297047f369cy	l_fp		first;
298047f369cy	u_char		mode;
299047f369cy	u_char		ver;
300047f369cy	u_short		rs;
301047f369cy	sockaddr_u	addr;
302047f369cy};
303047f369cy
304047f369cytypedef struct ifstats_row_tag {
305047f369cy	u_int		ifnum;
306047f369cy	sockaddr_u	addr;
307047f369cy	sockaddr_u	bcast;
308047f369cy	int		enabled;
309047f369cy	u_int		flags;
31030f4731delphij	u_int		mcast_count;
311047f369cy	char		name[32];
31230f4731delphij	u_int		peer_count;
31330f4731delphij	u_int		received;
31430f4731delphij	u_int		sent;
31530f4731delphij	u_int		send_errors;
316047f369cy	u_int		ttl;
317047f369cy	u_int		uptime;
318047f369cy} ifstats_row;
319047f369cy
320047f369cytypedef struct reslist_row_tag {
321047f369cy	u_int		idx;
322047f369cy	sockaddr_u	addr;
323047f369cy	sockaddr_u	mask;
324047f369cy	u_long		hits;
325047f369cy	char		flagstr[128];
326047f369cy} reslist_row;
327047f369cy
328047f369cytypedef struct var_display_collection_tag {
329047f369cy	const char * const tag;		/* system variable */
330047f369cy	const char * const display;	/* descriptive text */
331047f369cy	u_char type;			/* NTP_STR, etc */
332047f369cy	union {
333047f369cy		char *		str;
334047f369cy		sockaddr_u	sau;	/* NTP_ADD */
335047f369cy		l_fp		lfp;	/* NTP_LFP */
336047f369cy	} v;				/* retrieved value */
337047f369cy} vdc;
338f63afe2cy#if !defined(MISSING_C99_STRUCT_INIT)
339bdc155dcy# define VDC_INIT(a, b, c) { .tag = a, .display = b, .type = c }
340bdc155dcy#else
341bdc155dcy# define VDC_INIT(a, b, c) { a, b, c }
342bdc155dcy#endif
343047f369cy/*
344047f369cy * other local function prototypes
345047f369cy */
3468518518delphijstatic int	mrulist_ctrl_c_hook(void);
347047f369cystatic mru *	add_mru(mru *);
348047f369cystatic int	collect_mru_list(const char *, l_fp *);
349047f369cystatic int	fetch_nonce(char *, size_t);
350047f369cystatic int	qcmp_mru_avgint(const void *, const void *);
351047f369cystatic int	qcmp_mru_r_avgint(const void *, const void *);
352047f369cystatic int	qcmp_mru_addr(const void *, const void *);
353047f369cystatic int	qcmp_mru_r_addr(const void *, const void *);
354047f369cystatic int	qcmp_mru_count(const void *, const void *);
355047f369cystatic int	qcmp_mru_r_count(const void *, const void *);
356047f369cystatic void	validate_ifnum(FILE *, u_int, int *, ifstats_row *);
357047f369cystatic void	another_ifstats_field(int *, ifstats_row *, FILE *);
358047f369cystatic void	collect_display_vdc(associd_t as, vdc *table,
359047f369cy				    int decodestatus, FILE *fp);
360ef64b99roberto
361837c91fcystatic	int	xprintf(FILE *,	char const *, ...) NTP_PRINTF(2, 3);
362837c91fcystatic	int	xputs(char const *, FILE *);
363837c91fcystatic	int	xputc(int, FILE *);
364837c91fcy
365ef64b99roberto/*
366047f369cy * static globals
367ef64b99roberto */
368047f369cystatic u_int	mru_count;
369047f369cystatic u_int	mru_dupes;
370047f369cyvolatile int	mrulist_interrupted;
371047f369cystatic mru	mru_list;		/* listhead */
372047f369cystatic mru **	hash_table;
373ef64b99roberto
374047f369cy/*
375047f369cy * qsort comparison function table for mrulist().  The first two
376047f369cy * entries are NULL because they are handled without qsort().
377047f369cy */
378bdc155dcystatic const qsort_cmp mru_qcmp_table[MRUSORT_MAX] = {
379047f369cy	NULL,			/* MRUSORT_DEF unused */
380047f369cy	NULL,			/* MRUSORT_R_DEF unused */
381047f369cy	&qcmp_mru_avgint,	/* MRUSORT_AVGINT */
382047f369cy	&qcmp_mru_r_avgint,	/* MRUSORT_R_AVGINT */
383047f369cy	&qcmp_mru_addr,		/* MRUSORT_ADDR */
384047f369cy	&qcmp_mru_r_addr,	/* MRUSORT_R_ADDR */
385047f369cy	&qcmp_mru_count,	/* MRUSORT_COUNT */
386047f369cy	&qcmp_mru_r_count,	/* MRUSORT_R_COUNT */
387047f369cy};
388ef64b99roberto
389ef64b99roberto/*
390837c91fcy * NULL-pointer safe FILE I/O: use stderr if no file supplied.
391837c91fcy */
392837c91fcystatic	int
393837c91fcyxprintf(
394837c91fcy	FILE *		ofp,
395837c91fcy	char const *	fmt,
396837c91fcy	...
397837c91fcy	)
398837c91fcy{
399837c91fcy	va_list	va;
400837c91fcy	int	rc;
401837c91fcy
402837c91fcy	va_start(va, fmt);
403837c91fcy	rc = vfprintf((ofp ? ofp : stderr), fmt, va);
404837c91fcy	va_end(va);
405837c91fcy	return rc;
406837c91fcy}
407837c91fcy
408837c91fcystatic	int
409837c91fcyxputs(
410837c91fcy	char const *	str,
411837c91fcy	FILE *		ofp
412837c91fcy	)
413837c91fcy{
414837c91fcy	return fputs(str, (ofp ? ofp : stderr));
415837c91fcy}
416837c91fcy
417837c91fcystatic	int
418837c91fcyxputc(
419837c91fcy	int	ch,
420837c91fcy	FILE *	ofp
421837c91fcy	)
422837c91fcy{
423837c91fcy	return fputc(ch, (ofp ? ofp : stderr));
424837c91fcy}
425837c91fcy
426837c91fcy/*
427ef64b99roberto * checkassocid - return the association ID, checking to see if it is valid
428ef64b99roberto */
429d54cfbdrobertostatic associd_t
430ef64b99robertocheckassocid(
431ef64b99roberto	u_int32 value
432ef64b99roberto	)
433ef64b99roberto{
434d54cfbdroberto	associd_t	associd;
435d54cfbdroberto	u_long		ulvalue;
436d54cfbdroberto
437d54cfbdroberto	associd = (associd_t)value;
438d54cfbdroberto	if (0 == associd || value != associd) {
439d54cfbdroberto		ulvalue = value;
440837c91fcy		xprintf(stderr,
441d54cfbdroberto			"***Invalid association ID %lu specified\n",
442d54cfbdroberto			ulvalue);
443ef64b99roberto		return 0;
444ef64b99roberto	}
445ef64b99roberto
446d54cfbdroberto	return associd;
447ef64b99roberto}
448ef64b99roberto
449ef64b99roberto
450ef64b99roberto/*
451047f369cy * findlistvar - Look for the named variable in a varlist.  If found,
452047f369cy *		 return a pointer to it.  Otherwise, if the list has
453047f369cy *		 slots available, return the pointer to the first free
454047f369cy *		 slot, or NULL if it's full.
455ef64b99roberto */
456ef64b99robertostatic struct varlist *
457ef64b99robertofindlistvar(
458ef64b99roberto	struct varlist *list,
459ef64b99roberto	char *name
460ef64b99roberto	)
461ef64b99roberto{
462047f369cy	struct varlist *vl;
463ef64b99roberto
464047f369cy	for (vl = list; vl < list + MAXLIST && vl->name != NULL; vl++)
465047f369cy		if (!strcmp(name, vl->name))
466047f369cy			return vl;
467ef64b99roberto	if (vl < list + MAXLIST)
468ef64b99roberto		return vl;
469047f369cy
470047f369cy	return NULL;
471ef64b99roberto}
472ef64b99roberto
473ef64b99roberto
474ef64b99roberto/*
475ef64b99roberto * doaddvlist - add variable(s) to the variable list
476ef64b99roberto */
477ef64b99robertostatic void
478ef64b99robertodoaddvlist(
479ef64b99roberto	struct varlist *vlist,
480d54cfbdroberto	const char *vars
481ef64b99roberto	)
482ef64b99roberto{
483047f369cy	struct varlist *vl;
4848518518delphij	size_t len;
485ef64b99roberto	char *name;
486ef64b99roberto	char *value;
487ef64b99roberto
488ef64b99roberto	len = strlen(vars);
489ef64b99roberto	while (nextvar(&len, &vars, &name, &value)) {
49075715b3delphij		INSIST(name && value);
491ef64b99roberto		vl = findlistvar(vlist, name);
492047f369cy		if (NULL == vl) {
493837c91fcy			xprintf(stderr, "Variable list full\n");
494ef64b99roberto			return;
495ef64b99roberto		}
496ef64b99roberto
497047f369cy		if (NULL == vl->name) {
498d54cfbdroberto			vl->name = estrdup(name);
499047f369cy		} else if (vl->value != NULL) {
500ef64b99roberto			free(vl->value);
501047f369cy			vl->value = NULL;
502ef64b99roberto		}
503ef64b99roberto
504047f369cy		if (value != NULL)
505d54cfbdroberto			vl->value = estrdup(value);
506ef64b99roberto	}
507ef64b99roberto}
508ef64b99roberto
509ef64b99roberto
510ef64b99roberto/*
511ef64b99roberto * dormvlist - remove variable(s) from the variable list
512ef64b99roberto */
513ef64b99robertostatic void
514ef64b99robertodormvlist(
515ef64b99roberto	struct varlist *vlist,
516d54cfbdroberto	const char *vars
517ef64b99roberto	)
518ef64b99roberto{
519047f369cy	struct varlist *vl;
5208518518delphij	size_t len;
521ef64b99roberto	char *name;
522ef64b99roberto	char *value;
523ef64b99roberto
524ef64b99roberto	len = strlen(vars);
525ef64b99roberto	while (nextvar(&len, &vars, &name, &value)) {
52675715b3delphij		INSIST(name && value);
527ef64b99roberto		vl = findlistvar(vlist, name);
528ef64b99roberto		if (vl == 0 || vl->name == 0) {
529837c91fcy			(void) xprintf(stderr, "Variable `%s' not found\n",
530ef64b99roberto				       name);
531ef64b99roberto		} else {
532bdc155dcy			free((void *)(intptr_t)vl->name);
533ef64b99roberto			if (vl->value != 0)
534ef64b99roberto			    free(vl->value);
535d54cfbdroberto			for ( ; (vl+1) < (g_varlist + MAXLIST)
536ef64b99roberto				      && (vl+1)->name != 0; vl++) {
537ef64b99roberto				vl->name = (vl+1)->name;
538ef64b99roberto				vl->value = (vl+1)->value;
539ef64b99roberto			}
540ef64b99roberto			vl->name = vl->value = 0;
541ef64b99roberto		}
542ef64b99roberto	}
543ef64b99roberto}
544ef64b99roberto
545ef64b99roberto
546ef64b99roberto/*
547ef64b99roberto * doclearvlist - clear a variable list
548ef64b99roberto */
549ef64b99robertostatic void
550ef64b99robertodoclearvlist(
551ef64b99roberto	struct varlist *vlist
552ef64b99roberto	)
553ef64b99roberto{
554ef64b99roberto	register struct varlist *vl;
555ef64b99roberto
556ef64b99roberto	for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
557bdc155dcy		free((void *)(intptr_t)vl->name);
558ef64b99roberto		vl->name = 0;
559ef64b99roberto		if (vl->value != 0) {
560ef64b99roberto			free(vl->value);
561ef64b99roberto			vl->value = 0;
562ef64b99roberto		}
563ef64b99roberto	}
564ef64b99roberto}
565ef64b99roberto
566ef64b99roberto
567ef64b99roberto/*
568ef64b99roberto * makequerydata - form a data buffer to be included with a query
569ef64b99roberto */
570ef64b99robertostatic void
571ef64b99robertomakequerydata(
572ef64b99roberto	struct varlist *vlist,
5738518518delphij	size_t *datalen,
574ef64b99roberto	char *data
575ef64b99roberto	)
576ef64b99roberto{
577ef64b99roberto	register struct varlist *vl;
578ef64b99roberto	register char *cp, *cpend;
5798518518delphij	register size_t namelen, valuelen;
5808518518delphij	register size_t totallen;
581ef64b99roberto
582ef64b99roberto	cp = data;
583ef64b99roberto	cpend = data + *datalen;
584ef64b99roberto
585ef64b99roberto	for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
586ef64b99roberto		namelen = strlen(vl->name);
587ef64b99roberto		if (vl->value == 0)
588ef64b99roberto			valuelen = 0;
589ef64b99roberto		else
590ef64b99roberto			valuelen = strlen(vl->value);
591ef64b99roberto		totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
592047f369cy		if (cp + totallen > cpend) {
593837c91fcy		    xprintf(stderr,
594047f369cy			    "***Ignoring variables starting with `%s'\n",
595047f369cy			    vl->name);
596047f369cy		    break;
597047f369cy		}
598ef64b99roberto
599ef64b99roberto		if (cp != data)
600ef64b99roberto			*cp++ = ',';
601047f369cy		memcpy(cp, vl->name, (size_t)namelen);
602ef64b99roberto		cp += namelen;
603ef64b99roberto		if (valuelen != 0) {
604ef64b99roberto			*cp++ = '=';
605047f369cy			memcpy(cp, vl->value, (size_t)valuelen);
606ef64b99roberto			cp += valuelen;
607ef64b99roberto		}
608ef64b99roberto	}
6098518518delphij	*datalen = (size_t)(cp - data);
610ef64b99roberto}
611ef64b99roberto
612ef64b99roberto
613ef64b99roberto/*
614ef64b99roberto * doquerylist - send a message including variables in a list
615ef64b99roberto */
616ef64b99robertostatic int
617ef64b99robertodoquerylist(
618ef64b99roberto	struct varlist *vlist,
619ef64b99roberto	int op,
620d54cfbdroberto	associd_t associd,
621ef64b99roberto	int auth,
622ef64b99roberto	u_short *rstatus,
6238518518delphij	size_t *dsize,
624d54cfbdroberto	const char **datap
625ef64b99roberto	)
626ef64b99roberto{
627ef64b99roberto	char data[CTL_MAX_DATA_LEN];
6288518518delphij	size_t datalen;
629ef64b99roberto
630ef64b99roberto	datalen = sizeof(data);
631ef64b99roberto	makequerydata(vlist, &datalen, data);
632ef64b99roberto
633047f369cy	return doquery(op, associd, auth, datalen, data, rstatus, dsize,
634047f369cy		       datap);
635ef64b99roberto}
636ef64b99roberto
637ef64b99roberto
638ef64b99roberto/*
639ef64b99roberto * doprintvlist - print the variables on a list
640ef64b99roberto */
641ef64b99robertostatic void
642ef64b99robertodoprintvlist(
643ef64b99roberto	struct varlist *vlist,
644ef64b99roberto	FILE *fp
645ef64b99roberto	)
646ef64b99roberto{
647047f369cy	size_t n;
648ef64b99roberto
649047f369cy	if (NULL == vlist->name) {
650837c91fcy		xprintf(fp, "No variables on list\n");
651047f369cy		return;
652047f369cy	}
653047f369cy	for (n = 0; n < MAXLIST && vlist[n].name != NULL; n++) {
654047f369cy		if (NULL == vlist[n].value)
655837c91fcy			xprintf(fp, "%s\n", vlist[n].name);
656047f369cy		else
657837c91fcy			xprintf(fp, "%s=%s\n", vlist[n].name,
658047f369cy				vlist[n].value);
659ef64b99roberto	}
660ef64b99roberto}
661ef64b99roberto
662ef64b99roberto/*
663ef64b99roberto * addvars - add variables to the variable list
664ef64b99roberto */
665ef64b99roberto/*ARGSUSED*/
666ef64b99robertostatic void
667ef64b99robertoaddvars(
668ef64b99roberto	struct parse *pcmd,
669ef64b99roberto	FILE *fp
670ef64b99roberto	)
671ef64b99roberto{
672d54cfbdroberto	doaddvlist(g_varlist, pcmd->argval[0].string);
673ef64b99roberto}
674ef64b99roberto
675ef64b99roberto
676ef64b99roberto/*
677ef64b99roberto * rmvars - remove variables from the variable list
678ef64b99roberto */
679ef64b99roberto/*ARGSUSED*/
680ef64b99robertostatic void
681ef64b99robertormvars(
682ef64b99roberto	struct parse *pcmd,
683ef64b99roberto	FILE *fp
684ef64b99roberto	)
685ef64b99roberto{
686d54cfbdroberto	dormvlist(g_varlist, pcmd->argval[0].string);
687ef64b99roberto}
688ef64b99roberto
689ef64b99roberto
690ef64b99roberto/*
691ef64b99roberto * clearvars - clear the variable list
692ef64b99roberto */
693ef64b99roberto/*ARGSUSED*/
694ef64b99robertostatic void
695ef64b99robertoclearvars(
696ef64b99roberto	struct parse *pcmd,
697ef64b99roberto	FILE *fp
698ef64b99roberto	)
699ef64b99roberto{
700d54cfbdroberto	doclearvlist(g_varlist);
701ef64b99roberto}
702ef64b99roberto
703ef64b99roberto
704ef64b99roberto/*
705ef64b99roberto * showvars - show variables on the variable list
706ef64b99roberto */
707ef64b99roberto/*ARGSUSED*/
708ef64b99robertostatic void
709ef64b99robertoshowvars(
710ef64b99roberto	struct parse *pcmd,
711ef64b99roberto	FILE *fp
712ef64b99roberto	)
713ef64b99roberto{
714d54cfbdroberto	doprintvlist(g_varlist, fp);
715ef64b99roberto}
716ef64b99roberto
717ef64b99roberto
718ef64b99roberto/*
719ef64b99roberto * dolist - send a request with the given list of variables
720ef64b99roberto */
721ef64b99robertostatic int
722ef64b99robertodolist(
723ef64b99roberto	struct varlist *vlist,
724d54cfbdroberto	associd_t associd,
725ef64b99roberto	int op,
726ef64b99roberto	int type,
727ef64b99roberto	FILE *fp
728ef64b99roberto	)
729ef64b99roberto{
730d54cfbdroberto	const char *datap;
731ef64b99roberto	int res;
7328518518delphij	size_t dsize;
733ef64b99roberto	u_short rstatus;
734d54cfbdroberto	int quiet;
735d54cfbdroberto
736d54cfbdroberto	/*
737d54cfbdroberto	 * if we're asking for specific variables don't include the
738d54cfbdroberto	 * status header line in the output.
739d54cfbdroberto	 */
740d54cfbdroberto	if (old_rv)
741d54cfbdroberto		quiet = 0;
742d54cfbdroberto	else
743d54cfbdroberto		quiet = (vlist->name != NULL);
744ef64b99roberto
745ef64b99roberto	res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
746ef64b99roberto
747ef64b99roberto	if (res != 0)
748ef64b99roberto		return 0;
749ef64b99roberto
7507a6072eroberto	if (numhosts > 1)
751837c91fcy		xprintf(fp, "server=%s ", currenthost);
752ef64b99roberto	if (dsize == 0) {
753ef64b99roberto		if (associd == 0)
754837c91fcy			xprintf(fp, "No system%s variables returned\n",
755047f369cy				(type == TYPE_CLOCK) ? " clock" : "");
756ef64b99roberto		else
757837c91fcy			xprintf(fp,
758047f369cy				"No information returned for%s association %u\n",
759047f369cy				(type == TYPE_CLOCK) ? " clock" : "",
760047f369cy				associd);
761ef64b99roberto		return 1;
762ef64b99roberto	}
763ef64b99roberto
764d54cfbdroberto	if (!quiet)
765837c91fcy		xprintf(fp, "associd=%u ", associd);
766d54cfbdroberto	printvars(dsize, datap, (int)rstatus, type, quiet, fp);
767ef64b99roberto	return 1;
768ef64b99roberto}
769ef64b99roberto
770ef64b99roberto
771ef64b99roberto/*
772ef64b99roberto * readlist - send a read variables request with the variables on the list
773ef64b99roberto */
774ef64b99robertostatic void
775ef64b99robertoreadlist(
776ef64b99roberto	struct parse *pcmd,
777ef64b99roberto	FILE *fp
778ef64b99roberto	)
779ef64b99roberto{
780d54cfbdroberto	associd_t	associd;
781d54cfbdroberto	int		type;
782ef64b99roberto
783ef64b99roberto	if (pcmd->nargs == 0) {
784ef64b99roberto		associd = 0;
785ef64b99roberto	} else {
786ef64b99roberto	  /* HMS: I think we want the u_int32 target here, not the u_long */
787ef64b99roberto		if (pcmd->argval[0].uval == 0)
788ef64b99roberto			associd = 0;
789ef64b99roberto		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
790ef64b99roberto			return;
791ef64b99roberto	}
792ef64b99roberto
793d54cfbdroberto	type = (0 == associd)
794d54cfbdroberto		   ? TYPE_SYS
795d54cfbdroberto		   : TYPE_PEER;
796d54cfbdroberto	dolist(g_varlist, associd, CTL_OP_READVAR, type, fp);
797ef64b99roberto}
798ef64b99roberto
799ef64b99roberto
800ef64b99roberto/*
801ef64b99roberto * writelist - send a write variables request with the variables on the list
802ef64b99roberto */
803ef64b99robertostatic void
804ef64b99robertowritelist(
805ef64b99roberto	struct parse *pcmd,
806ef64b99roberto	FILE *fp
807ef64b99roberto	)
808ef64b99roberto{
809d54cfbdroberto	const char *datap;
810ef64b99roberto	int res;
811d54cfbdroberto	associd_t associd;
8128518518delphij	size_t dsize;
813ef64b99roberto	u_short rstatus;
814ef64b99roberto
815ef64b99roberto	if (pcmd->nargs == 0) {
816ef64b99roberto		associd = 0;
817ef64b99roberto	} else {
818ef64b99roberto		/* HMS: Do we really want uval here? */
819ef64b99roberto		if (pcmd->argval[0].uval == 0)
820ef64b99roberto			associd = 0;
821ef64b99roberto		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
822ef64b99roberto			return;
823ef64b99roberto	}
824ef64b99roberto
825d54cfbdroberto	res = doquerylist(g_varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
826ef64b99roberto			  &dsize, &datap);
827ef64b99roberto
828ef64b99roberto	if (res != 0)
829ef64b99roberto		return;
830ef64b99roberto
8317a6072eroberto	if (numhosts > 1)
832837c91fcy		(void) xprintf(fp, "server=%s ", currenthost);
833ef64b99roberto	if (dsize == 0)
834837c91fcy		(void) xprintf(fp, "done! (no data returned)\n");
8357a6072eroberto	else {
836837c91fcy		(void) xprintf(fp,"associd=%u ", associd);
837ef64b99roberto		printvars(dsize, datap, (int)rstatus,
838d54cfbdroberto			  (associd != 0) ? TYPE_PEER : TYPE_SYS, 0, fp);
8397a6072eroberto	}
840ef64b99roberto	return;
841ef64b99roberto}
842ef64b99roberto
843ef64b99roberto
844ef64b99roberto/*
845ef64b99roberto * readvar - send a read variables request with the specified variables
846ef64b99roberto */
847ef64b99robertostatic void
848ef64b99robertoreadvar(
849ef64b99roberto	struct parse *pcmd,
850ef64b99roberto	FILE *fp
851ef64b99roberto	)
852ef64b99roberto{
853d54cfbdroberto	associd_t	associd;
8548518518delphij	size_t		tmpcount;
8558518518delphij	size_t		u;
856d54cfbdroberto	int		type;
857d54cfbdroberto	struct varlist	tmplist[MAXLIST];
858d54cfbdroberto
859ef64b99roberto
860ef64b99roberto	/* HMS: uval? */
861ef64b99roberto	if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
862ef64b99roberto		associd = 0;
863ef64b99roberto	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
864ef64b99roberto		return;
865ef64b99roberto
866047f369cy	ZERO(tmplist);
867047f369cy	if (pcmd->nargs > 1) {
868047f369cy		tmpcount = pcmd->nargs - 1;
869047f369cy		for (u = 0; u < tmpcount; u++)
870047f369cy			doaddvlist(tmplist, pcmd->argval[1 + u].string);
871047f369cy	}
872ef64b99roberto
873d54cfbdroberto	type = (0 == associd)
874d54cfbdroberto		   ? TYPE_SYS
875d54cfbdroberto		   : TYPE_PEER;
876d54cfbdroberto	dolist(tmplist, associd, CTL_OP_READVAR, type, fp);
877ef64b99roberto
878ef64b99roberto	doclearvlist(tmplist);
879ef64b99roberto}
880ef64b99roberto
881ef64b99roberto
882ef64b99roberto/*
883ef64b99roberto * writevar - send a write variables request with the specified variables
884ef64b99roberto */
885ef64b99robertostatic void
886ef64b99robertowritevar(
887ef64b99roberto	struct parse *pcmd,
888ef64b99roberto	FILE *fp
889ef64b99roberto	)
890ef64b99roberto{
891d54cfbdroberto	const char *datap;
892ef64b99roberto	int res;
893d54cfbdroberto	associd_t associd;
894d54cfbdroberto	int type;
8958518518delphij	size_t dsize;
896ef64b99roberto	u_short rstatus;
897ef64b99roberto	struct varlist tmplist[MAXLIST];
898ef64b99roberto
899ef64b99roberto	/* HMS: uval? */
900ef64b99roberto	if (pcmd->argval[0].uval == 0)
901ef64b99roberto		associd = 0;
902ef64b99roberto	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
903ef64b99roberto		return;
904ef64b99roberto
905047f369cy	ZERO(tmplist);
906ef64b99roberto	doaddvlist(tmplist, pcmd->argval[1].string);
907ef64b99roberto
908ef64b99roberto	res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
909ef64b99roberto			  &dsize, &datap);
910ef64b99roberto
911ef64b99roberto	doclearvlist(tmplist);
912ef64b99roberto
913ef64b99roberto	if (res != 0)
914ef64b99roberto		return;
915ef64b99roberto
9167a6072eroberto	if (numhosts > 1)
917837c91fcy		xprintf(fp, "server=%s ", currenthost);
918ef64b99roberto	if (dsize == 0)
919837c91fcy		xprintf(fp, "done! (no data returned)\n");
9207a6072eroberto	else {
921837c91fcy		xprintf(fp,"associd=%u ", associd);
922d54cfbdroberto		type = (0 == associd)
923d54cfbdroberto			   ? TYPE_SYS
924d54cfbdroberto			   : TYPE_PEER;
925d54cfbdroberto		printvars(dsize, datap, (int)rstatus, type, 0, fp);
9267a6072eroberto	}
927ef64b99roberto	return;
928ef64b99roberto}
929ef64b99roberto
930ef64b99roberto
931ef64b99roberto/*
932ef64b99roberto * clocklist - send a clock variables request with the variables on the list
933ef64b99roberto */
934ef64b99robertostatic void
935ef64b99robertoclocklist(
936ef64b99roberto	struct parse *pcmd,
937ef64b99roberto	FILE *fp
938ef64b99roberto	)
939ef64b99roberto{
940d54cfbdroberto	associd_t associd;
941ef64b99roberto
942ef64b99roberto	/* HMS: uval? */
943ef64b99roberto	if (pcmd->nargs == 0) {
944ef64b99roberto		associd = 0;
945ef64b99roberto	} else {
946ef64b99roberto		if (pcmd->argval[0].uval == 0)
947ef64b99roberto			associd = 0;
948ef64b99roberto		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
949ef64b99roberto			return;
950ef64b99roberto	}
951ef64b99roberto
952d54cfbdroberto	dolist(g_varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
953ef64b99roberto}
954ef64b99roberto
955ef64b99roberto
956ef64b99roberto/*
957ef64b99roberto * clockvar - send a clock variables request with the specified variables
958ef64b99roberto */
959ef64b99robertostatic void
960ef64b99robertoclockvar(
961ef64b99roberto	struct parse *pcmd,
962ef64b99roberto	FILE *fp
963ef64b99roberto	)
964ef64b99roberto{
965d54cfbdroberto	associd_t associd;
966ef64b99roberto	struct varlist tmplist[MAXLIST];
967ef64b99roberto
968ef64b99roberto	/* HMS: uval? */
969ef64b99roberto	if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
970ef64b99roberto		associd = 0;
971ef64b99roberto	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
972ef64b99roberto		return;
973ef64b99roberto
974047f369cy	ZERO(tmplist);
975ef64b99roberto	if (pcmd->nargs >= 2)
976ef64b99roberto		doaddvlist(tmplist, pcmd->argval[1].string);
977ef64b99roberto
978d54cfbdroberto	dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
979ef64b99roberto
980ef64b99roberto	doclearvlist(tmplist);
981ef64b99roberto}
982ef64b99roberto
983ef64b99roberto
984ef64b99roberto/*
985ef64b99roberto * findassidrange - verify a range of association ID's
986ef64b99roberto */
987ef64b99robertostatic int
988ef64b99robertofindassidrange(
989047f369cy	u_int32	assid1,
990047f369cy	u_int32	assid2,
991047f369cy	int *	from,
992047f369cy	int *	to,
993047f369cy	FILE *	fp
994ef64b99roberto	)
995ef64b99roberto{
996d54cfbdroberto	associd_t	assids[2];
997d54cfbdroberto	int		ind[COUNTOF(assids)];
998047f369cy	u_int		i;
999d54cfbdroberto	size_t		a;
1000ef64b99roberto
1001047f369cy
1002047f369cy	if (0 == numassoc)
1003047f369cy		dogetassoc(fp);
1004047f369cy
1005d54cfbdroberto	assids[0] = checkassocid(assid1);
1006d54cfbdroberto	if (0 == assids[0])
1007ef64b99roberto		return 0;
1008d54cfbdroberto	assids[1] = checkassocid(assid2);
1009d54cfbdroberto	if (0 == assids[1])
1010ef64b99roberto		return 0;
1011ef64b99roberto
1012d54cfbdroberto	for (a = 0; a < COUNTOF(assids); a++) {
1013d54cfbdroberto		ind[a] = -1;
1014d54cfbdroberto		for (i = 0; i < numassoc; i++)
1015d54cfbdroberto			if (assoc_cache[i].assid == assids[a])
1016d54cfbdroberto				ind[a] = i;
1017ef64b99roberto	}
1018d54cfbdroberto	for (a = 0; a < COUNTOF(assids); a++)
1019d54cfbdroberto		if (-1 == ind[a]) {
1020837c91fcy			xprintf(stderr,
1021d54cfbdroberto				"***Association ID %u not found in list\n",
1022d54cfbdroberto				assids[a]);
1023d54cfbdroberto			return 0;
1024d54cfbdroberto		}
1025ef64b99roberto
1026d54cfbdroberto	if (ind[0] < ind[1]) {
1027d54cfbdroberto		*from = ind[0];
1028d54cfbdroberto		*to = ind[1];
1029ef64b99roberto	} else {
1030d54cfbdroberto		*to = ind[0];
1031d54cfbdroberto		*from = ind[1];
1032ef64b99roberto	}
1033ef64b99roberto	return 1;
1034ef64b99roberto}
1035ef64b99roberto
1036ef64b99roberto
1037ef64b99roberto
1038ef64b99roberto/*
1039ef64b99roberto * mreadlist - send a read variables request for multiple associations
1040ef64b99roberto */
1041ef64b99robertostatic void
1042ef64b99robertomreadlist(
1043ef64b99roberto	struct parse *pcmd,
1044ef64b99roberto	FILE *fp
1045ef64b99roberto	)
1046ef64b99roberto{
1047ef64b99roberto	int i;
1048ef64b99roberto	int from;
1049ef64b99roberto	int to;
1050ef64b99roberto
1051ef64b99roberto	if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1052047f369cy			    &from, &to, fp))
1053ef64b99roberto		return;
1054ef64b99roberto
1055ef64b99roberto	for (i = from; i <= to; i++) {
1056ef64b99roberto		if (i != from)
1057837c91fcy			xprintf(fp, "\n");
1058047f369cy		if (!dolist(g_varlist, assoc_cache[i].assid,
1059047f369cy			    CTL_OP_READVAR, TYPE_PEER, fp))
1060ef64b99roberto			return;
1061ef64b99roberto	}
1062ef64b99roberto	return;
1063ef64b99roberto}
1064ef64b99roberto
1065ef64b99roberto
1066ef64b99roberto/*
1067ef64b99roberto * mreadvar - send a read variables request for multiple associations
1068ef64b99roberto */
1069ef64b99robertostatic void
1070ef64b99robertomreadvar(
1071ef64b99roberto	struct parse *pcmd,
1072ef64b99roberto	FILE *fp
1073ef64b99roberto	)
1074ef64b99roberto{
1075ef64b99roberto	int i;
1076ef64b99roberto	int from;
1077ef64b99roberto	int to;
1078ef64b99roberto	struct varlist tmplist[MAXLIST];
1079d54cfbdroberto	struct varlist *pvars;
1080ef64b99roberto
1081ef64b99roberto	if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1082047f369cy				&from, &to, fp))
1083ef64b99roberto		return;
1084ef64b99roberto
1085047f369cy	ZERO(tmplist);
1086d54cfbdroberto	if (pcmd->nargs >= 3) {
1087ef64b99roberto		doaddvlist(tmplist, pcmd->argval[2].string);
1088d54cfbdroberto		pvars = tmplist;
1089d54cfbdroberto	} else {
1090d54cfbdroberto		pvars = g_varlist;
1091d54cfbdroberto	}
1092ef64b99roberto
1093ef64b99roberto	for (i = from; i <= to; i++) {
1094047f369cy		if (!dolist(pvars, assoc_cache[i].assid, CTL_OP_READVAR,
1095047f369cy			    TYPE_PEER, fp))
1096ef64b99roberto			break;
1097ef64b99roberto	}
1098047f369cy
1099047f369cy	if (pvars == tmplist)
1100047f369cy		doclearvlist(tmplist);
1101047f369cy
1102ef64b99roberto	return;
1103ef64b99roberto}
1104ef64b99roberto
1105ef64b99roberto
1106ef64b99roberto/*
1107ef64b99roberto * dogetassoc - query the host for its list of associations
1108ef64b99roberto */
1109047f369cyint
1110ef64b99robertodogetassoc(
1111ef64b99roberto	FILE *fp
1112ef64b99roberto	)
1113ef64b99roberto{
1114d54cfbdroberto	const char *datap;
1115047f369cy	const u_short *pus;
1116ef64b99roberto	int res;
11178518518delphij	size_t dsize;
1118ef64b99roberto	u_short rstatus;
1119ef64b99roberto
1120ef64b99roberto	res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
11217a6072eroberto			  &dsize, &datap);
1122ef64b99roberto
1123ef64b99roberto	if (res != 0)
1124ef64b99roberto		return 0;
1125ef64b99roberto
1126ef64b99roberto	if (dsize == 0) {
11277a6072eroberto		if (numhosts > 1)
1128837c91fcy			xprintf(fp, "server=%s ", currenthost);
1129837c91fcy		xprintf(fp, "No association ID's returned\n");
1130ef64b99roberto		return 0;
1131ef64b99roberto	}
1132ef64b99roberto
1133ef64b99roberto	if (dsize & 0x3) {
11347a6072eroberto		if (numhosts > 1)
1135837c91fcy			xprintf(stderr, "server=%s ", currenthost);
1136837c91fcy		xprintf(stderr,
11378518518delphij			"***Server returned %zu octets, should be multiple of 4\n",
1138047f369cy			dsize);
1139ef64b99roberto		return 0;
1140ef64b99roberto	}
1141ef64b99roberto
1142ef64b99roberto	numassoc = 0;
1143047f369cy
1144ef64b99roberto	while (dsize > 0) {
1145047f369cy		if (numassoc >= assoc_cache_slots) {
1146047f369cy			grow_assoc_cache();
1147047f369cy		}
1148047f369cy		pus = (const void *)datap;
1149047f369cy		assoc_cache[numassoc].assid = ntohs(*pus);
1150047f369cy		datap += sizeof(*pus);
1151047f369cy		pus = (const void *)datap;
1152047f369cy		assoc_cache[numassoc].status = ntohs(*pus);
1153047f369cy		datap += sizeof(*pus);
1154047f369cy		dsize -= 2 * sizeof(*pus);
1155047f369cy		if (debug) {
1156837c91fcy			xprintf(stderr, "[%u] ",
1157047f369cy				assoc_cache[numassoc].assid);
1158047f369cy		}
1159047f369cy		numassoc++;
1160047f369cy	}
1161047f369cy	if (debug) {
1162837c91fcy		xprintf(stderr, "\n%d associations total\n", numassoc);
1163ef64b99roberto	}
1164ef64b99roberto	sortassoc();
1165ef64b99roberto	return 1;
1166ef64b99roberto}
1167ef64b99roberto
1168ef64b99roberto
1169ef64b99roberto/*
1170ef64b99roberto * printassoc - print the current list of associations
1171ef64b99roberto */
1172ef64b99robertostatic void
1173ef64b99robertoprintassoc(
1174ef64b99roberto	int showall,
1175ef64b99roberto	FILE *fp
1176ef64b99roberto	)
1177ef64b99roberto{
1178ef64b99roberto	register char *bp;
1179047f369cy	u_int i;
1180ef64b99roberto	u_char statval;
1181ef64b99roberto	int event;
1182ef64b99roberto	u_long event_count;
1183ef64b99roberto	const char *conf;
1184ef64b99roberto	const char *reach;
1185ef64b99roberto	const char *auth;
1186ef64b99roberto	const char *condition = "";
1187ef64b99roberto	const char *last_event;
1188ef64b99roberto	char buf[128];
1189ef64b99roberto
1190ef64b99roberto	if (numassoc == 0) {
1191837c91fcy		(void) xprintf(fp, "No association ID's in list\n");
1192ef64b99roberto		return;
1193ef64b99roberto	}
1194ef64b99roberto
1195ef64b99roberto	/*
1196ef64b99roberto	 * Output a header
1197ef64b99roberto	 */
1198837c91fcy	(void) xprintf(fp,
119975715b3delphij			   "ind assid status  conf reach auth condition  last_event cnt\n");
1200837c91fcy	(void) xprintf(fp,
1201ef64b99roberto			   "===========================================================\n");
1202ef64b99roberto	for (i = 0; i < numassoc; i++) {
1203118e757roberto		statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status);
1204ef64b99roberto		if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
1205ef64b99roberto			continue;
1206ef64b99roberto		event = CTL_PEER_EVENT(assoc_cache[i].status);
1207ef64b99roberto		event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
1208ef64b99roberto		if (statval & CTL_PST_CONFIG)
1209ef64b99roberto			conf = "yes";
1210ef64b99roberto		else
1211ef64b99roberto			conf = "no";
1212d54cfbdroberto		if (statval & CTL_PST_BCAST) {
1213d54cfbdroberto			reach = "none";
1214d54cfbdroberto			if (statval & CTL_PST_AUTHENABLE)
1215d54cfbdroberto				auth = "yes";
1216d54cfbdroberto			else
1217d54cfbdroberto				auth = "none";
1218d54cfbdroberto		} else {
1219d54cfbdroberto			if (statval & CTL_PST_REACH)
1220d54cfbdroberto				reach = "yes";
1221d54cfbdroberto			else
1222d54cfbdroberto				reach = "no";
1223ef64b99roberto			if (statval & CTL_PST_AUTHENABLE) {
1224ef64b99roberto				if (statval & CTL_PST_AUTHENTIC)
1225ef64b99roberto					auth = "ok ";
1226ef64b99roberto				else
1227ef64b99roberto					auth = "bad";
1228d54cfbdroberto			} else {
1229ef64b99roberto				auth = "none";
1230d54cfbdroberto			}
1231d54cfbdroberto		}
1232d54cfbdroberto		if (pktversion > NTP_OLDVERSION) {
1233d54cfbdroberto			switch (statval & 0x7) {
1234ef64b99roberto
1235d54cfbdroberto			case CTL_PST_SEL_REJECT:
1236d54cfbdroberto				condition = "reject";
1237d54cfbdroberto				break;
1238d54cfbdroberto
1239d54cfbdroberto			case CTL_PST_SEL_SANE:
1240d54cfbdroberto				condition = "falsetick";
1241d54cfbdroberto				break;
1242d54cfbdroberto
1243d54cfbdroberto			case CTL_PST_SEL_CORRECT:
1244d54cfbdroberto				condition = "excess";
1245d54cfbdroberto				break;
1246d54cfbdroberto
1247d54cfbdroberto			case CTL_PST_SEL_SELCAND:
1248aae1e7dglebius				condition = "outlier";
1249d54cfbdroberto				break;
1250d54cfbdroberto
1251d54cfbdroberto			case CTL_PST_SEL_SYNCCAND:
1252d54cfbdroberto				condition = "candidate";
1253d54cfbdroberto				break;
1254d54cfbdroberto
1255d54cfbdroberto			case CTL_PST_SEL_EXCESS:
1256d54cfbdroberto				condition = "backup";
1257d54cfbdroberto				break;
1258d54cfbdroberto
1259d54cfbdroberto			case CTL_PST_SEL_SYSPEER:
1260d54cfbdroberto				condition = "sys.peer";
1261d54cfbdroberto				break;
1262d54cfbdroberto
1263d54cfbdroberto			case CTL_PST_SEL_PPS:
1264d54cfbdroberto				condition = "pps.peer";
1265d54cfbdroberto				break;
1266d54cfbdroberto			}
1267d54cfbdroberto		} else {
1268d54cfbdroberto			switch (statval & 0x3) {
1269d54cfbdroberto
1270d54cfbdroberto			case OLD_CTL_PST_SEL_REJECT:
1271d54cfbdroberto				if (!(statval & OLD_CTL_PST_SANE))
1272ef64b99roberto					condition = "insane";
1273d54cfbdroberto				else if (!(statval & OLD_CTL_PST_DISP))
1274ef64b99roberto					condition = "hi_disp";
1275d54cfbdroberto				else
1276ef64b99roberto					condition = "";
1277d54cfbdroberto				break;
1278ef64b99roberto
1279d54cfbdroberto			case OLD_CTL_PST_SEL_SELCAND:
1280d54cfbdroberto				condition = "sel_cand";
1281d54cfbdroberto				break;
1282ef64b99roberto
1283d54cfbdroberto			case OLD_CTL_PST_SEL_SYNCCAND:
1284d54cfbdroberto				condition = "sync_cand";
1285d54cfbdroberto				break;
1286d54cfbdroberto
1287d54cfbdroberto			case OLD_CTL_PST_SEL_SYSPEER:
1288d54cfbdroberto				condition = "sys_peer";
1289d54cfbdroberto				break;
1290d54cfbdroberto			}
1291d54cfbdroberto		}
1292ef64b99roberto		switch (PEER_EVENT|event) {
1293d54cfbdroberto
1294d54cfbdroberto		case PEVNT_MOBIL:
1295d54cfbdroberto			last_event = "mobilize";
1296ef64b99roberto			break;
1297d54cfbdroberto
1298d54cfbdroberto		case PEVNT_DEMOBIL:
1299d54cfbdroberto			last_event = "demobilize";
1300ef64b99roberto			break;
1301d54cfbdroberto
1302d54cfbdroberto		case PEVNT_REACH:
1303ef64b99roberto			last_event = "reachable";
1304ef64b99roberto			break;
1305d54cfbdroberto
1306d54cfbdroberto		case PEVNT_UNREACH:
1307d54cfbdroberto			last_event = "unreachable";
1308ef64b99roberto			break;
1309d54cfbdroberto
1310d54cfbdroberto		case PEVNT_RESTART:
1311d54cfbdroberto			last_event = "restart";
1312ef64b99roberto			break;
1313d54cfbdroberto
1314d54cfbdroberto		case PEVNT_REPLY:
1315d54cfbdroberto			last_event = "no_reply";
1316d54cfbdroberto			break;
1317d54cfbdroberto
1318d54cfbdroberto		case PEVNT_RATE:
1319d54cfbdroberto			last_event = "rate_exceeded";
1320d54cfbdroberto			break;
1321d54cfbdroberto
1322d54cfbdroberto		case PEVNT_DENY:
1323d54cfbdroberto			last_event = "access_denied";
1324d54cfbdroberto			break;
1325d54cfbdroberto
1326d54cfbdroberto		case PEVNT_ARMED:
1327d54cfbdroberto			last_event = "leap_armed";
1328d54cfbdroberto			break;
1329d54cfbdroberto
1330d54cfbdroberto		case PEVNT_NEWPEER:
1331d54cfbdroberto			last_event = "sys_peer";
1332d54cfbdroberto			break;
1333d54cfbdroberto
1334d54cfbdroberto		case PEVNT_CLOCK:
1335d54cfbdroberto			last_event = "clock_alarm";
1336d54cfbdroberto			break;
1337d54cfbdroberto
1338d54cfbdroberto		default:
1339ef64b99roberto			last_event = "";
1340ef64b99roberto			break;
1341ef64b99roberto		}
1342d54cfbdroberto		snprintf(buf, sizeof(buf),
1343047f369cy			 "%3d %5u  %04x   %3.3s  %4s  %4.4s %9.9s %11s %2lu",
1344d54cfbdroberto			 i + 1, assoc_cache[i].assid,
1345d54cfbdroberto			 assoc_cache[i].status, conf, reach, auth,
1346047f369cy			 condition, last_event, event_count);
1347d54cfbdroberto		bp = buf + strlen(buf);
1348d54cfbdroberto		while (bp > buf && ' ' == bp[-1])
1349d54cfbdroberto			--bp;
1350d54cfbdroberto		bp[0] = '\0';
1351837c91fcy		xprintf(fp, "%s\n", buf);
1352ef64b99roberto	}
1353ef64b99roberto}
1354ef64b99roberto
1355ef64b99roberto
1356ef64b99roberto/*
1357ef64b99roberto * associations - get, record and print a list of associations
1358ef64b99roberto */
1359ef64b99roberto/*ARGSUSED*/
1360ef64b99robertostatic void
1361ef64b99robertoassociations(
1362ef64b99roberto	struct parse *pcmd,
1363ef64b99roberto	FILE *fp
1364ef64b99roberto	)
1365ef64b99roberto{
1366ef64b99roberto	if (dogetassoc(fp))
1367ef64b99roberto		printassoc(0, fp);
1368ef64b99roberto}
1369ef64b99roberto
1370ef64b99roberto
1371ef64b99roberto/*
1372ef64b99roberto * lassociations - get, record and print a long list of associations
1373ef64b99roberto */
1374ef64b99roberto/*ARGSUSED*/
1375ef64b99robertostatic void
1376ef64b99robertolassociations(
1377ef64b99roberto	struct parse *pcmd,
1378ef64b99roberto	FILE *fp
1379ef64b99roberto	)
1380ef64b99roberto{
1381ef64b99roberto	if (dogetassoc(fp))
1382ef64b99roberto		printassoc(1, fp);
1383ef64b99roberto}
1384ef64b99roberto
1385ef64b99roberto
1386ef64b99roberto/*
1387ef64b99roberto * passociations - print the association list
1388ef64b99roberto */
1389ef64b99roberto/*ARGSUSED*/
1390ef64b99robertostatic void
1391ef64b99robertopassociations(
1392ef64b99roberto	struct parse *pcmd,
1393ef64b99roberto	FILE *fp
1394ef64b99roberto	)
1395ef64b99roberto{
1396ef64b99roberto	printassoc(0, fp);
1397ef64b99roberto}
1398ef64b99roberto
1399ef64b99roberto
1400ef64b99roberto/*
1401ef64b99roberto * lpassociations - print the long association list
1402ef64b99roberto */
1403ef64b99roberto/*ARGSUSED*/
1404ef64b99robertostatic void
1405ef64b99robertolpassociations(
1406ef64b99roberto	struct parse *pcmd,
1407ef64b99roberto	FILE *fp
1408ef64b99roberto	)
1409ef64b99roberto{
1410ef64b99roberto	printassoc(1, fp);
1411ef64b99roberto}
1412ef64b99roberto
1413ef64b99roberto
1414d54cfbdroberto/*
1415d54cfbdroberto *  saveconfig - dump ntp server configuration to server file
1416d54cfbdroberto */
1417d54cfbdrobertostatic void
1418d54cfbdrobertosaveconfig(
1419d54cfbdroberto	struct parse *pcmd,
1420d54cfbdroberto	FILE *fp
1421d54cfbdroberto	)
1422d54cfbdroberto{
1423d54cfbdroberto	const char *datap;
1424d54cfbdroberto	int res;
14258518518delphij	size_t dsize;
1426d54cfbdroberto	u_short rstatus;
1427d54cfbdroberto
1428d54cfbdroberto	if (0 == pcmd->nargs)
1429d54cfbdroberto		return;
1430837c91fcy
1431d54cfbdroberto	res = doquery(CTL_OP_SAVECONFIG, 0, 1,
1432d54cfbdroberto		      strlen(pcmd->argval[0].string),
1433d54cfbdroberto		      pcmd->argval[0].string, &rstatus, &dsize,
1434d54cfbdroberto		      &datap);
1435d54cfbdroberto
1436d54cfbdroberto	if (res != 0)
1437d54cfbdroberto		return;
1438d54cfbdroberto
1439d54cfbdroberto	if (0 == dsize)
1440837c91fcy		xprintf(fp, "(no response message, curiously)");
1441d54cfbdroberto	else
1442837c91fcy		xprintf(fp, "%.*s", (int)dsize, datap); /* cast is wobbly */
1443d54cfbdroberto}
1444d54cfbdroberto
1445d54cfbdroberto
1446ef64b99roberto#ifdef	UNUSED
1447ef64b99roberto/*
1448ef64b99roberto * radiostatus - print the radio status returned by the server
1449ef64b99roberto */
1450ef64b99roberto/*ARGSUSED*/
1451ef64b99robertostatic void
1452ef64b99robertoradiostatus(
1453ef64b99roberto	struct parse *pcmd,
1454ef64b99roberto	FILE *fp
1455ef64b99roberto	)
1456ef64b99roberto{
1457ef64b99roberto	char *datap;
1458ef64b99roberto	int res;
1459ef64b99roberto	int dsize;
1460ef64b99roberto	u_short rstatus;
1461ef64b99roberto
1462ef64b99roberto	res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
1463ef64b99roberto			  &dsize, &datap);
1464ef64b99roberto
1465ef64b99roberto	if (res != 0)
1466ef64b99roberto		return;
1467ef64b99roberto
14687a6072eroberto	if (numhosts > 1)
1469837c91fcy		(void) xprintf(fp, "server=%s ", currenthost);
1470ef64b99roberto	if (dsize == 0) {
1471837c91fcy		(void) xprintf(fp, "No radio status string returned\n");
1472ef64b99roberto		return;
1473ef64b99roberto	}
1474ef64b99roberto
1475ef64b99roberto	asciize(dsize, datap, fp);
1476ef64b99roberto}
1477ef64b99roberto#endif	/* UNUSED */
1478ef64b99roberto
1479ef64b99roberto/*
1480ef64b99roberto * when - print how long its been since his last packet arrived
1481ef64b99roberto */
1482ef64b99robertostatic long
1483ef64b99robertowhen(
1484ef64b99roberto	l_fp *ts,
1485ef64b99roberto	l_fp *rec,
1486ef64b99roberto	l_fp *reftime
1487ef64b99roberto	)
1488ef64b99roberto{
1489ef64b99roberto	l_fp *lasttime;
1490ef64b99roberto
1491ef64b99roberto	if (rec->l_ui != 0)
1492ef64b99roberto		lasttime = rec;
1493ef64b99roberto	else if (reftime->l_ui != 0)
1494ef64b99roberto		lasttime = reftime;
1495ef64b99roberto	else
1496ef64b99roberto		return 0;
1497ef64b99roberto
149830f4731delphij	if (ts->l_ui < lasttime->l_ui)
149930f4731delphij		return -1;
1500ef64b99roberto	return (ts->l_ui - lasttime->l_ui);
1501ef64b99roberto}
1502ef64b99roberto
1503ef64b99roberto
1504ef64b99roberto/*
1505ef64b99roberto * Pretty-print an interval into the given buffer, in a human-friendly format.
1506ef64b99roberto */
1507ef64b99robertostatic char *
1508ef64b99robertoprettyinterval(
1509ef64b99roberto	char *buf,
1510d54cfbdroberto	size_t cb,
1511ef64b99roberto	long diff
1512ef64b99roberto	)
1513ef64b99roberto{
1514ef64b99roberto	if (diff <= 0) {
1515ef64b99roberto		buf[0] = '-';
1516ef64b99roberto		buf[1] = 0;
1517ef64b99roberto		return buf;
1518ef64b99roberto	}
1519ef64b99roberto
1520ef64b99roberto	if (diff <= 2048) {
152175715b3delphij		snprintf(buf, cb, "%u", (unsigned int)diff);
1522ef64b99roberto		return buf;
1523ef64b99roberto	}
1524ef64b99roberto
1525ef64b99roberto	diff = (diff + 29) / 60;
1526ef64b99roberto	if (diff <= 300) {
152775715b3delphij		snprintf(buf, cb, "%um", (unsigned int)diff);
1528ef64b99roberto		return buf;
1529ef64b99roberto	}
1530ef64b99roberto
1531ef64b99roberto	diff = (diff + 29) / 60;
1532ef64b99roberto	if (diff <= 96) {
153375715b3delphij		snprintf(buf, cb, "%uh", (unsigned int)diff);
1534ef64b99roberto		return buf;
1535ef64b99roberto	}
1536ef64b99roberto
1537ef64b99roberto	diff = (diff + 11) / 24;
153830f4731delphij	if (diff <= 999) {
153975715b3delphij		snprintf(buf, cb, "%ud", (unsigned int)diff);
154030f4731delphij		return buf;
154130f4731delphij	}
154230f4731delphij
154330f4731delphij	/* years are only approximated... */
154430f4731delphij	diff = (long)floor(diff / 365.25 + 0.5);
154575715b3delphij	if (diff <= 999) {
154675715b3delphij		snprintf(buf, cb, "%uy", (unsigned int)diff);
154775715b3delphij		return buf;
154875715b3delphij	}
154975715b3delphij	/* Ok, this amounts to infinity... */
155075715b3delphij	strlcpy(buf, "INF", cb);
1551ef64b99roberto	return buf;
1552ef64b99roberto}
1553ef64b99roberto
1554118e757robertostatic char
1555118e757robertodecodeaddrtype(
1556d54cfbdroberto	sockaddr_u *sock
1557118e757roberto	)
1558118e757roberto{
1559118e757roberto	char ch = '-';
1560118e757roberto	u_int32 dummy;
1561118e757roberto
1562d54cfbdroberto	switch(AF(sock)) {
1563118e757roberto	case AF_INET:
1564d54cfbdroberto		dummy = SRCADR(sock);
1565118e757roberto		ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' :
1566118e757roberto			((dummy&0x000000ff)==0x000000ff) ? 'b' :
1567118e757roberto			((dummy&0xffffffff)==0x7f000001) ? 'l' :
1568118e757roberto			((dummy&0xffffffe0)==0x00000000) ? '-' :
1569118e757roberto			'u');
1570118e757roberto		break;
1571118e757roberto	case AF_INET6:
1572d54cfbdroberto		if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock)))
1573118e757roberto			ch = 'm';
1574118e757roberto		else
1575118e757roberto			ch = 'u';
1576118e757roberto		break;
1577118e757roberto	default:
1578118e757roberto		ch = '-';
1579118e757roberto		break;
1580118e757roberto	}
1581118e757roberto	return ch;
1582118e757roberto}
1583ef64b99roberto
1584ef64b99roberto/*
1585ef64b99roberto * A list of variables required by the peers command
1586ef64b99roberto */
1587ef64b99robertostruct varlist opeervarlist[] = {
1588047f369cy	{ "srcadr",	0 },	/* 0 */
1589047f369cy	{ "dstadr",	0 },	/* 1 */
1590047f369cy	{ "stratum",	0 },	/* 2 */
1591047f369cy	{ "hpoll",	0 },	/* 3 */
1592047f369cy	{ "ppoll",	0 },	/* 4 */
1593047f369cy	{ "reach",	0 },	/* 5 */
1594047f369cy	{ "delay",	0 },	/* 6 */
1595047f369cy	{ "offset",	0 },	/* 7 */
1596047f369cy	{ "jitter",	0 },	/* 8 */
1597047f369cy	{ "dispersion", 0 },	/* 9 */
1598047f369cy	{ "rec",	0 },	/* 10 */
1599047f369cy	{ "reftime",	0 },	/* 11 */
1600047f369cy	{ "srcport",	0 },	/* 12 */
1601047f369cy	{ "hmode",	0 },	/* 13 */
1602ef64b99roberto	{ 0,		0 }
1603ef64b99roberto};
1604ef64b99roberto
1605ef64b99robertostruct varlist peervarlist[] = {
1606047f369cy	{ "srcadr",	0 },	/* 0 */
1607047f369cy	{ "refid",	0 },	/* 1 */
1608047f369cy	{ "stratum",	0 },	/* 2 */
1609047f369cy	{ "hpoll",	0 },	/* 3 */
1610047f369cy	{ "ppoll",	0 },	/* 4 */
1611047f369cy	{ "reach",	0 },	/* 5 */
1612047f369cy	{ "delay",	0 },	/* 6 */
1613047f369cy	{ "offset",	0 },	/* 7 */
1614047f369cy	{ "jitter",	0 },	/* 8 */
1615047f369cy	{ "dispersion", 0 },	/* 9 */
1616047f369cy	{ "rec",	0 },	/* 10 */
1617047f369cy	{ "reftime",	0 },	/* 11 */
1618047f369cy	{ "srcport",	0 },	/* 12 */
1619047f369cy	{ "hmode",	0 },	/* 13 */
1620047f369cy	{ "srchost",	0 },	/* 14 */
1621ef64b99roberto	{ 0,		0 }
1622ef64b99roberto};
1623ef64b99roberto
16245ef283fcystruct varlist apeervarlist[] = {
16255ef283fcy	{ "srcadr",	0 },	/* 0 */
16265ef283fcy	{ "refid",	0 },	/* 1 */
16275ef283fcy	{ "assid",	0 },	/* 2 */
16285ef283fcy	{ "stratum",	0 },	/* 3 */
16295ef283fcy	{ "hpoll",	0 },	/* 4 */
16305ef283fcy	{ "ppoll",	0 },	/* 5 */
16315ef283fcy	{ "reach",	0 },	/* 6 */
16325ef283fcy	{ "delay",	0 },	/* 7 */
16335ef283fcy	{ "offset",	0 },	/* 8 */
16345ef283fcy	{ "jitter",	0 },	/* 9 */
16355ef283fcy	{ "dispersion", 0 },	/* 10 */
16365ef283fcy	{ "rec",	0 },	/* 11 */
16375ef283fcy	{ "reftime",	0 },	/* 12 */
16385ef283fcy	{ "srcport",	0 },	/* 13 */
16395ef283fcy	{ "hmode",	0 },	/* 14 */
16405ef283fcy	{ "srchost",	0 },	/* 15 */
16415ef283fcy	{ 0,		0 }
16425ef283fcy};
16435ef283fcy
1644ef64b99roberto
1645ef64b99roberto/*
1646ef64b99roberto * Decode an incoming data buffer and print a line in the peer list
1647ef64b99roberto */
1648ef64b99robertostatic int
1649ef64b99robertodoprintpeers(
1650ef64b99roberto	struct varlist *pvl,
1651ef64b99roberto	int associd,
1652ef64b99roberto	int rstatus,
16538518518delphij	size_t datalen,
1654d54cfbdroberto	const char *data,
1655118e757roberto	FILE *fp,
1656118e757roberto	int af
1657ef64b99roberto	)
1658ef64b99roberto{
1659ef64b99roberto	char *name;
1660118e757roberto	char *value = NULL;
1661ef64b99roberto	int c;
16628518518delphij	size_t len;
1663047f369cy	int have_srchost;
1664047f369cy	int have_dstadr;
1665047f369cy	int have_da_rid;
1666047f369cy	int have_jitter;
1667d54cfbdroberto	sockaddr_u srcadr;
1668d54cfbdroberto	sockaddr_u dstadr;
1669047f369cy	sockaddr_u dum_store;
1670d54cfbdroberto	sockaddr_u refidadr;
1671047f369cy	long hmode = 0;
1672118e757roberto	u_long srcport = 0;
1673047f369cy	u_int32 u32;
1674047f369cy	const char *dstadr_refid = "0.0.0.0";
1675047f369cy	const char *serverlocal;
1676d54cfbdroberto	size_t drlen;
1677118e757roberto	u_long stratum = 0;
1678118e757roberto	long ppoll = 0;
1679118e757roberto	long hpoll = 0;
1680118e757roberto	u_long reach = 0;
1681ef64b99roberto	l_fp estoffset;
1682ef64b99roberto	l_fp estdelay;
1683ef64b99roberto	l_fp estjitter;
1684ef64b99roberto	l_fp estdisp;
1685ef64b99roberto	l_fp reftime;
1686ef64b99roberto	l_fp rec;
1687ef64b99roberto	l_fp ts;
1688b5b40f9roberto	u_long poll_sec;
168975715b3delphij	u_long flash = 0;
1690ef64b99roberto	char type = '?';
1691118e757roberto	char clock_name[LENHOSTNAME];
169275715b3delphij	char whenbuf[12], pollbuf[12];
169375715b3delphij	/* [Bug 3482] formally whenbuf & pollbuf should be able to hold
169475715b3delphij	 * a full signed int. Not that we would use that much string
169575715b3delphij	 * data for it...
169675715b3delphij	 */
1697ef64b99roberto	get_systime(&ts);
1698837c91fcy
1699047f369cy	have_srchost = FALSE;
1700047f369cy	have_dstadr = FALSE;
1701047f369cy	have_da_rid = FALSE;
1702047f369cy	have_jitter = FALSE;
1703d54cfbdroberto	ZERO_SOCK(&srcadr);
1704d54cfbdroberto	ZERO_SOCK(&dstadr);
1705047f369cy	clock_name[0] = '\0';
1706047f369cy	ZERO(estoffset);
1707047f369cy	ZERO(estdelay);
1708047f369cy	ZERO(estjitter);
1709047f369cy	ZERO(estdisp);
1710ef64b99roberto
1711ef64b99roberto	while (nextvar(&datalen, &data, &name, &value)) {
171275715b3delphij		INSIST(name && value);
1713047f369cy		if (!strcmp("srcadr", name) ||
1714047f369cy		    !strcmp("peeradr", name)) {
1715047f369cy			if (!decodenetnum(value, &srcadr))
1716837c91fcy				xprintf(stderr, "malformed %s=%s\n",
1717047f369cy					name, value);
1718047f369cy		} else if (!strcmp("srchost", name)) {
17195ef283fcy			if (pvl == peervarlist || pvl == apeervarlist) {
1720047f369cy				len = strlen(value);
1721047f369cy				if (2 < len &&
1722047f369cy				    (size_t)len < sizeof(clock_name)) {
1723047f369cy					/* strip quotes */
1724047f369cy					value++;
1725047f369cy					len -= 2;
1726047f369cy					memcpy(clock_name, value, len);
1727047f369cy					clock_name[len] = '\0';
1728047f369cy					have_srchost = TRUE;
1729047f369cy				}
1730d54cfbdroberto			}
1731047f369cy		} else if (!strcmp("dstadr", name)) {
1732d54cfbdroberto			if (decodenetnum(value, &dum_store)) {
1733118e757roberto				type = decodeaddrtype(&dum_store);
1734047f369cy				have_dstadr = TRUE;
1735d54cfbdroberto				dstadr = dum_store;
1736d54cfbdroberto				if (pvl == opeervarlist) {
1737047f369cy					have_da_rid = TRUE;
1738d54cfbdroberto					dstadr_refid = trunc_left(stoa(&dstadr), 15);
1739ef64b99roberto				}
1740ef64b99roberto			}
1741047f369cy		} else if (!strcmp("hmode", name)) {
1742047f369cy			decodeint(value, &hmode);
1743047f369cy		} else if (!strcmp("refid", name)) {
17447598092delphij			if (   (pvl == peervarlist)
17457598092delphij			    && (drefid == REFID_IPV4)) {
1746047f369cy				have_da_rid = TRUE;
1747047f369cy				drlen = strlen(value);
1748047f369cy				if (0 == drlen) {
1749d54cfbdroberto					dstadr_refid = "";
1750047f369cy				} else if (drlen <= 4) {
1751047f369cy					ZERO(u32);
1752047f369cy					memcpy(&u32, value, drlen);
1753047f369cy					dstadr_refid = refid_str(u32, 1);
1754d54cfbdroberto				} else if (decodenetnum(value, &refidadr)) {
1755d54cfbdroberto					if (SOCK_UNSPEC(&refidadr))
1756ef64b99roberto						dstadr_refid = "0.0.0.0";
1757d54cfbdroberto					else if (ISREFCLOCKADR(&refidadr))
1758d54cfbdroberto						dstadr_refid =
1759d54cfbdroberto						    refnumtoa(&refidadr);
1760ef64b99roberto					else
1761118e757roberto						dstadr_refid =
1762d54cfbdroberto						    stoa(&refidadr);
1763ef64b99roberto				} else {
1764047f369cy					have_da_rid = FALSE;
1765ef64b99roberto				}
17667598092delphij			} else if (   (pvl == apeervarlist)
17677598092delphij				   || (pvl == peervarlist)) {
17687598092delphij				/* no need to check drefid == REFID_HASH */
17695ef283fcy				have_da_rid = TRUE;
17705ef283fcy				drlen = strlen(value);
17715ef283fcy				if (0 == drlen) {
17725ef283fcy					dstadr_refid = "";
17735ef283fcy				} else if (drlen <= 4) {
17745ef283fcy					ZERO(u32);
17755ef283fcy					memcpy(&u32, value, drlen);
17765ef283fcy					dstadr_refid = refid_str(u32, 1);
1777837c91fcy					//xprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value);
17785ef283fcy				} else if (decodenetnum(value, &refidadr)) {
17795ef283fcy					if (SOCK_UNSPEC(&refidadr))
17805ef283fcy						dstadr_refid = "0.0.0.0";
17815ef283fcy					else if (ISREFCLOCKADR(&refidadr))
17825ef283fcy						dstadr_refid =
17835ef283fcy						    refnumtoa(&refidadr);
17845ef283fcy					else {
17855ef283fcy						char *buf = emalloc(10);
17865ef283fcy						int i = ntohl(refidadr.sa4.sin_addr.s_addr);
17875ef283fcy
17885ef283fcy						snprintf(buf, 10,
17895ef283fcy							"%0x", i);
17905ef283fcy						dstadr_refid = buf;
1791837c91fcy					//xprintf(stderr, "apeervarlist refid: value=<%x>\n", i);
17925ef283fcy					}
1793837c91fcy					//xprintf(stderr, "apeervarlist refid: value=<%s>\n", value);
17945ef283fcy				} else {
17955ef283fcy					have_da_rid = FALSE;
17965ef283fcy				}
1797ef64b99roberto			}
1798047f369cy		} else if (!strcmp("stratum", name)) {
1799047f369cy			decodeuint(value, &stratum);
1800047f369cy		} else if (!strcmp("hpoll", name)) {
1801047f369cy			if (decodeint(value, &hpoll) && hpoll < 0)
1802047f369cy				hpoll = NTP_MINPOLL;
1803047f369cy		} else if (!strcmp("ppoll", name)) {
1804047f369cy			if (decodeint(value, &ppoll) && ppoll < 0)
1805047f369cy				ppoll = NTP_MINPOLL;
1806047f369cy		} else if (!strcmp("reach", name)) {
1807047f369cy			decodeuint(value, &reach);
1808047f369cy		} else if (!strcmp("delay", name)) {
1809047f369cy			decodetime(value, &estdelay);
1810047f369cy		} else if (!strcmp("offset", name)) {
1811047f369cy			decodetime(value, &estoffset);
1812047f369cy		} else if (!strcmp("jitter", name)) {
18135ef283fcy			if ((pvl == peervarlist || pvl == apeervarlist)
18145ef283fcy			    && decodetime(value, &estjitter))
1815047f369cy				have_jitter = 1;
1816047f369cy		} else if (!strcmp("rootdisp", name) ||
1817047f369cy			   !strcmp("dispersion", name)) {
1818047f369cy			decodetime(value, &estdisp);
1819047f369cy		} else if (!strcmp("rec", name)) {
1820047f369cy			decodets(value, &rec);
1821047f369cy		} else if (!strcmp("srcport", name) ||
1822047f369cy			   !strcmp("peerport", name)) {
1823047f369cy			decodeuint(value, &srcport);
1824047f369cy		} else if (!strcmp("reftime", name)) {
1825ef64b99roberto			if (!decodets(value, &reftime))
1826ef64b99roberto				L_CLR(&reftime);
182775715b3delphij		} else if (!strcmp("flash", name)) {
182875715b3delphij		    decodeuint(value, &flash);
18295ef283fcy		} else {
1830837c91fcy			// xprintf(stderr, "UNRECOGNIZED name=%s ", name);
1831ef64b99roberto		}
1832ef64b99roberto	}
1833ef64b99roberto
1834ef64b99roberto	/*
1835047f369cy	 * hmode gives the best guidance for the t column.  If the response
1836047f369cy	 * did not include hmode we'll use the old decodeaddrtype() result.
1837ef64b99roberto	 */
1838047f369cy	switch (hmode) {
1839047f369cy
1840047f369cy	case MODE_BCLIENT:
1841047f369cy		/* broadcastclient or multicastclient */
1842047f369cy		type = 'b';
1843047f369cy		break;
1844047f369cy
1845047f369cy	case MODE_BROADCAST:
1846047f369cy		/* broadcast or multicast server */
1847047f369cy		if (IS_MCAST(&srcadr))
1848047f369cy			type = 'M';
1849047f369cy		else
1850047f369cy			type = 'B';
1851047f369cy		break;
1852047f369cy
1853047f369cy	case MODE_CLIENT:
1854047f369cy		if (ISREFCLOCKADR(&srcadr))
1855047f369cy			type = 'l';	/* local refclock*/
1856047f369cy		else if (SOCK_UNSPEC(&srcadr))
1857047f369cy			type = 'p';	/* pool */
1858047f369cy		else if (IS_MCAST(&srcadr))
1859047f369cy			type = 'a';	/* manycastclient */
1860047f369cy		else
1861047f369cy			type = 'u';	/* unicast */
1862047f369cy		break;
1863047f369cy
1864047f369cy	case MODE_ACTIVE:
1865047f369cy		type = 's';		/* symmetric active */
1866047f369cy		break;			/* configured */
1867047f369cy
1868047f369cy	case MODE_PASSIVE:
1869047f369cy		type = 'S';		/* symmetric passive */
1870047f369cy		break;			/* ephemeral */
1871047f369cy	}
1872ef64b99roberto
1873ef64b99roberto	/*
1874ef64b99roberto	 * Got everything, format the line
1875ef64b99roberto	 */
1876047f369cy	poll_sec = 1 << min(ppoll, hpoll);
1877ef64b99roberto	if (pktversion > NTP_OLDVERSION)
1878ef64b99roberto		c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
1879ef64b99roberto	else
1880ef64b99roberto		c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
1881d54cfbdroberto	if (numhosts > 1) {
18825ef283fcy		if ((pvl == peervarlist || pvl == apeervarlist)
18835ef283fcy		    && have_dstadr) {
1884d54cfbdroberto			serverlocal = nntohost_col(&dstadr,
1885d54cfbdroberto			    (size_t)min(LIB_BUFLENGTH - 1, maxhostlen),
1886d54cfbdroberto			    TRUE);
1887d54cfbdroberto		} else {
1888d54cfbdroberto			if (currenthostisnum)
1889d54cfbdroberto				serverlocal = trunc_left(currenthost,
1890d54cfbdroberto							 maxhostlen);
1891d54cfbdroberto			else
1892d54cfbdroberto				serverlocal = currenthost;
1893d54cfbdroberto		}
1894837c91fcy		xprintf(fp, "%-*s ", (int)maxhostlen, serverlocal);
1895d54cfbdroberto	}
1896d54cfbdroberto	if (AF_UNSPEC == af || AF(&srcadr) == af) {
1897047f369cy		if (!have_srchost)
1898047f369cy			strlcpy(clock_name, nntohost(&srcadr),
1899047f369cy				sizeof(clock_name));
190030f4731delphij		/* wide and long source - space over on next line */
190130f4731delphij		/* allow for host + sp if > 1 and regular tally + source + sp */
1902047f369cy		if (wideremote && 15 < strlen(clock_name))
1903837c91fcy			xprintf(fp, "%c%s\n%*s", c, clock_name,
190430f4731delphij				((numhosts > 1) ? (int)maxhostlen + 1 : 0)
190530f4731delphij							+ 1 + 15 + 1, "");
1906047f369cy		else
1907837c91fcy			xprintf(fp, "%c%-15.15s ", c, clock_name);
190875715b3delphij		if ((flash & TEST12) && (pvl != opeervarlist)) {
1909837c91fcy			drlen = xprintf(fp, "(loop)");
191075715b3delphij		} else if (!have_da_rid) {
1911047f369cy			drlen = 0;
1912047f369cy		} else {
1913047f369cy			drlen = strlen(dstadr_refid);
1914047f369cy			makeascii(drlen, dstadr_refid, fp);
1915047f369cy		}
19165ef283fcy		if (pvl == apeervarlist) {
19175ef283fcy			while (drlen++ < 9)
1918837c91fcy				xputc(' ', fp);
1919837c91fcy			xprintf(fp, "%-6d", associd);
19205ef283fcy		} else {
19215ef283fcy			while (drlen++ < 15)
1922837c91fcy				xputc(' ', fp);
19235ef283fcy		}
1924837c91fcy		xprintf(fp,
1925d54cfbdroberto			" %2ld %c %4.4s %4.4s  %3lo  %7.7s %8.7s %7.7s\n",
1926d54cfbdroberto			stratum, type,
1927d54cfbdroberto			prettyinterval(whenbuf, sizeof(whenbuf),
1928d54cfbdroberto				       when(&ts, &rec, &reftime)),
1929837c91fcy			prettyinterval(pollbuf, sizeof(pollbuf),
1930d54cfbdroberto				       (int)poll_sec),
1931837c91fcy			reach, ulfptoms(&estdelay, 3),
1932d54cfbdroberto			lfptoms(&estoffset, 3),
1933047f369cy			(have_jitter)
1934837c91fcy			    ? ulfptoms(&estjitter, 3)
1935837c91fcy			    : ulfptoms(&estdisp, 3));
1936118e757roberto		return (1);
1937118e757roberto	}
1938118e757roberto	else
1939118e757roberto		return(1);
1940ef64b99roberto}
1941ef64b99roberto
1942ef64b99roberto
1943ef64b99roberto/*
1944ef64b99roberto * dogetpeers - given an association ID, read and print the spreadsheet
1945ef64b99roberto *		peer variables.
1946ef64b99roberto */
1947ef64b99robertostatic int
1948ef64b99robertodogetpeers(
1949ef64b99roberto	struct varlist *pvl,
1950d54cfbdroberto	associd_t associd,
1951118e757roberto	FILE *fp,
1952118e757roberto	int af
1953ef64b99roberto	)
1954ef64b99roberto{
1955d54cfbdroberto	const char *datap;
1956ef64b99roberto	int res;
19578518518delphij	size_t dsize;
1958ef64b99roberto	u_short rstatus;
1959ef64b99roberto
1960ef64b99roberto#ifdef notdef
1961ef64b99roberto	res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
1962ef64b99roberto			  &dsize, &datap);
1963ef64b99roberto#else
1964ef64b99roberto	/*
1965ef64b99roberto	 * Damn fuzzballs
1966ef64b99roberto	 */
1967d54cfbdroberto	res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
1968ef64b99roberto			  &dsize, &datap);
1969ef64b99roberto#endif
1970ef64b99roberto
1971ef64b99roberto	if (res != 0)
1972ef64b99roberto		return 0;
1973ef64b99roberto
1974ef64b99roberto	if (dsize == 0) {
19757a6072eroberto		if (numhosts > 1)
1976837c91fcy			xprintf(stderr, "server=%s ", currenthost);
1977837c91fcy		xprintf(stderr,
1978d54cfbdroberto			"***No information returned for association %u\n",
1979d54cfbdroberto			associd);
1980ef64b99roberto		return 0;
1981ef64b99roberto	}
1982ef64b99roberto
1983d54cfbdroberto	return doprintpeers(pvl, associd, (int)rstatus, dsize, datap,
1984d54cfbdroberto			    fp, af);
1985ef64b99roberto}
1986ef64b99roberto
1987ef64b99roberto
1988ef64b99roberto/*
1989ef64b99roberto * peers - print a peer spreadsheet
1990ef64b99roberto */
1991ef64b99robertostatic void
1992ef64b99robertodopeers(
1993ef64b99roberto	int showall,
1994118e757roberto	FILE *fp,
1995118e757roberto	int af
1996ef64b99roberto	)
1997ef64b99roberto{
1998047f369cy	u_int		u;
1999d54cfbdroberto	char		fullname[LENHOSTNAME];
2000d54cfbdroberto	sockaddr_u	netnum;
2001047f369cy	const char *	name_or_num;
2002d54cfbdroberto	size_t		sl;
2003ef64b99roberto
2004ef64b99roberto	if (!dogetassoc(fp))
2005ef64b99roberto		return;
2006ef64b99roberto
2007047f369cy	for (u = 0; u < numhosts; u++) {
2008047f369cy		if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
2009d54cfbdroberto			name_or_num = nntohost(&netnum);
2010d54cfbdroberto			sl = strlen(name_or_num);
2011047f369cy			maxhostlen = max(maxhostlen, sl);
2012d54cfbdroberto		}
2013b5b40f9roberto	}
2014118e757roberto	if (numhosts > 1)
2015837c91fcy		xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2016d54cfbdroberto			"server (local)");
2017837c91fcy	xprintf(fp,
2018d54cfbdroberto		"     remote           refid      st t when poll reach   delay   offset  jitter\n");
2019ef64b99roberto	if (numhosts > 1)
2020047f369cy		for (u = 0; u <= maxhostlen; u++)
2021837c91fcy			xprintf(fp, "=");
2022837c91fcy	xprintf(fp,
2023d54cfbdroberto		"==============================================================================\n");
2024ef64b99roberto
2025047f369cy	for (u = 0; u < numassoc; u++) {
2026ef64b99roberto		if (!showall &&
2027047f369cy		    !(CTL_PEER_STATVAL(assoc_cache[u].status)
2028047f369cy		      & (CTL_PST_CONFIG|CTL_PST_REACH))) {
2029047f369cy			if (debug)
2030837c91fcy				xprintf(stderr, "eliding [%d]\n",
2031047f369cy					(int)assoc_cache[u].assid);
2032ef64b99roberto			continue;
2033ef64b99roberto		}
2034047f369cy		if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid,
2035047f369cy				fp, af))
2036047f369cy			return;
2037ef64b99roberto	}
2038ef64b99roberto	return;
2039ef64b99roberto}
2040ef64b99roberto
2041ef64b99roberto
2042ef64b99roberto/*
20435ef283fcy * doapeers - print a peer spreadsheet with assocIDs
20445ef283fcy */
20455ef283fcystatic void
20465ef283fcydoapeers(
20475ef283fcy	int showall,
20485ef283fcy	FILE *fp,
20495ef283fcy	int af
20505ef283fcy	)
20515ef283fcy{
20525ef283fcy	u_int		u;
20535ef283fcy	char		fullname[LENHOSTNAME];
20545ef283fcy	sockaddr_u	netnum;
20555ef283fcy	const char *	name_or_num;
20565ef283fcy	size_t		sl;
20575ef283fcy
20585ef283fcy	if (!dogetassoc(fp))
20595ef283fcy		return;
20605ef283fcy
20615ef283fcy	for (u = 0; u < numhosts; u++) {
20625ef283fcy		if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
20635ef283fcy			name_or_num = nntohost(&netnum);
20645ef283fcy			sl = strlen(name_or_num);
20655ef283fcy			maxhostlen = max(maxhostlen, sl);
20665ef283fcy		}
20675ef283fcy	}
20685ef283fcy	if (numhosts > 1)
2069837c91fcy		xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
20705ef283fcy			"server (local)");
2071837c91fcy	xprintf(fp,
20725ef283fcy		"     remote       refid   assid  st t when poll reach   delay   offset  jitter\n");
20735ef283fcy	if (numhosts > 1)
20745ef283fcy		for (u = 0; u <= maxhostlen; u++)
2075837c91fcy			xprintf(fp, "=");
2076837c91fcy	xprintf(fp,
20775ef283fcy		"==============================================================================\n");
20785ef283fcy
20795ef283fcy	for (u = 0; u < numassoc; u++) {
20805ef283fcy		if (!showall &&
20815ef283fcy		    !(CTL_PEER_STATVAL(assoc_cache[u].status)
20825ef283fcy		      & (CTL_PST_CONFIG|CTL_PST_REACH))) {
20835ef283fcy			if (debug)
2084837c91fcy				xprintf(stderr, "eliding [%d]\n",
20855ef283fcy					(int)assoc_cache[u].assid);
20865ef283fcy			continue;
20875ef283fcy		}
20885ef283fcy		if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid,
20895ef283fcy				fp, af))
20905ef283fcy			return;
20915ef283fcy	}
20925ef283fcy	return;
20935ef283fcy}
20945ef283fcy
20955ef283fcy
20965ef283fcy/*
2097ef64b99roberto * peers - print a peer spreadsheet
2098ef64b99roberto */
2099ef64b99roberto/*ARGSUSED*/
2100ef64b99robertostatic void
2101ef64b99robertopeers(
2102ef64b99roberto	struct parse *pcmd,
2103ef64b99roberto	FILE *fp
2104ef64b99roberto	)
2105ef64b99roberto{
21067598092delphij	if (drefid == REFID_HASH) {
21077598092delphij		apeers(pcmd, fp);
21087598092delphij	} else {
21097598092delphij		int af = 0;
2110118e757roberto
21117598092delphij		if (pcmd->nargs == 1) {
21127598092delphij			if (pcmd->argval->ival == 6)
21137598092delphij				af = AF_INET6;
21147598092delphij			else
21157598092delphij				af = AF_INET;
21167598092delphij		}
21177598092delphij		dopeers(0, fp, af);
2118118e757roberto	}
2119ef64b99roberto}
2120ef64b99roberto
2121ef64b99roberto
2122ef64b99roberto/*
21235ef283fcy * apeers - print a peer spreadsheet, with assocIDs
21245ef283fcy */
21255ef283fcy/*ARGSUSED*/
21265ef283fcystatic void
21275ef283fcyapeers(
21285ef283fcy	struct parse *pcmd,
21295ef283fcy	FILE *fp
21305ef283fcy	)
21315ef283fcy{
21325ef283fcy	int af = 0;
21335ef283fcy
21345ef283fcy	if (pcmd->nargs == 1) {
21355ef283fcy		if (pcmd->argval->ival == 6)
21365ef283fcy			af = AF_INET6;
21375ef283fcy		else
21385ef283fcy			af = AF_INET;
21395ef283fcy	}
21405ef283fcy	doapeers(0, fp, af);
21415ef283fcy}
21425ef283fcy
21435ef283fcy
21445ef283fcy/*
2145ef64b99roberto * lpeers - print a peer spreadsheet including all fuzzball peers
2146ef64b99roberto */
2147ef64b99roberto/*ARGSUSED*/
2148ef64b99robertostatic void
2149ef64b99robertolpeers(
2150ef64b99roberto	struct parse *pcmd,
2151ef64b99roberto	FILE *fp
2152ef64b99roberto	)
2153ef64b99roberto{
2154118e757roberto	int af = 0;
2155118e757roberto
2156118e757roberto	if (pcmd->nargs == 1) {
2157118e757roberto		if (pcmd->argval->ival == 6)
2158118e757roberto			af = AF_INET6;
2159118e757roberto		else
2160118e757roberto			af = AF_INET;
2161118e757roberto	}
2162118e757roberto	dopeers(1, fp, af);
2163ef64b99roberto}
2164ef64b99roberto
2165ef64b99roberto
2166ef64b99roberto/*
2167ef64b99roberto * opeers - print a peer spreadsheet
2168ef64b99roberto */
2169ef64b99robertostatic void
2170ef64b99robertodoopeers(
2171ef64b99roberto	int showall,
2172118e757roberto	FILE *fp,
2173118e757roberto	int af
2174ef64b99roberto	)
2175ef64b99roberto{
2176047f369cy	u_int i;
21777a6072eroberto	char fullname[LENHOSTNAME];
2178d54cfbdroberto	sockaddr_u netnum;
2179ef64b99roberto
2180ef64b99roberto	if (!dogetassoc(fp))
2181ef64b99roberto		return;
2182ef64b99roberto
21837a6072eroberto	for (i = 0; i < numhosts; ++i) {
2184047f369cy		if (getnetnum(chosts[i].name, &netnum, fullname, af))
2185047f369cy			if (strlen(fullname) > maxhostlen)
21867a6072eroberto				maxhostlen = strlen(fullname);
21877a6072eroberto	}
21887a6072eroberto	if (numhosts > 1)
2189837c91fcy		xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2190047f369cy			"server");
2191837c91fcy	xprintf(fp,
2192047f369cy	    "     remote           local      st t when poll reach   delay   offset    disp\n");
21937a6072eroberto	if (numhosts > 1)
21947a6072eroberto		for (i = 0; i <= maxhostlen; ++i)
2195837c91fcy			xprintf(fp, "=");
2196837c91fcy	xprintf(fp,
2197047f369cy	    "==============================================================================\n");
2198ef64b99roberto
2199ef64b99roberto	for (i = 0; i < numassoc; i++) {
2200ef64b99roberto		if (!showall &&
2201047f369cy		    !(CTL_PEER_STATVAL(assoc_cache[i].status) &
2202047f369cy		      (CTL_PST_CONFIG | CTL_PST_REACH)))
2203ef64b99roberto			continue;
2204047f369cy		if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af))
2205ef64b99roberto			return;
2206ef64b99roberto	}
2207ef64b99roberto	return;
2208ef64b99roberto}
2209ef64b99roberto
2210ef64b99roberto
2211ef64b99roberto/*
2212ef64b99roberto * opeers - print a peer spreadsheet the old way
2213ef64b99roberto */
2214ef64b99roberto/*ARGSUSED*/
2215ef64b99robertostatic void
2216ef64b99robertoopeers(
2217ef64b99roberto	struct parse *pcmd,
2218ef64b99roberto	FILE *fp
2219ef64b99roberto	)
2220ef64b99roberto{
2221118e757roberto	int af = 0;
2222118e757roberto
2223118e757roberto	if (pcmd->nargs == 1) {
2224118e757roberto		if (pcmd->argval->ival == 6)
2225118e757roberto			af = AF_INET6;
2226118e757roberto		else
2227118e757roberto			af = AF_INET;
2228118e757roberto	}
2229118e757roberto	doopeers(0, fp, af);
2230ef64b99roberto}
2231ef64b99roberto
2232ef64b99roberto
2233ef64b99roberto/*
2234ef64b99roberto * lopeers - print a peer spreadsheet including all fuzzball peers
2235ef64b99roberto */
2236ef64b99roberto/*ARGSUSED*/
2237ef64b99robertostatic void
2238ef64b99robertolopeers(
2239ef64b99roberto	struct parse *pcmd,
2240ef64b99roberto	FILE *fp
2241ef64b99roberto	)
2242ef64b99roberto{
2243118e757roberto	int af = 0;
2244118e757roberto
2245118e757roberto	if (pcmd->nargs == 1) {
2246118e757roberto		if (pcmd->argval->ival == 6)
2247118e757roberto			af = AF_INET6;
2248118e757roberto		else
2249118e757roberto			af = AF_INET;
2250118e757roberto	}
2251118e757roberto	doopeers(1, fp, af);
2252ef64b99roberto}
2253d54cfbdroberto
2254d54cfbdroberto
2255837c91fcy/*
2256d54cfbdroberto * config - send a configuration command to a remote host
2257d54cfbdroberto */
2258837c91fcystatic void
2259d54cfbdrobertoconfig (
2260d54cfbdroberto	struct parse *pcmd,
2261d54cfbdroberto	FILE *fp
2262d54cfbdroberto	)
2263d54cfbdroberto{
2264047f369cy	const char *cfgcmd;
2265d54cfbdroberto	u_short rstatus;
22668518518delphij	size_t rsize;
2267d54cfbdroberto	const char *rdata;
2268d54cfbdroberto	char *resp;
2269d54cfbdroberto	int res;
2270d54cfbdroberto	int col;
2271d54cfbdroberto	int i;
2272d54cfbdroberto
2273d54cfbdroberto	cfgcmd = pcmd->argval[0].string;
2274d54cfbdroberto
2275d54cfbdroberto	if (debug > 2)
2276837c91fcy		xprintf(stderr,
2277d54cfbdroberto			"In Config\n"
2278d54cfbdroberto			"Keyword = %s\n"
2279d54cfbdroberto			"Command = %s\n", pcmd->keyword, cfgcmd);
2280d54cfbdroberto
22818518518delphij	res = doquery(CTL_OP_CONFIGURE, 0, 1,
22828518518delphij		      strlen(cfgcmd), cfgcmd,
2283d54cfbdroberto		      &rstatus, &rsize, &rdata);
2284d54cfbdroberto
2285d54cfbdroberto	if (res != 0)
2286d54cfbdroberto		return;
2287d54cfbdroberto
2288d54cfbdroberto	if (rsize > 0 && '\n' == rdata[rsize - 1])
2289d54cfbdroberto		rsize--;
2290d54cfbdroberto
2291d54cfbdroberto	resp = emalloc(rsize + 1);
2292d54cfbdroberto	memcpy(resp, rdata, rsize);
2293d54cfbdroberto	resp[rsize] = '\0';
2294d54cfbdroberto
2295d54cfbdroberto	col = -1;
2296d54cfbdroberto	if (1 == sscanf(resp, "column %d syntax error", &col)
2297d54cfbdroberto	    && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) {
229830f4731delphij		if (interactive)
2299837c91fcy			xputs("             *", stdout); /* "ntpq> :config " */
230030f4731delphij		else
2301d54cfbdroberto			printf("%s\n", cfgcmd);
230230f4731delphij		for (i = 0; i < col; i++)
2303837c91fcy			xputc('_', stdout);
2304837c91fcy		xputs("^\n", stdout);
2305d54cfbdroberto	}
2306d54cfbdroberto	printf("%s\n", resp);
2307d54cfbdroberto	free(resp);
2308d54cfbdroberto}
2309d54cfbdroberto
2310d54cfbdroberto
2311837c91fcy/*
2312d54cfbdroberto * config_from_file - remotely configure an ntpd daemon using the
2313d54cfbdroberto * specified configuration file
2314d54cfbdroberto * SK: This function is a kludge at best and is full of bad design
2315d54cfbdroberto * bugs:
2316d54cfbdroberto * 1. ntpq uses UDP, which means that there is no guarantee of in-order,
2317837c91fcy *    error-free delivery.
2318d54cfbdroberto * 2. The maximum length of a packet is constrained, and as a result, the
2319837c91fcy *    maximum length of a line in a configuration file is constrained.
2320d54cfbdroberto *    Longer lines will lead to unpredictable results.
2321d54cfbdroberto * 3. Since this function is sending a line at a time, we can't update
2322d54cfbdroberto *    the control key through the configuration file (YUCK!!)
23237598092delphij *
23247598092delphij * Pearly: There are a few places where 'size_t' is cast to 'int' based
23257598092delphij * on the assumption that 'int' can hold the size of the involved
23267598092delphij * buffers without overflow.
2327d54cfbdroberto */
2328837c91fcystatic void
2329d54cfbdrobertoconfig_from_file (
2330d54cfbdroberto	struct parse *pcmd,
2331d54cfbdroberto	FILE *fp
2332d54cfbdroberto	)
2333d54cfbdroberto{
2334d54cfbdroberto	u_short rstatus;
23358518518delphij	size_t rsize;
2336d54cfbdroberto	const char *rdata;
23377598092delphij	char * cp;
2338d54cfbdroberto	int res;
2339d54cfbdroberto	FILE *config_fd;
2340d54cfbdroberto	char config_cmd[MAXLINE];
2341d54cfbdroberto	size_t config_len;
2342d54cfbdroberto	int i;
2343d54cfbdroberto	int retry_limit;
2344d54cfbdroberto
2345d54cfbdroberto	if (debug > 2)
2346837c91fcy		xprintf(stderr,
2347d54cfbdroberto			"In Config\n"
2348d54cfbdroberto			"Keyword = %s\n"
2349d54cfbdroberto			"Filename = %s\n", pcmd->keyword,
2350d54cfbdroberto			pcmd->argval[0].string);
2351d54cfbdroberto
2352d54cfbdroberto	config_fd = fopen(pcmd->argval[0].string, "r");
2353d54cfbdroberto	if (NULL == config_fd) {
2354d54cfbdroberto		printf("ERROR!! Couldn't open file: %s\n",
2355d54cfbdroberto		       pcmd->argval[0].string);
2356d54cfbdroberto		return;
2357d54cfbdroberto	}
2358d54cfbdroberto
2359d54cfbdroberto	printf("Sending configuration file, one line at a time.\n");
2360d54cfbdroberto	i = 0;
2361d54cfbdroberto	while (fgets(config_cmd, MAXLINE, config_fd) != NULL) {
23627598092delphij		/* Eliminate comments first. */
23637598092delphij		cp = strchr(config_cmd, '#');
23647598092delphij		config_len = (NULL != cp)
23657598092delphij		    ? (size_t)(cp - config_cmd)
23667598092delphij		    : strlen(config_cmd);
2367837c91fcy
23687598092delphij		/* [Bug 3015] make sure there's no trailing whitespace;
23697598092delphij		 * the fix for [Bug 2853] on the server side forbids
23707598092delphij		 * those. And don't transmit empty lines, as this would
23717598092delphij		 * just be waste.
23727598092delphij		 */
23737598092delphij		while (config_len != 0 &&
23747598092delphij		       (u_char)config_cmd[config_len-1] <= ' ')
23757598092delphij			--config_len;
23767598092delphij		config_cmd[config_len] = '\0';
23777598092delphij
2378d54cfbdroberto		++i;
23797598092delphij		if (0 == config_len)
23807598092delphij			continue;
23817598092delphij
2382d54cfbdroberto		retry_limit = 2;
2383837c91fcy		do
2384d54cfbdroberto			res = doquery(CTL_OP_CONFIGURE, 0, 1,
23857598092delphij				      config_len, config_cmd,
2386d54cfbdroberto				      &rstatus, &rsize, &rdata);
2387d54cfbdroberto		while (res != 0 && retry_limit--);
2388d54cfbdroberto		if (res != 0) {
23897598092delphij			printf("Line No: %d query failed: %.*s\n"
23907598092delphij			       "Subsequent lines not sent.\n",
23917598092delphij			       i, (int)config_len, config_cmd);
2392d54cfbdroberto			fclose(config_fd);
2393d54cfbdroberto			return;
2394d54cfbdroberto		}
2395d54cfbdroberto
23967598092delphij		/* Right-strip the result code string, then output the
23977598092delphij		 * last line executed, with result code. */
23987598092delphij		while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ')
23997598092delphij			--rsize;
24007598092delphij		printf("Line No: %d %.*s: %.*s\n", i,
24017598092delphij		       (int)rsize, rdata,
24027598092delphij		       (int)config_len, config_cmd);
2403d54cfbdroberto	}
2404d54cfbdroberto	printf("Done sending file\n");
2405d54cfbdroberto	fclose(config_fd);
2406d54cfbdroberto}
2407047f369cy
2408047f369cy
2409047f369cystatic int
2410047f369cyfetch_nonce(
2411047f369cy	char *	nonce,
2412047f369cy	size_t	cb_nonce
2413047f369cy	)
2414047f369cy{
2415047f369cy	const char	nonce_eq[] = "nonce=";
2416047f369cy	int		qres;
2417047f369cy	u_short		rstatus;
24188518518delphij	size_t		rsize;
2419047f369cy	const char *	rdata;
24208518518delphij	size_t		chars;
2421047f369cy
2422047f369cy	/*
2423047f369cy	 * Retrieve a nonce specific to this client to demonstrate to
2424047f369cy	 * ntpd that we're capable of receiving responses to our source
2425047f369cy	 * IP address, and thereby unlikely to be forging the source.
2426047f369cy	 */
2427047f369cy	qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus,
2428047f369cy		       &rsize, &rdata);
2429047f369cy	if (qres) {
2430837c91fcy		xprintf(stderr, "nonce request failed\n");
2431047f369cy		return FALSE;
2432047f369cy	}
2433047f369cy
2434bdc155dcy	if ((size_t)rsize <= sizeof(nonce_eq) - 1 ||
2435047f369cy	    strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) {
2436837c91fcy		xprintf(stderr, "unexpected nonce response format: %.*s\n",
24378518518delphij			(int)rsize, rdata); /* cast is wobbly */
2438047f369cy		return FALSE;
2439047f369cy	}
2440047f369cy	chars = rsize - (sizeof(nonce_eq) - 1);
244175715b3delphij	if (chars >= cb_nonce)
2442047f369cy		return FALSE;
2443047f369cy	memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars);
2444047f369cy	nonce[chars] = '\0';
2445047f369cy	while (chars > 0 &&
2446047f369cy	       ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) {
2447047f369cy		chars--;
2448047f369cy		nonce[chars] = '\0';
2449047f369cy	}
2450837c91fcy
2451047f369cy	return TRUE;
2452047f369cy}
2453047f369cy
2454047f369cy
2455047f369cy/*
2456047f369cy * add_mru	Add and entry to mru list, hash table, and allocate
2457047f369cy *		and return a replacement.
2458047f369cy *		This is a helper for collect_mru_list().
2459047f369cy */
2460047f369cystatic mru *
2461047f369cyadd_mru(
2462047f369cy	mru *add
2463047f369cy	)
2464047f369cy{
2465047f369cy	u_short hash;
2466047f369cy	mru *mon;
2467047f369cy	mru *unlinked;
2468047f369cy
2469047f369cy
2470047f369cy	hash = NTP_HASH_ADDR(&add->addr);
2471047f369cy	/* see if we have it among previously received entries */
2472047f369cy	for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink)
2473047f369cy		if (SOCK_EQ(&mon->addr, &add->addr))
2474047f369cy			break;
2475047f369cy	if (mon != NULL) {
2476047f369cy		if (!L_ISGEQ(&add->first, &mon->first)) {
2477837c91fcy			xprintf(stderr,
2478047f369cy				"add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n",
2479047f369cy				sptoa(&add->addr), add->last.l_ui,
2480047f369cy				add->last.l_uf, mon->last.l_ui,
2481047f369cy				mon->last.l_uf);
2482047f369cy			exit(1);
2483047f369cy		}
2484047f369cy		UNLINK_DLIST(mon, mlink);
2485047f369cy		UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru);
2486aae1e7dglebius		INSIST(unlinked == mon);
2487047f369cy		mru_dupes++;
2488047f369cy		TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui,
2489047f369cy		      mon->last.l_uf));
2490047f369cy	}
2491047f369cy	LINK_DLIST(mru_list, add, mlink);
2492047f369cy	LINK_SLIST(hash_table[hash], add, hlink);
2493047f369cy	TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
2494047f369cy	      add->last.l_ui, add->last.l_uf, add->count,
2495047f369cy	      (int)add->mode, (int)add->ver, (u_int)add->rs,
2496047f369cy	      add->first.l_ui, add->first.l_uf, sptoa(&add->addr)));
2497047f369cy	/* if we didn't update an existing entry, alloc replacement */
2498047f369cy	if (NULL == mon) {
2499047f369cy		mon = emalloc(sizeof(*mon));
2500047f369cy		mru_count++;
2501047f369cy	}
2502047f369cy	ZERO(*mon);
2503047f369cy
2504047f369cy	return mon;
2505047f369cy}
2506047f369cy
2507047f369cy
2508047f369cy/* MGOT macro is specific to collect_mru_list() */
2509047f369cy#define MGOT(bit)				\
2510047f369cy	do {					\
2511047f369cy		got |= (bit);			\
2512047f369cy		if (MRU_GOT_ALL == got) {	\
2513047f369cy			got = 0;		\
2514047f369cy			mon = add_mru(mon);	\
2515047f369cy			ci++;			\
2516047f369cy		}				\
2517047f369cy	} while (0)
2518047f369cy
2519047f369cy
25208518518delphijint
2521047f369cymrulist_ctrl_c_hook(void)
2522047f369cy{
2523047f369cy	mrulist_interrupted = TRUE;
25248518518delphij	return TRUE;
2525047f369cy}
2526047f369cy
2527047f369cy
2528047f369cystatic int
2529047f369cycollect_mru_list(
2530047f369cy	const char *	parms,
2531047f369cy	l_fp *		pnow
2532047f369cy	)
2533047f369cy{
2534047f369cy	const u_int sleep_msecs = 5;
2535047f369cy	static int ntpd_row_limit = MRU_ROW_LIMIT;
2536047f369cy	int c_mru_l_rc;		/* this function's return code */
2537047f369cy	u_char got;		/* MRU_GOT_* bits */
2538047f369cy	time_t next_report;
2539047f369cy	size_t cb;
2540047f369cy	mru *mon;
2541047f369cy	mru *head;
2542047f369cy	mru *recent;
2543047f369cy	int list_complete;
2544047f369cy	char nonce[128];
2545047f369cy	char buf[128];
2546047f369cy	char req_buf[CTL_MAX_DATA_LEN];
2547047f369cy	char *req;
2548047f369cy	char *req_end;
25498518518delphij	size_t chars;
2550047f369cy	int qres;
2551047f369cy	u_short rstatus;
25528518518delphij	size_t rsize;
2553047f369cy	const char *rdata;
2554047f369cy	int limit;
2555047f369cy	int frags;
2556047f369cy	int cap_frags;
2557047f369cy	char *tag;
2558047f369cy	char *val;
2559047f369cy	int si;		/* server index in response */
2560047f369cy	int ci;		/* client (our) index for validation */
2561047f369cy	int ri;		/* request index (.# suffix) */
2562047f369cy	int mv;
2563047f369cy	l_fp newest;
2564047f369cy	l_fp last_older;
2565047f369cy	sockaddr_u addr_older;
2566047f369cy	int have_now;
2567837c91fcy	int have_addr_older;
2568047f369cy	int have_last_older;
2569047f369cy	u_int restarted_count;
2570047f369cy	u_int nonce_uses;
2571047f369cy	u_short hash;
2572047f369cy	mru *unlinked;
2573047f369cy
2574047f369cy	if (!fetch_nonce(nonce, sizeof(nonce)))
2575047f369cy		return FALSE;
2576047f369cy
2577047f369cy	nonce_uses = 0;
2578047f369cy	restarted_count = 0;
2579047f369cy	mru_count = 0;
2580047f369cy	INIT_DLIST(mru_list, mlink);
2581047f369cy	cb = NTP_HASH_SIZE * sizeof(*hash_table);
2582aae1e7dglebius	INSIST(NULL == hash_table);
2583047f369cy	hash_table = emalloc_zero(cb);
2584047f369cy
2585047f369cy	c_mru_l_rc = FALSE;
2586047f369cy	list_complete = FALSE;
2587047f369cy	have_now = FALSE;
2588047f369cy	cap_frags = TRUE;
2589047f369cy	got = 0;
2590047f369cy	ri = 0;
2591047f369cy	cb = sizeof(*mon);
2592047f369cy	mon = emalloc_zero(cb);
2593047f369cy	ZERO(*pnow);
2594047f369cy	ZERO(last_older);
2595047f369cy	next_report = time(NULL) + MRU_REPORT_SECS;
2596047f369cy
2597047f369cy	limit = min(3 * MAXFRAGS, ntpd_row_limit);
2598047f369cy	frags = MAXFRAGS;
2599047f369cy	snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s",
2600047f369cy		 nonce, frags, parms);
2601047f369cy	nonce_uses++;
2602047f369cy
2603047f369cy	while (TRUE) {
2604047f369cy		if (debug)
2605837c91fcy			xprintf(stderr, "READ_MRU parms: %s\n", req_buf);
2606047f369cy
26078518518delphij		qres = doqueryex(CTL_OP_READ_MRU, 0, 0,
26088518518delphij				 strlen(req_buf), req_buf,
26098518518delphij				 &rstatus, &rsize, &rdata, TRUE);
2610047f369cy
2611047f369cy		if (CERR_UNKNOWNVAR == qres && ri > 0) {
2612047f369cy			/*
2613047f369cy			 * None of the supplied prior entries match, so
2614047f369cy			 * toss them from our list and try again.
2615047f369cy			 */
2616047f369cy			if (debug)
2617837c91fcy				xprintf(stderr,
2618047f369cy					"no overlap between %d prior entries and server MRU list\n",
2619047f369cy					ri);
2620047f369cy			while (ri--) {
2621047f369cy				recent = HEAD_DLIST(mru_list, mlink);
2622aae1e7dglebius				INSIST(recent != NULL);
2623047f369cy				if (debug)
2624837c91fcy					xprintf(stderr,
2625047f369cy						"tossing prior entry %s to resync\n",
2626047f369cy						sptoa(&recent->addr));
2627047f369cy				UNLINK_DLIST(recent, mlink);
2628047f369cy				hash = NTP_HASH_ADDR(&recent->addr);
2629047f369cy				UNLINK_SLIST(unlinked, hash_table[hash],
2630047f369cy					     recent, hlink, mru);
2631aae1e7dglebius				INSIST(unlinked == recent);
2632047f369cy				free(recent);
2633047f369cy				mru_count--;
2634047f369cy			}
2635047f369cy			if (NULL == HEAD_DLIST(mru_list, mlink)) {
2636047f369cy				restarted_count++;
2637047f369cy				if (restarted_count > 8) {
2638837c91fcy					xprintf(stderr,
2639047f369cy						"Giving up after 8 restarts from the beginning.\n"
2640047f369cy						"With high-traffic NTP servers, this can occur if the\n"
2641047f369cy						"MRU list is limited to less than about 16 seconds' of\n"
2642047f369cy						"entries.  See the 'mru' ntp.conf directive to adjust.\n");
2643047f369cy					goto cleanup_return;
2644047f369cy				}
2645047f369cy				if (debug)
2646837c91fcy					xprintf(stderr,
2647837c91fcy						"--->   Restarting from the beginning, retry #%u\n",
2648047f369cy						restarted_count);
2649047f369cy			}
2650047f369cy		} else if (CERR_UNKNOWNVAR == qres) {
2651837c91fcy			xprintf(stderr,
2652047f369cy				"CERR_UNKNOWNVAR from ntpd but no priors given.\n");
2653047f369cy			goto cleanup_return;
2654047f369cy		} else if (CERR_BADVALUE == qres) {
2655047f369cy			if (cap_frags) {
2656047f369cy				cap_frags = FALSE;
2657047f369cy				if (debug)
2658837c91fcy					xprintf(stderr,
2659047f369cy						"Reverted to row limit from fragments limit.\n");
2660047f369cy			} else {
2661047f369cy				/* ntpd has lower cap on row limit */
2662047f369cy				ntpd_row_limit--;
2663047f369cy				limit = min(limit, ntpd_row_limit);
2664047f369cy				if (debug)
2665837c91fcy					xprintf(stderr,
2666047f369cy						"Row limit reduced to %d following CERR_BADVALUE.\n",
2667047f369cy						limit);
2668047f369cy			}
2669047f369cy		} else if (ERR_INCOMPLETE == qres ||
2670047f369cy			   ERR_TIMEOUT == qres) {
2671047f369cy			/*
2672047f369cy			 * Reduce the number of rows/frags requested by
2673047f369cy			 * half to recover from lost response fragments.
2674047f369cy			 */
2675047f369cy			if (cap_frags) {
2676047f369cy				frags = max(2, frags / 2);
2677047f369cy				if (debug)
2678837c91fcy					xprintf(stderr,
2679047f369cy						"Frag limit reduced to %d following incomplete response.\n",
2680047f369cy						frags);
2681047f369cy			} else {
2682047f369cy				limit = max(2, limit / 2);
2683047f369cy				if (debug)
2684837c91fcy					xprintf(stderr,
2685047f369cy						"Row limit reduced to %d following incomplete response.\n",
2686047f369cy						limit);
2687047f369cy			}
2688047f369cy		} else if (qres) {
2689047f369cy			show_error_msg(qres, 0);
2690047f369cy			goto cleanup_return;
2691047f369cy		}
2692047f369cy		/*
2693047f369cy		 * This is a cheap cop-out implementation of rawmode
2694047f369cy		 * output for mrulist.  A better approach would be to
2695047f369cy		 * dump similar output after the list is collected by
2696047f369cy		 * ntpq with a continuous sequence of indexes.  This
2697047f369cy		 * cheap approach has indexes resetting to zero for
2698837c91fcy		 * each query/response, and duplicates are not
2699047f369cy		 * coalesced.
2700047f369cy		 */
2701047f369cy		if (!qres && rawmode)
2702047f369cy			printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout);
2703047f369cy		ci = 0;
2704047f369cy		have_addr_older = FALSE;
2705047f369cy		have_last_older = FALSE;
2706047f369cy		while (!qres && nextvar(&rsize, &rdata, &tag, &val)) {
270775715b3delphij			INSIST(tag && val);
2708047f369cy			if (debug > 1)
2709837c91fcy				xprintf(stderr, "nextvar gave: %s = %s\n",
2710047f369cy					tag, val);
2711047f369cy			switch(tag[0]) {
2712047f369cy
2713047f369cy			case 'a':
2714047f369cy				if (!strcmp(tag, "addr.older")) {
2715047f369cy					if (!have_last_older) {
2716837c91fcy						xprintf(stderr,
2717047f369cy							"addr.older %s before last.older\n",
2718047f369cy							val);
2719047f369cy						goto cleanup_return;
2720047f369cy					}
2721047f369cy					if (!decodenetnum(val, &addr_older)) {
2722837c91fcy						xprintf(stderr,
2723047f369cy							"addr.older %s garbled\n",
2724047f369cy							val);
2725047f369cy						goto cleanup_return;
2726047f369cy					}
2727047f369cy					hash = NTP_HASH_ADDR(&addr_older);
2728047f369cy					for (recent = hash_table[hash];
2729047f369cy					     recent != NULL;
2730047f369cy					     recent = recent->hlink)
2731047f369cy						if (ADDR_PORT_EQ(
2732047f369cy						      &addr_older,
2733047f369cy						      &recent->addr))
2734047f369cy							break;
2735047f369cy					if (NULL == recent) {
2736837c91fcy						xprintf(stderr,
2737047f369cy							"addr.older %s not in hash table\n",
2738047f369cy							val);
2739047f369cy						goto cleanup_return;
2740047f369cy					}
2741047f369cy					if (!L_ISEQU(&last_older,
2742047f369cy						     &recent->last)) {
2743837c91fcy						xprintf(stderr,
2744047f369cy							"last.older %08x.%08x mismatches %08x.%08x expected.\n",
2745047f369cy							last_older.l_ui,
2746047f369cy							last_older.l_uf,
2747047f369cy							recent->last.l_ui,
2748047f369cy							recent->last.l_uf);
2749047f369cy						goto cleanup_return;
2750047f369cy					}
2751047f369cy					have_addr_older = TRUE;
2752047f369cy				} else if (1 != sscanf(tag, "addr.%d", &si)
2753047f369cy					   || si != ci)
2754047f369cy					goto nomatch;
2755047f369cy				else if (decodenetnum(val, &mon->addr))
2756047f369cy					MGOT(MRU_GOT_ADDR);
2757047f369cy				break;
2758047f369cy
2759047f369cy			case 'l':
2760047f369cy				if (!strcmp(tag, "last.older")) {
2761047f369cy					if ('0' != val[0] ||
2762047f369cy					    'x' != val[1] ||
2763047f369cy					    !hextolfp(val + 2, &last_older)) {
2764837c91fcy						xprintf(stderr,
2765047f369cy							"last.older %s garbled\n",
2766047f369cy							val);
2767047f369cy						goto cleanup_return;
2768047f369cy					}
2769047f369cy					have_last_older = TRUE;
2770047f369cy				} else if (!strcmp(tag, "last.newest")) {
2771047f369cy					if (0 != got) {
2772837c91fcy						xprintf(stderr,
2773047f369cy							"last.newest %s before complete row, got = 0x%x\n",
2774047f369cy							val, (u_int)got);
2775047f369cy						goto cleanup_return;
2776047f369cy					}
2777047f369cy					if (!have_now) {
2778837c91fcy						xprintf(stderr,
2779047f369cy							"last.newest %s before now=\n",
2780047f369cy							val);
2781047f369cy						goto cleanup_return;
2782047f369cy					}
2783047f369cy					head = HEAD_DLIST(mru_list, mlink);
2784047f369cy					if (NULL != head) {
2785047f369cy						if ('0' != val[0] ||
2786047f369cy						    'x' != val[1] ||
2787047f369cy						    !hextolfp(val + 2, &newest) ||
2788047f369cy						    !L_ISEQU(&newest,
2789047f369cy							     &head->last)) {
2790837c91fcy							xprintf(stderr,
2791047f369cy								"last.newest %s mismatches %08x.%08x",
2792047f369cy								val,
2793047f369cy								head->last.l_ui,
2794047f369cy								head->last.l_uf);
2795047f369cy							goto cleanup_return;
2796047f369cy						}
2797047f369cy					}
2798047f369cy					list_complete = TRUE;
2799047f369cy				} else if (1 != sscanf(tag, "last.%d", &si) ||
2800047f369cy					   si != ci || '0' != val[0] ||
2801047f369cy					   'x' != val[1] ||
2802047f369cy					   !hextolfp(val + 2, &mon->last)) {
2803047f369cy					goto nomatch;
2804047f369cy				} else {
2805047f369cy					MGOT(MRU_GOT_LAST);
2806047f369cy					/*
2807047f369cy					 * allow interrupted retrieval,
2808047f369cy					 * using most recent retrieved
2809047f369cy					 * entry's last seen timestamp
2810047f369cy					 * as the end of operation.
2811047f369cy					 */
2812047f369cy					*pnow = mon->last;
2813047f369cy				}
2814047f369cy				break;
2815047f369cy
2816047f369cy			case 'f':
2817047f369cy				if (1 != sscanf(tag, "first.%d", &si) ||
2818047f369cy				    si != ci || '0' != val[0] ||
2819047f369cy				    'x' != val[1] ||
2820047f369cy				    !hextolfp(val + 2, &mon->first))
2821047f369cy					goto nomatch;
2822047f369cy				MGOT(MRU_GOT_FIRST);
2823047f369cy				break;
2824047f369cy
2825047f369cy			case 'n':
2826047f369cy				if (!strcmp(tag, "nonce")) {
2827047f369cy					strlcpy(nonce, val, sizeof(nonce));
2828047f369cy					nonce_uses = 0;
2829047f369cy					break; /* case */
2830047f369cy				} else if (strcmp(tag, "now") ||
2831047f369cy					   '0' != val[0] ||
2832047f369cy					   'x' != val[1] ||
2833047f369cy					    !hextolfp(val + 2, pnow))
2834047f369cy					goto nomatch;
2835047f369cy				have_now = TRUE;
2836047f369cy				break;
2837047f369cy
2838047f369cy			case 'c':
2839047f369cy				if (1 != sscanf(tag, "ct.%d", &si) ||
2840047f369cy				    si != ci ||
2841047f369cy				    1 != sscanf(val, "%d", &mon->count)
2842047f369cy				    || mon->count < 1)
2843047f369cy					goto nomatch;
2844047f369cy				MGOT(MRU_GOT_COUNT);
2845047f369cy				break;
2846047f369cy
2847047f369cy			case 'm':
2848047f369cy				if (1 != sscanf(tag, "mv.%d", &si) ||
2849047f369cy				    si != ci ||
2850047f369cy				    1 != sscanf(val, "%d", &mv))
2851047f369cy					goto nomatch;
2852047f369cy				mon->mode = PKT_MODE(mv);
2853047f369cy				mon->ver = PKT_VERSION(mv);
2854047f369cy				MGOT(MRU_GOT_MV);
2855047f369cy				break;
2856047f369cy
2857047f369cy			case 'r':
2858047f369cy				if (1 != sscanf(tag, "rs.%d", &si) ||
2859047f369cy				    si != ci ||
2860047f369cy				    1 != sscanf(val, "0x%hx", &mon->rs))
2861047f369cy					goto nomatch;
2862047f369cy				MGOT(MRU_GOT_RS);
2863047f369cy				break;
2864047f369cy
2865047f369cy			default:
2866837c91fcy			nomatch:
2867047f369cy				/* empty stmt */ ;
2868047f369cy				/* ignore unknown tags */
2869047f369cy			}
2870047f369cy		}
2871047f369cy		if (have_now)
2872047f369cy			list_complete = TRUE;
2873047f369cy		if (list_complete) {
2874aae1e7dglebius			INSIST(0 == ri || have_addr_older);
2875047f369cy		}
2876047f369cy		if (mrulist_interrupted) {
2877047f369cy			printf("mrulist retrieval interrupted by operator.\n"
2878047f369cy			       "Displaying partial client list.\n");
2879047f369cy			fflush(stdout);
2880047f369cy		}
2881047f369cy		if (list_complete || mrulist_interrupted) {
2882837c91fcy			xprintf(stderr,
2883047f369cy				"\rRetrieved %u unique MRU entries and %u updates.\n",
2884047f369cy				mru_count, mru_dupes);
2885047f369cy			fflush(stderr);
2886047f369cy			break;
2887047f369cy		}
2888047f369cy		if (time(NULL) >= next_report) {
2889047f369cy			next_report += MRU_REPORT_SECS;
2890837c91fcy			xprintf(stderr, "\r%u (%u updates) ", mru_count,
2891047f369cy				mru_dupes);
2892047f369cy			f