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