summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2015-11-26 13:17:25 +0100
committerLennart Poettering <lennart@poettering.net>2015-11-26 13:17:25 +0100
commitf7b5b034e8758079ddc7a915394704fc2a350c37 (patch)
treec787077f4929444728e01b0657ef33377bd7869a
parentfedb9a51e61b51940b1fea0d736813ebd269e9e0 (diff)
parent91518d20ddf0376808544576d0ef0883cedc67d4 (diff)
Merge pull request #1994 from karelzak/rlimits
core: support <soft:hard> ranges for RLIMIT options
-rw-r--r--man/systemd-system.conf.xml27
-rw-r--r--man/systemd.exec.xml5
-rw-r--r--src/core/load-fragment.c229
-rw-r--r--src/test/test-unit-file.c30
4 files changed, 171 insertions, 120 deletions
diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
index ead52951da..edc6df914a 100644
--- a/man/systemd-system.conf.xml
+++ b/man/systemd-system.conf.xml
@@ -354,13 +354,26 @@
<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>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index ecc3a8973c..5f98ef163c 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -630,7 +630,10 @@
<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
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 3b55781d18..8847578bd7 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -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,
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index c3973a316e..854f60be80 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -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);