|
|
1abbee |
From 81a95ec724b7b874f850cb0f32f1981ccc4fb062 Mon Sep 17 00:00:00 2001
|
|
|
1abbee |
From: Karel Zak <kzak@redhat.com>
|
|
|
1abbee |
Date: Fri, 20 Nov 2015 12:54:10 +0100
|
|
|
1abbee |
Subject: [PATCH] core: support <soft:hard> ranges for RLIMIT options
|
|
|
1abbee |
|
|
|
1abbee |
The new parser supports:
|
|
|
1abbee |
|
|
|
1abbee |
<value> - specify both limits to the same value
|
|
|
1abbee |
<soft:hard> - specify both limits
|
|
|
1abbee |
|
|
|
1abbee |
the size or time specific suffixes are supported, for example
|
|
|
1abbee |
|
|
|
1abbee |
LimitRTTIME=1sec
|
|
|
1abbee |
LimitAS=4G:16G
|
|
|
1abbee |
|
|
|
1abbee |
The patch introduces parse_rlimit_range() and rlim type (size, sec,
|
|
|
1abbee |
usec, etc.) specific parsers. No code is duplicated now.
|
|
|
1abbee |
|
|
|
1abbee |
The patch also sync docs for DefaultLimitXXX= and LimitXXX=.
|
|
|
1abbee |
|
|
|
1abbee |
References: https://github.com/systemd/systemd/issues/1769
|
|
|
1abbee |
|
|
|
1abbee |
Cherry-picked from: 91518d20ddf0376808544576d0ef0883cedc67d4
|
|
|
1abbee |
Resolves: #1351415
|
|
|
1abbee |
---
|
|
|
1abbee |
man/systemd-system.conf.xml | 27 ++-
|
|
|
1abbee |
man/systemd.exec.xml | 5 +-
|
|
|
23b3cf |
src/core/load-fragment.c | 243 ++++++++++---------
|
|
|
23b3cf |
src/shared/util.c | 467 ++++++++++++++++++++++++++++++++++++
|
|
|
1abbee |
src/shared/util.h | 14 ++
|
|
|
1abbee |
src/test/test-unit-file.c | 31 +++
|
|
|
1abbee |
6 files changed, 667 insertions(+), 120 deletions(-)
|
|
|
1abbee |
|
|
|
1abbee |
diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
|
|
|
c62b8e |
index b7d9cdee05..39d19bc71a 100644
|
|
|
1abbee |
--- a/man/systemd-system.conf.xml
|
|
|
1abbee |
+++ b/man/systemd-system.conf.xml
|
|
|
1abbee |
@@ -326,13 +326,26 @@
|
|
|
1abbee |
<listitem><para>These settings control various default
|
|
|
1abbee |
resource limits for units. See
|
|
|
1abbee |
<citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
|
|
1abbee |
- for details. Use the string <varname>infinity</varname> to
|
|
|
1abbee |
- configure no limit on a specific resource. The multiplicative suffixes
|
|
|
1abbee |
- K (=1024), M (=1024*1024) and so on for G, T, P and E may be used for
|
|
|
1abbee |
- resource limits measured in bytes (e.g. DefaultLimitAS=16G). These
|
|
|
1abbee |
- settings may be overridden in individual units using the corresponding
|
|
|
1abbee |
- LimitXXX= directives. Note that these resource limits are only
|
|
|
1abbee |
- defaults for units, they are not applied to PID 1
|
|
|
1abbee |
+ for details. The resource limit is possible to specify in two formats,
|
|
|
1abbee |
+ <option>value</option> to set soft and hard limits to the same value,
|
|
|
1abbee |
+ or <option>soft:hard</option> to set both limits individually (e.g. DefaultLimitAS=4G:16G).
|
|
|
1abbee |
+ Use the string <varname>infinity</varname> to
|
|
|
1abbee |
+ configure no limit on a specific resource. The multiplicative
|
|
|
1abbee |
+ suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E
|
|
|
1abbee |
+ may be used for resource limits measured in bytes
|
|
|
1abbee |
+ (e.g. DefaultLimitAS=16G). For the limits referring to time values,
|
|
|
1abbee |
+ the usual time units ms, s, min, h and so on may be used (see
|
|
|
1abbee |
+ <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
|
|
1abbee |
+ for details). Note that if no time unit is specified for
|
|
|
1abbee |
+ <varname>DefaultLimitCPU=</varname> the default unit of seconds is
|
|
|
1abbee |
+ implied, while for <varname>DefaultLimitRTTIME=</varname> the default
|
|
|
1abbee |
+ unit of microseconds is implied. Also, note that the effective
|
|
|
1abbee |
+ granularity of the limits might influence their
|
|
|
1abbee |
+ enforcement. For example, time limits specified for
|
|
|
1abbee |
+ <varname>DefaultLimitCPU=</varname> will be rounded up implicitly to
|
|
|
1abbee |
+ multiples of 1s. These settings may be overridden in individual units
|
|
|
1abbee |
+ using the corresponding LimitXXX= directives. Note that these resource
|
|
|
1abbee |
+ limits are only defaults for units, they are not applied to PID 1
|
|
|
1abbee |
itself.</para></listitem>
|
|
|
1abbee |
</varlistentry>
|
|
|
1abbee |
</variablelist>
|
|
|
1abbee |
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
|
|
|
c62b8e |
index cfdcc3d173..0cd469cd98 100644
|
|
|
1abbee |
--- a/man/systemd.exec.xml
|
|
|
1abbee |
+++ b/man/systemd.exec.xml
|
|
|
1abbee |
@@ -558,7 +558,10 @@
|
|
|
1abbee |
<listitem><para>These settings set both soft and hard limits
|
|
|
1abbee |
of various resources for executed processes. See
|
|
|
1abbee |
<citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
|
|
1abbee |
- for details. Use the string <varname>infinity</varname> to
|
|
|
1abbee |
+ for details. The resource limit is possible to specify in two formats,
|
|
|
1abbee |
+ <option>value</option> to set soft and hard limits to the same value,
|
|
|
1abbee |
+ or <option>soft:hard</option> to set both limits individually (e.g. LimitAS=4G:16G).
|
|
|
1abbee |
+ Use the string <varname>infinity</varname> to
|
|
|
1abbee |
configure no limit on a specific resource. The multiplicative
|
|
|
1abbee |
suffixes K (=1024), M (=1024*1024) and so on for G, T, P and E
|
|
|
1abbee |
may be used for resource limits measured in bytes
|
|
|
1abbee |
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
|
|
|
c62b8e |
index 8afe9d7e83..d307f1c743 100644
|
|
|
1abbee |
--- a/src/core/load-fragment.c
|
|
|
1abbee |
+++ b/src/core/load-fragment.c
|
|
|
1abbee |
@@ -1075,81 +1075,108 @@ int config_parse_bounding_set(const char *unit,
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
-int config_parse_limit(const char *unit,
|
|
|
1abbee |
- const char *filename,
|
|
|
1abbee |
- unsigned line,
|
|
|
1abbee |
- const char *section,
|
|
|
1abbee |
- unsigned section_line,
|
|
|
1abbee |
- const char *lvalue,
|
|
|
1abbee |
- int ltype,
|
|
|
1abbee |
- const char *rvalue,
|
|
|
1abbee |
- void *data,
|
|
|
1abbee |
- void *userdata) {
|
|
|
1abbee |
|
|
|
1abbee |
- struct rlimit **rl = data;
|
|
|
1abbee |
- unsigned long long u;
|
|
|
1abbee |
+static int rlim_parse_u64(const char *val, rlim_t *res) {
|
|
|
1abbee |
+ int r = 0;
|
|
|
1abbee |
|
|
|
1abbee |
- assert(filename);
|
|
|
1abbee |
- assert(lvalue);
|
|
|
1abbee |
- assert(rvalue);
|
|
|
1abbee |
- assert(data);
|
|
|
1abbee |
+ if (streq(val, "infinity"))
|
|
|
1abbee |
+ *res = RLIM_INFINITY;
|
|
|
1abbee |
+ else {
|
|
|
1abbee |
+ uint64_t u;
|
|
|
1abbee |
|
|
|
1abbee |
- rl += ltype;
|
|
|
1abbee |
+ /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
|
|
|
1abbee |
+ assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = safe_atou64(val, &u);
|
|
|
1abbee |
+ if (r >= 0 && u >= (uint64_t) RLIM_INFINITY)
|
|
|
1abbee |
+ r = -ERANGE;
|
|
|
1abbee |
+ if (r == 0)
|
|
|
1abbee |
+ *res = (rlim_t) u;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+}
|
|
|
1abbee |
|
|
|
1abbee |
- if (streq(rvalue, "infinity"))
|
|
|
1abbee |
- u = (unsigned long long) RLIM_INFINITY;
|
|
|
1abbee |
+static int rlim_parse_size(const char *val, rlim_t *res) {
|
|
|
1abbee |
+ int r = 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (streq(val, "infinity"))
|
|
|
1abbee |
+ *res = RLIM_INFINITY;
|
|
|
1abbee |
else {
|
|
|
1abbee |
- int r;
|
|
|
1abbee |
+ off_t u;
|
|
|
1abbee |
|
|
|
1abbee |
- r = safe_atollu(rvalue, &u);
|
|
|
1abbee |
- if (r < 0) {
|
|
|
1abbee |
- log_syntax(unit, LOG_ERR, filename, line, -r,
|
|
|
1abbee |
- "Failed to parse resource value, ignoring: %s", rvalue);
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ r = parse_size(val, 1024, &u);
|
|
|
1abbee |
+ if (r >= 0 && u >= (off_t) RLIM_INFINITY)
|
|
|
1abbee |
+ r = -ERANGE;
|
|
|
1abbee |
+ if (r == 0)
|
|
|
1abbee |
+ *res = (rlim_t) u;
|
|
|
1abbee |
}
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+}
|
|
|
1abbee |
|
|
|
1abbee |
- if (!*rl) {
|
|
|
1abbee |
- *rl = new(struct rlimit, 1);
|
|
|
1abbee |
- if (!*rl)
|
|
|
1abbee |
- return log_oom();
|
|
|
1abbee |
- }
|
|
|
1abbee |
+static int rlim_parse_sec(const char *val, rlim_t *res) {
|
|
|
1abbee |
+ int r = 0;
|
|
|
1abbee |
|
|
|
1abbee |
- (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
+ if (streq(val, "infinity"))
|
|
|
1abbee |
+ *res = RLIM_INFINITY;
|
|
|
1abbee |
+ else {
|
|
|
1abbee |
+ usec_t t;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = parse_sec(val, &t);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ if (t == USEC_INFINITY)
|
|
|
1abbee |
+ *res = RLIM_INFINITY;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ *res = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));
|
|
|
1abbee |
+
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
-int config_parse_bytes_limit(const char *unit,
|
|
|
1abbee |
- const char *filename,
|
|
|
1abbee |
- unsigned line,
|
|
|
1abbee |
- const char *section,
|
|
|
1abbee |
- unsigned section_line,
|
|
|
1abbee |
- const char *lvalue,
|
|
|
1abbee |
- int ltype,
|
|
|
1abbee |
- const char *rvalue,
|
|
|
1abbee |
- void *data,
|
|
|
1abbee |
- void *userdata) {
|
|
|
1abbee |
+static int rlim_parse_usec(const char *val, rlim_t *res) {
|
|
|
1abbee |
+ int r = 0;
|
|
|
1abbee |
|
|
|
1abbee |
- struct rlimit **rl = data;
|
|
|
1abbee |
- uint64_t bytes;
|
|
|
1abbee |
+ if (streq(val, "infinity"))
|
|
|
1abbee |
+ *res = RLIM_INFINITY;
|
|
|
1abbee |
+ else {
|
|
|
1abbee |
+ usec_t t;
|
|
|
1abbee |
|
|
|
1abbee |
- assert(filename);
|
|
|
1abbee |
- assert(lvalue);
|
|
|
1abbee |
- assert(rvalue);
|
|
|
1abbee |
- assert(data);
|
|
|
1abbee |
+ r = parse_time(val, &t, 1);
|
|
|
1abbee |
+ if (r < 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ if (t == USEC_INFINITY)
|
|
|
1abbee |
+ *res = RLIM_INFINITY;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ *res = (rlim_t) t;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+}
|
|
|
1abbee |
|
|
|
1abbee |
- rl += ltype;
|
|
|
1abbee |
+static int parse_rlimit_range(
|
|
|
1abbee |
+ const char *unit,
|
|
|
1abbee |
+ const char *filename,
|
|
|
1abbee |
+ unsigned line,
|
|
|
1abbee |
+ const char *value,
|
|
|
1abbee |
+ struct rlimit **rl,
|
|
|
1abbee |
+ int (*rlim_parser)(const char *, rlim_t *)) {
|
|
|
1abbee |
|
|
|
1abbee |
- if (streq(rvalue, "infinity"))
|
|
|
1abbee |
- bytes = (uint64_t) RLIM_INFINITY;
|
|
|
1abbee |
- else {
|
|
|
1abbee |
- int r;
|
|
|
1abbee |
+ rlim_t soft, hard;
|
|
|
1abbee |
+ _cleanup_free_ char *sword = NULL, *hword = NULL;
|
|
|
1abbee |
+ int nwords, r;
|
|
|
1abbee |
|
|
|
1abbee |
- r = parse_size(rvalue, 1024, &bytes);
|
|
|
1abbee |
- if (r < 0) {
|
|
|
1abbee |
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ assert(value);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* <value> or <soft:hard> */
|
|
|
1abbee |
+ nwords = extract_many_words(&value, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &sword, &hword, NULL);
|
|
|
1abbee |
+ r = nwords < 0 ? nwords : nwords == 0 ? -EINVAL : 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (r == 0)
|
|
|
1abbee |
+ r = rlim_parser(sword, &soft);
|
|
|
1abbee |
+ if (r == 0 && nwords == 2)
|
|
|
1abbee |
+ r = rlim_parser(hword, &hard);
|
|
|
1abbee |
+ if (r < 0) {
|
|
|
1abbee |
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", value);
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
if (!*rl) {
|
|
|
1abbee |
@@ -1157,12 +1184,12 @@ int config_parse_bytes_limit(const char *unit,
|
|
|
1abbee |
if (!*rl)
|
|
|
1abbee |
return log_oom();
|
|
|
1abbee |
}
|
|
|
1abbee |
-
|
|
|
1abbee |
- (*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) bytes;
|
|
|
1abbee |
+ (*rl)->rlim_cur = soft;
|
|
|
1abbee |
+ (*rl)->rlim_max = nwords == 2 ? hard : soft;
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
-int config_parse_sec_limit(
|
|
|
1abbee |
+int config_parse_limit(
|
|
|
1abbee |
const char *unit,
|
|
|
1abbee |
const char *filename,
|
|
|
1abbee |
unsigned line,
|
|
|
1abbee |
@@ -1175,8 +1202,6 @@ int config_parse_sec_limit(
|
|
|
1abbee |
void *userdata) {
|
|
|
1abbee |
|
|
|
1abbee |
struct rlimit **rl = data;
|
|
|
1abbee |
- rlim_t seconds;
|
|
|
1abbee |
- int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(filename);
|
|
|
1abbee |
assert(lvalue);
|
|
|
1abbee |
@@ -1184,36 +1209,33 @@ int config_parse_sec_limit(
|
|
|
1abbee |
assert(data);
|
|
|
1abbee |
|
|
|
1abbee |
rl += ltype;
|
|
|
1abbee |
+ return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_u64);
|
|
|
1abbee |
+}
|
|
|
1abbee |
|
|
|
1abbee |
- if (streq(rvalue, "infinity"))
|
|
|
1abbee |
- seconds = RLIM_INFINITY;
|
|
|
1abbee |
- else {
|
|
|
1abbee |
- usec_t t;
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = parse_sec(rvalue, &t);
|
|
|
1abbee |
- if (r < 0) {
|
|
|
1abbee |
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+int config_parse_bytes_limit(
|
|
|
1abbee |
+ const char *unit,
|
|
|
1abbee |
+ const char *filename,
|
|
|
1abbee |
+ unsigned line,
|
|
|
1abbee |
+ const char *section,
|
|
|
1abbee |
+ unsigned section_line,
|
|
|
1abbee |
+ const char *lvalue,
|
|
|
1abbee |
+ int ltype,
|
|
|
1abbee |
+ const char *rvalue,
|
|
|
1abbee |
+ void *data,
|
|
|
1abbee |
+ void *userdata) {
|
|
|
1abbee |
|
|
|
1abbee |
- if (t == USEC_INFINITY)
|
|
|
1abbee |
- seconds = RLIM_INFINITY;
|
|
|
1abbee |
- else
|
|
|
1abbee |
- seconds = (rlim_t) (DIV_ROUND_UP(t, USEC_PER_SEC));
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ struct rlimit **rl = data;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!*rl) {
|
|
|
1abbee |
- *rl = new(struct rlimit, 1);
|
|
|
1abbee |
- if (!*rl)
|
|
|
1abbee |
- return log_oom();
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ assert(filename);
|
|
|
1abbee |
+ assert(lvalue);
|
|
|
1abbee |
+ assert(rvalue);
|
|
|
1abbee |
+ assert(data);
|
|
|
1abbee |
|
|
|
1abbee |
- (*rl)->rlim_cur = (*rl)->rlim_max = seconds;
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
+ rl += ltype;
|
|
|
1abbee |
+ return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_size);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
-
|
|
|
1abbee |
-int config_parse_usec_limit(
|
|
|
1abbee |
+int config_parse_sec_limit(
|
|
|
1abbee |
const char *unit,
|
|
|
1abbee |
const char *filename,
|
|
|
1abbee |
unsigned line,
|
|
|
1abbee |
@@ -1226,8 +1248,6 @@ int config_parse_usec_limit(
|
|
|
1abbee |
void *userdata) {
|
|
|
1abbee |
|
|
|
1abbee |
struct rlimit **rl = data;
|
|
|
1abbee |
- rlim_t useconds;
|
|
|
1abbee |
- int r;
|
|
|
1abbee |
|
|
|
1abbee |
assert(filename);
|
|
|
1abbee |
assert(lvalue);
|
|
|
1abbee |
@@ -1235,34 +1255,33 @@ int config_parse_usec_limit(
|
|
|
1abbee |
assert(data);
|
|
|
1abbee |
|
|
|
1abbee |
rl += ltype;
|
|
|
1abbee |
+ return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_sec);
|
|
|
1abbee |
+}
|
|
|
1abbee |
|
|
|
1abbee |
- if (streq(rvalue, "infinity"))
|
|
|
1abbee |
- useconds = RLIM_INFINITY;
|
|
|
1abbee |
- else {
|
|
|
1abbee |
- usec_t t;
|
|
|
1abbee |
-
|
|
|
1abbee |
- r = parse_time(rvalue, &t, 1);
|
|
|
1abbee |
- if (r < 0) {
|
|
|
1abbee |
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+int config_parse_usec_limit(
|
|
|
1abbee |
+ const char *unit,
|
|
|
1abbee |
+ const char *filename,
|
|
|
1abbee |
+ unsigned line,
|
|
|
1abbee |
+ const char *section,
|
|
|
1abbee |
+ unsigned section_line,
|
|
|
1abbee |
+ const char *lvalue,
|
|
|
1abbee |
+ int ltype,
|
|
|
1abbee |
+ const char *rvalue,
|
|
|
1abbee |
+ void *data,
|
|
|
1abbee |
+ void *userdata) {
|
|
|
1abbee |
|
|
|
1abbee |
- if (t == USEC_INFINITY)
|
|
|
1abbee |
- useconds = RLIM_INFINITY;
|
|
|
1abbee |
- else
|
|
|
1abbee |
- useconds = (rlim_t) t;
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ struct rlimit **rl = data;
|
|
|
1abbee |
|
|
|
1abbee |
- if (!*rl) {
|
|
|
1abbee |
- *rl = new(struct rlimit, 1);
|
|
|
1abbee |
- if (!*rl)
|
|
|
1abbee |
- return log_oom();
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ assert(filename);
|
|
|
1abbee |
+ assert(lvalue);
|
|
|
1abbee |
+ assert(rvalue);
|
|
|
1abbee |
+ assert(data);
|
|
|
1abbee |
|
|
|
1abbee |
- (*rl)->rlim_cur = (*rl)->rlim_max = useconds;
|
|
|
1abbee |
- return 0;
|
|
|
1abbee |
+ rl += ltype;
|
|
|
1abbee |
+ return parse_rlimit_range(unit, filename, line, rvalue, rl, rlim_parse_usec);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+
|
|
|
1abbee |
#ifdef HAVE_SYSV_COMPAT
|
|
|
1abbee |
int config_parse_sysv_priority(const char *unit,
|
|
|
1abbee |
const char *filename,
|
|
|
1abbee |
diff --git a/src/shared/util.c b/src/shared/util.c
|
|
|
c62b8e |
index 036677eb46..f75ed9dd42 100644
|
|
|
1abbee |
--- a/src/shared/util.c
|
|
|
1abbee |
+++ b/src/shared/util.c
|
|
|
1abbee |
@@ -93,6 +93,7 @@
|
|
|
1abbee |
#include "virt.h"
|
|
|
1abbee |
#include "def.h"
|
|
|
1abbee |
#include "sparse-endian.h"
|
|
|
1abbee |
+#include "conf-parser.h"
|
|
|
1abbee |
|
|
|
1abbee |
int saved_argc = 0;
|
|
|
1abbee |
char **saved_argv = NULL;
|
|
|
1abbee |
@@ -100,6 +101,8 @@ char **saved_argv = NULL;
|
|
|
1abbee |
static volatile unsigned cached_columns = 0;
|
|
|
1abbee |
static volatile unsigned cached_lines = 0;
|
|
|
1abbee |
|
|
|
1abbee |
+bool unichar_is_valid(int32_t ch);
|
|
|
1abbee |
+
|
|
|
1abbee |
size_t page_size(void) {
|
|
|
1abbee |
static thread_local size_t pgsz = 0;
|
|
|
1abbee |
long r;
|
|
|
1abbee |
@@ -1365,6 +1368,207 @@ char *cescape(const char *s) {
|
|
|
1abbee |
return r;
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+bool unichar_is_valid(int32_t ch) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (ch >= 0x110000) /* End of unicode space */
|
|
|
1abbee |
+ return false;
|
|
|
1abbee |
+ if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */
|
|
|
1abbee |
+ return false;
|
|
|
1abbee |
+ if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */
|
|
|
1abbee |
+ return false;
|
|
|
1abbee |
+ if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */
|
|
|
1abbee |
+ return false;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return true;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+int cunescape_one(const char *p, size_t length, int32_t *ret, bool *eight_bit) {
|
|
|
1abbee |
+ int r = 1;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(p);
|
|
|
1abbee |
+ assert(*p);
|
|
|
1abbee |
+ assert(ret);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Unescapes C style. Returns the unescaped character in ret.
|
|
|
1abbee |
+ * Sets *eight_bit to true if the escaped sequence either fits in
|
|
|
1abbee |
+ * one byte in UTF-8 or is a non-unicode literal byte and should
|
|
|
1abbee |
+ * instead be copied directly.
|
|
|
1abbee |
+ */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (length != (size_t) -1 && length < 1)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ switch (p[0]) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ case 'a':
|
|
|
1abbee |
+ *ret = '\a';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ case 'b':
|
|
|
1abbee |
+ *ret = '\b';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ case 'f':
|
|
|
1abbee |
+ *ret = '\f';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ case 'n':
|
|
|
1abbee |
+ *ret = '\n';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ case 'r':
|
|
|
1abbee |
+ *ret = '\r';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ case 't':
|
|
|
1abbee |
+ *ret = '\t';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ case 'v':
|
|
|
1abbee |
+ *ret = '\v';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ case '\\':
|
|
|
1abbee |
+ *ret = '\\';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ case '"':
|
|
|
1abbee |
+ *ret = '"';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ case '\'':
|
|
|
1abbee |
+ *ret = '\'';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ case 's':
|
|
|
1abbee |
+ /* This is an extension of the XDG syntax files */
|
|
|
1abbee |
+ *ret = ' ';
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ case 'x': {
|
|
|
1abbee |
+ /* hexadecimal encoding */
|
|
|
1abbee |
+ int a, b;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (length != (size_t) -1 && length < 3)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ a = unhexchar(p[1]);
|
|
|
1abbee |
+ if (a < 0)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ b = unhexchar(p[2]);
|
|
|
1abbee |
+ if (b < 0)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Don't allow NUL bytes */
|
|
|
1abbee |
+ if (a == 0 && b == 0)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ *ret = (a << 4U) | b;
|
|
|
1abbee |
+ *eight_bit = true;
|
|
|
1abbee |
+ r = 3;
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ case 'u': {
|
|
|
1abbee |
+ /* C++11 style 16bit unicode */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ int a[4];
|
|
|
1abbee |
+ unsigned i;
|
|
|
1abbee |
+ uint32_t c;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (length != (size_t) -1 && length < 5)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ for (i = 0; i < 4; i++) {
|
|
|
1abbee |
+ a[i] = unhexchar(p[1 + i]);
|
|
|
1abbee |
+ if (a[i] < 0)
|
|
|
1abbee |
+ return a[i];
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Don't allow 0 chars */
|
|
|
1abbee |
+ if (c == 0)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ *ret = c;
|
|
|
1abbee |
+ r = 5;
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ case 'U': {
|
|
|
1abbee |
+ /* C++11 style 32bit unicode */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ int a[8];
|
|
|
1abbee |
+ unsigned i;
|
|
|
1abbee |
+ int32_t c;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (length != (size_t) -1 && length < 9)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ for (i = 0; i < 8; i++) {
|
|
|
1abbee |
+ a[i] = unhexchar(p[1 + i]);
|
|
|
1abbee |
+ if (a[i] < 0)
|
|
|
1abbee |
+ return a[i];
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
|
|
|
1abbee |
+ ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Don't allow 0 chars */
|
|
|
1abbee |
+ if (c == 0)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Don't allow invalid code points */
|
|
|
1abbee |
+ if (!unichar_is_valid(c))
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ *ret = c;
|
|
|
1abbee |
+ r = 9;
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ case '0':
|
|
|
1abbee |
+ case '1':
|
|
|
1abbee |
+ case '2':
|
|
|
1abbee |
+ case '3':
|
|
|
1abbee |
+ case '4':
|
|
|
1abbee |
+ case '5':
|
|
|
1abbee |
+ case '6':
|
|
|
1abbee |
+ case '7': {
|
|
|
1abbee |
+ /* octal encoding */
|
|
|
1abbee |
+ int a, b, c;
|
|
|
1abbee |
+ int32_t m;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (length != (size_t) -1 && length < 3)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ a = unoctchar(p[0]);
|
|
|
1abbee |
+ if (a < 0)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ b = unoctchar(p[1]);
|
|
|
1abbee |
+ if (b < 0)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ c = unoctchar(p[2]);
|
|
|
1abbee |
+ if (c < 0)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* don't allow NUL bytes */
|
|
|
1abbee |
+ if (a == 0 && b == 0 && c == 0)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Don't allow bytes above 255 */
|
|
|
1abbee |
+ m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
|
|
|
1abbee |
+ if (m > 255)
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ *ret = m;
|
|
|
1abbee |
+ *eight_bit = true;
|
|
|
1abbee |
+ r = 3;
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ default:
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
|
|
|
1abbee |
char *r, *t;
|
|
|
1abbee |
const char *f;
|
|
|
1abbee |
@@ -8207,3 +8411,266 @@ bool colors_enabled(void) {
|
|
|
1abbee |
|
|
|
1abbee |
return parse_boolean(colors) != 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
+
|
|
|
1abbee |
+int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
|
|
|
1abbee |
+ _cleanup_free_ char *s = NULL;
|
|
|
1abbee |
+ size_t allocated = 0, sz = 0;
|
|
|
1abbee |
+ char c;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ char quote = 0; /* 0 or ' or " */
|
|
|
1abbee |
+ bool backslash = false; /* whether we've just seen a backslash */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(p);
|
|
|
1abbee |
+ assert(ret);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Bail early if called after last value or with no input */
|
|
|
1abbee |
+ if (!*p)
|
|
|
1abbee |
+ goto finish_force_terminate;
|
|
|
1abbee |
+ c = **p;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (!separators)
|
|
|
1abbee |
+ separators = WHITESPACE;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Parses the first word of a string, and returns it in
|
|
|
1abbee |
+ * *ret. Removes all quotes in the process. When parsing fails
|
|
|
1abbee |
+ * (because of an uneven number of quotes or similar), leaves
|
|
|
1abbee |
+ * the pointer *p at the first invalid character. */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
|
|
|
1abbee |
+ if (!GREEDY_REALLOC(s, allocated, sz+1))
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ for (;; (*p)++, c = **p) {
|
|
|
1abbee |
+ if (c == 0)
|
|
|
1abbee |
+ goto finish_force_terminate;
|
|
|
1abbee |
+ else if (strchr(separators, c)) {
|
|
|
1abbee |
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
|
|
|
1abbee |
+ (*p)++;
|
|
|
1abbee |
+ goto finish_force_next;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ } else {
|
|
|
1abbee |
+ /* We found a non-blank character, so we will always
|
|
|
1abbee |
+ * want to return a string (even if it is empty),
|
|
|
1abbee |
+ * allocate it here. */
|
|
|
1abbee |
+ if (!GREEDY_REALLOC(s, allocated, sz+1))
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ for (;; (*p)++, c = **p) {
|
|
|
1abbee |
+ if (backslash) {
|
|
|
1abbee |
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (c == 0) {
|
|
|
1abbee |
+ if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
|
|
|
1abbee |
+ (!quote || flags & EXTRACT_RELAX)) {
|
|
|
1abbee |
+ /* If we find an unquoted trailing backslash and we're in
|
|
|
1abbee |
+ * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
|
|
|
1abbee |
+ * output.
|
|
|
1abbee |
+ *
|
|
|
1abbee |
+ * Unbalanced quotes will only be allowed in EXTRACT_RELAX
|
|
|
1abbee |
+ * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
|
|
|
1abbee |
+ */
|
|
|
1abbee |
+ s[sz++] = '\\';
|
|
|
1abbee |
+ goto finish_force_terminate;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ if (flags & EXTRACT_RELAX)
|
|
|
1abbee |
+ goto finish_force_terminate;
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (flags & EXTRACT_CUNESCAPE) {
|
|
|
1abbee |
+ bool eight_bit = false;
|
|
|
1abbee |
+ int32_t u;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = cunescape_one(*p, (size_t) -1, &u, &eight_bit);
|
|
|
1abbee |
+ if (r < 0) {
|
|
|
1abbee |
+ if (flags & EXTRACT_CUNESCAPE_RELAX) {
|
|
|
1abbee |
+ s[sz++] = '\\';
|
|
|
1abbee |
+ s[sz++] = c;
|
|
|
1abbee |
+ } else
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+ } else {
|
|
|
1abbee |
+ (*p) += r - 1;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (eight_bit)
|
|
|
1abbee |
+ s[sz++] = u;
|
|
|
1abbee |
+ else
|
|
|
1abbee |
+ sz += utf8_encode_unichar(s + sz, u);
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ } else
|
|
|
1abbee |
+ s[sz++] = c;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ backslash = false;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ } else if (quote) { /* inside either single or double quotes */
|
|
|
1abbee |
+ for (;; (*p)++, c = **p) {
|
|
|
1abbee |
+ if (c == 0) {
|
|
|
1abbee |
+ if (flags & EXTRACT_RELAX)
|
|
|
1abbee |
+ goto finish_force_terminate;
|
|
|
1abbee |
+ return -EINVAL;
|
|
|
1abbee |
+ } else if (c == quote) { /* found the end quote */
|
|
|
1abbee |
+ quote = 0;
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
|
|
|
1abbee |
+ backslash = true;
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ } else {
|
|
|
1abbee |
+ if (!GREEDY_REALLOC(s, allocated, sz+2))
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ s[sz++] = c;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ } else {
|
|
|
1abbee |
+ for (;; (*p)++, c = **p) {
|
|
|
1abbee |
+ if (c == 0)
|
|
|
1abbee |
+ goto finish_force_terminate;
|
|
|
1abbee |
+ else if ((c == '\'' || c == '"') && (flags & EXTRACT_QUOTES)) {
|
|
|
1abbee |
+ quote = c;
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
|
|
|
1abbee |
+ backslash = true;
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ } else if (strchr(separators, c)) {
|
|
|
1abbee |
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
|
|
|
1abbee |
+ (*p)++;
|
|
|
1abbee |
+ goto finish_force_next;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ /* Skip additional coalesced separators. */
|
|
|
1abbee |
+ for (;; (*p)++, c = **p) {
|
|
|
1abbee |
+ if (c == 0)
|
|
|
1abbee |
+ goto finish_force_terminate;
|
|
|
1abbee |
+ if (!strchr(separators, c))
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ goto finish;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ } else {
|
|
|
1abbee |
+ if (!GREEDY_REALLOC(s, allocated, sz+2))
|
|
|
1abbee |
+ return -ENOMEM;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ s[sz++] = c;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+finish_force_terminate:
|
|
|
1abbee |
+ *p = NULL;
|
|
|
1abbee |
+finish:
|
|
|
1abbee |
+ if (!s) {
|
|
|
1abbee |
+ *p = NULL;
|
|
|
1abbee |
+ *ret = NULL;
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+finish_force_next:
|
|
|
1abbee |
+ s[sz] = 0;
|
|
|
1abbee |
+ *ret = s;
|
|
|
1abbee |
+ s = NULL;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return 1;
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+int extract_first_word_and_warn(
|
|
|
1abbee |
+ const char **p,
|
|
|
1abbee |
+ char **ret,
|
|
|
1abbee |
+ const char *separators,
|
|
|
1abbee |
+ ExtractFlags flags,
|
|
|
1abbee |
+ const char *unit,
|
|
|
1abbee |
+ const char *filename,
|
|
|
1abbee |
+ unsigned line,
|
|
|
1abbee |
+ const char *rvalue) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Try to unquote it, if it fails, warn about it and try again
|
|
|
1abbee |
+ * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
|
|
|
1abbee |
+ * backslashes verbatim in invalid escape sequences. */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ const char *save;
|
|
|
1abbee |
+ int r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ save = *p;
|
|
|
1abbee |
+ r = extract_first_word(p, ret, separators, flags);
|
|
|
1abbee |
+ if (r >= 0)
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
|
|
|
1abbee |
+ *p = save;
|
|
|
1abbee |
+ r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
|
|
|
1abbee |
+ if (r >= 0) {
|
|
|
1abbee |
+ /* It worked this time, hence it must have been an invalid escape sequence we could correct. */
|
|
|
1abbee |
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Invalid escape sequences in line, correcting: \"%s\"", rvalue);
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* If it's still EINVAL; then it must be unbalanced quoting, report this. */
|
|
|
1abbee |
+ if (r == -EINVAL)
|
|
|
1abbee |
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue);
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Can be any error, report it */
|
|
|
1abbee |
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue);
|
|
|
1abbee |
+}
|
|
|
1abbee |
+
|
|
|
1abbee |
+int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) {
|
|
|
1abbee |
+ va_list ap;
|
|
|
1abbee |
+ char **l;
|
|
|
1abbee |
+ int n = 0, i, c, r;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Parses a number of words from a string, stripping any
|
|
|
1abbee |
+ * quotes if necessary. */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert(p);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Count how many words are expected */
|
|
|
1abbee |
+ va_start(ap, flags);
|
|
|
1abbee |
+ for (;;) {
|
|
|
1abbee |
+ if (!va_arg(ap, char **))
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ n++;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ va_end(ap);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (n <= 0)
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* Read all words into a temporary array */
|
|
|
1abbee |
+ l = newa0(char*, n);
|
|
|
1abbee |
+ for (c = 0; c < n; c++) {
|
|
|
1abbee |
+
|
|
|
1abbee |
+ r = extract_first_word(p, &l[c], separators, flags);
|
|
|
1abbee |
+ if (r < 0) {
|
|
|
1abbee |
+ int j;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ for (j = 0; j < c; j++)
|
|
|
1abbee |
+ free(l[j]);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return r;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (r == 0)
|
|
|
1abbee |
+ break;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ /* If we managed to parse all words, return them in the passed
|
|
|
1abbee |
+ * in parameters */
|
|
|
1abbee |
+ va_start(ap, flags);
|
|
|
1abbee |
+ for (i = 0; i < n; i++) {
|
|
|
1abbee |
+ char **v;
|
|
|
1abbee |
+
|
|
|
1abbee |
+ v = va_arg(ap, char **);
|
|
|
1abbee |
+ assert(v);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ *v = l[i];
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+ va_end(ap);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ return c;
|
|
|
1abbee |
+}
|
|
|
1abbee |
diff --git a/src/shared/util.h b/src/shared/util.h
|
|
|
c62b8e |
index a441e44ff9..be04524cc9 100644
|
|
|
1abbee |
--- a/src/shared/util.h
|
|
|
1abbee |
+++ b/src/shared/util.h
|
|
|
1abbee |
@@ -315,6 +315,7 @@ int undecchar(char c) _const_;
|
|
|
1abbee |
char *cescape(const char *s);
|
|
|
1abbee |
char *cunescape(const char *s);
|
|
|
1abbee |
char *cunescape_length(const char *s, size_t length);
|
|
|
1abbee |
+int cunescape_one(const char *p, size_t length, int32_t *ret, bool *eight_bit);
|
|
|
1abbee |
char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix);
|
|
|
1abbee |
|
|
|
1abbee |
char *xescape(const char *s, const char *bad);
|
|
|
1abbee |
@@ -1082,3 +1083,16 @@ void sigkill_wait(pid_t *pid);
|
|
|
1abbee |
int syslog_parse_priority(const char **p, int *priority, bool with_facility);
|
|
|
1abbee |
|
|
|
1abbee |
char *shell_maybe_quote(const char *s);
|
|
|
1abbee |
+
|
|
|
1abbee |
+typedef enum ExtractFlags {
|
|
|
1abbee |
+ EXTRACT_RELAX = 1,
|
|
|
1abbee |
+ EXTRACT_CUNESCAPE = 2,
|
|
|
1abbee |
+ EXTRACT_CUNESCAPE_RELAX = 4,
|
|
|
1abbee |
+ EXTRACT_QUOTES = 8,
|
|
|
1abbee |
+ EXTRACT_DONT_COALESCE_SEPARATORS = 16,
|
|
|
1abbee |
+ EXTRACT_RETAIN_ESCAPE = 32,
|
|
|
1abbee |
+} ExtractFlags;
|
|
|
1abbee |
+
|
|
|
1abbee |
+int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
|
|
|
1abbee |
+int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
|
|
|
1abbee |
+int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_;
|
|
|
1abbee |
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
|
|
|
c62b8e |
index 87c81ccd71..931dfeda88 100644
|
|
|
1abbee |
--- a/src/test/test-unit-file.c
|
|
|
1abbee |
+++ b/src/test/test-unit-file.c
|
|
|
1abbee |
@@ -554,11 +554,22 @@ static void test_config_parse_rlimit(void) {
|
|
|
1abbee |
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
|
|
|
1abbee |
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
|
|
|
1abbee |
|
|
|
1abbee |
+
|
|
|
1abbee |
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "55:66", rl, NULL) >= 0);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_NOFILE]);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == 55);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_NOFILE]->rlim_max == 66);
|
|
|
1abbee |
+
|
|
|
1abbee |
assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity", rl, NULL) >= 0);
|
|
|
1abbee |
assert_se(rl[RLIMIT_NOFILE]);
|
|
|
1abbee |
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
|
|
|
1abbee |
assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
|
|
|
1abbee |
|
|
|
1abbee |
+ assert_se(config_parse_limit(NULL, "fake", 1, "section", 1, "LimitNOFILE", RLIMIT_NOFILE, "infinity:infinity", rl, NULL) >= 0);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_NOFILE]);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == RLIM_INFINITY);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_NOFILE]->rlim_cur == rl[RLIMIT_NOFILE]->rlim_max);
|
|
|
1abbee |
+
|
|
|
1abbee |
free(rl[RLIMIT_NOFILE]);
|
|
|
1abbee |
assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "56", rl, NULL) >= 0);
|
|
|
1abbee |
assert_se(rl[RLIMIT_CPU]);
|
|
|
1abbee |
@@ -570,6 +581,11 @@ static void test_config_parse_rlimit(void) {
|
|
|
1abbee |
assert_se(rl[RLIMIT_CPU]->rlim_cur == 57);
|
|
|
1abbee |
assert_se(rl[RLIMIT_CPU]->rlim_cur == rl[RLIMIT_CPU]->rlim_max);
|
|
|
1abbee |
|
|
|
1abbee |
+ assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "40s:1m", rl, NULL) >= 0);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_CPU]);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_CPU]->rlim_cur == 40);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_CPU]->rlim_max == 60);
|
|
|
1abbee |
+
|
|
|
1abbee |
assert_se(config_parse_sec_limit(NULL, "fake", 1, "section", 1, "LimitCPU", RLIMIT_CPU, "infinity", rl, NULL) >= 0);
|
|
|
1abbee |
assert_se(rl[RLIMIT_CPU]);
|
|
|
1abbee |
assert_se(rl[RLIMIT_CPU]->rlim_cur == RLIM_INFINITY);
|
|
|
1abbee |
@@ -587,16 +603,31 @@ static void test_config_parse_rlimit(void) {
|
|
|
1abbee |
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
|
|
|
1abbee |
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
|
|
|
1abbee |
|
|
|
1abbee |
+ assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "58:60", rl, NULL) >= 0);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_RTTIME]);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 58);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_RTTIME]->rlim_max == 60);
|
|
|
1abbee |
+
|
|
|
1abbee |
assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s", rl, NULL) >= 0);
|
|
|
1abbee |
assert_se(rl[RLIMIT_RTTIME]);
|
|
|
1abbee |
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
|
|
|
1abbee |
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
|
|
|
1abbee |
|
|
|
1abbee |
+ assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "59s:123s", rl, NULL) >= 0);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_RTTIME]);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 59 * USEC_PER_SEC);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_RTTIME]->rlim_max == 123 * USEC_PER_SEC);
|
|
|
1abbee |
+
|
|
|
1abbee |
assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity", rl, NULL) >= 0);
|
|
|
1abbee |
assert_se(rl[RLIMIT_RTTIME]);
|
|
|
1abbee |
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
|
|
|
1abbee |
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
|
|
|
1abbee |
|
|
|
1abbee |
+ assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "infinity:infinity", rl, NULL) >= 0);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_RTTIME]);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == RLIM_INFINITY);
|
|
|
1abbee |
+ assert_se(rl[RLIMIT_RTTIME]->rlim_cur == rl[RLIMIT_RTTIME]->rlim_max);
|
|
|
1abbee |
+
|
|
|
1abbee |
assert_se(config_parse_usec_limit(NULL, "fake", 1, "section", 1, "LimitRTTIME", RLIMIT_RTTIME, "2345ms", rl, NULL) >= 0);
|
|
|
1abbee |
assert_se(rl[RLIMIT_RTTIME]);
|
|
|
1abbee |
assert_se(rl[RLIMIT_RTTIME]->rlim_cur == 2345 * USEC_PER_MSEC);
|