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