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