summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--README5
-rw-r--r--TODO12
-rw-r--r--man/systemd-nspawn.xml13
-rw-r--r--shell-completion/bash/machinectl7
-rw-r--r--src/basic/env-util.c2
-rw-r--r--src/basic/strv.c27
-rw-r--r--src/basic/strv.h3
-rw-r--r--src/basic/util.c138
-rw-r--r--src/basic/util.h21
-rw-r--r--src/core/load-fragment.c8
-rw-r--r--src/journal-remote/journal-remote.c2
-rw-r--r--src/libsystemd/sd-bus/bus-convenience.c146
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h6
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c67
-rw-r--r--src/locale/localed.c6
-rw-r--r--src/nspawn/nspawn.c90
-rw-r--r--src/nss-myhostname/nss-myhostname.c3
-rw-r--r--src/resolve-host/resolve-host.c32
-rw-r--r--src/resolve/resolved-dns-cache.c64
-rw-r--r--src/resolve/resolved-dns-packet.c2
-rw-r--r--src/resolve/resolved-dns-query.c166
-rw-r--r--src/resolve/resolved-dns-scope.c8
-rw-r--r--src/resolve/resolved-dns-transaction.c3
-rw-r--r--src/shared/condition.c2
-rw-r--r--src/sysusers/sysusers.c4
-rw-r--r--src/test/test-strv.c35
-rw-r--r--src/test/test-util.c349
-rw-r--r--src/tmpfiles/tmpfiles.c7
29 files changed, 892 insertions, 338 deletions
diff --git a/NEWS b/NEWS
index 97dd000d4e..fe5eb31672 100644
--- a/NEWS
+++ b/NEWS
@@ -756,7 +756,7 @@ CHANGES WITH 218:
* When querying unit file enablement status (for example via
"systemctl is-enabled"), a new state "indirect" is now known
which indicates that a unit might not be enabled itself, but
- another unit listed in its Alias= setting might be.
+ another unit listed in its Also= setting might be.
* Similar to the various existing ConditionXYZ= settings for
units there are now matching AssertXYZ= settings. While
diff --git a/README b/README
index 7595949765..d84bf8ea69 100644
--- a/README
+++ b/README
@@ -274,8 +274,3 @@ WARNINGS:
servers if not specified otherwise at configure time. You
really should not ship an OS or device with this default
setting. See DISTRO_PORTING for details.
-
-ENGINEERING AND CONSULTING SERVICES:
- ENDOCODE <https://endocode.com/> offers professional
- engineering and consulting services for systemd. Please
- contact Chris Kühl <chris@endocode.com> for more information.
diff --git a/TODO b/TODO
index 8b6692ed1b..fdb073aa2c 100644
--- a/TODO
+++ b/TODO
@@ -6,7 +6,7 @@ Bugfixes:
automount points even when the original .automount file did not exist
anymore. Only the .mount unit was still around.
-* ExecStart with unicode characters fails in strv_split_quoted:
+* ExecStart with unicode characters fails in strv_split_extract:
[Service]
Environment=ONE='one' "TWO='two two' too" THREE=
@@ -273,7 +273,7 @@ Features:
* maybe add support for specifier expansion in user.conf, specifically DefaultEnvironment=
-* code cleanup: retire FOREACH_WORD_QUOTED, port to unquote_first_word() loops instead
+* code cleanup: retire FOREACH_WORD_QUOTED, port to extract_first_word() loops instead
* introduce systemd-timesync-wait.service or so to sync on an NTP fix?
@@ -309,7 +309,7 @@ Features:
* exponential backoff in timesyncd and resolved when we cannot reach a server
-* unquote_many_words() should probably be used by a lot of code that
+* extract_many_words() should probably be used by a lot of code that
currently uses FOREACH_WORD and friends. For example, most conf
parsing callbacks should use it.
@@ -354,7 +354,9 @@ Features:
- avahi compat
- DNS-SD service registration from socket units
- edns0
- - dname
+ - dname: Not necessary for plain DNS as synthesized cname is handed out instead if we do not
+ announce dname support. However, for DNSSEC it is necessary as the synthesized cname
+ will not be signed.
- cname on PTR (?)
* Allow multiple ExecStart= for all Type= settings, so that we can cover rescue.service nicely
@@ -470,7 +472,7 @@ Features:
ReadOnlyDirectories=... for whitelisting files for a service.
* sd-bus:
- - figure out wat the UNPRIVILEGED flag shall mean for props that can be read *and* written
+ - change argv list matching logic
- GetAllProperties() on a non-existing object does not result in a failure currently
- kdbus: process fd=-1 for incoming msgs
- port to sd-resolve for connecting to TCP dbus servers
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index d1e050cb89..4966749259 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -581,7 +581,9 @@
same path in the container --, or a colon-separated pair of
paths -- in which case the first specified path is the source
in the host, and the second path is the destination in the
- container. This option may be specified multiple times for
+ container. Backslash escapes are interpreted so
+ <literal>\:</literal> may be used to embed colons in either path.
+ This option may be specified multiple times for
creating multiple independent bind mount points. The
<option>--bind-ro=</option> option creates read-only bind
mounts.</para></listitem>
@@ -600,7 +602,10 @@
otherwise specified). This option is particularly useful for
mounting directories such as <filename>/var</filename> as
tmpfs, to allow state-less systems, in particular when
- combined with <option>--read-only</option>.</para></listitem>
+ combined with <option>--read-only</option>.
+ Backslash escapes are interpreted in the path so
+ <literal>\:</literal> may be used to embed colons in the path.
+ </para></listitem>
</varlistentry>
<varlistentry>
@@ -612,6 +617,10 @@
list of colon-separated paths to the directory trees to
combine and the destination mount point.</para>
+ <para>Backslash escapes are interpreted in the paths, so
+ <literal>\:</literal> may be used to embed colons in the paths.
+ </para>
+
<para>If three or more paths are specified, then the last
specified path is the destination mount point in the
container, all paths specified before refer to directory trees
diff --git a/shell-completion/bash/machinectl b/shell-completion/bash/machinectl
index 3789492d72..0d09258a0b 100644
--- a/shell-completion/bash/machinectl
+++ b/shell-completion/bash/machinectl
@@ -26,7 +26,8 @@ __contains_word() {
__get_machines() {
local a b
- machinectl list --no-legend --no-pager | { while read a b; do echo " $a"; done; };
+ (machinectl list-images --no-legend --no-pager; machinectl list --no-legend --no-pager) | \
+ { while read a b; do echo " $a"; done; } | sort -u;
}
_machinectl() {
@@ -39,8 +40,8 @@ _machinectl() {
)
local -A VERBS=(
- [STANDALONE]='list'
- [MACHINES]='status show terminate kill reboot login'
+ [STANDALONE]='list list-images'
+ [MACHINES]='status show start login enable disable poweroff reboot terminate kill copy-to copy-from image-status show-image clone rename read-only remove set-limit list-transfers cancel-transfer'
)
_init_completion || return
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index ac7bbdc711..4804a67f91 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -550,7 +550,7 @@ char **replace_env_argv(char **argv, char **env) {
if (e) {
int r;
- r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
+ r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
if (r < 0) {
ret[k] = NULL;
strv_free(ret);
diff --git a/src/basic/strv.c b/src/basic/strv.c
index d44a72fc48..eaf440a4b2 100644
--- a/src/basic/strv.c
+++ b/src/basic/strv.c
@@ -278,7 +278,7 @@ char **strv_split_newlines(const char *s) {
return l;
}
-int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
+int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) {
size_t n = 0, allocated = 0;
_cleanup_strv_free_ char **l = NULL;
int r;
@@ -289,11 +289,12 @@ int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags) {
for (;;) {
_cleanup_free_ char *word = NULL;
- r = unquote_first_word(&s, &word, flags);
+ r = extract_first_word(&s, &word, separators, flags);
if (r < 0)
return r;
- if (r == 0)
+ if (r == 0) {
break;
+ }
if (!GREEDY_REALLOC(l, allocated, n + 2))
return -ENOMEM;
@@ -693,6 +694,26 @@ char **strv_reverse(char **l) {
return l;
}
+char **strv_shell_escape(char **l, const char *bad) {
+ char **s;
+
+ /* Escapes every character in every string in l that is in bad,
+ * edits in-place, does not roll-back on error. */
+
+ STRV_FOREACH(s, l) {
+ char *v;
+
+ v = shell_escape(*s, bad);
+ if (!v)
+ return NULL;
+
+ free(*s);
+ *s = v;
+ }
+
+ return l;
+}
+
bool strv_fnmatch(char* const* patterns, const char *s, int flags) {
char* const* p;
diff --git a/src/basic/strv.h b/src/basic/strv.h
index 22f8f98fda..f07da8cdf3 100644
--- a/src/basic/strv.h
+++ b/src/basic/strv.h
@@ -73,7 +73,7 @@ static inline bool strv_isempty(char * const *l) {
char **strv_split(const char *s, const char *separator);
char **strv_split_newlines(const char *s);
-int strv_split_quoted(char ***t, const char *s, UnquoteFlags flags);
+int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags);
char *strv_join(char **l, const char *separator);
char *strv_join_quoted(char **l);
@@ -145,6 +145,7 @@ void strv_print(char **l);
}))
char **strv_reverse(char **l);
+char **strv_shell_escape(char **l, const char *bad);
bool strv_fnmatch(char* const* patterns, const char *s, int flags);
diff --git a/src/basic/util.c b/src/basic/util.c
index d4d3d3c83a..ebfc6c6a72 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -4843,7 +4843,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
_cleanup_free_ char *word = NULL;
char *value = NULL;
- r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
return r;
if (r == 0)
@@ -4883,7 +4883,7 @@ int get_proc_cmdline_key(const char *key, char **value) {
_cleanup_free_ char *word = NULL;
const char *e;
- r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
return r;
if (r == 0)
@@ -5698,7 +5698,7 @@ int is_device_node(const char *path) {
return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
}
-int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
+int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
_cleanup_free_ char *s = NULL;
size_t allocated = 0, sz = 0;
int r;
@@ -5711,13 +5711,19 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
SINGLE_QUOTE_ESCAPE,
DOUBLE_QUOTE,
DOUBLE_QUOTE_ESCAPE,
- SPACE,
+ SEPARATOR,
} state = START;
assert(p);
- assert(*p);
assert(ret);
+ if (!separators)
+ separators = WHITESPACE;
+
+ /* Bail early if called after last value or with no input */
+ if (!*p)
+ goto finish_force_terminate;
+
/* Parses the first word of a string, and returns it in
* *ret. Removes all quotes in the process. When parsing fails
* (because of an uneven number of quotes or similar), leaves
@@ -5729,32 +5735,46 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
switch (state) {
case START:
- if (c == 0)
- goto finish;
- else if (strchr(WHITESPACE, c))
+ if (c == 0) {
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
+ if (!GREEDY_REALLOC(s, allocated, sz+1))
+ return -ENOMEM;
+ goto finish_force_terminate;
+ } else if (strchr(separators, c)) {
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
+ if (!GREEDY_REALLOC(s, allocated, sz+1))
+ return -ENOMEM;
+ (*p) ++;
+ goto finish_force_next;
+ }
break;
+ }
state = VALUE;
/* fallthrough */
case VALUE:
if (c == 0)
- goto finish;
- else if (c == '\'') {
+ goto finish_force_terminate;
+ else if (c == '\'' && (flags & EXTRACT_QUOTES)) {
if (!GREEDY_REALLOC(s, allocated, sz+1))
return -ENOMEM;
state = SINGLE_QUOTE;
} else if (c == '\\')
state = VALUE_ESCAPE;
- else if (c == '\"') {
+ else if (c == '\"' && (flags & EXTRACT_QUOTES)) {
if (!GREEDY_REALLOC(s, allocated, sz+1))
return -ENOMEM;
state = DOUBLE_QUOTE;
- } else if (strchr(WHITESPACE, c))
- state = SPACE;
- else {
+ } else if (strchr(separators, c)) {
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
+ (*p) ++;
+ goto finish_force_next;
+ }
+ state = SEPARATOR;
+ } else {
if (!GREEDY_REALLOC(s, allocated, sz+2))
return -ENOMEM;
@@ -5765,8 +5785,8 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
case SINGLE_QUOTE:
if (c == 0) {
- if (flags & UNQUOTE_RELAX)
- goto finish;
+ if (flags & EXTRACT_RELAX)
+ goto finish_force_terminate;
return -EINVAL;
} else if (c == '\'')
state = VALUE;
@@ -5804,29 +5824,29 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
return -ENOMEM;
if (c == 0) {
- if ((flags & UNQUOTE_CUNESCAPE_RELAX) &&
- (state == VALUE_ESCAPE || flags & UNQUOTE_RELAX)) {
+ if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
+ (state == VALUE_ESCAPE || flags & EXTRACT_RELAX)) {
/* If we find an unquoted trailing backslash and we're in
- * UNQUOTE_CUNESCAPE_RELAX mode, keep it verbatim in the
+ * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
* output.
*
- * Unbalanced quotes will only be allowed in UNQUOTE_RELAX
- * mode, UNQUOTE_CUNESCAP_RELAX mode does not allow them.
+ * Unbalanced quotes will only be allowed in EXTRACT_RELAX
+ * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
*/
s[sz++] = '\\';
- goto finish;
+ goto finish_force_terminate;
}
- if (flags & UNQUOTE_RELAX)
- goto finish;
+ if (flags & EXTRACT_RELAX)
+ goto finish_force_terminate;
return -EINVAL;
}
- if (flags & UNQUOTE_CUNESCAPE) {
+ if (flags & EXTRACT_CUNESCAPE) {
uint32_t u;
r = cunescape_one(*p, (size_t) -1, &c, &u);
if (r < 0) {
- if (flags & UNQUOTE_CUNESCAPE_RELAX) {
+ if (flags & EXTRACT_CUNESCAPE_RELAX) {
s[sz++] = '\\';
s[sz++] = c;
goto end_escape;
@@ -5849,24 +5869,29 @@ end_escape:
VALUE;
break;
- case SPACE:
+ case SEPARATOR:
if (c == 0)
+ goto finish_force_terminate;
+ if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
+ goto finish_force_next;
+ if (!strchr(separators, c))
goto finish;
- if (!strchr(WHITESPACE, c))
- goto finish;
-
break;
}
(*p) ++;
}
+finish_force_terminate:
+ *p = NULL;
finish:
if (!s) {
+ *p = NULL;
*ret = NULL;
return 0;
}
+finish_force_next:
s[sz] = 0;
*ret = s;
s = NULL;
@@ -5874,26 +5899,27 @@ finish:
return 1;
}
-int unquote_first_word_and_warn(
+int extract_first_word_and_warn(
const char **p,
char **ret,
- UnquoteFlags flags,
+ const char *separators,
+ ExtractFlags flags,
const char *unit,
const char *filename,
unsigned line,
const char *rvalue) {
/* Try to unquote it, if it fails, warn about it and try again but this
- * time using UNQUOTE_CUNESCAPE_RELAX to keep the backslashes verbatim
+ * time using EXTRACT_CUNESCAPE_RELAX to keep the backslashes verbatim
* in invalid escape sequences. */
const char *save;
int r;
save = *p;
- r = unquote_first_word(p, ret, flags);
- if (r < 0 && !(flags&UNQUOTE_CUNESCAPE_RELAX)) {
- /* Retry it with UNQUOTE_CUNESCAPE_RELAX. */
+ r = extract_first_word(p, ret, separators, flags);
+ if (r < 0 && !(flags&EXTRACT_CUNESCAPE_RELAX)) {
+ /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
*p = save;
- r = unquote_first_word(p, ret, flags|UNQUOTE_CUNESCAPE_RELAX);
+ r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
if (r < 0)
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Unbalanced quoting in command line, ignoring: \"%s\"", rvalue);
@@ -5904,7 +5930,7 @@ int unquote_first_word_and_warn(
return r;
}
-int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
+int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) {
va_list ap;
char **l;
int n = 0, i, c, r;
@@ -5930,7 +5956,7 @@ int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
l = newa0(char*, n);
for (c = 0; c < n; c++) {
- r = unquote_first_word(p, &l[c], flags);
+ r = extract_first_word(p, &l[c], separators, flags);
if (r < 0) {
int j;
@@ -6511,6 +6537,32 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
return 0;
}
+static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
+ assert(bad);
+
+ for (; *s; s++) {
+ if (*s == '\\' || strchr(bad, *s))
+ *(t++) = '\\';
+
+ *(t++) = *s;
+ }
+
+ return t;
+}
+
+char *shell_escape(const char *s, const char *bad) {
+ char *r, *t;
+
+ r = new(char, strlen(s)*2+1);
+ if (!r)
+ return NULL;
+
+ t = strcpy_backslash_escaped(r, s, bad);
+ *t = 0;
+
+ return r;
+}
+
char *shell_maybe_quote(const char *s) {
const char *p;
char *r, *t;
@@ -6537,13 +6589,7 @@ char *shell_maybe_quote(const char *s) {
*(t++) = '"';
t = mempcpy(t, s, p - s);
- for (; *p; p++) {
-
- if (strchr(SHELL_NEED_ESCAPE, *p))
- *(t++) = '\\';
-
- *(t++) = *p;
- }
+ t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE);
*(t++)= '"';
*t = 0;
diff --git a/src/basic/util.h b/src/basic/util.h
index 426b7f7d16..098f9edcc1 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -854,15 +854,17 @@ int is_symlink(const char *path);
int is_dir(const char *path, bool follow);
int is_device_node(const char *path);
-typedef enum UnquoteFlags {
- UNQUOTE_RELAX = 1,
- UNQUOTE_CUNESCAPE = 2,
- UNQUOTE_CUNESCAPE_RELAX = 4,
-} UnquoteFlags;
-
-int unquote_first_word(const char **p, char **ret, UnquoteFlags flags);
-int unquote_first_word_and_warn(const char **p, char **ret, UnquoteFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue);
-int unquote_many_words(const char **p, UnquoteFlags flags, ...) _sentinel_;
+typedef enum ExtractFlags {
+ EXTRACT_RELAX = 1,
+ EXTRACT_CUNESCAPE = 2,
+ EXTRACT_CUNESCAPE_RELAX = 4,
+ EXTRACT_QUOTES = 8,
+ EXTRACT_DONT_COALESCE_SEPARATORS = 16,
+} ExtractFlags;
+
+int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags);
+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);
+int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_;
static inline void free_and_replace(char **s, char *v) {
free(*s);
@@ -917,6 +919,7 @@ void cmsg_close_all(struct msghdr *mh);
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath);
+char *shell_escape(const char *s, const char *bad);
char *shell_maybe_quote(const char *s);
int parse_mode(const char *s, mode_t *ret);
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 299172123e..e1e3a5ffb7 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -552,7 +552,7 @@ int config_parse_exec(
semicolon = false;
- r = unquote_first_word_and_warn(&p, &firstword, UNQUOTE_CUNESCAPE, unit, filename, line, rvalue);
+ r = extract_first_word_and_warn(&p, &firstword, WHITESPACE, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
if (r <= 0)
return 0;
@@ -614,7 +614,7 @@ int config_parse_exec(
path_kill_slashes(path);
- for (;;) {
+ while (!isempty(p)) {
_cleanup_free_ char *word = NULL;
/* Check explicitly for an unquoted semicolon as
@@ -627,7 +627,7 @@ int config_parse_exec(
}
/* Check for \; explicitly, to not confuse it with \\;
- * or "\;" or "\\;" etc. unquote_first_word would
+ * or "\;" or "\\;" etc. extract_first_word would
* return the same for all of those. */
if (p[0] == '\\' && p[1] == ';' && (!p[2] || strchr(WHITESPACE, p[2]))) {
p += 2;
@@ -642,7 +642,7 @@ int config_parse_exec(
continue;
}
- r = unquote_first_word_and_warn(&p, &word, UNQUOTE_CUNESCAPE, unit, filename, line, rvalue);
+ r = extract_first_word_and_warn(&p, &word, WHITESPACE, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
if (r == 0)
break;
else if (r < 0)
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index e3bd76051b..cdcda1cb52 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -148,7 +148,7 @@ static int spawn_getter(const char *getter, const char *url) {
_cleanup_strv_free_ char **words = NULL;
assert(getter);
- r = strv_split_quoted(&words, getter, 0);
+ r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_QUOTES);
if (r < 0)
return log_error_errno(r, "Failed to split getter option: %m");
diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c
index 87898449e6..79747e058b 100644
--- a/src/libsystemd/sd-bus/bus-convenience.c
+++ b/src/libsystemd/sd-bus/bus-convenience.c
@@ -107,15 +107,17 @@ _public_ int sd_bus_call_method(
_cleanup_bus_message_unref_ sd_bus_message *m = NULL;
int r;
- assert_return(bus, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
+ bus_assert_return(bus, -EINVAL, error);
+ bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
+ if (!BUS_IS_OPEN(bus->state)) {
+ r = -ENOTCONN;
+ goto fail;
+ }
r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
if (r < 0)
- return r;
+ goto fail;
if (!isempty(types)) {
va_list ap;
@@ -124,10 +126,13 @@ _public_ int sd_bus_call_method(
r = bus_message_append_ap(m, types, ap);
va_end(ap);
if (r < 0)
- return r;
+ goto fail;
}
return sd_bus_call(bus, m, 0, error, reply);
+
+fail:
+ return sd_bus_error_set_errno(error, r);
}
_public_ int sd_bus_reply_method_return(
@@ -289,15 +294,17 @@ _public_ int sd_bus_get_property(
sd_bus_message *rep = NULL;
int r;
- assert_return(bus, -EINVAL);
- assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(reply, -EINVAL);
- assert_return(signature_is_single(type, false), -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
+ bus_assert_return(bus, -EINVAL, error);
+ bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
+ bus_assert_return(member_name_is_valid(member), -EINVAL, error);
+ bus_assert_return(reply, -EINVAL, error);
+ bus_assert_return(signature_is_single(type, false), -EINVAL, error);
+ bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
+ if (!BUS_IS_OPEN(bus->state)) {
+ r = -ENOTCONN;
+ goto fail;
+ }
r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
if (r < 0)
@@ -306,11 +313,14 @@ _public_ int sd_bus_get_property(
r = sd_bus_message_enter_container(rep, 'v', type);
if (r < 0) {
sd_bus_message_unref(rep);
- return r;
+ goto fail;
}
*reply = rep;
return 0;
+
+fail:
+ return sd_bus_error_set_errno(error, r);
}
_public_ int sd_bus_get_property_trivial(
@@ -325,15 +335,17 @@ _public_ int sd_bus_get_property_trivial(
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
int r;
- assert_return(bus, -EINVAL);
- assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(bus_type_is_trivial(type), -EINVAL);
- assert_return(ptr, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
+ bus_assert_return(bus, -EINVAL, error);
+ bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
+ bus_assert_return(member_name_is_valid(member), -EINVAL, error);
+ bus_assert_return(bus_type_is_trivial(type), -EINVAL, error);
+ bus_assert_return(ptr, -EINVAL, error);
+ bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
+ if (!BUS_IS_OPEN(bus->state)) {
+ r = -ENOTCONN;
+ goto fail;
+ }
r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
if (r < 0)
@@ -341,13 +353,16 @@ _public_ int sd_bus_get_property_trivial(
r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
if (r < 0)
- return r;
+ goto fail;
r = sd_bus_message_read_basic(reply, type, ptr);
if (r < 0)
- return r;
+ goto fail;
return 0;
+
+fail:
+ return sd_bus_error_set_errno(error, r);
}
_public_ int sd_bus_get_property_string(
@@ -364,14 +379,16 @@ _public_ int sd_bus_get_property_string(
char *n;
int r;
- assert_return(bus, -EINVAL);
- assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
+ bus_assert_return(bus, -EINVAL, error);
+ bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
+ bus_assert_return(member_name_is_valid(member), -EINVAL, error);
+ bus_assert_return(ret, -EINVAL, error);
+ bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
+ if (!BUS_IS_OPEN(bus->state)) {
+ r = -ENOTCONN;
+ goto fail;
+ }
r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
if (r < 0)
@@ -379,18 +396,23 @@ _public_ int sd_bus_get_property_string(
r = sd_bus_message_enter_container(reply, 'v', "s");
if (r < 0)
- return r;
+ goto fail;
r = sd_bus_message_read_basic(reply, 's', &s);
if (r < 0)
- return r;
+ goto fail;
n = strdup(s);
- if (!n)
- return -ENOMEM;
+ if (!n) {
+ r = -ENOMEM;
+ goto fail;
+ }
*ret = n;
return 0;
+
+fail:
+ return sd_bus_error_set_errno(error, r);
}
_public_ int sd_bus_get_property_strv(
@@ -405,14 +427,16 @@ _public_ int sd_bus_get_property_strv(
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
int r;
- assert_return(bus, -EINVAL);
- assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
+ bus_assert_return(bus, -EINVAL, error);
+ bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
+ bus_assert_return(member_name_is_valid(member), -EINVAL, error);
+ bus_assert_return(ret, -EINVAL, error);
+ bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
+ if (!BUS_IS_OPEN(bus->state)) {
+ r = -ENOTCONN;
+ goto fail;
+ }
r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
if (r < 0)
@@ -420,13 +444,16 @@ _public_ int sd_bus_get_property_strv(
r = sd_bus_message_enter_container(reply, 'v', NULL);
if (r < 0)
- return r;
+ goto fail;
r = sd_bus_message_read_strv(reply, ret);
if (r < 0)
- return r;
+ goto fail;
return 0;
+
+fail:
+ return sd_bus_error_set_errno(error, r);
}
_public_ int sd_bus_set_property(
@@ -442,38 +469,43 @@ _public_ int sd_bus_set_property(
va_list ap;
int r;
- assert_return(bus, -EINVAL);
- assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
- assert_return(member_name_is_valid(member), -EINVAL);
- assert_return(signature_is_single(type, false), -EINVAL);
- assert_return(!bus_pid_changed(bus), -ECHILD);
+ bus_assert_return(bus, -EINVAL, error);
+ bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
+ bus_assert_return(member_name_is_valid(member), -EINVAL, error);
+ bus_assert_return(signature_is_single(type, false), -EINVAL, error);
+ bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
+ if (!BUS_IS_OPEN(bus->state)) {
+ r = -ENOTCONN;
+ goto fail;
+ }
r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set");
if (r < 0)
- return r;
+ goto fail;
r = sd_bus_message_append(m, "ss", strempty(interface), member);
if (r < 0)
- return r;
+ goto fail;
r = sd_bus_message_open_container(m, 'v', type);
if (r < 0)
- return r;
+ goto fail;
va_start(ap, type);
r = bus_message_append_ap(m, type, ap);
va_end(ap);
if (r < 0)
- return r;
+ goto fail;
r = sd_bus_message_close_container(m);
if (r < 0)
- return r;
+ goto fail;
return sd_bus_call(bus, m, 0, error, NULL);
+
+fail:
+ return sd_bus_error_set_errno(error, r);
}
_public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index d420a7594f..9b68fdd642 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -419,3 +419,9 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error);
bool is_kdbus_wanted(void);
bool is_kdbus_available(void);
+
+#define bus_assert_return(expr, r, error) \
+ do { \
+ if (!assert_log(expr)) \
+ return sd_bus_error_set_errno(error, r); \
+ } while (false)
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 763f830e2d..b9e2782564 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -1951,37 +1951,39 @@ _public_ int sd_bus_call(
unsigned i;
int r;
- assert_return(m, -EINVAL);
- assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
- assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL);
- assert_return(!bus_error_is_dirty(error), -EINVAL);
+ bus_assert_return(m, -EINVAL, error);
+ bus_assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL, error);
+ bus_assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL, error);
+ bus_assert_return(!bus_error_is_dirty(error), -EINVAL, error);
if (!bus)
bus = m->bus;
- assert_return(!bus_pid_changed(bus), -ECHILD);
- assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS);
+ bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
+ bus_assert_return(!bus->is_kernel || !(bus->hello_flags & KDBUS_HELLO_MONITOR), -EROFS, error);
- if (!BUS_IS_OPEN(bus->state))
- return -ENOTCONN;
+ if (!BUS_IS_OPEN(bus->state)) {
+ r = -ENOTCONN;
+ goto fail;
+ }
r = bus_ensure_running(bus);
if (r < 0)
- return r;
+ goto fail;
i = bus->rqueue_size;
r = bus_seal_message(bus, m, usec);
if (r < 0)
- return r;
+ goto fail;
r = bus_remarshal_message(bus, &m);
if (r < 0)
- return r;
+ goto fail;
r = bus_send_internal(bus, m, &cookie, true);
if (r < 0)
- return r;
+ goto fail;
timeout = calc_elapse(m->timeout);
@@ -2012,14 +2014,17 @@ _public_ int sd_bus_call(
}
r = sd_bus_error_setf(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry.");
+ sd_bus_message_unref(incoming);
+ return r;
- } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR)
+ } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR) {
r = sd_bus_error_copy(error, &incoming->error);
- else
+ sd_bus_message_unref(incoming);
+ return r;
+ } else {
r = -EIO;
-
- sd_bus_message_unref(incoming);
- return r;
+ goto fail;
+ }
} else if (BUS_MESSAGE_COOKIE(incoming) == cookie &&
bus->unique_name &&
@@ -2035,7 +2040,8 @@ _public_ int sd_bus_call(
* immediately. */
sd_bus_message_unref(incoming);
- return -ELOOP;
+ r = -ELOOP;
+ goto fail;
}
/* Try to read more, right-away */
@@ -2046,10 +2052,10 @@ _public_ int sd_bus_call(
if (r < 0) {
if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
bus_enter_closing(bus);
- return -ECONNRESET;
+ r = -ECONNRESET;
}
- return r;
+ goto fail;
}
if (r > 0)
continue;
@@ -2058,8 +2064,10 @@ _public_ int sd_bus_call(
usec_t n;
n = now(CLOCK_MONOTONIC);
- if (n >= timeout)
- return -ETIMEDOUT;
+ if (n >= timeout) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
left = timeout - n;
} else
@@ -2067,20 +2075,25 @@ _public_ int sd_bus_call(
r = bus_poll(bus, true, left);
if (r < 0)
- return r;
- if (r == 0)
- return -ETIMEDOUT;
+ goto fail;
+ if (r == 0) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
r = dispatch_wqueue(bus);
if (r < 0) {
if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) {
bus_enter_closing(bus);
- return -ECONNRESET;
+ r = -ECONNRESET;
}
- return r;
+ goto fail;
}
}
+
+fail:
+ return sd_bus_error_set_errno(error, r);
}
_public_ int sd_bus_get_fd(sd_bus *bus) {
diff --git a/src/locale/localed.c b/src/locale/localed.c
index cb4052fdd5..e9489f04c2 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -222,7 +222,7 @@ static int x11_read_data(Context *c) {
if (in_section && first_word(l, "Option")) {
_cleanup_strv_free_ char **a = NULL;
- r = strv_split_quoted(&a, l, 0);
+ r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES);
if (r < 0)
return r;
@@ -245,7 +245,7 @@ static int x11_read_data(Context *c) {
} else if (!in_section && first_word(l, "Section")) {
_cleanup_strv_free_ char **a = NULL;
- r = strv_split_quoted(&a, l, 0);
+ r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES);
if (r < 0)
return -ENOMEM;
@@ -544,7 +544,7 @@ static int read_next_mapping(const char* filename,
if (l[0] == 0 || l[0] == '#')
continue;
- r = strv_split_quoted(&b, l, 0);
+ r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_QUOTES);
if (r < 0)
return r;
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 0e74f20f1a..347260013a 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -655,17 +655,22 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_BIND:
case ARG_BIND_RO: {
+ const char *current = optarg;
_cleanup_free_ char *source = NULL, *destination = NULL;
CustomMount *m;
- char *e;
+ _cleanup_strv_free_ char **strv = NULL;
- e = strchr(optarg, ':');
- if (e) {
- source = strndup(optarg, e - optarg);
- destination = strdup(e + 1);
- } else {
- source = strdup(optarg);
- destination = strdup(optarg);
+ r = extract_many_words(&current, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL);
+ switch (r) {
+ case 1:
+ destination = strdup(source);
+ case 2:
+ break;
+ case -ENOMEM:
+ return log_oom();
+ default:
+ log_error("Invalid bind mount specification: %s", optarg);
+ return -EINVAL;
}
if (!source || !destination)
@@ -690,18 +695,21 @@ static int parse_argv(int argc, char *argv[]) {
}
case ARG_TMPFS: {
+ const char *current = optarg;
_cleanup_free_ char *path = NULL, *opts = NULL;
CustomMount *m;
- char *e;
- e = strchr(optarg, ':');
- if (e) {
- path = strndup(optarg, e - optarg);
- opts = strdup(e + 1);
- } else {
- path = strdup(optarg);
- opts = strdup("mode=0755");
+ r = extract_first_word(&current, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r == -ENOMEM)
+ return log_oom();
+ else if (r < 0) {
+ log_error("Invalid tmpfs specification: %s", optarg);
+ return r;
}
+ if (r)
+ opts = strdup(current);
+ else
+ opts = strdup("mode=0755");
if (!path || !opts)
return log_oom();
@@ -731,9 +739,13 @@ static int parse_argv(int argc, char *argv[]) {
unsigned n = 0;
char **i;
- lower = strv_split(optarg, ":");
- if (!lower)
+ r = strv_split_extract(&lower, optarg, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ if (r == -ENOMEM)
return log_oom();
+ else if (r < 0) {
+ log_error("Invalid overlay specification: %s", optarg);
+ return r;
+ }
STRV_FOREACH(i, lower) {
if (!path_is_absolute(*i)) {
@@ -1227,6 +1239,21 @@ static int mount_tmpfs(const char *dest, CustomMount *m) {
return 0;
}
+static char *joined_and_escaped_lower_dirs(char * const *lower) {
+ _cleanup_strv_free_ char **sv = NULL;
+
+ sv = strv_copy(lower);
+ if (!sv)
+ return NULL;
+
+ strv_reverse(sv);
+
+ if (!strv_shell_escape(sv, ",:"))
+ return NULL;
+
+ return strv_join(sv, ":");
+}
+
static int mount_overlay(const char *dest, CustomMount *m) {
_cleanup_free_ char *lower = NULL;
const char *where, *options;
@@ -1243,19 +1270,32 @@ static int mount_overlay(const char *dest, CustomMount *m) {
(void) mkdir_p_label(m->source, 0755);
- strv_reverse(m->lower);
- lower = strv_join(m->lower, ":");
- strv_reverse(m->lower);
+ lower = joined_and_escaped_lower_dirs(m->lower);
if (!lower)
return log_oom();
- if (m->read_only)
- options = strjoina("lowerdir=", m->source, ":", lower);
- else {
+ if (m->read_only) {
+ _cleanup_free_ char *escaped_source = NULL;
+
+ escaped_source = shell_escape(m->source, ",:");
+ if (!escaped_source)
+ return log_oom();
+
+ options = strjoina("lowerdir=", escaped_source, ":", lower);
+ } else {
+ _cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL;
+
assert(m->work_dir);
(void) mkdir_label(m->work_dir, 0700);
- options = strjoina("lowerdir=", lower, ",upperdir=", m->source, ",workdir=", m->work_dir);
+ escaped_source = shell_escape(m->source, ",:");
+ if (!escaped_source)
+ return log_oom();
+ escaped_work_dir = shell_escape(m->work_dir, ",:");
+ if (!escaped_work_dir)
+ return log_oom();
+
+ options = strjoina("lowerdir=", lower, ",upperdir=", escaped_source, ",workdir=", escaped_work_dir);
}
if (mount("overlay", where, "overlay", m->read_only ? MS_RDONLY : 0, options) < 0)
diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c
index ab96cb231e..69069cc75d 100644
--- a/src/nss-myhostname/nss-myhostname.c
+++ b/src/nss-myhostname/nss-myhostname.c
@@ -39,7 +39,6 @@
#define LOCALADDRESS_IPV4 (htonl(0x7F000002))
#define LOCALADDRESS_IPV6 &in6addr_loopback
-#define LOOPBACK_INTERFACE "lo"
NSS_GETHOSTBYNAME_PROTOTYPES(myhostname);
NSS_GETHOSTBYADDR_PROTOTYPES(myhostname);
@@ -117,7 +116,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
}
/* If this call fails we fill in 0 as scope. Which is fine */
- lo_ifi = n_addresses <= 0 ? if_nametoindex(LOOPBACK_INTERFACE) : 0;
+ lo_ifi = n_addresses <= 0 ? LOOPBACK_IFINDEX : 0;
l = strlen(canonical);
ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2);
diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c
index 0edba415b6..7525d7c32f 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve-host/resolve-host.c
@@ -41,7 +41,8 @@ static uint16_t arg_class = 0;
static bool arg_legend = true;
static uint64_t arg_flags = 0;
-static void print_source(int ifindex, uint64_t flags) {
+static void print_source(int ifindex, uint64_t flags, usec_t rtt) {
+ char rtt_str[FORMAT_TIMESTAMP_MAX];
if (!arg_legend)
return;
@@ -62,6 +63,10 @@ static void print_source(int ifindex, uint64_t flags) {
printf(" interface %s", strna(if_indextoname(ifindex, ifname)));
}
+ assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100));
+
+ printf(" in %s", rtt_str);
+
fputc('.', stdout);
fputc('\n', stdout);
}
@@ -74,6 +79,7 @@ static int resolve_host(sd_bus *bus, const char *name) {
unsigned c = 0;
int r, ifindex;
uint64_t flags;
+ usec_t ts;
assert(name);
@@ -93,12 +99,16 @@ static int resolve_host(sd_bus *bus, const char *name) {
if (r < 0)
return bus_log_create_error(r);
+ ts = now(CLOCK_MONOTONIC);
+
r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
return r;
}
+ ts = now(CLOCK_MONOTONIC) - ts;
+
r = sd_bus_message_read(reply, "i", &ifindex);
if (r < 0)
return bus_log_parse_error(r);
@@ -182,7 +192,7 @@ static int resolve_host(sd_bus *bus, const char *name) {
return -ESRCH;
}
- print_source(ifindex, flags);
+ print_source(ifindex, flags, ts);
return 0;
}
@@ -195,6 +205,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
uint64_t flags;
unsigned c = 0;
const char *n;
+ usec_t ts;
int r;
assert(bus);
@@ -243,12 +254,16 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
if (r < 0)
return bus_log_create_error(r);
+ ts = now(CLOCK_MONOTONIC);
+
r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r));
return r;
}
+ ts = now(CLOCK_MONOTONIC) - ts;
+
r = sd_bus_message_read(reply, "i", &ifindex);
if (r < 0)
return bus_log_parse_error(r);
@@ -283,7 +298,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
return -ESRCH;
}
- print_source(ifindex, flags);
+ print_source(ifindex, flags, ts);
return 0;
}
@@ -321,6 +336,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
unsigned n = 0;
uint64_t flags;
int r, ifindex;
+ usec_t ts;
assert(name);
@@ -336,21 +352,21 @@ static int resolve_record(sd_bus *bus, const char *name) {
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_set_auto_start(req, false);
- if (r < 0)
- return bus_log_create_error(r);
-
assert((uint16_t) arg_type == arg_type);
r = sd_bus_message_append(req, "isqqt", arg_ifindex, name, arg_class, arg_type, arg_flags);
if (r < 0)
return bus_log_create_error(r);
+ ts = now(CLOCK_MONOTONIC);
+
r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
if (r < 0) {
log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
return r;
}
+ ts = now(CLOCK_MONOTONIC) - ts;
+
r = sd_bus_message_read(reply, "i", &ifindex);
if (r < 0)
return bus_log_parse_error(r);
@@ -418,7 +434,7 @@ static int resolve_record(sd_bus *bus, const char *name) {
return -ESRCH;
}
- print_source(ifindex, flags);
+ print_source(ifindex, flags, ts);
return 0;
}
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 9ffaf4b19f..81ea1cafcb 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -95,14 +95,19 @@ void dns_cache_flush(DnsCache *c) {
c->by_expiry = prioq_free(c->by_expiry);
}
-static void dns_cache_remove(DnsCache *c, DnsResourceKey *key) {
+static bool dns_cache_remove(DnsCache *c, DnsResourceKey *key) {
DnsCacheItem *i;
+ bool exist = false;
assert(c);
assert(key);
- while ((i = hashmap_get(c->by_key, key)))
+ while ((i = hashmap_get(c->by_key, key))) {
dns_cache_item_remove_and_free(c, i);
+ exist = true;
+ }
+
+ return exist;
}
static void dns_cache_make_space(DnsCache *c, unsigned add) {
@@ -263,6 +268,7 @@ static int dns_cache_put_positive(
const union in_addr_union *owner_address) {
_cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
+ _cleanup_free_ char *key_str = NULL;
DnsCacheItem *existing;
int r;
@@ -272,7 +278,14 @@ static int dns_cache_put_positive(
/* New TTL is 0? Delete the entry... */
if (rr->ttl <= 0) {
- dns_cache_remove(c, rr->key);
+ if (dns_cache_remove(c, rr->key)) {
+ r = dns_resource_key_to_string(rr->key, &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("Removed zero TTL entry from cache: %s", key_str);
+ }
+
return 0;
}
@@ -311,6 +324,12 @@ static int dns_cache_put_positive(
if (r < 0)
return r;
+ r = dns_resource_key_to_string(i->key, &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("Added cache entry for %s", key_str);
+
i = NULL;
return 0;
}
@@ -325,6 +344,7 @@ static int dns_cache_put_negative(
const union in_addr_union *owner_address) {
_cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL;
+ _cleanup_free_ char *key_str = NULL;
int r;
assert(c);
@@ -337,8 +357,15 @@ static int dns_cache_put_negative(
return 0;
if (key->type == DNS_TYPE_ANY)
return 0;
- if (soa_ttl <= 0)
+ if (soa_ttl <= 0) {
+ r = dns_resource_key_to_string(key, &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("Ignored negative cache entry with zero SOA TTL: %s", key_str);
+
return 0;
+ }
if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN))
return 0;
@@ -364,6 +391,12 @@ static int dns_cache_put_negative(
if (r < 0)
return r;
+ r = dns_resource_key_to_string(i->key, &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str);
+
i = NULL;
return 0;
}
@@ -468,11 +501,19 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) {
}
for (i = 0; i < q->n_keys; i++) {
+ _cleanup_free_ char *key_str = NULL;
DnsCacheItem *j;
if (q->keys[i]->type == DNS_TYPE_ANY ||
q->keys[i]->class == DNS_CLASS_ANY) {
/* If we have ANY lookups we simply refresh */
+
+ r = dns_resource_key_to_string(q->keys[i], &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("Ignoring cache for ANY lookup: %s", key_str);
+
*ret = NULL;
*rcode = 0;
return 0;
@@ -481,9 +522,24 @@ int dns_cache_lookup(DnsCache *c, DnsQuestion *q, int *rcode, DnsAnswer **ret) {
j = hashmap_get(c->by_key, q->keys[i]);
if (!j) {
/* If one question cannot be answered we need to refresh */
+
+ r = dns_resource_key_to_string(q->keys[i], &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("Cache miss for %s", key_str);
+
*ret = NULL;
*rcode = 0;
return 0;
+ } else {
+ r = dns_resource_key_to_string(j->key, &key_str);
+ if (r < 0)
+ return r;
+
+ log_debug("%s cache hit for %s",
+ j->type == DNS_CACHE_POSITIVE ? "Positive" :
+ (j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN"), key_str);
}
LIST_FOREACH(by_key, j, j) {
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 39951a362c..35ad899544 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -1712,7 +1712,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) {
if (r < 0)
goto fail;
- r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL);
+ r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL);
if (r < 0)
goto fail;
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index 418d9721ef..3ada442006 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "hostname-util.h"
+#include "dns-domain.h"
#include "resolved-dns-query.h"
@@ -212,6 +214,156 @@ static int dns_query_add_transaction_split(DnsQuery *q, DnsScope *s) {
return 0;
}
+static int SYNTHESIZE_IFINDEX(int ifindex) {
+
+ /* When the caller asked for resolving on a specific interface,
+ * we synthesize the answer for that interface. However, if
+ * nothing specific was claimed, we synthesize the answer for
+ * localhost. */
+
+ if (ifindex > 0)
+ return ifindex;
+
+ return LOOPBACK_IFINDEX;
+}
+
+static int SYNTHESIZE_FAMILY(uint64_t flags) {
+
+ /* Picks an address family depending on set flags. This is
+ * purely for synthesized answers, where the family we return
+ * for the reply should match what was requested in the
+ * question, even though we are synthesizing the answer
+ * here. */
+
+ if (!(flags & SD_RESOLVED_DNS)) {
+ if (flags & SD_RESOLVED_LLMNR_IPV4)
+ return AF_INET;
+ if (flags & SD_RESOLVED_LLMNR_IPV6)
+ return AF_INET6;
+ }
+
+ return AF_UNSPEC;
+}
+
+static DnsProtocol SYNTHESIZE_PROTOCOL(uint64_t flags) {
+
+ /* Similar as SYNTHESIZE_FAMILY() but does this for the
+ * protocol. If resolving via DNS was requested, we claim it
+ * was DNS. Similar, if nothing specific was
+ * requested. However, if only resolving via LLMNR was
+ * requested we return that. */
+
+ if (flags & SD_RESOLVED_DNS)
+ return DNS_PROTOCOL_DNS;
+ if (flags & SD_RESOLVED_LLMNR)
+ return DNS_PROTOCOL_LLMNR;
+
+ return DNS_PROTOCOL_DNS;
+}
+
+static void dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ unsigned i;
+ int r;
+
+ assert(q);
+ assert(state);
+
+ /* Tries to synthesize localhost RR replies where appropriate */
+
+ if (!IN_SET(*state,
+ DNS_TRANSACTION_FAILURE,
+ DNS_TRANSACTION_NO_SERVERS,
+ DNS_TRANSACTION_TIMEOUT,
+ DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
+ return;
+
+ for (i = 0; i < q->question->n_keys; i++) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+ const char *name;
+
+ if (q->question->keys[i]->class != DNS_CLASS_IN &&
+ q->question->keys[i]->class != DNS_CLASS_ANY)
+ continue;
+
+ name = DNS_RESOURCE_KEY_NAME(q->question->keys[i]);
+
+ if (is_localhost(name)) {
+
+ switch (q->question->keys[i]->type) {
+
+ case DNS_TYPE_A:
+ case DNS_TYPE_ANY:
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, name);
+ if (!rr) {
+ log_oom();
+ return;
+ }
+
+ rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK);
+ break;
+
+ case DNS_TYPE_AAAA:
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, name);
+ if (!rr) {
+ log_oom();
+ return;
+ }
+
+ rr->aaaa.in6_addr = in6addr_loopback;
+ break;
+ }
+
+ } else if (IN_SET(q->question->keys[i]->type, DNS_TYPE_PTR, DNS_TYPE_ANY) &&
+ (dns_name_endswith(name, "127.in-addr.arpa") > 0 ||
+ dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)) {
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, name);
+ if (!rr) {
+ log_oom();
+ return;
+ }
+
+ rr->ptr.name = strdup("localhost");
+ if (!rr->ptr.name) {
+ log_oom();
+ return;
+ }
+ }
+
+ if (!rr)
+ continue;
+
+ if (!answer) {
+ answer = dns_answer_new(q->question->n_keys);
+ if (!answer) {
+ log_oom();
+ return;
+ }
+ }
+
+ r = dns_answer_add(answer, rr);
+ if (r < 0) {
+ log_error_errno(r, "Failed to add synthetic RR to answer: %m");
+ return;
+ }
+ }
+
+ if (!answer)
+ return;
+
+ dns_answer_unref(q->answer);
+ q->answer = answer;
+ answer = NULL;
+
+ q->answer_ifindex = SYNTHESIZE_IFINDEX(q->ifindex);
+ q->answer_family = SYNTHESIZE_FAMILY(q->flags);
+ q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
+ q->answer_rcode = DNS_RCODE_SUCCESS;
+
+ *state = DNS_TRANSACTION_SUCCESS;
+}
+
int dns_query_go(DnsQuery *q) {
DnsScopeMatch found = DNS_SCOPE_NO;
DnsScope *s, *first = NULL;
@@ -253,8 +405,17 @@ int dns_query_go(DnsQuery *q) {
}
}
- if (found == DNS_SCOPE_NO)
+ if (found == DNS_SCOPE_NO) {
+ DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
+
+ dns_query_synthesize_reply(q, &state);
+ if (state != DNS_TRANSACTION_NO_SERVERS) {
+ dns_query_complete(q, state);
+ return 1;
+ }
+
return -ESRCH;
+ }
r = dns_query_add_transaction_split(q, first);
if (r < 0)
@@ -428,6 +589,9 @@ void dns_query_ready(DnsQuery *q) {
q->answer_family = scope ? scope->family : AF_UNSPEC;
}
+ /* Try to synthesize a reply if we couldn't resolve something. */
+ dns_query_synthesize_reply(q, &state);
+
dns_query_complete(q, state);
}
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index b8414da87e..57d9071dfc 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -325,10 +325,6 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
if ((SD_RESOLVED_FLAGS_MAKE(s->protocol, s->family) & flags) == 0)
return DNS_SCOPE_NO;
- STRV_FOREACH(i, s->domains)
- if (dns_name_endswith(domain, *i) > 0)
- return DNS_SCOPE_YES;
-
if (dns_name_root(domain) != 0)
return DNS_SCOPE_NO;
@@ -340,6 +336,10 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
return DNS_SCOPE_NO;
+ STRV_FOREACH(i, s->domains)
+ if (dns_name_endswith(domain, *i) > 0)
+ return DNS_SCOPE_YES;
+
if (s->protocol == DNS_PROTOCOL_DNS) {
if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 &&
dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 &&
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 53779f3372..2d9d1a47ee 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -624,7 +624,6 @@ int dns_transaction_go(DnsTransaction *t) {
if (r < 0)
return r;
if (r > 0) {
- log_debug("Cache hit!");
if (t->cached_rcode == DNS_RCODE_SUCCESS)
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
else
@@ -661,8 +660,6 @@ int dns_transaction_go(DnsTransaction *t) {
return 0;
}
- log_debug("Cache miss!");
-
/* Otherwise, we need to ask the network */
r = dns_transaction_make_packet(t);
if (r == -EDOM) {
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 24871b0dae..f58e84a3d0 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -101,7 +101,7 @@ static int condition_test_kernel_command_line(Condition *c) {
_cleanup_free_ char *word = NULL;
bool found;
- r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
return r;
if (r == 0)
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 3a92d120d2..b5e09cad26 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -1380,7 +1380,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
/* Parse columns */
p = buffer;
- r = unquote_many_words(&p, 0, &action, &name, &id, &description, &home, NULL);
+ r = extract_many_words(&p, NULL, EXTRACT_QUOTES, &action, &name, &id, &description, &home, NULL);
if (r < 0) {
log_error("[%s:%u] Syntax error.", fname, line);
return r;
@@ -1389,7 +1389,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
log_error("[%s:%u] Missing action and name columns.", fname, line);
return -EINVAL;
}
- if (*p != 0) {
+ if (!isempty(p)) {
log_error("[%s:%u] Trailing garbage.", fname, line);
return -EINVAL;
}
diff --git a/src/test/test-strv.c b/src/test/test-strv.c
index 6e3c81395c..bff43950a9 100644
--- a/src/test/test-strv.c
+++ b/src/test/test-strv.c
@@ -165,7 +165,7 @@ static void test_strv_quote_unquote(const char* const *split, const char *quoted
assert_se(p);
assert_se(streq(p, quoted));
- r = strv_split_quoted(&s, quoted, 0);
+ r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES);
assert_se(r == 0);
assert_se(s);
STRV_FOREACH(t, s) {
@@ -182,7 +182,7 @@ static void test_strv_unquote(const char *quoted, char **list) {
char **t;
int r;
- r = strv_split_quoted(&s, quoted, 0);
+ r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES);
assert_se(r == 0);
assert_se(s);
j = strv_join(s, " | ");
@@ -199,7 +199,7 @@ static void test_invalid_unquote(const char *quoted) {
char **s = NULL;
int r;
- r = strv_split_quoted(&s, quoted, 0);
+ r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES);
assert_se(s == NULL);
assert_se(r == -EINVAL);
}
@@ -219,6 +219,21 @@ static void test_strv_split(void) {
}
}
+static void test_strv_split_extract(void) {
+ _cleanup_strv_free_ char **l = NULL;
+ const char *str = ":foo\\:bar::waldo:";
+ int r;
+
+ r = strv_split_extract(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+ assert_se(r == 0);
+ assert_se(streq_ptr(l[0], ""));
+ assert_se(streq_ptr(l[1], "foo:bar"));
+ assert_se(streq_ptr(l[2], ""));
+ assert_se(streq_ptr(l[3], "waldo"));
+ assert_se(streq_ptr(l[4], ""));
+ assert_se(streq_ptr(l[5], NULL));
+}
+
static void test_strv_split_newlines(void) {
unsigned i = 0;
char **s;
@@ -542,6 +557,18 @@ static void test_strv_reverse(void) {
assert_se(streq_ptr(d[3], NULL));
}
+static void test_strv_shell_escape(void) {
+ _cleanup_strv_free_ char **v = NULL;
+
+ v = strv_new("foo:bar", "bar,baz", "wal\\do", NULL);
+ assert_se(v);
+ assert_se(strv_shell_escape(v, ",:"));
+ assert_se(streq_ptr(v[0], "foo\\:bar"));
+ assert_se(streq_ptr(v[1], "bar\\,baz"));
+ assert_se(streq_ptr(v[2], "wal\\\\do"));
+ assert_se(streq_ptr(v[3], NULL));
+}
+
int main(int argc, char *argv[]) {
test_specifier_printf();
test_strv_foreach();
@@ -583,6 +610,7 @@ int main(int argc, char *argv[]) {
test_invalid_unquote("'x'y'g");
test_strv_split();
+ test_strv_split_extract();
test_strv_split_newlines();
test_strv_split_nulstr();
test_strv_parse_nulstr();
@@ -598,6 +626,7 @@ int main(int argc, char *argv[]) {
test_strv_equal();
test_strv_is_uniq();
test_strv_reverse();
+ test_strv_shell_escape();
return 0;
}
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 03e18df080..3c1bac4606 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -1487,350 +1487,448 @@ static void test_execute_directory(void) {
(void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL);
}
-static void test_unquote_first_word(void) {
+static void test_extract_first_word(void) {
const char *p, *original;
char *t;
p = original = "foobar waldo";
- assert_se(unquote_first_word(&p, &t, 0) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
assert_se(streq(t, "foobar"));
free(t);
assert_se(p == original + 7);
- assert_se(unquote_first_word(&p, &t, 0) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
assert_se(streq(t, "waldo"));
free(t);
- assert_se(p == original + 12);
+ assert_se(isempty(p));
- assert_se(unquote_first_word(&p, &t, 0) == 0);
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
assert_se(!t);
- assert_se(p == original + 12);
+ assert_se(isempty(p));
p = original = "\"foobar\" \'waldo\'";
- assert_se(unquote_first_word(&p, &t, 0) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "\"foobar\""));
+ free(t);
+ assert_se(p == original + 9);
+
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "\'waldo\'"));
+ free(t);
+ assert_se(isempty(p));
+
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
+ assert_se(!t);
+ assert_se(isempty(p));
+
+ p = original = "\"foobar\" \'waldo\'";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
assert_se(streq(t, "foobar"));
free(t);
assert_se(p == original + 9);
- assert_se(unquote_first_word(&p, &t, 0) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
assert_se(streq(t, "waldo"));
free(t);
- assert_se(p == original + 16);
+ assert_se(isempty(p));
- assert_se(unquote_first_word(&p, &t, 0) == 0);
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 0);
assert_se(!t);
- assert_se(p == original + 16);
+ assert_se(isempty(p));
p = original = "\"";
- assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
+ assert_se(streq(t, "\""));
+ assert_se(isempty(p));
+
+ p = original = "\"";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
assert_se(p == original + 1);
p = original = "\'";
- assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
+ assert_se(streq(t, "\'"));
+ assert_se(isempty(p));
+
+ p = original = "\'";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
assert_se(p == original + 1);
p = original = "\'fooo";
- assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, 0) == 1);
+ assert_se(streq(t, "\'fooo"));
+ assert_se(isempty(p));
+
+ p = original = "\'fooo";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
assert_se(p == original + 5);
p = original = "\'fooo";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
assert_se(streq(t, "fooo"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "yay\'foo\'bar";
- assert_se(unquote_first_word(&p, &t, 0) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
+ assert_se(streq(t, "yay\'foo\'bar"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "yay\'foo\'bar";
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
assert_se(streq(t, "yayfoobar"));
free(t);
- assert_se(p == original + 11);
+ assert_se(isempty(p));
p = original = " foobar ";
- assert_se(unquote_first_word(&p, &t, 0) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
assert_se(streq(t, "foobar"));
free(t);
- assert_se(p == original + 12);
+ assert_se(isempty(p));
p = original = " foo\\ba\\x6ar ";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
assert_se(streq(t, "foo\ba\x6ar"));
free(t);
- assert_se(p == original + 13);
+ assert_se(isempty(p));
p = original = " foo\\ba\\x6ar ";
- assert_se(unquote_first_word(&p, &t, 0) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, 0) > 0);
assert_se(streq(t, "foobax6ar"));
free(t);
- assert_se(p == original + 13);
+ assert_se(isempty(p));
p = original = " f\\u00f6o \"pi\\U0001F4A9le\" ";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) > 0);
assert_se(streq(t, "föo"));
free(t);
assert_se(p == original + 13);
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE) > 0);
assert_se(streq(t, "pi\360\237\222\251le"));
free(t);
- assert_se(p == original + 32);
+ assert_se(isempty(p));
p = original = "fooo\\";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
assert_se(streq(t, "fooo"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "fooo\\";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "fooo\\"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "fooo\\";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
assert_se(streq(t, "fooo\\"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "fooo\\";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "fooo\\"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "\"foo\\";
- assert_se(unquote_first_word(&p, &t, 0) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, 0) == -EINVAL);
assert_se(p == original + 5);
p = original = "\"foo\\";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
+ assert_se(streq(t, "foo"));
+ free(t);
+ assert_se(isempty(p));
+
+ p = original = "foo::bar";
+ assert_se(extract_first_word(&p, &t, ":", 0) == 1);
assert_se(streq(t, "foo"));
free(t);
assert_se(p == original + 5);
+ assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+ assert_se(streq(t, "bar"));
+ free(t);
+ assert_se(isempty(p));
+
+ assert_se(extract_first_word(&p, &t, ":", 0) == 0);
+ assert_se(!t);
+ assert_se(isempty(p));
+
+ p = original = "foo\\:bar::waldo";
+ assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+ assert_se(streq(t, "foo:bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ assert_se(extract_first_word(&p, &t, ":", 0) == 1);
+ assert_se(streq(t, "waldo"));
+ free(t);
+ assert_se(isempty(p));
+
+ assert_se(extract_first_word(&p, &t, ":", 0) == 0);
+ assert_se(!t);
+ assert_se(isempty(p));
+
p = original = "\"foo\\";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE_RELAX) == -EINVAL);
assert_se(p == original + 5);
p = original = "\"foo\\";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
assert_se(streq(t, "foo\\"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "\"foo\\";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
assert_se(streq(t, "foo\\"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "fooo\\ bar quux";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_RELAX) > 0);
assert_se(streq(t, "fooo bar"));
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "fooo bar"));
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE_RELAX|UNQUOTE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
assert_se(streq(t, "fooo bar"));
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) == -EINVAL);
assert_se(p == original + 5);
p = original = "fooo\\ bar quux";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "fooo\\ bar"));
free(t);
assert_se(p == original + 10);
p = original = "\\w+@\\K[\\d.]+";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE) == -EINVAL);
assert_se(p == original + 1);
p = original = "\\w+@\\K[\\d.]+";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "\\w+@\\K[\\d.]+"));
free(t);
- assert_se(p == original + 12);
+ assert_se(isempty(p));
p = original = "\\w+\\b";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_CUNESCAPE_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0);
assert_se(streq(t, "\\w+\b"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "-N ''";
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
assert_se(streq(t, "-N"));
free(t);
assert_se(p == original + 3);
- assert_se(unquote_first_word(&p, &t, UNQUOTE_CUNESCAPE) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
assert_se(streq(t, ""));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
+
+ p = original = ":foo\\:bar::waldo:";
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+ assert_se(t);
+ assert_se(streq(t, ""));
+ free(t);
+ assert_se(p == original + 1);
+
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+ assert_se(streq(t, "foo:bar"));
+ free(t);
+ assert_se(p == original + 10);
+
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+ assert_se(t);
+ assert_se(streq(t, ""));
+ free(t);
+ assert_se(p == original + 11);
+
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+ assert_se(streq(t, "waldo"));
+ free(t);
+ assert_se(p == original + 17);
+
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 1);
+ assert_se(streq(t, ""));
+ free(t);
+ assert_se(p == NULL);
+
+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_DONT_COALESCE_SEPARATORS) == 0);
+ assert_se(!t);
+ assert_se(!p);
}
-static void test_unquote_first_word_and_warn(void) {
+static void test_extract_first_word_and_warn(void) {
const char *p, *original;
char *t;
p = original = "foobar waldo";
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foobar"));
free(t);
assert_se(p == original + 7);
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "waldo"));
free(t);
- assert_se(p == original + 12);
+ assert_se(isempty(p));
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) == 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) == 0);
assert_se(!t);
- assert_se(p == original + 12);
+ assert_se(isempty(p));
p = original = "\"foobar\" \'waldo\'";
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foobar"));
free(t);
assert_se(p == original + 9);
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "waldo"));
free(t);
- assert_se(p == original + 16);
+ assert_se(isempty(p));
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) == 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) == 0);
assert_se(!t);
- assert_se(p == original + 16);
+ assert_se(isempty(p));
p = original = "\"";
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
assert_se(p == original + 1);
p = original = "\'";
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
assert_se(p == original + 1);
p = original = "\'fooo";
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
assert_se(p == original + 5);
p = original = "\'fooo";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "fooo"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = " foo\\ba\\x6ar ";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foo\ba\x6ar"));
free(t);
- assert_se(p == original + 13);
+ assert_se(isempty(p));
p = original = " foo\\ba\\x6ar ";
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foobax6ar"));
free(t);
- assert_se(p == original + 13);
+ assert_se(isempty(p));
p = original = " f\\u00f6o \"pi\\U0001F4A9le\" ";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "föo"));
free(t);
assert_se(p == original + 13);
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "pi\360\237\222\251le"));
free(t);
- assert_se(p == original + 32);
+ assert_se(isempty(p));
p = original = "fooo\\";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "fooo"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "fooo\\";
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "fooo\\"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "fooo\\";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "fooo\\"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "\"foo\\";
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
assert_se(p == original + 5);
p = original = "\"foo\\";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foo"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "\"foo\\";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_CUNESCAPE, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) == -EINVAL);
assert_se(p == original + 5);
p = original = "\"foo\\";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_CUNESCAPE|UNQUOTE_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foo"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
p = original = "fooo\\ bar quux";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "fooo bar"));
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
- assert_se(unquote_first_word_and_warn(&p, &t, 0, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "fooo bar"));
free(t);
assert_se(p == original + 10);
p = original = "fooo\\ bar quux";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "fooo\\ bar"));
free(t);
assert_se(p == original + 10);
p = original = "\\w+@\\K[\\d.]+";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "\\w+@\\K[\\d.]+"));
free(t);
- assert_se(p == original + 12);
+ assert_se(isempty(p));
p = original = "\\w+\\b";
- assert_se(unquote_first_word_and_warn(&p, &t, UNQUOTE_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "\\w+\b"));
free(t);
- assert_se(p == original + 5);
+ assert_se(isempty(p));
}
-static void test_unquote_many_words(void) {
+static void test_extract_many_words(void) {
const char *p, *original;
char *a, *b, *c;
p = original = "foobar waldi piep";
- assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 3);
- assert_se(p == original + 17);
+ assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 3);
+ assert_se(isempty(p));
assert_se(streq_ptr(a, "foobar"));
assert_se(streq_ptr(b, "waldi"));
assert_se(streq_ptr(c, "piep"));
@@ -1839,8 +1937,17 @@ static void test_unquote_many_words(void) {
free(c);
p = original = "'foobar' wa\"ld\"i ";
- assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 2);
- assert_se(p == original + 19);
+ assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 2);
+ assert_se(isempty(p));
+ assert_se(streq_ptr(a, "'foobar'"));
+ assert_se(streq_ptr(b, "wa\"ld\"i"));
+ assert_se(streq_ptr(c, NULL));
+ free(a);
+ free(b);
+
+ p = original = "'foobar' wa\"ld\"i ";
+ assert_se(extract_many_words(&p, NULL, EXTRACT_QUOTES, &a, &b, &c, NULL) == 2);
+ assert_se(isempty(p));
assert_se(streq_ptr(a, "foobar"));
assert_se(streq_ptr(b, "waldi"));
assert_se(streq_ptr(c, NULL));
@@ -1848,32 +1955,32 @@ static void test_unquote_many_words(void) {
free(b);
p = original = "";
- assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 0);
- assert_se(p == original);
+ assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 0);
+ assert_se(isempty(p));
assert_se(streq_ptr(a, NULL));
assert_se(streq_ptr(b, NULL));
assert_se(streq_ptr(c, NULL));
p = original = " ";
- assert_se(unquote_many_words(&p, 0, &a, &b, &c, NULL) == 0);
- assert_se(p == original+2);
+ assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 0);
+ assert_se(isempty(p));
assert_se(streq_ptr(a, NULL));
assert_se(streq_ptr(b, NULL));
assert_se(streq_ptr(c, NULL));
p = original = "foobar";
- assert_se(unquote_many_words(&p, 0, NULL) == 0);
+ assert_se(extract_many_words(&p, NULL, 0, NULL) == 0);
assert_se(p == original);
p = original = "foobar waldi";
- assert_se(unquote_many_words(&p, 0, &a, NULL) == 1);
+ assert_se(extract_many_words(&p, NULL, 0, &a, NULL) == 1);
assert_se(p == original+7);
assert_se(streq_ptr(a, "foobar"));
free(a);
p = original = " foobar ";
- assert_se(unquote_many_words(&p, 0, &a, NULL) == 1);
- assert_se(p == original+15);
+ assert_se(extract_many_words(&p, NULL, 0, &a, NULL) == 1);
+ assert_se(isempty(p));
assert_se(streq_ptr(a, "foobar"));
free(a);
}
@@ -1993,6 +2100,21 @@ static void test_sparse_write(void) {
test_sparse_write_one(fd, test_e, sizeof(test_e));
}
+static void test_shell_escape_one(const char *s, const char *bad, const char *expected) {
+ _cleanup_free_ char *r;
+
+ assert_se(r = shell_escape(s, bad));
+ assert_se(streq_ptr(r, expected));
+}
+
+static void test_shell_escape(void) {
+ test_shell_escape_one("", "", "");
+ test_shell_escape_one("\\", "", "\\\\");
+ test_shell_escape_one("foobar", "", "foobar");
+ test_shell_escape_one("foobar", "o", "f\\o\\obar");
+ test_shell_escape_one("foo:bar,baz", ",:", "foo\\:bar\\,baz");
+}
+
static void test_shell_maybe_quote_one(const char *s, const char *expected) {
_cleanup_free_ char *r;
@@ -2149,14 +2271,15 @@ int main(int argc, char *argv[]) {
test_search_and_fopen_nulstr();
test_glob_exists();
test_execute_directory();
- test_unquote_first_word();
- test_unquote_first_word_and_warn();
- test_unquote_many_words();
+ test_extract_first_word();
+ test_extract_first_word_and_warn();
+ test_extract_many_words();
test_parse_proc_cmdline();
test_raw_clone();
test_same_fd();
test_uid_ptr();
test_sparse_write();
+ test_shell_escape();
test_shell_maybe_quote();
test_parse_mode();
test_tempfn();
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index d6f7801561..8f29256c6d 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -662,7 +662,7 @@ static int parse_xattrs_from_arg(Item *i) {
for (;;) {
_cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL, *xattr_replaced = NULL;
- r = unquote_first_word(&p, &xattr, UNQUOTE_CUNESCAPE);
+ r = extract_first_word(&p, &xattr, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
if (r < 0)
log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
if (r <= 0)
@@ -1760,9 +1760,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
assert(line >= 1);
assert(buffer);
- r = unquote_many_words(
+ r = extract_many_words(
&buffer,
- 0,
+ NULL,
+ EXTRACT_QUOTES,
&action,
&path,
&mode,