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