ntpq-subs.c revision 358659
1/* 2 * ntpq-subs.c - subroutines which are called to perform ntpq commands. 3 */ 4#include <config.h> 5#include <stdio.h> 6#include <ctype.h> 7#include <sys/types.h> 8#include <sys/time.h> 9 10#include "ntpq.h" 11#include "ntpq-opts.h" 12 13extern char currenthost[]; 14extern int currenthostisnum; 15size_t maxhostlen; 16 17/* 18 * Declarations for command handlers in here 19 */ 20static associd_t checkassocid (u_int32); 21static struct varlist *findlistvar (struct varlist *, char *); 22static void doaddvlist (struct varlist *, const char *); 23static void dormvlist (struct varlist *, const char *); 24static void doclearvlist (struct varlist *); 25static void makequerydata (struct varlist *, size_t *, char *); 26static int doquerylist (struct varlist *, int, associd_t, int, 27 u_short *, size_t *, const char **); 28static void doprintvlist (struct varlist *, FILE *); 29static void addvars (struct parse *, FILE *); 30static void rmvars (struct parse *, FILE *); 31static void clearvars (struct parse *, FILE *); 32static void showvars (struct parse *, FILE *); 33static int dolist (struct varlist *, associd_t, int, int, 34 FILE *); 35static void readlist (struct parse *, FILE *); 36static void writelist (struct parse *, FILE *); 37static void readvar (struct parse *, FILE *); 38static void writevar (struct parse *, FILE *); 39static void clocklist (struct parse *, FILE *); 40static void clockvar (struct parse *, FILE *); 41static int findassidrange (u_int32, u_int32, int *, int *, 42 FILE *); 43static void mreadlist (struct parse *, FILE *); 44static void mreadvar (struct parse *, FILE *); 45static void printassoc (int, FILE *); 46static void associations (struct parse *, FILE *); 47static void lassociations (struct parse *, FILE *); 48static void passociations (struct parse *, FILE *); 49static void lpassociations (struct parse *, FILE *); 50 51#ifdef UNUSED 52static void radiostatus (struct parse *, FILE *); 53#endif /* UNUSED */ 54 55static void authinfo (struct parse *, FILE *); 56static void pstats (struct parse *, FILE *); 57static long when (l_fp *, l_fp *, l_fp *); 58static char * prettyinterval (char *, size_t, long); 59static int doprintpeers (struct varlist *, int, int, size_t, const char *, FILE *, int); 60static int dogetpeers (struct varlist *, associd_t, FILE *, int); 61static void dopeers (int, FILE *, int); 62static void peers (struct parse *, FILE *); 63static void doapeers (int, FILE *, int); 64static void apeers (struct parse *, FILE *); 65static void lpeers (struct parse *, FILE *); 66static void doopeers (int, FILE *, int); 67static void opeers (struct parse *, FILE *); 68static void lopeers (struct parse *, FILE *); 69static void config (struct parse *, FILE *); 70static void saveconfig (struct parse *, FILE *); 71static void config_from_file(struct parse *, FILE *); 72static void mrulist (struct parse *, FILE *); 73static void ifstats (struct parse *, FILE *); 74static void reslist (struct parse *, FILE *); 75static void sysstats (struct parse *, FILE *); 76static void sysinfo (struct parse *, FILE *); 77static void kerninfo (struct parse *, FILE *); 78static void monstats (struct parse *, FILE *); 79static void iostats (struct parse *, FILE *); 80static void timerstats (struct parse *, FILE *); 81 82/* 83 * Commands we understand. Ntpdc imports this. 84 */ 85struct xcmd opcmds[] = { 86 { "saveconfig", saveconfig, { NTP_STR, NO, NO, NO }, 87 { "filename", "", "", ""}, 88 "save ntpd configuration to file, . for current config file"}, 89 { "associations", associations, { NO, NO, NO, NO }, 90 { "", "", "", "" }, 91 "print list of association ID's and statuses for the server's peers" }, 92 { "passociations", passociations, { NO, NO, NO, NO }, 93 { "", "", "", "" }, 94 "print list of associations returned by last associations command" }, 95 { "lassociations", lassociations, { NO, NO, NO, NO }, 96 { "", "", "", "" }, 97 "print list of associations including all client information" }, 98 { "lpassociations", lpassociations, { NO, NO, NO, NO }, 99 { "", "", "", "" }, 100 "print last obtained list of associations, including client information" }, 101 { "addvars", addvars, { NTP_STR, NO, NO, NO }, 102 { "name[=value][,...]", "", "", "" }, 103 "add variables to the variable list or change their values" }, 104 { "rmvars", rmvars, { NTP_STR, NO, NO, NO }, 105 { "name[,...]", "", "", "" }, 106 "remove variables from the variable list" }, 107 { "clearvars", clearvars, { NO, NO, NO, NO }, 108 { "", "", "", "" }, 109 "remove all variables from the variable list" }, 110 { "showvars", showvars, { NO, NO, NO, NO }, 111 { "", "", "", "" }, 112 "print variables on the variable list" }, 113 { "readlist", readlist, { OPT|NTP_UINT, NO, NO, NO }, 114 { "assocID", "", "", "" }, 115 "read the system or peer variables included in the variable list" }, 116 { "rl", readlist, { OPT|NTP_UINT, NO, NO, NO }, 117 { "assocID", "", "", "" }, 118 "read the system or peer variables included in the variable list" }, 119 { "writelist", writelist, { OPT|NTP_UINT, NO, NO, NO }, 120 { "assocID", "", "", "" }, 121 "write the system or peer variables included in the variable list" }, 122 { "readvar", readvar, { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, }, 123 { "assocID", "varname1", "varname2", "varname3" }, 124 "read system or peer variables" }, 125 { "rv", readvar, { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, }, 126 { "assocID", "varname1", "varname2", "varname3" }, 127 "read system or peer variables" }, 128 { "writevar", writevar, { NTP_UINT, NTP_STR, NO, NO }, 129 { "assocID", "name=value,[...]", "", "" }, 130 "write system or peer variables" }, 131 { "mreadlist", mreadlist, { NTP_UINT, NTP_UINT, NO, NO }, 132 { "assocIDlow", "assocIDhigh", "", "" }, 133 "read the peer variables in the variable list for multiple peers" }, 134 { "mrl", mreadlist, { NTP_UINT, NTP_UINT, NO, NO }, 135 { "assocIDlow", "assocIDhigh", "", "" }, 136 "read the peer variables in the variable list for multiple peers" }, 137 { "mreadvar", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO }, 138 { "assocIDlow", "assocIDhigh", "name=value[,...]", "" }, 139 "read peer variables from multiple peers" }, 140 { "mrv", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO }, 141 { "assocIDlow", "assocIDhigh", "name=value[,...]", "" }, 142 "read peer variables from multiple peers" }, 143 { "clocklist", clocklist, { OPT|NTP_UINT, NO, NO, NO }, 144 { "assocID", "", "", "" }, 145 "read the clock variables included in the variable list" }, 146 { "cl", clocklist, { OPT|NTP_UINT, NO, NO, NO }, 147 { "assocID", "", "", "" }, 148 "read the clock variables included in the variable list" }, 149 { "clockvar", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO }, 150 { "assocID", "name=value[,...]", "", "" }, 151 "read clock variables" }, 152 { "cv", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO }, 153 { "assocID", "name=value[,...]", "", "" }, 154 "read clock variables" }, 155 { "pstats", pstats, { NTP_UINT, NO, NO, NO }, 156 { "assocID", "", "", "" }, 157 "show statistics for a peer" }, 158 { "peers", peers, { OPT|IP_VERSION, NO, NO, NO }, 159 { "-4|-6", "", "", "" }, 160 "obtain and print a list of the server's peers [IP version]" }, 161 { "apeers", apeers, { OPT|IP_VERSION, NO, NO, NO }, 162 { "-4|-6", "", "", "" }, 163 "obtain and print a list of the server's peers and their assocIDs [IP version]" }, 164 { "lpeers", lpeers, { OPT|IP_VERSION, NO, NO, NO }, 165 { "-4|-6", "", "", "" }, 166 "obtain and print a list of all peers and clients [IP version]" }, 167 { "opeers", opeers, { OPT|IP_VERSION, NO, NO, NO }, 168 { "-4|-6", "", "", "" }, 169 "print peer list the old way, with dstadr shown rather than refid [IP version]" }, 170 { "lopeers", lopeers, { OPT|IP_VERSION, NO, NO, NO }, 171 { "-4|-6", "", "", "" }, 172 "obtain and print a list of all peers and clients showing dstadr [IP version]" }, 173 { ":config", config, { NTP_STR, NO, NO, NO }, 174 { "<configuration command line>", "", "", "" }, 175 "send a remote configuration command to ntpd" }, 176 { "config-from-file", config_from_file, { NTP_STR, NO, NO, NO }, 177 { "<configuration filename>", "", "", "" }, 178 "configure ntpd using the configuration filename" }, 179 { "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR }, 180 { "tag=value", "tag=value", "tag=value", "tag=value" }, 181 "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." }, 182 { "ifstats", ifstats, { NO, NO, NO, NO }, 183 { "", "", "", "" }, 184 "show statistics for each local address ntpd is using" }, 185 { "reslist", reslist, { NO, NO, NO, NO }, 186 { "", "", "", "" }, 187 "show ntpd access control list" }, 188 { "sysinfo", sysinfo, { NO, NO, NO, NO }, 189 { "", "", "", "" }, 190 "display system summary" }, 191 { "kerninfo", kerninfo, { NO, NO, NO, NO }, 192 { "", "", "", "" }, 193 "display kernel loop and PPS statistics" }, 194 { "sysstats", sysstats, { NO, NO, NO, NO }, 195 { "", "", "", "" }, 196 "display system uptime and packet counts" }, 197 { "monstats", monstats, { NO, NO, NO, NO }, 198 { "", "", "", "" }, 199 "display monitor (mrulist) counters and limits" }, 200 { "authinfo", authinfo, { NO, NO, NO, NO }, 201 { "", "", "", "" }, 202 "display symmetric authentication counters" }, 203 { "iostats", iostats, { NO, NO, NO, NO }, 204 { "", "", "", "" }, 205 "display network input and output counters" }, 206 { "timerstats", timerstats, { NO, NO, NO, NO }, 207 { "", "", "", "" }, 208 "display interval timer counters" }, 209 { 0, 0, { NO, NO, NO, NO }, 210 { "-4|-6", "", "", "" }, "" } 211}; 212 213 214/* 215 * Variable list data space 216 */ 217#define MAXLINE 512 /* maximum length of a line */ 218#define MAXLIST 128 /* maximum variables in list */ 219#define LENHOSTNAME 256 /* host name limit */ 220 221#define MRU_GOT_COUNT 0x1 222#define MRU_GOT_LAST 0x2 223#define MRU_GOT_FIRST 0x4 224#define MRU_GOT_MV 0x8 225#define MRU_GOT_RS 0x10 226#define MRU_GOT_ADDR 0x20 227#define MRU_GOT_ALL (MRU_GOT_COUNT | MRU_GOT_LAST | MRU_GOT_FIRST \ 228 | MRU_GOT_MV | MRU_GOT_RS | MRU_GOT_ADDR) 229 230/* 231 * mrulist() depends on MRUSORT_DEF and MRUSORT_RDEF being the first two 232 */ 233typedef enum mru_sort_order_tag { 234 MRUSORT_DEF = 0, /* lstint ascending */ 235 MRUSORT_R_DEF, /* lstint descending */ 236 MRUSORT_AVGINT, /* avgint ascending */ 237 MRUSORT_R_AVGINT, /* avgint descending */ 238 MRUSORT_ADDR, /* IPv4 asc. then IPv6 asc. */ 239 MRUSORT_R_ADDR, /* IPv6 desc. then IPv4 desc. */ 240 MRUSORT_COUNT, /* hit count ascending */ 241 MRUSORT_R_COUNT, /* hit count descending */ 242 MRUSORT_MAX, /* special: count of this enum */ 243} mru_sort_order; 244 245const char * const mru_sort_keywords[MRUSORT_MAX] = { 246 "lstint", /* MRUSORT_DEF */ 247 "-lstint", /* MRUSORT_R_DEF */ 248 "avgint", /* MRUSORT_AVGINT */ 249 "-avgint", /* MRUSORT_R_AVGINT */ 250 "addr", /* MRUSORT_ADDR */ 251 "-addr", /* MRUSORT_R_ADDR */ 252 "count", /* MRUSORT_COUNT */ 253 "-count", /* MRUSORT_R_COUNT */ 254}; 255 256typedef int (*qsort_cmp)(const void *, const void *); 257 258/* 259 * Old CTL_PST defines for version 2. 260 */ 261#define OLD_CTL_PST_CONFIG 0x80 262#define OLD_CTL_PST_AUTHENABLE 0x40 263#define OLD_CTL_PST_AUTHENTIC 0x20 264#define OLD_CTL_PST_REACH 0x10 265#define OLD_CTL_PST_SANE 0x08 266#define OLD_CTL_PST_DISP 0x04 267 268#define OLD_CTL_PST_SEL_REJECT 0 269#define OLD_CTL_PST_SEL_SELCAND 1 270#define OLD_CTL_PST_SEL_SYNCCAND 2 271#define OLD_CTL_PST_SEL_SYSPEER 3 272 273char flash2[] = " .+* "; /* flash decode for version 2 */ 274char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */ 275 276struct varlist { 277 const char *name; 278 char *value; 279} g_varlist[MAXLIST] = { { 0, 0 } }; 280 281/* 282 * Imported from ntpq.c 283 */ 284extern int showhostnames; 285extern int wideremote; 286extern int rawmode; 287extern struct servent *server_entry; 288extern struct association *assoc_cache; 289extern u_char pktversion; 290 291typedef struct mru_tag mru; 292struct mru_tag { 293 mru * hlink; /* next in hash table bucket */ 294 DECL_DLIST_LINK(mru, mlink); 295 int count; 296 l_fp last; 297 l_fp first; 298 u_char mode; 299 u_char ver; 300 u_short rs; 301 sockaddr_u addr; 302}; 303 304typedef struct ifstats_row_tag { 305 u_int ifnum; 306 sockaddr_u addr; 307 sockaddr_u bcast; 308 int enabled; 309 u_int flags; 310 u_int mcast_count; 311 char name[32]; 312 u_int peer_count; 313 u_int received; 314 u_int sent; 315 u_int send_errors; 316 u_int ttl; 317 u_int uptime; 318} ifstats_row; 319 320typedef struct reslist_row_tag { 321 u_int idx; 322 sockaddr_u addr; 323 sockaddr_u mask; 324 u_long hits; 325 char flagstr[128]; 326} reslist_row; 327 328typedef struct var_display_collection_tag { 329 const char * const tag; /* system variable */ 330 const char * const display; /* descriptive text */ 331 u_char type; /* NTP_STR, etc */ 332 union { 333 char * str; 334 sockaddr_u sau; /* NTP_ADD */ 335 l_fp lfp; /* NTP_LFP */ 336 } v; /* retrieved value */ 337} vdc; 338#if !defined(MISSING_C99_STRUCT_INIT) 339# define VDC_INIT(a, b, c) { .tag = a, .display = b, .type = c } 340#else 341# define VDC_INIT(a, b, c) { a, b, c } 342#endif 343/* 344 * other local function prototypes 345 */ 346static int mrulist_ctrl_c_hook(void); 347static mru * add_mru(mru *); 348static int collect_mru_list(const char *, l_fp *); 349static int fetch_nonce(char *, size_t); 350static int qcmp_mru_avgint(const void *, const void *); 351static int qcmp_mru_r_avgint(const void *, const void *); 352static int qcmp_mru_addr(const void *, const void *); 353static int qcmp_mru_r_addr(const void *, const void *); 354static int qcmp_mru_count(const void *, const void *); 355static int qcmp_mru_r_count(const void *, const void *); 356static void validate_ifnum(FILE *, u_int, int *, ifstats_row *); 357static void another_ifstats_field(int *, ifstats_row *, FILE *); 358static void collect_display_vdc(associd_t as, vdc *table, 359 int decodestatus, FILE *fp); 360 361static int xprintf(FILE *, char const *, ...) NTP_PRINTF(2, 3); 362static int xputs(char const *, FILE *); 363static int xputc(int, FILE *); 364 365/* 366 * static globals 367 */ 368static u_int mru_count; 369static u_int mru_dupes; 370volatile int mrulist_interrupted; 371static mru mru_list; /* listhead */ 372static mru ** hash_table; 373 374/* 375 * qsort comparison function table for mrulist(). The first two 376 * entries are NULL because they are handled without qsort(). 377 */ 378static const qsort_cmp mru_qcmp_table[MRUSORT_MAX] = { 379 NULL, /* MRUSORT_DEF unused */ 380 NULL, /* MRUSORT_R_DEF unused */ 381 &qcmp_mru_avgint, /* MRUSORT_AVGINT */ 382 &qcmp_mru_r_avgint, /* MRUSORT_R_AVGINT */ 383 &qcmp_mru_addr, /* MRUSORT_ADDR */ 384 &qcmp_mru_r_addr, /* MRUSORT_R_ADDR */ 385 &qcmp_mru_count, /* MRUSORT_COUNT */ 386 &qcmp_mru_r_count, /* MRUSORT_R_COUNT */ 387}; 388 389/* 390 * NULL-pointer safe FILE I/O: use stderr if no file supplied. 391 */ 392static int 393xprintf( 394 FILE * ofp, 395 char const * fmt, 396 ... 397 ) 398{ 399 va_list va; 400 int rc; 401 402 va_start(va, fmt); 403 rc = vfprintf((ofp ? ofp : stderr), fmt, va); 404 va_end(va); 405 return rc; 406} 407 408static int 409xputs( 410 char const * str, 411 FILE * ofp 412 ) 413{ 414 return fputs(str, (ofp ? ofp : stderr)); 415} 416 417static int 418xputc( 419 int ch, 420 FILE * ofp 421 ) 422{ 423 return fputc(ch, (ofp ? ofp : stderr)); 424} 425 426/* 427 * checkassocid - return the association ID, checking to see if it is valid 428 */ 429static associd_t 430checkassocid( 431 u_int32 value 432 ) 433{ 434 associd_t associd; 435 u_long ulvalue; 436 437 associd = (associd_t)value; 438 if (0 == associd || value != associd) { 439 ulvalue = value; 440 xprintf(stderr, 441 "***Invalid association ID %lu specified\n", 442 ulvalue); 443 return 0; 444 } 445 446 return associd; 447} 448 449 450/* 451 * findlistvar - Look for the named variable in a varlist. If found, 452 * return a pointer to it. Otherwise, if the list has 453 * slots available, return the pointer to the first free 454 * slot, or NULL if it's full. 455 */ 456static struct varlist * 457findlistvar( 458 struct varlist *list, 459 char *name 460 ) 461{ 462 struct varlist *vl; 463 464 for (vl = list; vl < list + MAXLIST && vl->name != NULL; vl++) 465 if (!strcmp(name, vl->name)) 466 return vl; 467 if (vl < list + MAXLIST) 468 return vl; 469 470 return NULL; 471} 472 473 474/* 475 * doaddvlist - add variable(s) to the variable list 476 */ 477static void 478doaddvlist( 479 struct varlist *vlist, 480 const char *vars 481 ) 482{ 483 struct varlist *vl; 484 size_t len; 485 char *name; 486 char *value; 487 488 len = strlen(vars); 489 while (nextvar(&len, &vars, &name, &value)) { 490 INSIST(name && value); 491 vl = findlistvar(vlist, name); 492 if (NULL == vl) { 493 xprintf(stderr, "Variable list full\n"); 494 return; 495 } 496 497 if (NULL == vl->name) { 498 vl->name = estrdup(name); 499 } else if (vl->value != NULL) { 500 free(vl->value); 501 vl->value = NULL; 502 } 503 504 if (value != NULL) 505 vl->value = estrdup(value); 506 } 507} 508 509 510/* 511 * dormvlist - remove variable(s) from the variable list 512 */ 513static void 514dormvlist( 515 struct varlist *vlist, 516 const char *vars 517 ) 518{ 519 struct varlist *vl; 520 size_t len; 521 char *name; 522 char *value; 523 524 len = strlen(vars); 525 while (nextvar(&len, &vars, &name, &value)) { 526 INSIST(name && value); 527 vl = findlistvar(vlist, name); 528 if (vl == 0 || vl->name == 0) { 529 (void) xprintf(stderr, "Variable `%s' not found\n", 530 name); 531 } else { 532 free((void *)(intptr_t)vl->name); 533 if (vl->value != 0) 534 free(vl->value); 535 for ( ; (vl+1) < (g_varlist + MAXLIST) 536 && (vl+1)->name != 0; vl++) { 537 vl->name = (vl+1)->name; 538 vl->value = (vl+1)->value; 539 } 540 vl->name = vl->value = 0; 541 } 542 } 543} 544 545 546/* 547 * doclearvlist - clear a variable list 548 */ 549static void 550doclearvlist( 551 struct varlist *vlist 552 ) 553{ 554 register struct varlist *vl; 555 556 for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) { 557 free((void *)(intptr_t)vl->name); 558 vl->name = 0; 559 if (vl->value != 0) { 560 free(vl->value); 561 vl->value = 0; 562 } 563 } 564} 565 566 567/* 568 * makequerydata - form a data buffer to be included with a query 569 */ 570static void 571makequerydata( 572 struct varlist *vlist, 573 size_t *datalen, 574 char *data 575 ) 576{ 577 register struct varlist *vl; 578 register char *cp, *cpend; 579 register size_t namelen, valuelen; 580 register size_t totallen; 581 582 cp = data; 583 cpend = data + *datalen; 584 585 for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) { 586 namelen = strlen(vl->name); 587 if (vl->value == 0) 588 valuelen = 0; 589 else 590 valuelen = strlen(vl->value); 591 totallen = namelen + valuelen + (valuelen != 0) + (cp != data); 592 if (cp + totallen > cpend) { 593 xprintf(stderr, 594 "***Ignoring variables starting with `%s'\n", 595 vl->name); 596 break; 597 } 598 599 if (cp != data) 600 *cp++ = ','; 601 memcpy(cp, vl->name, (size_t)namelen); 602 cp += namelen; 603 if (valuelen != 0) { 604 *cp++ = '='; 605 memcpy(cp, vl->value, (size_t)valuelen); 606 cp += valuelen; 607 } 608 } 609 *datalen = (size_t)(cp - data); 610} 611 612 613/* 614 * doquerylist - send a message including variables in a list 615 */ 616static int 617doquerylist( 618 struct varlist *vlist, 619 int op, 620 associd_t associd, 621 int auth, 622 u_short *rstatus, 623 size_t *dsize, 624 const char **datap 625 ) 626{ 627 char data[CTL_MAX_DATA_LEN]; 628 size_t datalen; 629 630 datalen = sizeof(data); 631 makequerydata(vlist, &datalen, data); 632 633 return doquery(op, associd, auth, datalen, data, rstatus, dsize, 634 datap); 635} 636 637 638/* 639 * doprintvlist - print the variables on a list 640 */ 641static void 642doprintvlist( 643 struct varlist *vlist, 644 FILE *fp 645 ) 646{ 647 size_t n; 648 649 if (NULL == vlist->name) { 650 xprintf(fp, "No variables on list\n"); 651 return; 652 } 653 for (n = 0; n < MAXLIST && vlist[n].name != NULL; n++) { 654 if (NULL == vlist[n].value) 655 xprintf(fp, "%s\n", vlist[n].name); 656 else 657 xprintf(fp, "%s=%s\n", vlist[n].name, 658 vlist[n].value); 659 } 660} 661 662/* 663 * addvars - add variables to the variable list 664 */ 665/*ARGSUSED*/ 666static void 667addvars( 668 struct parse *pcmd, 669 FILE *fp 670 ) 671{ 672 doaddvlist(g_varlist, pcmd->argval[0].string); 673} 674 675 676/* 677 * rmvars - remove variables from the variable list 678 */ 679/*ARGSUSED*/ 680static void 681rmvars( 682 struct parse *pcmd, 683 FILE *fp 684 ) 685{ 686 dormvlist(g_varlist, pcmd->argval[0].string); 687} 688 689 690/* 691 * clearvars - clear the variable list 692 */ 693/*ARGSUSED*/ 694static void 695clearvars( 696 struct parse *pcmd, 697 FILE *fp 698 ) 699{ 700 doclearvlist(g_varlist); 701} 702 703 704/* 705 * showvars - show variables on the variable list 706 */ 707/*ARGSUSED*/ 708static void 709showvars( 710 struct parse *pcmd, 711 FILE *fp 712 ) 713{ 714 doprintvlist(g_varlist, fp); 715} 716 717 718/* 719 * dolist - send a request with the given list of variables 720 */ 721static int 722dolist( 723 struct varlist *vlist, 724 associd_t associd, 725 int op, 726 int type, 727 FILE *fp 728 ) 729{ 730 const char *datap; 731 int res; 732 size_t dsize; 733 u_short rstatus; 734 int quiet; 735 736 /* 737 * if we're asking for specific variables don't include the 738 * status header line in the output. 739 */ 740 if (old_rv) 741 quiet = 0; 742 else 743 quiet = (vlist->name != NULL); 744 745 res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap); 746 747 if (res != 0) 748 return 0; 749 750 if (numhosts > 1) 751 xprintf(fp, "server=%s ", currenthost); 752 if (dsize == 0) { 753 if (associd == 0) 754 xprintf(fp, "No system%s variables returned\n", 755 (type == TYPE_CLOCK) ? " clock" : ""); 756 else 757 xprintf(fp, 758 "No information returned for%s association %u\n", 759 (type == TYPE_CLOCK) ? " clock" : "", 760 associd); 761 return 1; 762 } 763 764 if (!quiet) 765 xprintf(fp, "associd=%u ", associd); 766 printvars(dsize, datap, (int)rstatus, type, quiet, fp); 767 return 1; 768} 769 770 771/* 772 * readlist - send a read variables request with the variables on the list 773 */ 774static void 775readlist( 776 struct parse *pcmd, 777 FILE *fp 778 ) 779{ 780 associd_t associd; 781 int type; 782 783 if (pcmd->nargs == 0) { 784 associd = 0; 785 } else { 786 /* HMS: I think we want the u_int32 target here, not the u_long */ 787 if (pcmd->argval[0].uval == 0) 788 associd = 0; 789 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) 790 return; 791 } 792 793 type = (0 == associd) 794 ? TYPE_SYS 795 : TYPE_PEER; 796 dolist(g_varlist, associd, CTL_OP_READVAR, type, fp); 797} 798 799 800/* 801 * writelist - send a write variables request with the variables on the list 802 */ 803static void 804writelist( 805 struct parse *pcmd, 806 FILE *fp 807 ) 808{ 809 const char *datap; 810 int res; 811 associd_t associd; 812 size_t dsize; 813 u_short rstatus; 814 815 if (pcmd->nargs == 0) { 816 associd = 0; 817 } else { 818 /* HMS: Do we really want uval here? */ 819 if (pcmd->argval[0].uval == 0) 820 associd = 0; 821 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) 822 return; 823 } 824 825 res = doquerylist(g_varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus, 826 &dsize, &datap); 827 828 if (res != 0) 829 return; 830 831 if (numhosts > 1) 832 (void) xprintf(fp, "server=%s ", currenthost); 833 if (dsize == 0) 834 (void) xprintf(fp, "done! (no data returned)\n"); 835 else { 836 (void) xprintf(fp,"associd=%u ", associd); 837 printvars(dsize, datap, (int)rstatus, 838 (associd != 0) ? TYPE_PEER : TYPE_SYS, 0, fp); 839 } 840 return; 841} 842 843 844/* 845 * readvar - send a read variables request with the specified variables 846 */ 847static void 848readvar( 849 struct parse *pcmd, 850 FILE *fp 851 ) 852{ 853 associd_t associd; 854 size_t tmpcount; 855 size_t u; 856 int type; 857 struct varlist tmplist[MAXLIST]; 858 859 860 /* HMS: uval? */ 861 if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0) 862 associd = 0; 863 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) 864 return; 865 866 ZERO(tmplist); 867 if (pcmd->nargs > 1) { 868 tmpcount = pcmd->nargs - 1; 869 for (u = 0; u < tmpcount; u++) 870 doaddvlist(tmplist, pcmd->argval[1 + u].string); 871 } 872 873 type = (0 == associd) 874 ? TYPE_SYS 875 : TYPE_PEER; 876 dolist(tmplist, associd, CTL_OP_READVAR, type, fp); 877 878 doclearvlist(tmplist); 879} 880 881 882/* 883 * writevar - send a write variables request with the specified variables 884 */ 885static void 886writevar( 887 struct parse *pcmd, 888 FILE *fp 889 ) 890{ 891 const char *datap; 892 int res; 893 associd_t associd; 894 int type; 895 size_t dsize; 896 u_short rstatus; 897 struct varlist tmplist[MAXLIST]; 898 899 /* HMS: uval? */ 900 if (pcmd->argval[0].uval == 0) 901 associd = 0; 902 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) 903 return; 904 905 ZERO(tmplist); 906 doaddvlist(tmplist, pcmd->argval[1].string); 907 908 res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus, 909 &dsize, &datap); 910 911 doclearvlist(tmplist); 912 913 if (res != 0) 914 return; 915 916 if (numhosts > 1) 917 xprintf(fp, "server=%s ", currenthost); 918 if (dsize == 0) 919 xprintf(fp, "done! (no data returned)\n"); 920 else { 921 xprintf(fp,"associd=%u ", associd); 922 type = (0 == associd) 923 ? TYPE_SYS 924 : TYPE_PEER; 925 printvars(dsize, datap, (int)rstatus, type, 0, fp); 926 } 927 return; 928} 929 930 931/* 932 * clocklist - send a clock variables request with the variables on the list 933 */ 934static void 935clocklist( 936 struct parse *pcmd, 937 FILE *fp 938 ) 939{ 940 associd_t associd; 941 942 /* HMS: uval? */ 943 if (pcmd->nargs == 0) { 944 associd = 0; 945 } else { 946 if (pcmd->argval[0].uval == 0) 947 associd = 0; 948 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) 949 return; 950 } 951 952 dolist(g_varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp); 953} 954 955 956/* 957 * clockvar - send a clock variables request with the specified variables 958 */ 959static void 960clockvar( 961 struct parse *pcmd, 962 FILE *fp 963 ) 964{ 965 associd_t associd; 966 struct varlist tmplist[MAXLIST]; 967 968 /* HMS: uval? */ 969 if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0) 970 associd = 0; 971 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) 972 return; 973 974 ZERO(tmplist); 975 if (pcmd->nargs >= 2) 976 doaddvlist(tmplist, pcmd->argval[1].string); 977 978 dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp); 979 980 doclearvlist(tmplist); 981} 982 983 984/* 985 * findassidrange - verify a range of association ID's 986 */ 987static int 988findassidrange( 989 u_int32 assid1, 990 u_int32 assid2, 991 int * from, 992 int * to, 993 FILE * fp 994 ) 995{ 996 associd_t assids[2]; 997 int ind[COUNTOF(assids)]; 998 u_int i; 999 size_t a; 1000 1001 1002 if (0 == numassoc) 1003 dogetassoc(fp); 1004 1005 assids[0] = checkassocid(assid1); 1006 if (0 == assids[0]) 1007 return 0; 1008 assids[1] = checkassocid(assid2); 1009 if (0 == assids[1]) 1010 return 0; 1011 1012 for (a = 0; a < COUNTOF(assids); a++) { 1013 ind[a] = -1; 1014 for (i = 0; i < numassoc; i++) 1015 if (assoc_cache[i].assid == assids[a]) 1016 ind[a] = i; 1017 } 1018 for (a = 0; a < COUNTOF(assids); a++) 1019 if (-1 == ind[a]) { 1020 xprintf(stderr, 1021 "***Association ID %u not found in list\n", 1022 assids[a]); 1023 return 0; 1024 } 1025 1026 if (ind[0] < ind[1]) { 1027 *from = ind[0]; 1028 *to = ind[1]; 1029 } else { 1030 *to = ind[0]; 1031 *from = ind[1]; 1032 } 1033 return 1; 1034} 1035 1036 1037 1038/* 1039 * mreadlist - send a read variables request for multiple associations 1040 */ 1041static void 1042mreadlist( 1043 struct parse *pcmd, 1044 FILE *fp 1045 ) 1046{ 1047 int i; 1048 int from; 1049 int to; 1050 1051 if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval, 1052 &from, &to, fp)) 1053 return; 1054 1055 for (i = from; i <= to; i++) { 1056 if (i != from) 1057 xprintf(fp, "\n"); 1058 if (!dolist(g_varlist, assoc_cache[i].assid, 1059 CTL_OP_READVAR, TYPE_PEER, fp)) 1060 return; 1061 } 1062 return; 1063} 1064 1065 1066/* 1067 * mreadvar - send a read variables request for multiple associations 1068 */ 1069static void 1070mreadvar( 1071 struct parse *pcmd, 1072 FILE *fp 1073 ) 1074{ 1075 int i; 1076 int from; 1077 int to; 1078 struct varlist tmplist[MAXLIST]; 1079 struct varlist *pvars; 1080 1081 if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval, 1082 &from, &to, fp)) 1083 return; 1084 1085 ZERO(tmplist); 1086 if (pcmd->nargs >= 3) { 1087 doaddvlist(tmplist, pcmd->argval[2].string); 1088 pvars = tmplist; 1089 } else { 1090 pvars = g_varlist; 1091 } 1092 1093 for (i = from; i <= to; i++) { 1094 if (!dolist(pvars, assoc_cache[i].assid, CTL_OP_READVAR, 1095 TYPE_PEER, fp)) 1096 break; 1097 } 1098 1099 if (pvars == tmplist) 1100 doclearvlist(tmplist); 1101 1102 return; 1103} 1104 1105 1106/* 1107 * dogetassoc - query the host for its list of associations 1108 */ 1109int 1110dogetassoc( 1111 FILE *fp 1112 ) 1113{ 1114 const char *datap; 1115 const u_short *pus; 1116 int res; 1117 size_t dsize; 1118 u_short rstatus; 1119 1120 res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus, 1121 &dsize, &datap); 1122 1123 if (res != 0) 1124 return 0; 1125 1126 if (dsize == 0) { 1127 if (numhosts > 1) 1128 xprintf(fp, "server=%s ", currenthost); 1129 xprintf(fp, "No association ID's returned\n"); 1130 return 0; 1131 } 1132 1133 if (dsize & 0x3) { 1134 if (numhosts > 1) 1135 xprintf(stderr, "server=%s ", currenthost); 1136 xprintf(stderr, 1137 "***Server returned %zu octets, should be multiple of 4\n", 1138 dsize); 1139 return 0; 1140 } 1141 1142 numassoc = 0; 1143 1144 while (dsize > 0) { 1145 if (numassoc >= assoc_cache_slots) { 1146 grow_assoc_cache(); 1147 } 1148 pus = (const void *)datap; 1149 assoc_cache[numassoc].assid = ntohs(*pus); 1150 datap += sizeof(*pus); 1151 pus = (const void *)datap; 1152 assoc_cache[numassoc].status = ntohs(*pus); 1153 datap += sizeof(*pus); 1154 dsize -= 2 * sizeof(*pus); 1155 if (debug) { 1156 xprintf(stderr, "[%u] ", 1157 assoc_cache[numassoc].assid); 1158 } 1159 numassoc++; 1160 } 1161 if (debug) { 1162 xprintf(stderr, "\n%d associations total\n", numassoc); 1163 } 1164 sortassoc(); 1165 return 1; 1166} 1167 1168 1169/* 1170 * printassoc - print the current list of associations 1171 */ 1172static void 1173printassoc( 1174 int showall, 1175 FILE *fp 1176 ) 1177{ 1178 register char *bp; 1179 u_int i; 1180 u_char statval; 1181 int event; 1182 u_long event_count; 1183 const char *conf; 1184 const char *reach; 1185 const char *auth; 1186 const char *condition = ""; 1187 const char *last_event; 1188 char buf[128]; 1189 1190 if (numassoc == 0) { 1191 (void) xprintf(fp, "No association ID's in list\n"); 1192 return; 1193 } 1194 1195 /* 1196 * Output a header 1197 */ 1198 (void) xprintf(fp, 1199 "ind assid status conf reach auth condition last_event cnt\n"); 1200 (void) xprintf(fp, 1201 "===========================================================\n"); 1202 for (i = 0; i < numassoc; i++) { 1203 statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status); 1204 if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH))) 1205 continue; 1206 event = CTL_PEER_EVENT(assoc_cache[i].status); 1207 event_count = CTL_PEER_NEVNT(assoc_cache[i].status); 1208 if (statval & CTL_PST_CONFIG) 1209 conf = "yes"; 1210 else 1211 conf = "no"; 1212 if (statval & CTL_PST_BCAST) { 1213 reach = "none"; 1214 if (statval & CTL_PST_AUTHENABLE) 1215 auth = "yes"; 1216 else 1217 auth = "none"; 1218 } else { 1219 if (statval & CTL_PST_REACH) 1220 reach = "yes"; 1221 else 1222 reach = "no"; 1223 if (statval & CTL_PST_AUTHENABLE) { 1224 if (statval & CTL_PST_AUTHENTIC) 1225 auth = "ok "; 1226 else 1227 auth = "bad"; 1228 } else { 1229 auth = "none"; 1230 } 1231 } 1232 if (pktversion > NTP_OLDVERSION) { 1233 switch (statval & 0x7) { 1234 1235 case CTL_PST_SEL_REJECT: 1236 condition = "reject"; 1237 break; 1238 1239 case CTL_PST_SEL_SANE: 1240 condition = "falsetick"; 1241 break; 1242 1243 case CTL_PST_SEL_CORRECT: 1244 condition = "excess"; 1245 break; 1246 1247 case CTL_PST_SEL_SELCAND: 1248 condition = "outlier"; 1249 break; 1250 1251 case CTL_PST_SEL_SYNCCAND: 1252 condition = "candidate"; 1253 break; 1254 1255 case CTL_PST_SEL_EXCESS: 1256 condition = "backup"; 1257 break; 1258 1259 case CTL_PST_SEL_SYSPEER: 1260 condition = "sys.peer"; 1261 break; 1262 1263 case CTL_PST_SEL_PPS: 1264 condition = "pps.peer"; 1265 break; 1266 } 1267 } else { 1268 switch (statval & 0x3) { 1269 1270 case OLD_CTL_PST_SEL_REJECT: 1271 if (!(statval & OLD_CTL_PST_SANE)) 1272 condition = "insane"; 1273 else if (!(statval & OLD_CTL_PST_DISP)) 1274 condition = "hi_disp"; 1275 else 1276 condition = ""; 1277 break; 1278 1279 case OLD_CTL_PST_SEL_SELCAND: 1280 condition = "sel_cand"; 1281 break; 1282 1283 case OLD_CTL_PST_SEL_SYNCCAND: 1284 condition = "sync_cand"; 1285 break; 1286 1287 case OLD_CTL_PST_SEL_SYSPEER: 1288 condition = "sys_peer"; 1289 break; 1290 } 1291 } 1292 switch (PEER_EVENT|event) { 1293 1294 case PEVNT_MOBIL: 1295 last_event = "mobilize"; 1296 break; 1297 1298 case PEVNT_DEMOBIL: 1299 last_event = "demobilize"; 1300 break; 1301 1302 case PEVNT_REACH: 1303 last_event = "reachable"; 1304 break; 1305 1306 case PEVNT_UNREACH: 1307 last_event = "unreachable"; 1308 break; 1309 1310 case PEVNT_RESTART: 1311 last_event = "restart"; 1312 break; 1313 1314 case PEVNT_REPLY: 1315 last_event = "no_reply"; 1316 break; 1317 1318 case PEVNT_RATE: 1319 last_event = "rate_exceeded"; 1320 break; 1321 1322 case PEVNT_DENY: 1323 last_event = "access_denied"; 1324 break; 1325 1326 case PEVNT_ARMED: 1327 last_event = "leap_armed"; 1328 break; 1329 1330 case PEVNT_NEWPEER: 1331 last_event = "sys_peer"; 1332 break; 1333 1334 case PEVNT_CLOCK: 1335 last_event = "clock_alarm"; 1336 break; 1337 1338 default: 1339 last_event = ""; 1340 break; 1341 } 1342 snprintf(buf, sizeof(buf), 1343 "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2lu", 1344 i + 1, assoc_cache[i].assid, 1345 assoc_cache[i].status, conf, reach, auth, 1346 condition, last_event, event_count); 1347 bp = buf + strlen(buf); 1348 while (bp > buf && ' ' == bp[-1]) 1349 --bp; 1350 bp[0] = '\0'; 1351 xprintf(fp, "%s\n", buf); 1352 } 1353} 1354 1355 1356/* 1357 * associations - get, record and print a list of associations 1358 */ 1359/*ARGSUSED*/ 1360static void 1361associations( 1362 struct parse *pcmd, 1363 FILE *fp 1364 ) 1365{ 1366 if (dogetassoc(fp)) 1367 printassoc(0, fp); 1368} 1369 1370 1371/* 1372 * lassociations - get, record and print a long list of associations 1373 */ 1374/*ARGSUSED*/ 1375static void 1376lassociations( 1377 struct parse *pcmd, 1378 FILE *fp 1379 ) 1380{ 1381 if (dogetassoc(fp)) 1382 printassoc(1, fp); 1383} 1384 1385 1386/* 1387 * passociations - print the association list 1388 */ 1389/*ARGSUSED*/ 1390static void 1391passociations( 1392 struct parse *pcmd, 1393 FILE *fp 1394 ) 1395{ 1396 printassoc(0, fp); 1397} 1398 1399 1400/* 1401 * lpassociations - print the long association list 1402 */ 1403/*ARGSUSED*/ 1404static void 1405lpassociations( 1406 struct parse *pcmd, 1407 FILE *fp 1408 ) 1409{ 1410 printassoc(1, fp); 1411} 1412 1413 1414/* 1415 * saveconfig - dump ntp server configuration to server file 1416 */ 1417static void 1418saveconfig( 1419 struct parse *pcmd, 1420 FILE *fp 1421 ) 1422{ 1423 const char *datap; 1424 int res; 1425 size_t dsize; 1426 u_short rstatus; 1427 1428 if (0 == pcmd->nargs) 1429 return; 1430 1431 res = doquery(CTL_OP_SAVECONFIG, 0, 1, 1432 strlen(pcmd->argval[0].string), 1433 pcmd->argval[0].string, &rstatus, &dsize, 1434 &datap); 1435 1436 if (res != 0) 1437 return; 1438 1439 if (0 == dsize) 1440 xprintf(fp, "(no response message, curiously)"); 1441 else 1442 xprintf(fp, "%.*s", (int)dsize, datap); /* cast is wobbly */ 1443} 1444 1445 1446#ifdef UNUSED 1447/* 1448 * radiostatus - print the radio status returned by the server 1449 */ 1450/*ARGSUSED*/ 1451static void 1452radiostatus( 1453 struct parse *pcmd, 1454 FILE *fp 1455 ) 1456{ 1457 char *datap; 1458 int res; 1459 int dsize; 1460 u_short rstatus; 1461 1462 res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus, 1463 &dsize, &datap); 1464 1465 if (res != 0) 1466 return; 1467 1468 if (numhosts > 1) 1469 (void) xprintf(fp, "server=%s ", currenthost); 1470 if (dsize == 0) { 1471 (void) xprintf(fp, "No radio status string returned\n"); 1472 return; 1473 } 1474 1475 asciize(dsize, datap, fp); 1476} 1477#endif /* UNUSED */ 1478 1479/* 1480 * when - print how long its been since his last packet arrived 1481 */ 1482static long 1483when( 1484 l_fp *ts, 1485 l_fp *rec, 1486 l_fp *reftime 1487 ) 1488{ 1489 l_fp *lasttime; 1490 1491 if (rec->l_ui != 0) 1492 lasttime = rec; 1493 else if (reftime->l_ui != 0) 1494 lasttime = reftime; 1495 else 1496 return 0; 1497 1498 if (ts->l_ui < lasttime->l_ui) 1499 return -1; 1500 return (ts->l_ui - lasttime->l_ui); 1501} 1502 1503 1504/* 1505 * Pretty-print an interval into the given buffer, in a human-friendly format. 1506 */ 1507static char * 1508prettyinterval( 1509 char *buf, 1510 size_t cb, 1511 long diff 1512 ) 1513{ 1514 if (diff <= 0) { 1515 buf[0] = '-'; 1516 buf[1] = 0; 1517 return buf; 1518 } 1519 1520 if (diff <= 2048) { 1521 snprintf(buf, cb, "%u", (unsigned int)diff); 1522 return buf; 1523 } 1524 1525 diff = (diff + 29) / 60; 1526 if (diff <= 300) { 1527 snprintf(buf, cb, "%um", (unsigned int)diff); 1528 return buf; 1529 } 1530 1531 diff = (diff + 29) / 60; 1532 if (diff <= 96) { 1533 snprintf(buf, cb, "%uh", (unsigned int)diff); 1534 return buf; 1535 } 1536 1537 diff = (diff + 11) / 24; 1538 if (diff <= 999) { 1539 snprintf(buf, cb, "%ud", (unsigned int)diff); 1540 return buf; 1541 } 1542 1543 /* years are only approximated... */ 1544 diff = (long)floor(diff / 365.25 + 0.5); 1545 if (diff <= 999) { 1546 snprintf(buf, cb, "%uy", (unsigned int)diff); 1547 return buf; 1548 } 1549 /* Ok, this amounts to infinity... */ 1550 strlcpy(buf, "INF", cb); 1551 return buf; 1552} 1553 1554static char 1555decodeaddrtype( 1556 sockaddr_u *sock 1557 ) 1558{ 1559 char ch = '-'; 1560 u_int32 dummy; 1561 1562 switch(AF(sock)) { 1563 case AF_INET: 1564 dummy = SRCADR(sock); 1565 ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' : 1566 ((dummy&0x000000ff)==0x000000ff) ? 'b' : 1567 ((dummy&0xffffffff)==0x7f000001) ? 'l' : 1568 ((dummy&0xffffffe0)==0x00000000) ? '-' : 1569 'u'); 1570 break; 1571 case AF_INET6: 1572 if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock))) 1573 ch = 'm'; 1574 else 1575 ch = 'u'; 1576 break; 1577 default: 1578 ch = '-'; 1579 break; 1580 } 1581 return ch; 1582} 1583 1584/* 1585 * A list of variables required by the peers command 1586 */ 1587struct varlist opeervarlist[] = { 1588 { "srcadr", 0 }, /* 0 */ 1589 { "dstadr", 0 }, /* 1 */ 1590 { "stratum", 0 }, /* 2 */ 1591 { "hpoll", 0 }, /* 3 */ 1592 { "ppoll", 0 }, /* 4 */ 1593 { "reach", 0 }, /* 5 */ 1594 { "delay", 0 }, /* 6 */ 1595 { "offset", 0 }, /* 7 */ 1596 { "jitter", 0 }, /* 8 */ 1597 { "dispersion", 0 }, /* 9 */ 1598 { "rec", 0 }, /* 10 */ 1599 { "reftime", 0 }, /* 11 */ 1600 { "srcport", 0 }, /* 12 */ 1601 { "hmode", 0 }, /* 13 */ 1602 { 0, 0 } 1603}; 1604 1605struct varlist peervarlist[] = { 1606 { "srcadr", 0 }, /* 0 */ 1607 { "refid", 0 }, /* 1 */ 1608 { "stratum", 0 }, /* 2 */ 1609 { "hpoll", 0 }, /* 3 */ 1610 { "ppoll", 0 }, /* 4 */ 1611 { "reach", 0 }, /* 5 */ 1612 { "delay", 0 }, /* 6 */ 1613 { "offset", 0 }, /* 7 */ 1614 { "jitter", 0 }, /* 8 */ 1615 { "dispersion", 0 }, /* 9 */ 1616 { "rec", 0 }, /* 10 */ 1617 { "reftime", 0 }, /* 11 */ 1618 { "srcport", 0 }, /* 12 */ 1619 { "hmode", 0 }, /* 13 */ 1620 { "srchost", 0 }, /* 14 */ 1621 { 0, 0 } 1622}; 1623 1624struct varlist apeervarlist[] = { 1625 { "srcadr", 0 }, /* 0 */ 1626 { "refid", 0 }, /* 1 */ 1627 { "assid", 0 }, /* 2 */ 1628 { "stratum", 0 }, /* 3 */ 1629 { "hpoll", 0 }, /* 4 */ 1630 { "ppoll", 0 }, /* 5 */ 1631 { "reach", 0 }, /* 6 */ 1632 { "delay", 0 }, /* 7 */ 1633 { "offset", 0 }, /* 8 */ 1634 { "jitter", 0 }, /* 9 */ 1635 { "dispersion", 0 }, /* 10 */ 1636 { "rec", 0 }, /* 11 */ 1637 { "reftime", 0 }, /* 12 */ 1638 { "srcport", 0 }, /* 13 */ 1639 { "hmode", 0 }, /* 14 */ 1640 { "srchost", 0 }, /* 15 */ 1641 { 0, 0 } 1642}; 1643 1644 1645/* 1646 * Decode an incoming data buffer and print a line in the peer list 1647 */ 1648static int 1649doprintpeers( 1650 struct varlist *pvl, 1651 int associd, 1652 int rstatus, 1653 size_t datalen, 1654 const char *data, 1655 FILE *fp, 1656 int af 1657 ) 1658{ 1659 char *name; 1660 char *value = NULL; 1661 int c; 1662 size_t len; 1663 int have_srchost; 1664 int have_dstadr; 1665 int have_da_rid; 1666 int have_jitter; 1667 sockaddr_u srcadr; 1668 sockaddr_u dstadr; 1669 sockaddr_u dum_store; 1670 sockaddr_u refidadr; 1671 long hmode = 0; 1672 u_long srcport = 0; 1673 u_int32 u32; 1674 const char *dstadr_refid = "0.0.0.0"; 1675 const char *serverlocal; 1676 size_t drlen; 1677 u_long stratum = 0; 1678 long ppoll = 0; 1679 long hpoll = 0; 1680 u_long reach = 0; 1681 l_fp estoffset; 1682 l_fp estdelay; 1683 l_fp estjitter; 1684 l_fp estdisp; 1685 l_fp reftime; 1686 l_fp rec; 1687 l_fp ts; 1688 u_long poll_sec; 1689 u_long flash = 0; 1690 char type = '?'; 1691 char clock_name[LENHOSTNAME]; 1692 char whenbuf[12], pollbuf[12]; 1693 /* [Bug 3482] formally whenbuf & pollbuf should be able to hold 1694 * a full signed int. Not that we would use that much string 1695 * data for it... 1696 */ 1697 get_systime(&ts); 1698 1699 have_srchost = FALSE; 1700 have_dstadr = FALSE; 1701 have_da_rid = FALSE; 1702 have_jitter = FALSE; 1703 ZERO_SOCK(&srcadr); 1704 ZERO_SOCK(&dstadr); 1705 clock_name[0] = '\0'; 1706 ZERO(estoffset); 1707 ZERO(estdelay); 1708 ZERO(estjitter); 1709 ZERO(estdisp); 1710 1711 while (nextvar(&datalen, &data, &name, &value)) { 1712 INSIST(name && value); 1713 if (!strcmp("srcadr", name) || 1714 !strcmp("peeradr", name)) { 1715 if (!decodenetnum(value, &srcadr)) 1716 xprintf(stderr, "malformed %s=%s\n", 1717 name, value); 1718 } else if (!strcmp("srchost", name)) { 1719 if (pvl == peervarlist || pvl == apeervarlist) { 1720 len = strlen(value); 1721 if (2 < len && 1722 (size_t)len < sizeof(clock_name)) { 1723 /* strip quotes */ 1724 value++; 1725 len -= 2; 1726 memcpy(clock_name, value, len); 1727 clock_name[len] = '\0'; 1728 have_srchost = TRUE; 1729 } 1730 } 1731 } else if (!strcmp("dstadr", name)) { 1732 if (decodenetnum(value, &dum_store)) { 1733 type = decodeaddrtype(&dum_store); 1734 have_dstadr = TRUE; 1735 dstadr = dum_store; 1736 if (pvl == opeervarlist) { 1737 have_da_rid = TRUE; 1738 dstadr_refid = trunc_left(stoa(&dstadr), 15); 1739 } 1740 } 1741 } else if (!strcmp("hmode", name)) { 1742 decodeint(value, &hmode); 1743 } else if (!strcmp("refid", name)) { 1744 if ( (pvl == peervarlist) 1745 && (drefid == REFID_IPV4)) { 1746 have_da_rid = TRUE; 1747 drlen = strlen(value); 1748 if (0 == drlen) { 1749 dstadr_refid = ""; 1750 } else if (drlen <= 4) { 1751 ZERO(u32); 1752 memcpy(&u32, value, drlen); 1753 dstadr_refid = refid_str(u32, 1); 1754 } else if (decodenetnum(value, &refidadr)) { 1755 if (SOCK_UNSPEC(&refidadr)) 1756 dstadr_refid = "0.0.0.0"; 1757 else if (ISREFCLOCKADR(&refidadr)) 1758 dstadr_refid = 1759 refnumtoa(&refidadr); 1760 else 1761 dstadr_refid = 1762 stoa(&refidadr); 1763 } else { 1764 have_da_rid = FALSE; 1765 } 1766 } else if ( (pvl == apeervarlist) 1767 || (pvl == peervarlist)) { 1768 /* no need to check drefid == REFID_HASH */ 1769 have_da_rid = TRUE; 1770 drlen = strlen(value); 1771 if (0 == drlen) { 1772 dstadr_refid = ""; 1773 } else if (drlen <= 4) { 1774 ZERO(u32); 1775 memcpy(&u32, value, drlen); 1776 dstadr_refid = refid_str(u32, 1); 1777 //xprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value); 1778 } else if (decodenetnum(value, &refidadr)) { 1779 if (SOCK_UNSPEC(&refidadr)) 1780 dstadr_refid = "0.0.0.0"; 1781 else if (ISREFCLOCKADR(&refidadr)) 1782 dstadr_refid = 1783 refnumtoa(&refidadr); 1784 else { 1785 char *buf = emalloc(10); 1786 int i = ntohl(refidadr.sa4.sin_addr.s_addr); 1787 1788 snprintf(buf, 10, 1789 "%0x", i); 1790 dstadr_refid = buf; 1791 //xprintf(stderr, "apeervarlist refid: value=<%x>\n", i); 1792 } 1793 //xprintf(stderr, "apeervarlist refid: value=<%s>\n", value); 1794 } else { 1795 have_da_rid = FALSE; 1796 } 1797 } 1798 } else if (!strcmp("stratum", name)) { 1799 decodeuint(value, &stratum); 1800 } else if (!strcmp("hpoll", name)) { 1801 if (decodeint(value, &hpoll) && hpoll < 0) 1802 hpoll = NTP_MINPOLL; 1803 } else if (!strcmp("ppoll", name)) { 1804 if (decodeint(value, &ppoll) && ppoll < 0) 1805 ppoll = NTP_MINPOLL; 1806 } else if (!strcmp("reach", name)) { 1807 decodeuint(value, &reach); 1808 } else if (!strcmp("delay", name)) { 1809 decodetime(value, &estdelay); 1810 } else if (!strcmp("offset", name)) { 1811 decodetime(value, &estoffset); 1812 } else if (!strcmp("jitter", name)) { 1813 if ((pvl == peervarlist || pvl == apeervarlist) 1814 && decodetime(value, &estjitter)) 1815 have_jitter = 1; 1816 } else if (!strcmp("rootdisp", name) || 1817 !strcmp("dispersion", name)) { 1818 decodetime(value, &estdisp); 1819 } else if (!strcmp("rec", name)) { 1820 decodets(value, &rec); 1821 } else if (!strcmp("srcport", name) || 1822 !strcmp("peerport", name)) { 1823 decodeuint(value, &srcport); 1824 } else if (!strcmp("reftime", name)) { 1825 if (!decodets(value, &reftime)) 1826 L_CLR(&reftime); 1827 } else if (!strcmp("flash", name)) { 1828 decodeuint(value, &flash); 1829 } else { 1830 // xprintf(stderr, "UNRECOGNIZED name=%s ", name); 1831 } 1832 } 1833 1834 /* 1835 * hmode gives the best guidance for the t column. If the response 1836 * did not include hmode we'll use the old decodeaddrtype() result. 1837 */ 1838 switch (hmode) { 1839 1840 case MODE_BCLIENT: 1841 /* broadcastclient or multicastclient */ 1842 type = 'b'; 1843 break; 1844 1845 case MODE_BROADCAST: 1846 /* broadcast or multicast server */ 1847 if (IS_MCAST(&srcadr)) 1848 type = 'M'; 1849 else 1850 type = 'B'; 1851 break; 1852 1853 case MODE_CLIENT: 1854 if (ISREFCLOCKADR(&srcadr)) 1855 type = 'l'; /* local refclock*/ 1856 else if (SOCK_UNSPEC(&srcadr)) 1857 type = 'p'; /* pool */ 1858 else if (IS_MCAST(&srcadr)) 1859 type = 'a'; /* manycastclient */ 1860 else 1861 type = 'u'; /* unicast */ 1862 break; 1863 1864 case MODE_ACTIVE: 1865 type = 's'; /* symmetric active */ 1866 break; /* configured */ 1867 1868 case MODE_PASSIVE: 1869 type = 'S'; /* symmetric passive */ 1870 break; /* ephemeral */ 1871 } 1872 1873 /* 1874 * Got everything, format the line 1875 */ 1876 poll_sec = 1 << min(ppoll, hpoll); 1877 if (pktversion > NTP_OLDVERSION) 1878 c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7]; 1879 else 1880 c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3]; 1881 if (numhosts > 1) { 1882 if ((pvl == peervarlist || pvl == apeervarlist) 1883 && have_dstadr) { 1884 serverlocal = nntohost_col(&dstadr, 1885 (size_t)min(LIB_BUFLENGTH - 1, maxhostlen), 1886 TRUE); 1887 } else { 1888 if (currenthostisnum) 1889 serverlocal = trunc_left(currenthost, 1890 maxhostlen); 1891 else 1892 serverlocal = currenthost; 1893 } 1894 xprintf(fp, "%-*s ", (int)maxhostlen, serverlocal); 1895 } 1896 if (AF_UNSPEC == af || AF(&srcadr) == af) { 1897 if (!have_srchost) 1898 strlcpy(clock_name, nntohost(&srcadr), 1899 sizeof(clock_name)); 1900 /* wide and long source - space over on next line */ 1901 /* allow for host + sp if > 1 and regular tally + source + sp */ 1902 if (wideremote && 15 < strlen(clock_name)) 1903 xprintf(fp, "%c%s\n%*s", c, clock_name, 1904 ((numhosts > 1) ? (int)maxhostlen + 1 : 0) 1905 + 1 + 15 + 1, ""); 1906 else 1907 xprintf(fp, "%c%-15.15s ", c, clock_name); 1908 if ((flash & TEST12) && (pvl != opeervarlist)) { 1909 drlen = xprintf(fp, "(loop)"); 1910 } else if (!have_da_rid) { 1911 drlen = 0; 1912 } else { 1913 drlen = strlen(dstadr_refid); 1914 makeascii(drlen, dstadr_refid, fp); 1915 } 1916 if (pvl == apeervarlist) { 1917 while (drlen++ < 9) 1918 xputc(' ', fp); 1919 xprintf(fp, "%-6d", associd); 1920 } else { 1921 while (drlen++ < 15) 1922 xputc(' ', fp); 1923 } 1924 xprintf(fp, 1925 " %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n", 1926 stratum, type, 1927 prettyinterval(whenbuf, sizeof(whenbuf), 1928 when(&ts, &rec, &reftime)), 1929 prettyinterval(pollbuf, sizeof(pollbuf), 1930 (int)poll_sec), 1931 reach, ulfptoms(&estdelay, 3), 1932 lfptoms(&estoffset, 3), 1933 (have_jitter) 1934 ? ulfptoms(&estjitter, 3) 1935 : ulfptoms(&estdisp, 3)); 1936 return (1); 1937 } 1938 else 1939 return(1); 1940} 1941 1942 1943/* 1944 * dogetpeers - given an association ID, read and print the spreadsheet 1945 * peer variables. 1946 */ 1947static int 1948dogetpeers( 1949 struct varlist *pvl, 1950 associd_t associd, 1951 FILE *fp, 1952 int af 1953 ) 1954{ 1955 const char *datap; 1956 int res; 1957 size_t dsize; 1958 u_short rstatus; 1959 1960#ifdef notdef 1961 res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus, 1962 &dsize, &datap); 1963#else 1964 /* 1965 * Damn fuzzballs 1966 */ 1967 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus, 1968 &dsize, &datap); 1969#endif 1970 1971 if (res != 0) 1972 return 0; 1973 1974 if (dsize == 0) { 1975 if (numhosts > 1) 1976 xprintf(stderr, "server=%s ", currenthost); 1977 xprintf(stderr, 1978 "***No information returned for association %u\n", 1979 associd); 1980 return 0; 1981 } 1982 1983 return doprintpeers(pvl, associd, (int)rstatus, dsize, datap, 1984 fp, af); 1985} 1986 1987 1988/* 1989 * peers - print a peer spreadsheet 1990 */ 1991static void 1992dopeers( 1993 int showall, 1994 FILE *fp, 1995 int af 1996 ) 1997{ 1998 u_int u; 1999 char fullname[LENHOSTNAME]; 2000 sockaddr_u netnum; 2001 const char * name_or_num; 2002 size_t sl; 2003 2004 if (!dogetassoc(fp)) 2005 return; 2006 2007 for (u = 0; u < numhosts; u++) { 2008 if (getnetnum(chosts[u].name, &netnum, fullname, af)) { 2009 name_or_num = nntohost(&netnum); 2010 sl = strlen(name_or_num); 2011 maxhostlen = max(maxhostlen, sl); 2012 } 2013 } 2014 if (numhosts > 1) 2015 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, 2016 "server (local)"); 2017 xprintf(fp, 2018 " remote refid st t when poll reach delay offset jitter\n"); 2019 if (numhosts > 1) 2020 for (u = 0; u <= maxhostlen; u++) 2021 xprintf(fp, "="); 2022 xprintf(fp, 2023 "==============================================================================\n"); 2024 2025 for (u = 0; u < numassoc; u++) { 2026 if (!showall && 2027 !(CTL_PEER_STATVAL(assoc_cache[u].status) 2028 & (CTL_PST_CONFIG|CTL_PST_REACH))) { 2029 if (debug) 2030 xprintf(stderr, "eliding [%d]\n", 2031 (int)assoc_cache[u].assid); 2032 continue; 2033 } 2034 if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid, 2035 fp, af)) 2036 return; 2037 } 2038 return; 2039} 2040 2041 2042/* 2043 * doapeers - print a peer spreadsheet with assocIDs 2044 */ 2045static void 2046doapeers( 2047 int showall, 2048 FILE *fp, 2049 int af 2050 ) 2051{ 2052 u_int u; 2053 char fullname[LENHOSTNAME]; 2054 sockaddr_u netnum; 2055 const char * name_or_num; 2056 size_t sl; 2057 2058 if (!dogetassoc(fp)) 2059 return; 2060 2061 for (u = 0; u < numhosts; u++) { 2062 if (getnetnum(chosts[u].name, &netnum, fullname, af)) { 2063 name_or_num = nntohost(&netnum); 2064 sl = strlen(name_or_num); 2065 maxhostlen = max(maxhostlen, sl); 2066 } 2067 } 2068 if (numhosts > 1) 2069 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, 2070 "server (local)"); 2071 xprintf(fp, 2072 " remote refid assid st t when poll reach delay offset jitter\n"); 2073 if (numhosts > 1) 2074 for (u = 0; u <= maxhostlen; u++) 2075 xprintf(fp, "="); 2076 xprintf(fp, 2077 "==============================================================================\n"); 2078 2079 for (u = 0; u < numassoc; u++) { 2080 if (!showall && 2081 !(CTL_PEER_STATVAL(assoc_cache[u].status) 2082 & (CTL_PST_CONFIG|CTL_PST_REACH))) { 2083 if (debug) 2084 xprintf(stderr, "eliding [%d]\n", 2085 (int)assoc_cache[u].assid); 2086 continue; 2087 } 2088 if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid, 2089 fp, af)) 2090 return; 2091 } 2092 return; 2093} 2094 2095 2096/* 2097 * peers - print a peer spreadsheet 2098 */ 2099/*ARGSUSED*/ 2100static void 2101peers( 2102 struct parse *pcmd, 2103 FILE *fp 2104 ) 2105{ 2106 if (drefid == REFID_HASH) { 2107 apeers(pcmd, fp); 2108 } else { 2109 int af = 0; 2110 2111 if (pcmd->nargs == 1) { 2112 if (pcmd->argval->ival == 6) 2113 af = AF_INET6; 2114 else 2115 af = AF_INET; 2116 } 2117 dopeers(0, fp, af); 2118 } 2119} 2120 2121 2122/* 2123 * apeers - print a peer spreadsheet, with assocIDs 2124 */ 2125/*ARGSUSED*/ 2126static void 2127apeers( 2128 struct parse *pcmd, 2129 FILE *fp 2130 ) 2131{ 2132 int af = 0; 2133 2134 if (pcmd->nargs == 1) { 2135 if (pcmd->argval->ival == 6) 2136 af = AF_INET6; 2137 else 2138 af = AF_INET; 2139 } 2140 doapeers(0, fp, af); 2141} 2142 2143 2144/* 2145 * lpeers - print a peer spreadsheet including all fuzzball peers 2146 */ 2147/*ARGSUSED*/ 2148static void 2149lpeers( 2150 struct parse *pcmd, 2151 FILE *fp 2152 ) 2153{ 2154 int af = 0; 2155 2156 if (pcmd->nargs == 1) { 2157 if (pcmd->argval->ival == 6) 2158 af = AF_INET6; 2159 else 2160 af = AF_INET; 2161 } 2162 dopeers(1, fp, af); 2163} 2164 2165 2166/* 2167 * opeers - print a peer spreadsheet 2168 */ 2169static void 2170doopeers( 2171 int showall, 2172 FILE *fp, 2173 int af 2174 ) 2175{ 2176 u_int i; 2177 char fullname[LENHOSTNAME]; 2178 sockaddr_u netnum; 2179 2180 if (!dogetassoc(fp)) 2181 return; 2182 2183 for (i = 0; i < numhosts; ++i) { 2184 if (getnetnum(chosts[i].name, &netnum, fullname, af)) 2185 if (strlen(fullname) > maxhostlen) 2186 maxhostlen = strlen(fullname); 2187 } 2188 if (numhosts > 1) 2189 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, 2190 "server"); 2191 xprintf(fp, 2192 " remote local st t when poll reach delay offset disp\n"); 2193 if (numhosts > 1) 2194 for (i = 0; i <= maxhostlen; ++i) 2195 xprintf(fp, "="); 2196 xprintf(fp, 2197 "==============================================================================\n"); 2198 2199 for (i = 0; i < numassoc; i++) { 2200 if (!showall && 2201 !(CTL_PEER_STATVAL(assoc_cache[i].status) & 2202 (CTL_PST_CONFIG | CTL_PST_REACH))) 2203 continue; 2204 if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af)) 2205 return; 2206 } 2207 return; 2208} 2209 2210 2211/* 2212 * opeers - print a peer spreadsheet the old way 2213 */ 2214/*ARGSUSED*/ 2215static void 2216opeers( 2217 struct parse *pcmd, 2218 FILE *fp 2219 ) 2220{ 2221 int af = 0; 2222 2223 if (pcmd->nargs == 1) { 2224 if (pcmd->argval->ival == 6) 2225 af = AF_INET6; 2226 else 2227 af = AF_INET; 2228 } 2229 doopeers(0, fp, af); 2230} 2231 2232 2233/* 2234 * lopeers - print a peer spreadsheet including all fuzzball peers 2235 */ 2236/*ARGSUSED*/ 2237static void 2238lopeers( 2239 struct parse *pcmd, 2240 FILE *fp 2241 ) 2242{ 2243 int af = 0; 2244 2245 if (pcmd->nargs == 1) { 2246 if (pcmd->argval->ival == 6) 2247 af = AF_INET6; 2248 else 2249 af = AF_INET; 2250 } 2251 doopeers(1, fp, af); 2252} 2253 2254 2255/* 2256 * config - send a configuration command to a remote host 2257 */ 2258static void 2259config ( 2260 struct parse *pcmd, 2261 FILE *fp 2262 ) 2263{ 2264 const char *cfgcmd; 2265 u_short rstatus; 2266 size_t rsize; 2267 const char *rdata; 2268 char *resp; 2269 int res; 2270 int col; 2271 int i; 2272 2273 cfgcmd = pcmd->argval[0].string; 2274 2275 if (debug > 2) 2276 xprintf(stderr, 2277 "In Config\n" 2278 "Keyword = %s\n" 2279 "Command = %s\n", pcmd->keyword, cfgcmd); 2280 2281 res = doquery(CTL_OP_CONFIGURE, 0, 1, 2282 strlen(cfgcmd), cfgcmd, 2283 &rstatus, &rsize, &rdata); 2284 2285 if (res != 0) 2286 return; 2287 2288 if (rsize > 0 && '\n' == rdata[rsize - 1]) 2289 rsize--; 2290 2291 resp = emalloc(rsize + 1); 2292 memcpy(resp, rdata, rsize); 2293 resp[rsize] = '\0'; 2294 2295 col = -1; 2296 if (1 == sscanf(resp, "column %d syntax error", &col) 2297 && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) { 2298 if (interactive) 2299 xputs(" *", stdout); /* "ntpq> :config " */ 2300 else 2301 printf("%s\n", cfgcmd); 2302 for (i = 0; i < col; i++) 2303 xputc('_', stdout); 2304 xputs("^\n", stdout); 2305 } 2306 printf("%s\n", resp); 2307 free(resp); 2308} 2309 2310 2311/* 2312 * config_from_file - remotely configure an ntpd daemon using the 2313 * specified configuration file 2314 * SK: This function is a kludge at best and is full of bad design 2315 * bugs: 2316 * 1. ntpq uses UDP, which means that there is no guarantee of in-order, 2317 * error-free delivery. 2318 * 2. The maximum length of a packet is constrained, and as a result, the 2319 * maximum length of a line in a configuration file is constrained. 2320 * Longer lines will lead to unpredictable results. 2321 * 3. Since this function is sending a line at a time, we can't update 2322 * the control key through the configuration file (YUCK!!) 2323 * 2324 * Pearly: There are a few places where 'size_t' is cast to 'int' based 2325 * on the assumption that 'int' can hold the size of the involved 2326 * buffers without overflow. 2327 */ 2328static void 2329config_from_file ( 2330 struct parse *pcmd, 2331 FILE *fp 2332 ) 2333{ 2334 u_short rstatus; 2335 size_t rsize; 2336 const char *rdata; 2337 char * cp; 2338 int res; 2339 FILE *config_fd; 2340 char config_cmd[MAXLINE]; 2341 size_t config_len; 2342 int i; 2343 int retry_limit; 2344 2345 if (debug > 2) 2346 xprintf(stderr, 2347 "In Config\n" 2348 "Keyword = %s\n" 2349 "Filename = %s\n", pcmd->keyword, 2350 pcmd->argval[0].string); 2351 2352 config_fd = fopen(pcmd->argval[0].string, "r"); 2353 if (NULL == config_fd) { 2354 printf("ERROR!! Couldn't open file: %s\n", 2355 pcmd->argval[0].string); 2356 return; 2357 } 2358 2359 printf("Sending configuration file, one line at a time.\n"); 2360 i = 0; 2361 while (fgets(config_cmd, MAXLINE, config_fd) != NULL) { 2362 /* Eliminate comments first. */ 2363 cp = strchr(config_cmd, '#'); 2364 config_len = (NULL != cp) 2365 ? (size_t)(cp - config_cmd) 2366 : strlen(config_cmd); 2367 2368 /* [Bug 3015] make sure there's no trailing whitespace; 2369 * the fix for [Bug 2853] on the server side forbids 2370 * those. And don't transmit empty lines, as this would 2371 * just be waste. 2372 */ 2373 while (config_len != 0 && 2374 (u_char)config_cmd[config_len-1] <= ' ') 2375 --config_len; 2376 config_cmd[config_len] = '\0'; 2377 2378 ++i; 2379 if (0 == config_len) 2380 continue; 2381 2382 retry_limit = 2; 2383 do 2384 res = doquery(CTL_OP_CONFIGURE, 0, 1, 2385 config_len, config_cmd, 2386 &rstatus, &rsize, &rdata); 2387 while (res != 0 && retry_limit--); 2388 if (res != 0) { 2389 printf("Line No: %d query failed: %.*s\n" 2390 "Subsequent lines not sent.\n", 2391 i, (int)config_len, config_cmd); 2392 fclose(config_fd); 2393 return; 2394 } 2395 2396 /* Right-strip the result code string, then output the 2397 * last line executed, with result code. */ 2398 while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ') 2399 --rsize; 2400 printf("Line No: %d %.*s: %.*s\n", i, 2401 (int)rsize, rdata, 2402 (int)config_len, config_cmd); 2403 } 2404 printf("Done sending file\n"); 2405 fclose(config_fd); 2406} 2407 2408 2409static int 2410fetch_nonce( 2411 char * nonce, 2412 size_t cb_nonce 2413 ) 2414{ 2415 const char nonce_eq[] = "nonce="; 2416 int qres; 2417 u_short rstatus; 2418 size_t rsize; 2419 const char * rdata; 2420 size_t chars; 2421 2422 /* 2423 * Retrieve a nonce specific to this client to demonstrate to 2424 * ntpd that we're capable of receiving responses to our source 2425 * IP address, and thereby unlikely to be forging the source. 2426 */ 2427 qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus, 2428 &rsize, &rdata); 2429 if (qres) { 2430 xprintf(stderr, "nonce request failed\n"); 2431 return FALSE; 2432 } 2433 2434 if ((size_t)rsize <= sizeof(nonce_eq) - 1 || 2435 strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) { 2436 xprintf(stderr, "unexpected nonce response format: %.*s\n", 2437 (int)rsize, rdata); /* cast is wobbly */ 2438 return FALSE; 2439 } 2440 chars = rsize - (sizeof(nonce_eq) - 1); 2441 if (chars >= cb_nonce) 2442 return FALSE; 2443 memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars); 2444 nonce[chars] = '\0'; 2445 while (chars > 0 && 2446 ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) { 2447 chars--; 2448 nonce[chars] = '\0'; 2449 } 2450 2451 return TRUE; 2452} 2453 2454 2455/* 2456 * add_mru Add and entry to mru list, hash table, and allocate 2457 * and return a replacement. 2458 * This is a helper for collect_mru_list(). 2459 */ 2460static mru * 2461add_mru( 2462 mru *add 2463 ) 2464{ 2465 u_short hash; 2466 mru *mon; 2467 mru *unlinked; 2468 2469 2470 hash = NTP_HASH_ADDR(&add->addr); 2471 /* see if we have it among previously received entries */ 2472 for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink) 2473 if (SOCK_EQ(&mon->addr, &add->addr)) 2474 break; 2475 if (mon != NULL) { 2476 if (!L_ISGEQ(&add->first, &mon->first)) { 2477 xprintf(stderr, 2478 "add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n", 2479 sptoa(&add->addr), add->last.l_ui, 2480 add->last.l_uf, mon->last.l_ui, 2481 mon->last.l_uf); 2482 exit(1); 2483 } 2484 UNLINK_DLIST(mon, mlink); 2485 UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru); 2486 INSIST(unlinked == mon); 2487 mru_dupes++; 2488 TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui, 2489 mon->last.l_uf)); 2490 } 2491 LINK_DLIST(mru_list, add, mlink); 2492 LINK_SLIST(hash_table[hash], add, hlink); 2493 TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n", 2494 add->last.l_ui, add->last.l_uf, add->count, 2495 (int)add->mode, (int)add->ver, (u_int)add->rs, 2496 add->first.l_ui, add->first.l_uf, sptoa(&add->addr))); 2497 /* if we didn't update an existing entry, alloc replacement */ 2498 if (NULL == mon) { 2499 mon = emalloc(sizeof(*mon)); 2500 mru_count++; 2501 } 2502 ZERO(*mon); 2503 2504 return mon; 2505} 2506 2507 2508/* MGOT macro is specific to collect_mru_list() */ 2509#define MGOT(bit) \ 2510 do { \ 2511 got |= (bit); \ 2512 if (MRU_GOT_ALL == got) { \ 2513 got = 0; \ 2514 mon = add_mru(mon); \ 2515 ci++; \ 2516 } \ 2517 } while (0) 2518 2519 2520int 2521mrulist_ctrl_c_hook(void) 2522{ 2523 mrulist_interrupted = TRUE; 2524 return TRUE; 2525} 2526 2527 2528static int 2529collect_mru_list( 2530 const char * parms, 2531 l_fp * pnow 2532 ) 2533{ 2534 const u_int sleep_msecs = 5; 2535 static int ntpd_row_limit = MRU_ROW_LIMIT; 2536 int c_mru_l_rc; /* this function's return code */ 2537 u_char got; /* MRU_GOT_* bits */ 2538 time_t next_report; 2539 size_t cb; 2540 mru *mon; 2541 mru *head; 2542 mru *recent; 2543 int list_complete; 2544 char nonce[128]; 2545 char buf[128]; 2546 char req_buf[CTL_MAX_DATA_LEN]; 2547 char *req; 2548 char *req_end; 2549 size_t chars; 2550 int qres; 2551 u_short rstatus; 2552 size_t rsize; 2553 const char *rdata; 2554 int limit; 2555 int frags; 2556 int cap_frags; 2557 char *tag; 2558 char *val; 2559 int si; /* server index in response */ 2560 int ci; /* client (our) index for validation */ 2561 int ri; /* request index (.# suffix) */ 2562 int mv; 2563 l_fp newest; 2564 l_fp last_older; 2565 sockaddr_u addr_older; 2566 int have_now; 2567 int have_addr_older; 2568 int have_last_older; 2569 u_int restarted_count; 2570 u_int nonce_uses; 2571 u_short hash; 2572 mru *unlinked; 2573 2574 if (!fetch_nonce(nonce, sizeof(nonce))) 2575 return FALSE; 2576 2577 nonce_uses = 0; 2578 restarted_count = 0; 2579 mru_count = 0; 2580 INIT_DLIST(mru_list, mlink); 2581 cb = NTP_HASH_SIZE * sizeof(*hash_table); 2582 INSIST(NULL == hash_table); 2583 hash_table = emalloc_zero(cb); 2584 2585 c_mru_l_rc = FALSE; 2586 list_complete = FALSE; 2587 have_now = FALSE; 2588 cap_frags = TRUE; 2589 got = 0; 2590 ri = 0; 2591 cb = sizeof(*mon); 2592 mon = emalloc_zero(cb); 2593 ZERO(*pnow); 2594 ZERO(last_older); 2595 next_report = time(NULL) + MRU_REPORT_SECS; 2596 2597 limit = min(3 * MAXFRAGS, ntpd_row_limit); 2598 frags = MAXFRAGS; 2599 snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s", 2600 nonce, frags, parms); 2601 nonce_uses++; 2602 2603 while (TRUE) { 2604 if (debug) 2605 xprintf(stderr, "READ_MRU parms: %s\n", req_buf); 2606 2607 qres = doqueryex(CTL_OP_READ_MRU, 0, 0, 2608 strlen(req_buf), req_buf, 2609 &rstatus, &rsize, &rdata, TRUE); 2610 2611 if (CERR_UNKNOWNVAR == qres && ri > 0) { 2612 /* 2613 * None of the supplied prior entries match, so 2614 * toss them from our list and try again. 2615 */ 2616 if (debug) 2617 xprintf(stderr, 2618 "no overlap between %d prior entries and server MRU list\n", 2619 ri); 2620 while (ri--) { 2621 recent = HEAD_DLIST(mru_list, mlink); 2622 INSIST(recent != NULL); 2623 if (debug) 2624 xprintf(stderr, 2625 "tossing prior entry %s to resync\n", 2626 sptoa(&recent->addr)); 2627 UNLINK_DLIST(recent, mlink); 2628 hash = NTP_HASH_ADDR(&recent->addr); 2629 UNLINK_SLIST(unlinked, hash_table[hash], 2630 recent, hlink, mru); 2631 INSIST(unlinked == recent); 2632 free(recent); 2633 mru_count--; 2634 } 2635 if (NULL == HEAD_DLIST(mru_list, mlink)) { 2636 restarted_count++; 2637 if (restarted_count > 8) { 2638 xprintf(stderr, 2639 "Giving up after 8 restarts from the beginning.\n" 2640 "With high-traffic NTP servers, this can occur if the\n" 2641 "MRU list is limited to less than about 16 seconds' of\n" 2642 "entries. See the 'mru' ntp.conf directive to adjust.\n"); 2643 goto cleanup_return; 2644 } 2645 if (debug) 2646 xprintf(stderr, 2647 "---> Restarting from the beginning, retry #%u\n", 2648 restarted_count); 2649 } 2650 } else if (CERR_UNKNOWNVAR == qres) { 2651 xprintf(stderr, 2652 "CERR_UNKNOWNVAR from ntpd but no priors given.\n"); 2653 goto cleanup_return; 2654 } else if (CERR_BADVALUE == qres) { 2655 if (cap_frags) { 2656 cap_frags = FALSE; 2657 if (debug) 2658 xprintf(stderr, 2659 "Reverted to row limit from fragments limit.\n"); 2660 } else { 2661 /* ntpd has lower cap on row limit */ 2662 ntpd_row_limit--; 2663 limit = min(limit, ntpd_row_limit); 2664 if (debug) 2665 xprintf(stderr, 2666 "Row limit reduced to %d following CERR_BADVALUE.\n", 2667 limit); 2668 } 2669 } else if (ERR_INCOMPLETE == qres || 2670 ERR_TIMEOUT == qres) { 2671 /* 2672 * Reduce the number of rows/frags requested by 2673 * half to recover from lost response fragments. 2674 */ 2675 if (cap_frags) { 2676 frags = max(2, frags / 2); 2677 if (debug) 2678 xprintf(stderr, 2679 "Frag limit reduced to %d following incomplete response.\n", 2680 frags); 2681 } else { 2682 limit = max(2, limit / 2); 2683 if (debug) 2684 xprintf(stderr, 2685 "Row limit reduced to %d following incomplete response.\n", 2686 limit); 2687 } 2688 } else if (qres) { 2689 show_error_msg(qres, 0); 2690 goto cleanup_return; 2691 } 2692 /* 2693 * This is a cheap cop-out implementation of rawmode 2694 * output for mrulist. A better approach would be to 2695 * dump similar output after the list is collected by 2696 * ntpq with a continuous sequence of indexes. This 2697 * cheap approach has indexes resetting to zero for 2698 * each query/response, and duplicates are not 2699 * coalesced. 2700 */ 2701 if (!qres && rawmode) 2702 printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout); 2703 ci = 0; 2704 have_addr_older = FALSE; 2705 have_last_older = FALSE; 2706 while (!qres && nextvar(&rsize, &rdata, &tag, &val)) { 2707 INSIST(tag && val); 2708 if (debug > 1) 2709 xprintf(stderr, "nextvar gave: %s = %s\n", 2710 tag, val); 2711 switch(tag[0]) { 2712 2713 case 'a': 2714 if (!strcmp(tag, "addr.older")) { 2715 if (!have_last_older) { 2716 xprintf(stderr, 2717 "addr.older %s before last.older\n", 2718 val); 2719 goto cleanup_return; 2720 } 2721 if (!decodenetnum(val, &addr_older)) { 2722 xprintf(stderr, 2723 "addr.older %s garbled\n", 2724 val); 2725 goto cleanup_return; 2726 } 2727 hash = NTP_HASH_ADDR(&addr_older); 2728 for (recent = hash_table[hash]; 2729 recent != NULL; 2730 recent = recent->hlink) 2731 if (ADDR_PORT_EQ( 2732 &addr_older, 2733 &recent->addr)) 2734 break; 2735 if (NULL == recent) { 2736 xprintf(stderr, 2737 "addr.older %s not in hash table\n", 2738 val); 2739 goto cleanup_return; 2740 } 2741 if (!L_ISEQU(&last_older, 2742 &recent->last)) { 2743 xprintf(stderr, 2744 "last.older %08x.%08x mismatches %08x.%08x expected.\n", 2745 last_older.l_ui, 2746 last_older.l_uf, 2747 recent->last.l_ui, 2748 recent->last.l_uf); 2749 goto cleanup_return; 2750 } 2751 have_addr_older = TRUE; 2752 } else if (1 != sscanf(tag, "addr.%d", &si) 2753 || si != ci) 2754 goto nomatch; 2755 else if (decodenetnum(val, &mon->addr)) 2756 MGOT(MRU_GOT_ADDR); 2757 break; 2758 2759 case 'l': 2760 if (!strcmp(tag, "last.older")) { 2761 if ('0' != val[0] || 2762 'x' != val[1] || 2763 !hextolfp(val + 2, &last_older)) { 2764 xprintf(stderr, 2765 "last.older %s garbled\n", 2766 val); 2767 goto cleanup_return; 2768 } 2769 have_last_older = TRUE; 2770 } else if (!strcmp(tag, "last.newest")) { 2771 if (0 != got) { 2772 xprintf(stderr, 2773 "last.newest %s before complete row, got = 0x%x\n", 2774 val, (u_int)got); 2775 goto cleanup_return; 2776 } 2777 if (!have_now) { 2778 xprintf(stderr, 2779 "last.newest %s before now=\n", 2780 val); 2781 goto cleanup_return; 2782 } 2783 head = HEAD_DLIST(mru_list, mlink); 2784 if (NULL != head) { 2785 if ('0' != val[0] || 2786 'x' != val[1] || 2787 !hextolfp(val + 2, &newest) || 2788 !L_ISEQU(&newest, 2789 &head->last)) { 2790 xprintf(stderr, 2791 "last.newest %s mismatches %08x.%08x", 2792 val, 2793 head->last.l_ui, 2794 head->last.l_uf); 2795 goto cleanup_return; 2796 } 2797 } 2798 list_complete = TRUE; 2799 } else if (1 != sscanf(tag, "last.%d", &si) || 2800 si != ci || '0' != val[0] || 2801 'x' != val[1] || 2802 !hextolfp(val + 2, &mon->last)) { 2803 goto nomatch; 2804 } else { 2805 MGOT(MRU_GOT_LAST); 2806 /* 2807 * allow interrupted retrieval, 2808 * using most recent retrieved 2809 * entry's last seen timestamp 2810 * as the end of operation. 2811 */ 2812 *pnow = mon->last; 2813 } 2814 break; 2815 2816 case 'f': 2817 if (1 != sscanf(tag, "first.%d", &si) || 2818 si != ci || '0' != val[0] || 2819 'x' != val[1] || 2820 !hextolfp(val + 2, &mon->first)) 2821 goto nomatch; 2822 MGOT(MRU_GOT_FIRST); 2823 break; 2824 2825 case 'n': 2826 if (!strcmp(tag, "nonce")) { 2827 strlcpy(nonce, val, sizeof(nonce)); 2828 nonce_uses = 0; 2829 break; /* case */ 2830 } else if (strcmp(tag, "now") || 2831 '0' != val[0] || 2832 'x' != val[1] || 2833 !hextolfp(val + 2, pnow)) 2834 goto nomatch; 2835 have_now = TRUE; 2836 break; 2837 2838 case 'c': 2839 if (1 != sscanf(tag, "ct.%d", &si) || 2840 si != ci || 2841 1 != sscanf(val, "%d", &mon->count) 2842 || mon->count < 1) 2843 goto nomatch; 2844 MGOT(MRU_GOT_COUNT); 2845 break; 2846 2847 case 'm': 2848 if (1 != sscanf(tag, "mv.%d", &si) || 2849 si != ci || 2850 1 != sscanf(val, "%d", &mv)) 2851 goto nomatch; 2852 mon->mode = PKT_MODE(mv); 2853 mon->ver = PKT_VERSION(mv); 2854 MGOT(MRU_GOT_MV); 2855 break; 2856 2857 case 'r': 2858 if (1 != sscanf(tag, "rs.%d", &si) || 2859 si != ci || 2860 1 != sscanf(val, "0x%hx", &mon->rs)) 2861 goto nomatch; 2862 MGOT(MRU_GOT_RS); 2863 break; 2864 2865 default: 2866 nomatch: 2867 /* empty stmt */ ; 2868 /* ignore unknown tags */ 2869 } 2870 } 2871 if (have_now) 2872 list_complete = TRUE; 2873 if (list_complete) { 2874 INSIST(0 == ri || have_addr_older); 2875 } 2876 if (mrulist_interrupted) { 2877 printf("mrulist retrieval interrupted by operator.\n" 2878 "Displaying partial client list.\n"); 2879 fflush(stdout); 2880 } 2881 if (list_complete || mrulist_interrupted) { 2882 xprintf(stderr, 2883 "\rRetrieved %u unique MRU entries and %u updates.\n", 2884 mru_count, mru_dupes); 2885 fflush(stderr); 2886 break; 2887 } 2888 if (time(NULL) >= next_report) { 2889 next_report += MRU_REPORT_SECS; 2890 xprintf(stderr, "\r%u (%u updates) ", mru_count, 2891 mru_dupes); 2892 fflush(stderr); 2893 } 2894 2895 /* 2896 * Snooze for a bit between queries to let ntpd catch 2897 * up with other duties. 2898 */ 2899#ifdef SYS_WINNT 2900 Sleep(sleep_msecs); 2901#elif !defined(HAVE_NANOSLEEP) 2902 sleep((sleep_msecs / 1000) + 1); 2903#else 2904 { 2905 struct timespec interv = { 0, 2906 1000 * sleep_msecs }; 2907 nanosleep(&interv, NULL); 2908 } 2909#endif 2910 /* 2911 * If there were no errors, increase the number of rows 2912 * to a maximum of 3 * MAXFRAGS (the most packets ntpq 2913 * can handle in one response), on the assumption that 2914 * no less than 3 rows fit in each packet, capped at 2915 * our best guess at the server's row limit. 2916 */ 2917 if (!qres) { 2918 if (cap_frags) { 2919 frags = min(MAXFRAGS, frags + 1); 2920 } else { 2921 limit = min3(3 * MAXFRAGS, 2922 ntpd_row_limit, 2923 max(limit + 1, 2924 limit * 33 / 32)); 2925 } 2926 } 2927 /* 2928 * prepare next query with as many address and last-seen 2929 * timestamps as will fit in a single packet. 2930 */ 2931 req = req_buf; 2932 req_end = req_buf + sizeof(req_buf); 2933#define REQ_ROOM (req_end - req) 2934 snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce, 2935 (cap_frags) 2936 ? "frags" 2937 : "limit", 2938 (cap_frags) 2939 ? frags 2940 : limit, 2941 parms); 2942 req += strlen(req); 2943 nonce_uses++; 2944 if (nonce_uses >= 4) { 2945 if (!fetch_nonce(nonce, sizeof(nonce))) 2946 goto cleanup_return; 2947 nonce_uses = 0; 2948 } 2949 2950 2951 for (ri = 0, recent = HEAD_DLIST(mru_list, mlink); 2952 recent != NULL; 2953 ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) { 2954 2955 snprintf(buf, sizeof(buf), 2956 ", addr.%d=%s, last.%d=0x%08x.%08x", 2957 ri, sptoa(&recent->addr), ri, 2958 recent->last.l_ui, recent->last.l_uf); 2959 chars = strlen(buf); 2960 if ((size_t)REQ_ROOM <= chars) 2961 break; 2962 memcpy(req, buf, chars + 1); 2963 req += chars; 2964 } 2965 } 2966 2967 c_mru_l_rc = TRUE; 2968 goto retain_hash_table; 2969 2970cleanup_return: 2971 free(hash_table); 2972 hash_table = NULL; 2973 2974retain_hash_table: 2975 if (mon != NULL) 2976 free(mon); 2977 2978 return c_mru_l_rc; 2979} 2980 2981 2982/* 2983 * qcmp_mru_addr - sort MRU entries by remote address. 2984 * 2985 * All IPv4 addresses sort before any IPv6, addresses are sorted by 2986 * value within address family. 2987 */ 2988static int 2989qcmp_mru_addr( 2990 const void *v1, 2991 const void *v2 2992 ) 2993{ 2994 const mru * const * ppm1 = v1; 2995 const mru * const * ppm2 = v2; 2996 const mru * pm1; 2997 const mru * pm2; 2998 u_short af1; 2999 u_short af2; 3000 size_t cmplen; 3001 size_t addr_off; 3002 3003 pm1 = *ppm1; 3004 pm2 = *ppm2; 3005 3006 af1 = AF(&pm1->addr); 3007 af2 = AF(&pm2->addr); 3008 3009 if (af1 != af2) 3010 return (AF_INET == af1) 3011 ? -1 3012 : 1; 3013 3014 cmplen = SIZEOF_INADDR(af1); 3015 addr_off = (AF_INET == af1) 3016 ? offsetof(struct sockaddr_in, sin_addr) 3017 : offsetof(struct sockaddr_in6, sin6_addr); 3018 3019 return memcmp((const char *)&pm1->addr + addr_off, 3020 (const char *)&pm2->addr + addr_off, 3021 cmplen); 3022} 3023 3024 3025static int 3026qcmp_mru_r_addr( 3027 const void *v1, 3028 const void *v2 3029 ) 3030{ 3031 return -qcmp_mru_addr(v1, v2); 3032} 3033 3034 3035/* 3036 * qcmp_mru_count - sort MRU entries by times seen (hit count). 3037 */ 3038static int 3039qcmp_mru_count( 3040 const void *v1, 3041 const void *v2 3042 ) 3043{ 3044 const mru * const * ppm1 = v1; 3045 const mru * const * ppm2 = v2; 3046 const mru * pm1; 3047 const mru * pm2; 3048 3049 pm1 = *ppm1; 3050 pm2 = *ppm2; 3051 3052 return (pm1->count < pm2->count) 3053 ? -1 3054 : ((pm1->count == pm2->count) 3055 ? 0 3056 : 1); 3057} 3058 3059 3060static int 3061qcmp_mru_r_count( 3062 const void *v1, 3063 const void *v2 3064 ) 3065{ 3066 return -qcmp_mru_count(v1, v2); 3067} 3068 3069 3070/* 3071 * qcmp_mru_avgint - sort MRU entries by average interval. 3072 */ 3073static int 3074qcmp_mru_avgint( 3075 const void *v1, 3076 const void *v2 3077 ) 3078{ 3079 const mru * const * ppm1 = v1; 3080 const mru * const * ppm2 = v2; 3081 const mru * pm1; 3082 const mru * pm2; 3083 l_fp interval; 3084 double avg1; 3085 double avg2; 3086 3087 pm1 = *ppm1; 3088 pm2 = *ppm2; 3089 3090 interval = pm1->last; 3091 L_SUB(&interval, &pm1->first); 3092 LFPTOD(&interval, avg1); 3093 avg1 /= pm1->count; 3094 3095 interval = pm2->last; 3096 L_SUB(&interval, &pm2->first); 3097 LFPTOD(&interval, avg2); 3098 avg2 /= pm2->count; 3099 3100 if (avg1 < avg2) 3101 return -1; 3102 else if (avg1 > avg2) 3103 return 1; 3104 3105 /* secondary sort on lstint - rarely tested */ 3106 if (L_ISEQU(&pm1->last, &pm2->last)) 3107 return 0; 3108 else if (L_ISGEQ(&pm1->last, &pm2->last)) 3109 return -1; 3110 else 3111 return 1; 3112} 3113 3114 3115static int 3116qcmp_mru_r_avgint( 3117 const void *v1, 3118 const void *v2 3119 ) 3120{ 3121 return -qcmp_mru_avgint(v1, v2); 3122} 3123 3124 3125/* 3126 * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most 3127 * Recently Used (seen) remote address list from ntpd. 3128 * 3129 * Similar to ntpdc's monlist command, but not limited to a single 3130 * request/response, and thereby not limited to a few hundred remote 3131 * addresses. 3132 * 3133 * See ntpd/ntp_control.c read_mru_list() for comments on the way 3134 * CTL_OP_READ_MRU is designed to be used. 3135 * 3136 * mrulist intentionally differs from monlist in the way the avgint 3137 * column is calculated. monlist includes the time after the last 3138 * packet from the client until the monlist query time in the average, 3139 * while mrulist excludes it. That is, monlist's average interval grows 3140 * over time for remote addresses not heard from in some time, while it 3141 * remains unchanged in mrulist. This also affects the avgint value for 3142 * entries representing a single packet, with identical first and last 3143 * timestamps. mrulist shows 0 avgint, monlist shows a value identical 3144 * to lstint. 3145 */ 3146static void 3147mrulist( 3148 struct parse * pcmd, 3149 FILE * fp 3150 ) 3151{ 3152 const char mincount_eq[] = "mincount="; 3153 const char resall_eq[] = "resall="; 3154 const char resany_eq[] = "resany="; 3155 const char maxlstint_eq[] = "maxlstint="; 3156 const char laddr_eq[] = "laddr="; 3157 const char sort_eq[] = "sort="; 3158 mru_sort_order order; 3159 size_t n; 3160 char parms_buf[128]; 3161 char buf[24]; 3162 char *parms; 3163 const char *arg; 3164 size_t cb; 3165 mru **sorted; 3166 mru **ppentry; 3167 mru *recent; 3168 l_fp now; 3169 l_fp interval; 3170 double favgint; 3171 double flstint; 3172 int avgint; 3173 int lstint; 3174 size_t i; 3175 3176 mrulist_interrupted = FALSE; 3177 push_ctrl_c_handler(&mrulist_ctrl_c_hook); 3178 xprintf(stderr, 3179 "Ctrl-C will stop MRU retrieval and display partial results.\n"); 3180 fflush(stderr); 3181 3182 order = MRUSORT_DEF; 3183 parms_buf[0] = '\0'; 3184 parms = parms_buf; 3185 for (i = 0; i < pcmd->nargs; i++) { 3186 arg = pcmd->argval[i].string; 3187 if (arg != NULL) { 3188 cb = strlen(arg) + 1; 3189 if ((!strncmp(resall_eq, arg, sizeof(resall_eq) 3190 - 1) || !strncmp(resany_eq, arg, 3191 sizeof(resany_eq) - 1) || !strncmp( 3192 mincount_eq, arg, sizeof(mincount_eq) - 1) 3193 || !strncmp(laddr_eq, arg, sizeof(laddr_eq) 3194 - 1) || !strncmp(maxlstint_eq, arg, 3195 sizeof(laddr_eq) - 1)) && parms + cb + 2 <= 3196 parms_buf + sizeof(parms_buf)) { 3197 /* these are passed intact to ntpd */ 3198 memcpy(parms, ", ", 2); 3199 parms += 2; 3200 memcpy(parms, arg, cb); 3201 parms += cb - 1; 3202 } else if (!strncmp(sort_eq, arg, 3203 sizeof(sort_eq) - 1)) { 3204 arg += sizeof(sort_eq) - 1; 3205 for (n = 0; 3206 n < COUNTOF(mru_sort_keywords); 3207 n++) 3208 if (!strcmp(mru_sort_keywords[n], 3209 arg)) 3210 break; 3211 if (n < COUNTOF(mru_sort_keywords)) 3212 order = n; 3213 } else if (!strcmp("limited", arg) || 3214 !strcmp("kod", arg)) { 3215 /* transform to resany=... */ 3216 snprintf(buf, sizeof(buf), 3217 ", resany=0x%x", 3218 ('k' == arg[0]) 3219 ? RES_KOD 3220 : RES_LIMITED); 3221 cb = 1 + strlen(buf); 3222 if (parms + cb < 3223 parms_buf + sizeof(parms_buf)) { 3224 memcpy(parms, buf, cb); 3225 parms += cb - 1; 3226 } 3227 } else 3228 xprintf(stderr, 3229 "ignoring unrecognized mrulist parameter: %s\n", 3230 arg); 3231 } 3232 } 3233 parms = parms_buf; 3234 3235 if (!collect_mru_list(parms, &now)) 3236 return; 3237 3238 /* display the results */ 3239 if (rawmode) 3240 goto cleanup_return; 3241 3242 /* construct an array of entry pointers in default order */ 3243 sorted = eallocarray(mru_count, sizeof(*sorted)); 3244 ppentry = sorted; 3245 if (MRUSORT_R_DEF != order) { 3246 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) 3247 INSIST(ppentry < sorted + mru_count); 3248 *ppentry = recent; 3249 ppentry++; 3250 ITER_DLIST_END() 3251 } else { 3252 REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) 3253 INSIST(ppentry < sorted + mru_count); 3254 *ppentry = recent; 3255 ppentry++; 3256 REV_ITER_DLIST_END() 3257 } 3258 3259 if (ppentry - sorted != (int)mru_count) { 3260 xprintf(stderr, 3261 "mru_count %u should match MRU list depth %ld.\n", 3262 mru_count, (long)(ppentry - sorted)); 3263 free(sorted); 3264 goto cleanup_return; 3265 } 3266 3267 /* re-sort sorted[] if not default or reverse default */ 3268 if (MRUSORT_R_DEF < order) 3269 qsort(sorted, mru_count, sizeof(sorted[0]), 3270 mru_qcmp_table[order]); 3271 3272 mrulist_interrupted = FALSE; 3273 printf( "lstint avgint rstr r m v count rport remote address\n" 3274 "==============================================================================\n"); 3275 /* '=' x 78 */ 3276 for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) { 3277 recent = *ppentry; 3278 interval = now; 3279 L_SUB(&interval, &recent->last); 3280 LFPTOD(&interval, flstint); 3281 lstint = (int)(flstint + 0.5); 3282 interval = recent->last; 3283 L_SUB(&interval, &recent->first); 3284 LFPTOD(&interval, favgint); 3285 favgint /= recent->count; 3286 avgint = (int)(favgint + 0.5); 3287 xprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n", 3288 lstint, avgint, recent->rs, 3289 (RES_KOD & recent->rs) 3290 ? 'K' 3291 : (RES_LIMITED & recent->rs) 3292 ? 'L' 3293 : '.', 3294 (int)recent->mode, (int)recent->ver, 3295 recent->count, SRCPORT(&recent->addr), 3296 nntohost(&recent->addr)); 3297 if (showhostnames) 3298 fflush(fp); 3299 if (mrulist_interrupted) { 3300 xputs("\n --interrupted--\n", fp); 3301 fflush(fp); 3302 break; 3303 } 3304 } 3305 fflush(fp); 3306 if (debug) { 3307 xprintf(stderr, 3308 "--- completed, freeing sorted[] pointers\n"); 3309 fflush(stderr); 3310 } 3311 free(sorted); 3312 3313cleanup_return: 3314 if (debug) { 3315 xprintf(stderr, "... freeing MRU entries\n"); 3316 fflush(stderr); 3317 } 3318 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) 3319 free(recent); 3320 ITER_DLIST_END() 3321 if (debug) { 3322 xprintf(stderr, "... freeing hash_table[]\n"); 3323 fflush(stderr); 3324 } 3325 free(hash_table); 3326 hash_table = NULL; 3327 INIT_DLIST(mru_list, mlink); 3328 3329 pop_ctrl_c_handler(&mrulist_ctrl_c_hook); 3330} 3331 3332 3333/* 3334 * validate_ifnum - helper for ifstats() 3335 * 3336 * Ensures rows are received in order and complete. 3337 */ 3338static void 3339validate_ifnum( 3340 FILE * fp, 3341 u_int ifnum, 3342 int * pfields, 3343 ifstats_row * prow 3344 ) 3345{ 3346 if (prow->ifnum == ifnum) 3347 return; 3348 if (prow->ifnum + 1 <= ifnum) { 3349 if (*pfields < IFSTATS_FIELDS) 3350 xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n", 3351 *pfields, IFSTATS_FIELDS); 3352 *pfields = 0; 3353 prow->ifnum = ifnum; 3354 return; 3355 } 3356 xprintf(stderr, 3357 "received if index %u, have %d of %d fields for index %u, aborting.\n", 3358 ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum); 3359 exit(1); 3360} 3361 3362 3363/* 3364 * another_ifstats_field - helper for ifstats() 3365 * 3366 * If all fields for the row have been received, print it. 3367 */ 3368static void 3369another_ifstats_field( 3370 int * pfields, 3371 ifstats_row * prow, 3372 FILE * fp 3373 ) 3374{ 3375 u_int ifnum; 3376 3377 (*pfields)++; 3378 /* we understand 12 tags */ 3379 if (IFSTATS_FIELDS > *pfields) 3380 return; 3381 /* 3382 " interface name send\n" 3383 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n" 3384 "==============================================================================\n"); 3385 */ 3386 xprintf(fp, 3387 "%3u %-24.24s %c %4x %3u %2u %6u %6u %6u %5u %8d\n" 3388 " %s\n", 3389 prow->ifnum, prow->name, 3390 (prow->enabled) 3391 ? '.' 3392 : 'D', 3393 prow->flags, prow->ttl, prow->mcast_count, 3394 prow->received, prow->sent, prow->send_errors, 3395 prow->peer_count, prow->uptime, sptoa(&prow->addr)); 3396 if (!SOCK_UNSPEC(&prow->bcast)) 3397 xprintf(fp, " %s\n", sptoa(&prow->bcast)); 3398 ifnum = prow->ifnum; 3399 ZERO(*prow); 3400 prow->ifnum = ifnum; 3401} 3402 3403 3404/* 3405 * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats. 3406 */ 3407static void 3408ifstats( 3409 struct parse * pcmd, 3410 FILE * fp 3411 ) 3412{ 3413 const char addr_fmt[] = "addr.%u"; 3414 const char bcast_fmt[] = "bcast.%u"; 3415 const char en_fmt[] = "en.%u"; /* enabled */ 3416 const char flags_fmt[] = "flags.%u"; 3417 const char mc_fmt[] = "mc.%u"; /* mcast count */ 3418 const char name_fmt[] = "name.%u"; 3419 const char pc_fmt[] = "pc.%u"; /* peer count */ 3420 const char rx_fmt[] = "rx.%u"; 3421 const char tl_fmt[] = "tl.%u"; /* ttl */ 3422 const char tx_fmt[] = "tx.%u"; 3423 const char txerr_fmt[] = "txerr.%u"; 3424 const char up_fmt[] = "up.%u"; /* uptime */ 3425 const char * datap; 3426 int qres; 3427 size_t dsize; 3428 u_short rstatus; 3429 char * tag; 3430 char * val; 3431 int fields; 3432 u_int ui; 3433 ifstats_row row; 3434 int comprende; 3435 size_t len; 3436 3437 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus, 3438 &dsize, &datap); 3439 if (qres) /* message already displayed */ 3440 return; 3441 3442 xprintf(fp, 3443 " interface name send\n" 3444 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n" 3445 "==============================================================================\n"); 3446 /* '=' x 78 */ 3447 3448 ZERO(row); 3449 fields = 0; 3450 ui = 0; 3451 while (nextvar(&dsize, &datap, &tag, &val)) { 3452 INSIST(tag && val); 3453 if (debug > 1) 3454 xprintf(stderr, "nextvar gave: %s = %s\n", tag, val); 3455 comprende = FALSE; 3456 switch(tag[0]) { 3457 3458 case 'a': 3459 if (1 == sscanf(tag, addr_fmt, &ui) && 3460 decodenetnum(val, &row.addr)) 3461 comprende = TRUE; 3462 break; 3463 3464 case 'b': 3465 if (1 == sscanf(tag, bcast_fmt, &ui) && 3466 ('\0' == *val || 3467 decodenetnum(val, &row.bcast))) 3468 comprende = TRUE; 3469 break; 3470 3471 case 'e': 3472 if (1 == sscanf(tag, en_fmt, &ui) && 3473 1 == sscanf(val, "%d", &row.enabled)) 3474 comprende = TRUE; 3475 break; 3476 3477 case 'f': 3478 if (1 == sscanf(tag, flags_fmt, &ui) && 3479 1 == sscanf(val, "0x%x", &row.flags)) 3480 comprende = TRUE; 3481 break; 3482 3483 case 'm': 3484 if (1 == sscanf(tag, mc_fmt, &ui) && 3485 1 == sscanf(val, "%u", &row.mcast_count)) 3486 comprende = TRUE; 3487 break; 3488 3489 case 'n': 3490 if (1 == sscanf(tag, name_fmt, &ui)) { 3491 /* strip quotes */ 3492 len = strlen(val); 3493 if (len >= 2 && 3494 len - 2 < sizeof(row.name)) { 3495 len -= 2; 3496 memcpy(row.name, val + 1, len); 3497 row.name[len] = '\0'; 3498 comprende = TRUE; 3499 } 3500 } 3501 break; 3502 3503 case 'p': 3504 if (1 == sscanf(tag, pc_fmt, &ui) && 3505 1 == sscanf(val, "%u", &row.peer_count)) 3506 comprende = TRUE; 3507 break; 3508 3509 case 'r': 3510 if (1 == sscanf(tag, rx_fmt, &ui) && 3511 1 == sscanf(val, "%u", &row.received)) 3512 comprende = TRUE; 3513 break; 3514 3515 case 't': 3516 if (1 == sscanf(tag, tl_fmt, &ui) && 3517 1 == sscanf(val, "%u", &row.ttl)) 3518 comprende = TRUE; 3519 else if (1 == sscanf(tag, tx_fmt, &ui) && 3520 1 == sscanf(val, "%u", &row.sent)) 3521 comprende = TRUE; 3522 else if (1 == sscanf(tag, txerr_fmt, &ui) && 3523 1 == sscanf(val, "%u", &row.send_errors)) 3524 comprende = TRUE; 3525 break; 3526 3527 case 'u': 3528 if (1 == sscanf(tag, up_fmt, &ui) && 3529 1 == sscanf(val, "%u", &row.uptime)) 3530 comprende = TRUE; 3531 break; 3532 } 3533 3534 if (comprende) { 3535 /* error out if rows out of order */ 3536 validate_ifnum(fp, ui, &fields, &row); 3537 /* if the row is complete, print it */ 3538 another_ifstats_field(&fields, &row, fp); 3539 } 3540 } 3541 if (fields != IFSTATS_FIELDS) 3542 xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n", 3543 fields, IFSTATS_FIELDS); 3544 3545 fflush(fp); 3546} 3547 3548 3549/* 3550 * validate_reslist_idx - helper for reslist() 3551 * 3552 * Ensures rows are received in order and complete. 3553 */ 3554static void 3555validate_reslist_idx( 3556 FILE * fp, 3557 u_int idx, 3558 int * pfields, 3559 reslist_row * prow 3560 ) 3561{ 3562 if (prow->idx == idx) 3563 return; 3564 if (prow->idx + 1 == idx) { 3565 if (*pfields < RESLIST_FIELDS) 3566 xprintf(fp, "Warning: incomplete row with %d (of %d) fields", 3567 *pfields, RESLIST_FIELDS); 3568 *pfields = 0; 3569 prow->idx = idx; 3570 return; 3571 } 3572 xprintf(stderr, 3573 "received reslist index %u, have %d of %d fields for index %u, aborting.\n", 3574 idx, *pfields, RESLIST_FIELDS, prow->idx); 3575 exit(1); 3576} 3577 3578 3579/* 3580 * another_reslist_field - helper for reslist() 3581 * 3582 * If all fields for the row have been received, print it. 3583 */ 3584static void 3585another_reslist_field( 3586 int * pfields, 3587 reslist_row * prow, 3588 FILE * fp 3589 ) 3590{ 3591 char addrmaskstr[128]; 3592 int prefix; /* subnet mask as prefix bits count */ 3593 u_int idx; 3594 3595 (*pfields)++; 3596 /* we understand 4 tags */ 3597 if (RESLIST_FIELDS > *pfields) 3598 return; 3599 3600 prefix = sockaddr_masktoprefixlen(&prow->mask); 3601 if (prefix >= 0) 3602 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d", 3603 stoa(&prow->addr), prefix); 3604 else 3605 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s", 3606 stoa(&prow->addr), stoa(&prow->mask)); 3607 3608 /* 3609 " hits addr/prefix or addr mask\n" 3610 " restrictions\n" 3611 "==============================================================================\n"); 3612 */ 3613 xprintf(fp, 3614 "%10lu %s\n" 3615 " %s\n", 3616 prow->hits, addrmaskstr, prow->flagstr); 3617 idx = prow->idx; 3618 ZERO(*prow); 3619 prow->idx = idx; 3620} 3621 3622 3623/* 3624 * reslist - ntpq -c reslist modeled on ntpdc -c reslist. 3625 */ 3626static void 3627reslist( 3628 struct parse * pcmd, 3629 FILE * fp 3630 ) 3631{ 3632 const char addr_fmtu[] = "addr.%u"; 3633 const char mask_fmtu[] = "mask.%u"; 3634 const char hits_fmt[] = "hits.%u"; 3635 const char flags_fmt[] = "flags.%u"; 3636 const char qdata[] = "addr_restrictions"; 3637 const int qdata_chars = COUNTOF(qdata) - 1; 3638 const char * datap; 3639 int qres; 3640 size_t dsize; 3641 u_short rstatus; 3642 char * tag; 3643 char * val; 3644 int fields; 3645 u_int ui; 3646 reslist_row row; 3647 int comprende; 3648 size_t len; 3649 3650 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars, 3651 qdata, &rstatus, &dsize, &datap); 3652 if (qres) /* message already displayed */ 3653 return; 3654 3655 xprintf(fp, 3656 " hits addr/prefix or addr mask\n" 3657 " restrictions\n" 3658 "==============================================================================\n"); 3659 /* '=' x 78 */ 3660 3661 ZERO(row); 3662 fields = 0; 3663 ui = 0; 3664 while (nextvar(&dsize, &datap, &tag, &val)) { 3665 INSIST(tag && val); 3666 if (debug > 1) 3667 xprintf(stderr, "nextvar gave: %s = %s\n", tag, val); 3668 comprende = FALSE; 3669 switch(tag[0]) { 3670 3671 case 'a': 3672 if (1 == sscanf(tag, addr_fmtu, &ui) && 3673 decodenetnum(val, &row.addr)) 3674 comprende = TRUE; 3675 break; 3676 3677 case 'f': 3678 if (1 == sscanf(tag, flags_fmt, &ui)) { 3679 if (NULL == val) { 3680 row.flagstr[0] = '\0'; 3681 comprende = TRUE; 3682 } else if ((len = strlen(val)) < sizeof(row.flagstr)) { 3683 memcpy(row.flagstr, val, len); 3684 row.flagstr[len] = '\0'; 3685 comprende = TRUE; 3686 } else { 3687 /* no flags, and still !comprende */ 3688 row.flagstr[0] = '\0'; 3689 } 3690 } 3691 break; 3692 3693 case 'h': 3694 if (1 == sscanf(tag, hits_fmt, &ui) && 3695 1 == sscanf(val, "%lu", &row.hits)) 3696 comprende = TRUE; 3697 break; 3698 3699 case 'm': 3700 if (1 == sscanf(tag, mask_fmtu, &ui) && 3701 decodenetnum(val, &row.mask)) 3702 comprende = TRUE; 3703 break; 3704 } 3705 3706 if (comprende) { 3707 /* error out if rows out of order */ 3708 validate_reslist_idx(fp, ui, &fields, &row); 3709 /* if the row is complete, print it */ 3710 another_reslist_field(&fields, &row, fp); 3711 } 3712 } 3713 if (fields != RESLIST_FIELDS) 3714 xprintf(fp, "Warning: incomplete row with %d (of %d) fields", 3715 fields, RESLIST_FIELDS); 3716 3717 fflush(fp); 3718} 3719 3720 3721/* 3722 * collect_display_vdc 3723 */ 3724static void 3725collect_display_vdc( 3726 associd_t as, 3727 vdc * table, 3728 int decodestatus, 3729 FILE * fp 3730 ) 3731{ 3732 static const char * const suf[2] = { "adr", "port" }; 3733 static const char * const leapbits[4] = { "00", "01", 3734 "10", "11" }; 3735 struct varlist vl[MAXLIST]; 3736 char tagbuf[32]; 3737 vdc *pvdc; 3738 u_short rstatus; 3739 size_t rsize; 3740 const char *rdata; 3741 int qres; 3742 char *tag; 3743 char *val; 3744 u_int n; 3745 size_t len; 3746 int match; 3747 u_long ul; 3748 int vtype; 3749 sockaddr_u sau; 3750 3751 ZERO(vl); 3752 for (pvdc = table; pvdc->tag != NULL; pvdc++) { 3753 ZERO(pvdc->v); 3754 if (NTP_ADD != pvdc->type) { 3755 doaddvlist(vl, pvdc->tag); 3756 } else { 3757 for (n = 0; n < COUNTOF(suf); n++) { 3758 snprintf(tagbuf, sizeof(tagbuf), "%s%s", 3759 pvdc->tag, suf[n]); 3760 doaddvlist(vl, tagbuf); 3761 } 3762 } 3763 } 3764 qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize, 3765 &rdata); 3766 doclearvlist(vl); 3767 if (qres) 3768 return; /* error msg already displayed */ 3769 3770 /* 3771 * iterate over the response variables filling vdc_table with 3772 * the retrieved values. 3773 */ 3774 while (nextvar(&rsize, &rdata, &tag, &val)) { 3775 INSIST(tag && val); 3776 n = 0; 3777 for (pvdc = table; pvdc->tag != NULL; pvdc++) { 3778 len = strlen(pvdc->tag); 3779 if (strncmp(tag, pvdc->tag, len)) 3780 continue; 3781 if (NTP_ADD != pvdc->type) { 3782 if ('\0' != tag[len]) 3783 continue; 3784 break; 3785 } 3786 match = FALSE; 3787 for (n = 0; n < COUNTOF(suf); n++) { 3788 if (strcmp(tag + len, suf[n])) 3789 continue; 3790 match = TRUE; 3791 break; 3792 } 3793 if (match) 3794 break; 3795 } 3796 if (NULL == pvdc->tag) 3797 continue; 3798 switch (pvdc->type) { 3799 3800 case NTP_STR: 3801 /* strip surrounding double quotes */ 3802 if ('"' == val[0]) { 3803 len = strlen(val); 3804 if (len > 0 && '"' == val[len - 1]) { 3805 val[len - 1] = '\0'; 3806 val++; 3807 } 3808 } 3809 /* fallthru */ 3810 case NTP_REFID: /* fallthru */ 3811 case NTP_MODE: /* fallthru */ 3812 case NTP_2BIT: 3813 pvdc->v.str = estrdup(val); 3814 break; 3815 3816 case NTP_LFP: 3817 decodets(val, &pvdc->v.lfp); 3818 break; 3819 3820 case NTP_ADP: 3821 if (!decodenetnum(val, &pvdc->v.sau)) 3822 xprintf(stderr, "malformed %s=%s\n", 3823 pvdc->tag, val); 3824 break; 3825 3826 case NTP_ADD: 3827 if (0 == n) { /* adr */ 3828 if (!decodenetnum(val, &pvdc->v.sau)) 3829 xprintf(stderr, 3830 "malformed %s=%s\n", 3831 pvdc->tag, val); 3832 } else { /* port */ 3833 if (atouint(val, &ul)) 3834 SET_PORT(&pvdc->v.sau, 3835 (u_short)ul); 3836 } 3837 break; 3838 } 3839 } 3840 3841 /* and display */ 3842 if (decodestatus) { 3843 vtype = (0 == as) 3844 ? TYPE_SYS 3845 : TYPE_PEER; 3846 xprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus, 3847 statustoa(vtype, rstatus)); 3848 } 3849 3850 for (pvdc = table; pvdc->tag != NULL; pvdc++) { 3851 switch (pvdc->type) { 3852 3853 case NTP_STR: 3854 if (pvdc->v.str != NULL) { 3855 xprintf(fp, "%s %s\n", pvdc->display, 3856 pvdc->v.str); 3857 free(pvdc->v.str); 3858 pvdc->v.str = NULL; 3859 } 3860 break; 3861 3862 case NTP_ADD: /* fallthru */ 3863 case NTP_ADP: 3864 xprintf(fp, "%s %s\n", pvdc->display, 3865 nntohostp(&pvdc->v.sau)); 3866 break; 3867 3868 case NTP_LFP: 3869 xprintf(fp, "%s %s\n", pvdc->display, 3870 prettydate(&pvdc->v.lfp)); 3871 break; 3872 3873 case NTP_MODE: 3874 atouint(pvdc->v.str, &ul); 3875 xprintf(fp, "%s %s\n", pvdc->display, 3876 modetoa((int)ul)); 3877 free(pvdc->v.str); 3878 pvdc->v.str = NULL; 3879 break; 3880 3881 case NTP_2BIT: 3882 atouint(pvdc->v.str, &ul); 3883 xprintf(fp, "%s %s\n", pvdc->display, 3884 leapbits[ul & 0x3]); 3885 free(pvdc->v.str); 3886 pvdc->v.str = NULL; 3887 break; 3888 3889 case NTP_REFID: 3890 if (!decodenetnum(pvdc->v.str, &sau)) { 3891 fprintf(fp, "%s %s\n", pvdc->display, /* Text fmt */ 3892 pvdc->v.str); 3893 } else if (drefid == REFID_IPV4) { 3894 fprintf(fp, "%s %s\n", pvdc->display, /* IPv4 fmt */ 3895 stoa(&sau)); 3896 } else { 3897 fprintf (fp, "%s 0x%08x\n", pvdc->display, /* Hex / hash */ 3898 ntohl(addr2refid(&sau))); 3899 } 3900 free(pvdc->v.str); 3901 pvdc->v.str = NULL; 3902 break; 3903 3904 default: 3905 xprintf(stderr, "unexpected vdc type %d for %s\n", 3906 pvdc->type, pvdc->tag); 3907 break; 3908 } 3909 } 3910} 3911 3912 3913/* 3914 * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats 3915 */ 3916static void 3917sysstats( 3918 struct parse *pcmd, 3919 FILE *fp 3920 ) 3921{ 3922 static vdc sysstats_vdc[] = { 3923 VDC_INIT("ss_uptime", "uptime: ", NTP_STR), 3924 VDC_INIT("ss_reset", "sysstats reset: ", NTP_STR), 3925 VDC_INIT("ss_received", "packets received: ", NTP_STR), 3926 VDC_INIT("ss_thisver", "current version: ", NTP_STR), 3927 VDC_INIT("ss_oldver", "older version: ", NTP_STR), 3928 VDC_INIT("ss_badformat", "bad length or format: ", NTP_STR), 3929 VDC_INIT("ss_badauth", "authentication failed:", NTP_STR), 3930 VDC_INIT("ss_declined", "declined: ", NTP_STR), 3931 VDC_INIT("ss_restricted", "restricted: ", NTP_STR), 3932 VDC_INIT("ss_limited", "rate limited: ", NTP_STR), 3933 VDC_INIT("ss_kodsent", "KoD responses: ", NTP_STR), 3934 VDC_INIT("ss_processed", "processed for time: ", NTP_STR), 3935#if 0 3936 VDC_INIT("ss_lamport", "Lamport violations: ", NTP_STR), 3937 VDC_INIT("ss_tsrounding", "bad timestamp rounding:", NTP_STR), 3938#endif 3939 VDC_INIT(NULL, NULL, 0) 3940 }; 3941 3942 collect_display_vdc(0, sysstats_vdc, FALSE, fp); 3943} 3944 3945 3946/* 3947 * sysinfo - modeled on ntpdc's sysinfo 3948 */ 3949static void 3950sysinfo( 3951 struct parse *pcmd, 3952 FILE *fp 3953 ) 3954{ 3955 static vdc sysinfo_vdc[] = { 3956 VDC_INIT("peeradr", "system peer: ", NTP_ADP), 3957 VDC_INIT("peermode", "system peer mode: ", NTP_MODE), 3958 VDC_INIT("leap", "leap indicator: ", NTP_2BIT), 3959 VDC_INIT("stratum", "stratum: ", NTP_STR), 3960 VDC_INIT("precision", "log2 precision: ", NTP_STR), 3961 VDC_INIT("rootdelay", "root delay: ", NTP_STR), 3962 VDC_INIT("rootdisp", "root dispersion: ", NTP_STR), 3963 VDC_INIT("refid", "reference ID: ", NTP_REFID), 3964 VDC_INIT("reftime", "reference time: ", NTP_LFP), 3965 VDC_INIT("sys_jitter", "system jitter: ", NTP_STR), 3966 VDC_INIT("clk_jitter", "clock jitter: ", NTP_STR), 3967 VDC_INIT("clk_wander", "clock wander: ", NTP_STR), 3968 VDC_INIT("bcastdelay", "broadcast delay: ", NTP_STR), 3969 VDC_INIT("authdelay", "symm. auth. delay:", NTP_STR), 3970 VDC_INIT(NULL, NULL, 0) 3971 }; 3972 3973 collect_display_vdc(0, sysinfo_vdc, TRUE, fp); 3974} 3975 3976 3977/* 3978 * kerninfo - modeled on ntpdc's kerninfo 3979 */ 3980static void 3981kerninfo( 3982 struct parse *pcmd, 3983 FILE *fp 3984 ) 3985{ 3986 static vdc kerninfo_vdc[] = { 3987 VDC_INIT("koffset", "pll offset: ", NTP_STR), 3988 VDC_INIT("kfreq", "pll frequency: ", NTP_STR), 3989 VDC_INIT("kmaxerr", "maximum error: ", NTP_STR), 3990 VDC_INIT("kesterr", "estimated error: ", NTP_STR), 3991 VDC_INIT("kstflags", "kernel status: ", NTP_STR), 3992 VDC_INIT("ktimeconst", "pll time constant: ", NTP_STR), 3993 VDC_INIT("kprecis", "precision: ", NTP_STR), 3994 VDC_INIT("kfreqtol", "frequency tolerance: ", NTP_STR), 3995 VDC_INIT("kppsfreq", "pps frequency: ", NTP_STR), 3996 VDC_INIT("kppsstab", "pps stability: ", NTP_STR), 3997 VDC_INIT("kppsjitter", "pps jitter: ", NTP_STR), 3998 VDC_INIT("kppscalibdur", "calibration interval ", NTP_STR), 3999 VDC_INIT("kppscalibs", "calibration cycles: ", NTP_STR), 4000 VDC_INIT("kppsjitexc", "jitter exceeded: ", NTP_STR), 4001 VDC_INIT("kppsstbexc", "stability exceeded: ", NTP_STR), 4002 VDC_INIT("kppscaliberrs", "calibration errors: ", NTP_STR), 4003 VDC_INIT(NULL, NULL, 0) 4004 }; 4005 4006 collect_display_vdc(0, kerninfo_vdc, TRUE, fp); 4007} 4008 4009 4010/* 4011 * monstats - implements ntpq -c monstats 4012 */ 4013static void 4014monstats( 4015 struct parse *pcmd, 4016 FILE *fp 4017 ) 4018{ 4019 static vdc monstats_vdc[] = { 4020 VDC_INIT("mru_enabled", "enabled: ", NTP_STR), 4021 VDC_INIT("mru_depth", "addresses: ", NTP_STR), 4022 VDC_INIT("mru_deepest", "peak addresses: ", NTP_STR), 4023 VDC_INIT("mru_maxdepth", "maximum addresses: ", NTP_STR), 4024 VDC_INIT("mru_mindepth", "reclaim above count:", NTP_STR), 4025 VDC_INIT("mru_maxage", "reclaim older than: ", NTP_STR), 4026 VDC_INIT("mru_mem", "kilobytes: ", NTP_STR), 4027 VDC_INIT("mru_maxmem", "maximum kilobytes: ", NTP_STR), 4028 VDC_INIT(NULL, NULL, 0) 4029 }; 4030 4031 collect_display_vdc(0, monstats_vdc, FALSE, fp); 4032} 4033 4034 4035/* 4036 * iostats - ntpq -c iostats - network input and output counters 4037 */ 4038static void 4039iostats( 4040 struct parse *pcmd, 4041 FILE *fp 4042 ) 4043{ 4044 static vdc iostats_vdc[] = { 4045 VDC_INIT("iostats_reset", "time since reset: ", NTP_STR), 4046 VDC_INIT("total_rbuf", "receive buffers: ", NTP_STR), 4047 VDC_INIT("free_rbuf", "free receive buffers: ", NTP_STR), 4048 VDC_INIT("used_rbuf", "used receive buffers: ", NTP_STR), 4049 VDC_INIT("rbuf_lowater", "low water refills: ", NTP_STR), 4050 VDC_INIT("io_dropped", "dropped packets: ", NTP_STR), 4051 VDC_INIT("io_ignored", "ignored packets: ", NTP_STR), 4052 VDC_INIT("io_received", "received packets: ", NTP_STR), 4053 VDC_INIT("io_sent", "packets sent: ", NTP_STR), 4054 VDC_INIT("io_sendfailed", "packet send failures: ", NTP_STR), 4055 VDC_INIT("io_wakeups", "input wakeups: ", NTP_STR), 4056 VDC_INIT("io_goodwakeups", "useful input wakeups: ", NTP_STR), 4057 VDC_INIT(NULL, NULL, 0) 4058 }; 4059 4060 collect_display_vdc(0, iostats_vdc, FALSE, fp); 4061} 4062 4063 4064/* 4065 * timerstats - ntpq -c timerstats - interval timer counters 4066 */ 4067static void 4068timerstats( 4069 struct parse *pcmd, 4070 FILE *fp 4071 ) 4072{ 4073 static vdc timerstats_vdc[] = { 4074 VDC_INIT("timerstats_reset", "time since reset: ", NTP_STR), 4075 VDC_INIT("timer_overruns", "timer overruns: ", NTP_STR), 4076 VDC_INIT("timer_xmts", "calls to transmit: ", NTP_STR), 4077 VDC_INIT(NULL, NULL, 0) 4078 }; 4079 4080 collect_display_vdc(0, timerstats_vdc, FALSE, fp); 4081} 4082 4083 4084/* 4085 * authinfo - implements ntpq -c authinfo 4086 */ 4087static void 4088authinfo( 4089 struct parse *pcmd, 4090 FILE *fp 4091 ) 4092{ 4093 static vdc authinfo_vdc[] = { 4094 VDC_INIT("authreset", "time since reset:", NTP_STR), 4095 VDC_INIT("authkeys", "stored keys: ", NTP_STR), 4096 VDC_INIT("authfreek", "free keys: ", NTP_STR), 4097 VDC_INIT("authklookups", "key lookups: ", NTP_STR), 4098 VDC_INIT("authknotfound", "keys not found: ", NTP_STR), 4099 VDC_INIT("authkuncached", "uncached keys: ", NTP_STR), 4100 VDC_INIT("authkexpired", "expired keys: ", NTP_STR), 4101 VDC_INIT("authencrypts", "encryptions: ", NTP_STR), 4102 VDC_INIT("authdecrypts", "decryptions: ", NTP_STR), 4103 VDC_INIT(NULL, NULL, 0) 4104 }; 4105 4106 collect_display_vdc(0, authinfo_vdc, FALSE, fp); 4107} 4108 4109 4110/* 4111 * pstats - show statistics for a peer 4112 */ 4113static void 4114pstats( 4115 struct parse *pcmd, 4116 FILE *fp 4117 ) 4118{ 4119 static vdc pstats_vdc[] = { 4120 VDC_INIT("src", "remote host: ", NTP_ADD), 4121 VDC_INIT("dst", "local address: ", NTP_ADD), 4122 VDC_INIT("timerec", "time last received: ", NTP_STR), 4123 VDC_INIT("timer", "time until next send:", NTP_STR), 4124 VDC_INIT("timereach", "reachability change: ", NTP_STR), 4125 VDC_INIT("sent", "packets sent: ", NTP_STR), 4126 VDC_INIT("received", "packets received: ", NTP_STR), 4127 VDC_INIT("badauth", "bad authentication: ", NTP_STR), 4128 VDC_INIT("bogusorg", "bogus origin: ", NTP_STR), 4129 VDC_INIT("oldpkt", "duplicate: ", NTP_STR), 4130 VDC_INIT("seldisp", "bad dispersion: ", NTP_STR), 4131 VDC_INIT("selbroken", "bad reference time: ", NTP_STR), 4132 VDC_INIT("candidate", "candidate order: ", NTP_STR), 4133 VDC_INIT(NULL, NULL, 0) 4134 }; 4135 associd_t associd; 4136 4137 associd = checkassocid(pcmd->argval[0].uval); 4138 if (0 == associd) 4139 return; 4140 4141 collect_display_vdc(associd, pstats_vdc, TRUE, fp); 4142} 4143