From 3271d1ac389d2ec93db9c5b9ce0991ce478476cf Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 18:09:41 +0200 Subject: Adding upstream version 4.3. Signed-off-by: Daniel Baumann --- client.c | 3451 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3451 insertions(+) create mode 100644 client.c (limited to 'client.c') diff --git a/client.c b/client.c new file mode 100644 index 0000000..0a7bcc8 --- /dev/null +++ b/client.c @@ -0,0 +1,3451 @@ +/* + chronyd/chronyc - Programs for keeping computer clocks accurate. + + ********************************************************************** + * Copyright (C) Richard P. Curnow 1997-2003 + * Copyright (C) Lonnie Abelbeck 2016, 2018 + * Copyright (C) Miroslav Lichvar 2009-2021 + * + * 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 "cmac.h" +#include "logging.h" +#include "memory.h" +#include "nameserv.h" +#include "getdate.h" +#include "cmdparse.h" +#include "pktlength.h" +#include "socket.h" +#include "util.h" + +#ifdef FEAT_READLINE +#include +#endif + +/* ================================================== */ + +struct Address { + SCK_AddressType type; + union { + IPSockAddr ip; + char *path; + } addr; +}; + +static ARR_Instance server_addresses; + +static int sock_fd = -1; + +static volatile int quit = 0; + +static int on_terminal = 0; + +static int no_dns = 0; + +static int source_names = 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. */ + +LOG_Severity log_min_severity = LOGS_INFO; + +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; + + if (severity < log_min_severity) + return; + + 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_addresses(const char *hostnames, int port) +{ + struct Address *addr; + ARR_Instance addrs; + char *hostname, *s1, *s2; + IPAddr ip_addrs[DNS_MAX_ADDRESSES]; + int i; + + addrs = ARR_CreateInstance(sizeof (*addr)); + 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 = ARR_GetNewElement(addrs); + addr->type = SCK_ADDR_UNIX; + addr->addr.path = Strdup(hostname); + } 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 = ARR_GetNewElement(addrs); + addr->type = SCK_ADDR_IP; + addr->addr.ip.ip_addr = ip_addrs[i]; + addr->addr.ip.port = port; + DEBUG_LOG("Resolved %s to %s", hostname, UTI_IPToString(&ip_addrs[i])); + } + } + } + + Free(s1); + return addrs; +} + +/* ================================================== */ + +static void +free_addresses(ARR_Instance addresses) +{ + struct Address *addr; + unsigned int i; + + for (i = 0; i < ARR_GetSize(addresses); i++) { + addr = ARR_GetElement(addresses, i); + + if (addr->type == SCK_ADDR_UNIX) + Free(addr->addr.path); + } + + ARR_DestroyInstance(addresses); +} + +/* ================================================== */ +/* Initialise the socket used to talk to the daemon */ + +static int +open_socket(struct Address *addr) +{ + char *dir, *local_addr; + size_t local_addr_len; + + switch (addr->type) { + case SCK_ADDR_IP: + sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, NULL, 0); + break; + case SCK_ADDR_UNIX: + /* 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->addr.path); + local_addr_len = strlen(dir) + 50; + local_addr = Malloc(local_addr_len); + + snprintf(local_addr, local_addr_len, "%s/chronyc.%d.sock", dir, (int)getpid()); + + sock_fd = SCK_OpenUnixDatagramSocket(addr->addr.path, local_addr, + SCK_FLAG_ALL_PERMISSIONS); + Free(dir); + Free(local_addr); + + break; + default: + assert(0); + } + + if (sock_fd < 0) + return 0; + + return 1; +} + +/* ================================================== */ + +static void +close_io(void) +{ + if (sock_fd < 0) + return; + + SCK_RemoveSocket(sock_fd); + SCK_CloseSocket(sock_fd); + sock_fd = -1; +} + +/* ================================================== */ + +static int +open_io(void) +{ + static unsigned int address_index = 0; + struct Address *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(server_addresses); address_index++) { + addr = ARR_GetElement(server_addresses, address_index); + + if (open_socket(addr)) + return 1; + + close_io(); + } + + /* Start from the first address if called again */ + address_index = 0; + + 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; + case IPADDR_ID: + mask->family = IPADDR_UNSPEC; + break; + default: + assert(0); + } +} + +/* ================================================== */ + +static int +parse_source_address(char *word, IPAddr *address) +{ + if (UTI_StringToIdIP(word, address)) + return 1; + + if (DNS_Name2IPAddress(word, address, 1) == DNS_Success) + return 1; + + return 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 (parse_source_address(p, address)) { + 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 void +process_cmd_onoffline(CMD_Request *msg, char *line) +{ + msg->command = htons(REQ_ONOFFLINE); +} + +/* ================================================== */ + +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 (!parse_source_address(hostname, address)) { + 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 (!parse_source_address(hostname, address)) { + 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 +process_cmd_allowdeny(CMD_Request *msg, char *line, int cmd, int allcmd) +{ + int all, subnet_bits; + IPAddr ip; + + if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits)) { + LOG(LOGS_ERR, "Could not read address"); + return 0; + } + + msg->command = htons(all ? allcmd : cmd); + UTI_IPHostToNetwork(&ip, &msg->data.allow_deny.ip); + msg->data.allow_deny.subnet_bits = htonl(subnet_bits); + + return 1; +} + +/* ================================================== */ + +static int +process_cmd_accheck(CMD_Request *msg, char *line) +{ + IPAddr ip; + msg->command = htons(REQ_ACCHECK); + if (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) { + 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 (DNS_Name2IPAddress(line, &ip, 1) == DNS_Success) { + UTI_IPHostToNetwork(&ip, &msg->data.ac_check.ip); + return 1; + } else { + LOG(LOGS_ERR, "Could not read address"); + return 0; + } +} + +/* ================================================== */ + +static int +process_cmd_dfreq(CMD_Request *msg, char *line) +{ + double dfreq; + + msg->command = htons(REQ_DFREQ); + + if (sscanf(line, "%lf", &dfreq) != 1) { + LOG(LOGS_ERR, "Invalid value"); + return 0; + } + + msg->data.dfreq.dfreq = UTI_FloatHostToNetwork(dfreq); + return 1; +} + +/* ================================================== */ + +static int +process_cmd_doffset(CMD_Request *msg, char *line) +{ + double doffset; + + msg->command = htons(REQ_DOFFSET2); + + if (sscanf(line, "%lf", &doffset) != 1) { + LOG(LOGS_ERR, "Invalid value"); + return 0; + } + + msg->data.doffset.doffset = UTI_FloatHostToNetwork(doffset); + return 1; +} + +/* ================================================== */ + +static int +process_cmd_add_source(CMD_Request *msg, char *line) +{ + CPS_NTP_Source data; + IPAddr ip_addr; + int result = 0, status, type; + const char *opt_name, *word; + + msg->command = htons(REQ_ADD_SOURCE); + + word = line; + line = CPS_SplitWord(line); + + if (!strcasecmp(word, "server")) { + type = REQ_ADDSRC_SERVER; + } else if (!strcasecmp(word, "peer")) { + type = REQ_ADDSRC_PEER; + } else if (!strcasecmp(word, "pool")) { + type = REQ_ADDSRC_POOL; + } else { + LOG(LOGS_ERR, "Invalid syntax for add command"); + return 0; + } + + status = CPS_ParseNTPSourceAdd(line, &data); + switch (status) { + case 0: + LOG(LOGS_ERR, "Invalid syntax for add command"); + break; + default: + /* Verify that the address is resolvable (chronyc and chronyd are + assumed to be running on the same host) */ + if (strlen(data.name) >= sizeof (msg->data.ntp_source.name) || + 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.type = htonl(type); + if (strlen(data.name) >= sizeof (msg->data.ntp_source.name)) + assert(0); + strncpy((char *)msg->data.ntp_source.name, data.name, + sizeof (msg->data.ntp_source.name)); + msg->data.ntp_source.port = htonl(data.port); + 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.nts_port = htonl(data.params.nts_port); + 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.connectivity == SRC_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.burst ? REQ_ADDSRC_BURST : 0) | + (data.params.nts ? REQ_ADDSRC_NTS : 0) | + (data.params.copy ? REQ_ADDSRC_COPY : 0) | + (data.params.ext_fields & NTP_EF_FLAG_EXP1 ? REQ_ADDSRC_EF_EXP1 : 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)); + msg->data.ntp_source.filter_length = htonl(data.params.filter_length); + msg->data.ntp_source.cert_set = htonl(data.params.cert_set); + msg->data.ntp_source.max_delay_quant = + UTI_FloatHostToNetwork(data.params.max_delay_quant); + memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved)); + + result = 1; + + break; + } + + return result; +} + +/* ================================================== */ + +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 (!parse_source_address(hostname, &address)) { + 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 [-a] [-v]\0Display information about current sources\0" + "sourcestats [-a] [-v]\0Display statistics about collected measurements\0" + "selectdata [-a] [-v]\0Display information about source selection\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" + "authdata [-a] [-v]\0Display information about authentication\0" + "ntpdata [
]\0Display information about last valid measurement\0" + "add server [options]\0Add new NTP server\0" + "add pool [options]\0Add new pool of NTP servers\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" + "onoffline\0Set all sources to online or offline status\0" + "\0according to network configuration\0" + "polltarget
\0Modify poll target\0" + "refresh\0Refresh IP addresses\0" + "reload sources\0Re-read *.sources files\0" + "sourcename
\0Display original name\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