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