mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
sysctl: support reading configuration from stdin and command line arguments (#39219)
This makes systemd-sysctl support following syntax, like systemd-sysusers and systemd-tmpfiles: ```sh $ /usr/lib/systemd/systemd-sysctl --inline 'foo.bar=42' $ /usr/lib/systemd/systemd-sysctl - <<EOF foo.bar=42 EOF ```
This commit is contained in:
@@ -42,8 +42,12 @@
|
||||
<para>When invoked with no arguments, <command>/usr/lib/systemd/systemd-sysctl</command> applies
|
||||
all directives from configuration files listed in
|
||||
<citerefentry><refentrytitle>sysctl.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
If one or more filenames are passed on the command line, only the directives in these files are
|
||||
applied.</para>
|
||||
When invoked with positional arguments, the configuration specified by the command line arguments is
|
||||
executed. If the string <literal>-</literal> is specified instead of a filename, the configuration is
|
||||
read from standard input. If the argument is a file name (without any slashes), all configuration
|
||||
directories are searched for a matching file and the file found that has the highest priority is
|
||||
executed. If the argument is a path, that file is used directly without searching the configuration
|
||||
directories for any other matching file.</para>
|
||||
|
||||
<para>In addition, <option>--prefix=</option> option may be used to limit which sysctl
|
||||
settings are applied.</para>
|
||||
@@ -77,6 +81,14 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--inline</option></term>
|
||||
<listitem><para>Treat each positional argument as a separate configuration line instead of a file
|
||||
name.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="cat-config" />
|
||||
<xi:include href="standard-options.xml" xpointer="tldr" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
static char **arg_prefixes = NULL;
|
||||
static CatFlags arg_cat_flags = CAT_CONFIG_OFF;
|
||||
static bool arg_strict = false;
|
||||
static bool arg_inline = false;
|
||||
static PagerFlags arg_pager_flags = 0;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_prefixes, strv_freep);
|
||||
@@ -36,7 +37,7 @@ typedef struct Option {
|
||||
bool ignore_failure;
|
||||
} Option;
|
||||
|
||||
static Option *option_free(Option *o) {
|
||||
static Option* option_free(Option *o) {
|
||||
if (!o)
|
||||
return NULL;
|
||||
|
||||
@@ -47,7 +48,10 @@ static Option *option_free(Option *o) {
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Option*, option_free);
|
||||
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(option_hash_ops, char, string_hash_func, string_compare_func, Option, option_free);
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
option_hash_ops,
|
||||
char, string_hash_func, string_compare_func,
|
||||
Option, option_free);
|
||||
|
||||
static bool test_prefix(const char *p) {
|
||||
if (strv_isempty(arg_prefixes))
|
||||
@@ -56,7 +60,7 @@ static bool test_prefix(const char *p) {
|
||||
return path_startswith_strv(p, arg_prefixes);
|
||||
}
|
||||
|
||||
static Option *option_new(
|
||||
static Option* option_new(
|
||||
const char *key,
|
||||
const char *value,
|
||||
bool ignore_failure) {
|
||||
@@ -155,14 +159,13 @@ static int apply_glob_option_with_prefix(OrderedHashmap *sysctl_options, Option
|
||||
if (option->ignore_failure || ERRNO_IS_PRIVILEGE(r)) {
|
||||
log_debug_errno(r, "Failed to resolve glob '%s', ignoring: %m", option->key);
|
||||
return 0;
|
||||
} else
|
||||
return log_error_errno(r, "Couldn't resolve glob '%s': %m", option->key);
|
||||
}
|
||||
|
||||
return log_error_errno(r, "Couldn't resolve glob '%s': %m", option->key);
|
||||
}
|
||||
|
||||
STRV_FOREACH(s, paths) {
|
||||
const char *key;
|
||||
|
||||
assert_se(key = path_startswith(*s, "/proc/sys"));
|
||||
const char *key = ASSERT_SE_PTR(path_startswith(*s, "/proc/sys"));
|
||||
|
||||
if (ordered_hashmap_contains(sysctl_options, key)) {
|
||||
log_debug("Not setting %s (explicit setting exists).", key);
|
||||
@@ -212,100 +215,79 @@ static int apply_all(OrderedHashmap *sysctl_options) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ignore_enoent) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *pp = NULL;
|
||||
unsigned c = 0;
|
||||
static int parse_line(const char *fname, unsigned line, const char *buffer, bool *invalid_config, void *userdata) {
|
||||
OrderedHashmap **sysctl_options = ASSERT_PTR(userdata);
|
||||
_cleanup_free_ char *k = NULL, *v = NULL;
|
||||
bool ignore_failure = false;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
|
||||
r = search_and_fopen(path, "re", NULL, (const char**) CONF_PATHS_STRV("sysctl.d"), &f, &pp);
|
||||
if (r < 0) {
|
||||
if (ignore_enoent && r == -ENOENT)
|
||||
return 0;
|
||||
|
||||
return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path);
|
||||
}
|
||||
|
||||
log_debug("Parsing %s", pp);
|
||||
for (;;) {
|
||||
_cleanup_(option_freep) Option *new_option = NULL;
|
||||
_cleanup_free_ char *l = NULL;
|
||||
bool ignore_failure = false;
|
||||
Option *existing;
|
||||
char *value;
|
||||
int k;
|
||||
|
||||
k = read_stripped_line(f, LONG_LINE_MAX, &l);
|
||||
if (k == 0)
|
||||
break;
|
||||
if (k < 0)
|
||||
return log_error_errno(k, "Failed to read file '%s', ignoring: %m", pp);
|
||||
|
||||
c++;
|
||||
|
||||
if (isempty(l))
|
||||
continue;
|
||||
if (strchr(COMMENTS, l[0]))
|
||||
continue;
|
||||
|
||||
char *p = l;
|
||||
value = strchr(p, '=');
|
||||
if (value) {
|
||||
if (p[0] == '-') {
|
||||
ignore_failure = true;
|
||||
p++;
|
||||
}
|
||||
|
||||
*value = 0;
|
||||
value++;
|
||||
value = strstrip(value);
|
||||
|
||||
} else {
|
||||
if (p[0] == '-')
|
||||
/* We have a "negative match" option. Let's continue with value==NULL. */
|
||||
p++;
|
||||
else {
|
||||
log_syntax(NULL, LOG_WARNING, pp, c, 0,
|
||||
"Line is not an assignment, ignoring: %s", p);
|
||||
if (r == 0)
|
||||
r = -EINVAL;
|
||||
continue;
|
||||
}
|
||||
const char *eq = strchr(buffer, '=');
|
||||
if (eq) {
|
||||
if (buffer[0] == '-') {
|
||||
ignore_failure = true;
|
||||
buffer++;
|
||||
}
|
||||
|
||||
p = strstrip(p);
|
||||
p = sysctl_normalize(p);
|
||||
|
||||
/* We can't filter out globs at this point, we'll need to do that later. */
|
||||
if (!string_is_glob(p) &&
|
||||
!test_prefix(p))
|
||||
continue;
|
||||
|
||||
existing = ordered_hashmap_get(*sysctl_options, p);
|
||||
if (existing) {
|
||||
if (streq_ptr(value, existing->value)) {
|
||||
existing->ignore_failure = existing->ignore_failure || ignore_failure;
|
||||
continue;
|
||||
}
|
||||
|
||||
log_debug("Overwriting earlier assignment of %s at '%s:%u'.", p, pp, c);
|
||||
option_free(ordered_hashmap_remove(*sysctl_options, p));
|
||||
}
|
||||
|
||||
new_option = option_new(p, value, ignore_failure);
|
||||
if (!new_option)
|
||||
k = strndup(buffer, eq - buffer);
|
||||
if (!k)
|
||||
return log_oom();
|
||||
|
||||
k = ordered_hashmap_ensure_put(sysctl_options, &option_hash_ops, new_option->key, new_option);
|
||||
if (k < 0)
|
||||
return log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", p);
|
||||
v = strdup(eq + 1);
|
||||
if (!v)
|
||||
return log_oom();
|
||||
|
||||
TAKE_PTR(new_option);
|
||||
} else {
|
||||
if (buffer[0] == '-')
|
||||
/* We have a "negative match" option. Let's continue with value==NULL. */
|
||||
buffer++;
|
||||
else
|
||||
return log_syntax(NULL, LOG_WARNING, fname, line, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Line is not an assignment, ignoring: %s", buffer);
|
||||
|
||||
k = strdup(buffer);
|
||||
if (!k)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
return r;
|
||||
const char *key = sysctl_normalize(strstrip(k)), *value = strstrip(v);
|
||||
|
||||
/* We can't filter out globs at this point, we'll need to do that later. */
|
||||
if (!string_is_glob(key) && !test_prefix(key))
|
||||
return 0;
|
||||
|
||||
Option *existing = ordered_hashmap_get(*sysctl_options, key);
|
||||
if (existing) {
|
||||
if (streq_ptr(value, existing->value)) {
|
||||
existing->ignore_failure = existing->ignore_failure || ignore_failure;
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_syntax(NULL, LOG_DEBUG, fname, line, 0,
|
||||
"Overwriting earlier assignment of '%s'.", key);
|
||||
option_free(ordered_hashmap_remove(*sysctl_options, key));
|
||||
}
|
||||
|
||||
_cleanup_(option_freep) Option *option = option_new(key, value, ignore_failure);
|
||||
if (!option)
|
||||
return log_oom();
|
||||
|
||||
r = ordered_hashmap_ensure_put(sysctl_options, &option_hash_ops, option->key, option);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add sysctl variable '%s' to hashmap: %m", key);
|
||||
|
||||
TAKE_PTR(option);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_file(OrderedHashmap **sysctl_options, const char *path, bool ignore_enoent) {
|
||||
return conf_file_read(
|
||||
/* root = */ NULL,
|
||||
(const char**) CONF_PATHS_STRV("sysctl.d"),
|
||||
path,
|
||||
parse_line,
|
||||
sysctl_options,
|
||||
ignore_enoent,
|
||||
/* invalid_config = */ NULL);
|
||||
}
|
||||
|
||||
static int read_credential_lines(OrderedHashmap **sysctl_options) {
|
||||
@@ -323,8 +305,7 @@ static int read_credential_lines(OrderedHashmap **sysctl_options) {
|
||||
if (!j)
|
||||
return log_oom();
|
||||
|
||||
(void) parse_file(sysctl_options, j, /* ignore_enoent= */ true);
|
||||
return 0;
|
||||
return parse_file(sysctl_options, j, /* ignore_enoent= */ true);
|
||||
}
|
||||
|
||||
static int cat_config(char **files) {
|
||||
@@ -341,16 +322,23 @@ static int help(void) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
|
||||
"Applies kernel sysctl settings.\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
printf("%1$s [OPTIONS...] [CONFIGURATION FILE...]\n"
|
||||
"\n%2$sApplies kernel sysctl settings.%4$s\n"
|
||||
"\n%3$sCommands:%4$s\n"
|
||||
" --cat-config Show configuration files\n"
|
||||
" --tldr Show non-comment parts of configuration\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
"\n%3$sOptions:%4$s\n"
|
||||
" --prefix=PATH Only apply rules with the specified prefix\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
"\nSee the %s for details.\n",
|
||||
" --strict Fail on any kind of failures\n"
|
||||
" --inline Treat arguments as configuration lines\n"
|
||||
"\nSee the %5$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
link);
|
||||
|
||||
return 0;
|
||||
@@ -365,6 +353,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_PREFIX,
|
||||
ARG_NO_PAGER,
|
||||
ARG_STRICT,
|
||||
ARG_INLINE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -375,6 +364,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "prefix", required_argument, NULL, ARG_PREFIX },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "strict", no_argument, NULL, ARG_STRICT },
|
||||
{ "inline", no_argument, NULL, ARG_INLINE },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -430,6 +420,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_strict = true;
|
||||
break;
|
||||
|
||||
case ARG_INLINE:
|
||||
arg_inline = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -457,11 +451,15 @@ static int run(int argc, char *argv[]) {
|
||||
umask(0022);
|
||||
|
||||
if (argc > optind) {
|
||||
r = 0;
|
||||
|
||||
for (int i = optind; i < argc; i++)
|
||||
RET_GATHER(r, parse_file(&sysctl_options, argv[i], false));
|
||||
unsigned pos = 0;
|
||||
|
||||
STRV_FOREACH(arg, strv_skip(argv, optind)) {
|
||||
if (arg_inline)
|
||||
/* Use (argument):n, where n==1 for the first positional arg */
|
||||
RET_GATHER(r, parse_line("(argument)", ++pos, *arg, /* invalid_config = */ NULL, &sysctl_options));
|
||||
else
|
||||
RET_GATHER(r, parse_file(&sysctl_options, *arg, false));
|
||||
}
|
||||
} else {
|
||||
_cleanup_strv_free_ char **files = NULL;
|
||||
|
||||
@@ -478,9 +476,7 @@ static int run(int argc, char *argv[]) {
|
||||
RET_GATHER(r, read_credential_lines(&sysctl_options));
|
||||
}
|
||||
|
||||
RET_GATHER(r, apply_all(sysctl_options));
|
||||
|
||||
return r;
|
||||
return RET_GATHER(r, apply_all(sysctl_options));
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
integration_tests += [
|
||||
integration_test_template + {
|
||||
'name' : fs.name(meson.current_source_dir()),
|
||||
},
|
||||
]
|
||||
@@ -90,7 +90,6 @@ foreach dirname : [
|
||||
'TEST-73-LOCALE',
|
||||
'TEST-74-AUX-UTILS',
|
||||
'TEST-75-RESOLVED',
|
||||
'TEST-76-SYSCTL',
|
||||
'TEST-78-SIGQUEUE',
|
||||
'TEST-79-MEMPRESS',
|
||||
'TEST-80-NOTIFYACCESS',
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
export SYSTEMD_LOG_LEVEL=debug
|
||||
|
||||
echo "foo.bar=42" >/tmp/foo.conf
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
|
||||
assert_rc 1 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
|
||||
|
||||
echo "-foo.foo=42" >/tmp/foo.conf
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
|
||||
|
||||
if ! systemd-detect-virt --quiet --container; then
|
||||
ip link add hoge type dummy
|
||||
udevadm wait --timeout=30 /sys/class/net/hoge
|
||||
|
||||
cat >/tmp/foo.conf <<EOF
|
||||
net.ipv4.conf.*.drop_gratuitous_arp=1
|
||||
net.ipv4.*.*.bootp_relay=1
|
||||
net.ipv4.aaa.*.disable_policy=1
|
||||
EOF
|
||||
|
||||
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
|
||||
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge /tmp/foo.conf
|
||||
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"
|
||||
fi
|
||||
|
||||
touch /testok
|
||||
80
test/units/TEST-87-AUX-UTILS-VM.sysctl.sh
Executable file
80
test/units/TEST-87-AUX-UTILS-VM.sysctl.sh
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
export SYSTEMD_LOG_LEVEL=debug
|
||||
|
||||
echo "foo.bar=42" >/tmp/foo.conf
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
|
||||
assert_rc 1 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
|
||||
|
||||
echo "-foo.foo=42" >/tmp/foo.conf
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl /tmp/foo.conf
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl --strict /tmp/foo.conf
|
||||
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl --inline "foo.bar=42"
|
||||
assert_rc 1 /usr/lib/systemd/systemd-sysctl --inline --strict "foo.bar=42"
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl --inline -- "-foo.bar=42"
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl --inline --strict -- "-foo.bar=42"
|
||||
|
||||
/usr/lib/systemd/systemd-sysctl - <<EOF
|
||||
foo.bar=42
|
||||
EOF
|
||||
(! /usr/lib/systemd/systemd-sysctl --strict - <<EOF
|
||||
foo.bar=42
|
||||
EOF
|
||||
)
|
||||
/usr/lib/systemd/systemd-sysctl - <<EOF
|
||||
-foo.bar=42
|
||||
EOF
|
||||
/usr/lib/systemd/systemd-sysctl --strict - <<EOF
|
||||
-foo.bar=42
|
||||
EOF
|
||||
|
||||
ip link add hoge type dummy
|
||||
trap 'ip link del hoge' EXIT
|
||||
udevadm wait --timeout=30 /sys/class/net/hoge
|
||||
|
||||
cat >/tmp/foo.conf <<EOF
|
||||
net.ipv4.conf.*.drop_gratuitous_arp=1
|
||||
net.ipv4.*.*.bootp_relay=1
|
||||
net.ipv4.aaa.*.disable_policy=1
|
||||
EOF
|
||||
|
||||
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
|
||||
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge /tmp/foo.conf
|
||||
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
|
||||
|
||||
assert_rc 0 /usr/lib/systemd/systemd-sysctl --prefix=/net/ipv4/conf/hoge --inline \
|
||||
'net.ipv4.conf.*.drop_gratuitous_arp=1' \
|
||||
'net.ipv4.*.*.bootp_relay=1' \
|
||||
'net.ipv4.aaa.*.disable_policy=1'
|
||||
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 - <<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"
|
||||
Reference in New Issue
Block a user