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