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