journal: log filtering options support in PID1

Define new unit parameter (LogFilterPatterns) to filter logs processed by
journald.

This option is used to store a regular expression which is carried from
PID1 to systemd-journald through a cgroup xattrs:
`user.journald_log_filter_patterns`.
This commit is contained in:
Quentin Deslandes 2022-11-07 20:30:01 +01:00
parent 96c648fecd
commit 523ea1237a
14 changed files with 339 additions and 1 deletions

View file

@ -148,6 +148,7 @@ All execution-related settings are available for transient units.
✓ SyslogLevelPrefix=
✓ LogLevelMax=
✓ LogExtraFields=
✓ LogFilterPatterns=
✓ LogRateLimitIntervalSec=
✓ LogRateLimitBurst=
✓ SecureBits=

View file

@ -2928,6 +2928,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly aay LogExtraFields = [[...], ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(bs) LogFilterPatterns = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s LogNamespace = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i SecureBits = ...;
@ -3479,6 +3481,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property LogExtraFields is not documented!-->
<!--property LogFilterPatterns is not documented!-->
<!--property LogNamespace is not documented!-->
<!--property AmbientCapabilities is not documented!-->
@ -4083,6 +4087,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="LogExtraFields"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogFilterPatterns"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogNamespace"/>
<variablelist class="dbus-property" generated="True" extra-ref="SecureBits"/>
@ -4822,6 +4828,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly aay LogExtraFields = [[...], ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(bs) LogFilterPatterns = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s LogNamespace = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i SecureBits = ...;
@ -5397,6 +5405,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<!--property LogExtraFields is not documented!-->
<!--property LogFilterPatterns is not documented!-->
<!--property LogNamespace is not documented!-->
<!--property AmbientCapabilities is not documented!-->
@ -5995,6 +6005,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="LogExtraFields"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogFilterPatterns"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogNamespace"/>
<variablelist class="dbus-property" generated="True" extra-ref="SecureBits"/>
@ -6623,6 +6635,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly aay LogExtraFields = [[...], ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(bs) LogFilterPatterns = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s LogNamespace = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i SecureBits = ...;
@ -7126,6 +7140,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<!--property LogExtraFields is not documented!-->
<!--property LogFilterPatterns is not documented!-->
<!--property LogNamespace is not documented!-->
<!--property AmbientCapabilities is not documented!-->
@ -7642,6 +7658,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="LogExtraFields"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogFilterPatterns"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogNamespace"/>
<variablelist class="dbus-property" generated="True" extra-ref="SecureBits"/>
@ -8397,6 +8415,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly aay LogExtraFields = [[...], ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(bs) LogFilterPatterns = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s LogNamespace = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i SecureBits = ...;
@ -8886,6 +8906,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<!--property LogExtraFields is not documented!-->
<!--property LogFilterPatterns is not documented!-->
<!--property LogNamespace is not documented!-->
<!--property AmbientCapabilities is not documented!-->
@ -9388,6 +9410,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<variablelist class="dbus-property" generated="True" extra-ref="LogExtraFields"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogFilterPatterns"/>
<variablelist class="dbus-property" generated="True" extra-ref="LogNamespace"/>
<variablelist class="dbus-property" generated="True" extra-ref="SecureBits"/>

View file

@ -2919,6 +2919,34 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>LogFilterPatterns=</varname></term>
<listitem><para>Define an extended regular expression to filter log messages based on the
<varname>MESSAGE=</varname> field of the structured message. If the first character of the pattern is
<literal>~</literal>, log entries matching the pattern should be discarded. This option takes a single
pattern as an argument but can be used multiple times to create a list of allowed and denied patterns.
If the empty string is assigned, the filter is reset, and all prior assignments will have no effect.</para>
<para>Because the <literal>~</literal> character is used to define denied patterns, it must be replaced
with <literal>\x7e</literal> to allow a message starting with <literal>~</literal>. For example,
<literal>~foobar</literal> would add a pattern matching <literal>foobar</literal> to the deny list, while
<literal>\x7efoobar</literal> would add a pattern matching <literal>~foobar</literal> to the allow list.</para>
<para>Log messages are tested against denied patterns (if any), then against allowed patterns
(if any). If a log message matches any of the denied patterns, it will be discarded, whatever the
allowed patterns. Then, remaining log messages are tested against allowed patterns. Messages matching
against none of the allowed pattern are discarded. If no allowed patterns are defined, then all
messages are processed directly after going through denied filters.</para>
<para>Filtering is based on the unit for which <varname>LogFilterPatterns=</varname> is defined, meaning log
messages coming from
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> about the
unit are not taken into account. Filtered log messages won't be forwarded to traditional syslog daemons,
the kernel log buffer (kmsg), the systemd console, or sent as wall messages to all logged-in
users.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>LogNamespace=</varname></term>

View file

@ -781,6 +781,51 @@ void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path) {
unit_remove_xattr_graceful(u, cgroup_path, "user.oomd_omit");
}
int cgroup_log_xattr_apply(Unit *u, const char *cgroup_path) {
ExecContext *c;
size_t len, allowed_patterns_len, denied_patterns_len;
_cleanup_free_ char *patterns = NULL, *allowed_patterns = NULL, *denied_patterns = NULL;
int r;
assert(u);
c = unit_get_exec_context(u);
if (!c)
/* Some unit types have a cgroup context but no exec context, so we do not log
* any error here to avoid confusion. */
return 0;
if (set_isempty(c->log_filter_allowed_patterns) && set_isempty(c->log_filter_denied_patterns)) {
unit_remove_xattr_graceful(u, cgroup_path, "user.journald_log_filter_patterns");
return 0;
}
r = set_make_nulstr(c->log_filter_allowed_patterns, &allowed_patterns, &allowed_patterns_len);
if (r < 0)
return log_debug_errno(r, "Failed to make nulstr from set: %m");
r = set_make_nulstr(c->log_filter_denied_patterns, &denied_patterns, &denied_patterns_len);
if (r < 0)
return log_debug_errno(r, "Failed to make nulstr from set: %m");
/* Use nul character separated strings without trailing nul */
allowed_patterns_len = LESS_BY(allowed_patterns_len, 1u);
denied_patterns_len = LESS_BY(denied_patterns_len, 1u);
len = allowed_patterns_len + 1 + denied_patterns_len;
patterns = new(char, len);
if (!patterns)
return log_oom_debug();
memcpy_safe(patterns, allowed_patterns, allowed_patterns_len);
patterns[allowed_patterns_len] = '\xff';
memcpy_safe(&patterns[allowed_patterns_len + 1], denied_patterns, denied_patterns_len);
unit_set_xattr_graceful(u, cgroup_path, "user.journald_log_filter_patterns", patterns, len);
return 0;
}
static void cgroup_xattr_apply(Unit *u) {
bool b;
@ -788,6 +833,7 @@ static void cgroup_xattr_apply(Unit *u) {
/* The 'user.*' xattrs can be set from a user manager. */
cgroup_oomd_xattr_apply(u, u->cgroup_path);
cgroup_log_xattr_apply(u, u->cgroup_path);
if (!MANAGER_IS_SYSTEM(u->manager))
return;

View file

@ -240,6 +240,7 @@ int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode)
int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path);
void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path);
int cgroup_log_xattr_apply(Unit *u, const char *cgroup_path);
CGroupMask unit_get_own_mask(Unit *u);
CGroupMask unit_get_delegate_mask(Unit *u);

View file

@ -31,6 +31,7 @@
#include "namespace.h"
#include "parse-util.h"
#include "path-util.h"
#include "pcre2-util.h"
#include "process-util.h"
#include "rlimit-util.h"
#if HAVE_SECCOMP
@ -799,6 +800,53 @@ static int property_get_log_extra_fields(
return sd_bus_message_close_container(reply);
}
static int sd_bus_message_append_log_filter_patterns(sd_bus_message *reply, Set *patterns, bool is_allowlist) {
const char *pattern;
int r;
assert(reply);
SET_FOREACH(pattern, patterns) {
r = sd_bus_message_append(reply, "(bs)", is_allowlist, pattern);
if (r < 0)
return r;
}
return 0;
}
static int property_get_log_filter_patterns(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
ExecContext *c = userdata;
int r;
assert(c);
assert(reply);
r = sd_bus_message_open_container(reply, 'a', "(bs)");
if (r < 0)
return r;
r = sd_bus_message_append_log_filter_patterns(reply, c->log_filter_allowed_patterns,
/* is_allowlist = */ true);
if (r < 0)
return r;
r = sd_bus_message_append_log_filter_patterns(reply, c->log_filter_denied_patterns,
/* is_allowlist = */ false);
if (r < 0)
return r;
return sd_bus_message_close_container(reply);
}
static int property_get_set_credential(
sd_bus *bus,
const char *path,
@ -1195,6 +1243,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("LogRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(ExecContext, log_ratelimit_interval_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogRateLimitBurst", "u", bus_property_get_unsigned, offsetof(ExecContext, log_ratelimit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogFilterPatterns", "a(bs)", property_get_log_filter_patterns, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogNamespace", "s", NULL, offsetof(ExecContext, log_namespace), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CapabilityBoundingSet", "t", NULL, offsetof(ExecContext, capability_bounding_set), SD_BUS_VTABLE_PROPERTY_CONST),
@ -1792,6 +1841,61 @@ int bus_exec_context_set_transient_property(
if (streq(name, "LogRateLimitBurst"))
return bus_set_transient_unsigned(u, name, &c->log_ratelimit_burst, message, flags, error);
if (streq(name, "LogFilterPatterns")) {
/* Use _cleanup_free_, not _cleanup_strv_free_, as we don't want the content of the strv
* to be freed. */
_cleanup_free_ char **allow_list = NULL, **deny_list = NULL;
const char *pattern;
int is_allowlist;
r = sd_bus_message_enter_container(message, 'a', "(bs)");
if (r < 0)
return r;
while ((r = sd_bus_message_read(message, "(bs)", &is_allowlist, &pattern)) > 0) {
_cleanup_(pattern_freep) pcre2_code *compiled_pattern = NULL;
if (isempty(pattern))
continue;
r = pattern_compile_and_log(pattern, 0, &compiled_pattern);
if (r < 0)
return r;
r = strv_push(is_allowlist ? &allow_list : &deny_list, (char *)pattern);
if (r < 0)
return r;
}
if (r < 0)
return r;
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (strv_isempty(allow_list) && strv_isempty(deny_list)) {
c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
unit_write_settingf(u, flags, name, "%s=", name);
} else {
r = set_put_strdupv(&c->log_filter_allowed_patterns, allow_list);
if (r < 0)
return r;
r = set_put_strdupv(&c->log_filter_denied_patterns, deny_list);
if (r < 0)
return r;
STRV_FOREACH(unit_pattern, allow_list)
unit_write_settingf(u, flags, name, "%s=%s", name, *unit_pattern);
STRV_FOREACH(unit_pattern, deny_list)
unit_write_settingf(u, flags, name, "%s=~%s", name, *unit_pattern);
}
}
return 1;
}
if (streq(name, "Personality"))
return bus_set_transient_personality(u, name, &c->personality, message, flags, error);

View file

@ -5250,9 +5250,10 @@ int exec_spawn(Unit *unit,
if (r < 0)
return log_unit_error_errno(unit, r, "Failed to create control group '%s': %m", subcgroup_path);
/* Normally we would not propagate the oomd xattrs to children but since we created this
/* Normally we would not propagate the xattrs to children but since we created this
* sub-cgroup internally we should do it. */
cgroup_oomd_xattr_apply(unit, subcgroup_path);
cgroup_log_xattr_apply(unit, subcgroup_path);
}
}
@ -5406,6 +5407,8 @@ void exec_context_done(ExecContext *c) {
c->log_level_max = -1;
exec_context_free_log_extra_fields(c);
c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
c->log_ratelimit_interval_usec = 0;
c->log_ratelimit_burst = 0;
@ -6000,6 +6003,17 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
if (c->log_ratelimit_burst > 0)
fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_ratelimit_burst);
if (!set_isempty(c->log_filter_allowed_patterns) || !set_isempty(c->log_filter_denied_patterns)) {
fprintf(f, "%sLogFilterPatterns:", prefix);
char *pattern;
SET_FOREACH(pattern, c->log_filter_allowed_patterns)
fprintf(f, " %s", pattern);
SET_FOREACH(pattern, c->log_filter_denied_patterns)
fprintf(f, " ~%s", pattern);
fputc('\n', f);
}
for (size_t j = 0; j < c->n_log_extra_fields; j++) {
fprintf(f, "%sLogExtraFields: ", prefix);
fwrite(c->log_extra_fields[j].iov_base,

View file

@ -24,6 +24,7 @@ typedef struct Manager Manager;
#include "nsflags.h"
#include "numa-util.h"
#include "path-util.h"
#include "set.h"
#include "time-util.h"
#define EXEC_STDIN_DATA_MAX (64U*1024U*1024U)
@ -286,6 +287,8 @@ struct ExecContext {
struct iovec* log_extra_fields;
size_t n_log_extra_fields;
Set *log_filter_allowed_patterns;
Set *log_filter_denied_patterns;
usec_t log_ratelimit_interval_usec;
unsigned log_ratelimit_burst;

View file

@ -52,6 +52,7 @@
{{type}}.LogRateLimitIntervalSec, config_parse_sec, 0, offsetof({{type}}, exec_context.log_ratelimit_interval_usec)
{{type}}.LogRateLimitBurst, config_parse_unsigned, 0, offsetof({{type}}, exec_context.log_ratelimit_burst)
{{type}}.LogExtraFields, config_parse_log_extra_fields, 0, offsetof({{type}}, exec_context)
{{type}}.LogFilterPatterns, config_parse_log_filter_patterns, 0, offsetof({{type}}, exec_context)
{{type}}.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof({{type}}, exec_context)
{{type}}.SecureBits, config_parse_exec_secure_bits, 0, offsetof({{type}}, exec_context.secure_bits)
{{type}}.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof({{type}}, exec_context.capability_bounding_set)

View file

@ -52,6 +52,7 @@
#include "parse-helpers.h"
#include "parse-util.h"
#include "path-util.h"
#include "pcre2-util.h"
#include "percent-util.h"
#include "process-util.h"
#if HAVE_SECCOMP
@ -6225,6 +6226,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_job_mode, "MODE" },
{ config_parse_job_mode_isolate, "BOOLEAN" },
{ config_parse_personality, "PERSONALITY" },
{ config_parse_log_filter_patterns, "REGEX" },
};
const char *prev = NULL;
@ -6489,3 +6491,54 @@ int config_parse_tty_size(
return config_parse_unsigned(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
}
int config_parse_log_filter_patterns(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = ASSERT_PTR(data);
_cleanup_(pattern_freep) pcre2_code *compiled_pattern = NULL;
const char *pattern = ASSERT_PTR(rvalue);
bool is_allowlist = true;
int r;
assert(filename);
assert(lvalue);
if (isempty(pattern)) {
/* Empty assignment resets the lists. */
c->log_filter_allowed_patterns = set_free(c->log_filter_allowed_patterns);
c->log_filter_denied_patterns = set_free(c->log_filter_denied_patterns);
return 0;
}
if (pattern[0] == '~') {
is_allowlist = false;
pattern++;
if (isempty(pattern))
/* LogFilterPatterns=~ is not considered a valid pattern. */
return log_syntax(unit, LOG_WARNING, filename, line, 0,
"Regex pattern invalid, ignoring: %s=%s", lvalue, rvalue);
}
if (pattern_compile_and_log(pattern, 0, &compiled_pattern) < 0)
return 0;
r = set_put_strdup(is_allowlist ? &c->log_filter_allowed_patterns : &c->log_filter_denied_patterns,
pattern);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to store log filtering pattern, ignoring: %s=%s", lvalue, rvalue);
return 0;
}
return 0;
}

View file

@ -150,6 +150,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_socket_bind);
CONFIG_PARSER_PROTOTYPE(config_parse_restrict_network_interfaces);
CONFIG_PARSER_PROTOTYPE(config_parse_watchdog_sec);
CONFIG_PARSER_PROTOTYPE(config_parse_tty_size);
CONFIG_PARSER_PROTOTYPE(config_parse_log_filter_patterns);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);

View file

@ -1218,6 +1218,16 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
return 1;
}
if (streq(field, "LogFilterPatterns")) {
r = sd_bus_message_append(m, "(sv)", "LogFilterPatterns", "a(bs)", 1,
eq[0] != '~',
eq[0] != '~' ? eq : eq + 1);
if (r < 0)
return bus_log_create_error(r);
return 1;
}
if (STR_IN_SET(field, "StandardInput",
"StandardOutput",
"StandardError")) {

View file

@ -22,6 +22,7 @@
#include "load-fragment.h"
#include "macro.h"
#include "memory-util.h"
#include "pcre2-util.h"
#include "rm-rf.h"
#include "specifier.h"
#include "string-util.h"
@ -997,6 +998,56 @@ TEST(unit_is_recursive_template_dependency) {
assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.mount", "foobar@%n.mount") == 0);
}
#define TEST_PATTERN(_regex, _allowed_patterns_count, _denied_patterns_count) \
{ \
.regex = _regex, \
.allowed_patterns_count = _allowed_patterns_count, \
.denied_patterns_count = _denied_patterns_count \
}
TEST(config_parse_log_filter_patterns) {
ExecContext c = {};
int r;
static const struct {
const char *regex;
size_t allowed_patterns_count;
size_t denied_patterns_count;
} regex_tests[] = {
TEST_PATTERN("", 0, 0),
TEST_PATTERN(".*", 1, 0),
TEST_PATTERN("~.*", 1, 1),
TEST_PATTERN("", 0, 0),
TEST_PATTERN("~.*", 0, 1),
TEST_PATTERN("[.*", 0, 1), /* Invalid pattern. */
TEST_PATTERN(".*gg.*", 1, 1),
TEST_PATTERN("~.*", 1, 1), /* Already in the patterns list. */
TEST_PATTERN("[.*", 1, 1), /* Invalid pattern. */
TEST_PATTERN("\\x7ehello", 2, 1),
TEST_PATTERN("", 0, 0),
TEST_PATTERN("~foobar", 0, 1),
};
if (ERRNO_IS_NOT_SUPPORTED(dlopen_pcre2()))
return (void) log_tests_skipped("PCRE2 support is not available");
for (size_t i = 0; i < ELEMENTSOF(regex_tests); i++) {
r = config_parse_log_filter_patterns(NULL, "fake", 1, "section", 1, "LogFilterPatterns", 1,
regex_tests[i].regex, &c, NULL);
assert_se(r >= 0);
assert_se(set_size(c.log_filter_allowed_patterns) == regex_tests[i].allowed_patterns_count);
assert_se(set_size(c.log_filter_denied_patterns) == regex_tests[i].denied_patterns_count);
/* Ensure `~` is properly removed */
const char *p;
SET_FOREACH(p, c.log_filter_allowed_patterns)
assert_se(p && p[0] != '~');
SET_FOREACH(p, c.log_filter_denied_patterns)
assert_se(p && p[0] != '~');
}
}
static int intro(void) {
if (enter_cgroup_subroot(NULL) == -ENOMEDIUM)
return log_tests_skipped("cgroupfs not available");

View file

@ -846,6 +846,7 @@ LogExtraFields=
LogLevelMax=
LogRateLimitIntervalSec=
LogRateLimitBurst=
LogFilterPatterns=
LogsDirectory=
LogsDirectoryMode=
MACVLAN=