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