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