diff --git a/candm.h b/candm.h
index 3071828..e742323 100644
--- a/candm.h
+++ b/candm.h
@@ -109,7 +109,8 @@
#define REQ_SELECT_DATA 69
#define REQ_RELOAD_SOURCES 70
#define REQ_DOFFSET2 71
-#define N_REQUEST_TYPES 72
+#define REQ_MODIFY_SELECTOPTS 72
+#define N_REQUEST_TYPES 73
/* Structure used to exchange timespecs independent of time_t size */
typedef struct {
@@ -371,6 +372,15 @@ typedef struct {
int32_t EOR;
} REQ_SelectData;
+/* Mask and options reuse the REQ_ADDSRC flags */
+typedef struct {
+ IPAddr address;
+ uint32_t ref_id;
+ uint32_t mask;
+ uint32_t options;
+ int32_t EOR;
+} REQ_Modify_SelectOpts;
+
/* ================================================== */
#define PKT_TYPE_CMD_REQUEST 1
@@ -477,6 +487,7 @@ typedef struct {
REQ_NTPSourceName ntp_source_name;
REQ_AuthData auth_data;
REQ_SelectData select_data;
+ REQ_Modify_SelectOpts modify_select_opts;
} data; /* Command specific parameters */
/* Padding used to prevent traffic amplification. It only defines the
diff --git a/client.c b/client.c
index e15747d..ad731b0 100644
--- a/client.c
+++ b/client.c
@@ -1021,6 +1021,7 @@ give_help(void)
"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"
+ "selectopts
<+|-options>\0Modify selection options\0"
"reselect\0Force reselecting synchronisation source\0"
"reselectdist \0Modify reselection distance\0"
"\0\0"
@@ -1135,8 +1136,8 @@ command_name_generator(const char *text, int state)
"manual", "maxdelay", "maxdelaydevratio", "maxdelayratio", "maxpoll",
"maxupdateskew", "minpoll", "minstratum", "ntpdata", "offline", "online", "onoffline",
"polltarget", "quit", "refresh", "rekey", "reload", "reselect", "reselectdist", "reset",
- "retries", "rtcdata", "selectdata", "serverstats", "settime", "shutdown", "smoothing",
- "smoothtime", "sourcename", "sources", "sourcestats",
+ "retries", "rtcdata", "selectdata", "selectopts", "serverstats", "settime",
+ "shutdown", "smoothing", "smoothtime", "sourcename", "sources", "sourcestats",
"timeout", "tracking", "trimrtc", "waitsync", "writertc",
NULL
};
@@ -2899,6 +2900,55 @@ process_cmd_reset(CMD_Request *msg, char *line)
/* ================================================== */
+static int
+process_cmd_selectopts(CMD_Request *msg, char *line)
+{
+ int mask, options, option;
+ uint32_t ref_id;
+ IPAddr ip_addr;
+ char *src, *opt;
+
+ src = line;
+ line = CPS_SplitWord(line);
+ ref_id = 0;
+
+ /* Don't allow hostnames to avoid conflicts with reference IDs */
+ if (!UTI_StringToIdIP(src, &ip_addr) && !UTI_StringToIP(src, &ip_addr)) {
+ ip_addr.family = IPADDR_UNSPEC;
+ if (CPS_ParseRefid(src, &ref_id) == 0) {
+ LOG(LOGS_ERR, "Invalid syntax for selectopts command");
+ return 0;
+ }
+ }
+
+ mask = options = 0;
+
+ while (*line != '\0') {
+ opt = line;
+ line = CPS_SplitWord(line);
+
+ if ((opt[0] != '+' && opt[0] != '-') || (option = CPS_GetSelectOption(opt + 1)) == 0) {
+ LOG(LOGS_ERR, "Invalid syntax for selectopts command");
+ return 0;
+ }
+
+ mask |= option;
+ if (opt[0] == '+')
+ options |= option;
+ }
+
+ UTI_IPHostToNetwork(&ip_addr, &msg->data.modify_select_opts.address);
+ msg->data.modify_select_opts.ref_id = htonl(ref_id);
+ msg->data.modify_select_opts.mask = htonl(mask);
+ msg->data.modify_select_opts.options = htonl(convert_addsrc_sel_options(options));
+
+ msg->command = htons(REQ_MODIFY_SELECTOPTS);
+
+ return 1;
+}
+
+/* ================================================== */
+
static int
process_cmd_waitsync(char *line)
{
@@ -3201,6 +3251,8 @@ process_line(char *line)
} else if (!strcmp(command, "selectdata")) {
do_normal_submit = 0;
ret = process_cmd_selectdata(line);
+ } else if (!strcmp(command, "selectopts")) {
+ do_normal_submit = process_cmd_selectopts(&tx_message, line);
} else if (!strcmp(command, "serverstats")) {
do_normal_submit = 0;
ret = process_cmd_serverstats(line);
diff --git a/cmdmon.c b/cmdmon.c
index d513cab..cdb1d86 100644
--- a/cmdmon.c
+++ b/cmdmon.c
@@ -144,6 +144,7 @@ static const char permissions[] = {
PERMIT_AUTH, /* SELECT_DATA */
PERMIT_AUTH, /* RELOAD_SOURCES */
PERMIT_AUTH, /* DOFFSET2 */
+ PERMIT_AUTH, /* MODIFY_SELECTOPTS */
};
/* ================================================== */
@@ -1370,6 +1371,24 @@ handle_select_data(CMD_Request *rx_message, CMD_Reply *tx_message)
tx_message->data.select_data.lo_limit = UTI_FloatHostToNetwork(report.lo_limit);
}
+/* ================================================== */
+
+static void
+handle_modify_selectopts(CMD_Request *rx_message, CMD_Reply *tx_message)
+{
+ int mask, options;
+ uint32_t ref_id;
+ IPAddr ip_addr;
+
+ UTI_IPNetworkToHost(&rx_message->data.modify_select_opts.address, &ip_addr);
+ ref_id = ntohl(rx_message->data.modify_select_opts.ref_id);
+ mask = ntohl(rx_message->data.modify_select_opts.mask);
+ options = convert_addsrc_select_options(ntohl(rx_message->data.modify_select_opts.options));
+
+ if (!SRC_ModifySelectOptions(&ip_addr, ref_id, options, mask))
+ tx_message->status = htons(STT_NOSUCHSOURCE);
+}
+
/* ================================================== */
/* Read a packet and process it */
@@ -1766,6 +1785,10 @@ read_from_cmd_socket(int sock_fd, int event, void *anything)
handle_reload_sources(&rx_message, &tx_message);
break;
+ case REQ_MODIFY_SELECTOPTS:
+ handle_modify_selectopts(&rx_message, &tx_message);
+ break;
+
default:
DEBUG_LOG("Unhandled command %d", rx_command);
tx_message.status = htons(STT_FAILED);
diff --git a/doc/chronyc.adoc b/doc/chronyc.adoc
index 57300ee..ca261c7 100644
--- a/doc/chronyc.adoc
+++ b/doc/chronyc.adoc
@@ -522,6 +522,23 @@ This column displays the current leap status of the source.
* _-_ indicates that a leap second will be deleted at the end of the month.
* _?_ indicates the unknown status (i.e. no valid measurement was made).
+[[selectopts]]*selectopts* _address|refid_ [_+|-option_]...::
+The *selectopts* command modifies the configured selection options of an NTP
+source specified by IP address (or the _ID#XXXXXXXXXX_ identifier used for
+unknown addresses), or a reference clock specified by reference ID as a string.
++
+The selection options can be added with the *+* symbol or removed with the *-*
+symbol. The *selectdata* command can be used to verify the configuration. The
+modified options will be applied in the next source selection, e.g. when a new
+measurement is made, or the *reselect* command is executed.
++
+An example of using this command is shown below.
++
+----
+selectopts 1.2.3.4 -noselect +prefer
+selectopts GPS +trust
+----
+
[[reselect]]*reselect*::
To avoid excessive switching between sources, *chronyd* can stay synchronised
to a source even when it is not currently the best one among the available
diff --git a/pktlength.c b/pktlength.c
index 642e477..263b16d 100644
--- a/pktlength.c
+++ b/pktlength.c
@@ -129,6 +129,7 @@ static const struct request_length request_lengths[] = {
REQ_LENGTH_ENTRY(select_data, select_data), /* SELECT_DATA */
REQ_LENGTH_ENTRY(null, null), /* RELOAD_SOURCES */
REQ_LENGTH_ENTRY(doffset, null), /* DOFFSET2 */
+ REQ_LENGTH_ENTRY(modify_select_opts, null), /* MODIFY_SELECTOPTS */
};
static const uint16_t reply_lengths[] = {
diff --git a/test/simulation/110-chronyc b/test/simulation/110-chronyc
index b78f0d8..7ecfcb5 100755
--- a/test/simulation/110-chronyc
+++ b/test/simulation/110-chronyc
@@ -164,6 +164,9 @@ for chronyc_conf in \
"reselectdist 1e-3" \
"reset sources" \
"selectdata" \
+ "selectopts 1.2.3.4 -noselect +trust +require +prefer" \
+ "selectopts ID#0000000001 +prefer" \
+ "selectopts PPS0 +prefer" \
"settime 16:30" \
"settime 16:30:05" \
"settime Nov 21, 2015 16:30:05" \
@@ -327,6 +330,8 @@ maxupdateskew 192.168.123.1 10.0
minpoll 192.168.123.1 3
minstratum 192.168.123.1 1
polltarget 192.168.123.1 10
+selectopts 192.168.123.1 +trust +prefer -require
+selectdata
delete 192.168.123.1"
run_test || test_fail
@@ -345,6 +350,10 @@ check_chronyc_output "^200 OK
200 OK
200 OK
200 OK
+200 OK
+S Name/IP Address Auth COpts EOpts Last Score Interval Leap
+=======================================================================
+M node1\.net1\.clk N \-PT\-\- \-PT\-\- 0 1\.0 \+0ns \+0ns \?
200 OK$" || test_fail
chronyc_conf="
diff --git a/test/system/007-cmdmon b/test/system/007-cmdmon
index 04d14c2..301d01c 100755
--- a/test/system/007-cmdmon
+++ b/test/system/007-cmdmon
@@ -42,6 +42,7 @@ for command in \
"reselect" \
"reselectdist 1e-3" \
"reset sources" \
+ "selectopts $server -noselect +trust +prefer +require" \
"smoothtime reset" \
"smoothtime activate" \
; do
@@ -101,7 +102,7 @@ Total good RX : [0-9]+$" || test_fail
run_chronyc "selectdata" || test_fail
check_chronyc_output "^S Name/IP Address Auth COpts EOpts Last Score Interval Leap
=======================================================================
-s 127\.0\.0\.1 N ----- ----- 0 1\.0 \+0ns \+0ns \?$" || test_fail
+s 127\.0\.0\.1 N -PTR- -PTR- 0 1\.0 \+0ns \+0ns \?$" || test_fail
run_chronyc "serverstats" || test_fail
check_chronyc_output "^NTP packets received : [0-9]+