Blame SOURCES/0356-core-accept-time-units-for-time-based-resource-limit.patch

17b0f1
From 128ef85392f68fa32650deab12d6cd2e01ad52cf Mon Sep 17 00:00:00 2001
17b0f1
From: Lennart Poettering <lennart@poettering.net>
17b0f1
Date: Tue, 10 Nov 2015 16:52:52 +0100
17b0f1
Subject: [PATCH] core: accept time units for time-based resource limits
17b0f1
17b0f1
Let's make sure "LimitCPU=30min" can be parsed properly, following the
17b0f1
usual logic how we parse time values. Similar for LimitRTTIME=.
17b0f1
17b0f1
While we are at it, extend a bit on the man page section about resource
17b0f1
limits.
17b0f1
17b0f1
Fixes: #1772
17b0f1
17b0f1
Cherry-picked from: a4c1800284e3546bbfab2dc19eb59bcb91c4a2ca
17b0f1
Related: #1351415
17b0f1
---
17b0f1
 man/systemd.exec.xml                  |  86 ++++++++++++++++------
17b0f1
 src/core/load-fragment-gperf.gperf.m4 |   4 +-
17b0f1
 src/core/load-fragment.c              | 101 ++++++++++++++++++++++++++
17b0f1
 src/core/load-fragment.h              |   2 +
17b0f1
 src/test/test-unit-file.c             |  61 ++++++++++++++++
17b0f1
 5 files changed, 231 insertions(+), 23 deletions(-)
17b0f1
17b0f1
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
17b0f1
index 25aea16553..cfdcc3d173 100644
17b0f1
--- a/man/systemd.exec.xml
17b0f1
+++ b/man/systemd.exec.xml
17b0f1
@@ -559,90 +559,133 @@
17b0f1
         of various resources for executed processes. See
17b0f1
         <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
17b0f1
         for details. Use the string <varname>infinity</varname> to
17b0f1
-        configure no limit on a specific resource. The multiplicative suffixes
17b0f1
-        K (=1024), M (=1024*1024) and so on for G, T, P and E may be used for
17b0f1
-        resource limits measured in bytes (e.g. LimitAS=16G).</para></listitem>
17b0f1
+        configure no limit on a specific resource. The multiplicative
17b0f1
+        suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E
17b0f1
+        may be used for resource limits measured in bytes
17b0f1
+        (e.g. LimitAS=16G). For the limits referring to time values,
17b0f1
+        the usual time units ms, s, min, h and so on may be used (see
17b0f1
+        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
17b0f1
+        for details). Note that if no time unit is specified for
17b0f1
+        <varname>LimitCPU=</varname> the default unit of seconds is
17b0f1
+        implied, while for <varname>LimitRTTIME=</varname> the default
17b0f1
+        unit of microseconds is implied. Also, note that the effective
17b0f1
+        granularity of the limits might influence their
17b0f1
+        enforcement. For example, time limits specified for
17b0f1
+        <varname>LimitCPU=</varname> will be rounded up implicitly to
17b0f1
+        multiples of 1s.</para>
17b0f1
+
17b0f1
+        <para>Note that most process resource limits configured with
17b0f1
+        these options are per-process, and processes may fork in order
17b0f1
+        to acquire a new set of resources that are accounted
17b0f1
+        independently of the original process, and may thus escape
17b0f1
+        limits set. Also note that <varname>LimitRSS=</varname> is not
17b0f1
+        implemented on Linux, and setting it has no effect. Often it
17b0f1
+        is advisable to prefer the resource controls listed in
17b0f1
+        <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
17b0f1
+        over these per-process limits, as they apply to services as a
17b0f1
+        whole, may be altered dynamically at runtime, and are
17b0f1
+        generally more expressive. For example,
17b0f1
+        <varname>MemoryLimit=</varname> is a more powerful (and
17b0f1
+        working) replacement for <varname>LimitRSS=</varname>.</para>
17b0f1
 
17b0f1
         
17b0f1
           <title>Limit directives and their equivalent with ulimit</title>
17b0f1
 
17b0f1
-          <tgroup cols='2'>
17b0f1
+          <tgroup cols='3'>
17b0f1
             <colspec colname='directive' />
17b0f1
             <colspec colname='equivalent' />
17b0f1
+            <colspec colname='unit' />
17b0f1
             
17b0f1
               <row>
17b0f1
                 <entry>Directive</entry>
17b0f1
                 <entry>ulimit equivalent</entry>
17b0f1
+                <entry>Unit</entry>
17b0f1
               </row>
17b0f1
             
17b0f1
             
17b0f1
               <row>
17b0f1
-                <entry>LimitCPU</entry>
17b0f1
+                <entry>LimitCPU=</entry>
17b0f1
                 <entry>ulimit -t</entry>
17b0f1
+                <entry>Seconds</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitFSIZE</entry>
17b0f1
+                <entry>LimitFSIZE=</entry>
17b0f1
                 <entry>ulimit -f</entry>
17b0f1
+                <entry>Bytes</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitDATA</entry>
17b0f1
+                <entry>LimitDATA=</entry>
17b0f1
                 <entry>ulimit -d</entry>
17b0f1
+                <entry>Bytes</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitSTACK</entry>
17b0f1
+                <entry>LimitSTACK=</entry>
17b0f1
                 <entry>ulimit -s</entry>
17b0f1
+                <entry>Bytes</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitCORE</entry>
17b0f1
+                <entry>LimitCORE=</entry>
17b0f1
                 <entry>ulimit -c</entry>
17b0f1
+                <entry>Bytes</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitRSS</entry>
17b0f1
+                <entry>LimitRSS=</entry>
17b0f1
                 <entry>ulimit -m</entry>
17b0f1
+                <entry>Bytes</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitNOFILE</entry>
17b0f1
+                <entry>LimitNOFILE=</entry>
17b0f1
                 <entry>ulimit -n</entry>
17b0f1
+                <entry>Number of File Descriptors</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitAS</entry>
17b0f1
+                <entry>LimitAS=</entry>
17b0f1
                 <entry>ulimit -v</entry>
17b0f1
+                <entry>Bytes</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitNPROC</entry>
17b0f1
+                <entry>LimitNPROC=</entry>
17b0f1
                 <entry>ulimit -u</entry>
17b0f1
+                <entry>Number of Processes</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitMEMLOCK</entry>
17b0f1
+                <entry>LimitMEMLOCK=</entry>
17b0f1
                 <entry>ulimit -l</entry>
17b0f1
+                <entry>Bytes</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitLOCKS</entry>
17b0f1
+                <entry>LimitLOCKS=</entry>
17b0f1
                 <entry>ulimit -x</entry>
17b0f1
+                <entry>Number of Locks</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitSIGPENDING</entry>
17b0f1
+                <entry>LimitSIGPENDING=</entry>
17b0f1
                 <entry>ulimit -i</entry>
17b0f1
+                <entry>Number of Queued Signals</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitMSGQUEUE</entry>
17b0f1
+                <entry>LimitMSGQUEUE=</entry>
17b0f1
                 <entry>ulimit -q</entry>
17b0f1
+                <entry>Bytes</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitNICE</entry>
17b0f1
+                <entry>LimitNICE=</entry>
17b0f1
                 <entry>ulimit -e</entry>
17b0f1
+                <entry>Nice Level</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitRTPRIO</entry>
17b0f1
+                <entry>LimitRTPRIO=</entry>
17b0f1
                 <entry>ulimit -r</entry>
17b0f1
+                <entry>Realtime Priority</entry>
17b0f1
               </row>
17b0f1
               <row>
17b0f1
-                <entry>LimitRTTIME</entry>
17b0f1
+                <entry>LimitRTTIME=</entry>
17b0f1
                 <entry>No equivalent</entry>
17b0f1
+                <entry>Microseconds</entry>
17b0f1
               </row>
17b0f1
             
17b0f1
           </tgroup>
17b0f1
-        
17b0f1
+        </listitem>
17b0f1
       </varlistentry>
17b0f1
 
17b0f1
       <varlistentry>
17b0f1
@@ -1266,6 +1309,7 @@
17b0f1
         <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
17b0f1
         <citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
17b0f1
         <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
17b0f1
+        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
17b0f1
         <citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
17b0f1
         <citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
17b0f1
         <citerefentry project='man-pages'><refentrytitle>exec</refentrytitle><manvolnum>3</manvolnum></citerefentry>
17b0f1
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
17b0f1
index c3461a0a69..ce1397c7e8 100644
17b0f1
--- a/src/core/load-fragment-gperf.gperf.m4
17b0f1
+++ b/src/core/load-fragment-gperf.gperf.m4
17b0f1
@@ -58,7 +58,7 @@ $1.RestrictAddressFamilies,      config_parse_address_families,      0,
17b0f1
 $1.SystemCallArchitectures,      config_parse_warn_compat,           DISABLED_CONFIGURATION,        0
17b0f1
 $1.SystemCallErrorNumber,        config_parse_warn_compat,           DISABLED_CONFIGURATION,        0
17b0f1
 $1.RestrictAddressFamilies,      config_parse_warn_compat,           DISABLED_CONFIGURATION,        0')
17b0f1
-$1.LimitCPU,                     config_parse_limit,                 RLIMIT_CPU,                    offsetof($1, exec_context.rlimit)
17b0f1
+$1.LimitCPU,                     config_parse_sec_limit,             RLIMIT_CPU,                    offsetof($1, exec_context.rlimit)
17b0f1
 $1.LimitFSIZE,                   config_parse_bytes_limit,           RLIMIT_FSIZE,                  offsetof($1, exec_context.rlimit)
17b0f1
 $1.LimitDATA,                    config_parse_bytes_limit,           RLIMIT_DATA,                   offsetof($1, exec_context.rlimit)
17b0f1
 $1.LimitSTACK,                   config_parse_bytes_limit,           RLIMIT_STACK,                  offsetof($1, exec_context.rlimit)
17b0f1
@@ -73,7 +73,7 @@ $1.LimitSIGPENDING,              config_parse_limit,                 RLIMIT_SIGP
17b0f1
 $1.LimitMSGQUEUE,                config_parse_bytes_limit,           RLIMIT_MSGQUEUE,               offsetof($1, exec_context.rlimit)
17b0f1
 $1.LimitNICE,                    config_parse_limit,                 RLIMIT_NICE,                   offsetof($1, exec_context.rlimit)
17b0f1
 $1.LimitRTPRIO,                  config_parse_limit,                 RLIMIT_RTPRIO,                 offsetof($1, exec_context.rlimit)
17b0f1
-$1.LimitRTTIME,                  config_parse_limit,                 RLIMIT_RTTIME,                 offsetof($1, exec_context.rlimit)
17b0f1
+$1.LimitRTTIME,                  config_parse_usec_limit,            RLIMIT_RTTIME,                 offsetof($1, exec_context.rlimit)
17b0f1
 $1.ReadWriteDirectories,         config_parse_namespace_path_strv,   0,                             offsetof($1, exec_context.read_write_dirs)
17b0f1
 $1.ReadOnlyDirectories,          config_parse_namespace_path_strv,   0,                             offsetof($1, exec_context.read_only_dirs)
17b0f1
 $1.InaccessibleDirectories,      config_parse_namespace_path_strv,   0,                             offsetof($1, exec_context.inaccessible_dirs)
17b0f1
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
17b0f1
index dbb45b20f3..8afe9d7e83 100644
17b0f1
--- a/src/core/load-fragment.c
17b0f1
+++ b/src/core/load-fragment.c
17b0f1
@@ -1162,6 +1162,107 @@ int config_parse_bytes_limit(const char *unit,
17b0f1
         return 0;
17b0f1
 }
17b0f1
 
17b0f1
+int config_parse_sec_limit(
17b0f1
+                const char *unit,
17b0f1
+                const char *filename,
17b0f1
+                unsigned line,
17b0f1
+                const char *section,
17b0f1
+                unsigned section_line,
17b0f1
+                const char *lvalue,
17b0f1
+                int ltype,
17b0f1
+                const char *rvalue,
17b0f1
+                void *data,
17b0f1
+                void *userdata) {
17b0f1
+
17b0f1
+        struct rlimit **rl = data;
17b0f1
+        rlim_t seconds;
17b0f1
+        int r;
17b0f1
+
17b0f1
+        assert(filename);
17b0f1
+        assert(lvalue);
17b0f1
+        assert(rvalue);
17b0f1
+        assert(data);
17b0f1
+
17b0f1
+        rl += ltype;
17b0f1
+
17b0f1
+        if (streq(rvalue, "infinity"))
17b0f1
+                seconds = RLIM_INFINITY;
17b0f1
+        else {
17b0f1
+                usec_t t;
17b0f1
+
17b0f1
+                r = parse_sec(rvalue, &t);
17b0f1
+                if (r < 0) {
17b0f1
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
17b0f1
+                        return 0;
17b0f1
+                }
17b0f1
+
17b0f1
+                if (t == USEC_INFINITY)
17b0f1
+                        seconds = RLIM_INFINITY;
17b0f1
+                else
17b0f1
+                        seconds = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));
17b0f1
+        }
17b0f1
+
17b0f1
+        if (!*rl) {
17b0f1
+                *rl = new(struct rlimit, 1);
17b0f1
+                if (!*rl)
17b0f1
+                        return log_oom();
17b0f1
+        }
17b0f1
+
17b0f1
+        (*rl)->rlim_cur = (*rl)->rlim_max = seconds;
17b0f1
+        return 0;
17b0f1
+}
17b0f1
+
17b0f1
+
17b0f1
+int config_parse_usec_limit(
17b0f1
+                const char *unit,
17b0f1
+                const char *filename,
17b0f1
+                unsigned line,
17b0f1
+                const char *section,
17b0f1
+                unsigned section_line,
17b0f1
+                const char *lvalue,
17b0f1
+                int ltype,
17b0f1
+                const char *rvalue,
17b0f1
+                void *data,
17b0f1
+                void *userdata) {
17b0f1
+
17b0f1
+        struct rlimit **rl = data;
17b0f1
+        rlim_t useconds;
17b0f1
+        int r;
17b0f1
+
17b0f1
+        assert(filename);
17b0f1
+        assert(lvalue);
17b0f1
+        assert(rvalue);
17b0f1
+        assert(data);
17b0f1
+
17b0f1
+        rl += ltype;
17b0f1
+
17b0f1
+        if (streq(rvalue, "infinity"))
17b0f1
+                useconds = RLIM_INFINITY;
17b0f1
+        else {
17b0f1
+                usec_t t;
17b0f1
+
17b0f1
+                r = parse_time(rvalue, &t, 1);
17b0f1
+                if (r < 0) {
17b0f1
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
17b0f1
+                        return 0;
17b0f1
+                }
17b0f1
+
17b0f1
+                if (t == USEC_INFINITY)
17b0f1
+                        useconds = RLIM_INFINITY;
17b0f1
+                else
17b0f1
+                        useconds = (rlim_t) t;
17b0f1
+        }
17b0f1
+
17b0f1
+        if (!*rl) {
17b0f1
+                *rl = new(struct rlimit, 1);
17b0f1
+                if (!*rl)
17b0f1
+                        return log_oom();
17b0f1
+        }
17b0f1
+
17b0f1
+        (*rl)->rlim_cur = (*rl)->rlim_max = useconds;
17b0f1
+        return 0;
17b0f1
+}
17b0f1
+
17b0f1
 #ifdef HAVE_SYSV_COMPAT
17b0f1
 int config_parse_sysv_priority(const char *unit,
17b0f1
                                const char *filename,
17b0f1
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
17b0f1
index 2d509d0cb4..359794d0ac 100644
17b0f1
--- a/src/core/load-fragment.h
17b0f1
+++ b/src/core/load-fragment.h
17b0f1
@@ -57,6 +57,8 @@ int config_parse_exec_secure_bits(const char *unit, const char *filename, unsign
17b0f1
 int config_parse_bounding_set(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);
17b0f1
 int config_parse_limit(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);
17b0f1
 int config_parse_bytes_limit(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);
17b0f1
+int config_parse_sec_limit(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);
17b0f1
+int config_parse_usec_limit(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);
17b0f1
 int config_parse_sysv_priority(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);
17b0f1
 int config_parse_kill_signal(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);
17b0f1
 int config_parse_exec_mount_flags(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);
17b0f1
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
17b0f1
index 5500983322..d151737960 100644
17b0f1
--- a/src/test/test-unit-file.c
17b0f1
+++ b/src/test/test-unit-file.c
17b0f1
@@ -545,6 +545,66 @@ static void test_install_printf(void) {
17b0f1
         expect(i4, "%U", "0");
17b0f1
 }
17b0f1
 
17b0f1
+
17b0f1
+static void test_config_parse_rlimit(void) {
17b0f1
+        struct rlimit * rl[_RLIMIT_MAX] = {};
17b0f1
+
17b0f1
+        assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55", rl, NULL) >= 0);
17b0f1
+        assert_se(rl[RLIMIT_NOFILE]);
17b0f1
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
17b0f1
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
17b0f1
+
17b0f1
+        assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity", rl, NULL) >= 0);
17b0f1
+        assert_se(rl[RLIMIT_NOFILE]);
17b0f1
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
17b0f1
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
17b0f1
+
17b0f1
+        rl[RLIMIT_NOFILE] = free(rl[RLIMIT_NOFILE]);
17b0f1
+        assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0);
17b0f1
+        assert_se(rl[RLIMIT_CPU]);
17b0f1
+        assert_se(rl[RLIMIT_CPU]->rlim_cur == 56);
17b0f1
+        assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
17b0f1
+
17b0f1
+        assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "57s", rl, NULL) >= 0);
17b0f1
+        assert_se(rl[RLIMIT_CPU]);
17b0f1
+        assert_se(rl[RLIMIT_CPU]->rlim_cur == 57);
17b0f1
+        assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
17b0f1
+
17b0f1
+        assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0);
17b0f1
+        assert_se(rl[RLIMIT_CPU]);
17b0f1
+        assert_se(rl[RLIMIT_CPU]->rlim_cur == RLIM_INFINITY);
17b0f1
+        assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
17b0f1
+
17b0f1
+        assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "1234ms", rl, NULL) >= 0);
17b0f1
+        assert_se(rl[RLIMIT_CPU]);
17b0f1
+        assert_se(rl[RLIMIT_CPU]->rlim_cur == 2);
17b0f1
+        assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
17b0f1
+
17b0f1
+        rl[RLIMIT_CPU] = free(rl[RLIMIT_CPU]);
17b0f1
+
17b0f1
+        assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58", rl, NULL) >= 0);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
17b0f1
+
17b0f1
+        assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
17b0f1
+
17b0f1
+        assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
17b0f1
+
17b0f1
+        assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 2345 * USEC_PER_MSEC);
17b0f1
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
17b0f1
+
17b0f1
+        rl[RLIMIT_RTTIME] = free(rl[RLIMIT_RTTIME]);
17b0f1
+}
17b0f1
+
17b0f1
 int main(int argc, char *argv[]) {
17b0f1
         int r;
17b0f1
 
17b0f1
@@ -553,6 +613,7 @@ int main(int argc, char *argv[]) {
17b0f1
 
17b0f1
         r = test_unit_file_get_set();
17b0f1
         test_config_parse_exec();
17b0f1
+        test_config_parse_rlimit();
17b0f1
         test_load_env_file_1();
17b0f1
         test_load_env_file_2();
17b0f1
         test_load_env_file_3();