/*
* 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]" },
{ "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]" },
{ "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 },
{ "<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", "", "", "" }, "" }
};
/*
* 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 */
static int
xputc(
int ch,
FILE * ofp
)
{
return fputc(ch, (ofp ? ofp : stderr));
}
/*
* 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;
xprintf(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;
/*
* 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,
size_t *dsize,
const char **datap
)
{
char data[CTL_MAX_DATA_LEN];
size_t datalen;
/*
* 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;
size_t 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)
xprintf(fp, "server=%s ", currenthost);
if (dsize == 0) {
if (associd == 0)
xprintf(fp, "No system%s variables returned\n",
(type == TYPE_CLOCK) ? " clock" : "");
else
xprintf(fp,
"No information returned for%s association %u\n",
(type == TYPE_CLOCK) ? " clock" : "",
associd);
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;
}
res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
&dsize, &datap);
doclearvlist(tmplist);
if (res != 0)
return;
if (numhosts > 1)
xprintf(fp, "server=%s ", currenthost);
if (dsize == 0)
xprintf(fp, "done! (no data returned)\n");
else {
xprintf(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;
/*
* 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]) {
xprintf(stderr,
"***Association ID %u not found in list\n",
assids[a]);
return 0;
}
/*
* 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)
xprintf(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;
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;
size_t dsize;
u_short rstatus;
if (dsize == 0) {
if (numhosts > 1)
xprintf(fp, "server=%s ", currenthost);
xprintf(fp, "No association ID's returned\n");
return 0;
}
if (dsize & 0x3) {
if (numhosts > 1)
xprintf(stderr, "server=%s ", currenthost);
xprintf(stderr,
"***Server returned %zu octets, should be multiple of 4\n",
dsize);
return 0;
}
/*
* 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;
size_t 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)
xprintf(fp, "(no response message, curiously)");
else
xprintf(fp, "%.*s", (int)dsize, datap); /* cast is wobbly */
}
#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;
/*
* 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;
}
/*
* Decode an incoming data buffer and print a line in the peer list
*/
static int
doprintpeers(
struct varlist *pvl,
int associd,
int rstatus,
size_t datalen,
const char *data,
FILE *fp,
int af
)
{
char *name;
char *value = NULL;
int c;
size_t 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;
char *drbuf = NULL;
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;
u_long flash = 0;
char type = '?';
char clock_name[LENHOSTNAME];
char whenbuf[12], pollbuf[12];
/* [Bug 3482] formally whenbuf & pollbuf should be able to hold
* a full signed int. Not that we would use that much string
* data for it...
*/
get_systime(&ts);
while (nextvar(&datalen, &data, &name, &value)) {
INSIST(name && value);
if (!strcmp("srcadr", name) ||
!strcmp("peeradr", name)) {
if (!decodenetnum(value, &srcadr))
xprintf(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);
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)
&& (drefid == REFID_IPV4)) {
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 ( (pvl == apeervarlist)
|| (pvl == peervarlist)) {
/* no need to check drefid == REFID_HASH */
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);
//xprintf(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);
if (pvl == apeervarlist) {
/*
* restrict refid to
* 8 chars [Bug 3850]
*/
dstadr_refid =
trunc_right(
dstadr_refid,
8);
}
} else {
drbuf = emalloc(10);
snprintf(drbuf, 10, "%0x",
SRCADR(&refidadr));
dstadr_refid = drbuf;
}
} 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 || 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);
} else if (!strcmp("flash", name)) {
decodeuint(value, &flash);
} else {
// xprintf(stderr, "UNRECOGNIZED name=%s ", name);
}
}
/*
* 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 ((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;
}
xprintf(fp, "%-*s ", (int)maxhostlen, serverlocal);
}
if (AF_UNSPEC == af || AF(&srcadr) == af) {
if (!have_srchost)
strlcpy(clock_name, nntohost(&srcadr),
sizeof(clock_name));
/* wide and long source - space over on next line */
/* allow for host + sp if > 1 and regular tally + source + sp */
if (wideremote && 15 < strlen(clock_name))
xprintf(fp, "%c%s\n%*s", c, clock_name,
((numhosts > 1) ? (int)maxhostlen + 1 : 0)
+ 1 + 15 + 1, "");
else
xprintf(fp, "%c%-15.15s ", c, clock_name);
if ((flash & TEST12) && (pvl != opeervarlist)) {
drlen = xprintf(fp, "(loop)");
} else if (!have_da_rid) {
drlen = 0;
} else {
drlen = strlen(dstadr_refid);
makeascii(drlen, dstadr_refid, fp);
}
free(drbuf);
if (pvl == apeervarlist) {
while (drlen++ < 9)
xputc(' ', fp);
xprintf(fp, "%-6d", associd);
} else {
while (drlen++ < 15)
xputc(' ', fp);
}
xprintf(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, ulfptoms(&estdelay, 3),
lfptoms(&estoffset, 3),
(have_jitter)
? ulfptoms(&estjitter, 3)
: ulfptoms(&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;
size_t dsize;
u_short rstatus;
if (dsize == 0) {
if (numhosts > 1)
xprintf(stderr, "server=%s ", currenthost);
xprintf(stderr,
"***No information returned for association %u\n",
associd);
return 0;
}
/*
* 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;
if (numhosts > 1) {
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);
}
}
xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
"server (local)");
}
xprintf(fp,
" remote refid st t when poll reach delay offset jitter\n");
if (numhosts > 1)
for (u = 0; u <= maxhostlen; u++)
xprintf(fp, "=");
xprintf(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)
xprintf(stderr, "eliding [%d]\n",
(int)assoc_cache[u].assid);
continue;
}
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;
if (numhosts > 1) {
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);
}
}
xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
"server (local)");
}
xprintf(fp,
" remote refid assid st t when poll reach delay offset jitter\n");
if (numhosts > 1)
for (u = 0; u <= maxhostlen; u++)
xprintf(fp, "=");
xprintf(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)
xprintf(stderr, "eliding [%d]\n",
(int)assoc_cache[u].assid);
continue;
}
if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid,
fp, af))
return;
}
return;
}
/*
* peers - print a peer spreadsheet
*/
/*ARGSUSED*/
static void
peers(
struct parse *pcmd,
FILE *fp
)
{
if (drefid == REFID_HASH) {
apeers(pcmd, fp);
} else {
int af = 0;
if (pcmd->nargs == 1) {
if (pcmd->argval->ival == 6)
af = AF_INET6;
else
af = AF_INET;
}
dopeers(0, fp, af);
}
}
/*
* 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*/
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;
if (numhosts > 1) {
for (i = 0; i < numhosts; ++i) {
if (getnetnum(chosts[i].name, &netnum, fullname, af)) {
maxhostlen = max(maxhostlen, strlen(fullname));
}
xprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
"server");
}
}
xprintf(fp,
" remote local st t when poll reach delay offset disp\n");
if (numhosts > 1)
for (i = 0; i <= maxhostlen; ++i)
xprintf(fp, "=");
xprintf(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;
size_t rsize;
const char *rdata;
char *resp;
int res;
int col;
int i;
cfgcmd = pcmd->argval[0].string;
if (debug > 2)
xprintf(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);
col = -1;
if (1 == sscanf(resp, "column %d syntax error", &col)
&& col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) {
if (interactive)
xputs(" *", stdout); /* "ntpq> :config " */
else
printf("%s\n", cfgcmd);
for (i = 0; i < col; i++)
xputc('_', stdout);
xputs("^\n", stdout);
}
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!!)
*
* Pearly: There are a few places where 'size_t' is cast to 'int' based
* on the assumption that 'int' can hold the size of the involved
* buffers without overflow.
*/
static void
config_from_file (
struct parse *pcmd,
FILE *fp
)
{
u_short rstatus;
size_t rsize;
const char *rdata;
char * cp;
int res;
FILE *config_fd;
char config_cmd[MAXLINE];
size_t config_len;
int i;
int retry_limit;
if (debug > 2)
xprintf(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) {
/* Eliminate comments first. */
cp = strchr(config_cmd, '#');
config_len = (NULL != cp)
? (size_t)(cp - config_cmd)
: strlen(config_cmd);
/* [Bug 3015] make sure there's no trailing whitespace;
* the fix for [Bug 2853] on the server side forbids
* those. And don't transmit empty lines, as this would
* just be waste.
*/
while (config_len != 0 &&
(u_char)config_cmd[config_len-1] <= ' ')
--config_len;
config_cmd[config_len] = '\0';
++i;
if (0 == config_len)
continue;
retry_limit = 2;
do
res = doquery(CTL_OP_CONFIGURE, 0, 1,
config_len, config_cmd,
&rstatus, &rsize, &rdata);
while (res != 0 && retry_limit--);
if (res != 0) {
printf("Line No: %d query failed: %.*s\n"
"Subsequent lines not sent.\n",
i, (int)config_len, config_cmd);
fclose(config_fd);
return;
}
/* Right-strip the result code string, then output the
* last line executed, with result code. */
while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ')
--rsize;
printf("Line No: %d %.*s: %.*s\n", i,
(int)rsize, rdata,
(int)config_len, config_cmd);
}
printf("Done sending file\n");
fclose(config_fd);
}
/*
* 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) {
xprintf(stderr, "nonce request failed\n");
return FALSE;
}
/*
* 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)) {
xprintf(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);
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)
int
mrulist_ctrl_c_hook(void)
{
mrulist_interrupted = TRUE;
return 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;
size_t chars;
int qres;
u_short rstatus;
size_t 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;
if (CERR_UNKNOWNVAR == qres && ri > 0) {
/*
* None of the supplied prior entries match, so
* toss them from our list and try again.
*/
if (debug)
xprintf(stderr,
"no overlap between %d prior entries and server MRU list\n",
ri);
while (ri--) {
recent = HEAD_DLIST(mru_list, mlink);
INSIST(recent != NULL);
if (debug)
xprintf(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);
INSIST(unlinked == recent);
free(recent);
mru_count--;
}
if (NULL == HEAD_DLIST(mru_list, mlink)) {
restarted_count++;
if (restarted_count > 8) {
xprintf(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)
xprintf(stderr,
"---> Restarting from the beginning, retry #%u\n",
restarted_count);
}
} else if (CERR_UNKNOWNVAR == qres) {
xprintf(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)
xprintf(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)
xprintf(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)
xprintf(stderr,
"Frag limit reduced to %d following incomplete response.\n",
frags);
} else {
limit = max(2, limit / 2);
if (debug)
xprintf(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)) {
INSIST(tag && val);
if (debug > 1)
xprintf(stderr, "nextvar gave: %s = %s\n",
tag, val);
switch(tag[0]) {
case 'a':
if (!strcmp(tag, "addr.older")) {
if (!have_last_older) {
xprintf(stderr,
"addr.older %s before last.older\n",
val);
goto cleanup_return;
}
if (!decodenetnum(val, &addr_older)) {
xprintf(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) {
xprintf(stderr,
"addr.older %s not in hash table\n",
val);
goto cleanup_return;
}
if (!L_ISEQU(&last_older,
&recent->last)) {
xprintf(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)) {
xprintf(stderr,
"last.older %s garbled\n",
val);
goto cleanup_return;
}
have_last_older = TRUE;
} else if (!strcmp(tag, "last.newest")) {
if (0 != got) {
xprintf(stderr,
"last.newest %s before complete row, got = 0x%x\n",
val, (u_int)got);
goto cleanup_return;
}
if (!have_now) {
xprintf(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)) {
xprintf(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) {
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) {
xprintf(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;
xprintf(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;
}
/*
* 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;
mrulist_interrupted = FALSE;
push_ctrl_c_handler(&mrulist_ctrl_c_hook);
xprintf(stderr,
"Ctrl-C will stop MRU retrieval and display partial results.\n");
fflush(stderr);
if (ppentry - sorted != (int)mru_count) {
xprintf(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]);
/*
* 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)
xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n",
*pfields, IFSTATS_FIELDS);
*pfields = 0;
prow->ifnum = ifnum;
return;
}
xprintf(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");
*/
xprintf(fp,
"%3u %-24.24s %c %4x %3u %2u %6u %6u %6u %5u %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))
xprintf(fp, " %s\n", sptoa(&prow->bcast));
ifnum = prow->ifnum;
ZERO(*prow);
prow->ifnum = ifnum;
}
xprintf(fp,
" interface name send\n"
" # address/broadcast drop flag ttl mc received sent failed peers uptime\n"
"==============================================================================\n");
/* '=' x 78 */
case 'u':
if (1 == sscanf(tag, up_fmt, &ui) &&
1 == sscanf(val, "%u", &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)
xprintf(fp, "Warning: incomplete row with %d (of %d) fields\n",
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)
xprintf(fp, "Warning: incomplete row with %d (of %d) fields",
*pfields, RESLIST_FIELDS);
*pfields = 0;
prow->idx = idx;
return;
}
xprintf(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;
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 if ((len = strlen(val)) < sizeof(row.flagstr)) {
memcpy(row.flagstr, val, len);
row.flagstr[len] = '\0';
comprende = TRUE;
} else {
/* no flags, and still !comprende */
row.flagstr[0] = '\0';
}
}
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)
xprintf(fp, "Warning: incomplete row with %d (of %d) fields",
fields, RESLIST_FIELDS);