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