diff --git a/man/systemd-sysctl.service.xml b/man/systemd-sysctl.service.xml index 61fe5238b73..e3d33bf1f4b 100644 --- a/man/systemd-sysctl.service.xml +++ b/man/systemd-sysctl.service.xml @@ -62,7 +62,7 @@ Options - + Only apply rules with the specified prefix. @@ -70,8 +70,8 @@ - - + + 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 @@ + + + + Verify sysctl values after write. + + + + + Treat each positional argument as a separate configuration line instead of a file diff --git a/src/basic/sysctl-util.c b/src/basic/sysctl-util.c index 7bec823f421..9834d0323ad 100644 --- a/src/basic/sysctl-util.c +++ b/src/basic/sysctl-util.c @@ -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, ¤t); + 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"; diff --git a/src/basic/sysctl-util.h b/src/basic/sysctl-util.h index a65c40b43f9..1e706c839cf 100644 --- a/src/basic/sysctl-util.h +++ b/src/basic/sysctl-util.h @@ -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); diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index e124b56fc9c..8149e755f59 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -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; diff --git a/test/units/TEST-87-AUX-UTILS-VM.sysctl.sh b/test/units/TEST-87-AUX-UTILS-VM.sysctl.sh index f84621431c9..9385c05a47d 100755 --- a/test/units/TEST-87-AUX-UTILS-VM.sysctl.sh +++ b/test/units/TEST-87-AUX-UTILS-VM.sysctl.sh @@ -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 - <