sysctl: add --verify option to verify written values (#43258)

This commit is contained in:
Yu Watanabe
2026-08-06 05:46:11 +09:00
committed by GitHub
5 changed files with 76 additions and 7 deletions

View File

@@ -62,7 +62,7 @@
<refsect1><title>Options</title>
<variablelist>
<varlistentry id='prefix'>
<varlistentry>
<term><option>--prefix=</option></term>
<listitem>
<para>Only apply rules with the specified prefix.</para>
@@ -70,8 +70,8 @@
<xi:include href="version-info.xml" xpointer="v230"/>
</listitem>
</varlistentry>
<varlistentry id='strict'>
<term><option>--strict=</option></term>
<varlistentry>
<term><option>--strict</option></term>
<listitem>
<para>Always return non-zero exit code on failure (including invalid sysctl variable
name and insufficient permissions), unless the sysctl variable name is prefixed with a "-"
@@ -81,6 +81,15 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--verify</option></term>
<listitem>
<para>Verify sysctl values after write.</para>
<xi:include href="version-info.xml" xpointer="v262"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--inline</option></term>
<listitem><para>Treat each positional argument as a separate configuration line instead of a file

View File

@@ -114,6 +114,43 @@ int sysctl_writef(const char *property, const char *format, ...) {
return sysctl_write(property, v);
}
int sysctl_write_verify(const char *property, const char *value) {
int r;
assert(property);
assert(value);
/* Some sysctl settings accept invalid values on write, but refuses on read. E.g. coredump pattern,
* be1e0283021ec73c2eb92839db9a471a068709d9 (v6.17) and 7d7c1fb85cba5627bbe741fb7539c709435e3848
* (v6.16.8), which is fixed by a779e27f24aeb679969ddd1fdd7f636e22ddbc1e (v6.18) and
* 304aa560385720baf3660fe8500f6dd425b63ea9 (v6.17.5). Let's first save the original value, and
* restore to the saved value if the new value is refused. */
_cleanup_free_ char *original = NULL;
r = sysctl_read(property, &original);
if (r >= 0 && streq(original, value))
return 0; /* Already set. */
r = sysctl_write(property, value);
if (r >= 0) {
_cleanup_free_ char *current = NULL;
r = sysctl_read(property, &current);
if (r >= 0) {
if (streq(current, value))
return 0; /* Yay! */
/* At least for coredump pattern, this does not happen, but let's handle this as the
* same as we wrote something invalid. */
r = -EINVAL;
}
}
if (original)
(void) sysctl_write(property, original);
return r;
}
static const char* af_to_sysctl_dir(int af) {
if (af == AF_MPLS)
return "mpls";

View File

@@ -12,6 +12,7 @@ int sysctl_writef(const char *property, const char *format, ...) _printf_(2, 3);
static inline int sysctl_write(const char *property, const char *value) {
return sysctl_write_full(property, value, NULL);
}
int sysctl_write_verify(const char *property, const char *value);
int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret);
int sysctl_read_ip_property_int(int af, const char *ifname, const char *property, int *ret);

View File

@@ -25,6 +25,7 @@
static char **arg_prefixes = NULL;
static CatFlags arg_cat_flags = CAT_CONFIG_OFF;
static bool arg_strict = false;
static bool arg_verify = false;
static bool arg_inline = false;
static PagerFlags arg_pager_flags = 0;
@@ -89,7 +90,10 @@ static SysctlOption* sysctl_option_new(
static int sysctl_write_or_warn(const char *key, const char *value, bool ignore_failure, bool ignore_enoent) {
int r;
r = sysctl_write(key, value);
if (arg_verify)
r = sysctl_write_verify(key, value);
else
r = sysctl_write(key, value);
if (r < 0) {
/* Proceed without failing if ignore_failure is true.
* If the sysctl is not available in the kernel or we are running with reduced privileges and
@@ -409,6 +413,11 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) {
arg_strict = true;
break;
OPTION_LONG("verify", NULL,
"Verify sysctl values after write"):
arg_verify = true;
break;
OPTION_LONG("inline", NULL,
"Treat arguments as configuration lines"):
arg_inline = true;
@@ -425,18 +434,18 @@ static int parse_argv(int argc, char *argv[], char ***remaining_args) {
}
static int run(int argc, char *argv[]) {
_cleanup_ordered_hashmap_free_ OrderedHashmap *sysctl_options = NULL;
int r;
log_setup();
char **args = NULL;
r = parse_argv(argc, argv, &args);
if (r <= 0)
return r;
log_setup();
umask(0022);
_cleanup_ordered_hashmap_free_ OrderedHashmap *sysctl_options = NULL;
if (!strv_isempty(args)) {
unsigned pos = 0;

View File

@@ -78,3 +78,16 @@ EOF
assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp)" "1"
assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/bootp_relay)" "1"
assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/disable_policy)" "0"
echo 0 >/proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp
echo 0 >/proc/sys/net/ipv4/conf/hoge/bootp_relay
echo 0 >/proc/sys/net/ipv4/conf/hoge/disable_policy
/usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge --verify - <<EOF
net.ipv4.conf.*.drop_gratuitous_arp=1
net.ipv4.*.*.bootp_relay=1
net.ipv4.aaa.*.disable_policy=1
EOF
assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/drop_gratuitous_arp)" "1"
assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/bootp_relay)" "1"
assert_eq "$(cat /proc/sys/net/ipv4/conf/hoge/disable_policy)" "0"