mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
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:
parent
96c648fecd
commit
523ea1237a
@ -148,6 +148,7 @@ All execution-related settings are available for transient units.
|
||||
✓ SyslogLevelPrefix=
|
||||
✓ LogLevelMax=
|
||||
✓ LogExtraFields=
|
||||
✓ LogFilterPatterns=
|
||||
✓ LogRateLimitIntervalSec=
|
||||
✓ LogRateLimitBurst=
|
||||
✓ SecureBits=
|
||||
|
@ -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"/>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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")) {
|
||||
|
@ -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");
|
||||
|
@ -846,6 +846,7 @@ LogExtraFields=
|
||||
LogLevelMax=
|
||||
LogRateLimitIntervalSec=
|
||||
LogRateLimitBurst=
|
||||
LogFilterPatterns=
|
||||
LogsDirectory=
|
||||
LogsDirectoryMode=
|
||||
MACVLAN=
|
||||
|
Loading…
Reference in New Issue
Block a user