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