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