You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
642 lines
26 KiB
642 lines
26 KiB
8 months ago
|
From a26f2b2732733aa361fec0a3a8f0ba377f48e75c Mon Sep 17 00:00:00 2001
|
||
|
From: Anita Zhang <anitzhang@gmail.com>
|
||
|
Date: Sun, 7 Oct 2018 20:28:36 -0700
|
||
|
Subject: [PATCH] core: implement per unit journal rate limiting
|
||
|
|
||
|
Add LogRateLimitIntervalSec= and LogRateLimitBurst= options for
|
||
|
services. If provided, these values get passed to the journald
|
||
|
client context, and those values are used in the rate limiting
|
||
|
function in the journal over the the journald.conf values.
|
||
|
|
||
|
Part of #10230
|
||
|
|
||
|
(cherry picked from commit 90fc172e191f44979005a524521112f2bd1ff21b)
|
||
|
|
||
|
Resolves: #1719577
|
||
|
---
|
||
|
catalog/systemd.catalog.in | 3 +-
|
||
|
doc/TRANSIENT-SETTINGS.md | 2 +
|
||
|
man/journald.conf.xml | 8 +-
|
||
|
man/systemd.exec.xml | 16 ++++
|
||
|
src/core/dbus-execute.c | 8 ++
|
||
|
src/core/execute.c | 14 ++++
|
||
|
src/core/execute.h | 3 +
|
||
|
src/core/load-fragment-gperf.gperf.m4 | 2 +
|
||
|
src/core/unit.c | 92 +++++++++++++++++++++
|
||
|
src/core/unit.h | 2 +
|
||
|
src/journal/journald-context.c | 50 ++++++++++-
|
||
|
src/journal/journald-context.h | 3 +
|
||
|
src/journal/journald-rate-limit.c | 38 ++++-----
|
||
|
src/journal/journald-rate-limit.h | 4 +-
|
||
|
src/journal/journald-server.c | 4 +-
|
||
|
src/shared/bus-unit-util.c | 8 ++
|
||
|
test/fuzz/fuzz-unit-file/directives.service | 2 +
|
||
|
17 files changed, 231 insertions(+), 28 deletions(-)
|
||
|
|
||
|
diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in
|
||
|
index f1bddc6f7d..8234e387cf 100644
|
||
|
--- a/catalog/systemd.catalog.in
|
||
|
+++ b/catalog/systemd.catalog.in
|
||
|
@@ -52,7 +52,8 @@ dropped, other services' messages are unaffected.
|
||
|
|
||
|
The limits controlling when messages are dropped may be configured
|
||
|
with RateLimitIntervalSec= and RateLimitBurst= in
|
||
|
-/etc/systemd/journald.conf. See journald.conf(5) for details.
|
||
|
+/etc/systemd/journald.conf or LogRateLimitIntervalSec= and LogRateLimitBurst=
|
||
|
+in the unit file. See journald.conf(5) and systemd.exec(5) for details.
|
||
|
|
||
|
-- e9bf28e6e834481bb6f48f548ad13606
|
||
|
Subject: Journal messages have been missed
|
||
|
diff --git a/doc/TRANSIENT-SETTINGS.md b/doc/TRANSIENT-SETTINGS.md
|
||
|
index ca9e8387b7..0ea444b133 100644
|
||
|
--- a/doc/TRANSIENT-SETTINGS.md
|
||
|
+++ b/doc/TRANSIENT-SETTINGS.md
|
||
|
@@ -135,6 +135,8 @@ All execution-related settings are available for transient units.
|
||
|
✓ SyslogLevelPrefix=
|
||
|
✓ LogLevelMax=
|
||
|
✓ LogExtraFields=
|
||
|
+✓ LogRateLimitIntervalSec=
|
||
|
+✓ LogRateLimitBurst=
|
||
|
✓ SecureBits=
|
||
|
✓ CapabilityBoundingSet=
|
||
|
✓ AmbientCapabilities=
|
||
|
diff --git a/man/journald.conf.xml b/man/journald.conf.xml
|
||
|
index ee8e8b7faf..b57a244b22 100644
|
||
|
--- a/man/journald.conf.xml
|
||
|
+++ b/man/journald.conf.xml
|
||
|
@@ -140,7 +140,13 @@
|
||
|
following units: <literal>s</literal>, <literal>min</literal>,
|
||
|
<literal>h</literal>, <literal>ms</literal>,
|
||
|
<literal>us</literal>. To turn off any kind of rate limiting,
|
||
|
- set either value to 0.</para></listitem>
|
||
|
+ set either value to 0.</para>
|
||
|
+
|
||
|
+ <para>If a service provides rate limits for itself through
|
||
|
+ <varname>LogRateLimitIntervalSec=</varname> and/or <varname>LogRateLimitBurst=</varname>
|
||
|
+ in <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||
|
+ those values will override the settings specified here.</para>
|
||
|
+ </listitem>
|
||
|
</varlistentry>
|
||
|
|
||
|
<varlistentry>
|
||
|
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
|
||
|
index 3bd790b485..737c52bcc4 100644
|
||
|
--- a/man/systemd.exec.xml
|
||
|
+++ b/man/systemd.exec.xml
|
||
|
@@ -1905,6 +1905,22 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
|
||
|
matching. Assign an empty string to reset the list.</para></listitem>
|
||
|
</varlistentry>
|
||
|
|
||
|
+ <varlistentry>
|
||
|
+ <term><varname>LogRateLimitIntervalSec=</varname></term>
|
||
|
+ <term><varname>LogRateLimitBurst=</varname></term>
|
||
|
+
|
||
|
+ <listitem><para>Configures the rate limiting that is applied to messages generated by this unit. If, in the
|
||
|
+ time interval defined by <varname>LogRateLimitIntervalSec=</varname>, more messages than specified in
|
||
|
+ <varname>LogRateLimitBurst=</varname> are logged by a service, all further messages within the interval are
|
||
|
+ dropped until the interval is over. A message about the number of dropped messages is generated. The time
|
||
|
+ specification for <varname>LogRateLimitIntervalSec=</varname> may be specified in the following units: "s",
|
||
|
+ "min", "h", "ms", "us" (see
|
||
|
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry> for details).
|
||
|
+ The default settings are set by <varname>RateLimitIntervalSec=</varname> and <varname>RateLimitBurst=</varname>
|
||
|
+ configured in <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||
|
+ </para></listitem>
|
||
|
+ </varlistentry>
|
||
|
+
|
||
|
<varlistentry>
|
||
|
<term><varname>SyslogIdentifier=</varname></term>
|
||
|
|
||
|
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
|
||
|
index c44970c10c..33a91c012e 100644
|
||
|
--- a/src/core/dbus-execute.c
|
||
|
+++ b/src/core/dbus-execute.c
|
||
|
@@ -718,6 +718,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
|
||
|
SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST),
|
||
|
SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST),
|
||
|
SD_BUS_PROPERTY("LogLevelMax", "i", bus_property_get_int, offsetof(ExecContext, log_level_max), SD_BUS_VTABLE_PROPERTY_CONST),
|
||
|
+ SD_BUS_PROPERTY("LogRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(ExecContext, log_rate_limit_interval_usec), SD_BUS_VTABLE_PROPERTY_CONST),
|
||
|
+ SD_BUS_PROPERTY("LogRateLimitBurst", "u", bus_property_get_unsigned, offsetof(ExecContext, log_rate_limit_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("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),
|
||
|
@@ -1073,6 +1075,12 @@ int bus_exec_context_set_transient_property(
|
||
|
if (streq(name, "CPUSchedulingPriority"))
|
||
|
return bus_set_transient_sched_priority(u, name, &c->cpu_sched_priority, message, flags, error);
|
||
|
|
||
|
+ if (streq(name, "LogRateLimitIntervalUSec"))
|
||
|
+ return bus_set_transient_usec(u, name, &c->log_rate_limit_interval_usec, message, flags, error);
|
||
|
+
|
||
|
+ if (streq(name, "LogRateLimitBurst"))
|
||
|
+ return bus_set_transient_unsigned(u, name, &c->log_rate_limit_burst, message, flags, error);
|
||
|
+
|
||
|
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 c62f3cf849..8293c522bc 100644
|
||
|
--- a/src/core/execute.c
|
||
|
+++ b/src/core/execute.c
|
||
|
@@ -3693,6 +3693,9 @@ void exec_context_done(ExecContext *c) {
|
||
|
|
||
|
exec_context_free_log_extra_fields(c);
|
||
|
|
||
|
+ c->log_rate_limit_interval_usec = 0;
|
||
|
+ c->log_rate_limit_burst = 0;
|
||
|
+
|
||
|
c->stdin_data = mfree(c->stdin_data);
|
||
|
c->stdin_data_size = 0;
|
||
|
}
|
||
|
@@ -4153,6 +4156,17 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||
|
fprintf(f, "%sLogLevelMax: %s\n", prefix, strna(t));
|
||
|
}
|
||
|
|
||
|
+ if (c->log_rate_limit_interval_usec > 0) {
|
||
|
+ char buf_timespan[FORMAT_TIMESPAN_MAX];
|
||
|
+
|
||
|
+ fprintf(f,
|
||
|
+ "%sLogRateLimitIntervalSec: %s\n",
|
||
|
+ prefix, format_timespan(buf_timespan, sizeof(buf_timespan), c->log_rate_limit_interval_usec, USEC_PER_SEC));
|
||
|
+ }
|
||
|
+
|
||
|
+ if (c->log_rate_limit_burst > 0)
|
||
|
+ fprintf(f, "%sLogRateLimitBurst: %u\n", prefix, c->log_rate_limit_burst);
|
||
|
+
|
||
|
if (c->n_log_extra_fields > 0) {
|
||
|
size_t j;
|
||
|
|
||
|
diff --git a/src/core/execute.h b/src/core/execute.h
|
||
|
index bff1634b88..8c91636adc 100644
|
||
|
--- a/src/core/execute.h
|
||
|
+++ b/src/core/execute.h
|
||
|
@@ -216,6 +216,9 @@ struct ExecContext {
|
||
|
struct iovec* log_extra_fields;
|
||
|
size_t n_log_extra_fields;
|
||
|
|
||
|
+ usec_t log_rate_limit_interval_usec;
|
||
|
+ unsigned log_rate_limit_burst;
|
||
|
+
|
||
|
bool cpu_sched_reset_on_fork;
|
||
|
bool non_blocking;
|
||
|
bool private_tmp;
|
||
|
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
|
||
|
index 15fb47838c..1066bcfb8f 100644
|
||
|
--- a/src/core/load-fragment-gperf.gperf.m4
|
||
|
+++ b/src/core/load-fragment-gperf.gperf.m4
|
||
|
@@ -57,6 +57,8 @@ $1.SyslogFacility, config_parse_log_facility, 0,
|
||
|
$1.SyslogLevel, config_parse_log_level, 0, offsetof($1, exec_context.syslog_priority)
|
||
|
$1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix)
|
||
|
$1.LogLevelMax, config_parse_log_level, 0, offsetof($1, exec_context.log_level_max)
|
||
|
+$1.LogRateLimitIntervalSec, config_parse_sec, 0, offsetof($1, exec_context.log_rate_limit_interval_usec)
|
||
|
+$1.LogRateLimitBurst, config_parse_unsigned, 0, offsetof($1, exec_context.log_rate_limit_burst)
|
||
|
$1.LogExtraFields, config_parse_log_extra_fields, 0, offsetof($1, exec_context)
|
||
|
$1.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof($1, exec_context)
|
||
|
$1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context.secure_bits)
|
||
|
diff --git a/src/core/unit.c b/src/core/unit.c
|
||
|
index b0b1c77ef7..115739f4c6 100644
|
||
|
--- a/src/core/unit.c
|
||
|
+++ b/src/core/unit.c
|
||
|
@@ -3245,6 +3245,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
|
||
|
unit_serialize_item(u, f, "exported-invocation-id", yes_no(u->exported_invocation_id));
|
||
|
unit_serialize_item(u, f, "exported-log-level-max", yes_no(u->exported_log_level_max));
|
||
|
unit_serialize_item(u, f, "exported-log-extra-fields", yes_no(u->exported_log_extra_fields));
|
||
|
+ unit_serialize_item(u, f, "exported-log-rate-limit-interval", yes_no(u->exported_log_rate_limit_interval));
|
||
|
+ unit_serialize_item(u, f, "exported-log-rate-limit-burst", yes_no(u->exported_log_rate_limit_burst));
|
||
|
|
||
|
unit_serialize_item_format(u, f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
|
||
|
if (u->cpu_usage_last != NSEC_INFINITY)
|
||
|
@@ -3508,6 +3510,26 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
|
||
|
|
||
|
continue;
|
||
|
|
||
|
+ } else if (streq(l, "exported-log-rate-limit-interval")) {
|
||
|
+
|
||
|
+ r = parse_boolean(v);
|
||
|
+ if (r < 0)
|
||
|
+ log_unit_debug(u, "Failed to parse exported log rate limit interval %s, ignoring.", v);
|
||
|
+ else
|
||
|
+ u->exported_log_rate_limit_interval = r;
|
||
|
+
|
||
|
+ continue;
|
||
|
+
|
||
|
+ } else if (streq(l, "exported-log-rate-limit-burst")) {
|
||
|
+
|
||
|
+ r = parse_boolean(v);
|
||
|
+ if (r < 0)
|
||
|
+ log_unit_debug(u, "Failed to parse exported log rate limit burst %s, ignoring.", v);
|
||
|
+ else
|
||
|
+ u->exported_log_rate_limit_burst = r;
|
||
|
+
|
||
|
+ continue;
|
||
|
+
|
||
|
} else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) {
|
||
|
|
||
|
r = safe_atou64(v, &u->cpu_usage_base);
|
||
|
@@ -5241,6 +5263,60 @@ fail:
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
+static int unit_export_log_rate_limit_interval(Unit *u, const ExecContext *c) {
|
||
|
+ _cleanup_free_ char *buf = NULL;
|
||
|
+ const char *p;
|
||
|
+ int r;
|
||
|
+
|
||
|
+ assert(u);
|
||
|
+ assert(c);
|
||
|
+
|
||
|
+ if (u->exported_log_rate_limit_interval)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ if (c->log_rate_limit_interval_usec == 0)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ p = strjoina("/run/systemd/units/log-rate-limit-interval:", u->id);
|
||
|
+
|
||
|
+ if (asprintf(&buf, "%" PRIu64, c->log_rate_limit_interval_usec) < 0)
|
||
|
+ return log_oom();
|
||
|
+
|
||
|
+ r = symlink_atomic(buf, p);
|
||
|
+ if (r < 0)
|
||
|
+ return log_unit_debug_errno(u, r, "Failed to create log rate limit interval symlink %s: %m", p);
|
||
|
+
|
||
|
+ u->exported_log_rate_limit_interval = true;
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int unit_export_log_rate_limit_burst(Unit *u, const ExecContext *c) {
|
||
|
+ _cleanup_free_ char *buf = NULL;
|
||
|
+ const char *p;
|
||
|
+ int r;
|
||
|
+
|
||
|
+ assert(u);
|
||
|
+ assert(c);
|
||
|
+
|
||
|
+ if (u->exported_log_rate_limit_burst)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ if (c->log_rate_limit_burst == 0)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ p = strjoina("/run/systemd/units/log-rate-limit-burst:", u->id);
|
||
|
+
|
||
|
+ if (asprintf(&buf, "%u", c->log_rate_limit_burst) < 0)
|
||
|
+ return log_oom();
|
||
|
+
|
||
|
+ r = symlink_atomic(buf, p);
|
||
|
+ if (r < 0)
|
||
|
+ return log_unit_debug_errno(u, r, "Failed to create log rate limit burst symlink %s: %m", p);
|
||
|
+
|
||
|
+ u->exported_log_rate_limit_burst = true;
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
void unit_export_state_files(Unit *u) {
|
||
|
const ExecContext *c;
|
||
|
|
||
|
@@ -5274,6 +5350,8 @@ void unit_export_state_files(Unit *u) {
|
||
|
if (c) {
|
||
|
(void) unit_export_log_level_max(u, c);
|
||
|
(void) unit_export_log_extra_fields(u, c);
|
||
|
+ (void) unit_export_log_rate_limit_interval(u, c);
|
||
|
+ (void) unit_export_log_rate_limit_burst(u, c);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@@ -5310,6 +5388,20 @@ void unit_unlink_state_files(Unit *u) {
|
||
|
|
||
|
u->exported_log_extra_fields = false;
|
||
|
}
|
||
|
+
|
||
|
+ if (u->exported_log_rate_limit_interval) {
|
||
|
+ p = strjoina("/run/systemd/units/log-rate-limit-interval:", u->id);
|
||
|
+ (void) unlink(p);
|
||
|
+
|
||
|
+ u->exported_log_rate_limit_interval = false;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (u->exported_log_rate_limit_burst) {
|
||
|
+ p = strjoina("/run/systemd/units/log-rate-limit-burst:", u->id);
|
||
|
+ (void) unlink(p);
|
||
|
+
|
||
|
+ u->exported_log_rate_limit_burst = false;
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
int unit_prepare_exec(Unit *u) {
|
||
|
diff --git a/src/core/unit.h b/src/core/unit.h
|
||
|
index 68cc1869e4..99755823eb 100644
|
||
|
--- a/src/core/unit.h
|
||
|
+++ b/src/core/unit.h
|
||
|
@@ -349,6 +349,8 @@ typedef struct Unit {
|
||
|
bool exported_invocation_id:1;
|
||
|
bool exported_log_level_max:1;
|
||
|
bool exported_log_extra_fields:1;
|
||
|
+ bool exported_log_rate_limit_interval:1;
|
||
|
+ bool exported_log_rate_limit_burst:1;
|
||
|
|
||
|
/* When writing transient unit files, stores which section we stored last. If < 0, we didn't write any yet. If
|
||
|
* == 0 we are in the [Unit] section, if > 0 we are in the unit type-specific section. */
|
||
|
diff --git a/src/journal/journald-context.c b/src/journal/journald-context.c
|
||
|
index dba3525ed8..c8e97e16de 100644
|
||
|
--- a/src/journal/journald-context.c
|
||
|
+++ b/src/journal/journald-context.c
|
||
|
@@ -140,6 +140,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
|
||
|
c->timestamp = USEC_INFINITY;
|
||
|
c->extra_fields_mtime = NSEC_INFINITY;
|
||
|
c->log_level_max = -1;
|
||
|
+ c->log_rate_limit_interval = s->rate_limit_interval;
|
||
|
+ c->log_rate_limit_burst = s->rate_limit_burst;
|
||
|
|
||
|
r = hashmap_put(s->client_contexts, PID_TO_PTR(pid), c);
|
||
|
if (r < 0) {
|
||
|
@@ -151,7 +153,8 @@ static int client_context_new(Server *s, pid_t pid, ClientContext **ret) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-static void client_context_reset(ClientContext *c) {
|
||
|
+static void client_context_reset(Server *s, ClientContext *c) {
|
||
|
+ assert(s);
|
||
|
assert(c);
|
||
|
|
||
|
c->timestamp = USEC_INFINITY;
|
||
|
@@ -186,6 +189,9 @@ static void client_context_reset(ClientContext *c) {
|
||
|
c->extra_fields_mtime = NSEC_INFINITY;
|
||
|
|
||
|
c->log_level_max = -1;
|
||
|
+
|
||
|
+ c->log_rate_limit_interval = s->rate_limit_interval;
|
||
|
+ c->log_rate_limit_burst = s->rate_limit_burst;
|
||
|
}
|
||
|
|
||
|
static ClientContext* client_context_free(Server *s, ClientContext *c) {
|
||
|
@@ -199,7 +205,7 @@ static ClientContext* client_context_free(Server *s, ClientContext *c) {
|
||
|
if (c->in_lru)
|
||
|
assert_se(prioq_remove(s->client_contexts_lru, c, &c->lru_index) >= 0);
|
||
|
|
||
|
- client_context_reset(c);
|
||
|
+ client_context_reset(s, c);
|
||
|
|
||
|
return mfree(c);
|
||
|
}
|
||
|
@@ -464,6 +470,42 @@ static int client_context_read_extra_fields(
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+static int client_context_read_log_rate_limit_interval(ClientContext *c) {
|
||
|
+ _cleanup_free_ char *value = NULL;
|
||
|
+ const char *p;
|
||
|
+ int r;
|
||
|
+
|
||
|
+ assert(c);
|
||
|
+
|
||
|
+ if (!c->unit)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ p = strjoina("/run/systemd/units/log-rate-limit-interval:", c->unit);
|
||
|
+ r = readlink_malloc(p, &value);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ return safe_atou64(value, &c->log_rate_limit_interval);
|
||
|
+}
|
||
|
+
|
||
|
+static int client_context_read_log_rate_limit_burst(ClientContext *c) {
|
||
|
+ _cleanup_free_ char *value = NULL;
|
||
|
+ const char *p;
|
||
|
+ int r;
|
||
|
+
|
||
|
+ assert(c);
|
||
|
+
|
||
|
+ if (!c->unit)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ p = strjoina("/run/systemd/units/log-rate-limit-burst:", c->unit);
|
||
|
+ r = readlink_malloc(p, &value);
|
||
|
+ if (r < 0)
|
||
|
+ return r;
|
||
|
+
|
||
|
+ return safe_atou(value, &c->log_rate_limit_burst);
|
||
|
+}
|
||
|
+
|
||
|
static void client_context_really_refresh(
|
||
|
Server *s,
|
||
|
ClientContext *c,
|
||
|
@@ -490,6 +532,8 @@ static void client_context_really_refresh(
|
||
|
(void) client_context_read_invocation_id(s, c);
|
||
|
(void) client_context_read_log_level_max(s, c);
|
||
|
(void) client_context_read_extra_fields(s, c);
|
||
|
+ (void) client_context_read_log_rate_limit_interval(c);
|
||
|
+ (void) client_context_read_log_rate_limit_burst(c);
|
||
|
|
||
|
c->timestamp = timestamp;
|
||
|
|
||
|
@@ -520,7 +564,7 @@ void client_context_maybe_refresh(
|
||
|
/* If the data isn't pinned and if the cashed data is older than the upper limit, we flush it out
|
||
|
* entirely. This follows the logic that as long as an entry is pinned the PID reuse is unlikely. */
|
||
|
if (c->n_ref == 0 && c->timestamp + MAX_USEC < timestamp) {
|
||
|
- client_context_reset(c);
|
||
|
+ client_context_reset(s, c);
|
||
|
goto refresh;
|
||
|
}
|
||
|
|
||
|
diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h
|
||
|
index 9df3a38eff..5e19c71f14 100644
|
||
|
--- a/src/journal/journald-context.h
|
||
|
+++ b/src/journal/journald-context.h
|
||
|
@@ -49,6 +49,9 @@ struct ClientContext {
|
||
|
size_t extra_fields_n_iovec;
|
||
|
void *extra_fields_data;
|
||
|
nsec_t extra_fields_mtime;
|
||
|
+
|
||
|
+ usec_t log_rate_limit_interval;
|
||
|
+ unsigned log_rate_limit_burst;
|
||
|
};
|
||
|
|
||
|
int client_context_get(
|
||
|
diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c
|
||
|
index 6a8a36a736..539efb8669 100644
|
||
|
--- a/src/journal/journald-rate-limit.c
|
||
|
+++ b/src/journal/journald-rate-limit.c
|
||
|
@@ -39,6 +39,10 @@ struct JournalRateLimitGroup {
|
||
|
JournalRateLimit *parent;
|
||
|
|
||
|
char *id;
|
||
|
+
|
||
|
+ /* Interval is stored to keep track of when the group expires */
|
||
|
+ usec_t interval;
|
||
|
+
|
||
|
JournalRateLimitPool pools[POOLS_MAX];
|
||
|
uint64_t hash;
|
||
|
|
||
|
@@ -47,8 +51,6 @@ struct JournalRateLimitGroup {
|
||
|
};
|
||
|
|
||
|
struct JournalRateLimit {
|
||
|
- usec_t interval;
|
||
|
- unsigned burst;
|
||
|
|
||
|
JournalRateLimitGroup* buckets[BUCKETS_MAX];
|
||
|
JournalRateLimitGroup *lru, *lru_tail;
|
||
|
@@ -58,18 +60,13 @@ struct JournalRateLimit {
|
||
|
uint8_t hash_key[16];
|
||
|
};
|
||
|
|
||
|
-JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst) {
|
||
|
+JournalRateLimit *journal_rate_limit_new(void) {
|
||
|
JournalRateLimit *r;
|
||
|
|
||
|
- assert(interval > 0 || burst == 0);
|
||
|
-
|
||
|
r = new0(JournalRateLimit, 1);
|
||
|
if (!r)
|
||
|
return NULL;
|
||
|
|
||
|
- r->interval = interval;
|
||
|
- r->burst = burst;
|
||
|
-
|
||
|
random_bytes(r->hash_key, sizeof(r->hash_key));
|
||
|
|
||
|
return r;
|
||
|
@@ -109,7 +106,7 @@ _pure_ static bool journal_rate_limit_group_expired(JournalRateLimitGroup *g, us
|
||
|
assert(g);
|
||
|
|
||
|
for (i = 0; i < POOLS_MAX; i++)
|
||
|
- if (g->pools[i].begin + g->parent->interval >= ts)
|
||
|
+ if (g->pools[i].begin + g->interval >= ts)
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
@@ -126,7 +123,7 @@ static void journal_rate_limit_vacuum(JournalRateLimit *r, usec_t ts) {
|
||
|
journal_rate_limit_group_free(r->lru_tail);
|
||
|
}
|
||
|
|
||
|
-static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t ts) {
|
||
|
+static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, const char *id, usec_t interval, usec_t ts) {
|
||
|
JournalRateLimitGroup *g;
|
||
|
struct siphash state;
|
||
|
|
||
|
@@ -145,6 +142,8 @@ static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r,
|
||
|
string_hash_func(g->id, &state);
|
||
|
g->hash = siphash24_finalize(&state);
|
||
|
|
||
|
+ g->interval = interval;
|
||
|
+
|
||
|
journal_rate_limit_vacuum(r, ts);
|
||
|
|
||
|
LIST_PREPEND(bucket, r->buckets[g->hash % BUCKETS_MAX], g);
|
||
|
@@ -189,7 +188,7 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) {
|
||
|
return burst;
|
||
|
}
|
||
|
|
||
|
-int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available) {
|
||
|
+int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available) {
|
||
|
uint64_t h;
|
||
|
JournalRateLimitGroup *g;
|
||
|
JournalRateLimitPool *p;
|
||
|
@@ -209,11 +208,6 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
|
||
|
if (!r)
|
||
|
return 1;
|
||
|
|
||
|
- if (r->interval == 0 || r->burst == 0)
|
||
|
- return 1;
|
||
|
-
|
||
|
- burst = burst_modulate(r->burst, available);
|
||
|
-
|
||
|
ts = now(CLOCK_MONOTONIC);
|
||
|
|
||
|
siphash24_init(&state, r->hash_key);
|
||
|
@@ -226,10 +220,16 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
|
||
|
break;
|
||
|
|
||
|
if (!g) {
|
||
|
- g = journal_rate_limit_group_new(r, id, ts);
|
||
|
+ g = journal_rate_limit_group_new(r, id, rl_interval, ts);
|
||
|
if (!g)
|
||
|
return -ENOMEM;
|
||
|
- }
|
||
|
+ } else
|
||
|
+ g->interval = rl_interval;
|
||
|
+
|
||
|
+ if (rl_interval == 0 || rl_burst == 0)
|
||
|
+ return 1;
|
||
|
+
|
||
|
+ burst = burst_modulate(rl_burst, available);
|
||
|
|
||
|
p = &g->pools[priority_map[priority]];
|
||
|
|
||
|
@@ -240,7 +240,7 @@ int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, u
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
- if (p->begin + r->interval < ts) {
|
||
|
+ if (p->begin + rl_interval < ts) {
|
||
|
unsigned s;
|
||
|
|
||
|
s = p->suppressed;
|
||
|
diff --git a/src/journal/journald-rate-limit.h b/src/journal/journald-rate-limit.h
|
||
|
index 3a7f106de0..a2992800fe 100644
|
||
|
--- a/src/journal/journald-rate-limit.h
|
||
|
+++ b/src/journal/journald-rate-limit.h
|
||
|
@@ -5,6 +5,6 @@
|
||
|
|
||
|
typedef struct JournalRateLimit JournalRateLimit;
|
||
|
|
||
|
-JournalRateLimit *journal_rate_limit_new(usec_t interval, unsigned burst);
|
||
|
+JournalRateLimit *journal_rate_limit_new(void);
|
||
|
void journal_rate_limit_free(JournalRateLimit *r);
|
||
|
-int journal_rate_limit_test(JournalRateLimit *r, const char *id, int priority, uint64_t available);
|
||
|
+int journal_rate_limit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available);
|
||
|
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
|
||
|
index 8de45552f6..0c983e102a 100644
|
||
|
--- a/src/journal/journald-server.c
|
||
|
+++ b/src/journal/journald-server.c
|
||
|
@@ -943,7 +943,7 @@ void server_dispatch_message(
|
||
|
if (c && c->unit) {
|
||
|
(void) determine_space(s, &available, NULL);
|
||
|
|
||
|
- rl = journal_rate_limit_test(s->rate_limit, c->unit, priority & LOG_PRIMASK, available);
|
||
|
+ rl = journal_rate_limit_test(s->rate_limit, c->unit, c->log_rate_limit_interval, c->log_rate_limit_burst, priority & LOG_PRIMASK, available);
|
||
|
if (rl == 0)
|
||
|
return;
|
||
|
|
||
|
@@ -1852,7 +1852,7 @@ int server_init(Server *s) {
|
||
|
if (!s->udev)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
- s->rate_limit = journal_rate_limit_new(s->rate_limit_interval, s->rate_limit_burst);
|
||
|
+ s->rate_limit = journal_rate_limit_new();
|
||
|
if (!s->rate_limit)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
|
||
|
index 3238b442c0..271cc054da 100644
|
||
|
--- a/src/shared/bus-unit-util.c
|
||
|
+++ b/src/shared/bus-unit-util.c
|
||
|
@@ -755,6 +755,14 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
|
||
|
|
||
|
return bus_append_parse_nsec(m, field, eq);
|
||
|
|
||
|
+ if (STR_IN_SET(field, "LogRateLimitIntervalSec"))
|
||
|
+
|
||
|
+ return bus_append_parse_sec_rename(m, field, eq);
|
||
|
+
|
||
|
+ if (streq(field, "LogRateLimitBurst"))
|
||
|
+
|
||
|
+ return bus_append_safe_atou(m, field, eq);
|
||
|
+
|
||
|
if (streq(field, "MountFlags"))
|
||
|
|
||
|
return bus_append_mount_propagation_flags_from_string(m, field, eq);
|
||
|
diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service
|
||
|
index c2334d3b19..d8d1fc68b8 100644
|
||
|
--- a/test/fuzz/fuzz-unit-file/directives.service
|
||
|
+++ b/test/fuzz/fuzz-unit-file/directives.service
|
||
|
@@ -792,6 +792,8 @@ LineMax=
|
||
|
LockPersonality=
|
||
|
LogExtraFields=
|
||
|
LogLevelMax=
|
||
|
+LogRateLimitIntervalSec=
|
||
|
+LogRateLimitBurst=
|
||
|
LogsDirectory=
|
||
|
LogsDirectoryMode=
|
||
|
MACVLAN=
|