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