1/*
2 * ntpq_ops.c - subroutines which are called to perform operations by ntpq
3 */
4
5#include <stdio.h>
6#include <ctype.h>
7#include <sys/types.h>
8#include <sys/time.h>
9
10#include "ntpq.h"
11#include "ntp_stdlib.h"
12
13extern char *	chosts[];
14extern char currenthost[];
15extern int	numhosts;
16int 	maxhostlen;
17
18/*
19 * Declarations for command handlers in here
20 */
21static	int checkassocid	P((u_int32));
22static	char *	strsave 	P((char *));
23static	struct varlist *findlistvar P((struct varlist *, char *));
24static	void	doaddvlist	P((struct varlist *, char *));
25static	void	dormvlist	P((struct varlist *, char *));
26static	void	doclearvlist	P((struct varlist *));
27static	void	makequerydata	P((struct varlist *, int *, char *));
28static	int doquerylist P((struct varlist *, int, int, int, u_short *, int *, char **));
29static	void	doprintvlist	P((struct varlist *, FILE *));
30static	void	addvars 	P((struct parse *, FILE *));
31static	void	rmvars		P((struct parse *, FILE *));
32static	void	clearvars	P((struct parse *, FILE *));
33static	void	showvars	P((struct parse *, FILE *));
34static	int dolist		P((struct varlist *, int, int, int, FILE *));
35static	void	readlist	P((struct parse *, FILE *));
36static	void	writelist	P((struct parse *, FILE *));
37static	void	readvar 	P((struct parse *, FILE *));
38static	void	writevar	P((struct parse *, FILE *));
39static	void	clocklist	P((struct parse *, FILE *));
40static	void	clockvar	P((struct parse *, FILE *));
41static	int findassidrange	P((u_int32, u_int32, int *, int *));
42static	void	mreadlist	P((struct parse *, FILE *));
43static	void	mreadvar	P((struct parse *, FILE *));
44static	int dogetassoc	P((FILE *));
45static	void	printassoc	P((int, FILE *));
46static	void	associations	P((struct parse *, FILE *));
47static	void	lassociations	P((struct parse *, FILE *));
48static	void	passociations	P((struct parse *, FILE *));
49static	void	lpassociations	P((struct parse *, FILE *));
50
51#ifdef	UNUSED
52static	void	radiostatus P((struct parse *, FILE *));
53#endif	/* UNUSED */
54
55static	void	pstatus 	P((struct parse *, FILE *));
56static	long	when		P((l_fp *, l_fp *, l_fp *));
57static	char *	prettyinterval	P((char *, long));
58static	int doprintpeers	P((struct varlist *, int, int, int, char *, FILE *, int));
59static	int dogetpeers	P((struct varlist *, int, FILE *, int));
60static	void	dopeers 	P((int, FILE *, int));
61static	void	peers		P((struct parse *, FILE *));
62static	void	lpeers		P((struct parse *, FILE *));
63static	void	doopeers	P((int, FILE *, int));
64static	void	opeers		P((struct parse *, FILE *));
65static	void	lopeers 	P((struct parse *, FILE *));
66
67
68/*
69 * Commands we understand.	Ntpdc imports this.
70 */
71struct xcmd opcmds[] = {
72	{ "associations", associations, {  NO, NO, NO, NO },
73	  { "", "", "", "" },
74	  "print list of association ID's and statuses for the server's peers" },
75	{ "passociations", passociations,   {  NO, NO, NO, NO },
76	  { "", "", "", "" },
77	  "print list of associations returned by last associations command" },
78	{ "lassociations", lassociations,   {  NO, NO, NO, NO },
79	  { "", "", "", "" },
80	  "print list of associations including all client information" },
81	{ "lpassociations", lpassociations, {  NO, NO, NO, NO },
82	  { "", "", "", "" },
83	  "print last obtained list of associations, including client information" },
84	{ "addvars",    addvars,    { NTP_STR, NO, NO, NO },
85	  { "name[=value][,...]", "", "", "" },
86	  "add variables to the variable list or change their values" },
87	{ "rmvars", rmvars,     { NTP_STR, NO, NO, NO },
88	  { "name[,...]", "", "", "" },
89	  "remove variables from the variable list" },
90	{ "clearvars",  clearvars,  { NO, NO, NO, NO },
91	  { "", "", "", "" },
92	  "remove all variables from the variable list" },
93	{ "showvars",   showvars,   { NO, NO, NO, NO },
94	  { "", "", "", "" },
95	  "print variables on the variable list" },
96	{ "readlist",   readlist,   { OPT|NTP_UINT, NO, NO, NO },
97	  { "assocID", "", "", "" },
98	  "read the system or peer variables included in the variable list" },
99	{ "rl",     readlist,   { OPT|NTP_UINT, NO, NO, NO },
100	  { "assocID", "", "", "" },
101	  "read the system or peer variables included in the variable list" },
102	{ "writelist",  writelist,  { OPT|NTP_UINT, NO, NO, NO },
103	  { "assocID", "", "", "" },
104	  "write the system or peer variables included in the variable list" },
105	{ "readvar",    readvar,    { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
106	  { "assocID", "name=value[,...]", "", "" },
107	  "read system or peer variables" },
108	{ "rv",     readvar,    { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
109	  { "assocID", "name=value[,...]", "", "" },
110	  "read system or peer variables" },
111	{ "writevar",   writevar,   { NTP_UINT, NTP_STR, NO, NO },
112	  { "assocID", "name=value,[...]", "", "" },
113	  "write system or peer variables" },
114	{ "mreadlist",  mreadlist,  { NTP_UINT, NTP_UINT, NO, NO },
115	  { "assocID", "assocID", "", "" },
116	  "read the peer variables in the variable list for multiple peers" },
117	{ "mrl",    mreadlist,  { NTP_UINT, NTP_UINT, NO, NO },
118	  { "assocID", "assocID", "", "" },
119	  "read the peer variables in the variable list for multiple peers" },
120	{ "mreadvar",   mreadvar,   { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
121	  { "assocID", "assocID", "name=value[,...]", "" },
122	  "read peer variables from multiple peers" },
123	{ "mrv",    mreadvar,   { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
124	  { "assocID", "assocID", "name=value[,...]", "" },
125	  "read peer variables from multiple peers" },
126	{ "clocklist",  clocklist,  { OPT|NTP_UINT, NO, NO, NO },
127	  { "assocID", "", "", "" },
128	  "read the clock variables included in the variable list" },
129	{ "cl",     clocklist,  { OPT|NTP_UINT, NO, NO, NO },
130	  { "assocID", "", "", "" },
131	  "read the clock variables included in the variable list" },
132	{ "clockvar",   clockvar,   { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
133	  { "assocID", "name=value[,...]", "", "" },
134	  "read clock variables" },
135	{ "cv",     clockvar,   { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
136	  { "assocID", "name=value[,...]", "", "" },
137	  "read clock variables" },
138	{ "pstatus",    pstatus,    { NTP_UINT, NO, NO, NO },
139	  { "assocID", "", "", "" },
140	  "print status information returned for a peer" },
141	{ "peers",  peers,      { OPT|IP_VERSION, NO, NO, NO },
142	  { "-4|-6", "", "", "" },
143	  "obtain and print a list of the server's peers [IP version]" },
144	{ "lpeers", lpeers,     { OPT|IP_VERSION, NO, NO, NO },
145	  { "-4|-6", "", "", "" },
146	  "obtain and print a list of all peers and clients [IP version]" },
147	{ "opeers", opeers,     { OPT|IP_VERSION, NO, NO, NO },
148	  { "-4|-6", "", "", "" },
149	  "print peer list the old way, with dstadr shown rather than refid [IP version]" },
150	{ "lopeers", lopeers,   { OPT|IP_VERSION, NO, NO, NO },
151	  { "-4|-6", "", "", "" },
152	  "obtain and print a list of all peers and clients showing dstadr [IP version]" },
153	{ 0,		0,		{ NO, NO, NO, NO },
154	  { "-4|-6", "", "", "" }, "" }
155};
156
157
158/*
159 * Variable list data space
160 */
161#define MAXLIST 	64	/* maximum number of variables in list */
162#define LENHOSTNAME 256 /* host name is 256 characters long */
163/*
164 * Old CTL_PST defines for version 2.
165 */
166#define OLD_CTL_PST_CONFIG			0x80
167#define OLD_CTL_PST_AUTHENABLE		0x40
168#define OLD_CTL_PST_AUTHENTIC		0x20
169#define OLD_CTL_PST_REACH			0x10
170#define OLD_CTL_PST_SANE			0x08
171#define OLD_CTL_PST_DISP			0x04
172#define OLD_CTL_PST_SEL_REJECT		0
173#define OLD_CTL_PST_SEL_SELCAND 	1
174#define OLD_CTL_PST_SEL_SYNCCAND	2
175#define OLD_CTL_PST_SEL_SYSPEER 	3
176
177
178char flash2[] = " .+*    "; /* flash decode for version 2 */
179char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
180
181struct varlist {
182	char *name;
183	char *value;
184} varlist[MAXLIST] = { { 0, 0 } };
185
186/*
187 * Imported from ntpq.c
188 */
189extern int showhostnames;
190extern int rawmode;
191extern struct servent *server_entry;
192extern struct association assoc_cache[];
193extern int numassoc;
194extern u_char pktversion;
195extern struct ctl_var peer_var[];
196
197/*
198 * For quick string comparisons
199 */
200#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
201
202
203/*
204 * checkassocid - return the association ID, checking to see if it is valid
205 */
206static int
207checkassocid(
208	u_int32 value
209	)
210{
211	if (value == 0 || value >= 65536) {
212		(void) fprintf(stderr, "***Invalid association ID specified\n");
213		return 0;
214	}
215	return (int)value;
216}
217
218
219/*
220 * strsave - save a string
221 * XXX - should be in libntp.a
222 */
223static char *
224strsave(
225	char *str
226	)
227{
228	char *cp;
229	u_int len;
230
231	len = strlen(str) + 1;
232	if ((cp = (char *)malloc(len)) == NULL) {
233		(void) fprintf(stderr, "Malloc failed!!\n");
234		exit(1);
235	}
236
237	memmove(cp, str, len);
238	return (cp);
239}
240
241
242/*
243 * findlistvar - look for the named variable in a list and return if found
244 */
245static struct varlist *
246findlistvar(
247	struct varlist *list,
248	char *name
249	)
250{
251	register struct varlist *vl;
252
253	for (vl = list; vl < list + MAXLIST && vl->name != 0; vl++)
254		if (STREQ(name, vl->name))
255		return vl;
256	if (vl < list + MAXLIST)
257		return vl;
258	return (struct varlist *)0;
259}
260
261
262/*
263 * doaddvlist - add variable(s) to the variable list
264 */
265static void
266doaddvlist(
267	struct varlist *vlist,
268	char *vars
269	)
270{
271	register struct varlist *vl;
272	int len;
273	char *name;
274	char *value;
275
276	len = strlen(vars);
277	while (nextvar(&len, &vars, &name, &value)) {
278		vl = findlistvar(vlist, name);
279		if (vl == 0) {
280			(void) fprintf(stderr, "Variable list full\n");
281			return;
282		}
283
284		if (vl->name == 0) {
285			vl->name = strsave(name);
286		} else if (vl->value != 0) {
287			free(vl->value);
288			vl->value = 0;
289		}
290
291		if (value != 0)
292			vl->value = strsave(value);
293	}
294}
295
296
297/*
298 * dormvlist - remove variable(s) from the variable list
299 */
300static void
301dormvlist(
302	struct varlist *vlist,
303	char *vars
304	)
305{
306	register struct varlist *vl;
307	int len;
308	char *name;
309	char *value;
310
311	len = strlen(vars);
312	while (nextvar(&len, &vars, &name, &value)) {
313		vl = findlistvar(vlist, name);
314		if (vl == 0 || vl->name == 0) {
315			(void) fprintf(stderr, "Variable `%s' not found\n",
316				       name);
317		} else {
318			free((void *)vl->name);
319			if (vl->value != 0)
320			    free(vl->value);
321			for ( ; (vl+1) < (varlist+MAXLIST)
322				      && (vl+1)->name != 0; vl++) {
323				vl->name = (vl+1)->name;
324				vl->value = (vl+1)->value;
325			}
326			vl->name = vl->value = 0;
327		}
328	}
329}
330
331
332/*
333 * doclearvlist - clear a variable list
334 */
335static void
336doclearvlist(
337	struct varlist *vlist
338	)
339{
340	register struct varlist *vl;
341
342	for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
343		free((void *)vl->name);
344		vl->name = 0;
345		if (vl->value != 0) {
346			free(vl->value);
347			vl->value = 0;
348		}
349	}
350}
351
352
353/*
354 * makequerydata - form a data buffer to be included with a query
355 */
356static void
357makequerydata(
358	struct varlist *vlist,
359	int *datalen,
360	char *data
361	)
362{
363	register struct varlist *vl;
364	register char *cp, *cpend;
365	register int namelen, valuelen;
366	register int totallen;
367
368	cp = data;
369	cpend = data + *datalen;
370
371	for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
372		namelen = strlen(vl->name);
373		if (vl->value == 0)
374			valuelen = 0;
375		else
376			valuelen = strlen(vl->value);
377		totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
378		if (cp + totallen > cpend)
379			break;
380
381		if (cp != data)
382			*cp++ = ',';
383		memmove(cp, vl->name, (unsigned)namelen);
384		cp += namelen;
385		if (valuelen != 0) {
386			*cp++ = '=';
387			memmove(cp, vl->value, (unsigned)valuelen);
388			cp += valuelen;
389		}
390	}
391	*datalen = cp - data;
392}
393
394
395/*
396 * doquerylist - send a message including variables in a list
397 */
398static int
399doquerylist(
400	struct varlist *vlist,
401	int op,
402	int associd,
403	int auth,
404	u_short *rstatus,
405	int *dsize,
406	char **datap
407	)
408{
409	char data[CTL_MAX_DATA_LEN];
410	int datalen;
411
412	datalen = sizeof(data);
413	makequerydata(vlist, &datalen, data);
414
415	return doquery(op, associd, auth, datalen, data, rstatus,
416			   dsize, datap);
417}
418
419
420/*
421 * doprintvlist - print the variables on a list
422 */
423static void
424doprintvlist(
425	struct varlist *vlist,
426	FILE *fp
427	)
428{
429	register struct varlist *vl;
430
431	if (vlist->name == 0) {
432		(void) fprintf(fp, "No variables on list\n");
433	} else {
434		for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
435			if (vl->value == 0) {
436				(void) fprintf(fp, "%s\n", vl->name);
437			} else {
438				(void) fprintf(fp, "%s=%s\n",
439						   vl->name, vl->value);
440			}
441		}
442	}
443}
444
445
446/*
447 * addvars - add variables to the variable list
448 */
449/*ARGSUSED*/
450static void
451addvars(
452	struct parse *pcmd,
453	FILE *fp
454	)
455{
456	doaddvlist(varlist, pcmd->argval[0].string);
457}
458
459
460/*
461 * rmvars - remove variables from the variable list
462 */
463/*ARGSUSED*/
464static void
465rmvars(
466	struct parse *pcmd,
467	FILE *fp
468	)
469{
470	dormvlist(varlist, pcmd->argval[0].string);
471}
472
473
474/*
475 * clearvars - clear the variable list
476 */
477/*ARGSUSED*/
478static void
479clearvars(
480	struct parse *pcmd,
481	FILE *fp
482	)
483{
484	doclearvlist(varlist);
485}
486
487
488/*
489 * showvars - show variables on the variable list
490 */
491/*ARGSUSED*/
492static void
493showvars(
494	struct parse *pcmd,
495	FILE *fp
496	)
497{
498	doprintvlist(varlist, fp);
499}
500
501
502/*
503 * dolist - send a request with the given list of variables
504 */
505static int
506dolist(
507	struct varlist *vlist,
508	int associd,
509	int op,
510	int type,
511	FILE *fp
512	)
513{
514	char *datap;
515	int res;
516	int dsize;
517	u_short rstatus;
518
519	res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
520
521	if (res != 0)
522		return 0;
523
524	if (numhosts > 1)
525		(void) fprintf(fp, "server=%s ", currenthost);
526	if (dsize == 0) {
527		if (associd == 0)
528			(void) fprintf(fp, "No system%s variables returned\n",
529				   (type == TYPE_CLOCK) ? " clock" : "");
530		else
531			(void) fprintf(fp,
532				   "No information returned for%s association %u\n",
533				   (type == TYPE_CLOCK) ? " clock" : "", associd);
534		return 1;
535	}
536
537	(void) fprintf(fp,"assID=%d ",associd);
538	printvars(dsize, datap, (int)rstatus, type, fp);
539	return 1;
540}
541
542
543/*
544 * readlist - send a read variables request with the variables on the list
545 */
546static void
547readlist(
548	struct parse *pcmd,
549	FILE *fp
550	)
551{
552	int associd;
553
554	if (pcmd->nargs == 0) {
555		associd = 0;
556	} else {
557	  /* HMS: I think we want the u_int32 target here, not the u_long */
558		if (pcmd->argval[0].uval == 0)
559			associd = 0;
560		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
561			return;
562	}
563
564	(void) dolist(varlist, associd, CTL_OP_READVAR,
565			  (associd == 0) ? TYPE_SYS : TYPE_PEER, fp);
566}
567
568
569/*
570 * writelist - send a write variables request with the variables on the list
571 */
572static void
573writelist(
574	struct parse *pcmd,
575	FILE *fp
576	)
577{
578	char *datap;
579	int res;
580	int associd;
581	int dsize;
582	u_short rstatus;
583
584	if (pcmd->nargs == 0) {
585		associd = 0;
586	} else {
587		/* HMS: Do we really want uval here? */
588		if (pcmd->argval[0].uval == 0)
589			associd = 0;
590		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
591			return;
592	}
593
594	res = doquerylist(varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
595			  &dsize, &datap);
596
597	if (res != 0)
598		return;
599
600	if (numhosts > 1)
601		(void) fprintf(fp, "server=%s ", currenthost);
602	if (dsize == 0)
603		(void) fprintf(fp, "done! (no data returned)\n");
604	else {
605		(void) fprintf(fp,"assID=%d ",associd);
606		printvars(dsize, datap, (int)rstatus,
607			  (associd != 0) ? TYPE_PEER : TYPE_SYS, fp);
608	}
609	return;
610}
611
612
613/*
614 * readvar - send a read variables request with the specified variables
615 */
616static void
617readvar(
618	struct parse *pcmd,
619	FILE *fp
620	)
621{
622	int associd;
623	struct varlist tmplist[MAXLIST];
624
625	/* HMS: uval? */
626	if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
627		associd = 0;
628	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
629		return;
630
631	memset((char *)tmplist, 0, sizeof(tmplist));
632	if (pcmd->nargs >= 2)
633		doaddvlist(tmplist, pcmd->argval[1].string);
634
635	(void) dolist(tmplist, associd, CTL_OP_READVAR,
636			  (associd == 0) ? TYPE_SYS : TYPE_PEER, fp);
637
638	doclearvlist(tmplist);
639}
640
641
642/*
643 * writevar - send a write variables request with the specified variables
644 */
645static void
646writevar(
647	struct parse *pcmd,
648	FILE *fp
649	)
650{
651	char *datap;
652	int res;
653	int associd;
654	int dsize;
655	u_short rstatus;
656	struct varlist tmplist[MAXLIST];
657
658	/* HMS: uval? */
659	if (pcmd->argval[0].uval == 0)
660		associd = 0;
661	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
662		return;
663
664	memset((char *)tmplist, 0, sizeof(tmplist));
665	doaddvlist(tmplist, pcmd->argval[1].string);
666
667	res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
668			  &dsize, &datap);
669
670	doclearvlist(tmplist);
671
672	if (res != 0)
673		return;
674
675	if (numhosts > 1)
676		(void) fprintf(fp, "server=%s ", currenthost);
677	if (dsize == 0)
678		(void) fprintf(fp, "done! (no data returned)\n");
679	else {
680		(void) fprintf(fp,"assID=%d ",associd);
681		printvars(dsize, datap, (int)rstatus,
682			  (associd != 0) ? TYPE_PEER : TYPE_SYS, fp);
683	}
684	return;
685}
686
687
688/*
689 * clocklist - send a clock variables request with the variables on the list
690 */
691static void
692clocklist(
693	struct parse *pcmd,
694	FILE *fp
695	)
696{
697	int associd;
698
699	/* HMS: uval? */
700	if (pcmd->nargs == 0) {
701		associd = 0;
702	} else {
703		if (pcmd->argval[0].uval == 0)
704			associd = 0;
705		else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
706			return;
707	}
708
709	(void) dolist(varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
710}
711
712
713/*
714 * clockvar - send a clock variables request with the specified variables
715 */
716static void
717clockvar(
718	struct parse *pcmd,
719	FILE *fp
720	)
721{
722	int associd;
723	struct varlist tmplist[MAXLIST];
724
725	/* HMS: uval? */
726	if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
727		associd = 0;
728	else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
729		return;
730
731	memset((char *)tmplist, 0, sizeof(tmplist));
732	if (pcmd->nargs >= 2)
733		doaddvlist(tmplist, pcmd->argval[1].string);
734
735	(void) dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
736
737	doclearvlist(tmplist);
738}
739
740
741/*
742 * findassidrange - verify a range of association ID's
743 */
744static int
745findassidrange(
746	u_int32 assid1,
747	u_int32 assid2,
748	int *from,
749	int *to
750	)
751{
752	register int i;
753	int f, t;
754
755	if (assid1 == 0 || assid1 > 65535) {
756		(void) fprintf(stderr,
757				   "***Invalid association ID %lu specified\n", (u_long)assid1);
758		return 0;
759	}
760
761	if (assid2 == 0 || assid2 > 65535) {
762		(void) fprintf(stderr,
763				   "***Invalid association ID %lu specified\n", (u_long)assid2);
764		return 0;
765	}
766
767	f = t = -1;
768	for (i = 0; i < numassoc; i++) {
769		if (assoc_cache[i].assid == assid1) {
770			f = i;
771			if (t != -1)
772				break;
773		}
774		if (assoc_cache[i].assid == assid2) {
775			t = i;
776			if (f != -1)
777				break;
778		}
779	}
780
781	if (f == -1 || t == -1) {
782		(void) fprintf(stderr,
783				   "***Association ID %lu not found in list\n",
784				   (f == -1) ? (u_long)assid1 : (u_long)assid2);
785		return 0;
786	}
787
788	if (f < t) {
789		*from = f;
790		*to = t;
791	} else {
792		*from = t;
793		*to = f;
794	}
795	return 1;
796}
797
798
799
800/*
801 * mreadlist - send a read variables request for multiple associations
802 */
803static void
804mreadlist(
805	struct parse *pcmd,
806	FILE *fp
807	)
808{
809	int i;
810	int from;
811	int to;
812
813	/* HMS: uval? */
814	if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
815				&from, &to))
816		return;
817
818	for (i = from; i <= to; i++) {
819		if (i != from)
820			(void) fprintf(fp, "\n");
821		if (!dolist(varlist, (int)assoc_cache[i].assid,
822				CTL_OP_READVAR, TYPE_PEER, fp))
823			return;
824	}
825	return;
826}
827
828
829/*
830 * mreadvar - send a read variables request for multiple associations
831 */
832static void
833mreadvar(
834	struct parse *pcmd,
835	FILE *fp
836	)
837{
838	int i;
839	int from;
840	int to;
841	struct varlist tmplist[MAXLIST];
842
843	/* HMS: uval? */
844	if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
845				&from, &to))
846		return;
847
848	memset((char *)tmplist, 0, sizeof(tmplist));
849	if (pcmd->nargs >= 3)
850		doaddvlist(tmplist, pcmd->argval[2].string);
851
852	for (i = from; i <= to; i++) {
853		if (i != from)
854			(void) fprintf(fp, "\n");
855		if (!dolist(varlist, (int)assoc_cache[i].assid,
856				CTL_OP_READVAR, TYPE_PEER, fp))
857			break;
858	}
859	doclearvlist(tmplist);
860	return;
861}
862
863
864/*
865 * dogetassoc - query the host for its list of associations
866 */
867static int
868dogetassoc(
869	FILE *fp
870	)
871{
872	char *datap;
873	int res;
874	int dsize;
875	u_short rstatus;
876
877	res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
878			  &dsize, &datap);
879
880	if (res != 0)
881		return 0;
882
883	if (dsize == 0) {
884		if (numhosts > 1)
885			(void) fprintf(fp, "server=%s ", currenthost);
886		(void) fprintf(fp, "No association ID's returned\n");
887		return 0;
888	}
889
890	if (dsize & 0x3) {
891		if (numhosts > 1)
892			(void) fprintf(stderr, "server=%s ", currenthost);
893		(void) fprintf(stderr,
894				   "***Server returned %d octets, should be multiple of 4\n",
895				   dsize);
896		return 0;
897	}
898
899	numassoc = 0;
900	while (dsize > 0) {
901		assoc_cache[numassoc].assid = ntohs(*((u_short *)datap));
902		datap += sizeof(u_short);
903		assoc_cache[numassoc].status = ntohs(*((u_short *)datap));
904		datap += sizeof(u_short);
905		if (++numassoc >= MAXASSOC)
906			break;
907		dsize -= sizeof(u_short) + sizeof(u_short);
908	}
909	sortassoc();
910	return 1;
911}
912
913
914/*
915 * printassoc - print the current list of associations
916 */
917static void
918printassoc(
919	int showall,
920	FILE *fp
921	)
922{
923	register char *bp;
924	int i;
925	u_char statval;
926	int event;
927	u_long event_count;
928	const char *conf;
929	const char *reach;
930	const char *auth;
931	const char *condition = "";
932	const char *last_event;
933	const char *cnt;
934	char buf[128];
935
936	if (numassoc == 0) {
937		(void) fprintf(fp, "No association ID's in list\n");
938		return;
939	}
940
941	/*
942	 * Output a header
943	 */
944	(void) fprintf(fp,
945			   "\nind assID status  conf reach auth condition  last_event cnt\n");
946	(void) fprintf(fp,
947			   "===========================================================\n");
948	for (i = 0; i < numassoc; i++) {
949		statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status);
950		if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
951			continue;
952		event = CTL_PEER_EVENT(assoc_cache[i].status);
953		event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
954		if (statval & CTL_PST_CONFIG)
955			conf = "yes";
956		else
957			conf = "no";
958		if (statval & CTL_PST_REACH || 1) {
959			reach = "yes";
960			if (statval & CTL_PST_AUTHENABLE) {
961				if (statval & CTL_PST_AUTHENTIC)
962					auth = "ok ";
963				else
964					auth = "bad";
965			} else
966				auth = "none";
967
968			if (pktversion > NTP_OLDVERSION)
969				switch (statval & 0x7) {
970				case CTL_PST_SEL_REJECT:
971					condition = "reject";
972					break;
973				case CTL_PST_SEL_SANE:
974					condition = "falsetick";
975					break;
976				case CTL_PST_SEL_CORRECT:
977					condition = "excess";
978					break;
979				case CTL_PST_SEL_SELCAND:
980					condition = "outlyer";
981					break;
982				case CTL_PST_SEL_SYNCCAND:
983					condition = "candidat";
984					break;
985				case CTL_PST_SEL_DISTSYSPEER:
986					condition = "selected";
987					break;
988				case CTL_PST_SEL_SYSPEER:
989					condition = "sys.peer";
990					break;
991				case CTL_PST_SEL_PPS:
992					condition = "pps.peer";
993					break;
994				}
995			else
996				switch (statval & 0x3) {
997				case OLD_CTL_PST_SEL_REJECT:
998					if (!(statval & OLD_CTL_PST_SANE))
999					condition = "insane";
1000					else if (!(statval & OLD_CTL_PST_DISP))
1001					condition = "hi_disp";
1002					else
1003					condition = "";
1004					break;
1005				case OLD_CTL_PST_SEL_SELCAND:
1006					condition = "sel_cand";
1007					break;
1008				case OLD_CTL_PST_SEL_SYNCCAND:
1009					condition = "sync_cand";
1010					break;
1011				case OLD_CTL_PST_SEL_SYSPEER:
1012					condition = "sys_peer";
1013					break;
1014				}
1015
1016		} else {
1017			reach = "no";
1018			auth = condition = "";
1019		}
1020
1021		switch (PEER_EVENT|event) {
1022			case EVNT_PEERIPERR:
1023			last_event = "IP error";
1024			break;
1025			case EVNT_PEERAUTH:
1026			last_event = "auth fail";
1027			break;
1028			case EVNT_UNREACH:
1029			last_event = "lost reach";
1030			break;
1031			case EVNT_REACH:
1032			last_event = "reachable";
1033			break;
1034			case EVNT_PEERCLOCK:
1035			last_event = "clock expt";
1036			break;
1037#if 0
1038			case EVNT_PEERSTRAT:
1039			last_event = "stratum chg";
1040			break;
1041#endif
1042			default:
1043			last_event = "";
1044			break;
1045		}
1046
1047		if (event_count != 0)
1048			cnt = uinttoa(event_count);
1049		else
1050			cnt = "";
1051		(void) sprintf(buf,
1052				   "%3d %5u  %04x   %3.3s  %4s  %4.4s %9.9s %11s %2s",
1053				   i+1, assoc_cache[i].assid, assoc_cache[i].status,
1054				   conf, reach, auth, condition, last_event, cnt);
1055		bp = &buf[strlen(buf)];
1056		while (bp > buf && *(bp-1) == ' ')
1057			*(--bp) = '\0';
1058		(void) fprintf(fp, "%s\n", buf);
1059	}
1060}
1061
1062
1063
1064/*
1065 * associations - get, record and print a list of associations
1066 */
1067/*ARGSUSED*/
1068static void
1069associations(
1070	struct parse *pcmd,
1071	FILE *fp
1072	)
1073{
1074	if (dogetassoc(fp))
1075		printassoc(0, fp);
1076}
1077
1078
1079/*
1080 * lassociations - get, record and print a long list of associations
1081 */
1082/*ARGSUSED*/
1083static void
1084lassociations(
1085	struct parse *pcmd,
1086	FILE *fp
1087	)
1088{
1089	if (dogetassoc(fp))
1090		printassoc(1, fp);
1091}
1092
1093
1094/*
1095 * passociations - print the association list
1096 */
1097/*ARGSUSED*/
1098static void
1099passociations(
1100	struct parse *pcmd,
1101	FILE *fp
1102	)
1103{
1104	printassoc(0, fp);
1105}
1106
1107
1108/*
1109 * lpassociations - print the long association list
1110 */
1111/*ARGSUSED*/
1112static void
1113lpassociations(
1114	struct parse *pcmd,
1115	FILE *fp
1116	)
1117{
1118	printassoc(1, fp);
1119}
1120
1121
1122#ifdef	UNUSED
1123/*
1124 * radiostatus - print the radio status returned by the server
1125 */
1126/*ARGSUSED*/
1127static void
1128radiostatus(
1129	struct parse *pcmd,
1130	FILE *fp
1131	)
1132{
1133	char *datap;
1134	int res;
1135	int dsize;
1136	u_short rstatus;
1137
1138	res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
1139			  &dsize, &datap);
1140
1141	if (res != 0)
1142		return;
1143
1144	if (numhosts > 1)
1145		(void) fprintf(fp, "server=%s ", currenthost);
1146	if (dsize == 0) {
1147		(void) fprintf(fp, "No radio status string returned\n");
1148		return;
1149	}
1150
1151	asciize(dsize, datap, fp);
1152}
1153#endif	/* UNUSED */
1154
1155/*
1156 * pstatus - print peer status returned by the server
1157 */
1158static void
1159pstatus(
1160	struct parse *pcmd,
1161	FILE *fp
1162	)
1163{
1164	char *datap;
1165	int res;
1166	int associd;
1167	int dsize;
1168	u_short rstatus;
1169
1170	/* HMS: uval? */
1171	if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
1172		return;
1173
1174	res = doquery(CTL_OP_READSTAT, associd, 0, 0, (char *)0, &rstatus,
1175			  &dsize, &datap);
1176
1177	if (res != 0)
1178		return;
1179
1180	if (numhosts > 1)
1181		(void) fprintf(fp, "server=%s ", currenthost);
1182	if (dsize == 0) {
1183		(void) fprintf(fp,
1184				   "No information returned for association %u\n",
1185				   associd);
1186		return;
1187	}
1188
1189	(void) fprintf(fp,"assID=%d ",associd);
1190	printvars(dsize, datap, (int)rstatus, TYPE_PEER, fp);
1191}
1192
1193
1194/*
1195 * when - print how long its been since his last packet arrived
1196 */
1197static long
1198when(
1199	l_fp *ts,
1200	l_fp *rec,
1201	l_fp *reftime
1202	)
1203{
1204	l_fp *lasttime;
1205
1206	if (rec->l_ui != 0)
1207		lasttime = rec;
1208	else if (reftime->l_ui != 0)
1209		lasttime = reftime;
1210	else
1211		return 0;
1212
1213	return (ts->l_ui - lasttime->l_ui);
1214}
1215
1216
1217/*
1218 * Pretty-print an interval into the given buffer, in a human-friendly format.
1219 */
1220static char *
1221prettyinterval(
1222	char *buf,
1223	long diff
1224	)
1225{
1226	if (diff <= 0) {
1227		buf[0] = '-';
1228		buf[1] = 0;
1229		return buf;
1230	}
1231
1232	if (diff <= 2048) {
1233		(void) sprintf(buf, "%ld", (long int)diff);
1234		return buf;
1235	}
1236
1237	diff = (diff + 29) / 60;
1238	if (diff <= 300) {
1239		(void) sprintf(buf, "%ldm", (long int)diff);
1240		return buf;
1241	}
1242
1243	diff = (diff + 29) / 60;
1244	if (diff <= 96) {
1245		(void) sprintf(buf, "%ldh", (long int)diff);
1246		return buf;
1247	}
1248
1249	diff = (diff + 11) / 24;
1250	(void) sprintf(buf, "%ldd", (long int)diff);
1251	return buf;
1252}
1253
1254static char
1255decodeaddrtype(
1256	struct sockaddr_storage *sock
1257	)
1258{
1259	char ch = '-';
1260	u_int32 dummy;
1261	struct sockaddr_in6 *sin6;
1262
1263	switch(sock->ss_family) {
1264	case AF_INET:
1265		dummy = ((struct sockaddr_in *)sock)->sin_addr.s_addr;
1266		dummy = ntohl(dummy);
1267		ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' :
1268			((dummy&0x000000ff)==0x000000ff) ? 'b' :
1269			((dummy&0xffffffff)==0x7f000001) ? 'l' :
1270			((dummy&0xffffffe0)==0x00000000) ? '-' :
1271			'u');
1272		break;
1273	case AF_INET6:
1274		sin6 = (struct sockaddr_in6 *)sock;
1275		if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
1276			ch = 'm';
1277		else
1278			ch = 'u';
1279		break;
1280	default:
1281		ch = '-';
1282		break;
1283	}
1284	return ch;
1285}
1286
1287/*
1288 * A list of variables required by the peers command
1289 */
1290struct varlist opeervarlist[] = {
1291	{ "srcadr", 0 },    /* 0 */
1292	{ "dstadr", 0 },    /* 1 */
1293	{ "stratum",    0 },    /* 2 */
1294	{ "hpoll",  0 },    /* 3 */
1295	{ "ppoll",  0 },    /* 4 */
1296	{ "reach",  0 },    /* 5 */
1297	{ "delay",  0 },    /* 6 */
1298	{ "offset", 0 },    /* 7 */
1299	{ "jitter", 0 },    /* 8 */
1300	{ "dispersion", 0 },    /* 9 */
1301	{ "rec",    0 },    /* 10 */
1302	{ "reftime",    0 },    /* 11 */
1303	{ "srcport",    0 },    /* 12 */
1304	{ 0,		0 }
1305};
1306
1307struct varlist peervarlist[] = {
1308	{ "srcadr", 0 },    /* 0 */
1309	{ "refid",  0 },    /* 1 */
1310	{ "stratum",    0 },    /* 2 */
1311	{ "hpoll",  0 },    /* 3 */
1312	{ "ppoll",  0 },    /* 4 */
1313	{ "reach",  0 },    /* 5 */
1314	{ "delay",  0 },    /* 6 */
1315	{ "offset", 0 },    /* 7 */
1316	{ "jitter", 0 },    /* 8 */
1317	{ "dispersion", 0 },    /* 9 */
1318	{ "rec",    0 },    /* 10 */
1319	{ "reftime",    0 },    /* 11 */
1320	{ "srcport",    0 },    /* 12 */
1321	{ 0,		0 }
1322};
1323
1324#define HAVE_SRCADR 0
1325#define HAVE_DSTADR 1
1326#define HAVE_REFID	1
1327#define HAVE_STRATUM	2
1328#define HAVE_HPOLL	3
1329#define HAVE_PPOLL	4
1330#define HAVE_REACH	5
1331#define HAVE_DELAY	6
1332#define HAVE_OFFSET 7
1333#define HAVE_JITTER 8
1334#define HAVE_DISPERSION 9
1335#define HAVE_REC	10
1336#define HAVE_REFTIME	11
1337#define HAVE_SRCPORT	12
1338#define MAXHAVE 	13
1339
1340/*
1341 * Decode an incoming data buffer and print a line in the peer list
1342 */
1343static int
1344doprintpeers(
1345	struct varlist *pvl,
1346	int associd,
1347	int rstatus,
1348	int datalen,
1349	char *data,
1350	FILE *fp,
1351	int af
1352	)
1353{
1354	char *name;
1355	char *value = NULL;
1356	int i;
1357	int c;
1358
1359	struct sockaddr_storage srcadr;
1360	struct sockaddr_storage dstadr;
1361	u_long srcport = 0;
1362	char *dstadr_refid = "0.0.0.0";
1363	u_long stratum = 0;
1364	long ppoll = 0;
1365	long hpoll = 0;
1366	u_long reach = 0;
1367	l_fp estoffset;
1368	l_fp estdelay;
1369	l_fp estjitter;
1370	l_fp estdisp;
1371	l_fp reftime;
1372	l_fp rec;
1373	l_fp ts;
1374	u_char havevar[MAXHAVE];
1375	u_long poll_sec;
1376	char type = '?';
1377	char refid_string[10];
1378	char whenbuf[8], pollbuf[8];
1379	char clock_name[LENHOSTNAME];
1380
1381	memset((char *)havevar, 0, sizeof(havevar));
1382	get_systime(&ts);
1383
1384	memset((char *)&srcadr, 0, sizeof(struct sockaddr_storage));
1385	memset((char *)&dstadr, 0, sizeof(struct sockaddr_storage));
1386
1387	/* Initialize by zeroing out estimate variables */
1388	memset((char *)&estoffset, 0, sizeof(l_fp));
1389	memset((char *)&estdelay, 0, sizeof(l_fp));
1390	memset((char *)&estjitter, 0, sizeof(l_fp));
1391	memset((char *)&estdisp, 0, sizeof(l_fp));
1392
1393	while (nextvar(&datalen, &data, &name, &value)) {
1394		struct sockaddr_storage dum_store;
1395
1396		i = findvar(name, peer_var, 1);
1397		if (i == 0)
1398			continue;	/* don't know this one */
1399		switch (i) {
1400			case CP_SRCADR:
1401			if (decodenetnum(value, &srcadr))
1402				havevar[HAVE_SRCADR] = 1;
1403			break;
1404			case CP_DSTADR:
1405			if (decodenetnum(value, &dum_store))
1406				type = decodeaddrtype(&dum_store);
1407			if (pvl == opeervarlist) {
1408				if (decodenetnum(value, &dstadr)) {
1409					havevar[HAVE_DSTADR] = 1;
1410					dstadr_refid = stoa(&dstadr);
1411				}
1412			}
1413			break;
1414			case CP_REFID:
1415			if (pvl == peervarlist) {
1416				havevar[HAVE_REFID] = 1;
1417				if (*value == '\0') {
1418					dstadr_refid = "0.0.0.0";
1419				} else if ((int)strlen(value) <= 4) {
1420					refid_string[0] = '.';
1421					(void) strcpy(&refid_string[1], value);
1422					i = strlen(refid_string);
1423					refid_string[i] = '.';
1424					refid_string[i+1] = '\0';
1425					dstadr_refid = refid_string;
1426				} else if (decodenetnum(value, &dstadr)) {
1427					if (SOCKNUL(&dstadr))
1428						dstadr_refid = "0.0.0.0";
1429					else if ((dstadr.ss_family == AF_INET)
1430					    && ISREFCLOCKADR(&dstadr))
1431    						dstadr_refid =
1432						    refnumtoa(&dstadr);
1433					else
1434						dstadr_refid =
1435						    stoa(&dstadr);
1436				} else {
1437					havevar[HAVE_REFID] = 0;
1438				}
1439			}
1440			break;
1441			case CP_STRATUM:
1442			if (decodeuint(value, &stratum))
1443				havevar[HAVE_STRATUM] = 1;
1444			break;
1445			case CP_HPOLL:
1446			if (decodeint(value, &hpoll)) {
1447				havevar[HAVE_HPOLL] = 1;
1448				if (hpoll < 0)
1449					hpoll = NTP_MINPOLL;
1450			}
1451			break;
1452			case CP_PPOLL:
1453			if (decodeint(value, &ppoll)) {
1454				havevar[HAVE_PPOLL] = 1;
1455				if (ppoll < 0)
1456					ppoll = NTP_MINPOLL;
1457			}
1458			break;
1459			case CP_REACH:
1460			if (decodeuint(value, &reach))
1461				havevar[HAVE_REACH] = 1;
1462			break;
1463			case CP_DELAY:
1464			if (decodetime(value, &estdelay))
1465				havevar[HAVE_DELAY] = 1;
1466			break;
1467			case CP_OFFSET:
1468			if (decodetime(value, &estoffset))
1469				havevar[HAVE_OFFSET] = 1;
1470			break;
1471			case CP_JITTER:
1472			if (pvl == peervarlist)
1473				if (decodetime(value, &estjitter))
1474					havevar[HAVE_JITTER] = 1;
1475			break;
1476			case CP_DISPERSION:
1477			if (decodetime(value, &estdisp))
1478				havevar[HAVE_DISPERSION] = 1;
1479			break;
1480			case CP_REC:
1481			if (decodets(value, &rec))
1482				havevar[HAVE_REC] = 1;
1483			break;
1484			case CP_SRCPORT:
1485			if (decodeuint(value, &srcport))
1486				havevar[HAVE_SRCPORT] = 1;
1487			break;
1488			case CP_REFTIME:
1489			havevar[HAVE_REFTIME] = 1;
1490			if (!decodets(value, &reftime))
1491				L_CLR(&reftime);
1492			break;
1493			default:
1494			break;
1495		}
1496	}
1497
1498	/*
1499	 * Check to see if the srcport is NTP's port.  If not this probably
1500	 * isn't a valid peer association.
1501	 */
1502	if (havevar[HAVE_SRCPORT] && srcport != NTP_PORT)
1503		return (1);
1504
1505	/*
1506	 * Got everything, format the line
1507	 */
1508	poll_sec = 1<<max(min3(ppoll, hpoll, NTP_MAXPOLL), NTP_MINPOLL);
1509	if (pktversion > NTP_OLDVERSION)
1510		c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
1511	else
1512		c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
1513	if (numhosts > 1)
1514		(void) fprintf(fp, "%-*s ", maxhostlen, currenthost);
1515	if (af == 0 || srcadr.ss_family == af){
1516		strcpy(clock_name, nntohost(&srcadr));
1517
1518		(void) fprintf(fp,
1519			"%c%-15.15s %-15.15s %2ld %c %4.4s %4.4s  %3lo  %7.7s %8.7s %7.7s\n",
1520			c, clock_name, dstadr_refid, stratum, type,
1521			prettyinterval(whenbuf, when(&ts, &rec, &reftime)),
1522			prettyinterval(pollbuf, (int)poll_sec), reach,
1523			lfptoms(&estdelay, 3), lfptoms(&estoffset, 3),
1524			havevar[HAVE_JITTER] ? lfptoms(&estjitter, 3) :
1525			lfptoms(&estdisp, 3));
1526		return (1);
1527	}
1528	else
1529		return(1);
1530}
1531
1532#undef	HAVE_SRCADR
1533#undef	HAVE_DSTADR
1534#undef	HAVE_STRATUM
1535#undef	HAVE_PPOLL
1536#undef	HAVE_HPOLL
1537#undef	HAVE_REACH
1538#undef	HAVE_ESTDELAY
1539#undef	HAVE_ESTOFFSET
1540#undef	HAVE_JITTER
1541#undef	HAVE_ESTDISP
1542#undef	HAVE_REFID
1543#undef	HAVE_REC
1544#undef	HAVE_SRCPORT
1545#undef	HAVE_REFTIME
1546#undef	MAXHAVE
1547
1548
1549/*
1550 * dogetpeers - given an association ID, read and print the spreadsheet
1551 *		peer variables.
1552 */
1553static int
1554dogetpeers(
1555	struct varlist *pvl,
1556	int associd,
1557	FILE *fp,
1558	int af
1559	)
1560{
1561	char *datap;
1562	int res;
1563	int dsize;
1564	u_short rstatus;
1565
1566#ifdef notdef
1567	res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
1568			  &dsize, &datap);
1569#else
1570	/*
1571	 * Damn fuzzballs
1572	 */
1573	res = doquery(CTL_OP_READVAR, associd, 0, 0, (char *)0, &rstatus,
1574			  &dsize, &datap);
1575#endif
1576
1577	if (res != 0)
1578		return 0;
1579
1580	if (dsize == 0) {
1581		if (numhosts > 1)
1582			(void) fprintf(stderr, "server=%s ", currenthost);
1583		(void) fprintf(stderr,
1584				   "***No information returned for association %d\n",
1585				   associd);
1586		return 0;
1587	}
1588
1589	return doprintpeers(pvl, associd, (int)rstatus, dsize, datap, fp, af);
1590}
1591
1592
1593/*
1594 * peers - print a peer spreadsheet
1595 */
1596static void
1597dopeers(
1598	int showall,
1599	FILE *fp,
1600	int af
1601	)
1602{
1603	register int i;
1604	char fullname[LENHOSTNAME];
1605	struct sockaddr_storage netnum;
1606
1607	if (!dogetassoc(fp))
1608		return;
1609
1610	for (i = 0; i < numhosts; ++i) {
1611		if (getnetnum(chosts[i], &netnum, fullname, af))
1612			if ((int)strlen(fullname) > maxhostlen)
1613				maxhostlen = strlen(fullname);
1614	}
1615	if (numhosts > 1)
1616		(void) fprintf(fp, "%-*.*s ", maxhostlen, maxhostlen, "server");
1617	(void) fprintf(fp,
1618			   "     remote           refid      st t when poll reach   delay   offset  jitter\n");
1619	if (numhosts > 1)
1620		for (i = 0; i <= maxhostlen; ++i)
1621		(void) fprintf(fp, "=");
1622	(void) fprintf(fp,
1623			   "==============================================================================\n");
1624
1625	for (i = 0; i < numassoc; i++) {
1626		if (!showall &&
1627			!(CTL_PEER_STATVAL(assoc_cache[i].status)
1628			  & (CTL_PST_CONFIG|CTL_PST_REACH)))
1629			continue;
1630		if (!dogetpeers(peervarlist, (int)assoc_cache[i].assid, fp, af)) {
1631			return;
1632		}
1633	}
1634	return;
1635}
1636
1637
1638/*
1639 * peers - print a peer spreadsheet
1640 */
1641/*ARGSUSED*/
1642static void
1643peers(
1644	struct parse *pcmd,
1645	FILE *fp
1646	)
1647{
1648	int af = 0;
1649
1650	if (pcmd->nargs == 1) {
1651		if (pcmd->argval->ival == 6)
1652			af = AF_INET6;
1653		else
1654			af = AF_INET;
1655	}
1656	dopeers(0, fp, af);
1657}
1658
1659
1660/*
1661 * lpeers - print a peer spreadsheet including all fuzzball peers
1662 */
1663/*ARGSUSED*/
1664static void
1665lpeers(
1666	struct parse *pcmd,
1667	FILE *fp
1668	)
1669{
1670	int af = 0;
1671
1672	if (pcmd->nargs == 1) {
1673		if (pcmd->argval->ival == 6)
1674			af = AF_INET6;
1675		else
1676			af = AF_INET;
1677	}
1678	dopeers(1, fp, af);
1679}
1680
1681
1682/*
1683 * opeers - print a peer spreadsheet
1684 */
1685static void
1686doopeers(
1687	int showall,
1688	FILE *fp,
1689	int af
1690	)
1691{
1692	register int i;
1693	char fullname[LENHOSTNAME];
1694	struct sockaddr_storage netnum;
1695
1696	if (!dogetassoc(fp))
1697		return;
1698
1699	for (i = 0; i < numhosts; ++i) {
1700		if (getnetnum(chosts[i], &netnum, fullname, af))
1701			if ((int)strlen(fullname) > maxhostlen)
1702				maxhostlen = strlen(fullname);
1703	}
1704	if (numhosts > 1)
1705		(void) fprintf(fp, "%-*.*s ", maxhostlen, maxhostlen, "server");
1706	(void) fprintf(fp,
1707			   "     remote           local      st t when poll reach   delay   offset    disp\n");
1708	if (numhosts > 1)
1709		for (i = 0; i <= maxhostlen; ++i)
1710		(void) fprintf(fp, "=");
1711	(void) fprintf(fp,
1712			   "==============================================================================\n");
1713
1714	for (i = 0; i < numassoc; i++) {
1715		if (!showall &&
1716			!(CTL_PEER_STATVAL(assoc_cache[i].status)
1717			  & (CTL_PST_CONFIG|CTL_PST_REACH)))
1718			continue;
1719		if (!dogetpeers(opeervarlist, (int)assoc_cache[i].assid, fp, af)) {
1720			return;
1721		}
1722	}
1723	return;
1724}
1725
1726
1727/*
1728 * opeers - print a peer spreadsheet the old way
1729 */
1730/*ARGSUSED*/
1731static void
1732opeers(
1733	struct parse *pcmd,
1734	FILE *fp
1735	)
1736{
1737	int af = 0;
1738
1739	if (pcmd->nargs == 1) {
1740		if (pcmd->argval->ival == 6)
1741			af = AF_INET6;
1742		else
1743			af = AF_INET;
1744	}
1745	doopeers(0, fp, af);
1746}
1747
1748
1749/*
1750 * lopeers - print a peer spreadsheet including all fuzzball peers
1751 */
1752/*ARGSUSED*/
1753static void
1754lopeers(
1755	struct parse *pcmd,
1756	FILE *fp
1757	)
1758{
1759	int af = 0;
1760
1761	if (pcmd->nargs == 1) {
1762		if (pcmd->argval->ival == 6)
1763			af = AF_INET6;
1764		else
1765			af = AF_INET;
1766	}
1767	doopeers(1, fp, af);
1768}
1769