]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: support <soft:hard> ranges for RLIMIT options 1994/head
authorKarel Zak <kzak@redhat.com>
Fri, 20 Nov 2015 11:54:10 +0000 (12:54 +0100)
committerKarel Zak <kzak@redhat.com>
Wed, 25 Nov 2015 11:03:32 +0000 (12:03 +0100)
The new parser supports:

 <value>       - specify both limits to the same value
 <soft:hard>   - specify both limits

the size or time specific suffixes are supported, for example

  LimitRTTIME=1sec
  LimitAS=4G:16G

The patch introduces parse_rlimit_range() and rlim type (size, sec,
usec, etc.) specific parsers. No code is duplicated now.

The patch also sync docs for DefaultLimitXXX= and LimitXXX=.

References: https://github.com/systemd/systemd/issues/1769

man/systemd-system.conf.xml
man/systemd.exec.xml
src/core/load-fragment.c
src/test/test-unit-file.c

index ead52951daace5a634872503f18e906479b8e13f..edc6df914aba2c1736a3c4f7594edd8cc0c6a949 100644 (file)
         <listitem><para>These settings control various default
         resource limits for units. See
         <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
-        for details. Use the string <varname>infinity</varname> to
-        configure no limit on a specific resource. The multiplicative suffixes
-        K (=1024), M (=1024*1024) and so on for G, T, P and E may be used for
-        resource limits measured in bytes (e.g. DefaultLimitAS=16G). These
-        settings may be overridden in individual units using the corresponding
-        LimitXXX= directives. Note that these resource limits are only
-        defaults for units, they are not applied to PID 1
+        for details. The resource limit is possible to specify in two formats,
+        <option>value</option> to set soft and hard limits to the same value,
+        or <option>soft:hard</option> to set both limits individually (e.g. DefaultLimitAS=4G:16G).
+        Use the string <varname>infinity</varname> to
+        configure no limit on a specific resource. The multiplicative
+        suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E
+        may be used for resource limits measured in bytes
+        (e.g. DefaultLimitAS=16G). For the limits referring to time values,
+        the usual time units ms, s, min, h and so on may be used (see
+        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+        for details). Note that if no time unit is specified for
+        <varname>DefaultLimitCPU=</varname> the default unit of seconds is
+        implied, while for <varname>DefaultLimitRTTIME=</varname> the default
+        unit of microseconds is implied. Also, note that the effective
+        granularity of the limits might influence their
+        enforcement. For example, time limits specified for
+        <varname>DefaultLimitCPU=</varname> will be rounded up implicitly to
+        multiples of 1s. These  settings may be overridden in individual units
+        using the corresponding LimitXXX= directives. Note that these resource
+        limits are only defaults for units, they are not applied to PID 1
         itself.</para></listitem>
       </varlistentry>
     </variablelist>
index ecc3a8973c051448640d82d23553139d859af848..5f98ef163c04cfc855ad433d36a998ba96e8d4b7 100644 (file)
         <listitem><para>These settings set both soft and hard limits
         of various resources for executed processes. See
         <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
-        for details. Use the string <varname>infinity</varname> to
+        for details. The resource limit is possible to specify in two formats,
+        <option>value</option> to set soft and hard limits to the same value,
+        or <option>soft:hard</option> to set both limits individually (e.g. LimitAS=4G:16G).
+        Use the string <varname>infinity</varname> to
         configure no limit on a specific resource. The multiplicative
         suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E
         may be used for resource limits measured in bytes
index d5cf476fda182d7d2c614c02bc2b0060070ab32c..3cb2cffedf3d3c692261b88cdecf79a8574e985c 100644 (file)
@@ -1089,46 +1089,107 @@ int config_parse_bounding_set(
         return 0;
 }
 
-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) {
-
-        struct rlimit **rl = data;
-        rlim_t v;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
+static int rlim_parse_u64(const char *val, rlim_t *res) {
+        int r = 0;
 
-        rl += ltype;
-
-        if (streq(rvalue, "infinity"))
-                v = RLIM_INFINITY;
+        if (streq(val, "infinity"))
+                *res = RLIM_INFINITY;
         else {
                 uint64_t u;
 
                 /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
                 assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
 
-                r = safe_atou64(rvalue, &u);
+                r = safe_atou64(val, &u);
                 if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
                         r = -ERANGE;
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
-                        return 0;
-                }
+                if (r == 0)
+                        *res = (rlim_t) u;
+        }
+        return r;
+}
+
+static int rlim_parse_size(const char *val, rlim_t *res) {
+        int r = 0;
+
+        if (streq(val, "infinity"))
+                *res = RLIM_INFINITY;
+        else {
+                uint64_t u;
+
+                r = parse_size(val, 1024, &u);
+                if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
+                        r = -ERANGE;
+                if (r == 0)
+                        *res = (rlim_t) u;
+        }
+        return r;
+}
+
+static int rlim_parse_sec(const char *val, rlim_t *res) {
+        int r = 0;
+
+        if (streq(val, "infinity"))
+                *res = RLIM_INFINITY;
+        else {
+                usec_t t;
+
+                r = parse_sec(val, &t);
+                if (r < 0)
+                        return r;
+                if (t == USEC_INFINITY)
+                        *res = RLIM_INFINITY;
+                else
+                        *res = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));
+
+        }
+        return r;
+}
+
+static int rlim_parse_usec(const char *val, rlim_t *res) {
+        int r = 0;
+
+        if (streq(val, "infinity"))
+                *res = RLIM_INFINITY;
+        else {
+                usec_t t;
+
+                r = parse_time(val, &t, 1);
+                if (r < 0)
+                        return r;
+                if (t == USEC_INFINITY)
+                        *res = RLIM_INFINITY;
+                else
+                        *res = (rlim_t) t;
+        }
+        return r;
+}
 
-                v = (rlim_t) u;
+static int parse_rlimit_range(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *value,
+                struct rlimit **rl,
+                int (*rlim_parser)(const char *, rlim_t *)) {
+
+        rlim_t soft, hard;
+        _cleanup_free_ char *sword = NULL, *hword = NULL;
+        int nwords, r;
+
+        assert(value);
+
+        /* <value> or <soft:hard> */
+        nwords = extract_many_words(&value, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &sword, &hword, NULL);
+        r = nwords < 0 ? nwords : nwords == 0 ? -EINVAL : 0;
+
+        if (r == 0)
+                r = rlim_parser(sword, &soft);
+        if (r == 0 && nwords == 2)
+                r = rlim_parser(hword, &hard);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", value);
+                return 0;
         }
 
         if (!*rl) {
@@ -1136,12 +1197,12 @@ int config_parse_limit(
                 if (!*rl)
                         return log_oom();
         }
-
-        (*rl)->rlim_cur = (*rl)->rlim_max = v;
+        (*rl)->rlim_cur = soft;
+        (*rl)->rlim_max = nwords == 2 ? hard : soft;
         return 0;
 }
 
-int config_parse_bytes_limit(
+int config_parse_limit(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -1154,8 +1215,6 @@ int config_parse_bytes_limit(
                 void *userdata) {
 
         struct rlimit **rl = data;
-        rlim_t bytes;
-        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1163,31 +1222,30 @@ int config_parse_bytes_limit(
         assert(data);
 
         rl += ltype;
+        return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_u64);
+}
 
-        if (streq(rvalue, "infinity"))
-                bytes = RLIM_INFINITY;
-        else {
-                uint64_t u;
-
-                r = parse_size(rvalue, 1024, &u);
-                if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
-                        r = -ERANGE;
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
-                        return 0;
-                }
+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) {
 
-                bytes = (rlim_t) u;
-        }
+        struct rlimit **rl = data;
 
-        if (!*rl) {
-                *rl = new(struct rlimit, 1);
-                if (!*rl)
-                        return log_oom();
-        }
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
 
-        (*rl)->rlim_cur = (*rl)->rlim_max = bytes;
-        return 0;
+        rl += ltype;
+        return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_size);
 }
 
 int config_parse_sec_limit(
@@ -1203,8 +1261,6 @@ int config_parse_sec_limit(
                 void *userdata) {
 
         struct rlimit **rl = data;
-        rlim_t seconds;
-        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1212,35 +1268,9 @@ int config_parse_sec_limit(
         assert(data);
 
         rl += ltype;
-
-        if (streq(rvalue, "infinity"))
-                seconds = RLIM_INFINITY;
-        else {
-                usec_t t;
-
-                r = parse_sec(rvalue, &t);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
-                        return 0;
-                }
-
-                if (t == USEC_INFINITY)
-                        seconds = RLIM_INFINITY;
-                else
-                        seconds = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));
-        }
-
-        if (!*rl) {
-                *rl = new(struct rlimit, 1);
-                if (!*rl)
-                        return log_oom();
-        }
-
-        (*rl)->rlim_cur = (*rl)->rlim_max = seconds;
-        return 0;
+        return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_sec);
 }
 
-
 int config_parse_usec_limit(
                 const char *unit,
                 const char *filename,
@@ -1254,8 +1284,6 @@ int config_parse_usec_limit(
                 void *userdata) {
 
         struct rlimit **rl = data;
-        rlim_t useconds;
-        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1263,33 +1291,10 @@ int config_parse_usec_limit(
         assert(data);
 
         rl += ltype;
+        return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_usec);
+}
 
-        if (streq(rvalue, "infinity"))
-                useconds = RLIM_INFINITY;
-        else {
-                usec_t t;
-
-                r = parse_time(rvalue, &t, 1);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
-                        return 0;
-                }
-
-                if (t == USEC_INFINITY)
-                        useconds = RLIM_INFINITY;
-                else
-                        useconds = (rlim_t) t;
-        }
-
-        if (!*rl) {
-                *rl = new(struct rlimit, 1);
-                if (!*rl)
-                        return log_oom();
-        }
 
-        (*rl)->rlim_cur = (*rl)->rlim_max = useconds;
-        return 0;
-}
 
 #ifdef HAVE_SYSV_COMPAT
 int config_parse_sysv_priority(const char *unit,
index c3973a316e08a596e0a32c75153e226c90733028..854f60be80f9cbcfd8e206bb0637caf7befb7832 100644 (file)
@@ -680,11 +680,21 @@ static void test_config_parse_rlimit(void) {
         assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
         assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
 
+        assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55:66", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_NOFILE]);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_max == 66);
+
         assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity", rl, NULL) >= 0);
         assert_se(rl[RLIMIT_NOFILE]);
         assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
         assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
 
+        assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity:infinity", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_NOFILE]);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
+        assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
+
         rl[RLIMIT_NOFILE] = mfree(rl[RLIMIT_NOFILE]);
 
         assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0);
@@ -697,6 +707,11 @@ static void test_config_parse_rlimit(void) {
         assert_se(rl[RLIMIT_CPU]->rlim_cur == 57);
         assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
 
+        assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "40s:1m", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_CPU]);
+        assert_se(rl[RLIMIT_CPU]->rlim_cur == 40);
+        assert_se(rl[RLIMIT_CPU]->rlim_max == 60);
+
         assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0);
         assert_se(rl[RLIMIT_CPU]);
         assert_se(rl[RLIMIT_CPU]->rlim_cur == RLIM_INFINITY);
@@ -714,16 +729,31 @@ static void test_config_parse_rlimit(void) {
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
 
+        assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58:60", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_RTTIME]);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_max == 60);
+
         assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0);
         assert_se(rl[RLIMIT_RTTIME]);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
 
+        assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s:123s", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_RTTIME]);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_max == 123 * USEC_PER_SEC);
+
         assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0);
         assert_se(rl[RLIMIT_RTTIME]);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
 
+        assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity:infinity", rl, NULL) >= 0);
+        assert_se(rl[RLIMIT_RTTIME]);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
+        assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
+
         assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0);
         assert_se(rl[RLIMIT_RTTIME]);
         assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 2345 * USEC_PER_MSEC);