1 /* $NetBSD: ntpq-subs.c,v 1.19 2024/08/18 20:47:19 christos Exp $ */ 2 3 /* 4 * ntpq-subs.c - subroutines which are called to perform ntpq commands. 5 */ 6 #include <config.h> 7 #include <stdio.h> 8 #include <ctype.h> 9 #include <sys/types.h> 10 #include <sys/time.h> 11 12 #include "ntpq.h" 13 #include "ntpq-opts.h" 14 15 extern char currenthost[]; 16 extern int currenthostisnum; 17 size_t maxhostlen; 18 19 /* 20 * Declarations for command handlers in here 21 */ 22 static associd_t checkassocid (u_int32); 23 static struct varlist *findlistvar (struct varlist *, char *); 24 static void doaddvlist (struct varlist *, const char *); 25 static void dormvlist (struct varlist *, const char *); 26 static void doclearvlist (struct varlist *); 27 static void makequerydata (struct varlist *, 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 char numev[32]; 1192 1193 if (numassoc == 0) { 1194 (void) xprintf(fp, "No association ID's in list\n"); 1195 return; 1196 } 1197 1198 /* 1199 * Output a header 1200 */ 1201 (void) xprintf(fp, 1202 "ind assid status conf reach auth condition last_event cnt\n"); 1203 (void) xprintf(fp, 1204 "===========================================================\n"); 1205 for (i = 0; i < numassoc; i++) { 1206 statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status); 1207 if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH))) 1208 continue; 1209 event = CTL_PEER_EVENT(assoc_cache[i].status); 1210 event_count = CTL_PEER_NEVNT(assoc_cache[i].status); 1211 if (statval & CTL_PST_CONFIG) 1212 conf = "yes"; 1213 else 1214 conf = "no"; 1215 if (statval & CTL_PST_BCAST) { 1216 reach = "none"; 1217 if (statval & CTL_PST_AUTHENABLE) 1218 auth = "yes"; 1219 else 1220 auth = "none"; 1221 } else { 1222 if (statval & CTL_PST_REACH) 1223 reach = "yes"; 1224 else 1225 reach = "no"; 1226 if (statval & CTL_PST_AUTHENABLE) { 1227 if (statval & CTL_PST_AUTHENTIC) 1228 auth = "ok "; 1229 else 1230 auth = "bad"; 1231 } else { 1232 auth = "none"; 1233 } 1234 } 1235 if (pktversion > NTP_OLDVERSION) { 1236 switch (statval & 0x7) { 1237 1238 case CTL_PST_SEL_REJECT: 1239 condition = "reject"; 1240 break; 1241 1242 case CTL_PST_SEL_SANE: 1243 condition = "falsetick"; 1244 break; 1245 1246 case CTL_PST_SEL_CORRECT: 1247 condition = "excess"; 1248 break; 1249 1250 case CTL_PST_SEL_SELCAND: 1251 condition = "outlier"; 1252 break; 1253 1254 case CTL_PST_SEL_SYNCCAND: 1255 condition = "candidate"; 1256 break; 1257 1258 case CTL_PST_SEL_EXCESS: 1259 condition = "backup"; 1260 break; 1261 1262 case CTL_PST_SEL_SYSPEER: 1263 condition = "sys.peer"; 1264 break; 1265 1266 case CTL_PST_SEL_PPS: 1267 condition = "pps.peer"; 1268 break; 1269 } 1270 } else { 1271 switch (statval & 0x3) { 1272 1273 case OLD_CTL_PST_SEL_REJECT: 1274 if (!(statval & OLD_CTL_PST_SANE)) 1275 condition = "insane"; 1276 else if (!(statval & OLD_CTL_PST_DISP)) 1277 condition = "hi_disp"; 1278 else 1279 condition = ""; 1280 break; 1281 1282 case OLD_CTL_PST_SEL_SELCAND: 1283 condition = "sel_cand"; 1284 break; 1285 1286 case OLD_CTL_PST_SEL_SYNCCAND: 1287 condition = "sync_cand"; 1288 break; 1289 1290 case OLD_CTL_PST_SEL_SYSPEER: 1291 condition = "sys_peer"; 1292 break; 1293 } 1294 } 1295 switch (PEER_EVENT|event) { 1296 1297 case PEVNT_MOBIL: 1298 last_event = "mobilize"; 1299 break; 1300 1301 case PEVNT_DEMOBIL: 1302 last_event = "demobilize"; 1303 break; 1304 1305 case PEVNT_REACH: 1306 last_event = "reachable"; 1307 break; 1308 1309 case PEVNT_UNREACH: 1310 last_event = "unreachable"; 1311 break; 1312 1313 case PEVNT_RESTART: 1314 last_event = "restart"; 1315 break; 1316 1317 case PEVNT_REPLY: 1318 last_event = "no_reply"; 1319 break; 1320 1321 case PEVNT_RATE: 1322 last_event = "rate_exceeded"; 1323 break; 1324 1325 case PEVNT_DENY: 1326 last_event = "access_denied"; 1327 break; 1328 1329 case PEVNT_ARMED: 1330 last_event = "leap_armed"; 1331 break; 1332 1333 case PEVNT_NEWPEER: 1334 last_event = "sys_peer"; 1335 break; 1336 1337 case PEVNT_CLOCK: 1338 last_event = "clock_alarm"; 1339 break; 1340 1341 case PEVNT_AUTH: 1342 last_event = "bad_auth"; 1343 break; 1344 1345 case PEVNT_POPCORN: 1346 last_event = "popcorn"; 1347 break; 1348 1349 case PEVNT_XLEAVE: 1350 last_event = "interleave"; 1351 break; 1352 1353 case PEVNT_XERR: 1354 last_event = "xleave_err"; 1355 break; 1356 1357 default: 1358 snprintf(numev, sizeof(numev), "<?%x?>", event); 1359 last_event = numev; 1360 break; 1361 } 1362 snprintf(buf, sizeof(buf), 1363 "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2lu", 1364 i + 1, assoc_cache[i].assid, 1365 assoc_cache[i].status, conf, reach, auth, 1366 condition, last_event, event_count); 1367 bp = buf + strlen(buf); 1368 while (bp > buf && ' ' == bp[-1]) 1369 --bp; 1370 bp[0] = '\0'; 1371 xprintf(fp, "%s\n", buf); 1372 } 1373 } 1374 1375 1376 /* 1377 * associations - get, record and print a list of associations 1378 */ 1379 /*ARGSUSED*/ 1380 static void 1381 associations( 1382 struct parse *pcmd, 1383 FILE *fp 1384 ) 1385 { 1386 if (dogetassoc(fp)) 1387 printassoc(0, fp); 1388 } 1389 1390 1391 /* 1392 * lassociations - get, record and print a long list of associations 1393 */ 1394 /*ARGSUSED*/ 1395 static void 1396 lassociations( 1397 struct parse *pcmd, 1398 FILE *fp 1399 ) 1400 { 1401 if (dogetassoc(fp)) 1402 printassoc(1, fp); 1403 } 1404 1405 1406 /* 1407 * passociations - print the association list 1408 */ 1409 /*ARGSUSED*/ 1410 static void 1411 passociations( 1412 struct parse *pcmd, 1413 FILE *fp 1414 ) 1415 { 1416 printassoc(0, fp); 1417 } 1418 1419 1420 /* 1421 * lpassociations - print the long association list 1422 */ 1423 /*ARGSUSED*/ 1424 static void 1425 lpassociations( 1426 struct parse *pcmd, 1427 FILE *fp 1428 ) 1429 { 1430 printassoc(1, fp); 1431 } 1432 1433 1434 /* 1435 * saveconfig - dump ntp server configuration to server file 1436 */ 1437 static void 1438 saveconfig( 1439 struct parse *pcmd, 1440 FILE *fp 1441 ) 1442 { 1443 const char *datap; 1444 int res; 1445 size_t dsize; 1446 u_short rstatus; 1447 1448 if (0 == pcmd->nargs) 1449 return; 1450 1451 res = doquery(CTL_OP_SAVECONFIG, 0, 1, 1452 strlen(pcmd->argval[0].string), 1453 pcmd->argval[0].string, &rstatus, &dsize, 1454 &datap); 1455 1456 if (res != 0) 1457 return; 1458 1459 if (0 == dsize) 1460 xprintf(fp, "(no response message, curiously)"); 1461 else 1462 xprintf(fp, "%.*s", (int)dsize, datap); /* cast is wobbly */ 1463 } 1464 1465 1466 #ifdef UNUSED 1467 /* 1468 * radiostatus - print the radio status returned by the server 1469 */ 1470 /*ARGSUSED*/ 1471 static void 1472 radiostatus( 1473 struct parse *pcmd, 1474 FILE *fp 1475 ) 1476 { 1477 char *datap; 1478 int res; 1479 int dsize; 1480 u_short rstatus; 1481 1482 res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus, 1483 &dsize, &datap); 1484 1485 if (res != 0) 1486 return; 1487 1488 if (numhosts > 1) 1489 (void) xprintf(fp, "server=%s ", currenthost); 1490 if (dsize == 0) { 1491 (void) xprintf(fp, "No radio status string returned\n"); 1492 return; 1493 } 1494 1495 asciize(dsize, datap, fp); 1496 } 1497 #endif /* UNUSED */ 1498 1499 /* 1500 * when - return how long its been since his last packet arrived 1501 */ 1502 static long 1503 when( 1504 l_fp *ts, 1505 l_fp *rec, 1506 l_fp *reftime 1507 ) 1508 { 1509 l_fp *lasttime; 1510 1511 if (rec->l_ui != 0) 1512 lasttime = rec; 1513 else if (reftime->l_ui != 0) 1514 lasttime = reftime; 1515 else 1516 return 0; 1517 1518 if (ts->l_ui < lasttime->l_ui) 1519 return -1; 1520 return (ts->l_ui - lasttime->l_ui); 1521 } 1522 1523 1524 /* 1525 * Pretty-print an interval into the given buffer, in a human-friendly format. 1526 */ 1527 static char * 1528 prettyinterval( 1529 char *buf, 1530 size_t cb, 1531 long diff 1532 ) 1533 { 1534 if (diff <= 0) { 1535 buf[0] = '-'; 1536 buf[1] = 0; 1537 return buf; 1538 } 1539 1540 if (diff <= 2048) { 1541 snprintf(buf, cb, "%u", (unsigned int)diff); 1542 return buf; 1543 } 1544 1545 diff = (diff + 29) / 60; 1546 if (diff <= 300) { 1547 snprintf(buf, cb, "%um", (unsigned int)diff); 1548 return buf; 1549 } 1550 1551 diff = (diff + 29) / 60; 1552 if (diff <= 96) { 1553 snprintf(buf, cb, "%uh", (unsigned int)diff); 1554 return buf; 1555 } 1556 1557 diff = (diff + 11) / 24; 1558 if (diff <= 999) { 1559 snprintf(buf, cb, "%ud", (unsigned int)diff); 1560 return buf; 1561 } 1562 1563 /* years are only approximated... */ 1564 diff = (long)floor(diff / 365.25 + 0.5); 1565 if (diff <= 999) { 1566 snprintf(buf, cb, "%uy", (unsigned int)diff); 1567 return buf; 1568 } 1569 /* Ok, this amounts to infinity... */ 1570 strlcpy(buf, "INF", cb); 1571 return buf; 1572 } 1573 1574 static char 1575 decodeaddrtype( 1576 sockaddr_u *sock 1577 ) 1578 { 1579 char ch = '-'; 1580 u_int32 dummy; 1581 1582 switch(AF(sock)) { 1583 case AF_INET: 1584 dummy = SRCADR(sock); 1585 ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' : 1586 ((dummy&0x000000ff)==0x000000ff) ? 'b' : 1587 ((dummy&0xffffffff)==0x7f000001) ? 'l' : 1588 ((dummy&0xffffffe0)==0x00000000) ? '-' : 1589 'u'); 1590 break; 1591 case AF_INET6: 1592 if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock))) 1593 ch = 'm'; 1594 else 1595 ch = 'u'; 1596 break; 1597 default: 1598 ch = '-'; 1599 break; 1600 } 1601 return ch; 1602 } 1603 1604 /* 1605 * A list of variables required by the peers command 1606 */ 1607 struct varlist opeervarlist[] = { 1608 { "srcadr", 0 }, /* 0 */ 1609 { "dstadr", 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 { 0, 0 } 1623 }; 1624 1625 struct varlist peervarlist[] = { 1626 { "srcadr", 0 }, /* 0 */ 1627 { "refid", 0 }, /* 1 */ 1628 { "stratum", 0 }, /* 2 */ 1629 { "hpoll", 0 }, /* 3 */ 1630 { "ppoll", 0 }, /* 4 */ 1631 { "reach", 0 }, /* 5 */ 1632 { "delay", 0 }, /* 6 */ 1633 { "offset", 0 }, /* 7 */ 1634 { "jitter", 0 }, /* 8 */ 1635 { "dispersion", 0 }, /* 9 */ 1636 { "rec", 0 }, /* 10 */ 1637 { "reftime", 0 }, /* 11 */ 1638 { "srcport", 0 }, /* 12 */ 1639 { "hmode", 0 }, /* 13 */ 1640 { "srchost", 0 }, /* 14 */ 1641 { 0, 0 } 1642 }; 1643 1644 struct varlist apeervarlist[] = { 1645 { "srcadr", 0 }, /* 0 */ 1646 { "refid", 0 }, /* 1 */ 1647 { "assid", 0 }, /* 2 */ 1648 { "stratum", 0 }, /* 3 */ 1649 { "hpoll", 0 }, /* 4 */ 1650 { "ppoll", 0 }, /* 5 */ 1651 { "reach", 0 }, /* 6 */ 1652 { "delay", 0 }, /* 7 */ 1653 { "offset", 0 }, /* 8 */ 1654 { "jitter", 0 }, /* 9 */ 1655 { "dispersion", 0 }, /* 10 */ 1656 { "rec", 0 }, /* 11 */ 1657 { "reftime", 0 }, /* 12 */ 1658 { "srcport", 0 }, /* 13 */ 1659 { "hmode", 0 }, /* 14 */ 1660 { "srchost", 0 }, /* 15 */ 1661 { 0, 0 } 1662 }; 1663 1664 1665 /* 1666 * Decode an incoming data buffer and print a line in the peer list 1667 */ 1668 static int 1669 doprintpeers( 1670 struct varlist *pvl, 1671 int associd, 1672 int rstatus, 1673 size_t datalen, 1674 const char *data, 1675 FILE *fp, 1676 int af 1677 ) 1678 { 1679 char *name; 1680 char *value = NULL; 1681 int c; 1682 size_t len; 1683 int have_srchost; 1684 int have_dstadr; 1685 int have_da_rid; 1686 int have_jitter; 1687 sockaddr_u srcadr; 1688 sockaddr_u dstadr; 1689 sockaddr_u dum_store; 1690 sockaddr_u refidadr; 1691 long hmode = 0; 1692 u_long srcport = 0; 1693 u_int32 u32; 1694 const char *dstadr_refid = "0.0.0.0"; 1695 const char *serverlocal; 1696 char *drbuf = NULL; 1697 size_t drlen; 1698 u_long stratum = 0; 1699 long ppoll = 0; 1700 long hpoll = 0; 1701 u_long reach = 0; 1702 l_fp estoffset; 1703 l_fp estdelay; 1704 l_fp estjitter; 1705 l_fp estdisp; 1706 l_fp reftime; 1707 l_fp rec; 1708 l_fp ts; 1709 u_long poll_sec; 1710 u_long flash = 0; 1711 char type = '?'; 1712 char clock_name[LENHOSTNAME]; 1713 char whenbuf[12], pollbuf[12]; 1714 /* [Bug 3482] formally whenbuf & pollbuf should be able to hold 1715 * a full signed int. Not that we would use that much string 1716 * data for it... 1717 */ 1718 get_systime(&ts); 1719 1720 have_srchost = FALSE; 1721 have_dstadr = FALSE; 1722 have_da_rid = FALSE; 1723 have_jitter = FALSE; 1724 ZERO_SOCK(&srcadr); 1725 ZERO_SOCK(&dstadr); 1726 clock_name[0] = '\0'; 1727 ZERO(estoffset); 1728 ZERO(estdelay); 1729 ZERO(estjitter); 1730 ZERO(estdisp); 1731 1732 while (nextvar(&datalen, &data, &name, &value)) { 1733 INSIST(name && value); 1734 if (!strcmp("srcadr", name) || 1735 !strcmp("peeradr", name)) { 1736 if (!decodenetnum(value, &srcadr)) 1737 xprintf(stderr, "malformed %s=%s\n", 1738 name, value); 1739 } else if (!strcmp("srchost", name)) { 1740 if (pvl == peervarlist || pvl == apeervarlist) { 1741 len = strlen(value); 1742 if (2 < len && 1743 (size_t)len < sizeof(clock_name)) { 1744 /* strip quotes */ 1745 value++; 1746 len -= 2; 1747 memcpy(clock_name, value, len); 1748 clock_name[len] = '\0'; 1749 have_srchost = TRUE; 1750 } 1751 } 1752 } else if (!strcmp("dstadr", name)) { 1753 if (decodenetnum(value, &dum_store)) { 1754 type = decodeaddrtype(&dum_store); 1755 have_dstadr = TRUE; 1756 dstadr = dum_store; 1757 if (pvl == opeervarlist) { 1758 have_da_rid = TRUE; 1759 dstadr_refid = trunc_left(stoa(&dstadr), 15); 1760 } 1761 } 1762 } else if (!strcmp("hmode", name)) { 1763 decodeint(value, &hmode); 1764 } else if (!strcmp("refid", name)) { 1765 if ( (pvl == peervarlist) 1766 && (drefid == REFID_IPV4)) { 1767 have_da_rid = TRUE; 1768 drlen = strlen(value); 1769 if (0 == drlen) { 1770 dstadr_refid = ""; 1771 } else if (drlen <= 4) { 1772 ZERO(u32); 1773 memcpy(&u32, value, drlen); 1774 dstadr_refid = refid_str(u32, 1); 1775 } else if (decodenetnum(value, &refidadr)) { 1776 if (SOCK_UNSPEC(&refidadr)) 1777 dstadr_refid = "0.0.0.0"; 1778 else if (ISREFCLOCKADR(&refidadr)) { 1779 dstadr_refid = 1780 refnumtoa(&refidadr); 1781 } else { 1782 dstadr_refid = 1783 stoa(&refidadr); 1784 } 1785 } else { 1786 have_da_rid = FALSE; 1787 } 1788 } else if ( (pvl == apeervarlist) 1789 || (pvl == peervarlist)) { 1790 /* no need to check drefid == REFID_HASH */ 1791 have_da_rid = TRUE; 1792 drlen = strlen(value); 1793 if (0 == drlen) { 1794 dstadr_refid = ""; 1795 } else if (drlen <= 4) { 1796 ZERO(u32); 1797 memcpy(&u32, value, drlen); 1798 dstadr_refid = refid_str(u32, 1); 1799 //xprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value); 1800 } else if (decodenetnum(value, &refidadr)) { 1801 if (SOCK_UNSPEC(&refidadr)) 1802 dstadr_refid = "0.0.0.0"; 1803 else if (ISREFCLOCKADR(&refidadr)) { 1804 dstadr_refid = 1805 refnumtoa(&refidadr); 1806 if (pvl == apeervarlist) { 1807 /* 1808 * restrict refid to 1809 * 8 chars [Bug 3850] 1810 */ 1811 dstadr_refid = 1812 trunc_right( 1813 dstadr_refid, 1814 8); 1815 } 1816 } else { 1817 drbuf = emalloc(10); 1818 snprintf(drbuf, 10, "%0x", 1819 SRCADR(&refidadr)); 1820 dstadr_refid = drbuf; 1821 } 1822 } else { 1823 have_da_rid = FALSE; 1824 } 1825 } 1826 } else if (!strcmp("stratum", name)) { 1827 decodeuint(value, &stratum); 1828 } else if (!strcmp("hpoll", name)) { 1829 if (decodeint(value, &hpoll) && hpoll < 0) 1830 hpoll = NTP_MINPOLL; 1831 } else if (!strcmp("ppoll", name)) { 1832 if (decodeint(value, &ppoll) && ppoll < 0) 1833 ppoll = NTP_MINPOLL; 1834 } else if (!strcmp("reach", name)) { 1835 decodeuint(value, &reach); 1836 } else if (!strcmp("delay", name)) { 1837 decodetime(value, &estdelay); 1838 } else if (!strcmp("offset", name)) { 1839 decodetime(value, &estoffset); 1840 } else if (!strcmp("jitter", name)) { 1841 if ((pvl == peervarlist || pvl == apeervarlist) 1842 && decodetime(value, &estjitter)) 1843 have_jitter = 1; 1844 } else if (!strcmp("rootdisp", name) || 1845 !strcmp("dispersion", name)) { 1846 decodetime(value, &estdisp); 1847 } else if (!strcmp("rec", name)) { 1848 decodets(value, &rec); 1849 } else if (!strcmp("srcport", name) || 1850 !strcmp("peerport", name)) { 1851 decodeuint(value, &srcport); 1852 } else if (!strcmp("reftime", name)) { 1853 if (!decodets(value, &reftime)) 1854 L_CLR(&reftime); 1855 } else if (!strcmp("flash", name)) { 1856 decodeuint(value, &flash); 1857 } else { 1858 // xprintf(stderr, "UNRECOGNIZED name=%s ", name); 1859 } 1860 } 1861 1862 /* 1863 * hmode gives the best guidance for the t column. If the response 1864 * did not include hmode we'll use the old decodeaddrtype() result. 1865 */ 1866 switch (hmode) { 1867 1868 case MODE_BCLIENT: 1869 /* broadcastclient or multicastclient */ 1870 type = 'b'; 1871 break; 1872 1873 case MODE_BROADCAST: 1874 /* broadcast or multicast server */ 1875 if (IS_MCAST(&srcadr)) 1876 type = 'M'; 1877 else 1878 type = 'B'; 1879 break; 1880 1881 case MODE_CLIENT: 1882 if (ISREFCLOCKADR(&srcadr)) 1883 type = 'l'; /* local refclock*/ 1884 else if (SOCK_UNSPEC(&srcadr)) 1885 type = 'p'; /* pool */ 1886 else if (IS_MCAST(&srcadr)) 1887 type = 'a'; /* manycastclient */ 1888 else 1889 type = 'u'; /* unicast */ 1890 break; 1891 1892 case MODE_ACTIVE: 1893 type = 's'; /* symmetric active */ 1894 break; /* configured */ 1895 1896 case MODE_PASSIVE: 1897 type = 'S'; /* symmetric passive */ 1898 break; /* ephemeral */ 1899 } 1900 1901 /* 1902 * Got everything, format the line 1903 */ 1904 poll_sec = 1 << min(ppoll, hpoll); 1905 if (pktversion > NTP_OLDVERSION) 1906 c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7]; 1907 else 1908 c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3]; 1909 if (numhosts > 1) { 1910 if ((pvl == peervarlist || pvl == apeervarlist) 1911 && have_dstadr) { 1912 serverlocal = nntohost_col(&dstadr, 1913 (size_t)min(LIB_BUFLENGTH - 1, maxhostlen), 1914 TRUE); 1915 } else { 1916 if (currenthostisnum) 1917 serverlocal = trunc_left(currenthost, 1918 maxhostlen); 1919 else 1920 serverlocal = currenthost; 1921 } 1922 xprintf(fp, "%-*s ", (int)maxhostlen, serverlocal); 1923 } 1924 if (AF_UNSPEC == af || AF(&srcadr) == af) { 1925 if (!have_srchost) 1926 strlcpy(clock_name, nntohost(&srcadr), 1927 sizeof(clock_name)); 1928 /* wide and long source - space over on next line */ 1929 /* allow for host + sp if > 1 and regular tally + source + sp */ 1930 if (wideremote && 15 < strlen(clock_name)) 1931 xprintf(fp, "%c%s\n%*s", c, clock_name, 1932 ((numhosts > 1) ? (int)maxhostlen + 1 : 0) 1933 + 1 + 15 + 1, ""); 1934 else 1935 xprintf(fp, "%c%-15.15s ", c, clock_name); 1936 if ((flash & TEST12) && (pvl != opeervarlist)) { 1937 drlen = xprintf(fp, "(loop)"); 1938 } else if (!have_da_rid) { 1939 drlen = 0; 1940 } else { 1941 drlen = strlen(dstadr_refid); 1942 makeascii(drlen, dstadr_refid, fp); 1943 } 1944 free(drbuf); 1945 if (pvl == apeervarlist) { 1946 while (drlen++ < 9) 1947 xputc(' ', fp); 1948 xprintf(fp, "%-6d", associd); 1949 } else { 1950 while (drlen++ < 15) 1951 xputc(' ', fp); 1952 } 1953 xprintf(fp, 1954 " %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n", 1955 stratum, type, 1956 prettyinterval(whenbuf, sizeof(whenbuf), 1957 when(&ts, &rec, &reftime)), 1958 prettyinterval(pollbuf, sizeof(pollbuf), 1959 (int)poll_sec), 1960 reach, ulfptoms(&estdelay, 3), 1961 lfptoms(&estoffset, 3), 1962 (have_jitter) 1963 ? ulfptoms(&estjitter, 3) 1964 : ulfptoms(&estdisp, 3)); 1965 return (1); 1966 } 1967 else 1968 return(1); 1969 } 1970 1971 1972 /* 1973 * dogetpeers - given an association ID, read and print the spreadsheet 1974 * peer variables. 1975 */ 1976 static int 1977 dogetpeers( 1978 struct varlist *pvl, 1979 associd_t associd, 1980 FILE *fp, 1981 int af 1982 ) 1983 { 1984 const char *datap; 1985 int res; 1986 size_t dsize; 1987 u_short rstatus; 1988 1989 #ifdef notdef 1990 res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus, 1991 &dsize, &datap); 1992 #else 1993 /* 1994 * Damn fuzzballs 1995 */ 1996 res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus, 1997 &dsize, &datap); 1998 #endif 1999 2000 if (res != 0) 2001 return 0; 2002 2003 if (dsize == 0) { 2004 if (numhosts > 1) 2005 xprintf(stderr, "server=%s ", currenthost); 2006 xprintf(stderr, 2007 "***No information returned for association %u\n", 2008 associd); 2009 return 0; 2010 } 2011 2012 return doprintpeers(pvl, associd, (int)rstatus, dsize, datap, 2013 fp, af); 2014 } 2015 2016 2017 /* 2018 * peers - print a peer spreadsheet 2019 */ 2020 static void 2021 dopeers( 2022 int showall, 2023 FILE *fp, 2024 int af 2025 ) 2026 { 2027 u_int u; 2028 char fullname[LENHOSTNAME]; 2029 sockaddr_u netnum; 2030 const char * name_or_num; 2031 size_t sl; 2032 2033 if (!dogetassoc(fp)) 2034 return; 2035 2036 if (numhosts > 1) { 2037 for (u = 0; u < numhosts; u++) { 2038 if (getnetnum(chosts[u].name, &netnum, fullname, af)) { 2039 name_or_num = nntohost(&netnum); 2040 sl = strlen(name_or_num); 2041 maxhostlen = max(maxhostlen, sl); 2042 } 2043 } 2044 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, 2045 "server (local)"); 2046 } 2047 xprintf(fp, 2048 " remote refid st t when poll reach delay offset jitter\n"); 2049 if (numhosts > 1) 2050 for (u = 0; u <= maxhostlen; u++) 2051 xprintf(fp, "="); 2052 xprintf(fp, 2053 "==============================================================================\n"); 2054 2055 for (u = 0; u < numassoc; u++) { 2056 if (!showall && 2057 !(CTL_PEER_STATVAL(assoc_cache[u].status) 2058 & (CTL_PST_CONFIG|CTL_PST_REACH))) { 2059 if (debug) 2060 xprintf(stderr, "eliding [%d]\n", 2061 (int)assoc_cache[u].assid); 2062 continue; 2063 } 2064 if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid, 2065 fp, af)) 2066 return; 2067 } 2068 return; 2069 } 2070 2071 2072 /* 2073 * doapeers - print a peer spreadsheet with assocIDs 2074 */ 2075 static void 2076 doapeers( 2077 int showall, 2078 FILE *fp, 2079 int af 2080 ) 2081 { 2082 u_int u; 2083 char fullname[LENHOSTNAME]; 2084 sockaddr_u netnum; 2085 const char * name_or_num; 2086 size_t sl; 2087 2088 if (!dogetassoc(fp)) 2089 return; 2090 2091 if (numhosts > 1) { 2092 for (u = 0; u < numhosts; u++) { 2093 if (getnetnum(chosts[u].name, &netnum, fullname, af)) { 2094 name_or_num = nntohost(&netnum); 2095 sl = strlen(name_or_num); 2096 maxhostlen = max(maxhostlen, sl); 2097 } 2098 } 2099 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, 2100 "server (local)"); 2101 } 2102 xprintf(fp, 2103 " remote refid assid st t when poll reach delay offset jitter\n"); 2104 if (numhosts > 1) 2105 for (u = 0; u <= maxhostlen; u++) 2106 xprintf(fp, "="); 2107 xprintf(fp, 2108 "==============================================================================\n"); 2109 2110 for (u = 0; u < numassoc; u++) { 2111 if (!showall && 2112 !(CTL_PEER_STATVAL(assoc_cache[u].status) 2113 & (CTL_PST_CONFIG|CTL_PST_REACH))) { 2114 if (debug) 2115 xprintf(stderr, "eliding [%d]\n", 2116 (int)assoc_cache[u].assid); 2117 continue; 2118 } 2119 if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid, 2120 fp, af)) 2121 return; 2122 } 2123 return; 2124 } 2125 2126 2127 /* 2128 * peers - print a peer spreadsheet 2129 */ 2130 /*ARGSUSED*/ 2131 static void 2132 peers( 2133 struct parse *pcmd, 2134 FILE *fp 2135 ) 2136 { 2137 if (drefid == REFID_HASH) { 2138 apeers(pcmd, fp); 2139 } else { 2140 int af = 0; 2141 2142 if (pcmd->nargs == 1) { 2143 if (pcmd->argval->ival == 6) 2144 af = AF_INET6; 2145 else 2146 af = AF_INET; 2147 } 2148 dopeers(0, fp, af); 2149 } 2150 } 2151 2152 2153 /* 2154 * apeers - print a peer spreadsheet, with assocIDs 2155 */ 2156 /*ARGSUSED*/ 2157 static void 2158 apeers( 2159 struct parse *pcmd, 2160 FILE *fp 2161 ) 2162 { 2163 int af = 0; 2164 2165 if (pcmd->nargs == 1) { 2166 if (pcmd->argval->ival == 6) 2167 af = AF_INET6; 2168 else 2169 af = AF_INET; 2170 } 2171 doapeers(0, fp, af); 2172 } 2173 2174 2175 /* 2176 * lpeers - print a peer spreadsheet including all fuzzball peers 2177 */ 2178 /*ARGSUSED*/ 2179 static void 2180 lpeers( 2181 struct parse *pcmd, 2182 FILE *fp 2183 ) 2184 { 2185 int af = 0; 2186 2187 if (pcmd->nargs == 1) { 2188 if (pcmd->argval->ival == 6) 2189 af = AF_INET6; 2190 else 2191 af = AF_INET; 2192 } 2193 dopeers(1, fp, af); 2194 } 2195 2196 2197 /* 2198 * opeers - print a peer spreadsheet 2199 */ 2200 static void 2201 doopeers( 2202 int showall, 2203 FILE *fp, 2204 int af 2205 ) 2206 { 2207 u_int i; 2208 char fullname[LENHOSTNAME]; 2209 sockaddr_u netnum; 2210 2211 if (!dogetassoc(fp)) 2212 return; 2213 2214 if (numhosts > 1) { 2215 for (i = 0; i < numhosts; ++i) { 2216 if (getnetnum(chosts[i].name, &netnum, fullname, af)) { 2217 maxhostlen = max(maxhostlen, strlen(fullname)); 2218 } 2219 xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, 2220 "server"); 2221 } 2222 } 2223 xprintf(fp, 2224 " remote local st t when poll reach delay offset disp\n"); 2225 if (numhosts > 1) 2226 for (i = 0; i <= maxhostlen; ++i) 2227 xprintf(fp, "="); 2228 xprintf(fp, 2229 "==============================================================================\n"); 2230 2231 for (i = 0; i < numassoc; i++) { 2232 if (!showall && 2233 !(CTL_PEER_STATVAL(assoc_cache[i].status) & 2234 (CTL_PST_CONFIG | CTL_PST_REACH))) 2235 continue; 2236 if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af)) 2237 return; 2238 } 2239 return; 2240 } 2241 2242 2243 /* 2244 * opeers - print a peer spreadsheet the old way 2245 */ 2246 /*ARGSUSED*/ 2247 static void 2248 opeers( 2249 struct parse *pcmd, 2250 FILE *fp 2251 ) 2252 { 2253 int af = 0; 2254 2255 if (pcmd->nargs == 1) { 2256 if (pcmd->argval->ival == 6) 2257 af = AF_INET6; 2258 else 2259 af = AF_INET; 2260 } 2261 doopeers(0, fp, af); 2262 } 2263 2264 2265 /* 2266 * lopeers - print a peer spreadsheet including all fuzzball peers 2267 */ 2268 /*ARGSUSED*/ 2269 static void 2270 lopeers( 2271 struct parse *pcmd, 2272 FILE *fp 2273 ) 2274 { 2275 int af = 0; 2276 2277 if (pcmd->nargs == 1) { 2278 if (pcmd->argval->ival == 6) 2279 af = AF_INET6; 2280 else 2281 af = AF_INET; 2282 } 2283 doopeers(1, fp, af); 2284 } 2285 2286 2287 /* 2288 * config - send a configuration command to a remote host 2289 */ 2290 static void 2291 config ( 2292 struct parse *pcmd, 2293 FILE *fp 2294 ) 2295 { 2296 const char *cfgcmd; 2297 u_short rstatus; 2298 size_t rsize; 2299 const char *rdata; 2300 char *resp; 2301 int res; 2302 int col; 2303 int i; 2304 2305 cfgcmd = pcmd->argval[0].string; 2306 2307 if (debug > 2) 2308 xprintf(stderr, 2309 "In Config\n" 2310 "Keyword = %s\n" 2311 "Command = %s\n", pcmd->keyword, cfgcmd); 2312 2313 res = doquery(CTL_OP_CONFIGURE, 0, 1, 2314 strlen(cfgcmd), cfgcmd, 2315 &rstatus, &rsize, &rdata); 2316 2317 if (res != 0) 2318 return; 2319 2320 if (rsize > 0 && '\n' == rdata[rsize - 1]) 2321 rsize--; 2322 2323 resp = emalloc(rsize + 1); 2324 memcpy(resp, rdata, rsize); 2325 resp[rsize] = '\0'; 2326 2327 col = -1; 2328 if (1 == sscanf(resp, "column %d syntax error", &col) 2329 && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) { 2330 if (interactive) 2331 xputs(" *", stdout); /* "ntpq> :config " */ 2332 else 2333 printf("%s\n", cfgcmd); 2334 for (i = 0; i < col; i++) 2335 xputc('_', stdout); 2336 xputs("^\n", stdout); 2337 } 2338 printf("%s\n", resp); 2339 free(resp); 2340 } 2341 2342 2343 /* 2344 * config_from_file - remotely configure an ntpd daemon using the 2345 * specified configuration file 2346 * SK: This function is a kludge at best and is full of bad design 2347 * bugs: 2348 * 1. ntpq uses UDP, which means that there is no guarantee of in-order, 2349 * error-free delivery. 2350 * 2. The maximum length of a packet is constrained, and as a result, the 2351 * maximum length of a line in a configuration file is constrained. 2352 * Longer lines will lead to unpredictable results. 2353 * 3. Since this function is sending a line at a time, we can't update 2354 * the control key through the configuration file (YUCK!!) 2355 * 2356 * Pearly: There are a few places where 'size_t' is cast to 'int' based 2357 * on the assumption that 'int' can hold the size of the involved 2358 * buffers without overflow. 2359 */ 2360 static void 2361 config_from_file ( 2362 struct parse *pcmd, 2363 FILE *fp 2364 ) 2365 { 2366 u_short rstatus; 2367 size_t rsize; 2368 const char *rdata; 2369 char * cp; 2370 int res; 2371 FILE *config_fd; 2372 char config_cmd[MAXLINE]; 2373 size_t config_len; 2374 int i; 2375 int retry_limit; 2376 2377 if (debug > 2) 2378 xprintf(stderr, 2379 "In Config\n" 2380 "Keyword = %s\n" 2381 "Filename = %s\n", pcmd->keyword, 2382 pcmd->argval[0].string); 2383 2384 config_fd = fopen(pcmd->argval[0].string, "r"); 2385 if (NULL == config_fd) { 2386 printf("ERROR!! Couldn't open file: %s\n", 2387 pcmd->argval[0].string); 2388 return; 2389 } 2390 2391 printf("Sending configuration file, one line at a time.\n"); 2392 i = 0; 2393 while (fgets(config_cmd, MAXLINE, config_fd) != NULL) { 2394 /* Eliminate comments first. */ 2395 cp = strchr(config_cmd, '#'); 2396 config_len = (NULL != cp) 2397 ? (size_t)(cp - config_cmd) 2398 : strlen(config_cmd); 2399 2400 /* [Bug 3015] make sure there's no trailing whitespace; 2401 * the fix for [Bug 2853] on the server side forbids 2402 * those. And don't transmit empty lines, as this would 2403 * just be waste. 2404 */ 2405 while (config_len != 0 && 2406 (u_char)config_cmd[config_len-1] <= ' ') 2407 --config_len; 2408 config_cmd[config_len] = '\0'; 2409 2410 ++i; 2411 if (0 == config_len) 2412 continue; 2413 2414 retry_limit = 2; 2415 do 2416 res = doquery(CTL_OP_CONFIGURE, 0, 1, 2417 config_len, config_cmd, 2418 &rstatus, &rsize, &rdata); 2419 while (res != 0 && retry_limit--); 2420 if (res != 0) { 2421 printf("Line No: %d query failed: %.*s\n" 2422 "Subsequent lines not sent.\n", 2423 i, (int)config_len, config_cmd); 2424 fclose(config_fd); 2425 return; 2426 } 2427 2428 /* Right-strip the result code string, then output the 2429 * last line executed, with result code. */ 2430 while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ') 2431 --rsize; 2432 printf("Line No: %d %.*s: %.*s\n", i, 2433 (int)rsize, rdata, 2434 (int)config_len, config_cmd); 2435 } 2436 printf("Done sending file\n"); 2437 fclose(config_fd); 2438 } 2439 2440 2441 static int 2442 fetch_nonce( 2443 char * nonce, 2444 size_t cb_nonce 2445 ) 2446 { 2447 const char nonce_eq[] = "nonce="; 2448 int qres; 2449 u_short rstatus; 2450 size_t rsize; 2451 const char * rdata; 2452 size_t chars; 2453 2454 /* 2455 * Retrieve a nonce specific to this client to demonstrate to 2456 * ntpd that we're capable of receiving responses to our source 2457 * IP address, and thereby unlikely to be forging the source. 2458 */ 2459 qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus, 2460 &rsize, &rdata); 2461 if (qres) { 2462 xprintf(stderr, "nonce request failed\n"); 2463 return FALSE; 2464 } 2465 2466 if (rsize <= sizeof(nonce_eq) - 1 || 2467 strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) { 2468 xprintf(stderr, "unexpected nonce response format: %.*s\n", 2469 (int)rsize, rdata); /* cast is wobbly */ 2470 return FALSE; 2471 } 2472 chars = rsize - (sizeof(nonce_eq) - 1); 2473 if (chars >= cb_nonce) 2474 return FALSE; 2475 memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars); 2476 nonce[chars] = '\0'; 2477 while (chars > 0 && 2478 ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) { 2479 chars--; 2480 nonce[chars] = '\0'; 2481 } 2482 2483 return TRUE; 2484 } 2485 2486 2487 /* 2488 * add_mru Add and entry to mru list, hash table, and allocate 2489 * and return a replacement. 2490 * This is a helper for collect_mru_list(). 2491 */ 2492 static mru * 2493 add_mru( 2494 mru *add 2495 ) 2496 { 2497 u_short hash; 2498 mru *mon; 2499 mru *unlinked; 2500 2501 2502 hash = NTP_HASH_ADDR(&add->addr); 2503 /* see if we have it among previously received entries */ 2504 for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink) 2505 if (SOCK_EQ(&mon->addr, &add->addr)) 2506 break; 2507 if (mon != NULL) { 2508 if (!L_ISGEQ(&add->first, &mon->first)) { 2509 xprintf(stderr, 2510 "add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n", 2511 sptoa(&add->addr), add->last.l_ui, 2512 add->last.l_uf, mon->last.l_ui, 2513 mon->last.l_uf); 2514 exit(1); 2515 } 2516 UNLINK_DLIST(mon, mlink); 2517 UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru); 2518 INSIST(unlinked == mon); 2519 mru_dupes++; 2520 TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui, 2521 mon->last.l_uf)); 2522 } 2523 LINK_DLIST(mru_list, add, mlink); 2524 LINK_SLIST(hash_table[hash], add, hlink); 2525 TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n", 2526 add->last.l_ui, add->last.l_uf, add->count, 2527 (int)add->mode, (int)add->ver, (u_int)add->rs, 2528 add->first.l_ui, add->first.l_uf, sptoa(&add->addr))); 2529 /* if we didn't update an existing entry, alloc replacement */ 2530 if (NULL == mon) { 2531 mon = emalloc(sizeof(*mon)); 2532 mru_count++; 2533 } 2534 ZERO(*mon); 2535 2536 return mon; 2537 } 2538 2539 2540 /* MGOT macro is specific to collect_mru_list() */ 2541 #define MGOT(bit) \ 2542 do { \ 2543 got |= (bit); \ 2544 if (MRU_GOT_ALL == got) { \ 2545 got = 0; \ 2546 mon = add_mru(mon); \ 2547 ci++; \ 2548 } \ 2549 } while (0) 2550 2551 2552 int 2553 mrulist_ctrl_c_hook(void) 2554 { 2555 mrulist_interrupted = TRUE; 2556 return TRUE; 2557 } 2558 2559 2560 static int 2561 collect_mru_list( 2562 const char * parms, 2563 l_fp * pnow 2564 ) 2565 { 2566 const u_int sleep_msecs = 5; 2567 static int ntpd_row_limit = MRU_ROW_LIMIT; 2568 int c_mru_l_rc; /* this function's return code */ 2569 u_char got; /* MRU_GOT_* bits */ 2570 time_t next_report; 2571 size_t cb; 2572 mru *mon; 2573 mru *head; 2574 mru *recent; 2575 int list_complete; 2576 char nonce[128]; 2577 char buf[128]; 2578 char req_buf[CTL_MAX_DATA_LEN]; 2579 char *req; 2580 char *req_end; 2581 size_t chars; 2582 int qres; 2583 u_short rstatus; 2584 size_t rsize; 2585 const char *rdata; 2586 int limit; 2587 int frags; 2588 int cap_frags; 2589 char *tag; 2590 char *val; 2591 int si; /* server index in response */ 2592 int ci; /* client (our) index for validation */ 2593 int ri; /* request index (.# suffix) */ 2594 int mv; 2595 l_fp newest; 2596 l_fp last_older; 2597 sockaddr_u addr_older; 2598 int have_now; 2599 int have_addr_older; 2600 int have_last_older; 2601 u_int restarted_count; 2602 u_int nonce_uses; 2603 u_short hash; 2604 mru *unlinked; 2605 2606 if (!fetch_nonce(nonce, sizeof(nonce))) 2607 return FALSE; 2608 2609 nonce_uses = 0; 2610 restarted_count = 0; 2611 mru_count = 0; 2612 INIT_DLIST(mru_list, mlink); 2613 cb = NTP_HASH_SIZE * sizeof(*hash_table); 2614 INSIST(NULL == hash_table); 2615 hash_table = emalloc_zero(cb); 2616 2617 c_mru_l_rc = FALSE; 2618 list_complete = FALSE; 2619 have_now = FALSE; 2620 cap_frags = TRUE; 2621 got = 0; 2622 ri = 0; 2623 cb = sizeof(*mon); 2624 mon = emalloc_zero(cb); 2625 ZERO(*pnow); 2626 ZERO(last_older); 2627 next_report = time(NULL) + MRU_REPORT_SECS; 2628 2629 limit = min(3 * MAXFRAGS, ntpd_row_limit); 2630 frags = MAXFRAGS; 2631 snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s", 2632 nonce, frags, parms); 2633 nonce_uses++; 2634 2635 while (TRUE) { 2636 if (debug) 2637 xprintf(stderr, "READ_MRU parms: %s\n", req_buf); 2638 2639 qres = doqueryex(CTL_OP_READ_MRU, 0, 0, 2640 strlen(req_buf), req_buf, 2641 &rstatus, &rsize, &rdata, TRUE); 2642 2643 if (CERR_UNKNOWNVAR == qres && ri > 0) { 2644 /* 2645 * None of the supplied prior entries match, so 2646 * toss them from our list and try again. 2647 */ 2648 if (debug) 2649 xprintf(stderr, 2650 "no overlap between %d prior entries and server MRU list\n", 2651 ri); 2652 while (ri--) { 2653 recent = HEAD_DLIST(mru_list, mlink); 2654 INSIST(recent != NULL); 2655 if (debug) 2656 xprintf(stderr, 2657 "tossing prior entry %s to resync\n", 2658 sptoa(&recent->addr)); 2659 UNLINK_DLIST(recent, mlink); 2660 hash = NTP_HASH_ADDR(&recent->addr); 2661 UNLINK_SLIST(unlinked, hash_table[hash], 2662 recent, hlink, mru); 2663 INSIST(unlinked == recent); 2664 free(recent); 2665 mru_count--; 2666 } 2667 if (NULL == HEAD_DLIST(mru_list, mlink)) { 2668 restarted_count++; 2669 if (restarted_count > 8) { 2670 xprintf(stderr, 2671 "Giving up after 8 restarts from the beginning.\n" 2672 "With high-traffic NTP servers, this can occur if the\n" 2673 "MRU list is limited to less than about 16 seconds' of\n" 2674 "entries. See the 'mru' ntp.conf directive to adjust.\n"); 2675 goto cleanup_return; 2676 } 2677 if (debug) 2678 xprintf(stderr, 2679 "---> Restarting from the beginning, retry #%u\n", 2680 restarted_count); 2681 } 2682 } else if (CERR_UNKNOWNVAR == qres) { 2683 xprintf(stderr, 2684 "CERR_UNKNOWNVAR from ntpd but no priors given.\n"); 2685 goto cleanup_return; 2686 } else if (CERR_BADVALUE == qres) { 2687 if (cap_frags) { 2688 cap_frags = FALSE; 2689 if (debug) 2690 xprintf(stderr, 2691 "Reverted to row limit from fragments limit.\n"); 2692 } else { 2693 /* ntpd has lower cap on row limit */ 2694 ntpd_row_limit--; 2695 limit = min(limit, ntpd_row_limit); 2696 if (debug) 2697 xprintf(stderr, 2698 "Row limit reduced to %d following CERR_BADVALUE.\n", 2699 limit); 2700 } 2701 } else if (ERR_INCOMPLETE == qres || 2702 ERR_TIMEOUT == qres) { 2703 /* 2704 * Reduce the number of rows/frags requested by 2705 * half to recover from lost response fragments. 2706 */ 2707 if (cap_frags) { 2708 frags = max(2, frags / 2); 2709 if (debug) 2710 xprintf(stderr, 2711 "Frag limit reduced to %d following incomplete response.\n", 2712 frags); 2713 } else { 2714 limit = max(2, limit / 2); 2715 if (debug) 2716 xprintf(stderr, 2717 "Row limit reduced to %d following incomplete response.\n", 2718 limit); 2719 } 2720 } else if (qres) { 2721 show_error_msg(qres, 0); 2722 goto cleanup_return; 2723 } 2724 /* 2725 * This is a cheap cop-out implementation of rawmode 2726 * output for mrulist. A better approach would be to 2727 * dump similar output after the list is collected by 2728 * ntpq with a continuous sequence of indexes. This 2729 * cheap approach has indexes resetting to zero for 2730 * each query/response, and duplicates are not 2731 * coalesced. 2732 */ 2733 if (!qres && rawmode) 2734 printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout); 2735 ci = 0; 2736 have_addr_older = FALSE; 2737 have_last_older = FALSE; 2738 while (!qres && nextvar(&rsize, &rdata, &tag, &val)) { 2739 INSIST(tag && val); 2740 if (debug > 1) 2741 xprintf(stderr, "nextvar gave: %s = %s\n", 2742 tag, val); 2743 switch(tag[0]) { 2744 2745 case 'a': 2746 if (!strcmp(tag, "addr.older")) { 2747 if (!have_last_older) { 2748 xprintf(stderr, 2749 "addr.older %s before last.older\n", 2750 val); 2751 goto cleanup_return; 2752 } 2753 if (!decodenetnum(val, &addr_older)) { 2754 xprintf(stderr, 2755 "addr.older %s garbled\n", 2756 val); 2757 goto cleanup_return; 2758 } 2759 hash = NTP_HASH_ADDR(&addr_older); 2760 for (recent = hash_table[hash]; 2761 recent != NULL; 2762 recent = recent->hlink) 2763 if (ADDR_PORT_EQ( 2764 &addr_older, 2765 &recent->addr)) 2766 break; 2767 if (NULL == recent) { 2768 xprintf(stderr, 2769 "addr.older %s not in hash table\n", 2770 val); 2771 goto cleanup_return; 2772 } 2773 if (!L_ISEQU(&last_older, 2774 &recent->last)) { 2775 xprintf(stderr, 2776 "last.older %08x.%08x mismatches %08x.%08x expected.\n", 2777 last_older.l_ui, 2778 last_older.l_uf, 2779 recent->last.l_ui, 2780 recent->last.l_uf); 2781 goto cleanup_return; 2782 } 2783 have_addr_older = TRUE; 2784 } else if (1 != sscanf(tag, "addr.%d", &si) 2785 || si != ci) 2786 goto nomatch; 2787 else if (decodenetnum(val, &mon->addr)) 2788 MGOT(MRU_GOT_ADDR); 2789 break; 2790 2791 case 'l': 2792 if (!strcmp(tag, "last.older")) { 2793 if ('0' != val[0] || 2794 'x' != val[1] || 2795 !hextolfp(val + 2, &last_older)) { 2796 xprintf(stderr, 2797 "last.older %s garbled\n", 2798 val); 2799 goto cleanup_return; 2800 } 2801 have_last_older = TRUE; 2802 } else if (!strcmp(tag, "last.newest")) { 2803 if (0 != got) { 2804 xprintf(stderr, 2805 "last.newest %s before complete row, got = 0x%x\n", 2806 val, (u_int)got); 2807 goto cleanup_return; 2808 } 2809 if (!have_now) { 2810 xprintf(stderr, 2811 "last.newest %s before now=\n", 2812 val); 2813 goto cleanup_return; 2814 } 2815 head = HEAD_DLIST(mru_list, mlink); 2816 if (NULL != head) { 2817 if ('0' != val[0] || 2818 'x' != val[1] || 2819 !hextolfp(val + 2, &newest) || 2820 !L_ISEQU(&newest, 2821 &head->last)) { 2822 xprintf(stderr, 2823 "last.newest %s mismatches %08x.%08x", 2824 val, 2825 head->last.l_ui, 2826 head->last.l_uf); 2827 goto cleanup_return; 2828 } 2829 } 2830 list_complete = TRUE; 2831 } else if (1 != sscanf(tag, "last.%d", &si) || 2832 si != ci || '0' != val[0] || 2833 'x' != val[1] || 2834 !hextolfp(val + 2, &mon->last)) { 2835 goto nomatch; 2836 } else { 2837 MGOT(MRU_GOT_LAST); 2838 /* 2839 * allow interrupted retrieval, 2840 * using most recent retrieved 2841 * entry's last seen timestamp 2842 * as the end of operation. 2843 */ 2844 *pnow = mon->last; 2845 } 2846 break; 2847 2848 case 'f': 2849 if (1 != sscanf(tag, "first.%d", &si) || 2850 si != ci || '0' != val[0] || 2851 'x' != val[1] || 2852 !hextolfp(val + 2, &mon->first)) 2853 goto nomatch; 2854 MGOT(MRU_GOT_FIRST); 2855 break; 2856 2857 case 'n': 2858 if (!strcmp(tag, "nonce")) { 2859 strlcpy(nonce, val, sizeof(nonce)); 2860 nonce_uses = 0; 2861 break; /* case */ 2862 } else if (strcmp(tag, "now") || 2863 '0' != val[0] || 2864 'x' != val[1] || 2865 !hextolfp(val + 2, pnow)) 2866 goto nomatch; 2867 have_now = TRUE; 2868 break; 2869 2870 case 'c': 2871 if (1 != sscanf(tag, "ct.%d", &si) || 2872 si != ci || 2873 1 != sscanf(val, "%d", &mon->count) 2874 || mon->count < 1) 2875 goto nomatch; 2876 MGOT(MRU_GOT_COUNT); 2877 break; 2878 2879 case 'm': 2880 if (1 != sscanf(tag, "mv.%d", &si) || 2881 si != ci || 2882 1 != sscanf(val, "%d", &mv)) 2883 goto nomatch; 2884 mon->mode = PKT_MODE(mv); 2885 mon->ver = PKT_VERSION(mv); 2886 MGOT(MRU_GOT_MV); 2887 break; 2888 2889 case 'r': 2890 if (1 != sscanf(tag, "rs.%d", &si) || 2891 si != ci || 2892 1 != sscanf(val, "0x%hx", &mon->rs)) 2893 goto nomatch; 2894 MGOT(MRU_GOT_RS); 2895 break; 2896 2897 default: 2898 nomatch: 2899 /* empty stmt */ ; 2900 /* ignore unknown tags */ 2901 } 2902 } 2903 if (have_now) 2904 list_complete = TRUE; 2905 if (list_complete) { 2906 INSIST(0 == ri || have_addr_older); 2907 } 2908 if (mrulist_interrupted) { 2909 printf("mrulist retrieval interrupted by operator.\n" 2910 "Displaying partial client list.\n"); 2911 fflush(stdout); 2912 } 2913 if (list_complete || mrulist_interrupted) { 2914 xprintf(stderr, 2915 "\rRetrieved %u unique MRU entries and %u updates.\n", 2916 mru_count, mru_dupes); 2917 fflush(stderr); 2918 break; 2919 } 2920 if (time(NULL) >= next_report) { 2921 next_report += MRU_REPORT_SECS; 2922 xprintf(stderr, "\r%u (%u updates) ", mru_count, 2923 mru_dupes); 2924 fflush(stderr); 2925 } 2926 2927 /* 2928 * Snooze for a bit between queries to let ntpd catch 2929 * up with other duties. 2930 */ 2931 #ifdef SYS_WINNT 2932 Sleep(sleep_msecs); 2933 #elif !defined(HAVE_NANOSLEEP) 2934 sleep((sleep_msecs / 1000) + 1); 2935 #else 2936 { 2937 struct timespec interv = { 0, 2938 1000 * sleep_msecs }; 2939 nanosleep(&interv, NULL); 2940 } 2941 #endif 2942 /* 2943 * If there were no errors, increase the number of rows 2944 * to a maximum of 3 * MAXFRAGS (the most packets ntpq 2945 * can handle in one response), on the assumption that 2946 * no less than 3 rows fit in each packet, capped at 2947 * our best guess at the server's row limit. 2948 */ 2949 if (!qres) { 2950 if (cap_frags) { 2951 frags = min(MAXFRAGS, frags + 1); 2952 } else { 2953 limit = min3(3 * MAXFRAGS, 2954 ntpd_row_limit, 2955 max(limit + 1, 2956 limit * 33 / 32)); 2957 } 2958 } 2959 /* 2960 * prepare next query with as many address and last-seen 2961 * timestamps as will fit in a single packet. 2962 */ 2963 req = req_buf; 2964 req_end = req_buf + sizeof(req_buf); 2965 #define REQ_ROOM (req_end - req) 2966 snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce, 2967 (cap_frags) 2968 ? "frags" 2969 : "limit", 2970 (cap_frags) 2971 ? frags 2972 : limit, 2973 parms); 2974 req += strlen(req); 2975 nonce_uses++; 2976 if (nonce_uses >= 4) { 2977 if (!fetch_nonce(nonce, sizeof(nonce))) 2978 goto cleanup_return; 2979 nonce_uses = 0; 2980 } 2981 2982 2983 for (ri = 0, recent = HEAD_DLIST(mru_list, mlink); 2984 recent != NULL; 2985 ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) { 2986 2987 snprintf(buf, sizeof(buf), 2988 ", addr.%d=%s, last.%d=0x%08x.%08x", 2989 ri, sptoa(&recent->addr), ri, 2990 recent->last.l_ui, recent->last.l_uf); 2991 chars = strlen(buf); 2992 if ((size_t)REQ_ROOM <= chars) 2993 break; 2994 memcpy(req, buf, chars + 1); 2995 req += chars; 2996 } 2997 } 2998 2999 c_mru_l_rc = TRUE; 3000 goto retain_hash_table; 3001 3002 cleanup_return: 3003 free(hash_table); 3004 hash_table = NULL; 3005 3006 retain_hash_table: 3007 if (mon != NULL) 3008 free(mon); 3009 3010 return c_mru_l_rc; 3011 } 3012 3013 3014 /* 3015 * qcmp_mru_addr - sort MRU entries by remote address. 3016 * 3017 * All IPv4 addresses sort before any IPv6, addresses are sorted by 3018 * value within address family. 3019 */ 3020 static int 3021 qcmp_mru_addr( 3022 const void *v1, 3023 const void *v2 3024 ) 3025 { 3026 const mru * const * ppm1 = v1; 3027 const mru * const * ppm2 = v2; 3028 const mru * pm1; 3029 const mru * pm2; 3030 u_short af1; 3031 u_short af2; 3032 size_t cmplen; 3033 size_t addr_off; 3034 3035 pm1 = *ppm1; 3036 pm2 = *ppm2; 3037 3038 af1 = AF(&pm1->addr); 3039 af2 = AF(&pm2->addr); 3040 3041 if (af1 != af2) 3042 return (AF_INET == af1) 3043 ? -1 3044 : 1; 3045 3046 cmplen = SIZEOF_INADDR(af1); 3047 addr_off = (AF_INET == af1) 3048 ? offsetof(struct sockaddr_in, sin_addr) 3049 : offsetof(struct sockaddr_in6, sin6_addr); 3050 3051 return memcmp((const char *)&pm1->addr + addr_off, 3052 (const char *)&pm2->addr + addr_off, 3053 cmplen); 3054 } 3055 3056 3057 static int 3058 qcmp_mru_r_addr( 3059 const void *v1, 3060 const void *v2 3061 ) 3062 { 3063 return -qcmp_mru_addr(v1, v2); 3064 } 3065 3066 3067 /* 3068 * qcmp_mru_count - sort MRU entries by times seen (hit count). 3069 */ 3070 static int 3071 qcmp_mru_count( 3072 const void *v1, 3073 const void *v2 3074 ) 3075 { 3076 const mru * const * ppm1 = v1; 3077 const mru * const * ppm2 = v2; 3078 const mru * pm1; 3079 const mru * pm2; 3080 3081 pm1 = *ppm1; 3082 pm2 = *ppm2; 3083 3084 return (pm1->count < pm2->count) 3085 ? -1 3086 : ((pm1->count == pm2->count) 3087 ? 0 3088 : 1); 3089 } 3090 3091 3092 static int 3093 qcmp_mru_r_count( 3094 const void *v1, 3095 const void *v2 3096 ) 3097 { 3098 return -qcmp_mru_count(v1, v2); 3099 } 3100 3101 3102 /* 3103 * qcmp_mru_avgint - sort MRU entries by average interval. 3104 */ 3105 static int 3106 qcmp_mru_avgint( 3107 const void *v1, 3108 const void *v2 3109 ) 3110 { 3111 const mru * const * ppm1 = v1; 3112 const mru * const * ppm2 = v2; 3113 const mru * pm1; 3114 const mru * pm2; 3115 l_fp interval; 3116 double avg1; 3117 double avg2; 3118 3119 pm1 = *ppm1; 3120 pm2 = *ppm2; 3121 3122 interval = pm1->last; 3123 L_SUB(&interval, &pm1->first); 3124 LFPTOD(&interval, avg1); 3125 avg1 /= pm1->count; 3126 3127 interval = pm2->last; 3128 L_SUB(&interval, &pm2->first); 3129 LFPTOD(&interval, avg2); 3130 avg2 /= pm2->count; 3131 3132 if (avg1 < avg2) 3133 return -1; 3134 else if (avg1 > avg2) 3135 return 1; 3136 3137 /* secondary sort on lstint - rarely tested */ 3138 if (L_ISEQU(&pm1->last, &pm2->last)) 3139 return 0; 3140 else if (L_ISGEQ(&pm1->last, &pm2->last)) 3141 return -1; 3142 else 3143 return 1; 3144 } 3145 3146 3147 static int 3148 qcmp_mru_r_avgint( 3149 const void *v1, 3150 const void *v2 3151 ) 3152 { 3153 return -qcmp_mru_avgint(v1, v2); 3154 } 3155 3156 3157 /* 3158 * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most 3159 * Recently Used (seen) remote address list from ntpd. 3160 * 3161 * Similar to ntpdc's monlist command, but not limited to a single 3162 * request/response, and thereby not limited to a few hundred remote 3163 * addresses. 3164 * 3165 * See ntpd/ntp_control.c read_mru_list() for comments on the way 3166 * CTL_OP_READ_MRU is designed to be used. 3167 * 3168 * mrulist intentionally differs from monlist in the way the avgint 3169 * column is calculated. monlist includes the time after the last 3170 * packet from the client until the monlist query time in the average, 3171 * while mrulist excludes it. That is, monlist's average interval grows 3172 * over time for remote addresses not heard from in some time, while it 3173 * remains unchanged in mrulist. This also affects the avgint value for 3174 * entries representing a single packet, with identical first and last 3175 * timestamps. mrulist shows 0 avgint, monlist shows a value identical 3176 * to lstint. 3177 */ 3178 static void 3179 mrulist( 3180 struct parse * pcmd, 3181 FILE * fp 3182 ) 3183 { 3184 const char mincount_eq[] = "mincount="; 3185 const char resall_eq[] = "resall="; 3186 const char resany_eq[] = "resany="; 3187 const char maxlstint_eq[] = "maxlstint="; 3188 const char laddr_eq[] = "laddr="; 3189 const char sort_eq[] = "sort="; 3190 mru_sort_order order; 3191 size_t n; 3192 char parms_buf[128]; 3193 char buf[24]; 3194 char *parms; 3195 const char *arg; 3196 size_t cb; 3197 mru **sorted; 3198 mru **ppentry; 3199 mru *recent; 3200 l_fp now; 3201 l_fp interval; 3202 double favgint; 3203 double flstint; 3204 int avgint; 3205 int lstint; 3206 size_t i; 3207 3208 mrulist_interrupted = FALSE; 3209 push_ctrl_c_handler(&mrulist_ctrl_c_hook); 3210 xprintf(stderr, 3211 "Ctrl-C will stop MRU retrieval and display partial results.\n"); 3212 fflush(stderr); 3213 3214 order = MRUSORT_DEF; 3215 parms_buf[0] = '\0'; 3216 parms = parms_buf; 3217 for (i = 0; i < pcmd->nargs; i++) { 3218 arg = pcmd->argval[i].string; 3219 if (arg != NULL) { 3220 cb = strlen(arg) + 1; 3221 if ((!strncmp(resall_eq, arg, sizeof(resall_eq) 3222 - 1) || !strncmp(resany_eq, arg, 3223 sizeof(resany_eq) - 1) || !strncmp( 3224 mincount_eq, arg, sizeof(mincount_eq) - 1) 3225 || !strncmp(laddr_eq, arg, sizeof(laddr_eq) 3226 - 1) || !strncmp(maxlstint_eq, arg, 3227 sizeof(laddr_eq) - 1)) && parms + cb + 2 <= 3228 parms_buf + sizeof(parms_buf)) { 3229 /* these are passed intact to ntpd */ 3230 memcpy(parms, ", ", 2); 3231 parms += 2; 3232 memcpy(parms, arg, cb); 3233 parms += cb - 1; 3234 } else if (!strncmp(sort_eq, arg, 3235 sizeof(sort_eq) - 1)) { 3236 arg += sizeof(sort_eq) - 1; 3237 for (n = 0; 3238 n < COUNTOF(mru_sort_keywords); 3239 n++) 3240 if (!strcmp(mru_sort_keywords[n], 3241 arg)) 3242 break; 3243 if (n < COUNTOF(mru_sort_keywords)) 3244 order = n; 3245 } else if (!strcmp("limited", arg) || 3246 !strcmp("kod", arg)) { 3247 /* transform to resany=... */ 3248 snprintf(buf, sizeof(buf), 3249 ", resany=0x%x", 3250 ('k' == arg[0]) 3251 ? RES_KOD 3252 : RES_LIMITED); 3253 cb = 1 + strlen(buf); 3254 if (parms + cb < 3255 parms_buf + sizeof(parms_buf)) { 3256 memcpy(parms, buf, cb); 3257 parms += cb - 1; 3258 } 3259 } else 3260 xprintf(stderr, 3261 "ignoring unrecognized mrulist parameter: %s\n", 3262 arg); 3263 } 3264 } 3265 parms = parms_buf; 3266 3267 if (!collect_mru_list(parms, &now)) 3268 return; 3269 3270 /* display the results */ 3271 if (rawmode) 3272 goto cleanup_return; 3273 3274 /* construct an array of entry pointers in default order */ 3275 sorted = eallocarray(mru_count, sizeof(*sorted)); 3276 ppentry = sorted; 3277 if (MRUSORT_R_DEF != order) { 3278 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) 3279 INSIST(ppentry < sorted + mru_count); 3280 *ppentry = recent; 3281 ppentry++; 3282 ITER_DLIST_END() 3283 } else { 3284 REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) 3285 INSIST(ppentry < sorted + mru_count); 3286 *ppentry = recent; 3287 ppentry++; 3288 REV_ITER_DLIST_END() 3289 } 3290 3291 if (ppentry - sorted != (int)mru_count) { 3292 xprintf(stderr, 3293 "mru_count %u should match MRU list depth %ld.\n", 3294 mru_count, (long)(ppentry - sorted)); 3295 free(sorted); 3296 goto cleanup_return; 3297 } 3298 3299 /* re-sort sorted[] if not default or reverse default */ 3300 if (MRUSORT_R_DEF < order) 3301 qsort(sorted, mru_count, sizeof(sorted[0]), 3302 mru_qcmp_table[order]); 3303 3304 mrulist_interrupted = FALSE; 3305 printf( "lstint avgint rstr r m v count rport remote address\n" 3306 "==============================================================================\n"); 3307 /* '=' x 78 */ 3308 for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) { 3309 recent = *ppentry; 3310 interval = now; 3311 L_SUB(&interval, &recent->last); 3312 LFPTOD(&interval, flstint); 3313 lstint = (int)(flstint + 0.5); 3314 interval = recent->last; 3315 L_SUB(&interval, &recent->first); 3316 LFPTOD(&interval, favgint); 3317 favgint /= recent->count; 3318 avgint = (int)(favgint + 0.5); 3319 xprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n", 3320 lstint, avgint, recent->rs, 3321 (RES_KOD & recent->rs) 3322 ? 'K' 3323 : (RES_LIMITED & recent->rs) 3324 ? 'L' 3325 : '.', 3326 (int)recent->mode, (int)recent->ver, 3327 recent->count, SRCPORT(&recent->addr), 3328 nntohost(&recent->addr)); 3329 if (showhostnames) 3330 fflush(fp); 3331 if (mrulist_interrupted) { 3332 xputs("\n --interrupted--\n", fp); 3333 fflush(fp); 3334 break; 3335 } 3336 } 3337 fflush(fp); 3338 if (debug) { 3339 xprintf(stderr, 3340 "--- completed, freeing sorted[] pointers\n"); 3341 fflush(stderr); 3342 } 3343 free(sorted); 3344 3345 cleanup_return: 3346 if (debug) { 3347 xprintf(stderr, "... freeing MRU entries\n"); 3348 fflush(stderr); 3349 } 3350 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) 3351 free(recent); 3352 ITER_DLIST_END() 3353 if (debug) { 3354 xprintf(stderr, "... freeing hash_table[]\n"); 3355 fflush(stderr); 3356 } 3357 free(hash_table); 3358 hash_table = NULL; 3359 INIT_DLIST(mru_list, mlink); 3360 3361 pop_ctrl_c_handler(&mrulist_ctrl_c_hook); 3362 } 3363 3364 3365 /* 3366 * validate_ifnum - helper for ifstats() 3367 * 3368 * Ensures rows are received in order and complete. 3369 */ 3370 static void 3371 validate_ifnum( 3372 FILE * fp, 3373 u_int ifnum, 3374 int * pfields, 3375 ifstats_row * prow 3376 ) 3377 { 3378 if (prow->ifnum == ifnum) 3379 return; 3380 if (prow->ifnum + 1 <= ifnum) { 3381 if (*pfields < IFSTATS_FIELDS) 3382 xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n", 3383 *pfields, IFSTATS_FIELDS); 3384 *pfields = 0; 3385 prow->ifnum = ifnum; 3386 return; 3387 } 3388 xprintf(stderr, 3389 "received if index %u, have %d of %d fields for index %u, aborting.\n", 3390 ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum); 3391 exit(1); 3392 } 3393 3394 3395 /* 3396 * another_ifstats_field - helper for ifstats() 3397 * 3398 * If all fields for the row have been received, print it. 3399 */ 3400 static void 3401 another_ifstats_field( 3402 int * pfields, 3403 ifstats_row * prow, 3404 FILE * fp 3405 ) 3406 { 3407 u_int ifnum; 3408 3409 (*pfields)++; 3410 /* we understand 12 tags */ 3411 if (IFSTATS_FIELDS > *pfields) 3412 return; 3413 /* 3414 " interface name send\n" 3415 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n" 3416 "==============================================================================\n"); 3417 */ 3418 xprintf(fp, 3419 "%3u %-24.24s %c %4x %3u %2u %6u %6u %6u %5u %8d\n" 3420 " %s\n", 3421 prow->ifnum, prow->name, 3422 (prow->enabled) 3423 ? '.' 3424 : 'D', 3425 prow->flags, prow->ttl, prow->mcast_count, 3426 prow->received, prow->sent, prow->send_errors, 3427 prow->peer_count, prow->uptime, sptoa(&prow->addr)); 3428 if (!SOCK_UNSPEC(&prow->bcast)) 3429 xprintf(fp, " %s\n", sptoa(&prow->bcast)); 3430 ifnum = prow->ifnum; 3431 ZERO(*prow); 3432 prow->ifnum = ifnum; 3433 } 3434 3435 3436 /* 3437 * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats. 3438 */ 3439 static void 3440 ifstats( 3441 struct parse * pcmd, 3442 FILE * fp 3443 ) 3444 { 3445 const char addr_fmt[] = "addr.%u"; 3446 const char bcast_fmt[] = "bcast.%u"; 3447 const char en_fmt[] = "en.%u"; /* enabled */ 3448 const char flags_fmt[] = "flags.%u"; 3449 const char mc_fmt[] = "mc.%u"; /* mcast count */ 3450 const char name_fmt[] = "name.%u"; 3451 const char pc_fmt[] = "pc.%u"; /* peer count */ 3452 const char rx_fmt[] = "rx.%u"; 3453 const char tl_fmt[] = "tl.%u"; /* ttl */ 3454 const char tx_fmt[] = "tx.%u"; 3455 const char txerr_fmt[] = "txerr.%u"; 3456 const char up_fmt[] = "up.%u"; /* uptime */ 3457 const char * datap; 3458 int qres; 3459 size_t dsize; 3460 u_short rstatus; 3461 char * tag; 3462 char * val; 3463 int fields; 3464 u_int ui; 3465 ifstats_row row; 3466 int comprende; 3467 size_t len; 3468 3469 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus, 3470 &dsize, &datap); 3471 if (qres) /* message already displayed */ 3472 return; 3473 3474 xprintf(fp, 3475 " interface name send\n" 3476 " # address/broadcast drop flag ttl mc received sent failed peers uptime\n" 3477 "==============================================================================\n"); 3478 /* '=' x 78 */ 3479 3480 ZERO(row); 3481 fields = 0; 3482 ui = 0; 3483 while (nextvar(&dsize, &datap, &tag, &val)) { 3484 INSIST(tag && val); 3485 if (debug > 1) 3486 xprintf(stderr, "nextvar gave: %s = %s\n", tag, val); 3487 comprende = FALSE; 3488 switch(tag[0]) { 3489 3490 case 'a': 3491 if (1 == sscanf(tag, addr_fmt, &ui) && 3492 decodenetnum(val, &row.addr)) 3493 comprende = TRUE; 3494 break; 3495 3496 case 'b': 3497 if (1 == sscanf(tag, bcast_fmt, &ui) && 3498 ('\0' == *val || 3499 decodenetnum(val, &row.bcast))) 3500 comprende = TRUE; 3501 break; 3502 3503 case 'e': 3504 if (1 == sscanf(tag, en_fmt, &ui) && 3505 1 == sscanf(val, "%d", &row.enabled)) 3506 comprende = TRUE; 3507 break; 3508 3509 case 'f': 3510 if (1 == sscanf(tag, flags_fmt, &ui) && 3511 1 == sscanf(val, "0x%x", &row.flags)) 3512 comprende = TRUE; 3513 break; 3514 3515 case 'm': 3516 if (1 == sscanf(tag, mc_fmt, &ui) && 3517 1 == sscanf(val, "%u", &row.mcast_count)) 3518 comprende = TRUE; 3519 break; 3520 3521 case 'n': 3522 if (1 == sscanf(tag, name_fmt, &ui)) { 3523 /* strip quotes */ 3524 len = strlen(val); 3525 if (len >= 2 && 3526 len - 2 < sizeof(row.name)) { 3527 len -= 2; 3528 memcpy(row.name, val + 1, len); 3529 row.name[len] = '\0'; 3530 comprende = TRUE; 3531 } 3532 } 3533 break; 3534 3535 case 'p': 3536 if (1 == sscanf(tag, pc_fmt, &ui) && 3537 1 == sscanf(val, "%u", &row.peer_count)) 3538 comprende = TRUE; 3539 break; 3540 3541 case 'r': 3542 if (1 == sscanf(tag, rx_fmt, &ui) && 3543 1 == sscanf(val, "%u", &row.received)) 3544 comprende = TRUE; 3545 break; 3546 3547 case 't': 3548 if (1 == sscanf(tag, tl_fmt, &ui) && 3549 1 == sscanf(val, "%u", &row.ttl)) 3550 comprende = TRUE; 3551 else if (1 == sscanf(tag, tx_fmt, &ui) && 3552 1 == sscanf(val, "%u", &row.sent)) 3553 comprende = TRUE; 3554 else if (1 == sscanf(tag, txerr_fmt, &ui) && 3555 1 == sscanf(val, "%u", &row.send_errors)) 3556 comprende = TRUE; 3557 break; 3558 3559 case 'u': 3560 if (1 == sscanf(tag, up_fmt, &ui) && 3561 1 == sscanf(val, "%u", &row.uptime)) 3562 comprende = TRUE; 3563 break; 3564 } 3565 3566 if (comprende) { 3567 /* error out if rows out of order */ 3568 validate_ifnum(fp, ui, &fields, &row); 3569 /* if the row is complete, print it */ 3570 another_ifstats_field(&fields, &row, fp); 3571 } 3572 } 3573 if (fields != IFSTATS_FIELDS) 3574 xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n", 3575 fields, IFSTATS_FIELDS); 3576 3577 fflush(fp); 3578 } 3579 3580 3581 /* 3582 * validate_reslist_idx - helper for reslist() 3583 * 3584 * Ensures rows are received in order and complete. 3585 */ 3586 static void 3587 validate_reslist_idx( 3588 FILE * fp, 3589 u_int idx, 3590 int * pfields, 3591 reslist_row * prow 3592 ) 3593 { 3594 if (prow->idx == idx) 3595 return; 3596 if (prow->idx + 1 == idx) { 3597 if (*pfields < RESLIST_FIELDS) 3598 xprintf(fp, "Warning: incomplete row with %d (of %d) fields", 3599 *pfields, RESLIST_FIELDS); 3600 *pfields = 0; 3601 prow->idx = idx; 3602 return; 3603 } 3604 xprintf(stderr, 3605 "received reslist index %u, have %d of %d fields for index %u, aborting.\n", 3606 idx, *pfields, RESLIST_FIELDS, prow->idx); 3607 exit(1); 3608 } 3609 3610 3611 /* 3612 * another_reslist_field - helper for reslist() 3613 * 3614 * If all fields for the row have been received, print it. 3615 */ 3616 static void 3617 another_reslist_field( 3618 int * pfields, 3619 reslist_row * prow, 3620 FILE * fp 3621 ) 3622 { 3623 char addrmaskstr[128]; 3624 int prefix; /* subnet mask as prefix bits count */ 3625 u_int idx; 3626 3627 (*pfields)++; 3628 /* we understand 4 tags */ 3629 if (RESLIST_FIELDS > *pfields) 3630 return; 3631 3632 prefix = sockaddr_masktoprefixlen(&prow->mask); 3633 if (prefix >= 0) 3634 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d", 3635 stoa(&prow->addr), prefix); 3636 else 3637 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s", 3638 stoa(&prow->addr), stoa(&prow->mask)); 3639 3640 /* 3641 " hits addr/prefix or addr mask\n" 3642 " restrictions\n" 3643 "==============================================================================\n"); 3644 */ 3645 xprintf(fp, 3646 "%10lu %s\n" 3647 " %s\n", 3648 prow->hits, addrmaskstr, prow->flagstr); 3649 idx = prow->idx; 3650 ZERO(*prow); 3651 prow->idx = idx; 3652 } 3653 3654 3655 /* 3656 * reslist - ntpq -c reslist modeled on ntpdc -c reslist. 3657 */ 3658 static void 3659 reslist( 3660 struct parse * pcmd, 3661 FILE * fp 3662 ) 3663 { 3664 const char addr_fmtu[] = "addr.%u"; 3665 const char mask_fmtu[] = "mask.%u"; 3666 const char hits_fmt[] = "hits.%u"; 3667 const char flags_fmt[] = "flags.%u"; 3668 const char qdata[] = "addr_restrictions"; 3669 const int qdata_chars = COUNTOF(qdata) - 1; 3670 const char * datap; 3671 int qres; 3672 size_t dsize; 3673 u_short rstatus; 3674 char * tag; 3675 char * val; 3676 int fields; 3677 u_int ui; 3678 reslist_row row; 3679 int comprende; 3680 size_t len; 3681 3682 qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars, 3683 qdata, &rstatus, &dsize, &datap); 3684 if (qres) /* message already displayed */ 3685 return; 3686 3687 xprintf(fp, 3688 " hits addr/prefix or addr mask\n" 3689 " restrictions\n" 3690 "==============================================================================\n"); 3691 /* '=' x 78 */ 3692 3693 ZERO(row); 3694 fields = 0; 3695 ui = 0; 3696 while (nextvar(&dsize, &datap, &tag, &val)) { 3697 INSIST(tag && val); 3698 if (debug > 1) 3699 xprintf(stderr, "nextvar gave: %s = %s\n", tag, val); 3700 comprende = FALSE; 3701 switch(tag[0]) { 3702 3703 case 'a': 3704 if (1 == sscanf(tag, addr_fmtu, &ui) && 3705 decodenetnum(val, &row.addr)) 3706 comprende = TRUE; 3707 break; 3708 3709 case 'f': 3710 if (1 == sscanf(tag, flags_fmt, &ui)) { 3711 if (NULL == val) { 3712 row.flagstr[0] = '\0'; 3713 comprende = TRUE; 3714 } else if ((len = strlen(val)) < sizeof(row.flagstr)) { 3715 memcpy(row.flagstr, val, len); 3716 row.flagstr[len] = '\0'; 3717 comprende = TRUE; 3718 } else { 3719 /* no flags, and still !comprende */ 3720 row.flagstr[0] = '\0'; 3721 } 3722 } 3723 break; 3724 3725 case 'h': 3726 if (1 == sscanf(tag, hits_fmt, &ui) && 3727 1 == sscanf(val, "%lu", &row.hits)) 3728 comprende = TRUE; 3729 break; 3730 3731 case 'm': 3732 if (1 == sscanf(tag, mask_fmtu, &ui) && 3733 decodenetnum(val, &row.mask)) 3734 comprende = TRUE; 3735 break; 3736 } 3737 3738 if (comprende) { 3739 /* error out if rows out of order */ 3740 validate_reslist_idx(fp, ui, &fields, &row); 3741 /* if the row is complete, print it */ 3742 another_reslist_field(&fields, &row, fp); 3743 } 3744 } 3745 if (fields != RESLIST_FIELDS) 3746 xprintf(fp, "Warning: incomplete row with %d (of %d) fields", 3747 fields, RESLIST_FIELDS); 3748 3749 fflush(fp); 3750 } 3751 3752 3753 /* 3754 * collect_display_vdc 3755 */ 3756 static void 3757 collect_display_vdc( 3758 associd_t as, 3759 vdc * table, 3760 int decodestatus, 3761 FILE * fp 3762 ) 3763 { 3764 static const char * const suf[2] = { "adr", "port" }; 3765 static const char * const leapbits[4] = { "00", "01", 3766 "10", "11" }; 3767 struct varlist vl[MAXLIST]; 3768 char tagbuf[32]; 3769 vdc *pvdc; 3770 u_short rstatus; 3771 size_t rsize; 3772 const char *rdata; 3773 int qres; 3774 char *tag; 3775 char *val; 3776 u_int n; 3777 size_t len; 3778 int match; 3779 u_long ul; 3780 int vtype; 3781 sockaddr_u sau; 3782 3783 ZERO(vl); 3784 for (pvdc = table; pvdc->tag != NULL; pvdc++) { 3785 ZERO(pvdc->v); 3786 if (NTP_ADD != pvdc->type) { 3787 doaddvlist(vl, pvdc->tag); 3788 } else { 3789 for (n = 0; n < COUNTOF(suf); n++) { 3790 snprintf(tagbuf, sizeof(tagbuf), "%s%s", 3791 pvdc->tag, suf[n]); 3792 doaddvlist(vl, tagbuf); 3793 } 3794 } 3795 } 3796 qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize, 3797 &rdata); 3798 doclearvlist(vl); 3799 if (qres) 3800 return; /* error msg already displayed */ 3801 3802 /* 3803 * iterate over the response variables filling vdc_table with 3804 * the retrieved values. 3805 */ 3806 while (nextvar(&rsize, &rdata, &tag, &val)) { 3807 INSIST(tag && val); 3808 n = 0; 3809 for (pvdc = table; pvdc->tag != NULL; pvdc++) { 3810 len = strlen(pvdc->tag); 3811 if (strncmp(tag, pvdc->tag, len)) 3812 continue; 3813 if (NTP_ADD != pvdc->type) { 3814 if ('\0' != tag[len]) 3815 continue; 3816 break; 3817 } 3818 match = FALSE; 3819 for (n = 0; n < COUNTOF(suf); n++) { 3820 if (strcmp(tag + len, suf[n])) 3821 continue; 3822 match = TRUE; 3823 break; 3824 } 3825 if (match) 3826 break; 3827 } 3828 if (NULL == pvdc->tag) 3829 continue; 3830 switch (pvdc->type) { 3831 3832 case NTP_STR: 3833 /* strip surrounding double quotes */ 3834 if ('"' == val[0]) { 3835 len = strlen(val); 3836 if (len > 0 && '"' == val[len - 1]) { 3837 val[len - 1] = '\0'; 3838 val++; 3839 } 3840 } 3841 /* fallthru */ 3842 case NTP_REFID: /* fallthru */ 3843 case NTP_MODE: /* fallthru */ 3844 case NTP_2BIT: 3845 pvdc->v.str = estrdup(val); 3846 break; 3847 3848 case NTP_LFP: 3849 decodets(val, &pvdc->v.lfp); 3850 break; 3851 3852 case NTP_ADP: 3853 if (!decodenetnum(val, &pvdc->v.sau)) 3854 xprintf(stderr, "malformed %s=%s\n", 3855 pvdc->tag, val); 3856 break; 3857 3858 case NTP_ADD: 3859 if (0 == n) { /* adr */ 3860 if (!decodenetnum(val, &pvdc->v.sau)) 3861 xprintf(stderr, 3862 "malformed %s=%s\n", 3863 pvdc->tag, val); 3864 } else { /* port */ 3865 if (atouint(val, &ul)) 3866 SET_PORT(&pvdc->v.sau, 3867 (u_short)ul); 3868 } 3869 break; 3870 } 3871 } 3872 3873 /* and display */ 3874 if (decodestatus) { 3875 vtype = (0 == as) 3876 ? TYPE_SYS 3877 : TYPE_PEER; 3878 xprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus, 3879 statustoa(vtype, rstatus)); 3880 } 3881 3882 for (pvdc = table; pvdc->tag != NULL; pvdc++) { 3883 switch (pvdc->type) { 3884 3885 case NTP_STR: 3886 if (pvdc->v.str != NULL) { 3887 xprintf(fp, "%s %s\n", pvdc->display, 3888 pvdc->v.str); 3889 free(pvdc->v.str); 3890 pvdc->v.str = NULL; 3891 } 3892 break; 3893 3894 case NTP_ADD: /* fallthru */ 3895 case NTP_ADP: 3896 xprintf(fp, "%s %s\n", pvdc->display, 3897 nntohostp(&pvdc->v.sau)); 3898 break; 3899 3900 case NTP_LFP: 3901 xprintf(fp, "%s %s\n", pvdc->display, 3902 prettydate(&pvdc->v.lfp)); 3903 break; 3904 3905 case NTP_MODE: 3906 atouint(pvdc->v.str, &ul); 3907 xprintf(fp, "%s %s\n", pvdc->display, 3908 modetoa((int)ul)); 3909 free(pvdc->v.str); 3910 pvdc->v.str = NULL; 3911 break; 3912 3913 case NTP_2BIT: 3914 atouint(pvdc->v.str, &ul); 3915 xprintf(fp, "%s %s\n", pvdc->display, 3916 leapbits[ul & 0x3]); 3917 free(pvdc->v.str); 3918 pvdc->v.str = NULL; 3919 break; 3920 3921 case NTP_REFID: 3922 if (!decodenetnum(pvdc->v.str, &sau)) { 3923 fprintf(fp, "%s %s\n", pvdc->display, /* Text fmt */ 3924 pvdc->v.str); 3925 } else if (drefid == REFID_IPV4) { 3926 fprintf(fp, "%s %s\n", pvdc->display, /* IPv4 fmt */ 3927 stoa(&sau)); 3928 } else { 3929 fprintf (fp, "%s 0x%08x\n", pvdc->display, /* Hex / hash */ 3930 ntohl(addr2refid(&sau))); 3931 } 3932 free(pvdc->v.str); 3933 pvdc->v.str = NULL; 3934 break; 3935 3936 default: 3937 xprintf(stderr, "unexpected vdc type %d for %s\n", 3938 pvdc->type, pvdc->tag); 3939 break; 3940 } 3941 } 3942 } 3943 3944 3945 /* 3946 * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats 3947 */ 3948 static void 3949 sysstats( 3950 struct parse *pcmd, 3951 FILE *fp 3952 ) 3953 { 3954 static vdc sysstats_vdc[] = { 3955 VDC_INIT("ss_uptime", "uptime: ", NTP_STR), 3956 VDC_INIT("ss_reset", "sysstats reset: ", NTP_STR), 3957 VDC_INIT("ss_received", "packets received: ", NTP_STR), 3958 VDC_INIT("ss_thisver", "current version: ", NTP_STR), 3959 VDC_INIT("ss_oldver", "older version: ", NTP_STR), 3960 VDC_INIT("ss_badformat", "bad length or format: ", NTP_STR), 3961 VDC_INIT("ss_badauth", "authentication failed:", NTP_STR), 3962 VDC_INIT("ss_declined", "declined: ", NTP_STR), 3963 VDC_INIT("ss_restricted", "restricted: ", NTP_STR), 3964 VDC_INIT("ss_limited", "rate limited: ", NTP_STR), 3965 VDC_INIT("ss_kodsent", "KoD responses: ", NTP_STR), 3966 VDC_INIT("ss_processed", "processed for time: ", NTP_STR), 3967 #if 0 3968 VDC_INIT("ss_lamport", "Lamport violations: ", NTP_STR), 3969 VDC_INIT("ss_tsrounding", "bad timestamp rounding:", NTP_STR), 3970 #endif 3971 VDC_INIT(NULL, NULL, 0) 3972 }; 3973 3974 collect_display_vdc(0, sysstats_vdc, FALSE, fp); 3975 } 3976 3977 3978 /* 3979 * sysinfo - modeled on ntpdc's sysinfo 3980 */ 3981 static void 3982 sysinfo( 3983 struct parse *pcmd, 3984 FILE *fp 3985 ) 3986 { 3987 static vdc sysinfo_vdc[] = { 3988 VDC_INIT("peeradr", "system peer: ", NTP_ADP), 3989 VDC_INIT("peermode", "system peer mode: ", NTP_MODE), 3990 VDC_INIT("leap", "leap indicator: ", NTP_2BIT), 3991 VDC_INIT("stratum", "stratum: ", NTP_STR), 3992 VDC_INIT("precision", "log2 precision: ", NTP_STR), 3993 VDC_INIT("rootdelay", "root delay: ", NTP_STR), 3994 VDC_INIT("rootdisp", "root dispersion: ", NTP_STR), 3995 VDC_INIT("refid", "reference ID: ", NTP_REFID), 3996 VDC_INIT("reftime", "reference time: ", NTP_LFP), 3997 VDC_INIT("sys_jitter", "system jitter: ", NTP_STR), 3998 VDC_INIT("clk_jitter", "clock jitter: ", NTP_STR), 3999 VDC_INIT("clk_wander", "clock wander: ", NTP_STR), 4000 VDC_INIT("bcastdelay", "broadcast delay: ", NTP_STR), 4001 VDC_INIT("authdelay", "symm. auth. delay:", NTP_STR), 4002 VDC_INIT(NULL, NULL, 0) 4003 }; 4004 4005 collect_display_vdc(0, sysinfo_vdc, TRUE, fp); 4006 } 4007 4008 4009 /* 4010 * kerninfo - modeled on ntpdc's kerninfo 4011 */ 4012 static void 4013 kerninfo( 4014 struct parse *pcmd, 4015 FILE *fp 4016 ) 4017 { 4018 static vdc kerninfo_vdc[] = { 4019 VDC_INIT("koffset", "pll offset: ", NTP_STR), 4020 VDC_INIT("kfreq", "pll frequency: ", NTP_STR), 4021 VDC_INIT("kmaxerr", "maximum error: ", NTP_STR), 4022 VDC_INIT("kesterr", "estimated error: ", NTP_STR), 4023 VDC_INIT("kstflags", "kernel status: ", NTP_STR), 4024 VDC_INIT("ktimeconst", "pll time constant: ", NTP_STR), 4025 VDC_INIT("kprecis", "precision: ", NTP_STR), 4026 VDC_INIT("kfreqtol", "frequency tolerance: ", NTP_STR), 4027 VDC_INIT("kppsfreq", "pps frequency: ", NTP_STR), 4028 VDC_INIT("kppsstab", "pps stability: ", NTP_STR), 4029 VDC_INIT("kppsjitter", "pps jitter: ", NTP_STR), 4030 VDC_INIT("kppscalibdur", "calibration interval ", NTP_STR), 4031 VDC_INIT("kppscalibs", "calibration cycles: ", NTP_STR), 4032 VDC_INIT("kppsjitexc", "jitter exceeded: ", NTP_STR), 4033 VDC_INIT("kppsstbexc", "stability exceeded: ", NTP_STR), 4034 VDC_INIT("kppscaliberrs", "calibration errors: ", NTP_STR), 4035 VDC_INIT(NULL, NULL, 0) 4036 }; 4037 4038 collect_display_vdc(0, kerninfo_vdc, TRUE, fp); 4039 } 4040 4041 4042 /* 4043 * monstats - implements ntpq -c monstats 4044 */ 4045 static void 4046 monstats( 4047 struct parse *pcmd, 4048 FILE *fp 4049 ) 4050 { 4051 static vdc monstats_vdc[] = { 4052 VDC_INIT("mru_enabled", "enabled: ", NTP_STR), 4053 VDC_INIT("mru_depth", "addresses: ", NTP_STR), 4054 VDC_INIT("mru_deepest", "peak addresses: ", NTP_STR), 4055 VDC_INIT("mru_maxdepth", "maximum addresses: ", NTP_STR), 4056 VDC_INIT("mru_mindepth", "reclaim above count:", NTP_STR), 4057 VDC_INIT("mru_maxage", "reclaim older than: ", NTP_STR), 4058 VDC_INIT("mru_mem", "kilobytes: ", NTP_STR), 4059 VDC_INIT("mru_maxmem", "maximum kilobytes: ", NTP_STR), 4060 VDC_INIT(NULL, NULL, 0) 4061 }; 4062 4063 collect_display_vdc(0, monstats_vdc, FALSE, fp); 4064 } 4065 4066 4067 /* 4068 * iostats - ntpq -c iostats - network input and output counters 4069 */ 4070 static void 4071 iostats( 4072 struct parse *pcmd, 4073 FILE *fp 4074 ) 4075 { 4076 static vdc iostats_vdc[] = { 4077 VDC_INIT("iostats_reset", "time since reset: ", NTP_STR), 4078 VDC_INIT("total_rbuf", "receive buffers: ", NTP_STR), 4079 VDC_INIT("free_rbuf", "free receive buffers: ", NTP_STR), 4080 VDC_INIT("used_rbuf", "used receive buffers: ", NTP_STR), 4081 VDC_INIT("rbuf_lowater", "low water refills: ", NTP_STR), 4082 VDC_INIT("io_dropped", "dropped packets: ", NTP_STR), 4083 VDC_INIT("io_ignored", "ignored packets: ", NTP_STR), 4084 VDC_INIT("io_received", "received packets: ", NTP_STR), 4085 VDC_INIT("io_sent", "packets sent: ", NTP_STR), 4086 VDC_INIT("io_sendfailed", "packet send failures: ", NTP_STR), 4087 VDC_INIT("io_wakeups", "input wakeups: ", NTP_STR), 4088 VDC_INIT("io_goodwakeups", "useful input wakeups: ", NTP_STR), 4089 VDC_INIT(NULL, NULL, 0) 4090 }; 4091 4092 collect_display_vdc(0, iostats_vdc, FALSE, fp); 4093 } 4094 4095 4096 /* 4097 * timerstats - ntpq -c timerstats - interval timer counters 4098 */ 4099 static void 4100 timerstats( 4101 struct parse *pcmd, 4102 FILE *fp 4103 ) 4104 { 4105 static vdc timerstats_vdc[] = { 4106 VDC_INIT("timerstats_reset", "time since reset: ", NTP_STR), 4107 VDC_INIT("timer_overruns", "timer overruns: ", NTP_STR), 4108 VDC_INIT("timer_xmts", "calls to transmit: ", NTP_STR), 4109 VDC_INIT(NULL, NULL, 0) 4110 }; 4111 4112 collect_display_vdc(0, timerstats_vdc, FALSE, fp); 4113 } 4114 4115 4116 /* 4117 * authinfo - implements ntpq -c authinfo 4118 */ 4119 static void 4120 authinfo( 4121 struct parse *pcmd, 4122 FILE *fp 4123 ) 4124 { 4125 static vdc authinfo_vdc[] = { 4126 VDC_INIT("authreset", "time since reset:", NTP_STR), 4127 VDC_INIT("authkeys", "stored keys: ", NTP_STR), 4128 VDC_INIT("authfreek", "free keys: ", NTP_STR), 4129 VDC_INIT("authklookups", "key lookups: ", NTP_STR), 4130 VDC_INIT("authknotfound", "keys not found: ", NTP_STR), 4131 VDC_INIT("authkuncached", "uncached keys: ", NTP_STR), 4132 VDC_INIT("authkexpired", "expired keys: ", NTP_STR), 4133 VDC_INIT("authencrypts", "encryptions: ", NTP_STR), 4134 VDC_INIT("authdecrypts", "decryptions: ", NTP_STR), 4135 VDC_INIT(NULL, NULL, 0) 4136 }; 4137 4138 collect_display_vdc(0, authinfo_vdc, FALSE, fp); 4139 } 4140 4141 4142 /* 4143 * pstats - show statistics for a peer 4144 */ 4145 static void 4146 pstats( 4147 struct parse *pcmd, 4148 FILE *fp 4149 ) 4150 { 4151 static vdc pstats_vdc[] = { 4152 VDC_INIT("src", "remote host: ", NTP_ADD), 4153 VDC_INIT("dst", "local address: ", NTP_ADD), 4154 VDC_INIT("timerec", "time last received: ", NTP_STR), 4155 VDC_INIT("timer", "time until next send:", NTP_STR), 4156 VDC_INIT("timereach", "reachability change: ", NTP_STR), 4157 VDC_INIT("sent", "packets sent: ", NTP_STR), 4158 VDC_INIT("received", "packets received: ", NTP_STR), 4159 VDC_INIT("badauth", "bad authentication: ", NTP_STR), 4160 VDC_INIT("bogusorg", "bogus origin: ", NTP_STR), 4161 VDC_INIT("oldpkt", "duplicate: ", NTP_STR), 4162 VDC_INIT("seldisp", "bad dispersion: ", NTP_STR), 4163 VDC_INIT("selbroken", "bad reference time: ", NTP_STR), 4164 VDC_INIT("candidate", "candidate order: ", NTP_STR), 4165 VDC_INIT(NULL, NULL, 0) 4166 }; 4167 associd_t associd; 4168 4169 associd = checkassocid(pcmd->argval[0].uval); 4170 if (0 == associd) 4171 return; 4172 4173 collect_display_vdc(associd, pstats_vdc, TRUE, fp); 4174 } 4175