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