diff options
Diffstat (limited to 'contrib/ntp/ntpd/ntp_control.c')
-rw-r--r-- | contrib/ntp/ntpd/ntp_control.c | 2652 |
1 files changed, 2652 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/ntp_control.c b/contrib/ntp/ntpd/ntp_control.c new file mode 100644 index 000000000000..e63765f0d2b4 --- /dev/null +++ b/contrib/ntp/ntpd/ntp_control.c @@ -0,0 +1,2652 @@ +/* + * ntp_control.c - respond to control messages and send async traps + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include <signal.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_control.h" +#include "ntp_stdlib.h" + +/* + * Structure to hold request procedure information + */ +#define NOAUTH 0 +#define AUTH 1 + +#define NO_REQUEST (-1) + +struct ctl_proc { + short control_code; /* defined request code */ + u_short flags; /* flags word */ + void (*handler) P((struct recvbuf *, int)); /* routine to handle request */ +}; + +/* + * Only one flag. Authentication required or not. + */ +#define NOAUTH 0 +#define AUTH 1 + +/* + * Request processing routines + */ +static void ctl_error P((int)); +static u_short ctlclkstatus P((struct refclockstat *)); +static void ctl_flushpkt P((int)); +static void ctl_putdata P((const char *, unsigned int, int)); +static void ctl_putstr P((const char *, const char *, unsigned int)); +static void ctl_putdbl P((const char *, double)); +static void ctl_putuint P((const char *, u_long)); +static void ctl_puthex P((const char *, u_long)); +static void ctl_putint P((const char *, long)); +static void ctl_putts P((const char *, l_fp *)); +static void ctl_putadr P((const char *, u_int32)); +static void ctl_putid P((const char *, char *)); +static void ctl_putarray P((const char *, double *, int)); +static void ctl_putsys P((int)); +static void ctl_putpeer P((int, struct peer *)); +#ifdef REFCLOCK +static void ctl_putclock P((int, struct refclockstat *, int)); +#endif /* REFCLOCK */ +static struct ctl_var *ctl_getitem P((struct ctl_var *, char **)); +static u_long count_var P((struct ctl_var *)); +static void control_unspec P((struct recvbuf *, int)); +static void read_status P((struct recvbuf *, int)); +static void read_variables P((struct recvbuf *, int)); +static void write_variables P((struct recvbuf *, int)); +static void read_clock_status P((struct recvbuf *, int)); +static void write_clock_status P((struct recvbuf *, int)); +static void set_trap P((struct recvbuf *, int)); +static void unset_trap P((struct recvbuf *, int)); +static struct ctl_trap *ctlfindtrap P((struct sockaddr_in *, struct interface *)); + +static struct ctl_proc control_codes[] = { + { CTL_OP_UNSPEC, NOAUTH, control_unspec }, + { CTL_OP_READSTAT, NOAUTH, read_status }, + { CTL_OP_READVAR, NOAUTH, read_variables }, + { CTL_OP_WRITEVAR, AUTH, write_variables }, + { CTL_OP_READCLOCK, NOAUTH, read_clock_status }, + { CTL_OP_WRITECLOCK, NOAUTH, write_clock_status }, + { CTL_OP_SETTRAP, NOAUTH, set_trap }, + { CTL_OP_UNSETTRAP, NOAUTH, unset_trap }, + { NO_REQUEST, 0 } +}; + +/* + * System variable values. The array can be indexed by + * the variable index to find the textual name. + */ +static struct ctl_var sys_var[] = { + { 0, PADDING, "" }, /* 0 */ + { CS_LEAP, RW, "leap" }, /* 1 */ + { CS_STRATUM, RO, "stratum" }, /* 2 */ + { CS_PRECISION, RO, "precision" }, /* 3 */ + { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */ + { CS_ROOTDISPERSION, RO, "rootdispersion" }, /* 5 */ + { CS_REFID, RO, "refid" }, /* 6 */ + { CS_REFTIME, RO, "reftime" }, /* 7 */ + { CS_POLL, RO, "poll" }, /* 8 */ + { CS_PEERID, RO, "peer" }, /* 9 */ + { CS_STATE, RO, "state" }, /* 10 */ + { CS_OFFSET, RO, "phase" }, /* 11 */ + { CS_DRIFT, RO, "frequency" }, /* 12 */ + { CS_COMPLIANCE, RO, "jitter" }, /* 13 */ + { CS_CLOCK, RO, "clock" }, /* 14 */ + { CS_PROCESSOR, RO, "processor" }, /* 15 */ + { CS_SYSTEM, RO, "system" }, /* 16 */ + { CS_STABIL, RO, "stability" }, /* 17 */ + { CS_VARLIST, RO, "sys_var_list" }, /* 18 */ + { 0, EOV, "" } +}; + +static struct ctl_var *ext_sys_var = (struct ctl_var *)0; + +/* + * System variables we print by default (in fuzzball order, more-or-less) + */ +static u_char def_sys_var[] = { + CS_PROCESSOR, + CS_SYSTEM, + CS_LEAP, + CS_STRATUM, + CS_PRECISION, + CS_ROOTDELAY, + CS_ROOTDISPERSION, + CS_PEERID, + CS_REFID, + CS_REFTIME, + CS_POLL, + CS_CLOCK, + CS_STATE, + CS_OFFSET, + CS_DRIFT, + CS_COMPLIANCE, + CS_STABIL, + 0 +}; + + +/* + * Peer variable list + */ +static struct ctl_var peer_var[] = { + { 0, PADDING, "" }, /* 0 */ + { CP_CONFIG, RO, "config" }, /* 1 */ + { CP_AUTHENABLE, RO, "authenable" }, /* 2 */ + { CP_AUTHENTIC, RO, "authentic" }, /* 3 */ + { CP_SRCADR, RO, "srcadr" }, /* 4 */ + { CP_SRCPORT, RO, "srcport" }, /* 5 */ + { CP_DSTADR, RO, "dstadr" }, /* 6 */ + { CP_DSTPORT, RO, "dstport" }, /* 7 */ + { CP_LEAP, RO, "leap" }, /* 8 */ + { CP_HMODE, RO, "hmode" }, /* 9 */ + { CP_STRATUM, RO, "stratum" }, /* 10 */ + { CP_PPOLL, RO, "ppoll" }, /* 11 */ + { CP_HPOLL, RO, "hpoll" }, /* 12 */ + { CP_PRECISION, RO, "precision" }, /* 13 */ + { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */ + { CP_ROOTDISPERSION, RO, "rootdispersion" }, /* 15 */ + { CP_REFID, RO, "refid" }, /* 16 */ + { CP_REFTIME, RO, "reftime" }, /* 17 */ + { CP_ORG, RO, "org" }, /* 18 */ + { CP_REC, RO, "rec" }, /* 19 */ + { CP_XMT, RO, "xmt" }, /* 20 */ + { CP_REACH, RO, "reach" }, /* 21 */ + { CP_VALID, RO, "valid" }, /* 22 */ + { CP_TIMER, RO, "timer" }, /* 23 */ + { CP_DELAY, RO, "delay" }, /* 24 */ + { CP_OFFSET, RO, "offset" }, /* 25 */ + { CP_JITTER, RO, "jitter" }, /* 26 */ + { CP_DISPERSION,RO, "dispersion" }, /* 27 */ + { CP_KEYID, RO, "keyid" }, /* 28 */ + { CP_FILTDELAY, RO, "filtdelay=" }, /* 29 */ + { CP_FILTOFFSET, RO, "filtoffset=" }, /* 30 */ + { CP_PMODE, RO, "pmode" }, /* 31 */ + { CP_RECEIVED, RO, "received"}, /* 32 */ + { CP_SENT, RO, "sent" }, /* 33 */ + { CP_FILTERROR, RO, "filtdisp=" }, /* 34 */ + { CP_FLASH, RO, "flash" }, /* 35 */ + { CP_DISP, PADDING,"" }, /* 36 */ + { CP_VARLIST, RO, "peer_var_list" }, /* 37 */ + { 0, EOV, "" } +}; + + +/* + * Peer variables we print by default + */ +static u_char def_peer_var[] = { + CP_SRCADR, + CP_SRCPORT, + CP_DSTADR, + CP_DSTPORT, + CP_KEYID, + CP_STRATUM, + CP_PRECISION, + CP_ROOTDELAY, + CP_ROOTDISPERSION, + CP_REFID, + CP_REFTIME, + CP_DELAY, + CP_OFFSET, + CP_JITTER, + CP_DISPERSION, + CP_REACH, + CP_VALID, + CP_HMODE, + CP_PMODE, + CP_HPOLL, + CP_PPOLL, + CP_LEAP, + CP_FLASH, + CP_ORG, + CP_REC, + CP_XMT, + CP_FILTDELAY, + CP_FILTOFFSET, + CP_FILTERROR, + 0 +}; + + +#ifdef REFCLOCK +/* + * Clock variable list + */ +static struct ctl_var clock_var[] = { + { 0, PADDING, "" }, /* 0 */ + { CC_TYPE, RO, "type" }, /* 1 */ + { CC_TIMECODE, RO, "timecode" }, /* 2 */ + { CC_POLL, RO, "poll" }, /* 3 */ + { CC_NOREPLY, RO, "noreply" }, /* 4 */ + { CC_BADFORMAT, RO, "badformat" }, /* 5 */ + { CC_BADDATA, RO, "baddata" }, /* 6 */ + { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */ + { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */ + { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */ + { CC_FUDGEVAL2, RO, "refid" }, /* 10 */ + { CC_FLAGS, RO, "flags" }, /* 11 */ + { CC_DEVICE, RO, "device" }, /* 12 */ + { CC_VARLIST, RO, "clock_var_list" },/* 13 */ + { 0, EOV, "" } +}; + + +/* + * Clock variables printed by default + */ +static u_char def_clock_var[] = { + CC_DEVICE, + CC_TYPE, /* won't be output if device= known */ + CC_TIMECODE, + CC_POLL, + CC_NOREPLY, + CC_BADFORMAT, + CC_BADDATA, + CC_FUDGETIME1, + CC_FUDGETIME2, + CC_FUDGEVAL1, + CC_FUDGEVAL2, + CC_FLAGS, + 0 +}; +#endif + + +/* + * System and processor definitions. These will change for the gizmo board. + */ +#ifndef HAVE_UNAME +# ifndef STR_SYSTEM +# define STR_SYSTEM "UNIX" +# endif +# ifndef STR_PROCESSOR +# define STR_PROCESSOR "unknown" +# endif + +static char str_system[] = STR_SYSTEM; +static char str_processor[] = STR_PROCESSOR; +#else +# include <sys/utsname.h> +static struct utsname utsnamebuf; +#endif /* HAVE_UNAME */ + +/* + * Trap structures. We only allow a few of these, and send + * a copy of each async message to each live one. Traps time + * out after an hour, it is up to the trap receipient to + * keep resetting it to avoid being timed out. + */ +/* ntp_request.c */ +struct ctl_trap ctl_trap[CTL_MAXTRAPS]; +int num_ctl_traps; + +/* + * Type bits, for ctlsettrap() call. + */ +#define TRAP_TYPE_CONFIG 0 /* used by configuration code */ +#define TRAP_TYPE_PRIO 1 /* priority trap */ +#define TRAP_TYPE_NONPRIO 2 /* nonpriority trap */ + + +/* + * List relating reference clock types to control message time sources. + * Index by the reference clock type. + * This list will only be used iff the reference clock driver doesn't + * set peer->sstclktype to something different than CTL_SST_TS_UNSPEC. + */ +static u_char clocktypes[] = { + CTL_SST_TS_NTP, /* REFCLK_NONE (0) */ + CTL_SST_TS_LOCAL, /* REFCLK_LOCALCLOCK (1) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_TRAK (2) */ + CTL_SST_TS_HF, /* REFCLK_WWV_PST (3) */ + CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM (4) */ + CTL_SST_TS_UHF, /* REFCLK_TRUETIME (5) */ + CTL_SST_TS_UHF, /* REFCLK_GOES_TRAK (6) */ + CTL_SST_TS_HF, /* REFCLK_CHU (7) */ + CTL_SST_TS_LF, /* REFCLOCK_PARSE (default) (8) */ + CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_ARBITER (11) */ + CTL_SST_TS_UHF, /* REFCLK_IRIG_TPRO (12) */ + CTL_SST_TS_ATOM, /* REFCLK_ATOM_LEITCH (13) */ + CTL_SST_TS_LF, /* REFCLK_MSF_EES (14) */ + CTL_SST_TS_UHF, /* REFCLK_TRUETIME (15) */ + CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */ + CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACTS (18) */ + CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_VME (21) */ + CTL_SST_TS_ATOM, /* REFCLK_ATOM_PPS (22) */ + CTL_SST_TS_TELEPHONE, /* REFCLK_PTB_ACTS (23) */ + CTL_SST_TS_TELEPHONE, /* REFCLK_USNO (24) */ + CTL_SST_TS_UHF, /* REFCLK_TRUETIME (25) */ + CTL_SST_TS_UHF, /* REFCLK_GPS_HP (26) */ + CTL_SST_TS_TELEPHONE, /* REFCLK_ARCRON_MSF (27) */ + CTL_SST_TS_TELEPHONE, /* REFCLK_SHM (28) */ + CTL_SST_TS_UHF, /* REFCLK_PALISADE (29) */ + CTL_SST_TS_UHF, /* REFCLK_ONCORE (30) */ + CTL_SST_TS_UHF, /* REFCLK_JUPITER (31) */ + CTL_SST_TS_LF, /* REFCLK_CHRONOLOG (32) */ + CTL_SST_TS_LF, /* REFCLK_DUMBCLOCK (32) */ + CTL_SST_TS_LF, /* REFCLK_ULINK (33) */ +}; + + +/* + * Keyid used for authenticating write requests. + */ +u_long ctl_auth_keyid; + +/* + * We keep track of the last error reported by the system internally + */ +static u_char ctl_sys_last_event; +static u_char ctl_sys_num_events; + + +/* + * Statistic counters to keep track of requests and responses. + */ +u_long ctltimereset; /* time stats reset */ +u_long numctlreq; /* number of requests we've received */ +u_long numctlbadpkts; /* number of bad control packets */ +u_long numctlresponses; /* number of resp packets sent with data */ +u_long numctlfrags; /* number of fragments sent */ +u_long numctlerrors; /* number of error responses sent */ +u_long numctltooshort; /* number of too short input packets */ +u_long numctlinputresp; /* number of responses on input */ +u_long numctlinputfrag; /* number of fragments on input */ +u_long numctlinputerr; /* number of input pkts with err bit set */ +u_long numctlbadoffset; /* number of input pkts with nonzero offset */ +u_long numctlbadversion; /* number of input pkts with unknown version */ +u_long numctldatatooshort; /* data too short for count */ +u_long numctlbadop; /* bad op code found in packet */ +u_long numasyncmsgs; /* number of async messages we've sent */ + +/* + * Response packet used by these routines. Also some state information + * so that we can handle packet formatting within a common set of + * subroutines. Note we try to enter data in place whenever possible, + * but the need to set the more bit correctly means we occasionally + * use the extra buffer and copy. + */ +static struct ntp_control rpkt; +static u_char res_version; +static u_char res_opcode; +static u_short res_associd; +static int res_offset; +static u_char * datapt; +static u_char * dataend; +static int datalinelen; +static int datanotbinflag; +static struct sockaddr_in *rmt_addr; +static struct interface *lcl_inter; + +static u_char res_authenticate; +static u_char res_authokay; +static u_long res_keyid; + +#define MAXDATALINELEN (72) + +static u_char res_async; /* set to 1 if this is async trap response */ + +/* + * Pointers for saving state when decoding request packets + */ +static char *reqpt; +static char *reqend; + +/* + * init_control - initialize request data + */ +void +init_control(void) +{ + int i; + +#ifdef HAVE_UNAME + uname(&utsnamebuf); +#endif /* HAVE_UNAME */ + + ctl_clr_stats(); + + ctl_auth_keyid = 0; + ctl_sys_last_event = EVNT_UNSPEC; + ctl_sys_num_events = 0; + + num_ctl_traps = 0; + for (i = 0; i < CTL_MAXTRAPS; i++) + ctl_trap[i].tr_flags = 0; +} + + +/* + * ctl_error - send an error response for the current request + */ +static void +ctl_error( + int errcode + ) +{ +#ifdef DEBUG + if (debug >= 4) + printf("sending control error %d\n", errcode); +#endif + /* + * fill in the fields. We assume rpkt.sequence and rpkt.associd + * have already been filled in. + */ + rpkt.r_m_e_op = (u_char) (CTL_RESPONSE|CTL_ERROR|(res_opcode & CTL_OP_MASK)); + rpkt.status = htons((u_short) ((errcode<<8) & 0xff00)); + rpkt.count = 0; + + /* + * send packet and bump counters + */ + if (res_authenticate && sys_authenticate) { + int maclen; + + *(u_int32 *)((u_char *)&rpkt + CTL_HEADER_LEN) + = htonl(res_keyid); + maclen = authencrypt(res_keyid, (u_int32 *)&rpkt, + CTL_HEADER_LEN); + sendpkt(rmt_addr, lcl_inter, -2, (struct pkt *)&rpkt, + CTL_HEADER_LEN + maclen); + } else { + sendpkt(rmt_addr, lcl_inter, -3, (struct pkt *)&rpkt, + CTL_HEADER_LEN); + } + numctlerrors++; +} + + +/* + * process_control - process an incoming control message + */ +void +process_control( + struct recvbuf *rbufp, + int restrict_mask + ) +{ + register struct ntp_control *pkt; + register int req_count; + register int req_data; + register struct ctl_proc *cc; + int properlen; + int maclen; + +#ifdef DEBUG + if (debug > 1) + printf("in process_control()\n"); +#endif + + /* + * Save the addresses for error responses + */ + numctlreq++; + rmt_addr = &rbufp->recv_srcadr; + lcl_inter = rbufp->dstadr; + pkt = (struct ntp_control *)&rbufp->recv_pkt; + + /* + * If the length is less than required for the header, or + * it is a response or a fragment, ignore this. + */ + if (rbufp->recv_length < CTL_HEADER_LEN + || pkt->r_m_e_op & (CTL_RESPONSE|CTL_MORE|CTL_ERROR) + || pkt->offset != 0) { +#ifdef DEBUG + if (debug) + printf("invalid format in control packet\n"); +#endif + if (rbufp->recv_length < CTL_HEADER_LEN) + numctltooshort++; + if (pkt->r_m_e_op & CTL_RESPONSE) + numctlinputresp++; + if (pkt->r_m_e_op & CTL_MORE) + numctlinputfrag++; + if (pkt->r_m_e_op & CTL_ERROR) + numctlinputerr++; + if (pkt->offset != 0) + numctlbadoffset++; + return; + } + res_version = PKT_VERSION(pkt->li_vn_mode); + if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) { +#ifdef DEBUG + if (debug) + printf("unknown version %d in control packet\n", + res_version); +#endif + numctlbadversion++; + return; + } + + /* + * Pull enough data from the packet to make intelligent responses + */ + rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version, MODE_CONTROL); + res_opcode = pkt->r_m_e_op; + rpkt.sequence = pkt->sequence; + rpkt.associd = pkt->associd; + rpkt.status = 0; + res_offset = 0; + res_associd = htons(pkt->associd); + res_async = 0; + res_authenticate = 0; + res_keyid = 0; + res_authokay = 0; + req_count = (int)htons(pkt->count); + datanotbinflag = 0; + datalinelen = 0; + datapt = rpkt.data; + dataend = &(rpkt.data[CTL_MAX_DATA_LEN]); + + /* + * We're set up now. Make sure we've got at least + * enough incoming data space to match the count. + */ + req_data = rbufp->recv_length - CTL_HEADER_LEN; + if (req_data < req_count || rbufp->recv_length & 0x3) { + ctl_error(CERR_BADFMT); + numctldatatooshort++; + return; + } + + properlen = req_count + CTL_HEADER_LEN; +#ifdef DEBUG + if (debug >= 2 && (rbufp->recv_length & 0x3) != 0) + printf("Packet length %d unrounded\n", rbufp->recv_length); +#endif + /* round up proper len to a 8 octet boundary */ + + properlen = (properlen + 7) & ~7; + maclen = rbufp->recv_length - properlen; + if ((rbufp->recv_length & (sizeof(u_long) - 1)) == 0 && + maclen >= MIN_MAC_LEN && maclen <= MAX_MAC_LEN && + sys_authenticate) { + res_authenticate = 1; + res_keyid = ntohl(*(u_int32 *)((u_char *)pkt + properlen)); + +#ifdef DEBUG + if (debug >= 3) + printf( + "recv_len %d, properlen %d, wants auth with keyid %ld, MAC length=%d\n", + rbufp->recv_length, properlen, res_keyid, maclen); +#endif + if (!authistrusted(res_keyid)) { +#ifdef DEBUG + if (debug >= 2) + printf("invalid keyid %lu\n", res_keyid); +#endif + } else if (authdecrypt(res_keyid, (u_int32 *)pkt, + rbufp->recv_length - maclen, maclen)) { +#ifdef DEBUG + if (debug >= 3) + printf("authenticated okay\n"); +#endif + res_authokay = 1; + } else { +#ifdef DEBUG + if (debug >= 3) + printf("authentication failed\n"); +#endif + res_keyid = 0; + } + } + + /* + * Set up translate pointers + */ + reqpt = (char *)pkt->data; + reqend = reqpt + req_count; + + /* + * Look for the opcode processor + */ + for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) { + if (cc->control_code == res_opcode) { +#ifdef DEBUG + if (debug >= 2) + printf("opcode %d, found command handler\n", + res_opcode); +#endif + if (cc->flags == AUTH && (!res_authokay + || res_keyid != ctl_auth_keyid)) { + ctl_error(CERR_PERMISSION); + return; + } + (cc->handler)(rbufp, restrict_mask); + return; + } + } + + /* + * Can't find this one, return an error. + */ + numctlbadop++; + ctl_error(CERR_BADOP); + return; +} + + +/* + * ctlpeerstatus - return a status word for this peer + */ +u_short +ctlpeerstatus( + register struct peer *peer + ) +{ + register u_short status; + + status = peer->status; + if (peer->flags & FLAG_CONFIG) + status |= CTL_PST_CONFIG; + if (peer->flags & FLAG_AUTHENABLE) + status |= CTL_PST_AUTHENABLE; + if (peer->flags & FLAG_AUTHENTIC) + status |= CTL_PST_AUTHENTIC; + if (peer->reach != 0) + status |= CTL_PST_REACH; + return (u_short)CTL_PEER_STATUS(status, peer->num_events, + peer->last_event); +} + + +/* + * ctlclkstatus - return a status word for this clock + */ +static u_short +ctlclkstatus( + struct refclockstat *this_clock + ) +{ + return ((u_short)(this_clock->currentstatus) << 8) + | (u_short)(this_clock->lastevent); +} + + + +/* + * ctlsysstatus - return the system status word + */ +u_short +ctlsysstatus(void) +{ + register u_char this_clock; + + this_clock = CTL_SST_TS_UNSPEC; + if (sys_peer != 0) { + if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) { + this_clock = sys_peer->sstclktype; + if (pps_control) + this_clock |= CTL_SST_TS_PPS; + } else { + if (sys_peer->refclktype < sizeof(clocktypes)) + this_clock = clocktypes[sys_peer->refclktype]; + if (pps_control) + this_clock |= CTL_SST_TS_PPS; + } + } + return (u_short)CTL_SYS_STATUS(sys_leap, this_clock, + ctl_sys_num_events, ctl_sys_last_event); +} + + + +/* + * ctl_flushpkt - write out the current packet and prepare + * another if necessary. + */ +static void +ctl_flushpkt( + int more + ) +{ + int dlen; + int sendlen; + + if (!more && datanotbinflag) { + /* + * Big hack, output a trailing \r\n + */ + *datapt++ = '\r'; + *datapt++ = '\n'; + } + dlen = datapt - (u_char *)rpkt.data; + sendlen = dlen + CTL_HEADER_LEN; + + /* + * Pad to a multiple of 32 bits + */ + while (sendlen & 0x3) { + *datapt++ = '\0'; + sendlen++; + } + + /* + * Fill in the packet with the current info + */ + rpkt.r_m_e_op = (u_char)(CTL_RESPONSE|more|(res_opcode & CTL_OP_MASK)); + rpkt.count = htons((u_short) dlen); + rpkt.offset = htons( (u_short) res_offset); + if (res_async) { + register int i; + + for (i = 0; i < CTL_MAXTRAPS; i++) { + if (ctl_trap[i].tr_flags & TRAP_INUSE) { + rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, + ctl_trap[i].tr_version, MODE_CONTROL); + rpkt.sequence = htons(ctl_trap[i].tr_sequence); + sendpkt(&ctl_trap[i].tr_addr, + ctl_trap[i].tr_localaddr, + -4, + (struct pkt *)&rpkt, sendlen); + if (!more) + ctl_trap[i].tr_sequence++; + numasyncmsgs++; + } + } + } else { + if (res_authenticate && sys_authenticate) { + int maclen; + int totlen = sendlen; + u_long keyid = htonl(res_keyid); + + /* + * If we are going to authenticate, then there is + * an additional requirement that the MAC begin on + * a 64 bit boundary. + */ + while (totlen & 7) { + *datapt++ = '\0'; + totlen++; + } + memcpy(datapt, &keyid, sizeof keyid); + maclen = authencrypt(res_keyid, (u_int32 *)&rpkt, + totlen); + sendpkt(rmt_addr, lcl_inter, -5, (struct pkt *)&rpkt, + totlen + maclen); + } else { + sendpkt(rmt_addr, lcl_inter, -6, (struct pkt *)&rpkt, + sendlen); + } + if (more) + numctlfrags++; + else + numctlresponses++; + } + + /* + * Set us up for another go around. + */ + res_offset += dlen; + datapt = (u_char *)rpkt.data; +} + + +/* + * ctl_putdata - write data into the packet, fragmenting and + * starting another if this one is full. + */ +static void +ctl_putdata( + const char *dp, + unsigned int dlen, + int bin /* set to 1 when data is binary */ + ) +{ + int overhead; + + overhead = 0; + if (!bin) { + datanotbinflag = 1; + overhead = 3; + if (datapt != rpkt.data) { + *datapt++ = ','; + datalinelen++; + if ((dlen + datalinelen + 1) >= MAXDATALINELEN) { + *datapt++ = '\r'; + *datapt++ = '\n'; + datalinelen = 0; + } else { + *datapt++ = ' '; + datalinelen++; + } + } + } + + /* + * Save room for trailing junk + */ + if (dlen + overhead + datapt > dataend) { + /* + * Not enough room in this one, flush it out. + */ + ctl_flushpkt(CTL_MORE); + } + + memmove((char *)datapt, dp, (unsigned)dlen); + datapt += dlen; + datalinelen += dlen; +} + + +/* + * ctl_putstr - write a tagged string into the response packet + */ +static void +ctl_putstr( + const char *tag, + const char *data, + unsigned int len + ) +{ + register char *cp; + register const char *cq; + char buffer[400]; + + cp = buffer; + cq = tag; + while (*cq != '\0') + *cp++ = *cq++; + + if (len > 0) { + *cp++ = '='; + *cp++ = '"'; + if (len > (int) (sizeof(buffer) - (cp - buffer) - 1)) + len = sizeof(buffer) - (cp - buffer) - 1; + memmove(cp, data, (unsigned)len); + cp += len; + *cp++ = '"'; + } + + ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); +} + + +/* + * ctl_putdbl - write a tagged, signed double into the response packet + */ +static void +ctl_putdbl( + const char *tag, + double ts + ) +{ + register char *cp; + register const char *cq; + char buffer[200]; + + cp = buffer; + cq = tag; + while (*cq != '\0') + *cp++ = *cq++; + *cp++ = '='; + (void)sprintf(cp, "%.3f", ts); + while (*cp != '\0') + cp++; + ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); +} + +/* + * ctl_putuint - write a tagged unsigned integer into the response + */ +static void +ctl_putuint( + const char *tag, + u_long uval + ) +{ + register char *cp; + register const char *cq; + char buffer[200]; + + cp = buffer; + cq = tag; + while (*cq != '\0') + *cp++ = *cq++; + + *cp++ = '='; + (void) sprintf(cp, "%lu", uval); + while (*cp != '\0') + cp++; + + ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); +} + + +/* + * ctl_puthex - write a tagged unsigned integer, in hex, into the response + */ +static void +ctl_puthex( + const char *tag, + u_long uval + ) +{ + register char *cp; + register const char *cq; + char buffer[200]; + + cp = buffer; + cq = tag; + while (*cq != '\0') + *cp++ = *cq++; + + *cp++ = '='; + (void) sprintf(cp, "0x%lx", uval); + while (*cp != '\0') + cp++; + + ctl_putdata(buffer,(unsigned)( cp - buffer ), 0); +} + + +/* + * ctl_putint - write a tagged signed integer into the response + */ +static void +ctl_putint( + const char *tag, + long ival + ) +{ + register char *cp; + register const char *cq; + char buffer[200]; + + cp = buffer; + cq = tag; + while (*cq != '\0') + *cp++ = *cq++; + + *cp++ = '='; + (void) sprintf(cp, "%ld", ival); + while (*cp != '\0') + cp++; + + ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); +} + + +/* + * ctl_putts - write a tagged timestamp, in hex, into the response + */ +static void +ctl_putts( + const char *tag, + l_fp *ts + ) +{ + register char *cp; + register const char *cq; + char buffer[200]; + + cp = buffer; + cq = tag; + while (*cq != '\0') + *cp++ = *cq++; + + *cp++ = '='; + (void) sprintf(cp, "0x%08lx.%08lx", ts->l_ui & 0xffffffffL, + ts->l_uf & 0xffffffffL); + while (*cp != '\0') + cp++; + + ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); +} + + +/* + * ctl_putadr - write a dotted quad IP address into the response + */ +static void +ctl_putadr( + const char *tag, + u_int32 addr + ) +{ + register char *cp; + register const char *cq; + char buffer[200]; + + cp = buffer; + cq = tag; + while (*cq != '\0') + *cp++ = *cq++; + + *cp++ = '='; + cq = numtoa(addr); + while (*cq != '\0') + *cp++ = *cq++; + + ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); +} + + +/* + * ctl_putid - write a tagged clock ID into the response + */ +static void +ctl_putid( + const char *tag, + char *id + ) +{ + register char *cp; + register const char *cq; + char buffer[200]; + + cp = buffer; + cq = tag; + while (*cq != '\0') + *cp++ = *cq++; + + *cp++ = '='; + cq = id; + while (*cq != '\0' && (cq - id) < 4) + *cp++ = *cq++; + + ctl_putdata(buffer, (unsigned)( cp - buffer ), 0); +} + + +/* + * ctl_putarray - write a tagged eight element double array into the response + */ +static void +ctl_putarray( + const char *tag, + double *arr, + int start + ) +{ + register char *cp; + register const char *cq; + char buffer[200]; + int i; + + cp = buffer; + cq = tag; + while (*cq != '\0') + *cp++ = *cq++; + i = start; + do { + if (i == 0) + i = NTP_SHIFT; + i--; + (void)sprintf(cp, " %.2f", arr[i] * 1e3); + while (*cp != '\0') + cp++; + } while(i != start); + ctl_putdata(buffer, (unsigned)(cp - buffer), 0); +} + + +/* + * ctl_putsys - output a system variable + */ +static void +ctl_putsys( + int varid + ) +{ + l_fp tmp; +#ifdef HAVE_UNAME + char str[50]; +#endif + + switch (varid) { + case CS_LEAP: + ctl_putuint(sys_var[CS_LEAP].text, sys_leap); + break; + case CS_STRATUM: + ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum); + break; + case CS_PRECISION: + ctl_putint(sys_var[CS_PRECISION].text, sys_precision); + break; + case CS_ROOTDELAY: + ctl_putdbl(sys_var[CS_ROOTDELAY].text, sys_rootdelay * 1e3); + break; + case CS_ROOTDISPERSION: + ctl_putdbl(sys_var[CS_ROOTDISPERSION].text, + sys_rootdispersion * 1e3); + break; + case CS_REFID: + if (sys_stratum > 1) + ctl_putadr(sys_var[CS_REFID].text, sys_refid); + else + ctl_putid(sys_var[CS_REFID].text, (char *)&sys_refid); + break; + case CS_REFTIME: + ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime); + break; + case CS_POLL: + ctl_putuint(sys_var[CS_POLL].text, sys_poll); + break; + case CS_PEERID: + if (sys_peer == NULL) + ctl_putuint(sys_var[CS_PEERID].text, 0); + else + ctl_putuint(sys_var[CS_PEERID].text, + sys_peer->associd); + break; + case CS_STATE: + ctl_putuint(sys_var[CS_STATE].text, (unsigned)state); + break; + case CS_OFFSET: + ctl_putdbl(sys_var[CS_OFFSET].text, last_offset * 1e3); + break; + case CS_DRIFT: + ctl_putdbl(sys_var[CS_DRIFT].text, drift_comp * 1e6); + break; + case CS_COMPLIANCE: + ctl_putdbl(sys_var[CS_COMPLIANCE].text, sys_error * 1e3); + break; + case CS_CLOCK: + get_systime(&tmp); + ctl_putts(sys_var[CS_CLOCK].text, &tmp); + break; + case CS_PROCESSOR: +#ifndef HAVE_UNAME + ctl_putstr(sys_var[CS_PROCESSOR].text, str_processor, + sizeof(str_processor) - 1); +#else + ctl_putstr(sys_var[CS_PROCESSOR].text, utsnamebuf.machine, + strlen(utsnamebuf.machine)); +#endif /* HAVE_UNAME */ + break; + case CS_SYSTEM: +#ifndef HAVE_UNAME + ctl_putstr(sys_var[CS_SYSTEM].text, str_system, + sizeof(str_system) - 1); +#else + (void)strcpy(str, utsnamebuf.sysname); + (void)strcat(str, utsnamebuf.release); + ctl_putstr(sys_var[CS_SYSTEM].text, str, strlen(str)); +#endif /* HAVE_UNAME */ + break; + case CS_STABIL: + ctl_putdbl(sys_var[CS_STABIL].text, clock_stability * 1e6); + break; + case CS_VARLIST: + { + char buf[CTL_MAX_DATA_LEN]; + register char *s, *t, *be; + register const char *ss; + register int i; + register struct ctl_var *k; + + s = buf; + be = buf + sizeof(buf) - strlen(sys_var[CS_VARLIST].text) - 4; + if (s > be) + break; /* really long var name 8-( - Killer */ + + strcpy(s, sys_var[CS_VARLIST].text); + strcat(s, "=\""); + s += strlen(s); + t = s; + + for (k = sys_var; !(k->flags &EOV); k++) + { + if (k->flags & PADDING) + continue; + + i = strlen(k->text); + if (s+i+1 >= be) + break; + if (s != t) + *s++ = ','; + strcpy(s, k->text); + s += i; + } + + for (k = ext_sys_var; k && !(k->flags &EOV); k++) + { + if (k->flags & PADDING) + continue; + + ss = k->text; + if (!ss) + continue; + + while (*ss && *ss != '=') + ss++; + + i = ss - k->text; + if (s+i+1 >= be) + break; + if (s != t) + *s++ = ','; + strncpy(s, k->text, (unsigned)i); + s += i; + } + + if (s+2 >= be) + break; + + *s++ = '"'; + *s = '\0'; + + ctl_putdata(buf, (unsigned)( s - buf ), 0); + } + break; + } +} + + +/* + * ctl_putpeer - output a peer variable + */ +static void +ctl_putpeer( + int varid, + struct peer *peer + ) +{ + switch (varid) { + case CP_CONFIG: + ctl_putuint(peer_var[CP_CONFIG].text, + (unsigned)((peer->flags & FLAG_CONFIG) != 0)); + break; + case CP_AUTHENABLE: + ctl_putuint(peer_var[CP_AUTHENABLE].text, + (unsigned)((peer->flags & FLAG_AUTHENABLE) != 0)); + break; + case CP_AUTHENTIC: + ctl_putuint(peer_var[CP_AUTHENTIC].text, + (unsigned)((peer->flags & FLAG_AUTHENTIC) != 0)); + break; + case CP_SRCADR: + ctl_putadr(peer_var[CP_SRCADR].text, + peer->srcadr.sin_addr.s_addr); + break; + case CP_SRCPORT: + ctl_putuint(peer_var[CP_SRCPORT].text, + ntohs(peer->srcadr.sin_port)); + break; + case CP_DSTADR: + ctl_putadr(peer_var[CP_DSTADR].text, + peer->processed ? + peer->cast_flags & MDF_BCAST ? + peer->dstadr->bcast.sin_addr.s_addr: + peer->cast_flags ? + peer->dstadr->sin.sin_addr.s_addr ? + peer->dstadr->sin.sin_addr.s_addr: + peer->dstadr->bcast.sin_addr.s_addr: + 8 : 12); + break; + case CP_DSTPORT: + ctl_putuint(peer_var[CP_DSTPORT].text, + (u_long)(peer->dstadr + ? ntohs(peer->dstadr->sin.sin_port) + : 0 + ) + ); + break; + case CP_LEAP: + ctl_putuint(peer_var[CP_LEAP].text, peer->leap); + break; + case CP_HMODE: + ctl_putuint(peer_var[CP_HMODE].text, peer->hmode); + break; + case CP_STRATUM: + ctl_putuint(peer_var[CP_STRATUM].text, peer->stratum); + break; + case CP_PPOLL: + ctl_putuint(peer_var[CP_PPOLL].text, peer->ppoll); + break; + case CP_HPOLL: + ctl_putuint(peer_var[CP_HPOLL].text, peer->hpoll); + break; + case CP_PRECISION: + ctl_putint(peer_var[CP_PRECISION].text, peer->precision); + break; + case CP_ROOTDELAY: + ctl_putdbl(peer_var[CP_ROOTDELAY].text, peer->rootdelay * 1e3); + break; + case CP_ROOTDISPERSION: + ctl_putdbl(peer_var[CP_ROOTDISPERSION].text, + peer->rootdispersion * 1e3); + break; + case CP_REFID: + if (peer->stratum > 1) + { + if (peer->flags & FLAG_REFCLOCK) + ctl_putadr(peer_var[CP_REFID].text, + peer->srcadr.sin_addr.s_addr); + else + ctl_putadr(peer_var[CP_REFID].text, + peer->refid); + } + else + ctl_putid(peer_var[CP_REFID].text, + (char *)&peer->refid); + break; + case CP_REFTIME: + ctl_putts(peer_var[CP_REFTIME].text, &peer->reftime); + break; + case CP_ORG: + ctl_putts(peer_var[CP_ORG].text, &peer->org); + break; + case CP_REC: + ctl_putts(peer_var[CP_REC].text, &peer->rec); + break; + case CP_XMT: + ctl_putts(peer_var[CP_XMT].text, &peer->xmt); + break; + case CP_REACH: + ctl_puthex(peer_var[CP_REACH].text, peer->reach); + break; + case CP_FLASH: + ctl_puthex(peer_var[CP_FLASH].text, peer->flash); + break; + case CP_VALID: + ctl_putuint(peer_var[CP_VALID].text, peer->valid); + break; + case CP_TIMER: + ctl_putuint(peer_var[CP_TIMER].text, + peer->nextdate - current_time); + break; + case CP_DELAY: + ctl_putdbl(peer_var[CP_DELAY].text, peer->delay * 1e3); + break; + case CP_OFFSET: + ctl_putdbl(peer_var[CP_OFFSET].text, peer->offset * 1e3); + break; + case CP_JITTER: + ctl_putdbl(peer_var[CP_JITTER].text, + SQRT(peer->variance) * 1e3); + break; + case CP_DISPERSION: + ctl_putdbl(peer_var[CP_DISPERSION].text, peer->disp * 1e3); + break; + case CP_KEYID: + ctl_putuint(peer_var[CP_KEYID].text, peer->keyid); + break; + case CP_FILTDELAY: + ctl_putarray(peer_var[CP_FILTDELAY].text, + peer->filter_delay, (int)peer->filter_nextpt); + break; + case CP_FILTOFFSET: + ctl_putarray(peer_var[CP_FILTOFFSET].text, + peer->filter_offset, (int)peer->filter_nextpt); + break; + case CP_FILTERROR: + ctl_putarray(peer_var[CP_FILTERROR].text, + peer->filter_disp, (int)peer->filter_nextpt); + break; + case CP_PMODE: + ctl_putuint(peer_var[CP_PMODE].text, peer->pmode); + break; + case CP_RECEIVED: + ctl_putuint(peer_var[CP_RECEIVED].text, peer->received); + break; + case CP_SENT: + ctl_putuint(peer_var[CP_SENT].text, peer->sent); + break; + case CP_VARLIST: + { + char buf[CTL_MAX_DATA_LEN]; + register char *s, *t, *be; + register int i; + register struct ctl_var *k; + + s = buf; + be = buf + sizeof(buf) - strlen(peer_var[CP_VARLIST].text) - 4; + if (s > be) + break; /* really long var name 8-( - Killer */ + + strcpy(s, peer_var[CP_VARLIST].text); + strcat(s, "=\""); + s += strlen(s); + t = s; + + for (k = peer_var; !(k->flags &EOV); k++) + { + if (k->flags & PADDING) + continue; + + i = strlen(k->text); + if (s+i+1 >= be) + break; + if (s != t) + *s++ = ','; + strcpy(s, k->text); + s += i; + } + + if (s+2 >= be) + break; + + *s++ = '"'; + *s = '\0'; + + ctl_putdata(buf, (unsigned)(s - buf), 0); + } + break; + } +} + + +#ifdef REFCLOCK +/* + * ctl_putclock - output clock variables + */ +static void +ctl_putclock( + int varid, + struct refclockstat *clock_stat, + int mustput + ) +{ + switch(varid) { + case CC_TYPE: + if (mustput || clock_stat->clockdesc == NULL + || *(clock_stat->clockdesc) == '\0') { + ctl_putuint(clock_var[CC_TYPE].text, clock_stat->type); + } + break; + case CC_TIMECODE: + ctl_putstr(clock_var[CC_TIMECODE].text, clock_stat->p_lastcode, + (unsigned)clock_stat->lencode); + break; + case CC_POLL: + ctl_putuint(clock_var[CC_POLL].text, clock_stat->polls); + break; + case CC_NOREPLY: + ctl_putuint(clock_var[CC_NOREPLY].text, clock_stat->noresponse); + break; + case CC_BADFORMAT: + ctl_putuint(clock_var[CC_BADFORMAT].text, clock_stat->badformat); + break; + case CC_BADDATA: + ctl_putuint(clock_var[CC_BADDATA].text, clock_stat->baddata); + break; + case CC_FUDGETIME1: + if (mustput || (clock_stat->haveflags & CLK_HAVETIME1)) { + ctl_putdbl(clock_var[CC_FUDGETIME1].text, + clock_stat->fudgetime1 * 1e3); + } + break; + case CC_FUDGETIME2: + if (mustput || (clock_stat->haveflags & CLK_HAVETIME2)) { + ctl_putdbl(clock_var[CC_FUDGETIME2].text, + clock_stat->fudgetime2 * 1e3); + } + break; + case CC_FUDGEVAL1: + if (mustput || (clock_stat->haveflags & CLK_HAVEVAL1)) + ctl_putint(clock_var[CC_FUDGEVAL1].text, + clock_stat->fudgeval1); + break; + case CC_FUDGEVAL2: + if (mustput || (clock_stat->haveflags & CLK_HAVEVAL2)) { + if (clock_stat->fudgeval1 > 1) + ctl_putadr(clock_var[CC_FUDGEVAL2].text, + (u_int32)clock_stat->fudgeval2); + else + ctl_putid(clock_var[CC_FUDGEVAL2].text, + (char *)&clock_stat->fudgeval2); + } + break; + case CC_FLAGS: + if (mustput || (clock_stat->haveflags & + (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4))) + ctl_putuint(clock_var[CC_FLAGS].text, clock_stat->flags); + break; + case CC_DEVICE: + if (clock_stat->clockdesc == NULL || *(clock_stat->clockdesc) == '\0') { + if (mustput) + ctl_putstr(clock_var[CC_DEVICE].text, "", 0); + } else { + ctl_putstr(clock_var[CC_DEVICE].text, clock_stat->clockdesc, + strlen(clock_stat->clockdesc)); + } + break; + case CC_VARLIST: + { + char buf[CTL_MAX_DATA_LEN]; + register char *s, *t, *be; + register const char *ss; + register int i; + register struct ctl_var *k; + + s = buf; + be = buf + sizeof(buf); + if (s + strlen(clock_var[CC_VARLIST].text) + 4 > be) + break; /* really long var name 8-( - Killer */ + + strcpy(s, clock_var[CC_VARLIST].text); + strcat(s, "=\""); + s += strlen(s); + t = s; + + for (k = clock_var; !(k->flags &EOV); k++) + { + if (k->flags & PADDING) + continue; + + i = strlen(k->text); + if (s+i+1 >= be) + break; + if (s != t) + *s++ = ','; + strcpy(s, k->text); + s += i; + } + + for (k = clock_stat->kv_list; k && !(k->flags &EOV); k++) + { + if (k->flags & PADDING) + continue; + + ss = k->text; + if (!ss) + continue; + + while (*ss && *ss != '=') + ss++; + + i = ss - k->text; + if (s+i+1 >= be) + break; + if (s != t) + *s++ = ','; + strncpy(s, k->text, (unsigned)i); + s += i; + *s = '\0'; + } + + if (s+2 >= be) + break; + + *s++ = '"'; + *s = '\0'; + + ctl_putdata(buf, (unsigned)( s - buf ), 0); + } + break; + } +} +#endif + + + +/* + * ctl_getitem - get the next data item from the incoming packet + */ +static struct ctl_var * +ctl_getitem( + struct ctl_var *var_list, + char **data + ) +{ + register struct ctl_var *v; + register char *cp; + register char *tp; + static struct ctl_var eol = { 0, EOV, }; + static char buf[128]; + + /* + * Delete leading commas and white space + */ + while (reqpt < reqend && (*reqpt == ',' || isspace((int)*reqpt))) { + reqpt++; + } + + if (reqpt >= reqend) + return 0; + + if (var_list == (struct ctl_var *)0) + return &eol; + + /* + * Look for a first character match on the tag. If we find + * one, see if it is a full match. + */ + v = var_list; + cp = reqpt; + while (!(v->flags & EOV)) { + if (!(v->flags & PADDING) && *cp == *(v->text)) { + tp = v->text; + while (*tp != '\0' && *tp != '=' && cp < reqend && *cp == *tp) { + cp++; + tp++; + } + if ((*tp == '\0') || (*tp == '=')) { + while (cp < reqend && isspace((int)*cp)) + cp++; + if (cp == reqend || *cp == ',') { + buf[0] = '\0'; + *data = buf; + if (cp < reqend) + cp++; + reqpt = cp; + return v; + } + if (*cp == '=') { + cp++; + tp = buf; + while (cp < reqend && isspace((int)*cp)) + cp++; + while (cp < reqend && *cp != ',') + *tp++ = *cp++; + if (cp < reqend) + cp++; + *tp = '\0'; + while (isspace((int)(*(tp-1)))) + *(--tp) = '\0'; + reqpt = cp; + *data = buf; + return v; + } + } + cp = reqpt; + } + v++; + } + return v; +} + + +/* + * control_unspec - response to an unspecified op-code + */ +/*ARGSUSED*/ +static void +control_unspec( + struct recvbuf *rbufp, + int restrict_mask + ) +{ + struct peer *peer; + + /* + * What is an appropriate response to an unspecified op-code? + * I return no errors and no data, unless a specified assocation + * doesn't exist. + */ + if (res_associd != 0) { + if ((peer = findpeerbyassoc((int)res_associd)) == 0) { + ctl_error(CERR_BADASSOC); + return; + } + rpkt.status = htons(ctlpeerstatus(peer)); + } else { + rpkt.status = htons(ctlsysstatus()); + } + ctl_flushpkt(0); +} + + +/* + * read_status - return either a list of associd's, or a particular + * peer's status. + */ +/*ARGSUSED*/ +static void +read_status( + struct recvbuf *rbufp, + int restrict_mask + ) +{ + register int i; + register struct peer *peer; + u_short ass_stat[CTL_MAX_DATA_LEN/sizeof(u_short)]; + +#ifdef DEBUG + if (debug >= 2) + printf("read_status: ID %d\n", res_associd); +#endif + /* + * Two choices here. If the specified association ID is + * zero we return all known assocation ID's. Otherwise + * we return a bunch of stuff about the particular peer. + */ + if (res_associd == 0) { + register int n; + + n = 0; + rpkt.status = htons(ctlsysstatus()); + for (i = 0; i < HASH_SIZE; i++) { + for (peer = assoc_hash[i]; peer != 0; + peer = peer->ass_next) { + ass_stat[n++] = htons(peer->associd); + ass_stat[n++] = htons(ctlpeerstatus(peer)); + if (n == CTL_MAX_DATA_LEN/sizeof(u_short)) { + ctl_putdata((char *)ass_stat, + n * sizeof(u_short), 1); + n = 0; + } + } + } + + if (n != 0) + ctl_putdata((char *)ass_stat, n * sizeof(u_short), 1); + ctl_flushpkt(0); + } else { + peer = findpeerbyassoc((int)res_associd); + if (peer == 0) { + ctl_error(CERR_BADASSOC); + } else { + register u_char *cp; + + rpkt.status = htons(ctlpeerstatus(peer)); + if (res_authokay) + peer->num_events = 0; + /* + * For now, output everything we know about the peer. + * May be more selective later. + */ + for (cp = def_peer_var; *cp != 0; cp++) + ctl_putpeer((int)*cp, peer); + ctl_flushpkt(0); + } + } +} + + +/* + * read_variables - return the variables the caller asks for + */ +/*ARGSUSED*/ +static void +read_variables( + struct recvbuf *rbufp, + int restrict_mask + ) +{ + register struct ctl_var *v; + register int i; + char *valuep; + u_char *wants; + unsigned int gotvar = (CS_MAXCODE>CP_MAXCODE) ? (CS_MAXCODE+1) : (CP_MAXCODE+1); + + if (res_associd == 0) { + /* + * Wants system variables. Figure out which he wants + * and give them to him. + */ + rpkt.status = htons(ctlsysstatus()); + if (res_authokay) + ctl_sys_num_events = 0; + gotvar += count_var(ext_sys_var); + wants = (u_char *)emalloc(gotvar); + memset((char *)wants, 0, gotvar); + gotvar = 0; + while ((v = ctl_getitem(sys_var, &valuep)) != 0) { + if (v->flags & EOV) { + if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) { + if (v->flags & EOV) { + ctl_error(CERR_UNKNOWNVAR); + free((char *)wants); + return; + } + wants[CS_MAXCODE+1+v->code] = 1; + gotvar = 1; + continue; + } else { + break; /* shouldn't happen ! */ + } + } + wants[v->code] = 1; + gotvar = 1; + } + if (gotvar) { + for (i = 1; i <= CS_MAXCODE; i++) + if (wants[i]) + ctl_putsys(i); + for (i = 0; ext_sys_var && !(ext_sys_var[i].flags & EOV); i++) + if (wants[i+CS_MAXCODE+1]) + ctl_putdata(ext_sys_var[i].text, + strlen(ext_sys_var[i].text), 0); + } else { + register u_char *cs; + register struct ctl_var *kv; + + for (cs = def_sys_var; *cs != 0; cs++) + ctl_putsys((int)*cs); + for (kv = ext_sys_var; kv && !(kv->flags & EOV); kv++) + if (kv->flags & DEF) + ctl_putdata(kv->text, strlen(kv->text), 0); + } + free((char *)wants); + } else { + register struct peer *peer; + + /* + * Wants info for a particular peer. See if we know + * the guy. + */ + peer = findpeerbyassoc((int)res_associd); + if (peer == 0) { + ctl_error(CERR_BADASSOC); + return; + } + + rpkt.status = htons(ctlpeerstatus(peer)); + if (res_authokay) + peer->num_events = 0; + wants = (u_char *)emalloc(gotvar); + memset((char*)wants, 0, gotvar); + gotvar = 0; + while ((v = ctl_getitem(peer_var, &valuep)) != 0) { + if (v->flags & EOV) { + ctl_error(CERR_UNKNOWNVAR); + free((char *)wants); + return; + } + wants[v->code] = 1; + gotvar = 1; + } + if (gotvar) { + for (i = 1; i <= CP_MAXCODE; i++) + if (wants[i]) + ctl_putpeer(i, peer); + } else { + register u_char *cp; + + for (cp = def_peer_var; *cp != 0; cp++) + ctl_putpeer((int)*cp, peer); + } + free((char *)wants); + } + ctl_flushpkt(0); +} + + +/* + * write_variables - write into variables. We only allow leap bit writing + * this way. + */ +/*ARGSUSED*/ +static void +write_variables( + struct recvbuf *rbufp, + int restrict_mask + ) +{ + register struct ctl_var *v; + register int ext_var; + char *valuep; + long val; + /*int leapind, leapwarn;*/ + + /* + * If he's trying to write into a peer tell him no way + */ + if (res_associd != 0) { + ctl_error(CERR_PERMISSION); + return; + } + + /* + * Set status + */ + rpkt.status = htons(ctlsysstatus()); + + /* + * Set flags to not-in-sync so we can tell when we get something. + */ + /* + leapind = ~0; + leapwarn = ~0; + */ + + /* + * Look through the variables. Dump out at the first sign of trouble. + */ + while ((v = ctl_getitem(sys_var, &valuep)) != 0) { + ext_var = 0; + if (v->flags & EOV) { + if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) { + if (v->flags & EOV) { + ctl_error(CERR_UNKNOWNVAR); + return; + } + ext_var = 1; + } else { + break; + } + } + if (!(v->flags & CAN_WRITE)) { + ctl_error(CERR_PERMISSION); + return; + } + if (!ext_var && (*valuep == '\0' || !atoint(valuep, &val))) { + ctl_error(CERR_BADFMT); + return; + } + if (!ext_var && (val & ~LEAP_NOTINSYNC) != 0) { + ctl_error(CERR_BADVALUE); + return; + } + + if (ext_var) { + char *s = (char *)emalloc(strlen(v->text)+strlen(valuep)+2); + const char *t; + char *tt = s; + + t = v->text; + while (*t && *t != '=') + *tt++ = *t++; + + *tt++ = '='; + strcat(tt, valuep); + + set_sys_var(s, strlen(s)+1, v->flags); + free(s); + } else { + /* + * This one seems sane. Save it. + */ + switch(v->code) { + case CS_LEAP: + default: + ctl_error(CERR_UNSPEC); /* our fault, really */ + return; + } + } + } + + /* + * If we got anything, do it. + */ + /* + if (leapind != ~0 || leapwarn != ~0) { + if (!leap_setleap((int)leapind, (int)leapwarn)) { + ctl_error(CERR_PERMISSION); + return; + } + } + */ + ctl_flushpkt(0); +} + + +/* + * read_clock_status - return clock radio status + */ +/*ARGSUSED*/ +static void +read_clock_status( + struct recvbuf *rbufp, + int restrict_mask + ) +{ +#ifndef REFCLOCK + /* + * If no refclock support, no data to return + */ + ctl_error(CERR_BADASSOC); +#else + register struct ctl_var *v; + register int i; + register struct peer *peer; + char *valuep; + u_char *wants; + unsigned int gotvar; + struct refclockstat clock_stat; + + if (res_associd == 0) { + /* + * Find a clock for this jerk. If the system peer + * is a clock use it, else search the hash tables + * for one. + */ + if (sys_peer != 0 && (sys_peer->flags & FLAG_REFCLOCK)) { + peer = sys_peer; + } else { + peer = 0; + for (i = 0; peer == 0 && i < HASH_SIZE; i++) { + for (peer = assoc_hash[i]; peer != 0; + peer = peer->ass_next) { + if (peer->flags & FLAG_REFCLOCK) + break; + } + } + if (peer == 0) { + ctl_error(CERR_BADASSOC); + return; + } + } + } else { + peer = findpeerbyassoc((int)res_associd); + if (peer == 0 || !(peer->flags & FLAG_REFCLOCK)) { + ctl_error(CERR_BADASSOC); + return; + } + } + + /* + * If we got here we have a peer which is a clock. Get his status. + */ + clock_stat.kv_list = (struct ctl_var *)0; + + refclock_control(&peer->srcadr, (struct refclockstat *)0, &clock_stat); + + /* + * Look for variables in the packet. + */ + rpkt.status = htons(ctlclkstatus(&clock_stat)); + gotvar = CC_MAXCODE+1+count_var(clock_stat.kv_list); + wants = (u_char *)emalloc(gotvar); + memset((char*)wants, 0, gotvar); + gotvar = 0; + while ((v = ctl_getitem(clock_var, &valuep)) != 0) { + if (v->flags & EOV) { + if ((v = ctl_getitem(clock_stat.kv_list, &valuep)) != 0) { + if (v->flags & EOV) { + ctl_error(CERR_UNKNOWNVAR); + free((char*)wants); + free_varlist(clock_stat.kv_list); + return; + } + wants[CC_MAXCODE+1+v->code] = 1; + gotvar = 1; + continue; + } else { + break; /* shouldn't happen ! */ + } + } + wants[v->code] = 1; + gotvar = 1; + } + + if (gotvar) { + for (i = 1; i <= CC_MAXCODE; i++) + if (wants[i]) + ctl_putclock(i, &clock_stat, 1); + for (i = 0; clock_stat.kv_list && !(clock_stat.kv_list[i].flags & EOV); i++) + if (wants[i+CC_MAXCODE+1]) + ctl_putdata(clock_stat.kv_list[i].text, + strlen(clock_stat.kv_list[i].text), 0); + } else { + register u_char *cc; + register struct ctl_var *kv; + + for (cc = def_clock_var; *cc != 0; cc++) + ctl_putclock((int)*cc, &clock_stat, 0); + for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV); kv++) + if (kv->flags & DEF) + ctl_putdata(kv->text, strlen(kv->text), 0); + } + + free((char*)wants); + free_varlist(clock_stat.kv_list); + + ctl_flushpkt(0); +#endif +} + + +/* + * write_clock_status - we don't do this + */ +/*ARGSUSED*/ +static void +write_clock_status( + struct recvbuf *rbufp, + int restrict_mask + ) +{ + ctl_error(CERR_PERMISSION); +} + +/* + * Trap support from here on down. We send async trap messages when the + * upper levels report trouble. Traps can by set either by control + * messages or by configuration. + */ + +/* + * set_trap - set a trap in response to a control message + */ +static void +set_trap( + struct recvbuf *rbufp, + int restrict_mask + ) +{ + int traptype; + + /* + * See if this guy is allowed + */ + if (restrict_mask & RES_NOTRAP) { + ctl_error(CERR_PERMISSION); + return; + } + + /* + * Determine his allowed trap type. + */ + traptype = TRAP_TYPE_PRIO; + if (restrict_mask & RES_LPTRAP) + traptype = TRAP_TYPE_NONPRIO; + + /* + * Call ctlsettrap() to do the work. Return + * an error if it can't assign the trap. + */ + if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype, + (int)res_version)) + ctl_error(CERR_NORESOURCE); + ctl_flushpkt(0); +} + + +/* + * unset_trap - unset a trap in response to a control message + */ +static void +unset_trap( + struct recvbuf *rbufp, + int restrict_mask + ) +{ + int traptype; + + /* + * We don't prevent anyone from removing his own + * trap unless the trap is configured. Note we also + * must be aware of the possibility that restriction + * flags were changed since this guy last set his trap. + * Set the trap type based on this. + */ + traptype = TRAP_TYPE_PRIO; + if (restrict_mask & RES_LPTRAP) + traptype = TRAP_TYPE_NONPRIO; + + /* + * Call ctlclrtrap() to clear this out. + */ + if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype)) + ctl_error(CERR_BADASSOC); + ctl_flushpkt(0); +} + + +/* + * ctlsettrap - called to set a trap + */ +int +ctlsettrap( + struct sockaddr_in *raddr, + struct interface *linter, + int traptype, + int version + ) +{ + register struct ctl_trap *tp; + register struct ctl_trap *tptouse; + + /* + * See if we can find this trap. If so, we only need update + * the flags and the time. + */ + if ((tp = ctlfindtrap(raddr, linter)) != NULL) { + switch (traptype) { + case TRAP_TYPE_CONFIG: + tp->tr_flags = TRAP_INUSE|TRAP_CONFIGURED; + break; + case TRAP_TYPE_PRIO: + if (tp->tr_flags & TRAP_CONFIGURED) + return 1; /* don't change anything */ + tp->tr_flags = TRAP_INUSE; + break; + case TRAP_TYPE_NONPRIO: + if (tp->tr_flags & TRAP_CONFIGURED) + return 1; /* don't change anything */ + tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO; + break; + } + tp->tr_settime = current_time; + tp->tr_resets++; + return 1; + } + + /* + * First we heard of this guy. Try to find a trap structure + * for him to use, clearing out lesser priority guys if we + * have to. Clear out anyone who's expired while we're at it. + */ + tptouse = NULL; + for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) { + if ((tp->tr_flags & TRAP_INUSE) && + !(tp->tr_flags & TRAP_CONFIGURED) && + ((tp->tr_settime + CTL_TRAPTIME) > current_time)) { + tp->tr_flags = 0; + num_ctl_traps--; + } + + if (!(tp->tr_flags & TRAP_INUSE)) { + tptouse = tp; + } else if (!(tp->tr_flags & TRAP_CONFIGURED)) { + switch (traptype) { + case TRAP_TYPE_CONFIG: + if (tptouse == NULL) { + tptouse = tp; + break; + } + if (tptouse->tr_flags & TRAP_NONPRIO + && !(tp->tr_flags & TRAP_NONPRIO)) + break; + if (!(tptouse->tr_flags & TRAP_NONPRIO) + && tp->tr_flags & TRAP_NONPRIO) { + tptouse = tp; + break; + } + if (tptouse->tr_origtime < tp->tr_origtime) + tptouse = tp; + break; + case TRAP_TYPE_PRIO: + if (tp->tr_flags & TRAP_NONPRIO) { + if (tptouse == NULL || + (tptouse->tr_flags & TRAP_INUSE + && tptouse->tr_origtime + < tp->tr_origtime)) + tptouse = tp; + } + break; + case TRAP_TYPE_NONPRIO: + break; + } + } + } + + /* + * If we don't have room for him return an error. + */ + if (tptouse == NULL) + return 0; + + /* + * Set up this structure for him. + */ + tptouse->tr_settime = tptouse->tr_origtime = current_time; + tptouse->tr_count = tptouse->tr_resets = 0; + tptouse->tr_sequence = 1; + tptouse->tr_addr = *raddr; + tptouse->tr_localaddr = linter; + tptouse->tr_version = version; + + tptouse->tr_flags = TRAP_INUSE; + if (traptype == TRAP_TYPE_CONFIG) + tptouse->tr_flags |= TRAP_CONFIGURED; + else if (traptype == TRAP_TYPE_NONPRIO) + tptouse->tr_flags |= TRAP_NONPRIO; + num_ctl_traps++; + return 1; +} + + +/* + * ctlclrtrap - called to clr a trap + */ +int +ctlclrtrap( + struct sockaddr_in *raddr, + struct interface *linter, + int traptype + ) +{ + register struct ctl_trap *tp; + + if ((tp = ctlfindtrap(raddr, linter)) == NULL) + return 0; + + if (tp->tr_flags & TRAP_CONFIGURED + && traptype != TRAP_TYPE_CONFIG) + return 0; + + tp->tr_flags = 0; + num_ctl_traps--; + return 1; +} + + +/* + * ctlfindtrap - find a trap given the remote and local addresses + */ +static struct ctl_trap * +ctlfindtrap( + struct sockaddr_in *raddr, + struct interface *linter + ) +{ + register struct ctl_trap *tp; + + for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) { + if (tp->tr_flags & TRAP_INUSE + && NSRCADR(raddr) == NSRCADR(&tp->tr_addr) + && NSRCPORT(raddr) == NSRCPORT(&tp->tr_addr) + && linter == tp->tr_localaddr) + return tp; + } + return (struct ctl_trap *)NULL; +} + + +/* + * report_event - report an event to the trappers + */ +void +report_event( + int err, + struct peer *peer + ) +{ + register int i; + + /* + * Record error code in proper spots, but have mercy on the + * log file. + */ + if (!(err & PEER_EVENT)) { + if (ctl_sys_num_events < CTL_SYS_MAXEVENTS) + ctl_sys_num_events++; + if (ctl_sys_last_event != (u_char)err) { + NLOG(NLOG_SYSEVENT) + msyslog(LOG_INFO, "system event '%s' (0x%02x) status '%s' (0x%02x)", + eventstr(err), err, + sysstatstr(ctlsysstatus()), ctlsysstatus()); +#ifdef DEBUG + if (debug) + printf("report_event: system event '%s' (0x%02x) status '%s' (0x%02x)\n", + eventstr(err), err, + sysstatstr(ctlsysstatus()), ctlsysstatus()); +#endif + ctl_sys_last_event = (u_char)err; + } + } else if (peer != 0) { + char *src; + +#ifdef REFCLOCK + if (ISREFCLOCKADR(&peer->srcadr)) + src = refnumtoa(peer->srcadr.sin_addr.s_addr); + else +#endif + src = ntoa(&peer->srcadr); + + peer->last_event = (u_char)(err & ~PEER_EVENT); + if (peer->num_events < CTL_PEER_MAXEVENTS) + peer->num_events++; + NLOG(NLOG_PEEREVENT) + msyslog(LOG_INFO, "peer %s event '%s' (0x%02x) status '%s' (0x%02x)", + src, eventstr(err), err, + peerstatstr(ctlpeerstatus(peer)), ctlpeerstatus(peer)); +#ifdef DEBUG + if (debug) + printf( "peer %s event '%s' (0x%02x) status '%s' (0x%02x)\n", + src, eventstr(err), err, + peerstatstr(ctlpeerstatus(peer)), ctlpeerstatus(peer)); +#endif + } else { + msyslog(LOG_ERR, "report_event: err '%s' (0x%02x), no peer", eventstr(err), err); +#ifdef DEBUG + printf("report_event: peer event '%s' (0x%02x), no peer\n", eventstr(err), err); +#endif + return; + } + + /* + * If no trappers, return. + */ + if (num_ctl_traps <= 0) + return; + + /* + * Set up the outgoing packet variables + */ + res_opcode = CTL_OP_ASYNCMSG; + res_offset = 0; + res_async = 1; + res_authenticate = 0; + datapt = rpkt.data; + dataend = &(rpkt.data[CTL_MAX_DATA_LEN]); + + if (!(err & PEER_EVENT)) { + rpkt.associd = 0; + rpkt.status = htons(ctlsysstatus()); + + /* + * For now, put everything we know about system + * variables. Maybe more selective later + */ + for (i = 1; i <= CS_MAXCODE; i++) + ctl_putsys(i); +#ifdef REFCLOCK + /* + * for clock exception events: + * add clock variables to reflect info on exception + */ + if (err == EVNT_CLOCKEXCPT) { + struct refclockstat clock_stat; + struct ctl_var *kv; + + clock_stat.kv_list = (struct ctl_var *)0; + + refclock_control(&peer->srcadr, + (struct refclockstat *)0, &clock_stat); + ctl_puthex("refclockstatus", ctlclkstatus(&clock_stat)); + + for (i = 1; i <= CC_MAXCODE; i++) + ctl_putclock(i, &clock_stat, 0); + for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV); kv++) + if (kv->flags & DEF) + ctl_putdata(kv->text, strlen(kv->text), 0); + + free_varlist(clock_stat.kv_list); + } +#endif /*REFCLOCK*/ + } else { + rpkt.associd = htons(peer->associd); + rpkt.status = htons(ctlpeerstatus(peer)); + + /* + * Dump it all. Later, maybe less. + */ + for (i = 1; i <= CP_MAXCODE; i++) + ctl_putpeer(i, peer); +#ifdef REFCLOCK + /* + * for clock exception events: + * add clock variables to reflect info on exception + */ + if (err == EVNT_PEERCLOCK) { + struct refclockstat clock_stat; + struct ctl_var *kv; + + clock_stat.kv_list = (struct ctl_var *)0; + + refclock_control(&peer->srcadr, + (struct refclockstat *)0, + &clock_stat); + + ctl_puthex("refclockstatus", + ctlclkstatus(&clock_stat)); + + for (i = 1; i <= CC_MAXCODE; i++) + ctl_putclock(i, &clock_stat, 0); + for (kv = clock_stat.kv_list; kv && !(kv->flags & EOV); kv++) + if (kv->flags & DEF) + ctl_putdata(kv->text, strlen(kv->text), 0); + + free_varlist(clock_stat.kv_list); + } +#endif /*REFCLOCK*/ + } + + /* + * We're done, return. + */ + ctl_flushpkt(0); +} + + +/* + * ctl_clr_stats - clear stat counters + */ +void +ctl_clr_stats(void) +{ + ctltimereset = current_time; + numctlreq = 0; + numctlbadpkts = 0; + numctlresponses = 0; + numctlfrags = 0; + numctlerrors = 0; + numctlfrags = 0; + numctltooshort = 0; + numctlinputresp = 0; + numctlinputfrag = 0; + numctlinputerr = 0; + numctlbadoffset = 0; + numctlbadversion = 0; + numctldatatooshort = 0; + numctlbadop = 0; + numasyncmsgs = 0; +} + +static u_long +count_var( + struct ctl_var *k + ) +{ + register u_long c; + + if (!k) + return 0; + + c = 0; + + while (!(k++->flags & EOV)) + c++; + + return c; +} + +char * +add_var( + struct ctl_var **kv, + u_long size, + int def + ) +{ + register u_long c; + register struct ctl_var *k; + + c = count_var(*kv); + + k = *kv; + *kv = (struct ctl_var *)emalloc((c+2)*sizeof(struct ctl_var)); + if (k) + { + memmove((char *)*kv, (char *)k, sizeof(struct ctl_var)*c); + free((char *)k); + } + + (*kv)[c].code = (u_short) c; + (*kv)[c].text = (char *)emalloc(size); + (*kv)[c].flags = def; + (*kv)[c+1].code = 0; + (*kv)[c+1].text = (char *)0; + (*kv)[c+1].flags = EOV; + return (char *)(*kv)[c].text; +} + +void +set_var( + struct ctl_var **kv, + const char *data, + u_long size, + int def + ) +{ + register struct ctl_var *k; + register const char *s; + register const char *t; + char *td; + + if (!data || !size) + return; + + if ((k = *kv)) + { + while (!(k->flags & EOV)) + { + s = data; + t = k->text; + if (t) + { + while (*t != '=' && *s - *t == 0) + { + s++; + t++; + } + if (*s == *t && ((*t == '=') || !*t)) + { + free((void *)k->text); + td = (char *)emalloc(size); + memmove(td, data, size); + k->text =td; + k->flags = def; + return; + } + } + else + { + td = (char *)emalloc(size); + memmove(td, data, size); + k->text = td; + k->flags = def; + return; + } + k++; + } + } + td = add_var(kv, size, def); + memmove(td, data, size); +} + +void +set_sys_var( + char *data, + u_long size, + int def + ) +{ + set_var(&ext_sys_var, data, size, def); +} + +void +free_varlist( + struct ctl_var *kv + ) +{ + struct ctl_var *k; + if (kv) + { + for (k = kv; !(k->flags & EOV); k++) + free((void *)k->text); + free((void *)kv); + } +} |