From bdab5265fcbf3f472545073a23f8999749a9f2b9 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 2 Dec 2014 09:01:21 +0000 Subject: Imported from /home/lorry/working-area/delta_ntp/ntp-dev-4.2.7p482.tar.gz. --- ntpq/ntpq-subs.c | 3875 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3875 insertions(+) create mode 100644 ntpq/ntpq-subs.c (limited to 'ntpq/ntpq-subs.c') diff --git a/ntpq/ntpq-subs.c b/ntpq/ntpq-subs.c new file mode 100644 index 0000000..5460f80 --- /dev/null +++ b/ntpq/ntpq-subs.c @@ -0,0 +1,3875 @@ +/* + * ntpq-subs.c - subroutines which are called to perform ntpq commands. + */ +#include +#include +#include +#include +#include + +#include "ntpq.h" +#include "ntpq-opts.h" + +extern char currenthost[]; +extern int currenthostisnum; +size_t maxhostlen; + +/* + * Declarations for command handlers in here + */ +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 (struct parse *, FILE *); +#endif /* UNUSED */ + +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 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" }, + { "passociations", passociations, { NO, NO, NO, NO }, + { "", "", "", "" }, + "print list of associations returned by last associations command" }, + { "lassociations", lassociations, { NO, NO, NO, NO }, + { "", "", "", "" }, + "print list of associations including all client information" }, + { "lpassociations", lpassociations, { NO, NO, NO, NO }, + { "", "", "", "" }, + "print last obtained list of associations, including client information" }, + { "addvars", addvars, { NTP_STR, NO, NO, NO }, + { "name[=value][,...]", "", "", "" }, + "add variables to the variable list or change their values" }, + { "rmvars", rmvars, { NTP_STR, NO, NO, NO }, + { "name[,...]", "", "", "" }, + "remove variables from the variable list" }, + { "clearvars", clearvars, { NO, NO, NO, NO }, + { "", "", "", "" }, + "remove all variables from the variable list" }, + { "showvars", showvars, { NO, NO, NO, NO }, + { "", "", "", "" }, + "print variables on the variable list" }, + { "readlist", readlist, { OPT|NTP_UINT, NO, NO, NO }, + { "assocID", "", "", "" }, + "read the system or peer variables included in the variable list" }, + { "rl", readlist, { OPT|NTP_UINT, NO, NO, NO }, + { "assocID", "", "", "" }, + "read the system or peer variables included in the variable list" }, + { "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, OPT|NTP_STR, OPT|NTP_STR, }, + { "assocID", "varname1", "varname2", "varname3" }, + "read system or peer variables" }, + { "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 }, + { "assocIDlow", "assocIDhigh", "", "" }, + "read the peer variables in the variable list for multiple peers" }, + { "mrl", mreadlist, { NTP_UINT, NTP_UINT, NO, NO }, + { "assocIDlow", "assocIDhigh", "", "" }, + "read the peer variables in the variable list for multiple peers" }, + { "mreadvar", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO }, + { "assocIDlow", "assocIDhigh", "name=value[,...]", "" }, + "read peer variables from multiple peers" }, + { "mrv", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO }, + { "assocIDlow", "assocIDhigh", "name=value[,...]", "" }, + "read peer variables from multiple peers" }, + { "clocklist", clocklist, { OPT|NTP_UINT, NO, NO, NO }, + { "assocID", "", "", "" }, + "read the clock variables included in the variable list" }, + { "cl", clocklist, { OPT|NTP_UINT, NO, NO, NO }, + { "assocID", "", "", "" }, + "read the clock variables included in the variable list" }, + { "clockvar", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO }, + { "assocID", "name=value[,...]", "", "" }, + "read clock variables" }, + { "cv", clockvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO }, + { "assocID", "name=value[,...]", "", "" }, + "read clock variables" }, + { "pstats", pstats, { NTP_UINT, NO, NO, NO }, + { "assocID", "", "", "" }, + "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]" }, + { "lpeers", lpeers, { OPT|IP_VERSION, NO, NO, NO }, + { "-4|-6", "", "", "" }, + "obtain and print a list of all peers and clients [IP version]" }, + { "opeers", opeers, { OPT|IP_VERSION, NO, NO, NO }, + { "-4|-6", "", "", "" }, + "print peer list the old way, with dstadr shown rather than refid [IP version]" }, + { "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 }, + { "", "", "", "" }, + "send a remote configuration command to ntpd" }, + { "config-from-file", config_from_file, { NTP_STR, NO, NO, NO }, + { "", "", "", "" }, + "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", "", "", "" }, "" } +}; + + +/* + * Variable list data space + */ +#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_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_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; + char *value; +} 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 u_char pktversion; + +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; + +/* + * 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); + +/* + * static globals + */ +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(). + */ +const static 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 associd_t +checkassocid( + u_int32 value + ) +{ + 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 associd; +} + + +/* + * 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( + struct varlist *list, + char *name + ) +{ + struct varlist *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 NULL; +} + + +/* + * doaddvlist - add variable(s) to the variable list + */ +static void +doaddvlist( + struct varlist *vlist, + const char *vars + ) +{ + struct varlist *vl; + int len; + char *name; + char *value; + + len = strlen(vars); + while (nextvar(&len, &vars, &name, &value)) { + vl = findlistvar(vlist, name); + if (NULL == vl) { + fprintf(stderr, "Variable list full\n"); + return; + } + + if (NULL == vl->name) { + vl->name = estrdup(name); + } else if (vl->value != NULL) { + free(vl->value); + vl->value = NULL; + } + + if (value != NULL) + vl->value = estrdup(value); + } +} + + +/* + * dormvlist - remove variable(s) from the variable list + */ +static void +dormvlist( + struct varlist *vlist, + const char *vars + ) +{ + struct varlist *vl; + int len; + char *name; + char *value; + + len = strlen(vars); + while (nextvar(&len, &vars, &name, &value)) { + vl = findlistvar(vlist, name); + if (vl == 0 || vl->name == 0) { + (void) fprintf(stderr, "Variable `%s' not found\n", + name); + } else { + free((void *)vl->name); + if (vl->value != 0) + free(vl->value); + for ( ; (vl+1) < (g_varlist + MAXLIST) + && (vl+1)->name != 0; vl++) { + vl->name = (vl+1)->name; + vl->value = (vl+1)->value; + } + vl->name = vl->value = 0; + } + } +} + + +/* + * doclearvlist - clear a variable list + */ +static void +doclearvlist( + struct varlist *vlist + ) +{ + register struct varlist *vl; + + for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) { + free((void *)vl->name); + vl->name = 0; + if (vl->value != 0) { + free(vl->value); + vl->value = 0; + } + } +} + + +/* + * makequerydata - form a data buffer to be included with a query + */ +static void +makequerydata( + struct varlist *vlist, + int *datalen, + char *data + ) +{ + register struct varlist *vl; + register char *cp, *cpend; + register int namelen, valuelen; + register int totallen; + + cp = data; + cpend = data + *datalen; + + for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) { + namelen = strlen(vl->name); + if (vl->value == 0) + valuelen = 0; + else + valuelen = strlen(vl->value); + totallen = namelen + valuelen + (valuelen != 0) + (cp != data); + if (cp + totallen > cpend) { + fprintf(stderr, + "***Ignoring variables starting with `%s'\n", + vl->name); + break; + } + + if (cp != data) + *cp++ = ','; + memcpy(cp, vl->name, (size_t)namelen); + cp += namelen; + if (valuelen != 0) { + *cp++ = '='; + memcpy(cp, vl->value, (size_t)valuelen); + cp += valuelen; + } + } + *datalen = cp - data; +} + + +/* + * doquerylist - send a message including variables in a list + */ +static int +doquerylist( + struct varlist *vlist, + int op, + associd_t associd, + int auth, + u_short *rstatus, + int *dsize, + const char **datap + ) +{ + char data[CTL_MAX_DATA_LEN]; + int datalen; + + datalen = sizeof(data); + makequerydata(vlist, &datalen, data); + + return doquery(op, associd, auth, datalen, data, rstatus, dsize, + datap); +} + + +/* + * doprintvlist - print the variables on a list + */ +static void +doprintvlist( + struct varlist *vlist, + FILE *fp + ) +{ + size_t n; + + 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 + */ +/*ARGSUSED*/ +static void +addvars( + struct parse *pcmd, + FILE *fp + ) +{ + doaddvlist(g_varlist, pcmd->argval[0].string); +} + + +/* + * rmvars - remove variables from the variable list + */ +/*ARGSUSED*/ +static void +rmvars( + struct parse *pcmd, + FILE *fp + ) +{ + dormvlist(g_varlist, pcmd->argval[0].string); +} + + +/* + * clearvars - clear the variable list + */ +/*ARGSUSED*/ +static void +clearvars( + struct parse *pcmd, + FILE *fp + ) +{ + doclearvlist(g_varlist); +} + + +/* + * showvars - show variables on the variable list + */ +/*ARGSUSED*/ +static void +showvars( + struct parse *pcmd, + FILE *fp + ) +{ + doprintvlist(g_varlist, fp); +} + + +/* + * dolist - send a request with the given list of variables + */ +static int +dolist( + struct varlist *vlist, + associd_t associd, + int op, + int type, + FILE *fp + ) +{ + 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); + + if (res != 0) + return 0; + + if (numhosts > 1) + fprintf(fp, "server=%s ", currenthost); + if (dsize == 0) { + if (associd == 0) + fprintf(fp, "No system%s variables returned\n", + (type == TYPE_CLOCK) ? " clock" : ""); + else + fprintf(fp, + "No information returned for%s association %u\n", + (type == TYPE_CLOCK) ? " clock" : "", + associd); + return 1; + } + + if (!quiet) + fprintf(fp, "associd=%u ", associd); + printvars(dsize, datap, (int)rstatus, type, quiet, fp); + return 1; +} + + +/* + * readlist - send a read variables request with the variables on the list + */ +static void +readlist( + struct parse *pcmd, + FILE *fp + ) +{ + associd_t associd; + int type; + + if (pcmd->nargs == 0) { + associd = 0; + } else { + /* HMS: I think we want the u_int32 target here, not the u_long */ + if (pcmd->argval[0].uval == 0) + associd = 0; + else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) + return; + } + + type = (0 == associd) + ? TYPE_SYS + : TYPE_PEER; + dolist(g_varlist, associd, CTL_OP_READVAR, type, fp); +} + + +/* + * writelist - send a write variables request with the variables on the list + */ +static void +writelist( + struct parse *pcmd, + FILE *fp + ) +{ + const char *datap; + int res; + associd_t associd; + int dsize; + u_short rstatus; + + if (pcmd->nargs == 0) { + associd = 0; + } else { + /* HMS: Do we really want uval here? */ + if (pcmd->argval[0].uval == 0) + associd = 0; + else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) + return; + } + + res = doquerylist(g_varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus, + &dsize, &datap); + + if (res != 0) + return; + + if (numhosts > 1) + (void) fprintf(fp, "server=%s ", currenthost); + if (dsize == 0) + (void) fprintf(fp, "done! (no data returned)\n"); + else { + (void) fprintf(fp,"associd=%u ", associd); + printvars(dsize, datap, (int)rstatus, + (associd != 0) ? TYPE_PEER : TYPE_SYS, 0, fp); + } + return; +} + + +/* + * readvar - send a read variables request with the specified variables + */ +static void +readvar( + struct parse *pcmd, + FILE *fp + ) +{ + 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) + associd = 0; + else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) + return; + + ZERO(tmplist); + if (pcmd->nargs > 1) { + tmpcount = pcmd->nargs - 1; + for (u = 0; u < tmpcount; u++) + doaddvlist(tmplist, pcmd->argval[1 + u].string); + } + + type = (0 == associd) + ? TYPE_SYS + : TYPE_PEER; + dolist(tmplist, associd, CTL_OP_READVAR, type, fp); + + doclearvlist(tmplist); +} + + +/* + * writevar - send a write variables request with the specified variables + */ +static void +writevar( + struct parse *pcmd, + FILE *fp + ) +{ + const char *datap; + int res; + associd_t associd; + int type; + int dsize; + u_short rstatus; + struct varlist tmplist[MAXLIST]; + + /* HMS: uval? */ + if (pcmd->argval[0].uval == 0) + associd = 0; + else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) + return; + + ZERO(tmplist); + doaddvlist(tmplist, pcmd->argval[1].string); + + res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus, + &dsize, &datap); + + doclearvlist(tmplist); + + if (res != 0) + return; + + if (numhosts > 1) + fprintf(fp, "server=%s ", currenthost); + if (dsize == 0) + fprintf(fp, "done! (no data returned)\n"); + else { + fprintf(fp,"associd=%u ", associd); + type = (0 == associd) + ? TYPE_SYS + : TYPE_PEER; + printvars(dsize, datap, (int)rstatus, type, 0, fp); + } + return; +} + + +/* + * clocklist - send a clock variables request with the variables on the list + */ +static void +clocklist( + struct parse *pcmd, + FILE *fp + ) +{ + associd_t associd; + + /* HMS: uval? */ + if (pcmd->nargs == 0) { + associd = 0; + } else { + if (pcmd->argval[0].uval == 0) + associd = 0; + else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) + return; + } + + dolist(g_varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp); +} + + +/* + * clockvar - send a clock variables request with the specified variables + */ +static void +clockvar( + struct parse *pcmd, + FILE *fp + ) +{ + associd_t associd; + struct varlist tmplist[MAXLIST]; + + /* HMS: uval? */ + if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0) + associd = 0; + else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) + return; + + ZERO(tmplist); + if (pcmd->nargs >= 2) + doaddvlist(tmplist, pcmd->argval[1].string); + + dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp); + + doclearvlist(tmplist); +} + + +/* + * findassidrange - verify a range of association ID's + */ +static int +findassidrange( + u_int32 assid1, + u_int32 assid2, + int * from, + int * to, + FILE * fp + ) +{ + associd_t assids[2]; + int ind[COUNTOF(assids)]; + u_int i; + size_t a; + + + if (0 == numassoc) + dogetassoc(fp); + + 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 (ind[0] < ind[1]) { + *from = ind[0]; + *to = ind[1]; + } else { + *to = ind[0]; + *from = ind[1]; + } + return 1; +} + + + +/* + * mreadlist - send a read variables request for multiple associations + */ +static void +mreadlist( + struct parse *pcmd, + FILE *fp + ) +{ + int i; + int from; + int to; + + if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval, + &from, &to, fp)) + return; + + for (i = from; i <= to; i++) { + if (i != from) + fprintf(fp, "\n"); + if (!dolist(g_varlist, assoc_cache[i].assid, + CTL_OP_READVAR, TYPE_PEER, fp)) + return; + } + return; +} + + +/* + * mreadvar - send a read variables request for multiple associations + */ +static void +mreadvar( + struct parse *pcmd, + FILE *fp + ) +{ + int i; + int from; + int to; + struct varlist tmplist[MAXLIST]; + struct varlist *pvars; + + if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval, + &from, &to, fp)) + return; + + 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 (!dolist(pvars, assoc_cache[i].assid, CTL_OP_READVAR, + TYPE_PEER, fp)) + break; + } + + if (pvars == tmplist) + doclearvlist(tmplist); + + return; +} + + +/* + * dogetassoc - query the host for its list of associations + */ +int +dogetassoc( + FILE *fp + ) +{ + const char *datap; + const u_short *pus; + int res; + int dsize; + u_short rstatus; + + res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus, + &dsize, &datap); + + if (res != 0) + return 0; + + if (dsize == 0) { + if (numhosts > 1) + fprintf(fp, "server=%s ", currenthost); + fprintf(fp, "No association ID's returned\n"); + return 0; + } + + if (dsize & 0x3) { + if (numhosts > 1) + 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) { + 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; +} + + +/* + * printassoc - print the current list of associations + */ +static void +printassoc( + int showall, + FILE *fp + ) +{ + register char *bp; + u_int i; + u_char statval; + int event; + u_long event_count; + const char *conf; + const char *reach; + const char *auth; + const char *condition = ""; + const char *last_event; + char buf[128]; + + if (numassoc == 0) { + (void) fprintf(fp, "No association ID's in list\n"); + return; + } + + /* + * Output a header + */ + (void) fprintf(fp, + "\nind assid status conf reach auth condition last_event cnt\n"); + (void) fprintf(fp, + "===========================================================\n"); + for (i = 0; i < numassoc; i++) { + statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status); + if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH))) + continue; + event = CTL_PEER_EVENT(assoc_cache[i].status); + event_count = CTL_PEER_NEVNT(assoc_cache[i].status); + if (statval & CTL_PST_CONFIG) + conf = "yes"; + else + conf = "no"; + 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 { + auth = "none"; + } + } + 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 = "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)) + condition = "hi_disp"; + 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; + } + } + switch (PEER_EVENT|event) { + + case PEVNT_MOBIL: + last_event = "mobilize"; + break; + + case PEVNT_DEMOBIL: + last_event = "demobilize"; + break; + + case PEVNT_REACH: + last_event = "reachable"; + break; + + case PEVNT_UNREACH: + last_event = "unreachable"; + break; + + case PEVNT_RESTART: + last_event = "restart"; + break; + + 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; + } + 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 + */ +/*ARGSUSED*/ +static void +associations( + struct parse *pcmd, + FILE *fp + ) +{ + if (dogetassoc(fp)) + printassoc(0, fp); +} + + +/* + * lassociations - get, record and print a long list of associations + */ +/*ARGSUSED*/ +static void +lassociations( + struct parse *pcmd, + FILE *fp + ) +{ + if (dogetassoc(fp)) + printassoc(1, fp); +} + + +/* + * passociations - print the association list + */ +/*ARGSUSED*/ +static void +passociations( + struct parse *pcmd, + FILE *fp + ) +{ + printassoc(0, fp); +} + + +/* + * lpassociations - print the long association list + */ +/*ARGSUSED*/ +static void +lpassociations( + struct parse *pcmd, + FILE *fp + ) +{ + printassoc(1, fp); +} + + +/* + * saveconfig - dump ntp server configuration to server file + */ +static void +saveconfig( + struct parse *pcmd, + FILE *fp + ) +{ + const char *datap; + int res; + int dsize; + u_short rstatus; + + 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 (res != 0) + return; + + if (0 == dsize) + fprintf(fp, "(no response message, curiously)"); + else + fprintf(fp, "%.*s", dsize, datap); +} + + +#ifdef UNUSED +/* + * radiostatus - print the radio status returned by the server + */ +/*ARGSUSED*/ +static void +radiostatus( + struct parse *pcmd, + FILE *fp + ) +{ + 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) + return; + + if (numhosts > 1) + (void) fprintf(fp, "server=%s ", currenthost); + if (dsize == 0) { + (void) fprintf(fp, "No radio status string returned\n"); + return; + } + + asciize(dsize, datap, fp); +} +#endif /* UNUSED */ + +/* + * when - print how long its been since his last packet arrived + */ +static long +when( + l_fp *ts, + l_fp *rec, + l_fp *reftime + ) +{ + l_fp *lasttime; + + if (rec->l_ui != 0) + lasttime = rec; + else if (reftime->l_ui != 0) + lasttime = reftime; + else + return 0; + + return (ts->l_ui - lasttime->l_ui); +} + + +/* + * Pretty-print an interval into the given buffer, in a human-friendly format. + */ +static char * +prettyinterval( + char *buf, + size_t cb, + long diff + ) +{ + if (diff <= 0) { + buf[0] = '-'; + buf[1] = 0; + return buf; + } + + if (diff <= 2048) { + snprintf(buf, cb, "%ld", diff); + return buf; + } + + diff = (diff + 29) / 60; + if (diff <= 300) { + snprintf(buf, cb, "%ldm", diff); + return buf; + } + + diff = (diff + 29) / 60; + if (diff <= 96) { + snprintf(buf, cb, "%ldh", diff); + return buf; + } + + diff = (diff + 11) / 24; + snprintf(buf, cb, "%ldd", diff); + return buf; +} + +static char +decodeaddrtype( + sockaddr_u *sock + ) +{ + char ch = '-'; + u_int32 dummy; + + switch(AF(sock)) { + case AF_INET: + dummy = SRCADR(sock); + ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' : + ((dummy&0x000000ff)==0x000000ff) ? 'b' : + ((dummy&0xffffffff)==0x7f000001) ? 'l' : + ((dummy&0xffffffe0)==0x00000000) ? '-' : + 'u'); + break; + case AF_INET6: + if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock))) + ch = 'm'; + else + ch = 'u'; + break; + default: + ch = '-'; + break; + } + return ch; +} + +/* + * 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 */ + { "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 */ + { "hmode", 0 }, /* 13 */ + { "srchost", 0 }, /* 14 */ + { 0, 0 } +}; + + +/* + * Decode an incoming data buffer and print a line in the peer list + */ +static int +doprintpeers( + struct varlist *pvl, + int associd, + int rstatus, + int datalen, + const char *data, + FILE *fp, + int af + ) +{ + char *name; + char *value = NULL; + int c; + 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; + 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; + u_long reach = 0; + l_fp estoffset; + l_fp estdelay; + l_fp estjitter; + l_fp estdisp; + l_fp reftime; + l_fp rec; + l_fp ts; + u_long poll_sec; + char type = '?'; + char whenbuf[8], pollbuf[8]; + char clock_name[LENHOSTNAME]; + + get_systime(&ts); + + 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)) { + 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) { + 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); + have_dstadr = TRUE; + dstadr = dum_store; + if (pvl == opeervarlist) { + have_da_rid = TRUE; + dstadr_refid = trunc_left(stoa(&dstadr), 15); + } + } + } else if (!strcmp("hmode", name)) { + decodeint(value, &hmode); + } else if (!strcmp("refid", name)) { + if (pvl == peervarlist) { + 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 (ISREFCLOCKADR(&refidadr)) + dstadr_refid = + refnumtoa(&refidadr); + else + dstadr_refid = + stoa(&refidadr); + } else { + have_da_rid = FALSE; + } + } + } 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 && + 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); + } + } + + /* + * hmode gives the best guidance for the t column. If the response + * did not include hmode we'll use the old decodeaddrtype() result. + */ + 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 << 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) { + if (peervarlist == pvl && 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); + } + 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); +} + + +/* + * dogetpeers - given an association ID, read and print the spreadsheet + * peer variables. + */ +static int +dogetpeers( + struct varlist *pvl, + associd_t associd, + FILE *fp, + int af + ) +{ + const char *datap; + int res; + int dsize; + u_short rstatus; + +#ifdef notdef + res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus, + &dsize, &datap); +#else + /* + * Damn fuzzballs + */ + res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus, + &dsize, &datap); +#endif + + if (res != 0) + return 0; + + if (dsize == 0) { + if (numhosts > 1) + 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); +} + + +/* + * peers - print a peer spreadsheet + */ +static void +dopeers( + 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 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(peervarlist, (int)assoc_cache[u].assid, + fp, af)) + return; + } + return; +} + + +/* + * peers - print a peer spreadsheet + */ +/*ARGSUSED*/ +static void +peers( + struct parse *pcmd, + FILE *fp + ) +{ + int af = 0; + + if (pcmd->nargs == 1) { + if (pcmd->argval->ival == 6) + af = AF_INET6; + else + af = AF_INET; + } + dopeers(0, fp, af); +} + + +/* + * lpeers - print a peer spreadsheet including all fuzzball peers + */ +/*ARGSUSED*/ +static void +lpeers( + struct parse *pcmd, + FILE *fp + ) +{ + int af = 0; + + if (pcmd->nargs == 1) { + if (pcmd->argval->ival == 6) + af = AF_INET6; + else + af = AF_INET; + } + dopeers(1, fp, af); +} + + +/* + * opeers - print a peer spreadsheet + */ +static void +doopeers( + int showall, + FILE *fp, + int af + ) +{ + u_int i; + char fullname[LENHOSTNAME]; + sockaddr_u netnum; + + if (!dogetassoc(fp)) + return; + + for (i = 0; i < numhosts; ++i) { + if (getnetnum(chosts[i].name, &netnum, fullname, af)) + if (strlen(fullname) > maxhostlen) + maxhostlen = strlen(fullname); + } + if (numhosts > 1) + 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) + 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))) + continue; + if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af)) + return; + } + return; +} + + +/* + * opeers - print a peer spreadsheet the old way + */ +/*ARGSUSED*/ +static void +opeers( + struct parse *pcmd, + FILE *fp + ) +{ + int af = 0; + + if (pcmd->nargs == 1) { + if (pcmd->argval->ival == 6) + af = AF_INET6; + else + af = AF_INET; + } + doopeers(0, fp, af); +} + + +/* + * lopeers - print a peer spreadsheet including all fuzzball peers + */ +/*ARGSUSED*/ +static void +lopeers( + struct parse *pcmd, + FILE *fp + ) +{ + int af = 0; + + if (pcmd->nargs == 1) { + if (pcmd->argval->ival == 6) + af = AF_INET6; + else + af = AF_INET; + } + 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 (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; + int 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 = emalloc(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 %5hu %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 ifnum; + 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; + ifnum = 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 */ + 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 idx; + 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; + idx = 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[] = { + { "ss_uptime", "uptime: ", NTP_STR }, + { "ss_reset", "sysstats reset: ", NTP_STR }, + { "ss_received", "packets received: ", NTP_STR }, + { "ss_thisver", "current version: ", NTP_STR }, + { "ss_oldver", "older version: ", NTP_STR }, + { "ss_badformat", "bad length or format: ", NTP_STR }, + { "ss_badauth", "authentication failed:", NTP_STR }, + { "ss_declined", "declined: ", NTP_STR }, + { "ss_restricted", "restricted: ", NTP_STR }, + { "ss_limited", "rate limited: ", NTP_STR }, + { "ss_kodsent", "KoD responses: ", NTP_STR }, + { "ss_processed", "processed for time: ", NTP_STR }, + { 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[] = { + { "peeradr", "system peer: ", NTP_ADP }, + { "peermode", "system peer mode: ", NTP_MODE }, + { "leap", "leap indicator: ", NTP_2BIT }, + { "stratum", "stratum: ", NTP_STR }, + { "precision", "log2 precision: ", NTP_STR }, + { "rootdelay", "root delay: ", NTP_STR }, + { "rootdisp", "root dispersion: ", NTP_STR }, + { "refid", "reference ID: ", NTP_STR }, + { "reftime", "reference time: ", NTP_LFP }, + { "sys_jitter", "system jitter: ", NTP_STR }, + { "clk_jitter", "clock jitter: ", NTP_STR }, + { "clk_wander", "clock wander: ", NTP_STR }, + { "bcastdelay", "broadcast delay: ", NTP_STR }, + { "authdelay", "symm. auth. delay:", NTP_STR }, + { 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[] = { + { "koffset", "pll offset: ", NTP_STR }, + { "kfreq", "pll frequency: ", NTP_STR }, + { "kmaxerr", "maximum error: ", NTP_STR }, + { "kesterr", "estimated error: ", NTP_STR }, + { "kstflags", "kernel status: ", NTP_STR }, + { "ktimeconst", "pll time constant: ", NTP_STR }, + { "kprecis", "precision: ", NTP_STR }, + { "kfreqtol", "frequency tolerance: ", NTP_STR }, + { "kppsfreq", "pps frequency: ", NTP_STR }, + { "kppsstab", "pps stability: ", NTP_STR }, + { "kppsjitter", "pps jitter: ", NTP_STR }, + { "kppscalibdur", "calibration interval ", NTP_STR }, + { "kppscalibs", "calibration cycles: ", NTP_STR }, + { "kppsjitexc", "jitter exceeded: ", NTP_STR }, + { "kppsstbexc", "stability exceeded: ", NTP_STR }, + { "kppscaliberrs", "calibration errors: ", NTP_STR }, + { 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[] = { + { "mru_enabled", "enabled: ", NTP_STR }, + { "mru_depth", "addresses: ", NTP_STR }, + { "mru_deepest", "peak addresses: ", NTP_STR }, + { "mru_maxdepth", "maximum addresses: ", NTP_STR }, + { "mru_mindepth", "reclaim above count:", NTP_STR }, + { "mru_maxage", "reclaim older than: ", NTP_STR }, + { "mru_mem", "kilobytes: ", NTP_STR }, + { "mru_maxmem", "maximum kilobytes: ", NTP_STR }, + { 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[] = { + { "iostats_reset", "time since reset: ", NTP_STR }, + { "total_rbuf", "receive buffers: ", NTP_STR }, + { "free_rbuf", "free receive buffers: ", NTP_STR }, + { "used_rbuf", "used receive buffers: ", NTP_STR }, + { "rbuf_lowater", "low water refills: ", NTP_STR }, + { "io_dropped", "dropped packets: ", NTP_STR }, + { "io_ignored", "ignored packets: ", NTP_STR }, + { "io_received", "received packets: ", NTP_STR }, + { "io_sent", "packets sent: ", NTP_STR }, + { "io_sendfailed", "packet send failures: ", NTP_STR }, + { "io_wakeups", "input wakeups: ", NTP_STR }, + { "io_goodwakeups", "useful input wakeups: ", NTP_STR }, + { 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[] = { + { "timerstats_reset", "time since reset: ", NTP_STR }, + { "timer_overruns", "timer overruns: ", NTP_STR }, + { "timer_xmts", "calls to transmit: ", NTP_STR }, + { 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[] = { + { "authreset", "time since reset:", NTP_STR }, + { "authkeys", "stored keys: ", NTP_STR }, + { "authfreek", "free keys: ", NTP_STR }, + { "authklookups", "key lookups: ", NTP_STR }, + { "authknotfound", "keys not found: ", NTP_STR }, + { "authkuncached", "uncached keys: ", NTP_STR }, + { "authkexpired", "expired keys: ", NTP_STR }, + { "authencrypts", "encryptions: ", NTP_STR }, + { "authdecrypts", "decryptions: ", NTP_STR }, + { 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[] = { + { "src", "remote host: ", NTP_ADD }, + { "dst", "local address: ", NTP_ADD }, + { "timerec", "time last received: ", NTP_STR }, + { "timer", "time until next send:", NTP_STR }, + { "timereach", "reachability change: ", NTP_STR }, + { "sent", "packets sent: ", NTP_STR }, + { "received", "packets received: ", NTP_STR }, + { "badauth", "bad authentication: ", NTP_STR }, + { "bogusorg", "bogus origin: ", NTP_STR }, + { "oldpkt", "duplicate: ", NTP_STR }, + { "seldisp", "bad dispersion: ", NTP_STR }, + { "selbroken", "bad reference time: ", NTP_STR }, + { "candidate", "candidate order: ", NTP_STR }, + { NULL, NULL, 0 } + }; + associd_t associd; + + associd = checkassocid(pcmd->argval[0].uval); + if (0 == associd) + return; + + collect_display_vdc(associd, pstats_vdc, TRUE, fp); +} + -- cgit v1.2.1