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