xref: /freebsd/contrib/ntp/ntpq/ntpq-subs.c (revision a9e8641da961bcf3d24afc85fd657f2083a872a2)
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 
13 extern char *	chosts[];
14 extern char currenthost[];
15 extern int	numhosts;
16 int 	maxhostlen;
17 
18 /*
19  * Declarations for command handlers in here
20  */
21 static	int checkassocid	P((u_int32));
22 static	char *	strsave 	P((char *));
23 static	struct varlist *findlistvar P((struct varlist *, char *));
24 static	void	doaddvlist	P((struct varlist *, char *));
25 static	void	dormvlist	P((struct varlist *, char *));
26 static	void	doclearvlist	P((struct varlist *));
27 static	void	makequerydata	P((struct varlist *, int *, char *));
28 static	int doquerylist P((struct varlist *, int, int, int, u_short *, int *, char **));
29 static	void	doprintvlist	P((struct varlist *, FILE *));
30 static	void	addvars 	P((struct parse *, FILE *));
31 static	void	rmvars		P((struct parse *, FILE *));
32 static	void	clearvars	P((struct parse *, FILE *));
33 static	void	showvars	P((struct parse *, FILE *));
34 static	int dolist		P((struct varlist *, int, int, int, FILE *));
35 static	void	readlist	P((struct parse *, FILE *));
36 static	void	writelist	P((struct parse *, FILE *));
37 static	void	readvar 	P((struct parse *, FILE *));
38 static	void	writevar	P((struct parse *, FILE *));
39 static	void	clocklist	P((struct parse *, FILE *));
40 static	void	clockvar	P((struct parse *, FILE *));
41 static	int findassidrange	P((u_int32, u_int32, int *, int *));
42 static	void	mreadlist	P((struct parse *, FILE *));
43 static	void	mreadvar	P((struct parse *, FILE *));
44 static	int dogetassoc	P((FILE *));
45 static	void	printassoc	P((int, FILE *));
46 static	void	associations	P((struct parse *, FILE *));
47 static	void	lassociations	P((struct parse *, FILE *));
48 static	void	passociations	P((struct parse *, FILE *));
49 static	void	lpassociations	P((struct parse *, FILE *));
50 
51 #ifdef	UNUSED
52 static	void	radiostatus P((struct parse *, FILE *));
53 #endif	/* UNUSED */
54 
55 static	void	pstatus 	P((struct parse *, FILE *));
56 static	long	when		P((l_fp *, l_fp *, l_fp *));
57 static	char *	prettyinterval	P((char *, long));
58 static	int doprintpeers	P((struct varlist *, int, int, int, char *, FILE *, int));
59 static	int dogetpeers	P((struct varlist *, int, FILE *, int));
60 static	void	dopeers 	P((int, FILE *, int));
61 static	void	peers		P((struct parse *, FILE *));
62 static	void	lpeers		P((struct parse *, FILE *));
63 static	void	doopeers	P((int, FILE *, int));
64 static	void	opeers		P((struct parse *, FILE *));
65 static	void	lopeers 	P((struct parse *, FILE *));
66 
67 
68 /*
69  * Commands we understand.	Ntpdc imports this.
70  */
71 struct 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 
178 char flash2[] = " .+*    "; /* flash decode for version 2 */
179 char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
180 
181 struct varlist {
182 	char *name;
183 	char *value;
184 } varlist[MAXLIST] = { { 0, 0 } };
185 
186 /*
187  * Imported from ntpq.c
188  */
189 extern int showhostnames;
190 extern int rawmode;
191 extern struct servent *server_entry;
192 extern struct association assoc_cache[];
193 extern int numassoc;
194 extern u_char pktversion;
195 extern 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  */
206 static int
207 checkassocid(
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  */
223 static char *
224 strsave(
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  */
245 static struct varlist *
246 findlistvar(
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  */
265 static void
266 doaddvlist(
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  */
300 static void
301 dormvlist(
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  */
335 static void
336 doclearvlist(
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  */
356 static void
357 makequerydata(
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  */
398 static int
399 doquerylist(
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  */
423 static void
424 doprintvlist(
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*/
450 static void
451 addvars(
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*/
464 static void
465 rmvars(
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*/
478 static void
479 clearvars(
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*/
492 static void
493 showvars(
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  */
505 static int
506 dolist(
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  */
546 static void
547 readlist(
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  */
572 static void
573 writelist(
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  */
616 static void
617 readvar(
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  */
645 static void
646 writevar(
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  */
691 static void
692 clocklist(
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  */
716 static void
717 clockvar(
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  */
744 static int
745 findassidrange(
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  */
803 static void
804 mreadlist(
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  */
832 static void
833 mreadvar(
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  */
867 static int
868 dogetassoc(
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  */
917 static void
918 printassoc(
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*/
1068 static void
1069 associations(
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*/
1083 static void
1084 lassociations(
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*/
1098 static void
1099 passociations(
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*/
1112 static void
1113 lpassociations(
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*/
1127 static void
1128 radiostatus(
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  */
1158 static void
1159 pstatus(
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  */
1197 static long
1198 when(
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  */
1220 static char *
1221 prettyinterval(
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 
1254 static char
1255 decodeaddrtype(
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  */
1290 struct 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 
1307 struct 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  */
1343 static int
1344 doprintpeers(
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  */
1553 static int
1554 dogetpeers(
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  */
1596 static void
1597 dopeers(
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*/
1642 static void
1643 peers(
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*/
1664 static void
1665 lpeers(
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  */
1685 static void
1686 doopeers(
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*/
1731 static void
1732 opeers(
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*/
1753 static void
1754 lopeers(
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