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