/* chronyd/chronyc - Programs for keeping computer clocks accurate. ********************************************************************** * Copyright (C) Richard P. Curnow 1997-2003 * Copyright (C) Lonnie Abelbeck 2016 * Copyright (C) Miroslav Lichvar 2009-2017 * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ********************************************************************** ======================================================================= Command line client for configuring the daemon and obtaining status from it whilst running. */ #include "config.h" #include "sysincl.h" #include "array.h" #include "candm.h" #include "logging.h" #include "memory.h" #include "nameserv.h" #include "getdate.h" #include "cmdparse.h" #include "pktlength.h" #include "util.h" #ifdef FEAT_READLINE #ifdef USE_EDITLINE #include #else #include #include #endif #endif /* ================================================== */ union sockaddr_all { struct sockaddr_in in4; #ifdef FEAT_IPV6 struct sockaddr_in6 in6; #endif struct sockaddr_un un; struct sockaddr sa; }; static ARR_Instance sockaddrs; static int sock_fd = -1; static int quit = 0; static int on_terminal = 0; static int no_dns = 0; static int csv_mode = 0; /* ================================================== */ /* Log a message. This is a minimalistic replacement of the logging.c implementation to avoid linking with it and other modules. */ int log_debug_enabled = 0; void LOG_Message(LOG_Severity severity, #if DEBUG > 0 int line_number, const char *filename, const char *function_name, #endif const char *format, ...) { va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); putc('\n', stderr); va_end(ap); } /* ================================================== */ /* Read a single line of commands from standard input */ #ifdef FEAT_READLINE static char **command_name_completion(const char *text, int start, int end); #endif static char * read_line(void) { static char line[2048]; static const char *prompt = "chronyc> "; if (on_terminal) { #ifdef FEAT_READLINE char *cmd; rl_attempted_completion_function = command_name_completion; rl_basic_word_break_characters = "\t\n\r"; /* save line only if not empty */ cmd = readline(prompt); if( cmd == NULL ) return( NULL ); /* user pressed return */ if( *cmd != '\0' ) { strncpy(line, cmd, sizeof(line) - 1); line[sizeof(line) - 1] = '\0'; add_history(cmd); /* free the buffer allocated by readline */ Free(cmd); } else { /* simulate the user has entered an empty line */ *line = '\0'; } return( line ); #else printf("%s", prompt); fflush(stdout); #endif } if (fgets(line, sizeof(line), stdin)) { return line; } else { return NULL; } } /* ================================================== */ static ARR_Instance get_sockaddrs(const char *hostnames, int port) { ARR_Instance addrs; char *hostname, *s1, *s2; IPAddr ip_addrs[DNS_MAX_ADDRESSES]; union sockaddr_all *addr; int i; addrs = ARR_CreateInstance(sizeof (union sockaddr_all)); s1 = Strdup(hostnames); /* Parse the comma-separated list of hostnames */ for (hostname = s1; hostname && *hostname; hostname = s2) { s2 = strchr(hostname, ','); if (s2) *s2++ = '\0'; /* hostname starting with / is considered a path of Unix domain socket */ if (hostname[0] == '/') { addr = (union sockaddr_all *)ARR_GetNewElement(addrs); if (snprintf(addr->un.sun_path, sizeof (addr->un.sun_path), "%s", hostname) >= sizeof (addr->un.sun_path)) LOG_FATAL("Unix socket path too long"); addr->un.sun_family = AF_UNIX; } else { if (DNS_Name2IPAddress(hostname, ip_addrs, DNS_MAX_ADDRESSES) != DNS_Success) { DEBUG_LOG("Could not get IP address for %s", hostname); continue; } for (i = 0; i < DNS_MAX_ADDRESSES && ip_addrs[i].family != IPADDR_UNSPEC; i++) { addr = (union sockaddr_all *)ARR_GetNewElement(addrs); UTI_IPAndPortToSockaddr(&ip_addrs[i], port, (struct sockaddr *)addr); DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i])); } } } Free(s1); return addrs; } /* ================================================== */ /* Initialise the socket used to talk to the daemon */ static int prepare_socket(union sockaddr_all *addr) { socklen_t addr_len; char *dir; switch (addr->sa.sa_family) { case AF_UNIX: addr_len = sizeof (addr->un); break; case AF_INET: addr_len = sizeof (addr->in4); break; #ifdef FEAT_IPV6 case AF_INET6: addr_len = sizeof (addr->in6); break; #endif default: assert(0); } sock_fd = socket(addr->sa.sa_family, SOCK_DGRAM, 0); if (sock_fd < 0) { DEBUG_LOG("Could not create socket : %s", strerror(errno)); return 0; } if (addr->sa.sa_family == AF_UNIX) { struct sockaddr_un sa_un; /* Construct path of our socket. Use the same directory as the server socket and include our process ID to allow multiple chronyc instances running at the same time. */ dir = UTI_PathToDir(addr->un.sun_path); if (snprintf(sa_un.sun_path, sizeof (sa_un.sun_path), "%s/chronyc.%d.sock", dir, (int)getpid()) >= sizeof (sa_un.sun_path)) LOG_FATAL("Unix socket path too long"); Free(dir); sa_un.sun_family = AF_UNIX; unlink(sa_un.sun_path); /* Bind the socket to the path */ if (bind(sock_fd, (struct sockaddr *)&sa_un, sizeof (sa_un)) < 0) { DEBUG_LOG("Could not bind socket : %s", strerror(errno)); return 0; } /* Allow server without root privileges to send replies to our socket */ if (chmod(sa_un.sun_path, 0666) < 0) { DEBUG_LOG("Could not change socket permissions : %s", strerror(errno)); return 0; } } if (connect(sock_fd, &addr->sa, addr_len) < 0) { DEBUG_LOG("Could not connect socket : %s", strerror(errno)); return 0; } return 1; } /* ================================================== */ static void close_io(void) { union sockaddr_all addr; socklen_t addr_len = sizeof (addr); if (sock_fd < 0) return; /* Remove our Unix domain socket */ if (getsockname(sock_fd, &addr.sa, &addr_len) < 0) LOG_FATAL("getsockname() failed : %s", strerror(errno)); if (addr_len <= sizeof (addr) && addr_len > sizeof (addr.sa.sa_family) && addr.sa.sa_family == AF_UNIX) unlink(addr.un.sun_path); close(sock_fd); sock_fd = -1; } /* ================================================== */ static int open_io(void) { static unsigned int address_index = 0; union sockaddr_all *addr; /* If a socket is already opened, close it and try the next address */ if (sock_fd >= 0) { close_io(); address_index++; } /* Find an address for which a socket can be opened and connected */ for (; address_index < ARR_GetSize(sockaddrs); address_index++) { addr = (union sockaddr_all *)ARR_GetElement(sockaddrs, address_index); DEBUG_LOG("Opening connection to %s", UTI_SockaddrToString(&addr->sa)); if (prepare_socket(addr)) return 1; close_io(); } return 0; } /* ================================================== */ static void bits_to_mask(int bits, int family, IPAddr *mask) { int i; mask->family = family; switch (family) { case IPADDR_INET4: if (bits > 32 || bits < 0) bits = 32; if (bits > 0) { mask->addr.in4 = -1; mask->addr.in4 <<= 32 - bits; } else { mask->addr.in4 = 0; } break; case IPADDR_INET6: if (bits > 128 || bits < 0) bits = 128; for (i = 0; i < bits / 8; i++) mask->addr.in6[i] = 0xff; if (i < 16) mask->addr.in6[i++] = (0xff << (8 - bits % 8)) & 0xff; for (; i < 16; i++) mask->addr.in6[i] = 0x0; break; default: assert(0); } } /* ================================================== */ static int read_mask_address(char *line, IPAddr *mask, IPAddr *address) { unsigned int bits; char *p, *q; p = line; if (!*p) { mask->family = address->family = IPADDR_UNSPEC; return 1; } else { q = strchr(p, '/'); if (q) { *q++ = 0; if (UTI_StringToIP(p, mask)) { p = q; if (UTI_StringToIP(p, address)) { if (address->family == mask->family) return 1; } else if (sscanf(p, "%u", &bits) == 1) { *address = *mask; bits_to_mask(bits, address->family, mask); return 1; } } } else { if (DNS_Name2IPAddress(p, address, 1) == DNS_Success) { bits_to_mask(-1, address->family, mask); return 1; } else { LOG(LOGS_ERR, "Could not get address for hostname"); return 0; } } } LOG(LOGS_ERR, "Invalid syntax for mask/address"); return 0; } /* ================================================== */ static int process_cmd_offline(CMD_Request *msg, char *line) { IPAddr mask, address; int ok; if (read_mask_address(line, &mask, &address)) { UTI_IPHostToNetwork(&mask, &msg->data.offline.mask); UTI_IPHostToNetwork(&address, &msg->data.offline.address); msg->command = htons(REQ_OFFLINE); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_online(CMD_Request *msg, char *line) { IPAddr mask, address; int ok; if (read_mask_address(line, &mask, &address)) { UTI_IPHostToNetwork(&mask, &msg->data.online.mask); UTI_IPHostToNetwork(&address, &msg->data.online.address); msg->command = htons(REQ_ONLINE); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int read_address_integer(char *line, IPAddr *address, int *value) { char *hostname; int ok = 0; hostname = line; line = CPS_SplitWord(line); if (sscanf(line, "%d", value) != 1) { LOG(LOGS_ERR, "Invalid syntax for address value"); ok = 0; } else { if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { LOG(LOGS_ERR, "Could not get address for hostname"); ok = 0; } else { ok = 1; } } return ok; } /* ================================================== */ static int read_address_double(char *line, IPAddr *address, double *value) { char *hostname; int ok = 0; hostname = line; line = CPS_SplitWord(line); if (sscanf(line, "%lf", value) != 1) { LOG(LOGS_ERR, "Invalid syntax for address value"); ok = 0; } else { if (DNS_Name2IPAddress(hostname, address, 1) != DNS_Success) { LOG(LOGS_ERR, "Could not get address for hostname"); ok = 0; } else { ok = 1; } } return ok; } /* ================================================== */ static int process_cmd_minpoll(CMD_Request *msg, char *line) { IPAddr address; int minpoll; int ok; if (read_address_integer(line, &address, &minpoll)) { UTI_IPHostToNetwork(&address, &msg->data.modify_minpoll.address); msg->data.modify_minpoll.new_minpoll = htonl(minpoll); msg->command = htons(REQ_MODIFY_MINPOLL); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_maxpoll(CMD_Request *msg, char *line) { IPAddr address; int maxpoll; int ok; if (read_address_integer(line, &address, &maxpoll)) { UTI_IPHostToNetwork(&address, &msg->data.modify_maxpoll.address); msg->data.modify_maxpoll.new_maxpoll = htonl(maxpoll); msg->command = htons(REQ_MODIFY_MAXPOLL); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_maxdelay(CMD_Request *msg, char *line) { IPAddr address; double max_delay; int ok; if (read_address_double(line, &address, &max_delay)) { UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelay.address); msg->data.modify_maxdelay.new_max_delay = UTI_FloatHostToNetwork(max_delay); msg->command = htons(REQ_MODIFY_MAXDELAY); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_maxdelaydevratio(CMD_Request *msg, char *line) { IPAddr address; double max_delay_dev_ratio; int ok; if (read_address_double(line, &address, &max_delay_dev_ratio)) { UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelaydevratio.address); msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_dev_ratio); msg->command = htons(REQ_MODIFY_MAXDELAYDEVRATIO); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_maxdelayratio(CMD_Request *msg, char *line) { IPAddr address; double max_delay_ratio; int ok; if (read_address_double(line, &address, &max_delay_ratio)) { UTI_IPHostToNetwork(&address, &msg->data.modify_maxdelayratio.address); msg->data.modify_maxdelayratio.new_max_delay_ratio = UTI_FloatHostToNetwork(max_delay_ratio); msg->command = htons(REQ_MODIFY_MAXDELAYRATIO); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_minstratum(CMD_Request *msg, char *line) { IPAddr address; int min_stratum; int ok; if (read_address_integer(line, &address, &min_stratum)) { UTI_IPHostToNetwork(&address, &msg->data.modify_minstratum.address); msg->data.modify_minstratum.new_min_stratum = htonl(min_stratum); msg->command = htons(REQ_MODIFY_MINSTRATUM); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_polltarget(CMD_Request *msg, char *line) { IPAddr address; int poll_target; int ok; if (read_address_integer(line, &address, &poll_target)) { UTI_IPHostToNetwork(&address, &msg->data.modify_polltarget.address); msg->data.modify_polltarget.new_poll_target = htonl(poll_target); msg->command = htons(REQ_MODIFY_POLLTARGET); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static int process_cmd_maxupdateskew(CMD_Request *msg, char *line) { int ok; double new_max_update_skew; if (sscanf(line, "%lf", &new_max_update_skew) == 1) { msg->data.modify_maxupdateskew.new_max_update_skew = UTI_FloatHostToNetwork(new_max_update_skew); msg->command = htons(REQ_MODIFY_MAXUPDATESKEW); ok = 1; } else { ok = 0; } return ok; } /* ================================================== */ static void process_cmd_dump(CMD_Request *msg, char *line) { msg->command = htons(REQ_DUMP); msg->data.dump.pad = htonl(0); } /* ================================================== */ static void process_cmd_writertc(CMD_Request *msg, char *line) { msg->command = htons(REQ_WRITERTC); } /* ================================================== */ static void process_cmd_trimrtc(CMD_Request *msg, char *line) { msg->command = htons(REQ_TRIMRTC); } /* ================================================== */ static void process_cmd_cyclelogs(CMD_Request *msg, char *line) { msg->command = htons(REQ_CYCLELOGS); } /* ================================================== */ static int process_cmd_burst(CMD_Request *msg, char *line) { int n_good_samples, n_total_samples; char *s1, *s2; IPAddr address, mask; s1 = line; s2 = CPS_SplitWord(s1); CPS_SplitWord(s2); if (sscanf(s1, "%d/%d", &n_good_samples, &n_total_samples) != 2) { LOG(LOGS_ERR, "Invalid syntax for burst command"); return 0; } mask.family = address.family = IPADDR_UNSPEC; if (*s2 && !read_mask_address(s2, &mask, &address)) { return 0; } msg->command = htons(REQ_BURST); msg->data.burst.n_good_samples = ntohl(n_good_samples); msg->data.burst.n_total_samples = ntohl(n_total_samples); UTI_IPHostToNetwork(&mask, &msg->data.burst.mask); UTI_IPHostToNetwork(&address, &msg->data.burst.address); return 1; } /* ================================================== */ static int process_cmd_local(CMD_Request *msg, char *line) { int on_off, stratum = 0, orphan = 0; double distance = 0.0; if (!strcmp(line, "off")) { on_off = 0; } else if (CPS_ParseLocal(line, &stratum, &orphan, &distance)) { on_off = 1; } else { LOG(LOGS_ERR, "Invalid syntax for local command"); return 0; } msg->command = htons(REQ_LOCAL2); msg->data.local.on_off = htonl(on_off); msg->data.local.stratum = htonl(stratum); msg->data.local.distance = UTI_FloatHostToNetwork(distance); msg->data.local.orphan = htonl(orphan); return 1; } /* ================================================== */ static int process_cmd_manual(CMD_Request *msg, const char *line) { const char *p; p = line; if (!strcmp(p, "off")) { msg->data.manual.option = htonl(0); } else if (!strcmp(p, "on")) { msg->data.manual.option = htonl(1); } else if (!strcmp(p, "reset")) { msg->data.manual.option = htonl(2); } else { LOG(LOGS_ERR, "Invalid syntax for manual command"); return 0; } msg->command = htons(REQ_MANUAL); return 1; } /* ================================================== */ static int parse_allow_deny(CMD_Request *msg, char *line) { unsigned long a, b, c, d; int n, specified_subnet_bits; IPAddr ip; char *p; p = line; if (!*p) { /* blank line - applies to all addresses */ ip.family = IPADDR_UNSPEC; UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); msg->data.allow_deny.subnet_bits = htonl(0); } else { char *slashpos; slashpos = strchr(p, '/'); if (slashpos) *slashpos = 0; n = 0; if (!UTI_StringToIP(p, &ip) && (n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) <= 0) { /* Try to parse as the name of a machine */ if (slashpos || DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) { LOG(LOGS_ERR, "Could not read address"); return 0; } else { UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); if (ip.family == IPADDR_INET6) msg->data.allow_deny.subnet_bits = htonl(128); else msg->data.allow_deny.subnet_bits = htonl(32); } } else { if (n == 0) { if (ip.family == IPADDR_INET6) msg->data.allow_deny.subnet_bits = htonl(128); else msg->data.allow_deny.subnet_bits = htonl(32); } else { ip.family = IPADDR_INET4; a &= 0xff; b &= 0xff; c &= 0xff; d &= 0xff; switch (n) { case 1: ip.addr.in4 = htonl((a<<24)); msg->data.allow_deny.subnet_bits = htonl(8); break; case 2: ip.addr.in4 = htonl((a<<24) | (b<<16)); msg->data.allow_deny.subnet_bits = htonl(16); break; case 3: ip.addr.in4 = htonl((a<<24) | (b<<16) | (c<<8)); msg->data.allow_deny.subnet_bits = htonl(24); break; case 4: ip.addr.in4 = htonl((a<<24) | (b<<16) | (c<<8) | d); msg->data.allow_deny.subnet_bits = htonl(32); break; default: assert(0); } } UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); if (slashpos) { n = sscanf(slashpos+1, "%d", &specified_subnet_bits); if (n == 1) { msg->data.allow_deny.subnet_bits = htonl(specified_subnet_bits); } else { LOG(LOGS_WARN, "Warning: badly formatted subnet size, using %d", (int)ntohl(msg->data.allow_deny.subnet_bits)); } } } } return 1; } /* ================================================== */ static int process_cmd_allow(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_ALLOW); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_allowall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_ALLOWALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_deny(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_DENY); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_denyall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_DENYALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmdallow(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDALLOW); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmdallowall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDALLOWALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmddeny(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDDENY); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int process_cmd_cmddenyall(CMD_Request *msg, char *line) { int status; msg->command = htons(REQ_CMDDENYALL); status = parse_allow_deny(msg, line); return status; } /* ================================================== */ static int accheck_getaddr(char *line, IPAddr *addr) { unsigned long a, b, c, d; IPAddr ip; char *p; p = line; if (!*p) { return 0; } else { if (sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d) == 4) { addr->family = IPADDR_INET4; addr->addr.in4 = (a<<24) | (b<<16) | (c<<8) | d; return 1; } else { if (DNS_Name2IPAddress(p, &ip, 1) != DNS_Success) { return 0; } else { *addr = ip; return 1; } } } } /* ================================================== */ static int process_cmd_accheck(CMD_Request *msg, char *line) { IPAddr ip; msg->command = htons(REQ_ACCHECK); if (accheck_getaddr(line, &ip)) { UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); return 1; } else { LOG(LOGS_ERR, "Could not read address"); return 0; } } /* ================================================== */ static int process_cmd_cmdaccheck(CMD_Request *msg, char *line) { IPAddr ip; msg->command = htons(REQ_CMDACCHECK); if (accheck_getaddr(line, &ip)) { UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); return 1; } else { LOG(LOGS_ERR, "Could not read address"); return 0; } } /* ================================================== */ static void process_cmd_dfreq(CMD_Request *msg, char *line) { double dfreq; msg->command = htons(REQ_DFREQ); if (sscanf(line, "%lf", &dfreq) == 1) { msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq); } else { msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(0.0); } } /* ================================================== */ static void cvt_to_sec_usec(double x, long *sec, long *usec) { long s, us; s = (long) x; us = (long)(0.5 + 1.0e6 * (x - (double) s)); while (us >= 1000000) { us -= 1000000; s += 1; } while (us < 0) { us += 1000000; s -= 1; } *sec = s; *usec = us; } /* ================================================== */ static void process_cmd_doffset(CMD_Request *msg, char *line) { double doffset; long sec, usec; msg->command = htons(REQ_DOFFSET); if (sscanf(line, "%lf", &doffset) == 1) { cvt_to_sec_usec(doffset, &sec, &usec); msg->data.doffset.sec = htonl(sec); msg->data.doffset.usec = htonl(usec); } else { msg->data.doffset.sec = htonl(0); msg->data.doffset.usec = htonl(0); } } /* ================================================== */ static int process_cmd_add_server_or_peer(CMD_Request *msg, char *line) { CPS_NTP_Source data; IPAddr ip_addr; int result = 0, status; const char *opt_name; status = CPS_ParseNTPSourceAdd(line, &data); switch (status) { case 0: LOG(LOGS_ERR, "Invalid syntax for add command"); break; default: if (DNS_Name2IPAddress(data.name, &ip_addr, 1) != DNS_Success) { LOG(LOGS_ERR, "Invalid host/IP address"); break; } opt_name = NULL; if (opt_name) { LOG(LOGS_ERR, "%s can't be set in chronyc", opt_name); break; } msg->data.ntp_source.port = htonl((unsigned long) data.port); UTI_IPHostToNetwork(&ip_addr, &msg->data.ntp_source.ip_addr); msg->data.ntp_source.minpoll = htonl(data.params.minpoll); msg->data.ntp_source.maxpoll = htonl(data.params.maxpoll); msg->data.ntp_source.presend_minpoll = htonl(data.params.presend_minpoll); msg->data.ntp_source.min_stratum = htonl(data.params.min_stratum); msg->data.ntp_source.poll_target = htonl(data.params.poll_target); msg->data.ntp_source.version = htonl(data.params.version); msg->data.ntp_source.max_sources = htonl(data.params.max_sources); msg->data.ntp_source.min_samples = htonl(data.params.min_samples); msg->data.ntp_source.max_samples = htonl(data.params.max_samples); msg->data.ntp_source.authkey = htonl(data.params.authkey); msg->data.ntp_source.max_delay = UTI_FloatHostToNetwork(data.params.max_delay); msg->data.ntp_source.max_delay_ratio = UTI_FloatHostToNetwork(data.params.max_delay_ratio); msg->data.ntp_source.max_delay_dev_ratio = UTI_FloatHostToNetwork(data.params.max_delay_dev_ratio); msg->data.ntp_source.min_delay = UTI_FloatHostToNetwork(data.params.min_delay); msg->data.ntp_source.asymmetry = UTI_FloatHostToNetwork(data.params.asymmetry); msg->data.ntp_source.offset = UTI_FloatHostToNetwork(data.params.offset); msg->data.ntp_source.flags = htonl( (data.params.online ? REQ_ADDSRC_ONLINE : 0) | (data.params.auto_offline ? REQ_ADDSRC_AUTOOFFLINE : 0) | (data.params.iburst ? REQ_ADDSRC_IBURST : 0) | (data.params.interleaved ? REQ_ADDSRC_INTERLEAVED : 0) | (data.params.sel_options & SRC_SELECT_PREFER ? REQ_ADDSRC_PREFER : 0) | (data.params.sel_options & SRC_SELECT_NOSELECT ? REQ_ADDSRC_NOSELECT : 0) | (data.params.sel_options & SRC_SELECT_TRUST ? REQ_ADDSRC_TRUST : 0) | (data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0)); memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved)); result = 1; break; } return result; } /* ================================================== */ static int process_cmd_add_server(CMD_Request *msg, char *line) { msg->command = htons(REQ_ADD_SERVER3); return process_cmd_add_server_or_peer(msg, line); } /* ================================================== */ static int process_cmd_add_peer(CMD_Request *msg, char *line) { msg->command = htons(REQ_ADD_PEER3); return process_cmd_add_server_or_peer(msg, line); } /* ================================================== */ static int process_cmd_delete(CMD_Request *msg, char *line) { char *hostname; int ok = 0; IPAddr address; msg->command = htons(REQ_DEL_SOURCE); hostname = line; CPS_SplitWord(line); if (!*hostname) { LOG(LOGS_ERR, "Invalid syntax for address"); ok = 0; } else { if (DNS_Name2IPAddress(hostname, &address, 1) != DNS_Success) { LOG(LOGS_ERR, "Could not get address for hostname"); ok = 0; } else { UTI_IPHostToNetwork(&address, &msg->data.del_source.ip_addr); ok = 1; } } return ok; } /* ================================================== */ static void give_help(void) { int line, len; const char *s, cols[] = "System clock:\0\0" "tracking\0Display system time information\0" "makestep\0Correct clock by stepping immediately\0" "makestep \0Configure automatic clock stepping\0" "maxupdateskew \0Modify maximum valid skew to update frequency\0" "waitsync [ [ [ []]]]\0" "Wait until synchronised in specified limits\0" "\0\0" "Time sources:\0\0" "sources [-v]\0Display information about current sources\0" "sourcestats [-v]\0Display statistics about collected measurements\0" "reselect\0Force reselecting synchronisation source\0" "reselectdist \0Modify reselection distance\0" "\0\0" "NTP sources:\0\0" "activity\0Check how many NTP sources are online/offline\0" "ntpdata [
]\0Display information about last valid measurement\0" "add server
[options]\0Add new NTP server\0" "add peer
[options]\0Add new NTP peer\0" "delete
\0Remove server or peer\0" "burst / [/
]\0Start rapid set of measurements\0" "maxdelay
\0Modify maximum valid sample delay\0" "maxdelayratio
\0Modify maximum valid delay/minimum ratio\0" "maxdelaydevratio
\0Modify maximum valid delay/deviation ratio\0" "minpoll
\0Modify minimum polling interval\0" "maxpoll
\0Modify maximum polling interval\0" "minstratum
\0Modify minimum stratum\0" "offline [/
]\0Set sources in subnet to offline status\0" "online [/
]\0Set sources in subnet to online status\0" "polltarget
\0Modify poll target\0" "refresh\0Refresh IP addresses\0" "\0\0" "Manual time input:\0\0" "manual off|on|reset\0Disable/enable/reset settime command\0" "manual list\0Show previous settime entries\0" "manual delete \0Delete previous settime entry\0" "settime