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