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