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