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 13 extern char currenthost[]; 14 extern int currenthostisnum; 15 size_t maxhostlen; 16 17 /* 18 * Declarations for command handlers in here 19 */ 20 static associd_t checkassocid (u_int32); 21 static struct varlist *findlistvar (struct varlist *, char *); 22 static void doaddvlist (struct varlist *, const char *); 23 static void dormvlist (struct varlist *, const char *); 24 static void doclearvlist (struct varlist *); 25 static void makequerydata (struct varlist *, size_t *, char *); 26 static int doquerylist (struct varlist *, int, associd_t, int, 27 u_short *, size_t *, const char **); 28 static void doprintvlist (struct varlist *, FILE *); 29 static void addvars (struct parse *, FILE *); 30 static void rmvars (struct parse *, FILE *); 31 static void clearvars (struct parse *, FILE *); 32 static void showvars (struct parse *, FILE *); 33 static int dolist (struct varlist *, associd_t, int, int, 34 FILE *); 35 static void readlist (struct parse *, FILE *); 36 static void writelist (struct parse *, FILE *); 37 static void readvar (struct parse *, FILE *); 38 static void writevar (struct parse *, FILE *); 39 static void clocklist (struct parse *, FILE *); 40 static void clockvar (struct parse *, FILE *); 41 static int findassidrange (u_int32, u_int32, int *, int *, 42 FILE *); 43 static void mreadlist (struct parse *, FILE *); 44 static void mreadvar (struct parse *, FILE *); 45 static void printassoc (int, FILE *); 46 static void associations (struct parse *, FILE *); 47 static void lassociations (struct parse *, FILE *); 48 static void passociations (struct parse *, FILE *); 49 static void lpassociations (struct parse *, FILE *); 50 51 #ifdef UNUSED 52 static void radiostatus (struct parse *, FILE *); 53 #endif /* UNUSED */ 54 55 static void authinfo (struct parse *, FILE *); 56 static void pstats (struct parse *, FILE *); 57 static long when (l_fp *, l_fp *, l_fp *); 58 static char * prettyinterval (char *, size_t, long); 59 static int doprintpeers (struct varlist *, int, int, size_t, const char *, FILE *, int); 60 static int dogetpeers (struct varlist *, associd_t, FILE *, int); 61 static void dopeers (int, FILE *, int); 62 static void peers (struct parse *, FILE *); 63 static void doapeers (int, FILE *, int); 64 static void apeers (struct parse *, FILE *); 65 static void lpeers (struct parse *, FILE *); 66 static void doopeers (int, FILE *, int); 67 static void opeers (struct parse *, FILE *); 68 static void lopeers (struct parse *, FILE *); 69 static void config (struct parse *, FILE *); 70 static void saveconfig (struct parse *, FILE *); 71 static void config_from_file(struct parse *, FILE *); 72 static void mrulist (struct parse *, FILE *); 73 static void ifstats (struct parse *, FILE *); 74 static void reslist (struct parse *, FILE *); 75 static void sysstats (struct parse *, FILE *); 76 static void sysinfo (struct parse *, FILE *); 77 static void kerninfo (struct parse *, FILE *); 78 static void monstats (struct parse *, FILE *); 79 static void iostats (struct parse *, FILE *); 80 static void timerstats (struct parse *, FILE *); 81 82 /* 83 * Commands we understand. Ntpdc imports this. 84 */ 85 struct 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 */ 233 typedef 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 245 const 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 256 typedef 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 273 char flash2[] = " .+* "; /* flash decode for version 2 */ 274 char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */ 275 276 struct varlist { 277 const char *name; 278 char *value; 279 } g_varlist[MAXLIST] = { { 0, 0 } }; 280 281 /* 282 * Imported from ntpq.c 283 */ 284 extern int showhostnames; 285 extern int wideremote; 286 extern int rawmode; 287 extern struct servent *server_entry; 288 extern struct association *assoc_cache; 289 extern u_char pktversion; 290 291 typedef struct mru_tag mru; 292 struct 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 304 typedef 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 320 typedef 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 328 typedef 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 */ 346 static int mrulist_ctrl_c_hook(void); 347 static mru * add_mru(mru *); 348 static int collect_mru_list(const char *, l_fp *); 349 static int fetch_nonce(char *, size_t); 350 static int qcmp_mru_avgint(const void *, const void *); 351 static int qcmp_mru_r_avgint(const void *, const void *); 352 static int qcmp_mru_addr(const void *, const void *); 353 static int qcmp_mru_r_addr(const void *, const void *); 354 static int qcmp_mru_count(const void *, const void *); 355 static int qcmp_mru_r_count(const void *, const void *); 356 static void validate_ifnum(FILE *, u_int, int *, ifstats_row *); 357 static void another_ifstats_field(int *, ifstats_row *, FILE *); 358 static void collect_display_vdc(associd_t as, vdc *table, 359 int decodestatus, FILE *fp); 360 361 static int xprintf(FILE *, char const *, ...) NTP_PRINTF(2, 3); 362 static int xputs(char const *, FILE *); 363 static int xputc(int, FILE *); 364 365 /* 366 * static globals 367 */ 368 static u_int mru_count; 369 static u_int mru_dupes; 370 volatile int mrulist_interrupted; 371 static mru mru_list; /* listhead */ 372 static 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 */ 378 static 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 */ 392 static int 393 xprintf( 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 408 static int 409 xputs( 410 char const * str, 411 FILE * ofp 412 ) 413 { 414 return fputs(str, (ofp ? ofp : stderr)); 415 } 416 417 static int 418 xputc( 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 */ 429 static associd_t 430 checkassocid( 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 */ 456 static struct varlist * 457 findlistvar( 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 */ 477 static void 478 doaddvlist( 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 */ 513 static void 514 dormvlist( 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 */ 549 static void 550 doclearvlist( 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 */ 570 static void 571 makequerydata( 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 */ 616 static int 617 doquerylist( 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 */ 641 static void 642 doprintvlist( 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*/ 666 static void 667 addvars( 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*/ 680 static void 681 rmvars( 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*/ 694 static void 695 clearvars( 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*/ 708 static void 709 showvars( 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 */ 721 static int 722 dolist( 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 */ 774 static void 775 readlist( 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 */ 803 static void 804 writelist( 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 */ 847 static void 848 readvar( 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 */ 885 static void 886 writevar( 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 */ 934 static void 935 clocklist( 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 */ 959 static void 960 clockvar( 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 */ 987 static int 988 findassidrange( 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 */ 1041 static void 1042 mreadlist( 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 */ 1069 static void 1070 mreadvar( 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 */ 1109 int 1110 dogetassoc( 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 */ 1172 static void 1173 printassoc( 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 char numev[32]; 1190 1191 if (numassoc == 0) { 1192 (void) xprintf(fp, "No association ID's in list\n"); 1193 return; 1194 } 1195 1196 /* 1197 * Output a header 1198 */ 1199 (void) xprintf(fp, 1200 "ind assid status conf reach auth condition last_event cnt\n"); 1201 (void) xprintf(fp, 1202 "===========================================================\n"); 1203 for (i = 0; i < numassoc; i++) { 1204 statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status); 1205 if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH))) 1206 continue; 1207 event = CTL_PEER_EVENT(assoc_cache[i].status); 1208 event_count = CTL_PEER_NEVNT(assoc_cache[i].status); 1209 if (statval & CTL_PST_CONFIG) 1210 conf = "yes"; 1211 else 1212 conf = "no"; 1213 if (statval & CTL_PST_BCAST) { 1214 reach = "none"; 1215 if (statval & CTL_PST_AUTHENABLE) 1216 auth = "yes"; 1217 else 1218 auth = "none"; 1219 } else { 1220 if (statval & CTL_PST_REACH) 1221 reach = "yes"; 1222 else 1223 reach = "no"; 1224 if (statval & CTL_PST_AUTHENABLE) { 1225 if (statval & CTL_PST_AUTHENTIC) 1226 auth = "ok "; 1227 else 1228 auth = "bad"; 1229 } else { 1230 auth = "none"; 1231 } 1232 } 1233 if (pktversion > NTP_OLDVERSION) { 1234 switch (statval & 0x7) { 1235 1236 case CTL_PST_SEL_REJECT: 1237 condition = "reject"; 1238 break; 1239 1240 case CTL_PST_SEL_SANE: 1241 condition = "falsetick"; 1242 break; 1243 1244 case CTL_PST_SEL_CORRECT: 1245 condition = "excess"; 1246 break; 1247 1248 case CTL_PST_SEL_SELCAND: 1249 condition = "outlier"; 1250 break; 1251 1252 case CTL_PST_SEL_SYNCCAND: 1253 condition = "candidate"; 1254 break; 1255 1256 case CTL_PST_SEL_EXCESS: 1257 condition = "backup"; 1258 break; 1259 1260 case CTL_PST_SEL_SYSPEER: 1261 condition = "sys.peer"; 1262 break; 1263 1264 case CTL_PST_SEL_PPS: 1265 condition = "pps.peer"; 1266 break; 1267 } 1268 } else { 1269 switch (statval & 0x3) { 1270 1271 case OLD_CTL_PST_SEL_REJECT: 1272 if (!(statval & OLD_CTL_PST_SANE)) 1273 condition = "insane"; 1274 else if (!(statval & OLD_CTL_PST_DISP)) 1275 condition = "hi_disp"; 1276 else 1277 condition = ""; 1278 break; 1279 1280 case OLD_CTL_PST_SEL_SELCAND: 1281 condition = "sel_cand"; 1282 break; 1283 1284 case OLD_CTL_PST_SEL_SYNCCAND: 1285 condition = "sync_cand"; 1286 break; 1287 1288 case OLD_CTL_PST_SEL_SYSPEER: 1289 condition = "sys_peer"; 1290 break; 1291 } 1292 } 1293 switch (PEER_EVENT|event) { 1294 1295 case PEVNT_MOBIL: 1296 last_event = "mobilize"; 1297 break; 1298 1299 case PEVNT_DEMOBIL: 1300 last_event = "demobilize"; 1301 break; 1302 1303 case PEVNT_REACH: 1304 last_event = "reachable"; 1305 break; 1306 1307 case PEVNT_UNREACH: 1308 last_event = "unreachable"; 1309 break; 1310 1311 case PEVNT_RESTART: 1312 last_event = "restart"; 1313 break; 1314 1315 case PEVNT_REPLY: 1316 last_event = "no_reply"; 1317 break; 1318 1319 case PEVNT_RATE: 1320 last_event = "rate_exceeded"; 1321 break; 1322 1323 case PEVNT_DENY: 1324 last_event = "access_denied"; 1325 break; 1326 1327 case PEVNT_ARMED: 1328 last_event = "leap_armed"; 1329 break; 1330 1331 case PEVNT_NEWPEER: 1332 last_event = "sys_peer"; 1333 break; 1334 1335 case PEVNT_CLOCK: 1336 last_event = "clock_alarm"; 1337 break; 1338 1339 case PEVNT_AUTH: 1340 last_event = "bad_auth"; 1341 break; 1342 1343 case PEVNT_POPCORN: 1344 last_event = "popcorn"; 1345 break; 1346 1347 case PEVNT_XLEAVE: 1348 last_event = "interleave"; 1349 break; 1350 1351 case PEVNT_XERR: 1352 last_event = "xleave_err"; 1353 break; 1354 1355 default: 1356 snprintf(numev, sizeof(numev), "<?%x?>", event); 1357 last_event = numev; 1358 break; 1359 } 1360 snprintf(buf, sizeof(buf), 1361 "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2lu", 1362 i + 1, assoc_cache[i].assid, 1363 assoc_cache[i].status, conf, reach, auth, 1364 condition, last_event, event_count); 1365 bp = buf + strlen(buf); 1366 while (bp > buf && ' ' == bp[-1]) 1367 --bp; 1368 bp[0] = '\0'; 1369 xprintf(fp, "%s\n", buf); 1370 } 1371 } 1372 1373 1374 /* 1375 * associations - get, record and print a list of associations 1376 */ 1377 /*ARGSUSED*/ 1378 static void 1379 associations( 1380 struct parse *pcmd, 1381 FILE *fp 1382 ) 1383 { 1384 if (dogetassoc(fp)) 1385 printassoc(0, fp); 1386 } 1387 1388 1389 /* 1390 * lassociations - get, record and print a long list of associations 1391 */ 1392 /*ARGSUSED*/ 1393 static void 1394 lassociations( 1395 struct parse *pcmd, 1396 FILE *fp 1397 ) 1398 { 1399 if (dogetassoc(fp)) 1400 printassoc(1, fp); 1401 } 1402 1403 1404 /* 1405 * passociations - print the association list 1406 */ 1407 /*ARGSUSED*/ 1408 static void 1409 passociations( 1410 struct parse *pcmd, 1411 FILE *fp 1412 ) 1413 { 1414 printassoc(0, fp); 1415 } 1416 1417 1418 /* 1419 * lpassociations - print the long association list 1420 */ 1421 /*ARGSUSED*/ 1422 static void 1423 lpassociations( 1424 struct parse *pcmd, 1425 FILE *fp 1426 ) 1427 { 1428 printassoc(1, fp); 1429 } 1430 1431 1432 /* 1433 * saveconfig - dump ntp server configuration to server file 1434 */ 1435 static void 1436 saveconfig( 1437 struct parse *pcmd, 1438 FILE *fp 1439 ) 1440 { 1441 const char *datap; 1442 int res; 1443 size_t dsize; 1444 u_short rstatus; 1445 1446 if (0 == pcmd->nargs) 1447 return; 1448 1449 res = doquery(CTL_OP_SAVECONFIG, 0, 1, 1450 strlen(pcmd->argval[0].string), 1451 pcmd->argval[0].string, &rstatus, &dsize, 1452 &datap); 1453 1454 if (res != 0) 1455 return; 1456 1457 if (0 == dsize) 1458 xprintf(fp, "(no response message, curiously)"); 1459 else 1460 xprintf(fp, "%.*s", (int)dsize, datap); /* cast is wobbly */ 1461 } 1462 1463 1464 #ifdef UNUSED 1465 /* 1466 * radiostatus - print the radio status returned by the server 1467 */ 1468 /*ARGSUSED*/ 1469 static void 1470 radiostatus( 1471 struct parse *pcmd, 1472 FILE *fp 1473 ) 1474 { 1475 char *datap; 1476 int res; 1477 int dsize; 1478 u_short rstatus; 1479 1480 res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus, 1481 &dsize, &datap); 1482 1483 if (res != 0) 1484 return; 1485 1486 if (numhosts > 1) 1487 (void) xprintf(fp, "server=%s ", currenthost); 1488 if (dsize == 0) { 1489 (void) xprintf(fp, "No radio status string returned\n"); 1490 return; 1491 } 1492 1493 asciize(dsize, datap, fp); 1494 } 1495 #endif /* UNUSED */ 1496 1497 /* 1498 * when - return how long its been since his last packet arrived 1499 */ 1500 static long 1501 when( 1502 l_fp *ts, 1503 l_fp *rec, 1504 l_fp *reftime 1505 ) 1506 { 1507 l_fp *lasttime; 1508 1509 if (rec->l_ui != 0) 1510 lasttime = rec; 1511 else if (reftime->l_ui != 0) 1512 lasttime = reftime; 1513 else 1514 return 0; 1515 1516 if (ts->l_ui < lasttime->l_ui) 1517 return -1; 1518 return (ts->l_ui - lasttime->l_ui); 1519 } 1520 1521 1522 /* 1523 * Pretty-print an interval into the given buffer, in a human-friendly format. 1524 */ 1525 static char * 1526 prettyinterval( 1527 char *buf, 1528 size_t cb, 1529 long diff 1530 ) 1531 { 1532 if (diff <= 0) { 1533 buf[0] = '-'; 1534 buf[1] = 0; 1535 return buf; 1536 } 1537 1538 if (diff <= 2048) { 1539 snprintf(buf, cb, "%u", (unsigned int)diff); 1540 return buf; 1541 } 1542 1543 diff = (diff + 29) / 60; 1544 if (diff <= 300) { 1545 snprintf(buf, cb, "%um", (unsigned int)diff); 1546 return buf; 1547 } 1548 1549 diff = (diff + 29) / 60; 1550 if (diff <= 96) { 1551 snprintf(buf, cb, "%uh", (unsigned int)diff); 1552 return buf; 1553 } 1554 1555 diff = (diff + 11) / 24; 1556 if (diff <= 999) { 1557 snprintf(buf, cb, "%ud", (unsigned int)diff); 1558 return buf; 1559 } 1560 1561 /* years are only approximated... */ 1562 diff = (long)floor(diff / 365.25 + 0.5); 1563 if (diff <= 999) { 1564 snprintf(buf, cb, "%uy", (unsigned int)diff); 1565 return buf; 1566 } 1567 /* Ok, this amounts to infinity... */ 1568 strlcpy(buf, "INF", cb); 1569 return buf; 1570 } 1571 1572 static char 1573 decodeaddrtype( 1574 sockaddr_u *sock 1575 ) 1576 { 1577 char ch = '-'; 1578 u_int32 dummy; 1579 1580 switch(AF(sock)) { 1581 case AF_INET: 1582 dummy = SRCADR(sock); 1583 ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' : 1584 ((dummy&0x000000ff)==0x000000ff) ? 'b' : 1585 ((dummy&0xffffffff)==0x7f000001) ? 'l' : 1586 ((dummy&0xffffffe0)==0x00000000) ? '-' : 1587 'u'); 1588 break; 1589 case AF_INET6: 1590 if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock))) 1591 ch = 'm'; 1592 else 1593 ch = 'u'; 1594 break; 1595 default: 1596 ch = '-'; 1597 break; 1598 } 1599 return ch; 1600 } 1601 1602 /* 1603 * A list of variables required by the peers command 1604 */ 1605 struct varlist opeervarlist[] = { 1606 { "srcadr", 0 }, /* 0 */ 1607 { "dstadr", 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 { 0, 0 } 1621 }; 1622 1623 struct varlist peervarlist[] = { 1624 { "srcadr", 0 }, /* 0 */ 1625 { "refid", 0 }, /* 1 */ 1626 { "stratum", 0 }, /* 2 */ 1627 { "hpoll", 0 }, /* 3 */ 1628 { "ppoll", 0 }, /* 4 */ 1629 { "reach", 0 }, /* 5 */ 1630 { "delay", 0 }, /* 6 */ 1631 { "offset", 0 }, /* 7 */ 1632 { "jitter", 0 }, /* 8 */ 1633 { "dispersion", 0 }, /* 9 */ 1634 { "rec", 0 }, /* 10 */ 1635 { "reftime", 0 }, /* 11 */ 1636 { "srcport", 0 }, /* 12 */ 1637 { "hmode", 0 }, /* 13 */ 1638 { "srchost", 0 }, /* 14 */ 1639 { 0, 0 } 1640 }; 1641 1642 struct varlist apeervarlist[] = { 1643 { "srcadr", 0 }, /* 0 */ 1644 { "refid", 0 }, /* 1 */ 1645 { "assid", 0 }, /* 2 */ 1646 { "stratum", 0 }, /* 3 */ 1647 { "hpoll", 0 }, /* 4 */ 1648 { "ppoll", 0 }, /* 5 */ 1649 { "reach", 0 }, /* 6 */ 1650 { "delay", 0 }, /* 7 */ 1651 { "offset", 0 }, /* 8 */ 1652 { "jitter", 0 }, /* 9 */ 1653 { "dispersion", 0 }, /* 10 */ 1654 { "rec", 0 }, /* 11 */ 1655 { "reftime", 0 }, /* 12 */ 1656 { "srcport", 0 }, /* 13 */ 1657 { "hmode", 0 }, /* 14 */ 1658 { "srchost", 0 }, /* 15 */ 1659 { 0, 0 } 1660 }; 1661 1662 1663 /* 1664 * Decode an incoming data buffer and print a line in the peer list 1665 */ 1666 static int 1667 doprintpeers( 1668 struct varlist *pvl, 1669 int associd, 1670 int rstatus, 1671 size_t datalen, 1672 const char *data, 1673 FILE *fp, 1674 int af 1675 ) 1676 { 1677 char *name; 1678 char *value = NULL; 1679 int c; 1680 size_t len; 1681 int have_srchost; 1682 int have_dstadr; 1683 int have_da_rid; 1684 int have_jitter; 1685 sockaddr_u srcadr; 1686 sockaddr_u dstadr; 1687 sockaddr_u dum_store; 1688 sockaddr_u refidadr; 1689 long hmode = 0; 1690 u_long srcport = 0; 1691 u_int32 u32; 1692 const char *dstadr_refid = "0.0.0.0"; 1693 const char *serverlocal; 1694 char *drbuf = NULL; 1695 size_t drlen; 1696 u_long stratum = 0; 1697 long ppoll = 0; 1698 long hpoll = 0; 1699 u_long reach = 0; 1700 l_fp estoffset; 1701 l_fp estdelay; 1702 l_fp estjitter; 1703 l_fp estdisp; 1704 l_fp reftime; 1705 l_fp rec; 1706 l_fp ts; 1707 u_long poll_sec; 1708 u_long flash = 0; 1709 char type = '?'; 1710 char clock_name[LENHOSTNAME]; 1711 char whenbuf[12], pollbuf[12]; 1712 /* [Bug 3482] formally whenbuf & pollbuf should be able to hold 1713 * a full signed int. Not that we would use that much string 1714 * data for it... 1715 */ 1716 get_systime(&ts); 1717 1718 have_srchost = FALSE; 1719 have_dstadr = FALSE; 1720 have_da_rid = FALSE; 1721 have_jitter = FALSE; 1722 ZERO_SOCK(&srcadr); 1723 ZERO_SOCK(&dstadr); 1724 clock_name[0] = '\0'; 1725 ZERO(estoffset); 1726 ZERO(estdelay); 1727 ZERO(estjitter); 1728 ZERO(estdisp); 1729 1730 while (nextvar(&datalen, &data, &name, &value)) { 1731 INSIST(name && value); 1732 if (!strcmp("srcadr", name) || 1733 !strcmp("peeradr", name)) { 1734 if (!decodenetnum(value, &srcadr)) 1735 xprintf(stderr, "malformed %s=%s\n", 1736 name, value); 1737 } else if (!strcmp("srchost", name)) { 1738 if (pvl == peervarlist || pvl == apeervarlist) { 1739 len = strlen(value); 1740 if (2 < len && 1741 (size_t)len < sizeof(clock_name)) { 1742 /* strip quotes */ 1743 value++; 1744 len -= 2; 1745 memcpy(clock_name, value, len); 1746 clock_name[len] = '\0'; 1747 have_srchost = TRUE; 1748 } 1749 } 1750 } else if (!strcmp("dstadr", name)) { 1751 if (decodenetnum(value, &dum_store)) { 1752 type = decodeaddrtype(&dum_store); 1753 have_dstadr = TRUE; 1754 dstadr = dum_store; 1755 if (pvl == opeervarlist) { 1756 have_da_rid = TRUE; 1757 dstadr_refid = trunc_left(stoa(&dstadr), 15); 1758 } 1759 } 1760 } else if (!strcmp("hmode", name)) { 1761 decodeint(value, &hmode); 1762 } else if (!strcmp("refid", name)) { 1763 if ( (pvl == peervarlist) 1764 && (drefid == REFID_IPV4)) { 1765 have_da_rid = TRUE; 1766 drlen = strlen(value); 1767 if (0 == drlen) { 1768 dstadr_refid = ""; 1769 } else if (drlen <= 4) { 1770 ZERO(u32); 1771 memcpy(&u32, value, drlen); 1772 dstadr_refid = refid_str(u32, 1); 1773 } else if (decodenetnum(value, &refidadr)) { 1774 if (SOCK_UNSPEC(&refidadr)) 1775 dstadr_refid = "0.0.0.0"; 1776 else if (ISREFCLOCKADR(&refidadr)) { 1777 dstadr_refid = 1778 refnumtoa(&refidadr); 1779 } else { 1780 dstadr_refid = 1781 stoa(&refidadr); 1782 } 1783 } else { 1784 have_da_rid = FALSE; 1785 } 1786 } else if ( (pvl == apeervarlist) 1787 || (pvl == peervarlist)) { 1788 /* no need to check drefid == REFID_HASH */ 1789 have_da_rid = TRUE; 1790 drlen = strlen(value); 1791 if (0 == drlen) { 1792 dstadr_refid = ""; 1793 } else if (drlen <= 4) { 1794 ZERO(u32); 1795 memcpy(&u32, value, drlen); 1796 dstadr_refid = refid_str(u32, 1); 1797 //xprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value); 1798 } else if (decodenetnum(value, &refidadr)) { 1799 if (SOCK_UNSPEC(&refidadr)) 1800 dstadr_refid = "0.0.0.0"; 1801 else if (ISREFCLOCKADR(&refidadr)) { 1802 dstadr_refid = 1803 refnumtoa(&refidadr); 1804 if (pvl == apeervarlist) { 1805 /* 1806 * restrict refid to 1807 * 8 chars [Bug 3850] 1808 */ 1809 dstadr_refid = 1810 trunc_right( 1811 dstadr_refid, 1812 8); 1813 } 1814 } else { 1815 drbuf = emalloc(10); 1816 snprintf(drbuf, 10, "%0x", 1817 SRCADR(&refidadr)); 1818 dstadr_refid = drbuf; 1819 } 1820 } else { 1821 have_da_rid = FALSE; 1822 } 1823 } 1824 } else if (!strcmp("stratum", name)) { 1825 decodeuint(value, &stratum); 1826 } else if (!strcmp("hpoll", name)) { 1827 if (decodeint(value, &hpoll) && hpoll < 0) 1828 hpoll = NTP_MINPOLL; 1829 } else if (!strcmp("ppoll", name)) { 1830 if (decodeint(value, &ppoll) && ppoll < 0) 1831 ppoll = NTP_MINPOLL; 1832 } else if (!strcmp("reach", name)) { 1833 decodeuint(value, &reach); 1834 } else if (!strcmp("delay", name)) { 1835 decodetime(value, &estdelay); 1836 } else if (!strcmp("offset", name)) { 1837 decodetime(value, &estoffset); 1838 } else if (!strcmp("jitter", name)) { 1839 if ((pvl == peervarlist || pvl == apeervarlist) 1840 && decodetime(value, &estjitter)) 1841 have_jitter = 1; 1842 } else if (!strcmp("rootdisp", name) || 1843 !strcmp("dispersion", name)) { 1844 decodetime(value, &estdisp); 1845 } else if (!strcmp("rec", name)) { 1846 decodets(value, &rec); 1847 } else if (!strcmp("srcport", name) || 1848 !strcmp("peerport", name)) { 1849 decodeuint(value, &srcport); 1850 } else if (!strcmp("reftime", name)) { 1851 if (!decodets(value, &reftime)) 1852 L_CLR(&reftime); 1853 } else if (!strcmp("flash", name)) { 1854 decodeuint(value, &flash); 1855 } else { 1856 // xprintf(stderr, "UNRECOGNIZED name=%s ", name); 1857 } 1858 } 1859 1860 /* 1861 * hmode gives the best guidance for the t column. If the response 1862 * did not include hmode we'll use the old decodeaddrtype() result. 1863 */ 1864 switch (hmode) { 1865 1866 case MODE_BCLIENT: 1867 /* broadcastclient or multicastclient */ 1868 type = 'b'; 1869 break; 1870 1871 case MODE_BROADCAST: 1872 /* broadcast or multicast server */ 1873 if (IS_MCAST(&srcadr)) 1874 type = 'M'; 1875 else 1876 type = 'B'; 1877 break; 1878 1879 case MODE_CLIENT: 1880 if (ISREFCLOCKADR(&srcadr)) 1881 type = 'l'; /* local refclock*/ 1882 else if (SOCK_UNSPEC(&srcadr)) 1883 type = 'p'; /* pool */ 1884 else if (IS_MCAST(&srcadr)) 1885 type = 'a'; /* manycastclient */ 1886 else 1887 type = 'u'; /* unicast */ 1888 break; 1889 1890 case MODE_ACTIVE: 1891 type = 's'; /* symmetric active */ 1892 break; /* configured */ 1893 1894 case MODE_PASSIVE: 1895 type = 'S'; /* symmetric passive */ 1896 break; /* ephemeral */ 1897 } 1898 1899 /* 1900 * Got everything, format the line 1901 */ 1902 poll_sec = 1 << min(ppoll, hpoll); 1903 if (pktversion > NTP_OLDVERSION) 1904 c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7]; 1905 else 1906 c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3]; 1907 if (numhosts > 1) { 1908 if ((pvl == peervarlist || pvl == apeervarlist) 1909 && have_dstadr) { 1910 serverlocal = nntohost_col(&dstadr, 1911 (size_t)min(LIB_BUFLENGTH - 1, maxhostlen), 1912 TRUE); 1913 } else { 1914 if (currenthostisnum) 1915 serverlocal = trunc_left(currenthost, 1916 maxhostlen); 1917 else 1918 serverlocal = currenthost; 1919 } 1920 xprintf(fp, "%-*s ", (int)maxhostlen, serverlocal); 1921 } 1922 if (AF_UNSPEC == af || AF(&srcadr) == af) { 1923 if (!have_srchost) 1924 strlcpy(clock_name, nntohost(&srcadr), 1925 sizeof(clock_name)); 1926 /* wide and long source - space over on next line */ 1927 /* allow for host + sp if > 1 and regular tally + source + sp */ 1928 if (wideremote && 15 < strlen(clock_name)) 1929 xprintf(fp, "%c%s\n%*s", c, clock_name, 1930 ((numhosts > 1) ? (int)maxhostlen + 1 : 0) 1931 + 1 + 15 + 1, ""); 1932 else 1933 xprintf(fp, "%c%-15.15s ", c, clock_name); 1934 if ((flash & TEST12) && (pvl != opeervarlist)) { 1935 drlen = xprintf(fp, "(loop)"); 1936 } else if (!have_da_rid) { 1937 drlen = 0; 1938 } else { 1939 drlen = strlen(dstadr_refid); 1940 makeascii(drlen, dstadr_refid, fp); 1941 } 1942 free(drbuf); 1943 if (pvl == apeervarlist) { 1944 while (drlen++ < 9) 1945 xputc(' ', fp); 1946 xprintf(fp, "%-6d", associd); 1947 } else { 1948 while (drlen++ < 15) 1949 xputc(' ', fp); 1950 } 1951 xprintf(fp, 1952 " %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n", 1953 stratum, type, 1954 prettyinterval(whenbuf, sizeof(whenbuf), 1955 when(&ts, &rec, &reftime)), 1956 prettyinterval(pollbuf, sizeof(pollbuf), 1957 (int)poll_sec), 1958 reach, ulfptoms(&estdelay, 3), 1959 lfptoms(&estoffset, 3), 1960 (have_jitter) 1961 ? ulfptoms(&estjitter, 3) 1962 : ulfptoms(&estdisp, 3)); 1963 return (1); 1964 } 1965 else 1966 return(1); 1967 } 1968 1969 1970 /* 1971 * dogetpeers - given an association ID, read and print the spreadsheet 1972 * peer variables. 1973 */ 1974 static int 1975 dogetpeers( 1976 struct varlist *pvl, 1977 associd_t associd, 1978 FILE *fp, 1979 int af 1980 ) 1981 { 1982 const char *datap; 1983 int res; 1984 size_t dsize; 1985 u_short rstatus; 1986 1987 #ifdef notdef 1988 res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus, 1989 &dsize, &datap); 1990 #else 1991 /* 1992 * Damn fuzzballs 1993 */ 1994 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus, 1995 &dsize, &datap); 1996 #endif 1997 1998 if (res != 0) 1999 return 0; 2000 2001 if (dsize == 0) { 2002 if (numhosts > 1) 2003 xprintf(stderr, "server=%s ", currenthost); 2004 xprintf(stderr, 2005 "***No information returned for association %u\n", 2006 associd); 2007 return 0; 2008 } 2009 2010 return doprintpeers(pvl, associd, (int)rstatus, dsize, datap, 2011 fp, af); 2012 } 2013 2014 2015 /* 2016 * peers - print a peer spreadsheet 2017 */ 2018 static void 2019 dopeers( 2020 int showall, 2021 FILE *fp, 2022 int af 2023 ) 2024 { 2025 u_int u; 2026 char fullname[LENHOSTNAME]; 2027 sockaddr_u netnum; 2028 const char * name_or_num; 2029 size_t sl; 2030 2031 if (!dogetassoc(fp)) 2032 return; 2033 2034 if (numhosts > 1) { 2035 for (u = 0; u < numhosts; u++) { 2036 if (getnetnum(chosts[u].name, &netnum, fullname, af)) { 2037 name_or_num = nntohost(&netnum); 2038 sl = strlen(name_or_num); 2039 maxhostlen = max(maxhostlen, sl); 2040 } 2041 } 2042 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, 2043 "server (local)"); 2044 } 2045 xprintf(fp, 2046 " remote refid st t when poll reach delay offset jitter\n"); 2047 if (numhosts > 1) 2048 for (u = 0; u <= maxhostlen; u++) 2049 xprintf(fp, "="); 2050 xprintf(fp, 2051 "==============================================================================\n"); 2052 2053 for (u = 0; u < numassoc; u++) { 2054 if (!showall && 2055 !(CTL_PEER_STATVAL(assoc_cache[u].status) 2056 & (CTL_PST_CONFIG|CTL_PST_REACH))) { 2057 if (debug) 2058 xprintf(stderr, "eliding [%d]\n", 2059 (int)assoc_cache[u].assid); 2060 continue; 2061 } 2062 if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid, 2063 fp, af)) 2064 return; 2065 } 2066 return; 2067 } 2068 2069 2070 /* 2071 * doapeers - print a peer spreadsheet with assocIDs 2072 */ 2073 static void 2074 doapeers( 2075 int showall, 2076 FILE *fp, 2077 int af 2078 ) 2079 { 2080 u_int u; 2081 char fullname[LENHOSTNAME]; 2082 sockaddr_u netnum; 2083 const char * name_or_num; 2084 size_t sl; 2085 2086 if (!dogetassoc(fp)) 2087 return; 2088 2089 if (numhosts > 1) { 2090 for (u = 0; u < numhosts; u++) { 2091 if (getnetnum(chosts[u].name, &netnum, fullname, af)) { 2092 name_or_num = nntohost(&netnum); 2093 sl = strlen(name_or_num); 2094 maxhostlen = max(maxhostlen, sl); 2095 } 2096 } 2097 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, 2098 "server (local)"); 2099 } 2100 xprintf(fp, 2101 " remote refid assid st t when poll reach delay offset jitter\n"); 2102 if (numhosts > 1) 2103 for (u = 0; u <= maxhostlen; u++) 2104 xprintf(fp, "="); 2105 xprintf(fp, 2106 "==============================================================================\n"); 2107 2108 for (u = 0; u < numassoc; u++) { 2109 if (!showall && 2110 !(CTL_PEER_STATVAL(assoc_cache[u].status) 2111 & (CTL_PST_CONFIG|CTL_PST_REACH))) { 2112 if (debug) 2113 xprintf(stderr, "eliding [%d]\n", 2114 (int)assoc_cache[u].assid); 2115 continue; 2116 } 2117 if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid, 2118 fp, af)) 2119 return; 2120 } 2121 return; 2122 } 2123 2124 2125 /* 2126 * peers - print a peer spreadsheet 2127 */ 2128 /*ARGSUSED*/ 2129 static void 2130 peers( 2131 struct parse *pcmd, 2132 FILE *fp 2133 ) 2134 { 2135 if (drefid == REFID_HASH) { 2136 apeers(pcmd, fp); 2137 } else { 2138 int af = 0; 2139 2140 if (pcmd->nargs == 1) { 2141 if (pcmd->argval->ival == 6) 2142 af = AF_INET6; 2143 else 2144 af = AF_INET; 2145 } 2146 dopeers(0, fp, af); 2147 } 2148 } 2149 2150 2151 /* 2152 * apeers - print a peer spreadsheet, with assocIDs 2153 */ 2154 /*ARGSUSED*/ 2155 static void 2156 apeers( 2157 struct parse *pcmd, 2158 FILE *fp 2159 ) 2160 { 2161 int af = 0; 2162 2163 if (pcmd->nargs == 1) { 2164 if (pcmd->argval->ival == 6) 2165 af = AF_INET6; 2166 else 2167 af = AF_INET; 2168 } 2169 doapeers(0, fp, af); 2170 } 2171 2172 2173 /* 2174 * lpeers - print a peer spreadsheet including all fuzzball peers 2175 */ 2176 /*ARGSUSED*/ 2177 static void 2178 lpeers( 2179 struct parse *pcmd, 2180 FILE *fp 2181 ) 2182 { 2183 int af = 0; 2184 2185 if (pcmd->nargs == 1) { 2186 if (pcmd->argval->ival == 6) 2187 af = AF_INET6; 2188 else 2189 af = AF_INET; 2190 } 2191 dopeers(1, fp, af); 2192 } 2193 2194 2195 /* 2196 * opeers - print a peer spreadsheet 2197 */ 2198 static void 2199 doopeers( 2200 int showall, 2201 FILE *fp, 2202 int af 2203 ) 2204 { 2205 u_int i; 2206 char fullname[LENHOSTNAME]; 2207 sockaddr_u netnum; 2208 2209 if (!dogetassoc(fp)) 2210 return; 2211 2212 if (numhosts > 1) { 2213 for (i = 0; i < numhosts; ++i) { 2214 if (getnetnum(chosts[i].name, &netnum, fullname, af)) { 2215 maxhostlen = max(maxhostlen, strlen(fullname)); 2216 } 2217 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, 2218 "server"); 2219 } 2220 } 2221 xprintf(fp, 2222 " remote local st t when poll reach delay offset disp\n"); 2223 if (numhosts > 1) 2224 for (i = 0; i <= maxhostlen; ++i) 2225 xprintf(fp, "="); 2226 xprintf(fp, 2227 "==============================================================================\n"); 2228 2229 for (i = 0; i < numassoc; i++) { 2230 if (!showall && 2231 !(CTL_PEER_STATVAL(assoc_cache[i].status) & 2232 (CTL_PST_CONFIG | CTL_PST_REACH))) 2233 continue; 2234 if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af)) 2235 return; 2236 } 2237 return; 2238 } 2239 2240 2241 /* 2242 * opeers - print a peer spreadsheet the old way 2243 */ 2244 /*ARGSUSED*/ 2245 static void 2246 opeers( 2247 struct parse *pcmd, 2248 FILE *fp 2249 ) 2250 { 2251 int af = 0; 2252 2253 if (pcmd->nargs == 1) { 2254 if (pcmd->argval->ival == 6) 2255 af = AF_INET6; 2256 else 2257 af = AF_INET; 2258 } 2259 doopeers(0, fp, af); 2260 } 2261 2262 2263 /* 2264 * lopeers - print a peer spreadsheet including all fuzzball peers 2265 */ 2266 /*ARGSUSED*/ 2267 static void 2268 lopeers( 2269 struct parse *pcmd, 2270 FILE *fp 2271 ) 2272 { 2273 int af = 0; 2274 2275 if (pcmd->nargs == 1) { 2276 if (pcmd->argval->ival == 6) 2277 af = AF_INET6; 2278 else 2279 af = AF_INET; 2280 } 2281 doopeers(1, fp, af); 2282 } 2283 2284 2285 /* 2286 * config - send a configuration command to a remote host 2287 */ 2288 static void 2289 config ( 2290 struct parse *pcmd, 2291 FILE *fp 2292 ) 2293 { 2294 const char *cfgcmd; 2295 u_short rstatus; 2296 size_t rsize; 2297 const char *rdata; 2298 char *resp; 2299 int res; 2300 int col; 2301 int i; 2302 2303 cfgcmd = pcmd->argval[0].string; 2304 2305 if (debug > 2) 2306 xprintf(stderr, 2307 "In Config\n" 2308 "Keyword = %s\n" 2309 "Command = %s\n", pcmd->keyword, cfgcmd); 2310 2311 res = doquery(CTL_OP_CONFIGURE, 0, 1, 2312 strlen(cfgcmd), cfgcmd, 2313 &rstatus, &rsize, &rdata); 2314 2315 if (res != 0) 2316 return; 2317 2318 if (rsize > 0 && '\n' == rdata[rsize - 1]) 2319 rsize--; 2320 2321 resp = emalloc(rsize + 1); 2322 memcpy(resp, rdata, rsize); 2323 resp[rsize] = '\0'; 2324 2325 col = -1; 2326 if (1 == sscanf(resp, "column %d syntax error", &col) 2327 && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) { 2328 if (interactive) 2329 xputs(" *", stdout); /* "ntpq> :config " */ 2330 else 2331 printf("%s\n", cfgcmd); 2332 for (i = 0; i < col; i++) 2333 xputc('_', stdout); 2334 xputs("^\n", stdout); 2335 } 2336 printf("%s\n", resp); 2337 free(resp); 2338 } 2339 2340 2341 /* 2342 * config_from_file - remotely configure an ntpd daemon using the 2343 * specified configuration file 2344 * SK: This function is a kludge at best and is full of bad design 2345 * bugs: 2346 * 1. ntpq uses UDP, which means that there is no guarantee of in-order, 2347 * error-free delivery. 2348 * 2. The maximum length of a packet is constrained, and as a result, the 2349 * maximum length of a line in a configuration file is constrained. 2350 * Longer lines will lead to unpredictable results. 2351 * 3. Since this function is sending a line at a time, we can't update 2352 * the control key through the configuration file (YUCK!!) 2353 * 2354 * Pearly: There are a few places where 'size_t' is cast to 'int' based 2355 * on the assumption that 'int' can hold the size of the involved 2356 * buffers without overflow. 2357 */ 2358 static void 2359 config_from_file ( 2360 struct parse *pcmd, 2361 FILE *fp 2362 ) 2363 { 2364 u_short rstatus; 2365 size_t rsize; 2366 const char *rdata; 2367 char * cp; 2368 int res; 2369 FILE *config_fd; 2370 char config_cmd[MAXLINE]; 2371 size_t config_len; 2372 int i; 2373 int retry_limit; 2374 2375 if (debug > 2) 2376 xprintf(stderr, 2377 "In Config\n" 2378 "Keyword = %s\n" 2379 "Filename = %s\n", pcmd->keyword, 2380 pcmd->argval[0].string); 2381 2382 config_fd = fopen(pcmd->argval[0].string, "r"); 2383 if (NULL == config_fd) { 2384 printf("ERROR!! Couldn't open file: %s\n", 2385 pcmd->argval[0].string); 2386 return; 2387 } 2388 2389 printf("Sending configuration file, one line at a time.\n"); 2390 i = 0; 2391 while (fgets(config_cmd, MAXLINE, config_fd) != NULL) { 2392 /* Eliminate comments first. */ 2393 cp = strchr(config_cmd, '#'); 2394 config_len = (NULL != cp) 2395 ? (size_t)(cp - config_cmd) 2396 : strlen(config_cmd); 2397 2398 /* [Bug 3015] make sure there's no trailing whitespace; 2399 * the fix for [Bug 2853] on the server side forbids 2400 * those. And don't transmit empty lines, as this would 2401 * just be waste. 2402 */ 2403 while (config_len != 0 && 2404 (u_char)config_cmd[config_len-1] <= ' ') 2405 --config_len; 2406 config_cmd[config_len] = '\0'; 2407 2408 ++i; 2409 if (0 == config_len) 2410 continue; 2411 2412 retry_limit = 2; 2413 do 2414 res = doquery(CTL_OP_CONFIGURE, 0, 1, 2415 config_len, config_cmd, 2416 &rstatus, &rsize, &rdata); 2417 while (res != 0 && retry_limit--); 2418 if (res != 0) { 2419 printf("Line No: %d query failed: %.*s\n" 2420 "Subsequent lines not sent.\n", 2421 i, (int)config_len, config_cmd); 2422 fclose(config_fd); 2423 return; 2424 } 2425 2426 /* Right-strip the result code string, then output the 2427 * last line executed, with result code. */ 2428 while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ') 2429 --rsize; 2430 printf("Line No: %d %.*s: %.*s\n", i, 2431 (int)rsize, rdata, 2432 (int)config_len, config_cmd); 2433 } 2434 printf("Done sending file\n"); 2435 fclose(config_fd); 2436 } 2437 2438 2439 static int 2440 fetch_nonce( 2441 char * nonce, 2442 size_t cb_nonce 2443 ) 2444 { 2445 const char nonce_eq[] = "nonce="; 2446 int qres; 2447 u_short rstatus; 2448 size_t rsize; 2449 const char * rdata; 2450 size_t chars; 2451 2452 /* 2453 * Retrieve a nonce specific to this client to demonstrate to 2454 * ntpd that we're capable of receiving responses to our source 2455 * IP address, and thereby unlikely to be forging the source. 2456 */ 2457 qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus, 2458 &rsize, &rdata); 2459 if (qres) { 2460 xprintf(stderr, "nonce request failed\n"); 2461 return FALSE; 2462 } 2463 2464 if (rsize <= sizeof(nonce_eq) - 1 || 2465 strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) { 2466 xprintf(stderr, "unexpected nonce response format: %.*s\n", 2467 (int)rsize, rdata); /* cast is wobbly */ 2468 return FALSE; 2469 } 2470 chars = rsize - (sizeof(nonce_eq) - 1); 2471 if (chars >= cb_nonce) 2472 return FALSE; 2473 memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars); 2474 nonce[chars] = '\0'; 2475 while (chars > 0 && 2476 ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) { 2477 chars--; 2478 nonce[chars] = '\0'; 2479 } 2480 2481 return TRUE; 2482 } 2483 2484 2485 /* 2486 * add_mru Add and entry to mru list, hash table, and allocate 2487 * and return a replacement. 2488 * This is a helper for collect_mru_list(). 2489 */ 2490 static mru * 2491 add_mru( 2492 mru *add 2493 ) 2494 { 2495 u_short hash; 2496 mru *mon; 2497 mru *unlinked; 2498 2499 2500 hash = NTP_HASH_ADDR(&add->addr); 2501 /* see if we have it among previously received entries */ 2502 for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink) 2503 if (SOCK_EQ(&mon->addr, &add->addr)) 2504 break; 2505 if (mon != NULL) { 2506 if (!L_ISGEQ(&add->first, &mon->first)) { 2507 xprintf(stderr, 2508 "add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n", 2509 sptoa(&add->addr), add->last.l_ui, 2510 add->last.l_uf, mon->last.l_ui, 2511 mon->last.l_uf); 2512 exit(1); 2513 } 2514 UNLINK_DLIST(mon, mlink); 2515 UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru); 2516 INSIST(unlinked == mon); 2517 mru_dupes++; 2518 TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui, 2519 mon->last.l_uf)); 2520 } 2521 LINK_DLIST(mru_list, add, mlink); 2522 LINK_SLIST(hash_table[hash], add, hlink); 2523 TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n", 2524 add->last.l_ui, add->last.l_uf, add->count, 2525 (int)add->mode, (int)add->ver, (u_int)add->rs, 2526 add->first.l_ui, add->first.l_uf, sptoa(&add->addr))); 2527 /* if we didn't update an existing entry, alloc replacement */ 2528 if (NULL == mon) { 2529 mon = emalloc(sizeof(*mon)); 2530 mru_count++; 2531 } 2532 ZERO(*mon); 2533 2534 return mon; 2535 } 2536 2537 2538 /* MGOT macro is specific to collect_mru_list() */ 2539 #define MGOT(bit) \ 2540 do { \ 2541 got |= (bit); \ 2542 if (MRU_GOT_ALL == got) { \ 2543 got = 0; \ 2544 mon = add_mru(mon); \ 2545 ci++; \ 2546 } \ 2547 } while (0) 2548 2549 2550 int 2551 mrulist_ctrl_c_hook(void) 2552 { 2553 mrulist_interrupted = TRUE; 2554 return TRUE; 2555 } 2556 2557 2558 static int 2559 collect_mru_list( 2560 const char * parms, 2561 l_fp * pnow 2562 ) 2563 { 2564 const u_int sleep_msecs = 5; 2565 static int ntpd_row_limit = MRU_ROW_LIMIT; 2566 int c_mru_l_rc; /* this function's return code */ 2567 u_char got; /* MRU_GOT_* bits */ 2568 time_t next_report; 2569 size_t cb; 2570 mru *mon; 2571 mru *head; 2572 mru *recent; 2573 int list_complete; 2574 char nonce[128]; 2575 char buf[128]; 2576 char req_buf[CTL_MAX_DATA_LEN]; 2577 char *req; 2578 char *req_end; 2579 size_t chars; 2580 int qres; 2581 u_short rstatus; 2582 size_t rsize; 2583 const char *rdata; 2584 int limit; 2585 int frags; 2586 int cap_frags; 2587 char *tag; 2588 char *val; 2589 int si; /* server index in response */ 2590 int ci; /* client (our) index for validation */ 2591 int ri; /* request index (.# suffix) */ 2592 int mv; 2593 l_fp newest; 2594 l_fp last_older; 2595 sockaddr_u addr_older; 2596 int have_now; 2597 int have_addr_older; 2598 int have_last_older; 2599 u_int restarted_count; 2600 u_int nonce_uses; 2601 u_short hash; 2602 mru *unlinked; 2603 2604 if (!fetch_nonce(nonce, sizeof(nonce))) 2605 return FALSE; 2606 2607 nonce_uses = 0; 2608 restarted_count = 0; 2609 mru_count = 0; 2610 INIT_DLIST(mru_list, mlink); 2611 cb = NTP_HASH_SIZE * sizeof(*hash_table); 2612 INSIST(NULL == hash_table); 2613 hash_table = emalloc_zero(cb); 2614 2615 c_mru_l_rc = FALSE; 2616 list_complete = FALSE; 2617 have_now = FALSE; 2618 cap_frags = TRUE; 2619 got = 0; 2620 ri = 0; 2621 cb = sizeof(*mon); 2622 mon = emalloc_zero(cb); 2623 ZERO(*pnow); 2624 ZERO(last_older); 2625 next_report = time(NULL) + MRU_REPORT_SECS; 2626 2627 limit = min(3 * MAXFRAGS, ntpd_row_limit); 2628 frags = MAXFRAGS; 2629 snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s", 2630 nonce, frags, parms); 2631 nonce_uses++; 2632 2633 while (TRUE) { 2634 if (debug) 2635 xprintf(stderr, "READ_MRU parms: %s\n", req_buf); 2636 2637 qres = doqueryex(CTL_OP_READ_MRU, 0, 0, 2638 strlen(req_buf), req_buf, 2639 &rstatus, &rsize, &rdata, TRUE); 2640 2641 if (CERR_UNKNOWNVAR == qres && ri > 0) { 2642 /* 2643 * None of the supplied prior entries match, so 2644 * toss them from our list and try again. 2645 */ 2646 if (debug) 2647 xprintf(stderr, 2648 "no overlap between %d prior entries and server MRU list\n", 2649 ri); 2650 while (ri--) { 2651 recent = HEAD_DLIST(mru_list, mlink); 2652 INSIST(recent != NULL); 2653 if (debug) 2654 xprintf(stderr, 2655 "tossing prior entry %s to resync\n", 2656 sptoa(&recent->addr)); 2657 UNLINK_DLIST(recent, mlink); 2658 hash = NTP_HASH_ADDR(&recent->addr); 2659 UNLINK_SLIST(unlinked, hash_table[hash], 2660 recent, hlink, mru); 2661 INSIST(unlinked == recent); 2662 free(recent); 2663 mru_count--; 2664 } 2665 if (NULL == HEAD_DLIST(mru_list, mlink)) { 2666 restarted_count++; 2667 if (restarted_count > 8) { 2668 xprintf(stderr, 2669 "Giving up after 8 restarts from the beginning.\n" 2670 "With high-traffic NTP servers, this can occur if the\n" 2671 "MRU list is limited to less than about 16 seconds' of\n" 2672 "entries. See the 'mru' ntp.conf directive to adjust.\n"); 2673 goto cleanup_return; 2674 } 2675 if (debug) 2676 xprintf(stderr, 2677 "---> Restarting from the beginning, retry #%u\n", 2678 restarted_count); 2679 } 2680 } else if (CERR_UNKNOWNVAR == qres) { 2681 xprintf(stderr, 2682 "CERR_UNKNOWNVAR from ntpd but no priors given.\n"); 2683 goto cleanup_return; 2684 } else if (CERR_BADVALUE == qres) { 2685 if (cap_frags) { 2686 cap_frags = FALSE; 2687 if (debug) 2688 xprintf(stderr, 2689 "Reverted to row limit from fragments limit.\n"); 2690 } else { 2691 /* ntpd has lower cap on row limit */ 2692 ntpd_row_limit--; 2693 limit = min(limit, ntpd_row_limit); 2694 if (debug) 2695 xprintf(stderr, 2696 "Row limit reduced to %d following CERR_BADVALUE.\n", 2697 limit); 2698 } 2699 } else if (ERR_INCOMPLETE == qres || 2700 ERR_TIMEOUT == qres) { 2701 /* 2702 * Reduce the number of rows/frags requested by 2703 * half to recover from lost response fragments. 2704 */ 2705 if (cap_frags) { 2706 frags = max(2, frags / 2); 2707 if (debug) 2708 xprintf(stderr, 2709 "Frag limit reduced to %d following incomplete response.\n", 2710 frags); 2711 } else { 2712 limit = max(2, limit / 2); 2713 if (debug) 2714 xprintf(stderr, 2715 "Row limit reduced to %d following incomplete response.\n", 2716 limit); 2717 } 2718 } else if (qres) { 2719 show_error_msg(qres, 0); 2720 goto cleanup_return; 2721 } 2722 /* 2723 * This is a cheap cop-out implementation of rawmode 2724 * output for mrulist. A better approach would be to 2725 * dump similar output after the list is collected by 2726 * ntpq with a continuous sequence of indexes. This 2727 * cheap approach has indexes resetting to zero for 2728 * each query/response, and duplicates are not 2729 * coalesced. 2730 */ 2731 if (!qres && rawmode) 2732 printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout); 2733 ci = 0; 2734 have_addr_older = FALSE; 2735 have_last_older = FALSE; 2736 while (!qres && nextvar(&rsize, &rdata, &tag, &val)) { 2737 INSIST(tag && val); 2738 if (debug > 1) 2739 xprintf(stderr, "nextvar gave: %s = %s\n", 2740 tag, val); 2741 switch(tag[0]) { 2742 2743 case 'a': 2744 if (!strcmp(tag, "addr.older")) { 2745 if (!have_last_older) { 2746 xprintf(stderr, 2747 "addr.older %s before last.older\n", 2748 val); 2749 goto cleanup_return; 2750 } 2751 if (!decodenetnum(val, &addr_older)) { 2752 xprintf(stderr, 2753 "addr.older %s garbled\n", 2754 val); 2755 goto cleanup_return; 2756 } 2757 hash = NTP_HASH_ADDR(&addr_older); 2758 for (recent = hash_table[hash]; 2759 recent != NULL; 2760 recent = recent->hlink) 2761 if (ADDR_PORT_EQ( 2762 &addr_older, 2763 &recent->addr)) 2764 break; 2765 if (NULL == recent) { 2766 xprintf(stderr, 2767 "addr.older %s not in hash table\n", 2768 val); 2769 goto cleanup_return; 2770 } 2771 if (!L_ISEQU(&last_older, 2772 &recent->last)) { 2773 xprintf(stderr, 2774 "last.older %08x.%08x mismatches %08x.%08x expected.\n", 2775 last_older.l_ui, 2776 last_older.l_uf, 2777 recent->last.l_ui, 2778 recent->last.l_uf); 2779 goto cleanup_return; 2780 } 2781 have_addr_older = TRUE; 2782 } else if (1 != sscanf(tag, "addr.%d", &si) 2783 || si != ci) 2784 goto nomatch; 2785 else if (decodenetnum(val, &mon->addr)) 2786 MGOT(MRU_GOT_ADDR); 2787 break; 2788 2789 case 'l': 2790 if (!strcmp(tag, "last.older")) { 2791 if ('0' != val[0] || 2792 'x' != val[1] || 2793 !hextolfp(val + 2, &last_older)) { 2794 xprintf(stderr, 2795 "last.older %s garbled\n", 2796 val); 2797 goto cleanup_return; 2798 } 2799 have_last_older = TRUE; 2800 } else if (!strcmp(tag, "last.newest")) { 2801 if (0 != got) { 2802 xprintf(stderr, 2803 "last.newest %s before complete row, got = 0x%x\n", 2804 val, (u_int)got); 2805 goto cleanup_return; 2806 } 2807 if (!have_now) { 2808 xprintf(stderr, 2809 "last.newest %s before now=\n", 2810 val); 2811 goto cleanup_return; 2812 } 2813 head = HEAD_DLIST(mru_list, mlink); 2814 if (NULL != head) { 2815 if ('0' != val[0] || 2816 'x' != val[1] || 2817 !hextolfp(val + 2, &newest) || 2818 !L_ISEQU(&newest, 2819 &head->last)) { 2820 xprintf(stderr, 2821 "last.newest %s mismatches %08x.%08x", 2822 val, 2823 head->last.l_ui, 2824 head->last.l_uf); 2825 goto cleanup_return; 2826 } 2827 } 2828 list_complete = TRUE; 2829 } else if (1 != sscanf(tag, "last.%d", &si) || 2830 si != ci || '0' != val[0] || 2831 'x' != val[1] || 2832 !hextolfp(val + 2, &mon->last)) { 2833 goto nomatch; 2834 } else { 2835 MGOT(MRU_GOT_LAST); 2836 /* 2837 * allow interrupted retrieval, 2838 * using most recent retrieved 2839 * entry's last seen timestamp 2840 * as the end of operation. 2841 */ 2842 *pnow = mon->last; 2843 } 2844 break; 2845 2846 case 'f': 2847 if (1 != sscanf(tag, "first.%d", &si) || 2848 si != ci || '0' != val[0] || 2849 'x' != val[1] || 2850 !hextolfp(val + 2, &mon->first)) 2851 goto nomatch; 2852 MGOT(MRU_GOT_FIRST); 2853 break; 2854 2855 case 'n': 2856 if (!strcmp(tag, "nonce")) { 2857 strlcpy(nonce, val, sizeof(nonce)); 2858 nonce_uses = 0; 2859 break; /* case */ 2860 } else if (strcmp(tag, "now") || 2861 '0' != val[0] || 2862 'x' != val[1] || 2863 !hextolfp(val + 2, pnow)) 2864 goto nomatch; 2865 have_now = TRUE; 2866 break; 2867 2868 case 'c': 2869 if (1 != sscanf(tag, "ct.%d", &si) || 2870 si != ci || 2871 1 != sscanf(val, "%d", &mon->count) 2872 || mon->count < 1) 2873 goto nomatch; 2874 MGOT(MRU_GOT_COUNT); 2875 break; 2876 2877 case 'm': 2878 if (1 != sscanf(tag, "mv.%d", &si) || 2879 si != ci || 2880 1 != sscanf(val, "%d", &mv)) 2881 goto nomatch; 2882 mon->mode = PKT_MODE(mv); 2883 mon->ver = PKT_VERSION(mv); 2884 MGOT(MRU_GOT_MV); 2885 break; 2886 2887 case 'r': 2888 if (1 != sscanf(tag, "rs.%d", &si) || 2889 si != ci || 2890 1 != sscanf(val, "0x%hx", &mon->rs)) 2891 goto nomatch; 2892 MGOT(MRU_GOT_RS); 2893 break; 2894 2895 default: 2896 nomatch: 2897 /* empty stmt */ ; 2898 /* ignore unknown tags */ 2899 } 2900 } 2901 if (have_now) 2902 list_complete = TRUE; 2903 if (list_complete) { 2904 INSIST(0 == ri || have_addr_older); 2905 } 2906 if (mrulist_interrupted) { 2907 printf("mrulist retrieval interrupted by operator.\n" 2908 "Displaying partial client list.\n"); 2909 fflush(stdout); 2910 } 2911 if (list_complete || mrulist_interrupted) { 2912 xprintf(stderr, 2913 "\rRetrieved %u unique MRU entries and %u updates.\n", 2914 mru_count, mru_dupes); 2915 fflush(stderr); 2916 break; 2917 } 2918 if (time(NULL) >= next_report) { 2919 next_report += MRU_REPORT_SECS; 2920 xprintf(stderr, "\r%u (%u updates) ", mru_count, 2921 mru_dupes); 2922 fflush(stderr); 2923 } 2924 2925 /* 2926 * Snooze for a bit between queries to let ntpd catch 2927 * up with other duties. 2928 */ 2929 #ifdef SYS_WINNT 2930 Sleep(sleep_msecs); 2931 #elif !defined(HAVE_NANOSLEEP) 2932 sleep((sleep_msecs / 1000) + 1); 2933 #else 2934 { 2935 struct timespec interv = { 0, 2936 1000 * sleep_msecs }; 2937 nanosleep(&interv, NULL); 2938 } 2939 #endif 2940 /* 2941 * If there were no errors, increase the number of rows 2942 * to a maximum of 3 * MAXFRAGS (the most packets ntpq 2943 * can handle in one response), on the assumption that 2944 * no less than 3 rows fit in each packet, capped at 2945 * our best guess at the server's row limit. 2946 */ 2947 if (!qres) { 2948 if (cap_frags) { 2949 frags = min(MAXFRAGS, frags + 1); 2950 } else { 2951 limit = min3(3 * MAXFRAGS, 2952 ntpd_row_limit, 2953 max(limit + 1, 2954 limit * 33 / 32)); 2955 } 2956 } 2957 /* 2958 * prepare next query with as many address and last-seen 2959 * timestamps as will fit in a single packet. 2960 */ 2961 req = req_buf; 2962 req_end = req_buf + sizeof(req_buf); 2963 #define REQ_ROOM (req_end - req) 2964 snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce, 2965 (cap_frags) 2966 ? "frags" 2967 : "limit", 2968 (cap_frags) 2969 ? frags 2970 : limit, 2971 parms); 2972 req += strlen(req); 2973 nonce_uses++; 2974 if (nonce_uses >= 4) { 2975 if (!fetch_nonce(nonce, sizeof(nonce))) 2976 goto cleanup_return; 2977 nonce_uses = 0; 2978 } 2979 2980 2981 for (ri = 0, recent = HEAD_DLIST(mru_list, mlink); 2982 recent != NULL; 2983 ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) { 2984 2985 snprintf(buf, sizeof(buf), 2986 ", addr.%d=%s, last.%d=0x%08x.%08x", 2987 ri, sptoa(&recent->addr), ri, 2988 recent->last.l_ui, recent->last.l_uf); 2989 chars = strlen(buf); 2990 if ((size_t)REQ_ROOM <= chars) 2991 break; 2992 memcpy(req, buf, chars + 1); 2993 req += chars; 2994 } 2995 } 2996 2997 c_mru_l_rc = TRUE; 2998 goto retain_hash_table; 2999 3000 cleanup_return: 3001 free(hash_table); 3002 hash_table = NULL; 3003 3004 retain_hash_table: 3005 if (mon != NULL) 3006 free(mon); 3007 3008 return c_mru_l_rc; 3009 } 3010 3011 3012 /* 3013 * qcmp_mru_addr - sort MRU entries by remote address. 3014 * 3015 * All IPv4 addresses sort before any IPv6, addresses are sorted by 3016 * value within address family. 3017 */ 3018 static int 3019 qcmp_mru_addr( 3020 const void *v1, 3021 const void *v2 3022 ) 3023 { 3024 const mru * const * ppm1 = v1; 3025 const mru * const * ppm2 = v2; 3026 const mru * pm1; 3027 const mru * pm2; 3028 u_short af1; 3029 u_short af2; 3030 size_t cmplen; 3031 size_t addr_off; 3032 3033 pm1 = *ppm1; 3034 pm2 = *ppm2; 3035 3036 af1 = AF(&pm1->addr); 3037 af2 = AF(&pm2->addr); 3038 3039 if (af1 != af2) 3040 return (AF_INET == af1) 3041 ? -1 3042 : 1; 3043 3044 cmplen = SIZEOF_INADDR(af1); 3045 addr_off = (AF_INET == af1) 3046 ? offsetof(struct sockaddr_in, sin_addr) 3047 : offsetof(struct sockaddr_in6, sin6_addr); 3048 3049 return memcmp((const char *)&pm1->addr + addr_off, 3050 (const char *)&pm2->addr + addr_off, 3051 cmplen); 3052 } 3053 3054 3055 static int 3056 qcmp_mru_r_addr( 3057 const void *v1, 3058 const void *v2 3059 ) 3060 { 3061 return -qcmp_mru_addr(v1, v2); 3062 } 3063 3064 3065 /* 3066 * qcmp_mru_count - sort MRU entries by times seen (hit count). 3067 */ 3068 static int 3069 qcmp_mru_count( 3070 const void *v1, 3071 const void *v2 3072 ) 3073 { 3074 const mru * const * ppm1 = v1; 3075 const mru * const * ppm2 = v2; 3076 const mru * pm1; 3077 const mru * pm2; 3078 3079 pm1 = *ppm1; 3080 pm2 = *ppm2; 3081 3082 return (pm1->count < pm2->count) 3083 ? -1 3084 : ((pm1->count == pm2->count) 3085 ? 0 3086 : 1); 3087 } 3088 3089 3090 static int 3091 qcmp_mru_r_count( 3092 const void *v1, 3093 const void *v2 3094 ) 3095 { 3096 return -qcmp_mru_count(v1, v2); 3097 } 3098 3099 3100 /* 3101 * qcmp_mru_avgint - sort MRU entries by average interval. 3102 */ 3103 static int 3104 qcmp_mru_avgint( 3105 const void *v1, 3106 const void *v2 3107 ) 3108 { 3109 const mru * const * ppm1 = v1; 3110 const mru * const * ppm2 = v2; 3111 const mru * pm1; 3112 const mru * pm2; 3113 l_fp interval; 3114 double avg1; 3115 double avg2; 3116 3117 pm1 = *ppm1; 3118 pm2 = *ppm2; 3119 3120 interval = pm1->last; 3121 L_SUB(&interval, &pm1->first); 3122 LFPTOD(&interval, avg1); 3123 avg1 /= pm1->count; 3124 3125 interval = pm2->last; 3126 L_SUB(&interval, &pm2->first); 3127 LFPTOD(&interval, avg2); 3128 avg2 /= pm2->count; 3129 3130 if (avg1 < avg2) 3131 return -1; 3132 else if (avg1 > avg2) 3133 return 1; 3134 3135 /* secondary sort on lstint - rarely tested */ 3136 if (L_ISEQU(&pm1->last, &pm2->last)) 3137 return 0; 3138 else if (L_ISGEQ(&pm1->last, &pm2->last)) 3139 return -1; 3140 else 3141 return 1; 3142 } 3143 3144 3145 static int 3146 qcmp_mru_r_avgint( 3147 const void *v1, 3148 const void *v2 3149 ) 3150 { 3151 return -qcmp_mru_avgint(v1, v2); 3152 } 3153 3154 3155 /* 3156 * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most 3157 * Recently Used (seen) remote address list from ntpd. 3158 * 3159 * Similar to ntpdc's monlist command, but not limited to a single 3160 * request/response, and thereby not limited to a few hundred remote 3161 * addresses. 3162 * 3163 * See ntpd/ntp_control.c read_mru_list() for comments on the way 3164 * CTL_OP_READ_MRU is designed to be used. 3165 * 3166 * mrulist intentionally differs from monlist in the way the avgint 3167 * column is calculated. monlist includes the time after the last 3168 * packet from the client until the monlist query time in the average, 3169 * while mrulist excludes it. That is, monlist's average interval grows 3170 * over time for remote addresses not heard from in some time, while it 3171 * remains unchanged in mrulist. This also affects the avgint value for 3172 * entries representing a single packet, with identical first and last 3173 * timestamps. mrulist shows 0 avgint, monlist shows a value identical 3174 * to lstint. 3175 */ 3176 static void 3177 mrulist( 3178 struct parse * pcmd, 3179 FILE * fp 3180 ) 3181 { 3182 const char mincount_eq[] = "mincount="; 3183 const char resall_eq[] = "resall="; 3184 const char resany_eq[] = "resany="; 3185 const char maxlstint_eq[] = "maxlstint="; 3186 const char laddr_eq[] = "laddr="; 3187 const char sort_eq[] = "sort="; 3188 mru_sort_order order; 3189 size_t n; 3190 char parms_buf[128]; 3191 char buf[24]; 3192 char *parms; 3193 const char *arg; 3194 size_t cb; 3195 mru **sorted; 3196 mru **ppentry; 3197 mru *recent; 3198 l_fp now; 3199 l_fp interval; 3200 double favgint; 3201 double flstint; 3202 int avgint; 3203 int lstint; 3204 size_t i; 3205 3206 mrulist_interrupted = FALSE; 3207 push_ctrl_c_handler(&mrulist_ctrl_c_hook); 3208 xprintf(stderr, 3209 "Ctrl-C will stop MRU retrieval and display partial results.\n"); 3210 fflush(stderr); 3211 3212 order = MRUSORT_DEF; 3213 parms_buf[0] = '\0'; 3214 parms = parms_buf; 3215 for (i = 0; i < pcmd->nargs; i++) { 3216 arg = pcmd->argval[i].string; 3217 if (arg != NULL) { 3218 cb = strlen(arg) + 1; 3219 if ((!strncmp(resall_eq, arg, sizeof(resall_eq) 3220 - 1) || !strncmp(resany_eq, arg, 3221 sizeof(resany_eq) - 1) || !strncmp( 3222 mincount_eq, arg, sizeof(mincount_eq) - 1) 3223 || !strncmp(laddr_eq, arg, sizeof(laddr_eq) 3224 - 1) || !strncmp(maxlstint_eq, arg, 3225 sizeof(laddr_eq) - 1)) && parms + cb + 2 <= 3226 parms_buf + sizeof(parms_buf)) { 3227 /* these are passed intact to ntpd */ 3228 memcpy(parms, ", ", 2); 3229 parms += 2; 3230 memcpy(parms, arg, cb); 3231 parms += cb - 1; 3232 } else if (!strncmp(sort_eq, arg, 3233 sizeof(sort_eq) - 1)) { 3234 arg += sizeof(sort_eq) - 1; 3235 for (n = 0; 3236 n < COUNTOF(mru_sort_keywords); 3237 n++) 3238 if (!strcmp(mru_sort_keywords[n], 3239 arg)) 3240 break; 3241 if (n < COUNTOF(mru_sort_keywords)) 3242 order = n; 3243 } else if (!strcmp("limited", arg) || 3244 !strcmp("kod", arg)) { 3245 /* transform to resany=... */ 3246 snprintf(buf, sizeof(buf), 3247 ", resany=0x%x", 3248 ('k' == arg[0]) 3249 ? RES_KOD 3250 : RES_LIMITED); 3251 cb = 1 + strlen(buf); 3252 if (parms + cb < 3253 parms_buf + sizeof(parms_buf)) { 3254 memcpy(parms, buf, cb); 3255 parms += cb - 1; 3256 } 3257 } else 3258 xprintf(stderr, 3259 "ignoring unrecognized mrulist parameter: %s\n", 3260 arg); 3261 } 3262 } 3263 parms = parms_buf; 3264 3265 if (!collect_mru_list(parms, &now)) 3266 return; 3267 3268 /* display the results */ 3269 if (rawmode) 3270 goto cleanup_return; 3271 3272 /* construct an array of entry pointers in default order */ 3273 sorted = eallocarray(mru_count, sizeof(*sorted)); 3274 ppentry = sorted; 3275 if (MRUSORT_R_DEF != order) { 3276 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) 3277 INSIST(ppentry < sorted + mru_count); 3278 *ppentry = recent; 3279 ppentry++; 3280 ITER_DLIST_END() 3281 } else { 3282 REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) 3283 INSIST(ppentry < sorted + mru_count); 3284 *ppentry = recent; 3285 ppentry++; 3286 REV_ITER_DLIST_END() 3287 } 3288 3289 if (ppentry - sorted != (int)mru_count) { 3290 xprintf(stderr, 3291 "mru_count %u should match MRU list depth %ld.\n", 3292 mru_count, (long)(ppentry - sorted)); 3293 free(sorted); 3294 goto cleanup_return; 3295 } 3296 3297 /* re-sort sorted[] if not default or reverse default */ 3298 if (MRUSORT_R_DEF < order) 3299 qsort(sorted, mru_count, sizeof(sorted[0]), 3300 mru_qcmp_table[order]); 3301 3302 mrulist_interrupted = FALSE; 3303 printf( "lstint avgint rstr r m v count rport remote address\n" 3304 "==============================================================================\n"); 3305 /* '=' x 78 */ 3306 for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) { 3307 recent = *ppentry; 3308 interval = now; 3309 L_SUB(&interval, &recent->last); 3310 LFPTOD(&interval, flstint); 3311 lstint = (int)(flstint + 0.5); 3312 interval = recent->last; 3313 L_SUB(&interval, &recent->first); 3314 LFPTOD(&interval, favgint); 3315 favgint /= recent->count; 3316 avgint = (int)(favgint + 0.5); 3317 xprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n", 3318 lstint, avgint, recent->rs, 3319 (RES_KOD & recent->rs) 3320 ? 'K' 3321 : (RES_LIMITED & recent->rs) 3322 ? 'L' 3323 : '.', 3324 (int)recent->mode, (int)recent->ver, 3325 recent->count, SRCPORT(&recent->addr), 3326 nntohost(&recent->addr)); 3327 if (showhostnames) 3328 fflush(fp); 3329 if (mrulist_interrupted) { 3330 xputs("\n --interrupted--\n", fp); 3331 fflush(fp); 3332 break; 3333 } 3334 } 3335 fflush(fp); 3336 if (debug) { 3337 xprintf(stderr, 3338 "--- completed, freeing sorted[] pointers\n"); 3339 fflush(stderr); 3340 } 3341 free(sorted); 3342 3343 cleanup_return: 3344 if (debug) { 3345 xprintf(stderr, "... freeing MRU entries\n"); 3346 fflush(stderr); 3347 } 3348 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) 3349 free(recent); 3350 ITER_DLIST_END() 3351 if (debug) { 3352 xprintf(stderr, "... freeing hash_table[]\n"); 3353 fflush(stderr); 3354 } 3355 free(hash_table); 3356 hash_table = NULL; 3357 INIT_DLIST(mru_list, mlink); 3358 3359 pop_ctrl_c_handler(&mrulist_ctrl_c_hook); 3360 } 3361 3362 3363 /* 3364 * validate_ifnum - helper for ifstats() 3365 * 3366 * Ensures rows are received in order and complete. 3367 */ 3368 static void 3369 validate_ifnum( 3370 FILE * fp, 3371 u_int ifnum, 3372 int * pfields, 3373 ifstats_row * prow 3374 ) 3375 { 3376 if (prow->ifnum == ifnum) 3377 return; 3378 if (prow->ifnum + 1 <= ifnum) { 3379 if (*pfields < IFSTATS_FIELDS) 3380 xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n", 3381 *pfields, IFSTATS_FIELDS); 3382 *pfields = 0; 3383 prow->ifnum = ifnum; 3384 return; 3385 } 3386 xprintf(stderr, 3387 "received if index %u, have %d of %d fields for index %u, aborting.\n", 3388 ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum); 3389 exit(1); 3390 } 3391 3392 3393 /* 3394 * another_ifstats_field - helper for ifstats() 3395 * 3396 * If all fields for the row have been received, print it. 3397 */ 3398 static void 3399 another_ifstats_field( 3400 int * pfields, 3401 ifstats_row * prow, 3402 FILE * fp 3403 ) 3404 { 3405 u_int ifnum; 3406 3407 (*pfields)++; 3408 /* we understand 12 tags */ 3409 if (IFSTATS_FIELDS > *pfields) 3410 return; 3411 /* 3412 " interface name send\n" 3413 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n" 3414 "==============================================================================\n"); 3415 */ 3416 xprintf(fp, 3417 "%3u %-24.24s %c %4x %3u %2u %6u %6u %6u %5u %8d\n" 3418 " %s\n", 3419 prow->ifnum, prow->name, 3420 (prow->enabled) 3421 ? '.' 3422 : 'D', 3423 prow->flags, prow->ttl, prow->mcast_count, 3424 prow->received, prow->sent, prow->send_errors, 3425 prow->peer_count, prow->uptime, sptoa(&prow->addr)); 3426 if (!SOCK_UNSPEC(&prow->bcast)) 3427 xprintf(fp, " %s\n", sptoa(&prow->bcast)); 3428 ifnum = prow->ifnum; 3429 ZERO(*prow); 3430 prow->ifnum = ifnum; 3431 } 3432 3433 3434 /* 3435 * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats. 3436 */ 3437 static void 3438 ifstats( 3439 struct parse * pcmd, 3440 FILE * fp 3441 ) 3442 { 3443 const char addr_fmt[] = "addr.%u"; 3444 const char bcast_fmt[] = "bcast.%u"; 3445 const char en_fmt[] = "en.%u"; /* enabled */ 3446 const char flags_fmt[] = "flags.%u"; 3447 const char mc_fmt[] = "mc.%u"; /* mcast count */ 3448 const char name_fmt[] = "name.%u"; 3449 const char pc_fmt[] = "pc.%u"; /* peer count */ 3450 const char rx_fmt[] = "rx.%u"; 3451 const char tl_fmt[] = "tl.%u"; /* ttl */ 3452 const char tx_fmt[] = "tx.%u"; 3453 const char txerr_fmt[] = "txerr.%u"; 3454 const char up_fmt[] = "up.%u"; /* uptime */ 3455 const char * datap; 3456 int qres; 3457 size_t dsize; 3458 u_short rstatus; 3459 char * tag; 3460 char * val; 3461 int fields; 3462 u_int ui; 3463 ifstats_row row; 3464 int comprende; 3465 size_t len; 3466 3467 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus, 3468 &dsize, &datap); 3469 if (qres) /* message already displayed */ 3470 return; 3471 3472 xprintf(fp, 3473 " interface name send\n" 3474 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n" 3475 "==============================================================================\n"); 3476 /* '=' x 78 */ 3477 3478 ZERO(row); 3479 fields = 0; 3480 ui = 0; 3481 while (nextvar(&dsize, &datap, &tag, &val)) { 3482 INSIST(tag && val); 3483 if (debug > 1) 3484 xprintf(stderr, "nextvar gave: %s = %s\n", tag, val); 3485 comprende = FALSE; 3486 switch(tag[0]) { 3487 3488 case 'a': 3489 if (1 == sscanf(tag, addr_fmt, &ui) && 3490 decodenetnum(val, &row.addr)) 3491 comprende = TRUE; 3492 break; 3493 3494 case 'b': 3495 if (1 == sscanf(tag, bcast_fmt, &ui) && 3496 ('\0' == *val || 3497 decodenetnum(val, &row.bcast))) 3498 comprende = TRUE; 3499 break; 3500 3501 case 'e': 3502 if (1 == sscanf(tag, en_fmt, &ui) && 3503 1 == sscanf(val, "%d", &row.enabled)) 3504 comprende = TRUE; 3505 break; 3506 3507 case 'f': 3508 if (1 == sscanf(tag, flags_fmt, &ui) && 3509 1 == sscanf(val, "0x%x", &row.flags)) 3510 comprende = TRUE; 3511 break; 3512 3513 case 'm': 3514 if (1 == sscanf(tag, mc_fmt, &ui) && 3515 1 == sscanf(val, "%u", &row.mcast_count)) 3516 comprende = TRUE; 3517 break; 3518 3519 case 'n': 3520 if (1 == sscanf(tag, name_fmt, &ui)) { 3521 /* strip quotes */ 3522 len = strlen(val); 3523 if (len >= 2 && 3524 len - 2 < sizeof(row.name)) { 3525 len -= 2; 3526 memcpy(row.name, val + 1, len); 3527 row.name[len] = '\0'; 3528 comprende = TRUE; 3529 } 3530 } 3531 break; 3532 3533 case 'p': 3534 if (1 == sscanf(tag, pc_fmt, &ui) && 3535 1 == sscanf(val, "%u", &row.peer_count)) 3536 comprende = TRUE; 3537 break; 3538 3539 case 'r': 3540 if (1 == sscanf(tag, rx_fmt, &ui) && 3541 1 == sscanf(val, "%u", &row.received)) 3542 comprende = TRUE; 3543 break; 3544 3545 case 't': 3546 if (1 == sscanf(tag, tl_fmt, &ui) && 3547 1 == sscanf(val, "%u", &row.ttl)) 3548 comprende = TRUE; 3549 else if (1 == sscanf(tag, tx_fmt, &ui) && 3550 1 == sscanf(val, "%u", &row.sent)) 3551 comprende = TRUE; 3552 else if (1 == sscanf(tag, txerr_fmt, &ui) && 3553 1 == sscanf(val, "%u", &row.send_errors)) 3554 comprende = TRUE; 3555 break; 3556 3557 case 'u': 3558 if (1 == sscanf(tag, up_fmt, &ui) && 3559 1 == sscanf(val, "%u", &row.uptime)) 3560 comprende = TRUE; 3561 break; 3562 } 3563 3564 if (comprende) { 3565 /* error out if rows out of order */ 3566 validate_ifnum(fp, ui, &fields, &row); 3567 /* if the row is complete, print it */ 3568 another_ifstats_field(&fields, &row, fp); 3569 } 3570 } 3571 if (fields != IFSTATS_FIELDS) 3572 xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n", 3573 fields, IFSTATS_FIELDS); 3574 3575 fflush(fp); 3576 } 3577 3578 3579 /* 3580 * validate_reslist_idx - helper for reslist() 3581 * 3582 * Ensures rows are received in order and complete. 3583 */ 3584 static void 3585 validate_reslist_idx( 3586 FILE * fp, 3587 u_int idx, 3588 int * pfields, 3589 reslist_row * prow 3590 ) 3591 { 3592 if (prow->idx == idx) 3593 return; 3594 if (prow->idx + 1 == idx) { 3595 if (*pfields < RESLIST_FIELDS) 3596 xprintf(fp, "Warning: incomplete row with %d (of %d) fields", 3597 *pfields, RESLIST_FIELDS); 3598 *pfields = 0; 3599 prow->idx = idx; 3600 return; 3601 } 3602 xprintf(stderr, 3603 "received reslist index %u, have %d of %d fields for index %u, aborting.\n", 3604 idx, *pfields, RESLIST_FIELDS, prow->idx); 3605 exit(1); 3606 } 3607 3608 3609 /* 3610 * another_reslist_field - helper for reslist() 3611 * 3612 * If all fields for the row have been received, print it. 3613 */ 3614 static void 3615 another_reslist_field( 3616 int * pfields, 3617 reslist_row * prow, 3618 FILE * fp 3619 ) 3620 { 3621 char addrmaskstr[128]; 3622 int prefix; /* subnet mask as prefix bits count */ 3623 u_int idx; 3624 3625 (*pfields)++; 3626 /* we understand 4 tags */ 3627 if (RESLIST_FIELDS > *pfields) 3628 return; 3629 3630 prefix = sockaddr_masktoprefixlen(&prow->mask); 3631 if (prefix >= 0) 3632 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d", 3633 stoa(&prow->addr), prefix); 3634 else 3635 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s", 3636 stoa(&prow->addr), stoa(&prow->mask)); 3637 3638 /* 3639 " hits addr/prefix or addr mask\n" 3640 " restrictions\n" 3641 "==============================================================================\n"); 3642 */ 3643 xprintf(fp, 3644 "%10lu %s\n" 3645 " %s\n", 3646 prow->hits, addrmaskstr, prow->flagstr); 3647 idx = prow->idx; 3648 ZERO(*prow); 3649 prow->idx = idx; 3650 } 3651 3652 3653 /* 3654 * reslist - ntpq -c reslist modeled on ntpdc -c reslist. 3655 */ 3656 static void 3657 reslist( 3658 struct parse * pcmd, 3659 FILE * fp 3660 ) 3661 { 3662 const char addr_fmtu[] = "addr.%u"; 3663 const char mask_fmtu[] = "mask.%u"; 3664 const char hits_fmt[] = "hits.%u"; 3665 const char flags_fmt[] = "flags.%u"; 3666 const char qdata[] = "addr_restrictions"; 3667 const int qdata_chars = COUNTOF(qdata) - 1; 3668 const char * datap; 3669 int qres; 3670 size_t dsize; 3671 u_short rstatus; 3672 char * tag; 3673 char * val; 3674 int fields; 3675 u_int ui; 3676 reslist_row row; 3677 int comprende; 3678 size_t len; 3679 3680 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars, 3681 qdata, &rstatus, &dsize, &datap); 3682 if (qres) /* message already displayed */ 3683 return; 3684 3685 xprintf(fp, 3686 " hits addr/prefix or addr mask\n" 3687 " restrictions\n" 3688 "==============================================================================\n"); 3689 /* '=' x 78 */ 3690 3691 ZERO(row); 3692 fields = 0; 3693 ui = 0; 3694 while (nextvar(&dsize, &datap, &tag, &val)) { 3695 INSIST(tag && val); 3696 if (debug > 1) 3697 xprintf(stderr, "nextvar gave: %s = %s\n", tag, val); 3698 comprende = FALSE; 3699 switch(tag[0]) { 3700 3701 case 'a': 3702 if (1 == sscanf(tag, addr_fmtu, &ui) && 3703 decodenetnum(val, &row.addr)) 3704 comprende = TRUE; 3705 break; 3706 3707 case 'f': 3708 if (1 == sscanf(tag, flags_fmt, &ui)) { 3709 if (NULL == val) { 3710 row.flagstr[0] = '\0'; 3711 comprende = TRUE; 3712 } else if ((len = strlen(val)) < sizeof(row.flagstr)) { 3713 memcpy(row.flagstr, val, len); 3714 row.flagstr[len] = '\0'; 3715 comprende = TRUE; 3716 } else { 3717 /* no flags, and still !comprende */ 3718 row.flagstr[0] = '\0'; 3719 } 3720 } 3721 break; 3722 3723 case 'h': 3724 if (1 == sscanf(tag, hits_fmt, &ui) && 3725 1 == sscanf(val, "%lu", &row.hits)) 3726 comprende = TRUE; 3727 break; 3728 3729 case 'm': 3730 if (1 == sscanf(tag, mask_fmtu, &ui) && 3731 decodenetnum(val, &row.mask)) 3732 comprende = TRUE; 3733 break; 3734 } 3735 3736 if (comprende) { 3737 /* error out if rows out of order */ 3738 validate_reslist_idx(fp, ui, &fields, &row); 3739 /* if the row is complete, print it */ 3740 another_reslist_field(&fields, &row, fp); 3741 } 3742 } 3743 if (fields != RESLIST_FIELDS) 3744 xprintf(fp, "Warning: incomplete row with %d (of %d) fields", 3745 fields, RESLIST_FIELDS); 3746 3747 fflush(fp); 3748 } 3749 3750 3751 /* 3752 * collect_display_vdc 3753 */ 3754 static void 3755 collect_display_vdc( 3756 associd_t as, 3757 vdc * table, 3758 int decodestatus, 3759 FILE * fp 3760 ) 3761 { 3762 static const char * const suf[2] = { "adr", "port" }; 3763 static const char * const leapbits[4] = { "00", "01", 3764 "10", "11" }; 3765 struct varlist vl[MAXLIST]; 3766 char tagbuf[32]; 3767 vdc *pvdc; 3768 u_short rstatus; 3769 size_t rsize; 3770 const char *rdata; 3771 int qres; 3772 char *tag; 3773 char *val; 3774 u_int n; 3775 size_t len; 3776 int match; 3777 u_long ul; 3778 int vtype; 3779 sockaddr_u sau; 3780 3781 ZERO(vl); 3782 for (pvdc = table; pvdc->tag != NULL; pvdc++) { 3783 ZERO(pvdc->v); 3784 if (NTP_ADD != pvdc->type) { 3785 doaddvlist(vl, pvdc->tag); 3786 } else { 3787 for (n = 0; n < COUNTOF(suf); n++) { 3788 snprintf(tagbuf, sizeof(tagbuf), "%s%s", 3789 pvdc->tag, suf[n]); 3790 doaddvlist(vl, tagbuf); 3791 } 3792 } 3793 } 3794 qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize, 3795 &rdata); 3796 doclearvlist(vl); 3797 if (qres) 3798 return; /* error msg already displayed */ 3799 3800 /* 3801 * iterate over the response variables filling vdc_table with 3802 * the retrieved values. 3803 */ 3804 while (nextvar(&rsize, &rdata, &tag, &val)) { 3805 INSIST(tag && val); 3806 n = 0; 3807 for (pvdc = table; pvdc->tag != NULL; pvdc++) { 3808 len = strlen(pvdc->tag); 3809 if (strncmp(tag, pvdc->tag, len)) 3810 continue; 3811 if (NTP_ADD != pvdc->type) { 3812 if ('\0' != tag[len]) 3813 continue; 3814 break; 3815 } 3816 match = FALSE; 3817 for (n = 0; n < COUNTOF(suf); n++) { 3818 if (strcmp(tag + len, suf[n])) 3819 continue; 3820 match = TRUE; 3821 break; 3822 } 3823 if (match) 3824 break; 3825 } 3826 if (NULL == pvdc->tag) 3827 continue; 3828 switch (pvdc->type) { 3829 3830 case NTP_STR: 3831 /* strip surrounding double quotes */ 3832 if ('"' == val[0]) { 3833 len = strlen(val); 3834 if (len > 0 && '"' == val[len - 1]) { 3835 val[len - 1] = '\0'; 3836 val++; 3837 } 3838 } 3839 /* fallthru */ 3840 case NTP_REFID: /* fallthru */ 3841 case NTP_MODE: /* fallthru */ 3842 case NTP_2BIT: 3843 pvdc->v.str = estrdup(val); 3844 break; 3845 3846 case NTP_LFP: 3847 decodets(val, &pvdc->v.lfp); 3848 break; 3849 3850 case NTP_ADP: 3851 if (!decodenetnum(val, &pvdc->v.sau)) 3852 xprintf(stderr, "malformed %s=%s\n", 3853 pvdc->tag, val); 3854 break; 3855 3856 case NTP_ADD: 3857 if (0 == n) { /* adr */ 3858 if (!decodenetnum(val, &pvdc->v.sau)) 3859 xprintf(stderr, 3860 "malformed %s=%s\n", 3861 pvdc->tag, val); 3862 } else { /* port */ 3863 if (atouint(val, &ul)) 3864 SET_PORT(&pvdc->v.sau, 3865 (u_short)ul); 3866 } 3867 break; 3868 } 3869 } 3870 3871 /* and display */ 3872 if (decodestatus) { 3873 vtype = (0 == as) 3874 ? TYPE_SYS 3875 : TYPE_PEER; 3876 xprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus, 3877 statustoa(vtype, rstatus)); 3878 } 3879 3880 for (pvdc = table; pvdc->tag != NULL; pvdc++) { 3881 switch (pvdc->type) { 3882 3883 case NTP_STR: 3884 if (pvdc->v.str != NULL) { 3885 xprintf(fp, "%s %s\n", pvdc->display, 3886 pvdc->v.str); 3887 free(pvdc->v.str); 3888 pvdc->v.str = NULL; 3889 } 3890 break; 3891 3892 case NTP_ADD: /* fallthru */ 3893 case NTP_ADP: 3894 xprintf(fp, "%s %s\n", pvdc->display, 3895 nntohostp(&pvdc->v.sau)); 3896 break; 3897 3898 case NTP_LFP: 3899 xprintf(fp, "%s %s\n", pvdc->display, 3900 prettydate(&pvdc->v.lfp)); 3901 break; 3902 3903 case NTP_MODE: 3904 atouint(pvdc->v.str, &ul); 3905 xprintf(fp, "%s %s\n", pvdc->display, 3906 modetoa((int)ul)); 3907 free(pvdc->v.str); 3908 pvdc->v.str = NULL; 3909 break; 3910 3911 case NTP_2BIT: 3912 atouint(pvdc->v.str, &ul); 3913 xprintf(fp, "%s %s\n", pvdc->display, 3914 leapbits[ul & 0x3]); 3915 free(pvdc->v.str); 3916 pvdc->v.str = NULL; 3917 break; 3918 3919 case NTP_REFID: 3920 if (!decodenetnum(pvdc->v.str, &sau)) { 3921 fprintf(fp, "%s %s\n", pvdc->display, /* Text fmt */ 3922 pvdc->v.str); 3923 } else if (drefid == REFID_IPV4) { 3924 fprintf(fp, "%s %s\n", pvdc->display, /* IPv4 fmt */ 3925 stoa(&sau)); 3926 } else { 3927 fprintf (fp, "%s 0x%08x\n", pvdc->display, /* Hex / hash */ 3928 ntohl(addr2refid(&sau))); 3929 } 3930 free(pvdc->v.str); 3931 pvdc->v.str = NULL; 3932 break; 3933 3934 default: 3935 xprintf(stderr, "unexpected vdc type %d for %s\n", 3936 pvdc->type, pvdc->tag); 3937 break; 3938 } 3939 } 3940 } 3941 3942 3943 /* 3944 * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats 3945 */ 3946 static void 3947 sysstats( 3948 struct parse *pcmd, 3949 FILE *fp 3950 ) 3951 { 3952 static vdc sysstats_vdc[] = { 3953 VDC_INIT("ss_uptime", "uptime: ", NTP_STR), 3954 VDC_INIT("ss_reset", "sysstats reset: ", NTP_STR), 3955 VDC_INIT("ss_received", "packets received: ", NTP_STR), 3956 VDC_INIT("ss_thisver", "current version: ", NTP_STR), 3957 VDC_INIT("ss_oldver", "older version: ", NTP_STR), 3958 VDC_INIT("ss_badformat", "bad length or format: ", NTP_STR), 3959 VDC_INIT("ss_badauth", "authentication failed:", NTP_STR), 3960 VDC_INIT("ss_declined", "declined: ", NTP_STR), 3961 VDC_INIT("ss_restricted", "restricted: ", NTP_STR), 3962 VDC_INIT("ss_limited", "rate limited: ", NTP_STR), 3963 VDC_INIT("ss_kodsent", "KoD responses: ", NTP_STR), 3964 VDC_INIT("ss_processed", "processed for time: ", NTP_STR), 3965 #if 0 3966 VDC_INIT("ss_lamport", "Lamport violations: ", NTP_STR), 3967 VDC_INIT("ss_tsrounding", "bad timestamp rounding:", NTP_STR), 3968 #endif 3969 VDC_INIT(NULL, NULL, 0) 3970 }; 3971 3972 collect_display_vdc(0, sysstats_vdc, FALSE, fp); 3973 } 3974 3975 3976 /* 3977 * sysinfo - modeled on ntpdc's sysinfo 3978 */ 3979 static void 3980 sysinfo( 3981 struct parse *pcmd, 3982 FILE *fp 3983 ) 3984 { 3985 static vdc sysinfo_vdc[] = { 3986 VDC_INIT("peeradr", "system peer: ", NTP_ADP), 3987 VDC_INIT("peermode", "system peer mode: ", NTP_MODE), 3988 VDC_INIT("leap", "leap indicator: ", NTP_2BIT), 3989 VDC_INIT("stratum", "stratum: ", NTP_STR), 3990 VDC_INIT("precision", "log2 precision: ", NTP_STR), 3991 VDC_INIT("rootdelay", "root delay: ", NTP_STR), 3992 VDC_INIT("rootdisp", "root dispersion: ", NTP_STR), 3993 VDC_INIT("refid", "reference ID: ", NTP_REFID), 3994 VDC_INIT("reftime", "reference time: ", NTP_LFP), 3995 VDC_INIT("sys_jitter", "system jitter: ", NTP_STR), 3996 VDC_INIT("clk_jitter", "clock jitter: ", NTP_STR), 3997 VDC_INIT("clk_wander", "clock wander: ", NTP_STR), 3998 VDC_INIT("bcastdelay", "broadcast delay: ", NTP_STR), 3999 VDC_INIT("authdelay", "symm. auth. delay:", NTP_STR), 4000 VDC_INIT(NULL, NULL, 0) 4001 }; 4002 4003 collect_display_vdc(0, sysinfo_vdc, TRUE, fp); 4004 } 4005 4006 4007 /* 4008 * kerninfo - modeled on ntpdc's kerninfo 4009 */ 4010 static void 4011 kerninfo( 4012 struct parse *pcmd, 4013 FILE *fp 4014 ) 4015 { 4016 static vdc kerninfo_vdc[] = { 4017 VDC_INIT("koffset", "pll offset: ", NTP_STR), 4018 VDC_INIT("kfreq", "pll frequency: ", NTP_STR), 4019 VDC_INIT("kmaxerr", "maximum error: ", NTP_STR), 4020 VDC_INIT("kesterr", "estimated error: ", NTP_STR), 4021 VDC_INIT("kstflags", "kernel status: ", NTP_STR), 4022 VDC_INIT("ktimeconst", "pll time constant: ", NTP_STR), 4023 VDC_INIT("kprecis", "precision: ", NTP_STR), 4024 VDC_INIT("kfreqtol", "frequency tolerance: ", NTP_STR), 4025 VDC_INIT("kppsfreq", "pps frequency: ", NTP_STR), 4026 VDC_INIT("kppsstab", "pps stability: ", NTP_STR), 4027 VDC_INIT("kppsjitter", "pps jitter: ", NTP_STR), 4028 VDC_INIT("kppscalibdur", "calibration interval ", NTP_STR), 4029 VDC_INIT("kppscalibs", "calibration cycles: ", NTP_STR), 4030 VDC_INIT("kppsjitexc", "jitter exceeded: ", NTP_STR), 4031 VDC_INIT("kppsstbexc", "stability exceeded: ", NTP_STR), 4032 VDC_INIT("kppscaliberrs", "calibration errors: ", NTP_STR), 4033 VDC_INIT(NULL, NULL, 0) 4034 }; 4035 4036 collect_display_vdc(0, kerninfo_vdc, TRUE, fp); 4037 } 4038 4039 4040 /* 4041 * monstats - implements ntpq -c monstats 4042 */ 4043 static void 4044 monstats( 4045 struct parse *pcmd, 4046 FILE *fp 4047 ) 4048 { 4049 static vdc monstats_vdc[] = { 4050 VDC_INIT("mru_enabled", "enabled: ", NTP_STR), 4051 VDC_INIT("mru_depth", "addresses: ", NTP_STR), 4052 VDC_INIT("mru_deepest", "peak addresses: ", NTP_STR), 4053 VDC_INIT("mru_maxdepth", "maximum addresses: ", NTP_STR), 4054 VDC_INIT("mru_mindepth", "reclaim above count:", NTP_STR), 4055 VDC_INIT("mru_maxage", "reclaim older than: ", NTP_STR), 4056 VDC_INIT("mru_mem", "kilobytes: ", NTP_STR), 4057 VDC_INIT("mru_maxmem", "maximum kilobytes: ", NTP_STR), 4058 VDC_INIT(NULL, NULL, 0) 4059 }; 4060 4061 collect_display_vdc(0, monstats_vdc, FALSE, fp); 4062 } 4063 4064 4065 /* 4066 * iostats - ntpq -c iostats - network input and output counters 4067 */ 4068 static void 4069 iostats( 4070 struct parse *pcmd, 4071 FILE *fp 4072 ) 4073 { 4074 static vdc iostats_vdc[] = { 4075 VDC_INIT("iostats_reset", "time since reset: ", NTP_STR), 4076 VDC_INIT("total_rbuf", "receive buffers: ", NTP_STR), 4077 VDC_INIT("free_rbuf", "free receive buffers: ", NTP_STR), 4078 VDC_INIT("used_rbuf", "used receive buffers: ", NTP_STR), 4079 VDC_INIT("rbuf_lowater", "low water refills: ", NTP_STR), 4080 VDC_INIT("io_dropped", "dropped packets: ", NTP_STR), 4081 VDC_INIT("io_ignored", "ignored packets: ", NTP_STR), 4082 VDC_INIT("io_received", "received packets: ", NTP_STR), 4083 VDC_INIT("io_sent", "packets sent: ", NTP_STR), 4084 VDC_INIT("io_sendfailed", "packet send failures: ", NTP_STR), 4085 VDC_INIT("io_wakeups", "input wakeups: ", NTP_STR), 4086 VDC_INIT("io_goodwakeups", "useful input wakeups: ", NTP_STR), 4087 VDC_INIT(NULL, NULL, 0) 4088 }; 4089 4090 collect_display_vdc(0, iostats_vdc, FALSE, fp); 4091 } 4092 4093 4094 /* 4095 * timerstats - ntpq -c timerstats - interval timer counters 4096 */ 4097 static void 4098 timerstats( 4099 struct parse *pcmd, 4100 FILE *fp 4101 ) 4102 { 4103 static vdc timerstats_vdc[] = { 4104 VDC_INIT("timerstats_reset", "time since reset: ", NTP_STR), 4105 VDC_INIT("timer_overruns", "timer overruns: ", NTP_STR), 4106 VDC_INIT("timer_xmts", "calls to transmit: ", NTP_STR), 4107 VDC_INIT(NULL, NULL, 0) 4108 }; 4109 4110 collect_display_vdc(0, timerstats_vdc, FALSE, fp); 4111 } 4112 4113 4114 /* 4115 * authinfo - implements ntpq -c authinfo 4116 */ 4117 static void 4118 authinfo( 4119 struct parse *pcmd, 4120 FILE *fp 4121 ) 4122 { 4123 static vdc authinfo_vdc[] = { 4124 VDC_INIT("authreset", "time since reset:", NTP_STR), 4125 VDC_INIT("authkeys", "stored keys: ", NTP_STR), 4126 VDC_INIT("authfreek", "free keys: ", NTP_STR), 4127 VDC_INIT("authklookups", "key lookups: ", NTP_STR), 4128 VDC_INIT("authknotfound", "keys not found: ", NTP_STR), 4129 VDC_INIT("authkuncached", "uncached keys: ", NTP_STR), 4130 VDC_INIT("authkexpired", "expired keys: ", NTP_STR), 4131 VDC_INIT("authencrypts", "encryptions: ", NTP_STR), 4132 VDC_INIT("authdecrypts", "decryptions: ", NTP_STR), 4133 VDC_INIT(NULL, NULL, 0) 4134 }; 4135 4136 collect_display_vdc(0, authinfo_vdc, FALSE, fp); 4137 } 4138 4139 4140 /* 4141 * pstats - show statistics for a peer 4142 */ 4143 static void 4144 pstats( 4145 struct parse *pcmd, 4146 FILE *fp 4147 ) 4148 { 4149 static vdc pstats_vdc[] = { 4150 VDC_INIT("src", "remote host: ", NTP_ADD), 4151 VDC_INIT("dst", "local address: ", NTP_ADD), 4152 VDC_INIT("timerec", "time last received: ", NTP_STR), 4153 VDC_INIT("timer", "time until next send:", NTP_STR), 4154 VDC_INIT("timereach", "reachability change: ", NTP_STR), 4155 VDC_INIT("sent", "packets sent: ", NTP_STR), 4156 VDC_INIT("received", "packets received: ", NTP_STR), 4157 VDC_INIT("badauth", "bad authentication: ", NTP_STR), 4158 VDC_INIT("bogusorg", "bogus origin: ", NTP_STR), 4159 VDC_INIT("oldpkt", "duplicate: ", NTP_STR), 4160 VDC_INIT("seldisp", "bad dispersion: ", NTP_STR), 4161 VDC_INIT("selbroken", "bad reference time: ", NTP_STR), 4162 VDC_INIT("candidate", "candidate order: ", NTP_STR), 4163 VDC_INIT(NULL, NULL, 0) 4164 }; 4165 associd_t associd; 4166 4167 associd = checkassocid(pcmd->argval[0].uval); 4168 if (0 == associd) 4169 return; 4170 4171 collect_display_vdc(associd, pstats_vdc, TRUE, fp); 4172 } 4173