diff options
author | Xin LI <delphij@FreeBSD.org> | 2015-07-15 19:21:26 +0000 |
---|---|---|
committer | Xin LI <delphij@FreeBSD.org> | 2015-07-15 19:21:26 +0000 |
commit | 07267002fe1d040435330820b8a86cad74c6f5c3 (patch) | |
tree | b0302ac4be59e104f4e1e54014561a1389397192 /contrib/ntp/ntpq/ntpq-subs.c | |
parent | e57f86038ca56b3be7d0d81eab57e135da3fbe72 (diff) | |
download | src-07267002fe1d040435330820b8a86cad74c6f5c3.tar.gz src-07267002fe1d040435330820b8a86cad74c6f5c3.zip |
MFC r280849,280915-280916,281015-281016,282097,282408,282415,283542,
284864,285169-285170,285435:
ntp 4.2.8p3.
Relnotes: yes
Approved by: re (?)
Notes
Notes:
svn path=/stable/10/; revision=285612
Diffstat (limited to 'contrib/ntp/ntpq/ntpq-subs.c')
-rw-r--r-- | contrib/ntp/ntpq/ntpq-subs.c | 3438 |
1 files changed, 2842 insertions, 596 deletions
diff --git a/contrib/ntp/ntpq/ntpq-subs.c b/contrib/ntp/ntpq/ntpq-subs.c index 07f25cecefda..c6478701eb27 100644 --- a/contrib/ntp/ntpq/ntpq-subs.c +++ b/contrib/ntp/ntpq/ntpq-subs.c @@ -1,74 +1,91 @@ /* - * ntpq_ops.c - subroutines which are called to perform operations by ntpq + * ntpq-subs.c - subroutines which are called to perform ntpq commands. */ - +#include <config.h> #include <stdio.h> #include <ctype.h> #include <sys/types.h> #include <sys/time.h> #include "ntpq.h" -#include "ntp_stdlib.h" +#include "ntpq-opts.h" -extern char * chosts[]; -extern char currenthost[]; -extern int numhosts; -int maxhostlen; +extern char currenthost[]; +extern int currenthostisnum; +size_t maxhostlen; /* * Declarations for command handlers in here */ -static int checkassocid P((u_int32)); -static char * strsave P((char *)); -static struct varlist *findlistvar P((struct varlist *, char *)); -static void doaddvlist P((struct varlist *, char *)); -static void dormvlist P((struct varlist *, char *)); -static void doclearvlist P((struct varlist *)); -static void makequerydata P((struct varlist *, int *, char *)); -static int doquerylist P((struct varlist *, int, int, int, u_short *, int *, char **)); -static void doprintvlist P((struct varlist *, FILE *)); -static void addvars P((struct parse *, FILE *)); -static void rmvars P((struct parse *, FILE *)); -static void clearvars P((struct parse *, FILE *)); -static void showvars P((struct parse *, FILE *)); -static int dolist P((struct varlist *, int, int, int, FILE *)); -static void readlist P((struct parse *, FILE *)); -static void writelist P((struct parse *, FILE *)); -static void readvar P((struct parse *, FILE *)); -static void writevar P((struct parse *, FILE *)); -static void clocklist P((struct parse *, FILE *)); -static void clockvar P((struct parse *, FILE *)); -static int findassidrange P((u_int32, u_int32, int *, int *)); -static void mreadlist P((struct parse *, FILE *)); -static void mreadvar P((struct parse *, FILE *)); -static int dogetassoc P((FILE *)); -static void printassoc P((int, FILE *)); -static void associations P((struct parse *, FILE *)); -static void lassociations P((struct parse *, FILE *)); -static void passociations P((struct parse *, FILE *)); -static void lpassociations P((struct parse *, FILE *)); +static associd_t checkassocid (u_int32); +static struct varlist *findlistvar (struct varlist *, char *); +static void doaddvlist (struct varlist *, const char *); +static void dormvlist (struct varlist *, const char *); +static void doclearvlist (struct varlist *); +static void makequerydata (struct varlist *, int *, char *); +static int doquerylist (struct varlist *, int, associd_t, int, + u_short *, int *, const char **); +static void doprintvlist (struct varlist *, FILE *); +static void addvars (struct parse *, FILE *); +static void rmvars (struct parse *, FILE *); +static void clearvars (struct parse *, FILE *); +static void showvars (struct parse *, FILE *); +static int dolist (struct varlist *, associd_t, int, int, + FILE *); +static void readlist (struct parse *, FILE *); +static void writelist (struct parse *, FILE *); +static void readvar (struct parse *, FILE *); +static void writevar (struct parse *, FILE *); +static void clocklist (struct parse *, FILE *); +static void clockvar (struct parse *, FILE *); +static int findassidrange (u_int32, u_int32, int *, int *, + FILE *); +static void mreadlist (struct parse *, FILE *); +static void mreadvar (struct parse *, FILE *); +static void printassoc (int, FILE *); +static void associations (struct parse *, FILE *); +static void lassociations (struct parse *, FILE *); +static void passociations (struct parse *, FILE *); +static void lpassociations (struct parse *, FILE *); #ifdef UNUSED -static void radiostatus P((struct parse *, FILE *)); +static void radiostatus (struct parse *, FILE *); #endif /* UNUSED */ -static void pstatus P((struct parse *, FILE *)); -static long when P((l_fp *, l_fp *, l_fp *)); -static char * prettyinterval P((char *, long)); -static int doprintpeers P((struct varlist *, int, int, int, char *, FILE *, int)); -static int dogetpeers P((struct varlist *, int, FILE *, int)); -static void dopeers P((int, FILE *, int)); -static void peers P((struct parse *, FILE *)); -static void lpeers P((struct parse *, FILE *)); -static void doopeers P((int, FILE *, int)); -static void opeers P((struct parse *, FILE *)); -static void lopeers P((struct parse *, FILE *)); - +static void authinfo (struct parse *, FILE *); +static void pstats (struct parse *, FILE *); +static long when (l_fp *, l_fp *, l_fp *); +static char * prettyinterval (char *, size_t, long); +static int doprintpeers (struct varlist *, int, int, int, const char *, FILE *, int); +static int dogetpeers (struct varlist *, associd_t, FILE *, int); +static void dopeers (int, FILE *, int); +static void peers (struct parse *, FILE *); +static void doapeers (int, FILE *, int); +static void apeers (struct parse *, FILE *); +static void lpeers (struct parse *, FILE *); +static void doopeers (int, FILE *, int); +static void opeers (struct parse *, FILE *); +static void lopeers (struct parse *, FILE *); +static void config (struct parse *, FILE *); +static void saveconfig (struct parse *, FILE *); +static void config_from_file(struct parse *, FILE *); +static void mrulist (struct parse *, FILE *); +static void ifstats (struct parse *, FILE *); +static void reslist (struct parse *, FILE *); +static void sysstats (struct parse *, FILE *); +static void sysinfo (struct parse *, FILE *); +static void kerninfo (struct parse *, FILE *); +static void monstats (struct parse *, FILE *); +static void iostats (struct parse *, FILE *); +static void timerstats (struct parse *, FILE *); /* * Commands we understand. Ntpdc imports this. */ struct xcmd opcmds[] = { + { "saveconfig", saveconfig, { NTP_STR, NO, NO, NO }, + { "filename", "", "", ""}, + "save ntpd configuration to file, . for current config file"}, { "associations", associations, { NO, NO, NO, NO }, { "", "", "", "" }, "print list of association ID's and statuses for the server's peers" }, @@ -102,26 +119,26 @@ struct xcmd opcmds[] = { { "writelist", writelist, { OPT|NTP_UINT, NO, NO, NO }, { "assocID", "", "", "" }, "write the system or peer variables included in the variable list" }, - { "readvar", readvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO }, - { "assocID", "name=value[,...]", "", "" }, + { "readvar", readvar, { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, }, + { "assocID", "varname1", "varname2", "varname3" }, "read system or peer variables" }, - { "rv", readvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO }, - { "assocID", "name=value[,...]", "", "" }, + { "rv", readvar, { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, }, + { "assocID", "varname1", "varname2", "varname3" }, "read system or peer variables" }, { "writevar", writevar, { NTP_UINT, NTP_STR, NO, NO }, { "assocID", "name=value,[...]", "", "" }, "write system or peer variables" }, { "mreadlist", mreadlist, { NTP_UINT, NTP_UINT, NO, NO }, - { "assocID", "assocID", "", "" }, + { "assocIDlow", "assocIDhigh", "", "" }, "read the peer variables in the variable list for multiple peers" }, { "mrl", mreadlist, { NTP_UINT, NTP_UINT, NO, NO }, - { "assocID", "assocID", "", "" }, + { "assocIDlow", "assocIDhigh", "", "" }, "read the peer variables in the variable list for multiple peers" }, { "mreadvar", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO }, - { "assocID", "assocID", "name=value[,...]", "" }, + { "assocIDlow", "assocIDhigh", "name=value[,...]", "" }, "read peer variables from multiple peers" }, { "mrv", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO }, - { "assocID", "assocID", "name=value[,...]", "" }, + { "assocIDlow", "assocIDhigh", "name=value[,...]", "" }, "read peer variables from multiple peers" }, { "clocklist", clocklist, { OPT|NTP_UINT, NO, NO, NO }, { "assocID", "", "", "" }, @@ -135,12 +152,15 @@ struct xcmd opcmds[] = { { "cv", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO }, { "assocID", "name=value[,...]", "", "" }, "read clock variables" }, - { "pstatus", pstatus, { NTP_UINT, NO, NO, NO }, + { "pstats", pstats, { NTP_UINT, NO, NO, NO }, { "assocID", "", "", "" }, - "print status information returned for a peer" }, + "show statistics for a peer" }, { "peers", peers, { OPT|IP_VERSION, NO, NO, NO }, { "-4|-6", "", "", "" }, "obtain and print a list of the server's peers [IP version]" }, + { "apeers", apeers, { OPT|IP_VERSION, NO, NO, NO }, + { "-4|-6", "", "", "" }, + "obtain and print a list of the server's peers and their assocIDs [IP version]" }, { "lpeers", lpeers, { OPT|IP_VERSION, NO, NO, NO }, { "-4|-6", "", "", "" }, "obtain and print a list of all peers and clients [IP version]" }, @@ -150,6 +170,42 @@ struct xcmd opcmds[] = { { "lopeers", lopeers, { OPT|IP_VERSION, NO, NO, NO }, { "-4|-6", "", "", "" }, "obtain and print a list of all peers and clients showing dstadr [IP version]" }, + { ":config", config, { NTP_STR, NO, NO, NO }, + { "<configuration command line>", "", "", "" }, + "send a remote configuration command to ntpd" }, + { "config-from-file", config_from_file, { NTP_STR, NO, NO, NO }, + { "<configuration filename>", "", "", "" }, + "configure ntpd using the configuration filename" }, + { "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR }, + { "tag=value", "tag=value", "tag=value", "tag=value" }, + "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." }, + { "ifstats", ifstats, { NO, NO, NO, NO }, + { "", "", "", "" }, + "show statistics for each local address ntpd is using" }, + { "reslist", reslist, { NO, NO, NO, NO }, + { "", "", "", "" }, + "show ntpd access control list" }, + { "sysinfo", sysinfo, { NO, NO, NO, NO }, + { "", "", "", "" }, + "display system summary" }, + { "kerninfo", kerninfo, { NO, NO, NO, NO }, + { "", "", "", "" }, + "display kernel loop and PPS statistics" }, + { "sysstats", sysstats, { NO, NO, NO, NO }, + { "", "", "", "" }, + "display system uptime and packet counts" }, + { "monstats", monstats, { NO, NO, NO, NO }, + { "", "", "", "" }, + "display monitor (mrulist) counters and limits" }, + { "authinfo", authinfo, { NO, NO, NO, NO }, + { "", "", "", "" }, + "display symmetric authentication counters" }, + { "iostats", iostats, { NO, NO, NO, NO }, + { "", "", "", "" }, + "display network input and output counters" }, + { "timerstats", timerstats, { NO, NO, NO, NO }, + { "", "", "", "" }, + "display interval timer counters" }, { 0, 0, { NO, NO, NO, NO }, { "-4|-6", "", "", "" }, "" } }; @@ -158,89 +214,203 @@ struct xcmd opcmds[] = { /* * Variable list data space */ -#define MAXLIST 64 /* maximum number of variables in list */ -#define LENHOSTNAME 256 /* host name is 256 characters long */ +#define MAXLINE 512 /* maximum length of a line */ +#define MAXLIST 128 /* maximum variables in list */ +#define LENHOSTNAME 256 /* host name limit */ + +#define MRU_GOT_COUNT 0x1 +#define MRU_GOT_LAST 0x2 +#define MRU_GOT_FIRST 0x4 +#define MRU_GOT_MV 0x8 +#define MRU_GOT_RS 0x10 +#define MRU_GOT_ADDR 0x20 +#define MRU_GOT_ALL (MRU_GOT_COUNT | MRU_GOT_LAST | MRU_GOT_FIRST \ + | MRU_GOT_MV | MRU_GOT_RS | MRU_GOT_ADDR) + +/* + * mrulist() depends on MRUSORT_DEF and MRUSORT_RDEF being the first two + */ +typedef enum mru_sort_order_tag { + MRUSORT_DEF = 0, /* lstint ascending */ + MRUSORT_R_DEF, /* lstint descending */ + MRUSORT_AVGINT, /* avgint ascending */ + MRUSORT_R_AVGINT, /* avgint descending */ + MRUSORT_ADDR, /* IPv4 asc. then IPv6 asc. */ + MRUSORT_R_ADDR, /* IPv6 desc. then IPv4 desc. */ + MRUSORT_COUNT, /* hit count ascending */ + MRUSORT_R_COUNT, /* hit count descending */ + MRUSORT_MAX, /* special: count of this enum */ +} mru_sort_order; + +const char * const mru_sort_keywords[MRUSORT_MAX] = { + "lstint", /* MRUSORT_DEF */ + "-lstint", /* MRUSORT_R_DEF */ + "avgint", /* MRUSORT_AVGINT */ + "-avgint", /* MRUSORT_R_AVGINT */ + "addr", /* MRUSORT_ADDR */ + "-addr", /* MRUSORT_R_ADDR */ + "count", /* MRUSORT_COUNT */ + "-count", /* MRUSORT_R_COUNT */ +}; + +typedef int (*qsort_cmp)(const void *, const void *); + /* * Old CTL_PST defines for version 2. */ -#define OLD_CTL_PST_CONFIG 0x80 +#define OLD_CTL_PST_CONFIG 0x80 #define OLD_CTL_PST_AUTHENABLE 0x40 #define OLD_CTL_PST_AUTHENTIC 0x20 -#define OLD_CTL_PST_REACH 0x10 -#define OLD_CTL_PST_SANE 0x08 -#define OLD_CTL_PST_DISP 0x04 +#define OLD_CTL_PST_REACH 0x10 +#define OLD_CTL_PST_SANE 0x08 +#define OLD_CTL_PST_DISP 0x04 + #define OLD_CTL_PST_SEL_REJECT 0 #define OLD_CTL_PST_SEL_SELCAND 1 #define OLD_CTL_PST_SEL_SYNCCAND 2 #define OLD_CTL_PST_SEL_SYSPEER 3 - char flash2[] = " .+* "; /* flash decode for version 2 */ char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */ struct varlist { - char *name; + const char *name; char *value; -} varlist[MAXLIST] = { { 0, 0 } }; +} g_varlist[MAXLIST] = { { 0, 0 } }; /* * Imported from ntpq.c */ extern int showhostnames; +extern int wideremote; extern int rawmode; extern struct servent *server_entry; -extern struct association assoc_cache[]; -extern int numassoc; +extern struct association *assoc_cache; extern u_char pktversion; -extern struct ctl_var peer_var[]; + +typedef struct mru_tag mru; +struct mru_tag { + mru * hlink; /* next in hash table bucket */ + DECL_DLIST_LINK(mru, mlink); + int count; + l_fp last; + l_fp first; + u_char mode; + u_char ver; + u_short rs; + sockaddr_u addr; +}; + +typedef struct ifstats_row_tag { + u_int ifnum; + sockaddr_u addr; + sockaddr_u bcast; + int enabled; + u_int flags; + int mcast_count; + char name[32]; + int peer_count; + int received; + int sent; + int send_errors; + u_int ttl; + u_int uptime; +} ifstats_row; + +typedef struct reslist_row_tag { + u_int idx; + sockaddr_u addr; + sockaddr_u mask; + u_long hits; + char flagstr[128]; +} reslist_row; + +typedef struct var_display_collection_tag { + const char * const tag; /* system variable */ + const char * const display; /* descriptive text */ + u_char type; /* NTP_STR, etc */ + union { + char * str; + sockaddr_u sau; /* NTP_ADD */ + l_fp lfp; /* NTP_LFP */ + } v; /* retrieved value */ +} vdc; +#if !defined(MISSING_C99_STRUCT_INIT) +# define VDC_INIT(a, b, c) { .tag = a, .display = b, .type = c } +#else +# define VDC_INIT(a, b, c) { a, b, c } +#endif +/* + * other local function prototypes + */ +void mrulist_ctrl_c_hook(void); +static mru * add_mru(mru *); +static int collect_mru_list(const char *, l_fp *); +static int fetch_nonce(char *, size_t); +static int qcmp_mru_avgint(const void *, const void *); +static int qcmp_mru_r_avgint(const void *, const void *); +static int qcmp_mru_addr(const void *, const void *); +static int qcmp_mru_r_addr(const void *, const void *); +static int qcmp_mru_count(const void *, const void *); +static int qcmp_mru_r_count(const void *, const void *); +static void validate_ifnum(FILE *, u_int, int *, ifstats_row *); +static void another_ifstats_field(int *, ifstats_row *, FILE *); +static void collect_display_vdc(associd_t as, vdc *table, + int decodestatus, FILE *fp); /* - * For quick string comparisons + * static globals */ -#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) +static u_int mru_count; +static u_int mru_dupes; +volatile int mrulist_interrupted; +static mru mru_list; /* listhead */ +static mru ** hash_table; +/* + * qsort comparison function table for mrulist(). The first two + * entries are NULL because they are handled without qsort(). + */ +static const qsort_cmp mru_qcmp_table[MRUSORT_MAX] = { + NULL, /* MRUSORT_DEF unused */ + NULL, /* MRUSORT_R_DEF unused */ + &qcmp_mru_avgint, /* MRUSORT_AVGINT */ + &qcmp_mru_r_avgint, /* MRUSORT_R_AVGINT */ + &qcmp_mru_addr, /* MRUSORT_ADDR */ + &qcmp_mru_r_addr, /* MRUSORT_R_ADDR */ + &qcmp_mru_count, /* MRUSORT_COUNT */ + &qcmp_mru_r_count, /* MRUSORT_R_COUNT */ +}; /* * checkassocid - return the association ID, checking to see if it is valid */ -static int +static associd_t checkassocid( u_int32 value ) { - if (value == 0 || value >= 65536) { - (void) fprintf(stderr, "***Invalid association ID specified\n"); + associd_t associd; + u_long ulvalue; + + associd = (associd_t)value; + if (0 == associd || value != associd) { + ulvalue = value; + fprintf(stderr, + "***Invalid association ID %lu specified\n", + ulvalue); return 0; } - return (int)value; -} - - -/* - * strsave - save a string - * XXX - should be in libntp.a - */ -static char * -strsave( - char *str - ) -{ - char *cp; - u_int len; - - len = strlen(str) + 1; - if ((cp = (char *)malloc(len)) == NULL) { - (void) fprintf(stderr, "Malloc failed!!\n"); - exit(1); - } - memmove(cp, str, len); - return (cp); + return associd; } /* - * findlistvar - look for the named variable in a list and return if found + * findlistvar - Look for the named variable in a varlist. If found, + * return a pointer to it. Otherwise, if the list has + * slots available, return the pointer to the first free + * slot, or NULL if it's full. */ static struct varlist * findlistvar( @@ -248,14 +418,15 @@ findlistvar( char *name ) { - register struct varlist *vl; + struct varlist *vl; - for (vl = list; vl < list + MAXLIST && vl->name != 0; vl++) - if (STREQ(name, vl->name)) - return vl; + for (vl = list; vl < list + MAXLIST && vl->name != NULL; vl++) + if (!strcmp(name, vl->name)) + return vl; if (vl < list + MAXLIST) return vl; - return (struct varlist *)0; + + return NULL; } @@ -265,10 +436,10 @@ findlistvar( static void doaddvlist( struct varlist *vlist, - char *vars + const char *vars ) { - register struct varlist *vl; + struct varlist *vl; int len; char *name; char *value; @@ -276,20 +447,20 @@ doaddvlist( len = strlen(vars); while (nextvar(&len, &vars, &name, &value)) { vl = findlistvar(vlist, name); - if (vl == 0) { - (void) fprintf(stderr, "Variable list full\n"); + if (NULL == vl) { + fprintf(stderr, "Variable list full\n"); return; } - if (vl->name == 0) { - vl->name = strsave(name); - } else if (vl->value != 0) { + if (NULL == vl->name) { + vl->name = estrdup(name); + } else if (vl->value != NULL) { free(vl->value); - vl->value = 0; + vl->value = NULL; } - if (value != 0) - vl->value = strsave(value); + if (value != NULL) + vl->value = estrdup(value); } } @@ -300,10 +471,10 @@ doaddvlist( static void dormvlist( struct varlist *vlist, - char *vars + const char *vars ) { - register struct varlist *vl; + struct varlist *vl; int len; char *name; char *value; @@ -315,10 +486,10 @@ dormvlist( (void) fprintf(stderr, "Variable `%s' not found\n", name); } else { - free((void *)vl->name); + free((void *)(intptr_t)vl->name); if (vl->value != 0) free(vl->value); - for ( ; (vl+1) < (varlist+MAXLIST) + for ( ; (vl+1) < (g_varlist + MAXLIST) && (vl+1)->name != 0; vl++) { vl->name = (vl+1)->name; vl->value = (vl+1)->value; @@ -340,7 +511,7 @@ doclearvlist( register struct varlist *vl; for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) { - free((void *)vl->name); + free((void *)(intptr_t)vl->name); vl->name = 0; if (vl->value != 0) { free(vl->value); @@ -375,16 +546,20 @@ makequerydata( else valuelen = strlen(vl->value); totallen = namelen + valuelen + (valuelen != 0) + (cp != data); - if (cp + totallen > cpend) - break; + if (cp + totallen > cpend) { + fprintf(stderr, + "***Ignoring variables starting with `%s'\n", + vl->name); + break; + } if (cp != data) *cp++ = ','; - memmove(cp, vl->name, (unsigned)namelen); + memcpy(cp, vl->name, (size_t)namelen); cp += namelen; if (valuelen != 0) { *cp++ = '='; - memmove(cp, vl->value, (unsigned)valuelen); + memcpy(cp, vl->value, (size_t)valuelen); cp += valuelen; } } @@ -399,11 +574,11 @@ static int doquerylist( struct varlist *vlist, int op, - int associd, + associd_t associd, int auth, u_short *rstatus, int *dsize, - char **datap + const char **datap ) { char data[CTL_MAX_DATA_LEN]; @@ -412,8 +587,8 @@ doquerylist( datalen = sizeof(data); makequerydata(vlist, &datalen, data); - return doquery(op, associd, auth, datalen, data, rstatus, - dsize, datap); + return doquery(op, associd, auth, datalen, data, rstatus, dsize, + datap); } @@ -426,23 +601,21 @@ doprintvlist( FILE *fp ) { - register struct varlist *vl; + size_t n; - if (vlist->name == 0) { - (void) fprintf(fp, "No variables on list\n"); - } else { - for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) { - if (vl->value == 0) { - (void) fprintf(fp, "%s\n", vl->name); - } else { - (void) fprintf(fp, "%s=%s\n", - vl->name, vl->value); - } - } + if (NULL == vlist->name) { + fprintf(fp, "No variables on list\n"); + return; + } + for (n = 0; n < MAXLIST && vlist[n].name != NULL; n++) { + if (NULL == vlist[n].value) + fprintf(fp, "%s\n", vlist[n].name); + else + fprintf(fp, "%s=%s\n", vlist[n].name, + vlist[n].value); } } - /* * addvars - add variables to the variable list */ @@ -453,7 +626,7 @@ addvars( FILE *fp ) { - doaddvlist(varlist, pcmd->argval[0].string); + doaddvlist(g_varlist, pcmd->argval[0].string); } @@ -467,7 +640,7 @@ rmvars( FILE *fp ) { - dormvlist(varlist, pcmd->argval[0].string); + dormvlist(g_varlist, pcmd->argval[0].string); } @@ -481,7 +654,7 @@ clearvars( FILE *fp ) { - doclearvlist(varlist); + doclearvlist(g_varlist); } @@ -495,7 +668,7 @@ showvars( FILE *fp ) { - doprintvlist(varlist, fp); + doprintvlist(g_varlist, fp); } @@ -505,16 +678,26 @@ showvars( static int dolist( struct varlist *vlist, - int associd, + associd_t associd, int op, int type, FILE *fp ) { - char *datap; + const char *datap; int res; int dsize; u_short rstatus; + int quiet; + + /* + * if we're asking for specific variables don't include the + * status header line in the output. + */ + if (old_rv) + quiet = 0; + else + quiet = (vlist->name != NULL); res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap); @@ -522,20 +705,22 @@ dolist( return 0; if (numhosts > 1) - (void) fprintf(fp, "server=%s ", currenthost); + fprintf(fp, "server=%s ", currenthost); if (dsize == 0) { if (associd == 0) - (void) fprintf(fp, "No system%s variables returned\n", - (type == TYPE_CLOCK) ? " clock" : ""); + fprintf(fp, "No system%s variables returned\n", + (type == TYPE_CLOCK) ? " clock" : ""); else - (void) fprintf(fp, - "No information returned for%s association %u\n", - (type == TYPE_CLOCK) ? " clock" : "", associd); + fprintf(fp, + "No information returned for%s association %u\n", + (type == TYPE_CLOCK) ? " clock" : "", + associd); return 1; } - (void) fprintf(fp,"assID=%d ",associd); - printvars(dsize, datap, (int)rstatus, type, fp); + if (!quiet) + fprintf(fp, "associd=%u ", associd); + printvars(dsize, datap, (int)rstatus, type, quiet, fp); return 1; } @@ -549,7 +734,8 @@ readlist( FILE *fp ) { - int associd; + associd_t associd; + int type; if (pcmd->nargs == 0) { associd = 0; @@ -561,8 +747,10 @@ readlist( return; } - (void) dolist(varlist, associd, CTL_OP_READVAR, - (associd == 0) ? TYPE_SYS : TYPE_PEER, fp); + type = (0 == associd) + ? TYPE_SYS + : TYPE_PEER; + dolist(g_varlist, associd, CTL_OP_READVAR, type, fp); } @@ -575,9 +763,9 @@ writelist( FILE *fp ) { - char *datap; + const char *datap; int res; - int associd; + associd_t associd; int dsize; u_short rstatus; @@ -591,7 +779,7 @@ writelist( return; } - res = doquerylist(varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus, + res = doquerylist(g_varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus, &dsize, &datap); if (res != 0) @@ -602,9 +790,9 @@ writelist( if (dsize == 0) (void) fprintf(fp, "done! (no data returned)\n"); else { - (void) fprintf(fp,"assID=%d ",associd); + (void) fprintf(fp,"associd=%u ", associd); printvars(dsize, datap, (int)rstatus, - (associd != 0) ? TYPE_PEER : TYPE_SYS, fp); + (associd != 0) ? TYPE_PEER : TYPE_SYS, 0, fp); } return; } @@ -619,8 +807,12 @@ readvar( FILE *fp ) { - int associd; - struct varlist tmplist[MAXLIST]; + associd_t associd; + u_int tmpcount; + u_int u; + int type; + struct varlist tmplist[MAXLIST]; + /* HMS: uval? */ if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0) @@ -628,12 +820,17 @@ readvar( else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) return; - memset((char *)tmplist, 0, sizeof(tmplist)); - if (pcmd->nargs >= 2) - doaddvlist(tmplist, pcmd->argval[1].string); + ZERO(tmplist); + if (pcmd->nargs > 1) { + tmpcount = pcmd->nargs - 1; + for (u = 0; u < tmpcount; u++) + doaddvlist(tmplist, pcmd->argval[1 + u].string); + } - (void) dolist(tmplist, associd, CTL_OP_READVAR, - (associd == 0) ? TYPE_SYS : TYPE_PEER, fp); + type = (0 == associd) + ? TYPE_SYS + : TYPE_PEER; + dolist(tmplist, associd, CTL_OP_READVAR, type, fp); doclearvlist(tmplist); } @@ -648,9 +845,10 @@ writevar( FILE *fp ) { - char *datap; + const char *datap; int res; - int associd; + associd_t associd; + int type; int dsize; u_short rstatus; struct varlist tmplist[MAXLIST]; @@ -661,7 +859,7 @@ writevar( else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) return; - memset((char *)tmplist, 0, sizeof(tmplist)); + ZERO(tmplist); doaddvlist(tmplist, pcmd->argval[1].string); res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus, @@ -673,13 +871,15 @@ writevar( return; if (numhosts > 1) - (void) fprintf(fp, "server=%s ", currenthost); + fprintf(fp, "server=%s ", currenthost); if (dsize == 0) - (void) fprintf(fp, "done! (no data returned)\n"); + fprintf(fp, "done! (no data returned)\n"); else { - (void) fprintf(fp,"assID=%d ",associd); - printvars(dsize, datap, (int)rstatus, - (associd != 0) ? TYPE_PEER : TYPE_SYS, fp); + fprintf(fp,"associd=%u ", associd); + type = (0 == associd) + ? TYPE_SYS + : TYPE_PEER; + printvars(dsize, datap, (int)rstatus, type, 0, fp); } return; } @@ -694,7 +894,7 @@ clocklist( FILE *fp ) { - int associd; + associd_t associd; /* HMS: uval? */ if (pcmd->nargs == 0) { @@ -706,7 +906,7 @@ clocklist( return; } - (void) dolist(varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp); + dolist(g_varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp); } @@ -719,7 +919,7 @@ clockvar( FILE *fp ) { - int associd; + associd_t associd; struct varlist tmplist[MAXLIST]; /* HMS: uval? */ @@ -728,11 +928,11 @@ clockvar( else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) return; - memset((char *)tmplist, 0, sizeof(tmplist)); + ZERO(tmplist); if (pcmd->nargs >= 2) doaddvlist(tmplist, pcmd->argval[1].string); - (void) dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp); + dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp); doclearvlist(tmplist); } @@ -743,54 +943,49 @@ clockvar( */ static int findassidrange( - u_int32 assid1, - u_int32 assid2, - int *from, - int *to + u_int32 assid1, + u_int32 assid2, + int * from, + int * to, + FILE * fp ) { - register int i; - int f, t; - - if (assid1 == 0 || assid1 > 65535) { - (void) fprintf(stderr, - "***Invalid association ID %lu specified\n", (u_long)assid1); - return 0; - } + associd_t assids[2]; + int ind[COUNTOF(assids)]; + u_int i; + size_t a; - if (assid2 == 0 || assid2 > 65535) { - (void) fprintf(stderr, - "***Invalid association ID %lu specified\n", (u_long)assid2); - return 0; - } - f = t = -1; - for (i = 0; i < numassoc; i++) { - if (assoc_cache[i].assid == assid1) { - f = i; - if (t != -1) - break; - } - if (assoc_cache[i].assid == assid2) { - t = i; - if (f != -1) - break; - } - } + if (0 == numassoc) + dogetassoc(fp); - if (f == -1 || t == -1) { - (void) fprintf(stderr, - "***Association ID %lu not found in list\n", - (f == -1) ? (u_long)assid1 : (u_long)assid2); + assids[0] = checkassocid(assid1); + if (0 == assids[0]) return 0; + assids[1] = checkassocid(assid2); + if (0 == assids[1]) + return 0; + + for (a = 0; a < COUNTOF(assids); a++) { + ind[a] = -1; + for (i = 0; i < numassoc; i++) + if (assoc_cache[i].assid == assids[a]) + ind[a] = i; } + for (a = 0; a < COUNTOF(assids); a++) + if (-1 == ind[a]) { + fprintf(stderr, + "***Association ID %u not found in list\n", + assids[a]); + return 0; + } - if (f < t) { - *from = f; - *to = t; + if (ind[0] < ind[1]) { + *from = ind[0]; + *to = ind[1]; } else { - *from = t; - *to = f; + *to = ind[0]; + *from = ind[1]; } return 1; } @@ -810,16 +1005,15 @@ mreadlist( int from; int to; - /* HMS: uval? */ if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval, - &from, &to)) + &from, &to, fp)) return; for (i = from; i <= to; i++) { if (i != from) - (void) fprintf(fp, "\n"); - if (!dolist(varlist, (int)assoc_cache[i].assid, - CTL_OP_READVAR, TYPE_PEER, fp)) + fprintf(fp, "\n"); + if (!dolist(g_varlist, assoc_cache[i].assid, + CTL_OP_READVAR, TYPE_PEER, fp)) return; } return; @@ -839,24 +1033,29 @@ mreadvar( int from; int to; struct varlist tmplist[MAXLIST]; + struct varlist *pvars; - /* HMS: uval? */ if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval, - &from, &to)) + &from, &to, fp)) return; - memset((char *)tmplist, 0, sizeof(tmplist)); - if (pcmd->nargs >= 3) + ZERO(tmplist); + if (pcmd->nargs >= 3) { doaddvlist(tmplist, pcmd->argval[2].string); + pvars = tmplist; + } else { + pvars = g_varlist; + } for (i = from; i <= to; i++) { - if (i != from) - (void) fprintf(fp, "\n"); - if (!dolist(varlist, (int)assoc_cache[i].assid, - CTL_OP_READVAR, TYPE_PEER, fp)) + if (!dolist(pvars, assoc_cache[i].assid, CTL_OP_READVAR, + TYPE_PEER, fp)) break; } - doclearvlist(tmplist); + + if (pvars == tmplist) + doclearvlist(tmplist); + return; } @@ -864,12 +1063,13 @@ mreadvar( /* * dogetassoc - query the host for its list of associations */ -static int +int dogetassoc( FILE *fp ) { - char *datap; + const char *datap; + const u_short *pus; int res; int dsize; u_short rstatus; @@ -882,29 +1082,41 @@ dogetassoc( if (dsize == 0) { if (numhosts > 1) - (void) fprintf(fp, "server=%s ", currenthost); - (void) fprintf(fp, "No association ID's returned\n"); + fprintf(fp, "server=%s ", currenthost); + fprintf(fp, "No association ID's returned\n"); return 0; } if (dsize & 0x3) { if (numhosts > 1) - (void) fprintf(stderr, "server=%s ", currenthost); - (void) fprintf(stderr, - "***Server returned %d octets, should be multiple of 4\n", - dsize); + fprintf(stderr, "server=%s ", currenthost); + fprintf(stderr, + "***Server returned %d octets, should be multiple of 4\n", + dsize); return 0; } numassoc = 0; + while (dsize > 0) { - assoc_cache[numassoc].assid = ntohs(*((u_short *)datap)); - datap += sizeof(u_short); - assoc_cache[numassoc].status = ntohs(*((u_short *)datap)); - datap += sizeof(u_short); - if (++numassoc >= MAXASSOC) - break; - dsize -= sizeof(u_short) + sizeof(u_short); + if (numassoc >= assoc_cache_slots) { + grow_assoc_cache(); + } + pus = (const void *)datap; + assoc_cache[numassoc].assid = ntohs(*pus); + datap += sizeof(*pus); + pus = (const void *)datap; + assoc_cache[numassoc].status = ntohs(*pus); + datap += sizeof(*pus); + dsize -= 2 * sizeof(*pus); + if (debug) { + fprintf(stderr, "[%u] ", + assoc_cache[numassoc].assid); + } + numassoc++; + } + if (debug) { + fprintf(stderr, "\n%d associations total\n", numassoc); } sortassoc(); return 1; @@ -921,7 +1133,7 @@ printassoc( ) { register char *bp; - int i; + u_int i; u_char statval; int event; u_long event_count; @@ -930,7 +1142,6 @@ printassoc( const char *auth; const char *condition = ""; const char *last_event; - const char *cnt; char buf[128]; if (numassoc == 0) { @@ -942,7 +1153,7 @@ printassoc( * Output a header */ (void) fprintf(fp, - "\nind assID status conf reach auth condition last_event cnt\n"); + "\nind assid status conf reach auth condition last_event cnt\n"); (void) fprintf(fp, "===========================================================\n"); for (i = 0; i < numassoc; i++) { @@ -955,112 +1166,150 @@ printassoc( conf = "yes"; else conf = "no"; - if (statval & CTL_PST_REACH || 1) { - reach = "yes"; + if (statval & CTL_PST_BCAST) { + reach = "none"; + if (statval & CTL_PST_AUTHENABLE) + auth = "yes"; + else + auth = "none"; + } else { + if (statval & CTL_PST_REACH) + reach = "yes"; + else + reach = "no"; if (statval & CTL_PST_AUTHENABLE) { if (statval & CTL_PST_AUTHENTIC) auth = "ok "; else auth = "bad"; - } else + } else { auth = "none"; + } + } + if (pktversion > NTP_OLDVERSION) { + switch (statval & 0x7) { - if (pktversion > NTP_OLDVERSION) - switch (statval & 0x7) { - case CTL_PST_SEL_REJECT: - condition = "reject"; - break; - case CTL_PST_SEL_SANE: - condition = "falsetick"; - break; - case CTL_PST_SEL_CORRECT: - condition = "excess"; - break; - case CTL_PST_SEL_SELCAND: - condition = "outlyer"; - break; - case CTL_PST_SEL_SYNCCAND: - condition = "candidat"; - break; - case CTL_PST_SEL_DISTSYSPEER: - condition = "selected"; - break; - case CTL_PST_SEL_SYSPEER: - condition = "sys.peer"; - break; - case CTL_PST_SEL_PPS: - condition = "pps.peer"; - break; - } - else - switch (statval & 0x3) { - case OLD_CTL_PST_SEL_REJECT: - if (!(statval & OLD_CTL_PST_SANE)) + case CTL_PST_SEL_REJECT: + condition = "reject"; + break; + + case CTL_PST_SEL_SANE: + condition = "falsetick"; + break; + + case CTL_PST_SEL_CORRECT: + condition = "excess"; + break; + + case CTL_PST_SEL_SELCAND: + condition = "outlyer"; + break; + + case CTL_PST_SEL_SYNCCAND: + condition = "candidate"; + break; + + case CTL_PST_SEL_EXCESS: + condition = "backup"; + break; + + case CTL_PST_SEL_SYSPEER: + condition = "sys.peer"; + break; + + case CTL_PST_SEL_PPS: + condition = "pps.peer"; + break; + } + } else { + switch (statval & 0x3) { + + case OLD_CTL_PST_SEL_REJECT: + if (!(statval & OLD_CTL_PST_SANE)) condition = "insane"; - else if (!(statval & OLD_CTL_PST_DISP)) + else if (!(statval & OLD_CTL_PST_DISP)) condition = "hi_disp"; - else + else condition = ""; - break; - case OLD_CTL_PST_SEL_SELCAND: - condition = "sel_cand"; - break; - case OLD_CTL_PST_SEL_SYNCCAND: - condition = "sync_cand"; - break; - case OLD_CTL_PST_SEL_SYSPEER: - condition = "sys_peer"; - break; - } + break; - } else { - reach = "no"; - auth = condition = ""; - } + case OLD_CTL_PST_SEL_SELCAND: + condition = "sel_cand"; + break; + case OLD_CTL_PST_SEL_SYNCCAND: + condition = "sync_cand"; + break; + + case OLD_CTL_PST_SEL_SYSPEER: + condition = "sys_peer"; + break; + } + } switch (PEER_EVENT|event) { - case EVNT_PEERIPERR: - last_event = "IP error"; - break; - case EVNT_PEERAUTH: - last_event = "auth fail"; + + case PEVNT_MOBIL: + last_event = "mobilize"; break; - case EVNT_UNREACH: - last_event = "lost reach"; + + case PEVNT_DEMOBIL: + last_event = "demobilize"; break; - case EVNT_REACH: + + case PEVNT_REACH: last_event = "reachable"; break; - case EVNT_PEERCLOCK: - last_event = "clock expt"; + + case PEVNT_UNREACH: + last_event = "unreachable"; break; -#if 0 - case EVNT_PEERSTRAT: - last_event = "stratum chg"; + + case PEVNT_RESTART: + last_event = "restart"; break; -#endif - default: + + case PEVNT_REPLY: + last_event = "no_reply"; + break; + + case PEVNT_RATE: + last_event = "rate_exceeded"; + break; + + case PEVNT_DENY: + last_event = "access_denied"; + break; + + case PEVNT_ARMED: + last_event = "leap_armed"; + break; + + case PEVNT_NEWPEER: + last_event = "sys_peer"; + break; + + case PEVNT_CLOCK: + last_event = "clock_alarm"; + break; + + default: last_event = ""; break; } - - if (event_count != 0) - cnt = uinttoa(event_count); - else - cnt = ""; - (void) sprintf(buf, - "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2s", - i+1, assoc_cache[i].assid, assoc_cache[i].status, - conf, reach, auth, condition, last_event, cnt); - bp = &buf[strlen(buf)]; - while (bp > buf && *(bp-1) == ' ') - *(--bp) = '\0'; - (void) fprintf(fp, "%s\n", buf); + snprintf(buf, sizeof(buf), + "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2lu", + i + 1, assoc_cache[i].assid, + assoc_cache[i].status, conf, reach, auth, + condition, last_event, event_count); + bp = buf + strlen(buf); + while (bp > buf && ' ' == bp[-1]) + --bp; + bp[0] = '\0'; + fprintf(fp, "%s\n", buf); } } - /* * associations - get, record and print a list of associations */ @@ -1119,59 +1368,55 @@ lpassociations( } -#ifdef UNUSED /* - * radiostatus - print the radio status returned by the server + * saveconfig - dump ntp server configuration to server file */ -/*ARGSUSED*/ static void -radiostatus( +saveconfig( struct parse *pcmd, FILE *fp ) { - char *datap; + const char *datap; int res; int dsize; u_short rstatus; - res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus, - &dsize, &datap); - - if (res != 0) + if (0 == pcmd->nargs) return; + + res = doquery(CTL_OP_SAVECONFIG, 0, 1, + strlen(pcmd->argval[0].string), + pcmd->argval[0].string, &rstatus, &dsize, + &datap); - if (numhosts > 1) - (void) fprintf(fp, "server=%s ", currenthost); - if (dsize == 0) { - (void) fprintf(fp, "No radio status string returned\n"); + if (res != 0) return; - } - asciize(dsize, datap, fp); + if (0 == dsize) + fprintf(fp, "(no response message, curiously)"); + else + fprintf(fp, "%.*s", dsize, datap); } -#endif /* UNUSED */ + +#ifdef UNUSED /* - * pstatus - print peer status returned by the server + * radiostatus - print the radio status returned by the server */ +/*ARGSUSED*/ static void -pstatus( +radiostatus( struct parse *pcmd, FILE *fp ) { char *datap; int res; - int associd; int dsize; u_short rstatus; - /* HMS: uval? */ - if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) - return; - - res = doquery(CTL_OP_READSTAT, associd, 0, 0, (char *)0, &rstatus, + res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus, &dsize, &datap); if (res != 0) @@ -1180,16 +1425,13 @@ pstatus( if (numhosts > 1) (void) fprintf(fp, "server=%s ", currenthost); if (dsize == 0) { - (void) fprintf(fp, - "No information returned for association %u\n", - associd); + (void) fprintf(fp, "No radio status string returned\n"); return; } - (void) fprintf(fp,"assID=%d ",associd); - printvars(dsize, datap, (int)rstatus, TYPE_PEER, fp); + asciize(dsize, datap, fp); } - +#endif /* UNUSED */ /* * when - print how long its been since his last packet arrived @@ -1220,6 +1462,7 @@ when( static char * prettyinterval( char *buf, + size_t cb, long diff ) { @@ -1230,40 +1473,38 @@ prettyinterval( } if (diff <= 2048) { - (void) sprintf(buf, "%ld", (long int)diff); + snprintf(buf, cb, "%ld", diff); return buf; } diff = (diff + 29) / 60; if (diff <= 300) { - (void) sprintf(buf, "%ldm", (long int)diff); + snprintf(buf, cb, "%ldm", diff); return buf; } diff = (diff + 29) / 60; if (diff <= 96) { - (void) sprintf(buf, "%ldh", (long int)diff); + snprintf(buf, cb, "%ldh", diff); return buf; } diff = (diff + 11) / 24; - (void) sprintf(buf, "%ldd", (long int)diff); + snprintf(buf, cb, "%ldd", diff); return buf; } static char decodeaddrtype( - struct sockaddr_storage *sock + sockaddr_u *sock ) { char ch = '-'; u_int32 dummy; - struct sockaddr_in6 *sin6; - switch(sock->ss_family) { + switch(AF(sock)) { case AF_INET: - dummy = ((struct sockaddr_in *)sock)->sin_addr.s_addr; - dummy = ntohl(dummy); + dummy = SRCADR(sock); ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' : ((dummy&0x000000ff)==0x000000ff) ? 'b' : ((dummy&0xffffffff)==0x7f000001) ? 'l' : @@ -1271,8 +1512,7 @@ decodeaddrtype( 'u'); break; case AF_INET6: - sin6 = (struct sockaddr_in6 *)sock; - if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock))) ch = 'm'; else ch = 'u'; @@ -1288,54 +1528,62 @@ decodeaddrtype( * A list of variables required by the peers command */ struct varlist opeervarlist[] = { - { "srcadr", 0 }, /* 0 */ - { "dstadr", 0 }, /* 1 */ - { "stratum", 0 }, /* 2 */ - { "hpoll", 0 }, /* 3 */ - { "ppoll", 0 }, /* 4 */ - { "reach", 0 }, /* 5 */ - { "delay", 0 }, /* 6 */ - { "offset", 0 }, /* 7 */ - { "jitter", 0 }, /* 8 */ - { "dispersion", 0 }, /* 9 */ - { "rec", 0 }, /* 10 */ - { "reftime", 0 }, /* 11 */ - { "srcport", 0 }, /* 12 */ + { "srcadr", 0 }, /* 0 */ + { "dstadr", 0 }, /* 1 */ + { "stratum", 0 }, /* 2 */ + { "hpoll", 0 }, /* 3 */ + { "ppoll", 0 }, /* 4 */ + { "reach", 0 }, /* 5 */ + { "delay", 0 }, /* 6 */ + { "offset", 0 }, /* 7 */ + { "jitter", 0 }, /* 8 */ + { "dispersion", 0 }, /* 9 */ + { "rec", 0 }, /* 10 */ + { "reftime", 0 }, /* 11 */ + { "srcport", 0 }, /* 12 */ + { "hmode", 0 }, /* 13 */ { 0, 0 } }; struct varlist peervarlist[] = { - { "srcadr", 0 }, /* 0 */ - { "refid", 0 }, /* 1 */ - { "stratum", 0 }, /* 2 */ - { "hpoll", 0 }, /* 3 */ - { "ppoll", 0 }, /* 4 */ - { "reach", 0 }, /* 5 */ - { "delay", 0 }, /* 6 */ - { "offset", 0 }, /* 7 */ - { "jitter", 0 }, /* 8 */ - { "dispersion", 0 }, /* 9 */ - { "rec", 0 }, /* 10 */ - { "reftime", 0 }, /* 11 */ - { "srcport", 0 }, /* 12 */ + { "srcadr", 0 }, /* 0 */ + { "refid", 0 }, /* 1 */ + { "stratum", 0 }, /* 2 */ + { "hpoll", 0 }, /* 3 */ + { "ppoll", 0 }, /* 4 */ + { "reach", 0 }, /* 5 */ + { "delay", 0 }, /* 6 */ + { "offset", 0 }, /* 7 */ + { "jitter", 0 }, /* 8 */ + { "dispersion", 0 }, /* 9 */ + { "rec", 0 }, /* 10 */ + { "reftime", 0 }, /* 11 */ + { "srcport", 0 }, /* 12 */ + { "hmode", 0 }, /* 13 */ + { "srchost", 0 }, /* 14 */ + { 0, 0 } +}; + +struct varlist apeervarlist[] = { + { "srcadr", 0 }, /* 0 */ + { "refid", 0 }, /* 1 */ + { "assid", 0 }, /* 2 */ + { "stratum", 0 }, /* 3 */ + { "hpoll", 0 }, /* 4 */ + { "ppoll", 0 }, /* 5 */ + { "reach", 0 }, /* 6 */ + { "delay", 0 }, /* 7 */ + { "offset", 0 }, /* 8 */ + { "jitter", 0 }, /* 9 */ + { "dispersion", 0 }, /* 10 */ + { "rec", 0 }, /* 11 */ + { "reftime", 0 }, /* 12 */ + { "srcport", 0 }, /* 13 */ + { "hmode", 0 }, /* 14 */ + { "srchost", 0 }, /* 15 */ { 0, 0 } }; -#define HAVE_SRCADR 0 -#define HAVE_DSTADR 1 -#define HAVE_REFID 1 -#define HAVE_STRATUM 2 -#define HAVE_HPOLL 3 -#define HAVE_PPOLL 4 -#define HAVE_REACH 5 -#define HAVE_DELAY 6 -#define HAVE_OFFSET 7 -#define HAVE_JITTER 8 -#define HAVE_DISPERSION 9 -#define HAVE_REC 10 -#define HAVE_REFTIME 11 -#define HAVE_SRCPORT 12 -#define MAXHAVE 13 /* * Decode an incoming data buffer and print a line in the peer list @@ -1346,20 +1594,29 @@ doprintpeers( int associd, int rstatus, int datalen, - char *data, + const char *data, FILE *fp, int af ) { char *name; char *value = NULL; - int i; int c; - - struct sockaddr_storage srcadr; - struct sockaddr_storage dstadr; + int len; + int have_srchost; + int have_dstadr; + int have_da_rid; + int have_jitter; + sockaddr_u srcadr; + sockaddr_u dstadr; + sockaddr_u dum_store; + sockaddr_u refidadr; + long hmode = 0; u_long srcport = 0; - char *dstadr_refid = "0.0.0.0"; + u_int32 u32; + const char *dstadr_refid = "0.0.0.0"; + const char *serverlocal; + size_t drlen; u_long stratum = 0; long ppoll = 0; long hpoll = 0; @@ -1371,180 +1628,244 @@ doprintpeers( l_fp reftime; l_fp rec; l_fp ts; - u_char havevar[MAXHAVE]; u_long poll_sec; char type = '?'; - char refid_string[10]; char whenbuf[8], pollbuf[8]; char clock_name[LENHOSTNAME]; - memset((char *)havevar, 0, sizeof(havevar)); get_systime(&ts); - memset((char *)&srcadr, 0, sizeof(struct sockaddr_storage)); - memset((char *)&dstadr, 0, sizeof(struct sockaddr_storage)); - - /* Initialize by zeroing out estimate variables */ - memset((char *)&estoffset, 0, sizeof(l_fp)); - memset((char *)&estdelay, 0, sizeof(l_fp)); - memset((char *)&estjitter, 0, sizeof(l_fp)); - memset((char *)&estdisp, 0, sizeof(l_fp)); + have_srchost = FALSE; + have_dstadr = FALSE; + have_da_rid = FALSE; + have_jitter = FALSE; + ZERO_SOCK(&srcadr); + ZERO_SOCK(&dstadr); + clock_name[0] = '\0'; + ZERO(estoffset); + ZERO(estdelay); + ZERO(estjitter); + ZERO(estdisp); while (nextvar(&datalen, &data, &name, &value)) { - struct sockaddr_storage dum_store; - - i = findvar(name, peer_var, 1); - if (i == 0) - continue; /* don't know this one */ - switch (i) { - case CP_SRCADR: - if (decodenetnum(value, &srcadr)) - havevar[HAVE_SRCADR] = 1; - break; - case CP_DSTADR: - if (decodenetnum(value, &dum_store)) + if (!strcmp("srcadr", name) || + !strcmp("peeradr", name)) { + if (!decodenetnum(value, &srcadr)) + fprintf(stderr, "malformed %s=%s\n", + name, value); + } else if (!strcmp("srchost", name)) { + if (pvl == peervarlist || pvl == apeervarlist) { + len = strlen(value); + if (2 < len && + (size_t)len < sizeof(clock_name)) { + /* strip quotes */ + value++; + len -= 2; + memcpy(clock_name, value, len); + clock_name[len] = '\0'; + have_srchost = TRUE; + } + } + } else if (!strcmp("dstadr", name)) { + if (decodenetnum(value, &dum_store)) { type = decodeaddrtype(&dum_store); - if (pvl == opeervarlist) { - if (decodenetnum(value, &dstadr)) { - havevar[HAVE_DSTADR] = 1; - dstadr_refid = stoa(&dstadr); + have_dstadr = TRUE; + dstadr = dum_store; + if (pvl == opeervarlist) { + have_da_rid = TRUE; + dstadr_refid = trunc_left(stoa(&dstadr), 15); } } - break; - case CP_REFID: + } else if (!strcmp("hmode", name)) { + decodeint(value, &hmode); + } else if (!strcmp("refid", name)) { if (pvl == peervarlist) { - havevar[HAVE_REFID] = 1; - if (*value == '\0') { - dstadr_refid = "0.0.0.0"; - } else if ((int)strlen(value) <= 4) { - refid_string[0] = '.'; - (void) strcpy(&refid_string[1], value); - i = strlen(refid_string); - refid_string[i] = '.'; - refid_string[i+1] = '\0'; - dstadr_refid = refid_string; - } else if (decodenetnum(value, &dstadr)) { - if (SOCKNUL(&dstadr)) + have_da_rid = TRUE; + drlen = strlen(value); + if (0 == drlen) { + dstadr_refid = ""; + } else if (drlen <= 4) { + ZERO(u32); + memcpy(&u32, value, drlen); + dstadr_refid = refid_str(u32, 1); + } else if (decodenetnum(value, &refidadr)) { + if (SOCK_UNSPEC(&refidadr)) dstadr_refid = "0.0.0.0"; - else if ((dstadr.ss_family == AF_INET) - && ISREFCLOCKADR(&dstadr)) - dstadr_refid = - refnumtoa(&dstadr); + else if (ISREFCLOCKADR(&refidadr)) + dstadr_refid = + refnumtoa(&refidadr); else dstadr_refid = - stoa(&dstadr); + stoa(&refidadr); } else { - havevar[HAVE_REFID] = 0; + have_da_rid = FALSE; + } + } else if (pvl == apeervarlist) { + have_da_rid = TRUE; + drlen = strlen(value); + if (0 == drlen) { + dstadr_refid = ""; + } else if (drlen <= 4) { + ZERO(u32); + memcpy(&u32, value, drlen); + dstadr_refid = refid_str(u32, 1); + //fprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value); + } else if (decodenetnum(value, &refidadr)) { + if (SOCK_UNSPEC(&refidadr)) + dstadr_refid = "0.0.0.0"; + else if (ISREFCLOCKADR(&refidadr)) + dstadr_refid = + refnumtoa(&refidadr); + else { + char *buf = emalloc(10); + int i = ntohl(refidadr.sa4.sin_addr.s_addr); + + snprintf(buf, 10, + "%0x", i); + dstadr_refid = buf; + //fprintf(stderr, "apeervarlist refid: value=<%x>\n", i); + } + //fprintf(stderr, "apeervarlist refid: value=<%s>\n", value); + } else { + have_da_rid = FALSE; } } - break; - case CP_STRATUM: - if (decodeuint(value, &stratum)) - havevar[HAVE_STRATUM] = 1; - break; - case CP_HPOLL: - if (decodeint(value, &hpoll)) { - havevar[HAVE_HPOLL] = 1; - if (hpoll < 0) - hpoll = NTP_MINPOLL; - } - break; - case CP_PPOLL: - if (decodeint(value, &ppoll)) { - havevar[HAVE_PPOLL] = 1; - if (ppoll < 0) - ppoll = NTP_MINPOLL; - } - break; - case CP_REACH: - if (decodeuint(value, &reach)) - havevar[HAVE_REACH] = 1; - break; - case CP_DELAY: - if (decodetime(value, &estdelay)) - havevar[HAVE_DELAY] = 1; - break; - case CP_OFFSET: - if (decodetime(value, &estoffset)) - havevar[HAVE_OFFSET] = 1; - break; - case CP_JITTER: - if (pvl == peervarlist) - if (decodetime(value, &estjitter)) - havevar[HAVE_JITTER] = 1; - break; - case CP_DISPERSION: - if (decodetime(value, &estdisp)) - havevar[HAVE_DISPERSION] = 1; - break; - case CP_REC: - if (decodets(value, &rec)) - havevar[HAVE_REC] = 1; - break; - case CP_SRCPORT: - if (decodeuint(value, &srcport)) - havevar[HAVE_SRCPORT] = 1; - break; - case CP_REFTIME: - havevar[HAVE_REFTIME] = 1; + } else if (!strcmp("stratum", name)) { + decodeuint(value, &stratum); + } else if (!strcmp("hpoll", name)) { + if (decodeint(value, &hpoll) && hpoll < 0) + hpoll = NTP_MINPOLL; + } else if (!strcmp("ppoll", name)) { + if (decodeint(value, &ppoll) && ppoll < 0) + ppoll = NTP_MINPOLL; + } else if (!strcmp("reach", name)) { + decodeuint(value, &reach); + } else if (!strcmp("delay", name)) { + decodetime(value, &estdelay); + } else if (!strcmp("offset", name)) { + decodetime(value, &estoffset); + } else if (!strcmp("jitter", name)) { + if ((pvl == peervarlist || pvl == apeervarlist) + && decodetime(value, &estjitter)) + have_jitter = 1; + } else if (!strcmp("rootdisp", name) || + !strcmp("dispersion", name)) { + decodetime(value, &estdisp); + } else if (!strcmp("rec", name)) { + decodets(value, &rec); + } else if (!strcmp("srcport", name) || + !strcmp("peerport", name)) { + decodeuint(value, &srcport); + } else if (!strcmp("reftime", name)) { if (!decodets(value, &reftime)) L_CLR(&reftime); - break; - default: - break; + } else { + // fprintf(stderr, "UNRECOGNIZED name=%s ", name); } } /* - * Check to see if the srcport is NTP's port. If not this probably - * isn't a valid peer association. + * hmode gives the best guidance for the t column. If the response + * did not include hmode we'll use the old decodeaddrtype() result. */ - if (havevar[HAVE_SRCPORT] && srcport != NTP_PORT) - return (1); + switch (hmode) { + + case MODE_BCLIENT: + /* broadcastclient or multicastclient */ + type = 'b'; + break; + + case MODE_BROADCAST: + /* broadcast or multicast server */ + if (IS_MCAST(&srcadr)) + type = 'M'; + else + type = 'B'; + break; + + case MODE_CLIENT: + if (ISREFCLOCKADR(&srcadr)) + type = 'l'; /* local refclock*/ + else if (SOCK_UNSPEC(&srcadr)) + type = 'p'; /* pool */ + else if (IS_MCAST(&srcadr)) + type = 'a'; /* manycastclient */ + else + type = 'u'; /* unicast */ + break; + + case MODE_ACTIVE: + type = 's'; /* symmetric active */ + break; /* configured */ + + case MODE_PASSIVE: + type = 'S'; /* symmetric passive */ + break; /* ephemeral */ + } /* * Got everything, format the line */ - poll_sec = 1<<max(min3(ppoll, hpoll, NTP_MAXPOLL), NTP_MINPOLL); + poll_sec = 1 << min(ppoll, hpoll); if (pktversion > NTP_OLDVERSION) c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7]; else c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3]; - if (numhosts > 1) - (void) fprintf(fp, "%-*s ", maxhostlen, currenthost); - if (af == 0 || srcadr.ss_family == af){ - strcpy(clock_name, nntohost(&srcadr)); - - (void) fprintf(fp, - "%c%-15.15s %-15.15s %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n", - c, clock_name, dstadr_refid, stratum, type, - prettyinterval(whenbuf, when(&ts, &rec, &reftime)), - prettyinterval(pollbuf, (int)poll_sec), reach, - lfptoms(&estdelay, 3), lfptoms(&estoffset, 3), - havevar[HAVE_JITTER] ? lfptoms(&estjitter, 3) : - lfptoms(&estdisp, 3)); + if (numhosts > 1) { + if ((pvl == peervarlist || pvl == apeervarlist) + && have_dstadr) { + serverlocal = nntohost_col(&dstadr, + (size_t)min(LIB_BUFLENGTH - 1, maxhostlen), + TRUE); + } else { + if (currenthostisnum) + serverlocal = trunc_left(currenthost, + maxhostlen); + else + serverlocal = currenthost; + } + fprintf(fp, "%-*s ", (int)maxhostlen, serverlocal); + } + if (AF_UNSPEC == af || AF(&srcadr) == af) { + if (!have_srchost) + strlcpy(clock_name, nntohost(&srcadr), + sizeof(clock_name)); + if (wideremote && 15 < strlen(clock_name)) + fprintf(fp, "%c%s\n ", c, clock_name); + else + fprintf(fp, "%c%-15.15s ", c, clock_name); + if (!have_da_rid) { + drlen = 0; + } else { + drlen = strlen(dstadr_refid); + makeascii(drlen, dstadr_refid, fp); + } + if (pvl == apeervarlist) { + while (drlen++ < 9) + fputc(' ', fp); + fprintf(fp, "%-6d", associd); + } else { + while (drlen++ < 15) + fputc(' ', fp); + } + fprintf(fp, + " %2ld %c %4.4s %4.4s %3lo %7.7s %8.7s %7.7s\n", + stratum, type, + prettyinterval(whenbuf, sizeof(whenbuf), + when(&ts, &rec, &reftime)), + prettyinterval(pollbuf, sizeof(pollbuf), + (int)poll_sec), + reach, lfptoms(&estdelay, 3), + lfptoms(&estoffset, 3), + (have_jitter) + ? lfptoms(&estjitter, 3) + : lfptoms(&estdisp, 3)); return (1); } else return(1); } -#undef HAVE_SRCADR -#undef HAVE_DSTADR -#undef HAVE_STRATUM -#undef HAVE_PPOLL -#undef HAVE_HPOLL -#undef HAVE_REACH -#undef HAVE_ESTDELAY -#undef HAVE_ESTOFFSET -#undef HAVE_JITTER -#undef HAVE_ESTDISP -#undef HAVE_REFID -#undef HAVE_REC -#undef HAVE_SRCPORT -#undef HAVE_REFTIME -#undef MAXHAVE - /* * dogetpeers - given an association ID, read and print the spreadsheet @@ -1553,12 +1874,12 @@ doprintpeers( static int dogetpeers( struct varlist *pvl, - int associd, + associd_t associd, FILE *fp, int af ) { - char *datap; + const char *datap; int res; int dsize; u_short rstatus; @@ -1570,7 +1891,7 @@ dogetpeers( /* * Damn fuzzballs */ - res = doquery(CTL_OP_READVAR, associd, 0, 0, (char *)0, &rstatus, + res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus, &dsize, &datap); #endif @@ -1579,14 +1900,15 @@ dogetpeers( if (dsize == 0) { if (numhosts > 1) - (void) fprintf(stderr, "server=%s ", currenthost); - (void) fprintf(stderr, - "***No information returned for association %d\n", - associd); + fprintf(stderr, "server=%s ", currenthost); + fprintf(stderr, + "***No information returned for association %u\n", + associd); return 0; } - return doprintpeers(pvl, associd, (int)rstatus, dsize, datap, fp, af); + return doprintpeers(pvl, associd, (int)rstatus, dsize, datap, + fp, af); } @@ -1600,36 +1922,99 @@ dopeers( int af ) { - register int i; - char fullname[LENHOSTNAME]; - struct sockaddr_storage netnum; + u_int u; + char fullname[LENHOSTNAME]; + sockaddr_u netnum; + const char * name_or_num; + size_t sl; if (!dogetassoc(fp)) return; - for (i = 0; i < numhosts; ++i) { - if (getnetnum(chosts[i], &netnum, fullname, af)) - if ((int)strlen(fullname) > maxhostlen) - maxhostlen = strlen(fullname); + for (u = 0; u < numhosts; u++) { + if (getnetnum(chosts[u].name, &netnum, fullname, af)) { + name_or_num = nntohost(&netnum); + sl = strlen(name_or_num); + maxhostlen = max(maxhostlen, sl); + } } if (numhosts > 1) - (void) fprintf(fp, "%-*.*s ", maxhostlen, maxhostlen, "server"); - (void) fprintf(fp, - " remote refid st t when poll reach delay offset jitter\n"); + fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, + "server (local)"); + fprintf(fp, + " remote refid st t when poll reach delay offset jitter\n"); if (numhosts > 1) - for (i = 0; i <= maxhostlen; ++i) - (void) fprintf(fp, "="); - (void) fprintf(fp, - "==============================================================================\n"); + for (u = 0; u <= maxhostlen; u++) + fprintf(fp, "="); + fprintf(fp, + "==============================================================================\n"); - for (i = 0; i < numassoc; i++) { + for (u = 0; u < numassoc; u++) { if (!showall && - !(CTL_PEER_STATVAL(assoc_cache[i].status) - & (CTL_PST_CONFIG|CTL_PST_REACH))) + !(CTL_PEER_STATVAL(assoc_cache[u].status) + & (CTL_PST_CONFIG|CTL_PST_REACH))) { + if (debug) + fprintf(stderr, "eliding [%d]\n", + (int)assoc_cache[u].assid); continue; - if (!dogetpeers(peervarlist, (int)assoc_cache[i].assid, fp, af)) { + } + if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid, + fp, af)) return; + } + return; +} + + +/* + * doapeers - print a peer spreadsheet with assocIDs + */ +static void +doapeers( + int showall, + FILE *fp, + int af + ) +{ + u_int u; + char fullname[LENHOSTNAME]; + sockaddr_u netnum; + const char * name_or_num; + size_t sl; + + if (!dogetassoc(fp)) + return; + + for (u = 0; u < numhosts; u++) { + if (getnetnum(chosts[u].name, &netnum, fullname, af)) { + name_or_num = nntohost(&netnum); + sl = strlen(name_or_num); + maxhostlen = max(maxhostlen, sl); + } + } + if (numhosts > 1) + fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, + "server (local)"); + fprintf(fp, + " remote refid assid st t when poll reach delay offset jitter\n"); + if (numhosts > 1) + for (u = 0; u <= maxhostlen; u++) + fprintf(fp, "="); + fprintf(fp, + "==============================================================================\n"); + + for (u = 0; u < numassoc; u++) { + if (!showall && + !(CTL_PEER_STATVAL(assoc_cache[u].status) + & (CTL_PST_CONFIG|CTL_PST_REACH))) { + if (debug) + fprintf(stderr, "eliding [%d]\n", + (int)assoc_cache[u].assid); + continue; } + if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid, + fp, af)) + return; } return; } @@ -1658,6 +2043,28 @@ peers( /* + * apeers - print a peer spreadsheet, with assocIDs + */ +/*ARGSUSED*/ +static void +apeers( + struct parse *pcmd, + FILE *fp + ) +{ + int af = 0; + + if (pcmd->nargs == 1) { + if (pcmd->argval->ival == 6) + af = AF_INET6; + else + af = AF_INET; + } + doapeers(0, fp, af); +} + + +/* * lpeers - print a peer spreadsheet including all fuzzball peers */ /*ARGSUSED*/ @@ -1689,36 +2096,36 @@ doopeers( int af ) { - register int i; + u_int i; char fullname[LENHOSTNAME]; - struct sockaddr_storage netnum; + sockaddr_u netnum; if (!dogetassoc(fp)) return; for (i = 0; i < numhosts; ++i) { - if (getnetnum(chosts[i], &netnum, fullname, af)) - if ((int)strlen(fullname) > maxhostlen) + if (getnetnum(chosts[i].name, &netnum, fullname, af)) + if (strlen(fullname) > maxhostlen) maxhostlen = strlen(fullname); } if (numhosts > 1) - (void) fprintf(fp, "%-*.*s ", maxhostlen, maxhostlen, "server"); - (void) fprintf(fp, - " remote local st t when poll reach delay offset disp\n"); + fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen, + "server"); + fprintf(fp, + " remote local st t when poll reach delay offset disp\n"); if (numhosts > 1) for (i = 0; i <= maxhostlen; ++i) - (void) fprintf(fp, "="); - (void) fprintf(fp, - "==============================================================================\n"); + fprintf(fp, "="); + fprintf(fp, + "==============================================================================\n"); for (i = 0; i < numassoc; i++) { if (!showall && - !(CTL_PEER_STATVAL(assoc_cache[i].status) - & (CTL_PST_CONFIG|CTL_PST_REACH))) + !(CTL_PEER_STATVAL(assoc_cache[i].status) & + (CTL_PST_CONFIG | CTL_PST_REACH))) continue; - if (!dogetpeers(opeervarlist, (int)assoc_cache[i].assid, fp, af)) { + if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af)) return; - } } return; } @@ -1766,3 +2173,1842 @@ lopeers( } doopeers(1, fp, af); } + + +/* + * config - send a configuration command to a remote host + */ +static void +config ( + struct parse *pcmd, + FILE *fp + ) +{ + const char *cfgcmd; + u_short rstatus; + int rsize; + const char *rdata; + char *resp; + int res; + int col; + int i; + + cfgcmd = pcmd->argval[0].string; + + if (debug > 2) + fprintf(stderr, + "In Config\n" + "Keyword = %s\n" + "Command = %s\n", pcmd->keyword, cfgcmd); + + res = doquery(CTL_OP_CONFIGURE, 0, 1, strlen(cfgcmd), cfgcmd, + &rstatus, &rsize, &rdata); + + if (res != 0) + return; + + if (rsize > 0 && '\n' == rdata[rsize - 1]) + rsize--; + + resp = emalloc(rsize + 1); + memcpy(resp, rdata, rsize); + resp[rsize] = '\0'; + + col = -1; + if (1 == sscanf(resp, "column %d syntax error", &col) + && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) { + if (interactive) { + printf("______"); /* "ntpq> " */ + printf("________"); /* ":config " */ + } else + printf("%s\n", cfgcmd); + for (i = 1; i < col; i++) + putchar('_'); + printf("^\n"); + } + printf("%s\n", resp); + free(resp); +} + + +/* + * config_from_file - remotely configure an ntpd daemon using the + * specified configuration file + * SK: This function is a kludge at best and is full of bad design + * bugs: + * 1. ntpq uses UDP, which means that there is no guarantee of in-order, + * error-free delivery. + * 2. The maximum length of a packet is constrained, and as a result, the + * maximum length of a line in a configuration file is constrained. + * Longer lines will lead to unpredictable results. + * 3. Since this function is sending a line at a time, we can't update + * the control key through the configuration file (YUCK!!) + */ +static void +config_from_file ( + struct parse *pcmd, + FILE *fp + ) +{ + u_short rstatus; + int rsize; + const char *rdata; + int res; + FILE *config_fd; + char config_cmd[MAXLINE]; + size_t config_len; + int i; + int retry_limit; + + if (debug > 2) + fprintf(stderr, + "In Config\n" + "Keyword = %s\n" + "Filename = %s\n", pcmd->keyword, + pcmd->argval[0].string); + + config_fd = fopen(pcmd->argval[0].string, "r"); + if (NULL == config_fd) { + printf("ERROR!! Couldn't open file: %s\n", + pcmd->argval[0].string); + return; + } + + printf("Sending configuration file, one line at a time.\n"); + i = 0; + while (fgets(config_cmd, MAXLINE, config_fd) != NULL) { + config_len = strlen(config_cmd); + /* ensure even the last line has newline, if possible */ + if (config_len > 0 && + config_len + 2 < sizeof(config_cmd) && + '\n' != config_cmd[config_len - 1]) + config_cmd[config_len++] = '\n'; + ++i; + retry_limit = 2; + do + res = doquery(CTL_OP_CONFIGURE, 0, 1, + strlen(config_cmd), config_cmd, + &rstatus, &rsize, &rdata); + while (res != 0 && retry_limit--); + if (res != 0) { + printf("Line No: %d query failed: %s", i, + config_cmd); + printf("Subsequent lines not sent.\n"); + fclose(config_fd); + return; + } + + if (rsize > 0 && '\n' == rdata[rsize - 1]) + rsize--; + if (rsize > 0 && '\r' == rdata[rsize - 1]) + rsize--; + printf("Line No: %d %.*s: %s", i, rsize, rdata, + config_cmd); + } + printf("Done sending file\n"); + fclose(config_fd); +} + + +static int +fetch_nonce( + char * nonce, + size_t cb_nonce + ) +{ + const char nonce_eq[] = "nonce="; + int qres; + u_short rstatus; + int rsize; + const char * rdata; + int chars; + + /* + * Retrieve a nonce specific to this client to demonstrate to + * ntpd that we're capable of receiving responses to our source + * IP address, and thereby unlikely to be forging the source. + */ + qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus, + &rsize, &rdata); + if (qres) { + fprintf(stderr, "nonce request failed\n"); + return FALSE; + } + + if ((size_t)rsize <= sizeof(nonce_eq) - 1 || + strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) { + fprintf(stderr, "unexpected nonce response format: %.*s\n", + rsize, rdata); + return FALSE; + } + chars = rsize - (sizeof(nonce_eq) - 1); + if (chars >= (int)cb_nonce) + return FALSE; + memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars); + nonce[chars] = '\0'; + while (chars > 0 && + ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) { + chars--; + nonce[chars] = '\0'; + } + + return TRUE; +} + + +/* + * add_mru Add and entry to mru list, hash table, and allocate + * and return a replacement. + * This is a helper for collect_mru_list(). + */ +static mru * +add_mru( + mru *add + ) +{ + u_short hash; + mru *mon; + mru *unlinked; + + + hash = NTP_HASH_ADDR(&add->addr); + /* see if we have it among previously received entries */ + for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink) + if (SOCK_EQ(&mon->addr, &add->addr)) + break; + if (mon != NULL) { + if (!L_ISGEQ(&add->first, &mon->first)) { + fprintf(stderr, + "add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n", + sptoa(&add->addr), add->last.l_ui, + add->last.l_uf, mon->last.l_ui, + mon->last.l_uf); + exit(1); + } + UNLINK_DLIST(mon, mlink); + UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru); + NTP_INSIST(unlinked == mon); + mru_dupes++; + TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui, + mon->last.l_uf)); + } + LINK_DLIST(mru_list, add, mlink); + LINK_SLIST(hash_table[hash], add, hlink); + TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n", + add->last.l_ui, add->last.l_uf, add->count, + (int)add->mode, (int)add->ver, (u_int)add->rs, + add->first.l_ui, add->first.l_uf, sptoa(&add->addr))); + /* if we didn't update an existing entry, alloc replacement */ + if (NULL == mon) { + mon = emalloc(sizeof(*mon)); + mru_count++; + } + ZERO(*mon); + + return mon; +} + + +/* MGOT macro is specific to collect_mru_list() */ +#define MGOT(bit) \ + do { \ + got |= (bit); \ + if (MRU_GOT_ALL == got) { \ + got = 0; \ + mon = add_mru(mon); \ + ci++; \ + } \ + } while (0) + + +void +mrulist_ctrl_c_hook(void) +{ + mrulist_interrupted = TRUE; +} + + +static int +collect_mru_list( + const char * parms, + l_fp * pnow + ) +{ + const u_int sleep_msecs = 5; + static int ntpd_row_limit = MRU_ROW_LIMIT; + int c_mru_l_rc; /* this function's return code */ + u_char got; /* MRU_GOT_* bits */ + time_t next_report; + size_t cb; + mru *mon; + mru *head; + mru *recent; + int list_complete; + char nonce[128]; + char buf[128]; + char req_buf[CTL_MAX_DATA_LEN]; + char *req; + char *req_end; + int chars; + int qres; + u_short rstatus; + int rsize; + const char *rdata; + int limit; + int frags; + int cap_frags; + char *tag; + char *val; + int si; /* server index in response */ + int ci; /* client (our) index for validation */ + int ri; /* request index (.# suffix) */ + int mv; + l_fp newest; + l_fp last_older; + sockaddr_u addr_older; + int have_now; + int have_addr_older; + int have_last_older; + u_int restarted_count; + u_int nonce_uses; + u_short hash; + mru *unlinked; + + if (!fetch_nonce(nonce, sizeof(nonce))) + return FALSE; + + nonce_uses = 0; + restarted_count = 0; + mru_count = 0; + INIT_DLIST(mru_list, mlink); + cb = NTP_HASH_SIZE * sizeof(*hash_table); + NTP_INSIST(NULL == hash_table); + hash_table = emalloc_zero(cb); + + c_mru_l_rc = FALSE; + list_complete = FALSE; + have_now = FALSE; + cap_frags = TRUE; + got = 0; + ri = 0; + cb = sizeof(*mon); + mon = emalloc_zero(cb); + ZERO(*pnow); + ZERO(last_older); + mrulist_interrupted = FALSE; + set_ctrl_c_hook(&mrulist_ctrl_c_hook); + fprintf(stderr, + "Ctrl-C will stop MRU retrieval and display partial results.\n"); + fflush(stderr); + next_report = time(NULL) + MRU_REPORT_SECS; + + limit = min(3 * MAXFRAGS, ntpd_row_limit); + frags = MAXFRAGS; + snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s", + nonce, frags, parms); + nonce_uses++; + + while (TRUE) { + if (debug) + fprintf(stderr, "READ_MRU parms: %s\n", req_buf); + + qres = doqueryex(CTL_OP_READ_MRU, 0, 0, strlen(req_buf), + req_buf, &rstatus, &rsize, &rdata, TRUE); + + if (CERR_UNKNOWNVAR == qres && ri > 0) { + /* + * None of the supplied prior entries match, so + * toss them from our list and try again. + */ + if (debug) + fprintf(stderr, + "no overlap between %d prior entries and server MRU list\n", + ri); + while (ri--) { + recent = HEAD_DLIST(mru_list, mlink); + NTP_INSIST(recent != NULL); + if (debug) + fprintf(stderr, + "tossing prior entry %s to resync\n", + sptoa(&recent->addr)); + UNLINK_DLIST(recent, mlink); + hash = NTP_HASH_ADDR(&recent->addr); + UNLINK_SLIST(unlinked, hash_table[hash], + recent, hlink, mru); + NTP_INSIST(unlinked == recent); + free(recent); + mru_count--; + } + if (NULL == HEAD_DLIST(mru_list, mlink)) { + restarted_count++; + if (restarted_count > 8) { + fprintf(stderr, + "Giving up after 8 restarts from the beginning.\n" + "With high-traffic NTP servers, this can occur if the\n" + "MRU list is limited to less than about 16 seconds' of\n" + "entries. See the 'mru' ntp.conf directive to adjust.\n"); + goto cleanup_return; + } + if (debug) + fprintf(stderr, + "---> Restarting from the beginning, retry #%u\n", + restarted_count); + } + } else if (CERR_UNKNOWNVAR == qres) { + fprintf(stderr, + "CERR_UNKNOWNVAR from ntpd but no priors given.\n"); + goto cleanup_return; + } else if (CERR_BADVALUE == qres) { + if (cap_frags) { + cap_frags = FALSE; + if (debug) + fprintf(stderr, + "Reverted to row limit from fragments limit.\n"); + } else { + /* ntpd has lower cap on row limit */ + ntpd_row_limit--; + limit = min(limit, ntpd_row_limit); + if (debug) + fprintf(stderr, + "Row limit reduced to %d following CERR_BADVALUE.\n", + limit); + } + } else if (ERR_INCOMPLETE == qres || + ERR_TIMEOUT == qres) { + /* + * Reduce the number of rows/frags requested by + * half to recover from lost response fragments. + */ + if (cap_frags) { + frags = max(2, frags / 2); + if (debug) + fprintf(stderr, + "Frag limit reduced to %d following incomplete response.\n", + frags); + } else { + limit = max(2, limit / 2); + if (debug) + fprintf(stderr, + "Row limit reduced to %d following incomplete response.\n", + limit); + } + } else if (qres) { + show_error_msg(qres, 0); + goto cleanup_return; + } + /* + * This is a cheap cop-out implementation of rawmode + * output for mrulist. A better approach would be to + * dump similar output after the list is collected by + * ntpq with a continuous sequence of indexes. This + * cheap approach has indexes resetting to zero for + * each query/response, and duplicates are not + * coalesced. + */ + if (!qres && rawmode) + printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout); + ci = 0; + have_addr_older = FALSE; + have_last_older = FALSE; + while (!qres && nextvar(&rsize, &rdata, &tag, &val)) { + if (debug > 1) + fprintf(stderr, "nextvar gave: %s = %s\n", + tag, val); + switch(tag[0]) { + + case 'a': + if (!strcmp(tag, "addr.older")) { + if (!have_last_older) { + fprintf(stderr, + "addr.older %s before last.older\n", + val); + goto cleanup_return; + } + if (!decodenetnum(val, &addr_older)) { + fprintf(stderr, + "addr.older %s garbled\n", + val); + goto cleanup_return; + } + hash = NTP_HASH_ADDR(&addr_older); + for (recent = hash_table[hash]; + recent != NULL; + recent = recent->hlink) + if (ADDR_PORT_EQ( + &addr_older, + &recent->addr)) + break; + if (NULL == recent) { + fprintf(stderr, + "addr.older %s not in hash table\n", + val); + goto cleanup_return; + } + if (!L_ISEQU(&last_older, + &recent->last)) { + fprintf(stderr, + "last.older %08x.%08x mismatches %08x.%08x expected.\n", + last_older.l_ui, + last_older.l_uf, + recent->last.l_ui, + recent->last.l_uf); + goto cleanup_return; + } + have_addr_older = TRUE; + } else if (1 != sscanf(tag, "addr.%d", &si) + || si != ci) + goto nomatch; + else if (decodenetnum(val, &mon->addr)) + MGOT(MRU_GOT_ADDR); + break; + + case 'l': + if (!strcmp(tag, "last.older")) { + if ('0' != val[0] || + 'x' != val[1] || + !hextolfp(val + 2, &last_older)) { + fprintf(stderr, + "last.older %s garbled\n", + val); + goto cleanup_return; + } + have_last_older = TRUE; + } else if (!strcmp(tag, "last.newest")) { + if (0 != got) { + fprintf(stderr, + "last.newest %s before complete row, got = 0x%x\n", + val, (u_int)got); + goto cleanup_return; + } + if (!have_now) { + fprintf(stderr, + "last.newest %s before now=\n", + val); + goto cleanup_return; + } + head = HEAD_DLIST(mru_list, mlink); + if (NULL != head) { + if ('0' != val[0] || + 'x' != val[1] || + !hextolfp(val + 2, &newest) || + !L_ISEQU(&newest, + &head->last)) { + fprintf(stderr, + "last.newest %s mismatches %08x.%08x", + val, + head->last.l_ui, + head->last.l_uf); + goto cleanup_return; + } + } + list_complete = TRUE; + } else if (1 != sscanf(tag, "last.%d", &si) || + si != ci || '0' != val[0] || + 'x' != val[1] || + !hextolfp(val + 2, &mon->last)) { + goto nomatch; + } else { + MGOT(MRU_GOT_LAST); + /* + * allow interrupted retrieval, + * using most recent retrieved + * entry's last seen timestamp + * as the end of operation. + */ + *pnow = mon->last; + } + break; + + case 'f': + if (1 != sscanf(tag, "first.%d", &si) || + si != ci || '0' != val[0] || + 'x' != val[1] || + !hextolfp(val + 2, &mon->first)) + goto nomatch; + MGOT(MRU_GOT_FIRST); + break; + + case 'n': + if (!strcmp(tag, "nonce")) { + strlcpy(nonce, val, sizeof(nonce)); + nonce_uses = 0; + break; /* case */ + } else if (strcmp(tag, "now") || + '0' != val[0] || + 'x' != val[1] || + !hextolfp(val + 2, pnow)) + goto nomatch; + have_now = TRUE; + break; + + case 'c': + if (1 != sscanf(tag, "ct.%d", &si) || + si != ci || + 1 != sscanf(val, "%d", &mon->count) + || mon->count < 1) + goto nomatch; + MGOT(MRU_GOT_COUNT); + break; + + case 'm': + if (1 != sscanf(tag, "mv.%d", &si) || + si != ci || + 1 != sscanf(val, "%d", &mv)) + goto nomatch; + mon->mode = PKT_MODE(mv); + mon->ver = PKT_VERSION(mv); + MGOT(MRU_GOT_MV); + break; + + case 'r': + if (1 != sscanf(tag, "rs.%d", &si) || + si != ci || + 1 != sscanf(val, "0x%hx", &mon->rs)) + goto nomatch; + MGOT(MRU_GOT_RS); + break; + + default: + nomatch: + /* empty stmt */ ; + /* ignore unknown tags */ + } + } + if (have_now) + list_complete = TRUE; + if (list_complete) { + NTP_INSIST(0 == ri || have_addr_older); + } + if (mrulist_interrupted) { + printf("mrulist retrieval interrupted by operator.\n" + "Displaying partial client list.\n"); + fflush(stdout); + } + if (list_complete || mrulist_interrupted) { + fprintf(stderr, + "\rRetrieved %u unique MRU entries and %u updates.\n", + mru_count, mru_dupes); + fflush(stderr); + break; + } + if (time(NULL) >= next_report) { + next_report += MRU_REPORT_SECS; + fprintf(stderr, "\r%u (%u updates) ", mru_count, + mru_dupes); + fflush(stderr); + } + + /* + * Snooze for a bit between queries to let ntpd catch + * up with other duties. + */ +#ifdef SYS_WINNT + Sleep(sleep_msecs); +#elif !defined(HAVE_NANOSLEEP) + sleep((sleep_msecs / 1000) + 1); +#else + { + struct timespec interv = { 0, + 1000 * sleep_msecs }; + nanosleep(&interv, NULL); + } +#endif + /* + * If there were no errors, increase the number of rows + * to a maximum of 3 * MAXFRAGS (the most packets ntpq + * can handle in one response), on the assumption that + * no less than 3 rows fit in each packet, capped at + * our best guess at the server's row limit. + */ + if (!qres) { + if (cap_frags) { + frags = min(MAXFRAGS, frags + 1); + } else { + limit = min3(3 * MAXFRAGS, + ntpd_row_limit, + max(limit + 1, + limit * 33 / 32)); + } + } + /* + * prepare next query with as many address and last-seen + * timestamps as will fit in a single packet. + */ + req = req_buf; + req_end = req_buf + sizeof(req_buf); +#define REQ_ROOM (req_end - req) + snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce, + (cap_frags) + ? "frags" + : "limit", + (cap_frags) + ? frags + : limit, + parms); + req += strlen(req); + nonce_uses++; + if (nonce_uses >= 4) { + if (!fetch_nonce(nonce, sizeof(nonce))) + goto cleanup_return; + nonce_uses = 0; + } + + + for (ri = 0, recent = HEAD_DLIST(mru_list, mlink); + recent != NULL; + ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) { + + snprintf(buf, sizeof(buf), + ", addr.%d=%s, last.%d=0x%08x.%08x", + ri, sptoa(&recent->addr), ri, + recent->last.l_ui, recent->last.l_uf); + chars = strlen(buf); + if (REQ_ROOM - chars < 1) + break; + memcpy(req, buf, chars + 1); + req += chars; + } + } + + set_ctrl_c_hook(NULL); + c_mru_l_rc = TRUE; + goto retain_hash_table; + +cleanup_return: + free(hash_table); + hash_table = NULL; + +retain_hash_table: + if (mon != NULL) + free(mon); + + return c_mru_l_rc; +} + + +/* + * qcmp_mru_addr - sort MRU entries by remote address. + * + * All IPv4 addresses sort before any IPv6, addresses are sorted by + * value within address family. + */ +static int +qcmp_mru_addr( + const void *v1, + const void *v2 + ) +{ + const mru * const * ppm1 = v1; + const mru * const * ppm2 = v2; + const mru * pm1; + const mru * pm2; + u_short af1; + u_short af2; + size_t cmplen; + size_t addr_off; + + pm1 = *ppm1; + pm2 = *ppm2; + + af1 = AF(&pm1->addr); + af2 = AF(&pm2->addr); + + if (af1 != af2) + return (AF_INET == af1) + ? -1 + : 1; + + cmplen = SIZEOF_INADDR(af1); + addr_off = (AF_INET == af1) + ? offsetof(struct sockaddr_in, sin_addr) + : offsetof(struct sockaddr_in6, sin6_addr); + + return memcmp((const char *)&pm1->addr + addr_off, + (const char *)&pm2->addr + addr_off, + cmplen); +} + + +static int +qcmp_mru_r_addr( + const void *v1, + const void *v2 + ) +{ + return -qcmp_mru_addr(v1, v2); +} + + +/* + * qcmp_mru_count - sort MRU entries by times seen (hit count). + */ +static int +qcmp_mru_count( + const void *v1, + const void *v2 + ) +{ + const mru * const * ppm1 = v1; + const mru * const * ppm2 = v2; + const mru * pm1; + const mru * pm2; + + pm1 = *ppm1; + pm2 = *ppm2; + + return (pm1->count < pm2->count) + ? -1 + : ((pm1->count == pm2->count) + ? 0 + : 1); +} + + +static int +qcmp_mru_r_count( + const void *v1, + const void *v2 + ) +{ + return -qcmp_mru_count(v1, v2); +} + + +/* + * qcmp_mru_avgint - sort MRU entries by average interval. + */ +static int +qcmp_mru_avgint( + const void *v1, + const void *v2 + ) +{ + const mru * const * ppm1 = v1; + const mru * const * ppm2 = v2; + const mru * pm1; + const mru * pm2; + l_fp interval; + double avg1; + double avg2; + + pm1 = *ppm1; + pm2 = *ppm2; + + interval = pm1->last; + L_SUB(&interval, &pm1->first); + LFPTOD(&interval, avg1); + avg1 /= pm1->count; + + interval = pm2->last; + L_SUB(&interval, &pm2->first); + LFPTOD(&interval, avg2); + avg2 /= pm2->count; + + if (avg1 < avg2) + return -1; + else if (avg1 > avg2) + return 1; + + /* secondary sort on lstint - rarely tested */ + if (L_ISEQU(&pm1->last, &pm2->last)) + return 0; + else if (L_ISGEQ(&pm1->last, &pm2->last)) + return -1; + else + return 1; +} + + +static int +qcmp_mru_r_avgint( + const void *v1, + const void *v2 + ) +{ + return -qcmp_mru_avgint(v1, v2); +} + + +/* + * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most + * Recently Used (seen) remote address list from ntpd. + * + * Similar to ntpdc's monlist command, but not limited to a single + * request/response, and thereby not limited to a few hundred remote + * addresses. + * + * See ntpd/ntp_control.c read_mru_list() for comments on the way + * CTL_OP_READ_MRU is designed to be used. + * + * mrulist intentionally differs from monlist in the way the avgint + * column is calculated. monlist includes the time after the last + * packet from the client until the monlist query time in the average, + * while mrulist excludes it. That is, monlist's average interval grows + * over time for remote addresses not heard from in some time, while it + * remains unchanged in mrulist. This also affects the avgint value for + * entries representing a single packet, with identical first and last + * timestamps. mrulist shows 0 avgint, monlist shows a value identical + * to lstint. + */ +static void +mrulist( + struct parse * pcmd, + FILE * fp + ) +{ + const char mincount_eq[] = "mincount="; + const char resall_eq[] = "resall="; + const char resany_eq[] = "resany="; + const char maxlstint_eq[] = "maxlstint="; + const char laddr_eq[] = "laddr="; + const char sort_eq[] = "sort="; + mru_sort_order order; + size_t n; + char parms_buf[128]; + char buf[24]; + char *parms; + const char *arg; + size_t cb; + mru **sorted; + mru **ppentry; + mru *recent; + l_fp now; + l_fp interval; + double favgint; + double flstint; + int avgint; + int lstint; + size_t i; + + order = MRUSORT_DEF; + parms_buf[0] = '\0'; + parms = parms_buf; + for (i = 0; i < pcmd->nargs; i++) { + arg = pcmd->argval[i].string; + if (arg != NULL) { + cb = strlen(arg) + 1; + if ((!strncmp(resall_eq, arg, sizeof(resall_eq) + - 1) || !strncmp(resany_eq, arg, + sizeof(resany_eq) - 1) || !strncmp( + mincount_eq, arg, sizeof(mincount_eq) - 1) + || !strncmp(laddr_eq, arg, sizeof(laddr_eq) + - 1) || !strncmp(maxlstint_eq, arg, + sizeof(laddr_eq) - 1)) && parms + cb + 2 <= + parms_buf + sizeof(parms_buf)) { + /* these are passed intact to ntpd */ + memcpy(parms, ", ", 2); + parms += 2; + memcpy(parms, arg, cb); + parms += cb - 1; + } else if (!strncmp(sort_eq, arg, + sizeof(sort_eq) - 1)) { + arg += sizeof(sort_eq) - 1; + for (n = 0; + n < COUNTOF(mru_sort_keywords); + n++) + if (!strcmp(mru_sort_keywords[n], + arg)) + break; + if (n < COUNTOF(mru_sort_keywords)) + order = n; + } else if (!strcmp("limited", arg) || + !strcmp("kod", arg)) { + /* transform to resany=... */ + snprintf(buf, sizeof(buf), + ", resany=0x%x", + ('k' == arg[0]) + ? RES_KOD + : RES_LIMITED); + cb = 1 + strlen(buf); + if (parms + cb < + parms_buf + sizeof(parms_buf)) { + memcpy(parms, buf, cb); + parms += cb - 1; + } + } else + fprintf(stderr, + "ignoring unrecognized mrulist parameter: %s\n", + arg); + } + } + parms = parms_buf; + + if (!collect_mru_list(parms, &now)) + return; + + /* display the results */ + if (rawmode) + goto cleanup_return; + + /* construct an array of entry pointers in default order */ + sorted = eallocarray(mru_count, sizeof(*sorted)); + ppentry = sorted; + if (MRUSORT_R_DEF != order) { + ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) + NTP_INSIST(ppentry < sorted + mru_count); + *ppentry = recent; + ppentry++; + ITER_DLIST_END() + } else { + REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) + NTP_INSIST(ppentry < sorted + mru_count); + *ppentry = recent; + ppentry++; + REV_ITER_DLIST_END() + } + + if (ppentry - sorted != (int)mru_count) { + fprintf(stderr, + "mru_count %u should match MRU list depth %ld.\n", + mru_count, (long)(ppentry - sorted)); + free(sorted); + goto cleanup_return; + } + + /* re-sort sorted[] if not default or reverse default */ + if (MRUSORT_R_DEF < order) + qsort(sorted, mru_count, sizeof(sorted[0]), + mru_qcmp_table[order]); + + printf( "lstint avgint rstr r m v count rport remote address\n" + "==============================================================================\n"); + /* '=' x 78 */ + for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) { + recent = *ppentry; + interval = now; + L_SUB(&interval, &recent->last); + LFPTOD(&interval, flstint); + lstint = (int)(flstint + 0.5); + interval = recent->last; + L_SUB(&interval, &recent->first); + LFPTOD(&interval, favgint); + favgint /= recent->count; + avgint = (int)(favgint + 0.5); + fprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n", + lstint, avgint, recent->rs, + (RES_KOD & recent->rs) + ? 'K' + : (RES_LIMITED & recent->rs) + ? 'L' + : '.', + (int)recent->mode, (int)recent->ver, + recent->count, SRCPORT(&recent->addr), + nntohost(&recent->addr)); + if (showhostnames) + fflush(fp); + } + fflush(fp); + if (debug) { + fprintf(stderr, + "--- completed, freeing sorted[] pointers\n"); + fflush(stderr); + } + free(sorted); + +cleanup_return: + if (debug) { + fprintf(stderr, "... freeing MRU entries\n"); + fflush(stderr); + } + ITER_DLIST_BEGIN(mru_list, recent, mlink, mru) + free(recent); + ITER_DLIST_END() + if (debug) { + fprintf(stderr, "... freeing hash_table[]\n"); + fflush(stderr); + } + free(hash_table); + hash_table = NULL; + INIT_DLIST(mru_list, mlink); +} + + +/* + * validate_ifnum - helper for ifstats() + * + * Ensures rows are received in order and complete. + */ +static void +validate_ifnum( + FILE * fp, + u_int ifnum, + int * pfields, + ifstats_row * prow + ) +{ + if (prow->ifnum == ifnum) + return; + if (prow->ifnum + 1 <= ifnum) { + if (*pfields < IFSTATS_FIELDS) + fprintf(fp, "Warning: incomplete row with %d (of %d) fields", + *pfields, IFSTATS_FIELDS); + *pfields = 0; + prow->ifnum = ifnum; + return; + } + fprintf(stderr, + "received if index %u, have %d of %d fields for index %u, aborting.\n", + ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum); + exit(1); +} + + +/* + * another_ifstats_field - helper for ifstats() + * + * If all fields for the row have been received, print it. + */ +static void +another_ifstats_field( + int * pfields, + ifstats_row * prow, + FILE * fp + ) +{ + u_int ifnum; + + (*pfields)++; + /* we understand 12 tags */ + if (IFSTATS_FIELDS > *pfields) + return; + /* + " interface name send\n" + " # address/broadcast drop flag ttl mc received sent failed peers uptime\n" + "==============================================================================\n"); + */ + fprintf(fp, + "%3u %-24.24s %c %4x %3d %2d %6d %6d %6d %5d %8d\n" + " %s\n", + prow->ifnum, prow->name, + (prow->enabled) + ? '.' + : 'D', + prow->flags, prow->ttl, prow->mcast_count, + prow->received, prow->sent, prow->send_errors, + prow->peer_count, prow->uptime, sptoa(&prow->addr)); + if (!SOCK_UNSPEC(&prow->bcast)) + fprintf(fp, " %s\n", sptoa(&prow->bcast)); + ifnum = prow->ifnum; + ZERO(*prow); + prow->ifnum = ifnum; +} + + +/* + * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats. + */ +static void +ifstats( + struct parse * pcmd, + FILE * fp + ) +{ + const char addr_fmt[] = "addr.%u"; + const char bcast_fmt[] = "bcast.%u"; + const char en_fmt[] = "en.%u"; /* enabled */ + const char flags_fmt[] = "flags.%u"; + const char mc_fmt[] = "mc.%u"; /* mcast count */ + const char name_fmt[] = "name.%u"; + const char pc_fmt[] = "pc.%u"; /* peer count */ + const char rx_fmt[] = "rx.%u"; + const char tl_fmt[] = "tl.%u"; /* ttl */ + const char tx_fmt[] = "tx.%u"; + const char txerr_fmt[] = "txerr.%u"; + const char up_fmt[] = "up.%u"; /* uptime */ + const char * datap; + int qres; + int dsize; + u_short rstatus; + char * tag; + char * val; + int fields; + u_int ui; + ifstats_row row; + int comprende; + size_t len; + + qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus, + &dsize, &datap); + if (qres) /* message already displayed */ + return; + + fprintf(fp, + " interface name send\n" + " # address/broadcast drop flag ttl mc received sent failed peers uptime\n" + "==============================================================================\n"); + /* '=' x 78 */ + + ZERO(row); + fields = 0; + ui = 0; + while (nextvar(&dsize, &datap, &tag, &val)) { + if (debug > 1) + fprintf(stderr, "nextvar gave: %s = %s\n", tag, + (NULL == val) + ? "" + : val); + comprende = FALSE; + switch(tag[0]) { + + case 'a': + if (1 == sscanf(tag, addr_fmt, &ui) && + decodenetnum(val, &row.addr)) + comprende = TRUE; + break; + + case 'b': + if (1 == sscanf(tag, bcast_fmt, &ui) && + (NULL == val || + decodenetnum(val, &row.bcast))) + comprende = TRUE; + break; + + case 'e': + if (1 == sscanf(tag, en_fmt, &ui) && + 1 == sscanf(val, "%d", &row.enabled)) + comprende = TRUE; + break; + + case 'f': + if (1 == sscanf(tag, flags_fmt, &ui) && + 1 == sscanf(val, "0x%x", &row.flags)) + comprende = TRUE; + break; + + case 'm': + if (1 == sscanf(tag, mc_fmt, &ui) && + 1 == sscanf(val, "%d", &row.mcast_count)) + comprende = TRUE; + break; + + case 'n': + if (1 == sscanf(tag, name_fmt, &ui)) { + /* strip quotes */ + INSIST(val); + len = strlen(val); + if (len >= 2 && + len - 2 < sizeof(row.name)) { + len -= 2; + memcpy(row.name, val + 1, len); + row.name[len] = '\0'; + comprende = TRUE; + } + } + break; + + case 'p': + if (1 == sscanf(tag, pc_fmt, &ui) && + 1 == sscanf(val, "%d", &row.peer_count)) + comprende = TRUE; + break; + + case 'r': + if (1 == sscanf(tag, rx_fmt, &ui) && + 1 == sscanf(val, "%d", &row.received)) + comprende = TRUE; + break; + + case 't': + if (1 == sscanf(tag, tl_fmt, &ui) && + 1 == sscanf(val, "%d", &row.ttl)) + comprende = TRUE; + else if (1 == sscanf(tag, tx_fmt, &ui) && + 1 == sscanf(val, "%d", &row.sent)) + comprende = TRUE; + else if (1 == sscanf(tag, txerr_fmt, &ui) && + 1 == sscanf(val, "%d", &row.send_errors)) + comprende = TRUE; + break; + + case 'u': + if (1 == sscanf(tag, up_fmt, &ui) && + 1 == sscanf(val, "%d", &row.uptime)) + comprende = TRUE; + break; + } + + if (comprende) { + /* error out if rows out of order */ + validate_ifnum(fp, ui, &fields, &row); + /* if the row is complete, print it */ + another_ifstats_field(&fields, &row, fp); + } + } + if (fields != IFSTATS_FIELDS) + fprintf(fp, "Warning: incomplete row with %d (of %d) fields", + fields, IFSTATS_FIELDS); + + fflush(fp); +} + + +/* + * validate_reslist_idx - helper for reslist() + * + * Ensures rows are received in order and complete. + */ +static void +validate_reslist_idx( + FILE * fp, + u_int idx, + int * pfields, + reslist_row * prow + ) +{ + if (prow->idx == idx) + return; + if (prow->idx + 1 == idx) { + if (*pfields < RESLIST_FIELDS) + fprintf(fp, "Warning: incomplete row with %d (of %d) fields", + *pfields, RESLIST_FIELDS); + *pfields = 0; + prow->idx = idx; + return; + } + fprintf(stderr, + "received reslist index %u, have %d of %d fields for index %u, aborting.\n", + idx, *pfields, RESLIST_FIELDS, prow->idx); + exit(1); +} + + +/* + * another_reslist_field - helper for reslist() + * + * If all fields for the row have been received, print it. + */ +static void +another_reslist_field( + int * pfields, + reslist_row * prow, + FILE * fp + ) +{ + char addrmaskstr[128]; + int prefix; /* subnet mask as prefix bits count */ + u_int idx; + + (*pfields)++; + /* we understand 4 tags */ + if (RESLIST_FIELDS > *pfields) + return; + + prefix = sockaddr_masktoprefixlen(&prow->mask); + if (prefix >= 0) + snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d", + stoa(&prow->addr), prefix); + else + snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s", + stoa(&prow->addr), stoa(&prow->mask)); + + /* + " hits addr/prefix or addr mask\n" + " restrictions\n" + "==============================================================================\n"); + */ + fprintf(fp, + "%10lu %s\n" + " %s\n", + prow->hits, addrmaskstr, prow->flagstr); + idx = prow->idx; + ZERO(*prow); + prow->idx = idx; +} + + +/* + * reslist - ntpq -c reslist modeled on ntpdc -c reslist. + */ +static void +reslist( + struct parse * pcmd, + FILE * fp + ) +{ + const char addr_fmtu[] = "addr.%u"; + const char mask_fmtu[] = "mask.%u"; + const char hits_fmt[] = "hits.%u"; + const char flags_fmt[] = "flags.%u"; + const char qdata[] = "addr_restrictions"; + const int qdata_chars = COUNTOF(qdata) - 1; + const char * datap; + int qres; + int dsize; + u_short rstatus; + char * tag; + char * val; + int fields; + u_int ui; + reslist_row row; + int comprende; + size_t len; + + qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars, + qdata, &rstatus, &dsize, &datap); + if (qres) /* message already displayed */ + return; + + fprintf(fp, + " hits addr/prefix or addr mask\n" + " restrictions\n" + "==============================================================================\n"); + /* '=' x 78 */ + + ZERO(row); + fields = 0; + ui = 0; + while (nextvar(&dsize, &datap, &tag, &val)) { + if (debug > 1) + fprintf(stderr, "nextvar gave: %s = %s\n", tag, + (NULL == val) + ? "" + : val); + comprende = FALSE; + switch(tag[0]) { + + case 'a': + if (1 == sscanf(tag, addr_fmtu, &ui) && + decodenetnum(val, &row.addr)) + comprende = TRUE; + break; + + case 'f': + if (1 == sscanf(tag, flags_fmt, &ui)) { + if (NULL == val) { + row.flagstr[0] = '\0'; + comprende = TRUE; + } else { + len = strlen(val); + memcpy(row.flagstr, val, len); + row.flagstr[len] = '\0'; + comprende = TRUE; + } + } + break; + + case 'h': + if (1 == sscanf(tag, hits_fmt, &ui) && + 1 == sscanf(val, "%lu", &row.hits)) + comprende = TRUE; + break; + + case 'm': + if (1 == sscanf(tag, mask_fmtu, &ui) && + decodenetnum(val, &row.mask)) + comprende = TRUE; + break; + } + + if (comprende) { + /* error out if rows out of order */ + validate_reslist_idx(fp, ui, &fields, &row); + /* if the row is complete, print it */ + another_reslist_field(&fields, &row, fp); + } + } + if (fields != RESLIST_FIELDS) + fprintf(fp, "Warning: incomplete row with %d (of %d) fields", + fields, RESLIST_FIELDS); + + fflush(fp); +} + + +/* + * collect_display_vdc + */ +static void +collect_display_vdc( + associd_t as, + vdc * table, + int decodestatus, + FILE * fp + ) +{ + static const char * const suf[2] = { "adr", "port" }; + static const char * const leapbits[4] = { "00", "01", + "10", "11" }; + struct varlist vl[MAXLIST]; + char tagbuf[32]; + vdc *pvdc; + u_short rstatus; + int rsize; + const char *rdata; + int qres; + char *tag; + char *val; + u_int n; + size_t len; + int match; + u_long ul; + int vtype; + + ZERO(vl); + for (pvdc = table; pvdc->tag != NULL; pvdc++) { + ZERO(pvdc->v); + if (NTP_ADD != pvdc->type) { + doaddvlist(vl, pvdc->tag); + } else { + for (n = 0; n < COUNTOF(suf); n++) { + snprintf(tagbuf, sizeof(tagbuf), "%s%s", + pvdc->tag, suf[n]); + doaddvlist(vl, tagbuf); + } + } + } + qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize, + &rdata); + doclearvlist(vl); + if (qres) + return; /* error msg already displayed */ + + /* + * iterate over the response variables filling vdc_table with + * the retrieved values. + */ + while (nextvar(&rsize, &rdata, &tag, &val)) { + if (NULL == val) + continue; + n = 0; + for (pvdc = table; pvdc->tag != NULL; pvdc++) { + len = strlen(pvdc->tag); + if (strncmp(tag, pvdc->tag, len)) + continue; + if (NTP_ADD != pvdc->type) { + if ('\0' != tag[len]) + continue; + break; + } + match = FALSE; + for (n = 0; n < COUNTOF(suf); n++) { + if (strcmp(tag + len, suf[n])) + continue; + match = TRUE; + break; + } + if (match) + break; + } + if (NULL == pvdc->tag) + continue; + switch (pvdc->type) { + + case NTP_STR: + /* strip surrounding double quotes */ + if ('"' == val[0]) { + len = strlen(val); + if (len > 0 && '"' == val[len - 1]) { + val[len - 1] = '\0'; + val++; + } + } + /* fallthru */ + case NTP_MODE: /* fallthru */ + case NTP_2BIT: + pvdc->v.str = estrdup(val); + break; + + case NTP_LFP: + decodets(val, &pvdc->v.lfp); + break; + + case NTP_ADP: + if (!decodenetnum(val, &pvdc->v.sau)) + fprintf(stderr, "malformed %s=%s\n", + pvdc->tag, val); + break; + + case NTP_ADD: + if (0 == n) { /* adr */ + if (!decodenetnum(val, &pvdc->v.sau)) + fprintf(stderr, + "malformed %s=%s\n", + pvdc->tag, val); + } else { /* port */ + if (atouint(val, &ul)) + SET_PORT(&pvdc->v.sau, + (u_short)ul); + } + break; + } + } + + /* and display */ + if (decodestatus) { + vtype = (0 == as) + ? TYPE_SYS + : TYPE_PEER; + fprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus, + statustoa(vtype, rstatus)); + } + + for (pvdc = table; pvdc->tag != NULL; pvdc++) { + switch (pvdc->type) { + + case NTP_STR: + if (pvdc->v.str != NULL) { + fprintf(fp, "%s %s\n", pvdc->display, + pvdc->v.str); + free(pvdc->v.str); + pvdc->v.str = NULL; + } + break; + + case NTP_ADD: /* fallthru */ + case NTP_ADP: + fprintf(fp, "%s %s\n", pvdc->display, + nntohostp(&pvdc->v.sau)); + break; + + case NTP_LFP: + fprintf(fp, "%s %s\n", pvdc->display, + prettydate(&pvdc->v.lfp)); + break; + + case NTP_MODE: + atouint(pvdc->v.str, &ul); + fprintf(fp, "%s %s\n", pvdc->display, + modetoa((int)ul)); + break; + + case NTP_2BIT: + atouint(pvdc->v.str, &ul); + fprintf(fp, "%s %s\n", pvdc->display, + leapbits[ul & 0x3]); + break; + + default: + fprintf(stderr, "unexpected vdc type %d for %s\n", + pvdc->type, pvdc->tag); + break; + } + } +} + + +/* + * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats + */ +static void +sysstats( + struct parse *pcmd, + FILE *fp + ) +{ + static vdc sysstats_vdc[] = { + VDC_INIT("ss_uptime", "uptime: ", NTP_STR), + VDC_INIT("ss_reset", "sysstats reset: ", NTP_STR), + VDC_INIT("ss_received", "packets received: ", NTP_STR), + VDC_INIT("ss_thisver", "current version: ", NTP_STR), + VDC_INIT("ss_oldver", "older version: ", NTP_STR), + VDC_INIT("ss_badformat", "bad length or format: ", NTP_STR), + VDC_INIT("ss_badauth", "authentication failed:", NTP_STR), + VDC_INIT("ss_declined", "declined: ", NTP_STR), + VDC_INIT("ss_restricted", "restricted: ", NTP_STR), + VDC_INIT("ss_limited", "rate limited: ", NTP_STR), + VDC_INIT("ss_kodsent", "KoD responses: ", NTP_STR), + VDC_INIT("ss_processed", "processed for time: ", NTP_STR), + VDC_INIT(NULL, NULL, 0) + }; + + collect_display_vdc(0, sysstats_vdc, FALSE, fp); +} + + +/* + * sysinfo - modeled on ntpdc's sysinfo + */ +static void +sysinfo( + struct parse *pcmd, + FILE *fp + ) +{ + static vdc sysinfo_vdc[] = { + VDC_INIT("peeradr", "system peer: ", NTP_ADP), + VDC_INIT("peermode", "system peer mode: ", NTP_MODE), + VDC_INIT("leap", "leap indicator: ", NTP_2BIT), + VDC_INIT("stratum", "stratum: ", NTP_STR), + VDC_INIT("precision", "log2 precision: ", NTP_STR), + VDC_INIT("rootdelay", "root delay: ", NTP_STR), + VDC_INIT("rootdisp", "root dispersion: ", NTP_STR), + VDC_INIT("refid", "reference ID: ", NTP_STR), + VDC_INIT("reftime", "reference time: ", NTP_LFP), + VDC_INIT("sys_jitter", "system jitter: ", NTP_STR), + VDC_INIT("clk_jitter", "clock jitter: ", NTP_STR), + VDC_INIT("clk_wander", "clock wander: ", NTP_STR), + VDC_INIT("bcastdelay", "broadcast delay: ", NTP_STR), + VDC_INIT("authdelay", "symm. auth. delay:", NTP_STR), + VDC_INIT(NULL, NULL, 0) + }; + + collect_display_vdc(0, sysinfo_vdc, TRUE, fp); +} + + +/* + * kerninfo - modeled on ntpdc's kerninfo + */ +static void +kerninfo( + struct parse *pcmd, + FILE *fp + ) +{ + static vdc kerninfo_vdc[] = { + VDC_INIT("koffset", "pll offset: ", NTP_STR), + VDC_INIT("kfreq", "pll frequency: ", NTP_STR), + VDC_INIT("kmaxerr", "maximum error: ", NTP_STR), + VDC_INIT("kesterr", "estimated error: ", NTP_STR), + VDC_INIT("kstflags", "kernel status: ", NTP_STR), + VDC_INIT("ktimeconst", "pll time constant: ", NTP_STR), + VDC_INIT("kprecis", "precision: ", NTP_STR), + VDC_INIT("kfreqtol", "frequency tolerance: ", NTP_STR), + VDC_INIT("kppsfreq", "pps frequency: ", NTP_STR), + VDC_INIT("kppsstab", "pps stability: ", NTP_STR), + VDC_INIT("kppsjitter", "pps jitter: ", NTP_STR), + VDC_INIT("kppscalibdur", "calibration interval ", NTP_STR), + VDC_INIT("kppscalibs", "calibration cycles: ", NTP_STR), + VDC_INIT("kppsjitexc", "jitter exceeded: ", NTP_STR), + VDC_INIT("kppsstbexc", "stability exceeded: ", NTP_STR), + VDC_INIT("kppscaliberrs", "calibration errors: ", NTP_STR), + VDC_INIT(NULL, NULL, 0) + }; + + collect_display_vdc(0, kerninfo_vdc, TRUE, fp); +} + + +/* + * monstats - implements ntpq -c monstats + */ +static void +monstats( + struct parse *pcmd, + FILE *fp + ) +{ + static vdc monstats_vdc[] = { + VDC_INIT("mru_enabled", "enabled: ", NTP_STR), + VDC_INIT("mru_depth", "addresses: ", NTP_STR), + VDC_INIT("mru_deepest", "peak addresses: ", NTP_STR), + VDC_INIT("mru_maxdepth", "maximum addresses: ", NTP_STR), + VDC_INIT("mru_mindepth", "reclaim above count:", NTP_STR), + VDC_INIT("mru_maxage", "reclaim older than: ", NTP_STR), + VDC_INIT("mru_mem", "kilobytes: ", NTP_STR), + VDC_INIT("mru_maxmem", "maximum kilobytes: ", NTP_STR), + VDC_INIT(NULL, NULL, 0) + }; + + collect_display_vdc(0, monstats_vdc, FALSE, fp); +} + + +/* + * iostats - ntpq -c iostats - network input and output counters + */ +static void +iostats( + struct parse *pcmd, + FILE *fp + ) +{ + static vdc iostats_vdc[] = { + VDC_INIT("iostats_reset", "time since reset: ", NTP_STR), + VDC_INIT("total_rbuf", "receive buffers: ", NTP_STR), + VDC_INIT("free_rbuf", "free receive buffers: ", NTP_STR), + VDC_INIT("used_rbuf", "used receive buffers: ", NTP_STR), + VDC_INIT("rbuf_lowater", "low water refills: ", NTP_STR), + VDC_INIT("io_dropped", "dropped packets: ", NTP_STR), + VDC_INIT("io_ignored", "ignored packets: ", NTP_STR), + VDC_INIT("io_received", "received packets: ", NTP_STR), + VDC_INIT("io_sent", "packets sent: ", NTP_STR), + VDC_INIT("io_sendfailed", "packet send failures: ", NTP_STR), + VDC_INIT("io_wakeups", "input wakeups: ", NTP_STR), + VDC_INIT("io_goodwakeups", "useful input wakeups: ", NTP_STR), + VDC_INIT(NULL, NULL, 0) + }; + + collect_display_vdc(0, iostats_vdc, FALSE, fp); +} + + +/* + * timerstats - ntpq -c timerstats - interval timer counters + */ +static void +timerstats( + struct parse *pcmd, + FILE *fp + ) +{ + static vdc timerstats_vdc[] = { + VDC_INIT("timerstats_reset", "time since reset: ", NTP_STR), + VDC_INIT("timer_overruns", "timer overruns: ", NTP_STR), + VDC_INIT("timer_xmts", "calls to transmit: ", NTP_STR), + VDC_INIT(NULL, NULL, 0) + }; + + collect_display_vdc(0, timerstats_vdc, FALSE, fp); +} + + +/* + * authinfo - implements ntpq -c authinfo + */ +static void +authinfo( + struct parse *pcmd, + FILE *fp + ) +{ + static vdc authinfo_vdc[] = { + VDC_INIT("authreset", "time since reset:", NTP_STR), + VDC_INIT("authkeys", "stored keys: ", NTP_STR), + VDC_INIT("authfreek", "free keys: ", NTP_STR), + VDC_INIT("authklookups", "key lookups: ", NTP_STR), + VDC_INIT("authknotfound", "keys not found: ", NTP_STR), + VDC_INIT("authkuncached", "uncached keys: ", NTP_STR), + VDC_INIT("authkexpired", "expired keys: ", NTP_STR), + VDC_INIT("authencrypts", "encryptions: ", NTP_STR), + VDC_INIT("authdecrypts", "decryptions: ", NTP_STR), + VDC_INIT(NULL, NULL, 0) + }; + + collect_display_vdc(0, authinfo_vdc, FALSE, fp); +} + + +/* + * pstats - show statistics for a peer + */ +static void +pstats( + struct parse *pcmd, + FILE *fp + ) +{ + static vdc pstats_vdc[] = { + VDC_INIT("src", "remote host: ", NTP_ADD), + VDC_INIT("dst", "local address: ", NTP_ADD), + VDC_INIT("timerec", "time last received: ", NTP_STR), + VDC_INIT("timer", "time until next send:", NTP_STR), + VDC_INIT("timereach", "reachability change: ", NTP_STR), + VDC_INIT("sent", "packets sent: ", NTP_STR), + VDC_INIT("received", "packets received: ", NTP_STR), + VDC_INIT("badauth", "bad authentication: ", NTP_STR), + VDC_INIT("bogusorg", "bogus origin: ", NTP_STR), + VDC_INIT("oldpkt", "duplicate: ", NTP_STR), + VDC_INIT("seldisp", "bad dispersion: ", NTP_STR), + VDC_INIT("selbroken", "bad reference time: ", NTP_STR), + VDC_INIT("candidate", "candidate order: ", NTP_STR), + VDC_INIT(NULL, NULL, 0) + }; + associd_t associd; + + associd = checkassocid(pcmd->argval[0].uval); + if (0 == associd) + return; + + collect_display_vdc(associd, pstats_vdc, TRUE, fp); +} |