diff options
author | Lennart Poettering <lennart@poettering.net> | 2015-11-10 16:52:52 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2015-11-10 17:36:46 +0100 |
commit | a4c1800284e3546bbfab2dc19eb59bcb91c4a2ca (patch) | |
tree | 4cd44c560d87c8cc1ea86d7c1311feaa885140ff | |
parent | d580265eb4bbbafabdb3b7f0b501c0b05b76b2b7 (diff) |
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
-rw-r--r-- | man/systemd.exec.xml | 86 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 4 | ||||
-rw-r--r-- | src/core/load-fragment.c | 101 | ||||
-rw-r--r-- | src/core/load-fragment.h | 2 | ||||
-rw-r--r-- | 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 96298f11ed..2b090871ff 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -568,90 +568,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> @@ -1320,6 +1363,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 5b7954dbf9..75388659e3 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 cdc2ad950d..28b90eccc1 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1136,6 +1136,107 @@ int config_parse_bytes_limit( 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 029775bb46..0cf821289c 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 f9107e0d0d..3648ec9c58 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -681,6 +681,66 @@ static void test_config_parse_bounding_set(void) { assert_se(capability_bounding_set_drop == ~(make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN))); } +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] = mfree(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] = mfree(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] = mfree(rl[RLIMIT_RTTIME]); +} + int main(int argc, char *argv[]) { int r; @@ -690,6 +750,7 @@ int main(int argc, char *argv[]) { r = test_unit_file_get_set(); test_config_parse_exec(); test_config_parse_bounding_set(); + test_config_parse_rlimit(); test_load_env_file_1(); test_load_env_file_2(); test_load_env_file_3(); |