summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorQuentin Deslandes <qde@naccy.de>2022-11-07 20:30:01 +0100
committerQuentin Deslandes <qde@naccy.de>2022-12-15 09:57:39 +0000
commit523ea1237a3db96c98eae02d87ec189816437c4c (patch)
tree76f5b68b17b97d087894a3fb8dfaac2dda9c8a89 /src/core
parent96c648fecde74ed48270b2d6e29d11b12bf2e2b9 (diff)
downloadsystemd-523ea1237a3db96c98eae02d87ec189816437c4c.tar.gz
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`.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/cgroup.c46
-rw-r--r--src/core/cgroup.h1
-rw-r--r--src/core/dbus-execute.c104
-rw-r--r--src/core/execute.c16
-rw-r--r--src/core/execute.h3
-rw-r--r--src/core/load-fragment-gperf.gperf.in1
-rw-r--r--src/core/load-fragment.c53
-rw-r--r--src/core/load-fragment.h1
8 files changed, 224 insertions, 1 deletions
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index ecc3cb32ef..2d671566ac 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -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;
diff --git a/src/core/cgroup.h b/src/core/cgroup.h
index 09352bafc6..a6a3d186ac 100644
--- a/src/core/cgroup.h
+++ b/src/core/cgroup.h
@@ -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);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index c5a51bf5cd..86f88cb1c0 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -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);
diff --git a/src/core/execute.c b/src/core/execute.c
index 7963582ea2..42c95556ac 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -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,
diff --git a/src/core/execute.h b/src/core/execute.h
index a2cf22806b..24cd4640d7 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -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;
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index b315dd0afa..2850da5cc1 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -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)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 734a5941cc..83e88971be 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -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;
+}
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index c57a6b2277..74b3633695 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -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);