/* * ntpq_ops.c - subroutines which are called to perform operations by ntpq */ #include #include #include #include #include "ntp_stdlib.h" #include "ntpq.h" #include "ntpq-opts.h" extern char * chosts[]; extern char currenthost[]; extern int currenthostisnum; extern int numhosts; int 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 *); static void mreadlist (struct parse *, FILE *); static void mreadvar (struct parse *, FILE *); static int dogetassoc (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 pstatus (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 *); /* * 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, NO, NO }, { "assocID", "name=value[,...]", "", "" }, "read system or peer variables" }, { "rv", readvar, { OPT|NTP_UINT, OPT|NTP_STR, NO, NO }, { "assocID", "name=value[,...]", "", "" }, "read system or peer variables" }, { "writevar", writevar, { NTP_UINT, NTP_STR, NO, NO }, { "assocID", "name=value,[...]", "", "" }, "write system or peer variables" }, { "mreadlist", mreadlist, { NTP_UINT, NTP_UINT, NO, NO }, { "assocID", "assocID", "", "" }, "read the peer variables in the variable list for multiple peers" }, { "mrl", mreadlist, { NTP_UINT, NTP_UINT, NO, NO }, { "assocID", "assocID", "", "" }, "read the peer variables in the variable list for multiple peers" }, { "mreadvar", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO }, { "assocID", "assocID", "name=value[,...]", "" }, "read peer variables from multiple peers" }, { "mrv", mreadvar, { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO }, { "assocID", "assocID", "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" }, { "pstatus", pstatus, { NTP_UINT, NO, NO, NO }, { "assocID", "", "", "" }, "print status information returned 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" }, { 0, 0, { NO, NO, NO, NO }, { "-4|-6", "", "", "" }, "" } }; /* * Variable list data space */ #define MAXLINE 512 /* maximum length of a line */ #define MAXLIST 64 /* maximum number of variables in list */ #define LENHOSTNAME 256 /* host name is 256 characters long */ /* * 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 rawmode; extern struct servent *server_entry; extern struct association assoc_cache[]; extern int numassoc; extern u_char pktversion; extern struct ctl_var peer_var[]; /* * For quick string comparisons */ #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) /* * 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 list and return if found */ static struct varlist * findlistvar( struct varlist *list, char *name ) { register struct varlist *vl; for (vl = list; vl < list + MAXLIST && vl->name != 0; vl++) if (STREQ(name, vl->name)) return vl; if (vl < list + MAXLIST) return vl; return (struct varlist *)0; } /* * doaddvlist - add variable(s) to the variable list */ static void doaddvlist( struct varlist *vlist, const char *vars ) { register 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) { (void) fprintf(stderr, "Variable list full\n"); return; } if (vl->name == 0) { vl->name = estrdup(name); } else if (vl->value != 0) { free(vl->value); vl->value = 0; } if (value != 0) vl->value = estrdup(value); } } /* * dormvlist - remove variable(s) from the variable list */ static void dormvlist( struct varlist *vlist, const char *vars ) { register 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) break; if (cp != data) *cp++ = ','; memmove(cp, vl->name, (unsigned)namelen); cp += namelen; if (valuelen != 0) { *cp++ = '='; memmove(cp, vl->value, (unsigned)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 ) { register struct varlist *vl; if (vlist->name == 0) { (void) fprintf(fp, "No variables on list\n"); } else { for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) { if (vl->value == 0) { (void) fprintf(fp, "%s\n", vl->name); } else { (void) fprintf(fp, "%s=%s\n", vl->name, vl->value); } } } } /* * 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) (void) fprintf(fp, "server=%s ", currenthost); if (dsize == 0) { if (associd == 0) (void) fprintf(fp, "No system%s variables returned\n", (type == TYPE_CLOCK) ? " clock" : ""); else (void) fprintf(fp, "No information returned for%s association %u\n", (type == TYPE_CLOCK) ? " clock" : "", associd); return 1; } if (!quiet) fprintf(fp,"associd=%d ",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=%d ",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; 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; memset(tmplist, 0, sizeof(tmplist)); if (pcmd->nargs >= 2) doaddvlist(tmplist, pcmd->argval[1].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; memset((char *)tmplist, 0, sizeof(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=%d ",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; memset(tmplist, 0, sizeof(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 ) { associd_t assids[2]; int ind[COUNTOF(assids)]; int i; size_t a; 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; /* HMS: uval? */ if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval, &from, &to)) return; for (i = from; i <= to; i++) { if (i != from) (void) fprintf(fp, "\n"); if (!dolist(g_varlist, (int)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; /* HMS: uval? */ if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval, &from, &to)) return; if (pcmd->nargs >= 3) { memset(tmplist, 0, sizeof(tmplist)); doaddvlist(tmplist, pcmd->argval[2].string); pvars = tmplist; } else { pvars = g_varlist; } for (i = from; i <= to; i++) { if (i != from) fprintf(fp, "\n"); if (!dolist(pvars, (int)assoc_cache[i].assid, CTL_OP_READVAR, TYPE_PEER, fp)) break; } doclearvlist(tmplist); return; } /* * dogetassoc - query the host for its list of associations */ static int dogetassoc( FILE *fp ) { const char *datap; 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) (void) fprintf(fp, "server=%s ", currenthost); (void) fprintf(fp, "No association ID's returned\n"); return 0; } if (dsize & 0x3) { if (numhosts > 1) (void) fprintf(stderr, "server=%s ", currenthost); (void) fprintf(stderr, "***Server returned %d octets, should be multiple of 4\n", dsize); return 0; } numassoc = 0; while (dsize > 0) { assoc_cache[numassoc].assid = ntohs(*((const u_short *)datap)); datap += sizeof(u_short); assoc_cache[numassoc].status = ntohs(*((const u_short *)datap)); datap += sizeof(u_short); if (++numassoc >= MAXASSOC) break; dsize -= sizeof(u_short) + sizeof(u_short); } sortassoc(); return 1; } /* * printassoc - print the current list of associations */ static void printassoc( int showall, FILE *fp ) { register char *bp; 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; const char *cnt; 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; } cnt = uinttoa(event_count); snprintf(buf, sizeof(buf), "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2s", i + 1, assoc_cache[i].assid, assoc_cache[i].status, conf, reach, auth, condition, last_event, cnt); bp = buf + strlen(buf); while (bp > buf && ' ' == bp[-1]) --bp; 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 */ /* * pstatus - print peer status returned by the server */ static void pstatus( struct parse *pcmd, FILE *fp ) { const char *datap; int res; associd_t associd; int dsize; u_short rstatus; /* HMS: uval? */ if ((associd = checkassocid(pcmd->argval[0].uval)) == 0) return; res = doquery(CTL_OP_READSTAT, associd, 0, 0, NULL, &rstatus, &dsize, &datap); if (res != 0) return; if (numhosts > 1) fprintf(fp, "server=%s ", currenthost); if (dsize == 0) { fprintf(fp, "No information returned for association %u\n", associd); return; } fprintf(fp, "associd=%u ", associd); printvars(dsize, datap, (int)rstatus, TYPE_PEER, 0, fp); } /* * 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 */ { 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 */ { 0, 0 } }; #define HAVE_SRCADR 0 #define HAVE_DSTADR 1 #define HAVE_REFID 1 #define HAVE_STRATUM 2 #define HAVE_HPOLL 3 #define HAVE_PPOLL 4 #define HAVE_REACH 5 #define HAVE_DELAY 6 #define HAVE_OFFSET 7 #define HAVE_JITTER 8 #define HAVE_DISPERSION 9 #define HAVE_REC 10 #define HAVE_REFTIME 11 #define HAVE_SRCPORT 12 #define MAXHAVE 13 /* * Decode an incoming data buffer and print a line in the peer list */ 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 i; int c; sockaddr_u srcadr; sockaddr_u dstadr; sockaddr_u refidadr; u_long srcport = 0; char *dstadr_refid = "0.0.0.0"; 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_char havevar[MAXHAVE]; u_long poll_sec; char type = '?'; char refid_string[10]; char whenbuf[8], pollbuf[8]; char clock_name[LENHOSTNAME]; memset((char *)havevar, 0, sizeof(havevar)); get_systime(&ts); ZERO_SOCK(&srcadr); ZERO_SOCK(&dstadr); /* Initialize by zeroing out estimate variables */ memset((char *)&estoffset, 0, sizeof(l_fp)); memset((char *)&estdelay, 0, sizeof(l_fp)); memset((char *)&estjitter, 0, sizeof(l_fp)); memset((char *)&estdisp, 0, sizeof(l_fp)); while (nextvar(&datalen, &data, &name, &value)) { sockaddr_u dum_store; i = findvar(name, peer_var, 1); if (i == 0) continue; /* don't know this one */ switch (i) { case CP_SRCADR: if (decodenetnum(value, &srcadr)) { havevar[HAVE_SRCADR] = 1; } break; case CP_DSTADR: if (decodenetnum(value, &dum_store)) { type = decodeaddrtype(&dum_store); havevar[HAVE_DSTADR] = 1; dstadr = dum_store; if (pvl == opeervarlist) { dstadr_refid = trunc_left(stoa(&dstadr), 15); } } break; case CP_REFID: if (pvl == peervarlist) { havevar[HAVE_REFID] = 1; if (*value == '\0') { dstadr_refid = ""; } else if (strlen(value) <= 4) { refid_string[0] = '.'; (void) strcpy(&refid_string[1], value); i = strlen(refid_string); refid_string[i] = '.'; refid_string[i+1] = '\0'; dstadr_refid = refid_string; } else if (decodenetnum(value, &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 { havevar[HAVE_REFID] = 0; } } break; case CP_STRATUM: if (decodeuint(value, &stratum)) havevar[HAVE_STRATUM] = 1; break; case CP_HPOLL: if (decodeint(value, &hpoll)) { havevar[HAVE_HPOLL] = 1; if (hpoll < 0) hpoll = NTP_MINPOLL; } break; case CP_PPOLL: if (decodeint(value, &ppoll)) { havevar[HAVE_PPOLL] = 1; if (ppoll < 0) ppoll = NTP_MINPOLL; } break; case CP_REACH: if (decodeuint(value, &reach)) havevar[HAVE_REACH] = 1; break; case CP_DELAY: if (decodetime(value, &estdelay)) havevar[HAVE_DELAY] = 1; break; case CP_OFFSET: if (decodetime(value, &estoffset)) havevar[HAVE_OFFSET] = 1; break; case CP_JITTER: if (pvl == peervarlist) if (decodetime(value, &estjitter)) havevar[HAVE_JITTER] = 1; break; case CP_DISPERSION: if (decodetime(value, &estdisp)) havevar[HAVE_DISPERSION] = 1; break; case CP_REC: if (decodets(value, &rec)) havevar[HAVE_REC] = 1; break; case CP_SRCPORT: if (decodeuint(value, &srcport)) havevar[HAVE_SRCPORT] = 1; break; case CP_REFTIME: havevar[HAVE_REFTIME] = 1; if (!decodets(value, &reftime)) L_CLR(&reftime); break; default: break; } } /* * Check to see if the srcport is NTP's port. If not this probably * isn't a valid peer association. */ if (havevar[HAVE_SRCPORT] && srcport != NTP_PORT) return (1); /* * Got everything, format the line */ poll_sec = 1< NTP_OLDVERSION) c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7]; else c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3]; if (numhosts > 1) { if (peervarlist == pvl && havevar[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 ", maxhostlen, serverlocal); } if (AF_UNSPEC == af || AF(&srcadr) == af) { strncpy(clock_name, nntohost(&srcadr), sizeof(clock_name)); fprintf(fp, "%c%-15.15s ", c, clock_name); 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), (havevar[HAVE_JITTER]) ? lfptoms(&estjitter, 3) : lfptoms(&estdisp, 3)); return (1); } else return(1); } #undef HAVE_SRCADR #undef HAVE_DSTADR #undef HAVE_STRATUM #undef HAVE_PPOLL #undef HAVE_HPOLL #undef HAVE_REACH #undef HAVE_ESTDELAY #undef HAVE_ESTOFFSET #undef HAVE_JITTER #undef HAVE_ESTDISP #undef HAVE_REFID #undef HAVE_REC #undef HAVE_SRCPORT #undef HAVE_REFTIME #undef MAXHAVE /* * dogetpeers - given an association ID, read and print the spreadsheet * 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 ) { int i; char fullname[LENHOSTNAME]; sockaddr_u netnum; char * name_or_num; size_t sl; if (!dogetassoc(fp)) return; for (i = 0; i < numhosts; ++i) { if (getnetnum(chosts[i], &netnum, fullname, af)) { name_or_num = nntohost(&netnum); sl = strlen(name_or_num); maxhostlen = max(maxhostlen, (int)sl); } } if (numhosts > 1) fprintf(fp, "%-*.*s ", maxhostlen, maxhostlen, "server (local)"); fprintf(fp, " remote refid st t when poll reach delay offset jitter\n"); if (numhosts > 1) for (i = 0; i <= maxhostlen; ++i) 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(peervarlist, (int)assoc_cache[i].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 ) { register int i; char fullname[LENHOSTNAME]; sockaddr_u netnum; if (!dogetassoc(fp)) return; for (i = 0; i < numhosts; ++i) { if (getnetnum(chosts[i], &netnum, fullname, af)) if ((int)strlen(fullname) > maxhostlen) maxhostlen = strlen(fullname); } if (numhosts > 1) (void) fprintf(fp, "%-*.*s ", maxhostlen, maxhostlen, "server"); (void) fprintf(fp, " remote local st t when poll reach delay offset disp\n"); if (numhosts > 1) for (i = 0; i <= maxhostlen; ++i) (void) fprintf(fp, "="); (void) 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, (int)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 ) { 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); }