diff options
36 files changed, 669 insertions, 624 deletions
diff --git a/.clang-format b/.clang-format index ab27960a67..8e5cfca535 100644 --- a/.clang-format +++ b/.clang-format @@ -74,9 +74,6 @@ ForEachMacros: - FOREACH_INOTIFY_EVENT - FOREACH_STRING - FOREACH_SUBSYSTEM - - _FOREACH_WORD - - FOREACH_WORD - - FOREACH_WORD_SEPARATOR - HASHMAP_FOREACH - HASHMAP_FOREACH_IDX - HASHMAP_FOREACH_KEY diff --git a/man/sd_machine_get_class.xml b/man/sd_machine_get_class.xml index cd259c863f..a8db371230 100644 --- a/man/sd_machine_get_class.xml +++ b/man/sd_machine_get_class.xml @@ -35,7 +35,7 @@ <funcprototype> <funcdef>int <function>sd_machine_get_ifindices</function></funcdef> <paramdef>const char* <parameter>machine</parameter></paramdef> - <paramdef>int **<parameter>ifindices</parameter></paramdef> + <paramdef>int **<parameter>ret_ifindices</parameter></paramdef> </funcprototype> </funcsynopsis> </refsynopsisdiv> @@ -53,21 +53,22 @@ project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after use.</para> - <para><function>sd_machine_get_ifindices()</function> may be used - to determine the numeric indices of the network interfaces on the - host that are pointing towards the specified locally running - virtual machine or container that is registered with + <para><function>sd_machine_get_ifindices()</function> may be used to determine the numeric indices of the + network interfaces on the host that are pointing towards the specified locally running virtual machine or + container. The vm or container must be registered with <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. - The returned array needs to be freed with the libc <citerefentry - project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> - call after use.</para> + The output parameter <parameter>ret_ifindices</parameter> may be passed as <constant>NULL</constant> when + the output value is not needed. The returned array needs to be freed with the libc <citerefentry + project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after + use.</para> </refsect1> <refsect1> <title>Return Value</title> - <para>On success, these calls return 0 or a positive integer. On failure, these calls return a negative - errno-style error code.</para> + <para>On success, these functions return a non-negative integer. + <function>sd_machine_get_ifindices()</function> returns the number of the relevant network interfaces. + On failure, these calls return a negative errno-style error code.</para> <refsect2> <title>Errors</title> diff --git a/man/sd_seat_get_active.xml b/man/sd_seat_get_active.xml index cf70b35785..94401caa72 100644 --- a/man/sd_seat_get_active.xml +++ b/man/sd_seat_get_active.xml @@ -38,9 +38,9 @@ <funcprototype> <funcdef>int <function>sd_seat_get_sessions</function></funcdef> <paramdef>const char *<parameter>seat</parameter></paramdef> - <paramdef>char ***<parameter>sessions</parameter></paramdef> - <paramdef>uid_t **<parameter>uid</parameter></paramdef> - <paramdef>unsigned int *<parameter>n_uids</parameter></paramdef> + <paramdef>char ***<parameter>ret_sessions</parameter></paramdef> + <paramdef>uid_t **<parameter>ret_uids</parameter></paramdef> + <paramdef>unsigned int *<parameter>ret_n_uids</parameter></paramdef> </funcprototype> <funcprototype> @@ -68,21 +68,16 @@ <citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after use.</para> - <para><function>sd_seat_get_sessions()</function> may be used to - determine all sessions on the specified seat. Returns two arrays, - one (<constant>NULL</constant> terminated) with the session - identifiers of the sessions and one with the user identifiers of - the Unix users the sessions belong to. An additional parameter may - be used to return the number of entries in the latter array. This - value is the same the return value, if the latter is nonnegative. - The two arrays and the last parameter may be passed as - <constant>NULL</constant> in case these values need not to be - determined. The arrays and the strings referenced by them need to - be freed with the libc - <citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> - call after use. Note that instead of an empty array - <constant>NULL</constant> may be returned and should be considered - equivalent to an empty array.</para> + <para><function>sd_seat_get_sessions()</function> may be used to determine all sessions on the specified + seat. Returns two arrays, one (<constant>NULL</constant> terminated) with the session identifiers of the + sessions and one with the user identifiers of the Unix users the sessions belong to. An additional + parameter may be used to return the number of entries in the latter array. This value is the same as the + return value if the return value is nonnegative. The output parameters may be passed as + <constant>NULL</constant> in case these output values are not needed. The arrays and the strings + referenced by them need to be freed with the libc <citerefentry + project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry> call after + use. Note that instead of an empty array <constant>NULL</constant> may be returned and should be + considered equivalent to an empty array.</para> <para><function>sd_seat_can_tty()</function> may be used to determine whether a specific seat provides TTY functionality, i.e. diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index e94fcfad02..6210347553 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -652,14 +652,13 @@ int cg_remove_xattr(const char *controller, const char *path, const char *name) return 0; } -int cg_pid_get_path(const char *controller, pid_t pid, char **path) { +int cg_pid_get_path(const char *controller, pid_t pid, char **ret_path) { _cleanup_fclose_ FILE *f = NULL; const char *fs, *controller_str; int unified, r; - size_t cs = 0; - assert(path); assert(pid >= 0); + assert(ret_path); if (controller) { if (!cg_controller_is_valid(controller)) @@ -675,8 +674,6 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) { controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY; else controller_str = controller; - - cs = strlen(controller_str); } fs = procfs_file_alloca(pid, "cgroup"); @@ -688,13 +685,13 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) { for (;;) { _cleanup_free_ char *line = NULL; - char *e, *p; + char *e; r = read_line(f, LONG_LINE_MAX, &line); if (r < 0) return r; if (r == 0) - break; + return -ENODATA; if (unified) { e = startswith(line, "0:"); @@ -706,9 +703,6 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) { continue; } else { char *l; - size_t k; - const char *word, *state; - bool found = false; l = strchr(line, ':'); if (!l) @@ -718,31 +712,27 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) { e = strchr(l, ':'); if (!e) continue; - *e = 0; - FOREACH_WORD_SEPARATOR(word, k, l, ",", state) - if (k == cs && memcmp(word, controller_str, cs) == 0) { - found = true; - break; - } - if (!found) + + r = string_contains_word(l, ",", controller_str); + if (r < 0) + return r; + if (r == 0) continue; } - p = strdup(e + 1); - if (!p) + char *path = strdup(e + 1); + if (!path) return -ENOMEM; /* Truncate suffix indicating the process is a zombie */ - e = endswith(p, " (deleted)"); + e = endswith(path, " (deleted)"); if (e) *e = 0; - *path = p; + *ret_path = path; return 0; } - - return -ENODATA; } int cg_install_release_agent(const char *controller, const char *agent) { diff --git a/src/basic/env-util.c b/src/basic/env-util.c index b8dc98915f..179408c399 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -687,7 +687,7 @@ char **replace_env_argv(char **argv, char **env) { if (e) { int r; - r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE); + r = strv_split_full(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE); if (r < 0) { ret[k] = NULL; strv_free(ret); diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index 1a53da334a..d64dddd641 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -14,6 +14,7 @@ #include "log.h" #include "macro.h" #include "string-util.h" +#include "strv.h" #include "utf8.h" int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { diff --git a/src/basic/macro.h b/src/basic/macro.h index ceea8176f5..41c2c3289e 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -538,6 +538,9 @@ static inline int __coverity_check_and_return__(int condition) { (y) = (_t); \ } while (false) +#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) +#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) + /* Iterates through a specified list of pointers. Accepts NULL pointers, but uses (void*) -1 as internal marker for EOL. */ #define FOREACH_POINTER(p, x, ...) \ for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, (void*) -1 }; \ diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 755a37f667..ab725d0dab 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -8,12 +8,14 @@ #include "alloc-util.h" #include "escape.h" +#include "extract-word.h" #include "fileio.h" #include "gunicode.h" #include "locale-util.h" #include "macro.h" #include "memory-util.h" #include "string-util.h" +#include "strv.h" #include "terminal-util.h" #include "utf8.h" #include "util.h" @@ -110,83 +112,6 @@ char* first_word(const char *s, const char *word) { return (char*) p; } -static size_t strcspn_escaped(const char *s, const char *reject) { - bool escaped = false; - int n; - - for (n = 0; s[n] != '\0'; n++) { - if (escaped) - escaped = false; - else if (s[n] == '\\') - escaped = true; - else if (strchr(reject, s[n])) - break; - } - - return n; -} - -/* Split a string into words. */ -const char* split( - const char **state, - size_t *l, - const char *separator, - SplitFlags flags) { - - const char *current; - - assert(state); - assert(l); - - if (!separator) - separator = WHITESPACE; - - current = *state; - - if (*current == '\0') /* already at the end? */ - return NULL; - - current += strspn(current, separator); /* skip leading separators */ - if (*current == '\0') { /* at the end now? */ - *state = current; - return NULL; - } - - if (FLAGS_SET(flags, SPLIT_QUOTES)) { - - if (strchr(QUOTES, *current)) { - /* We are looking at a quote */ - *l = strcspn_escaped(current + 1, CHAR_TO_STR(*current)); - if (current[*l + 1] != *current || - (current[*l + 2] != 0 && !strchr(separator, current[*l + 2]))) { - /* right quote missing or garbage at the end */ - if (FLAGS_SET(flags, SPLIT_RELAX)) { - *state = current + *l + 1 + (current[*l + 1] != '\0'); - return current + 1; - } - *state = current; - return NULL; - } - *state = current++ + *l + 2; - - } else { - /* We are looking at a something that is not a quote */ - *l = strcspn_escaped(current, separator); - if (current[*l] && !strchr(separator, current[*l]) && !FLAGS_SET(flags, SPLIT_RELAX)) { - /* unfinished escape */ - *state = current; - return NULL; - } - *state = current + *l; - } - } else { - *l = strcspn(current, separator); - *state = current + *l; - } - - return current; -} - char *strnappend(const char *s, const char *suffix, size_t b) { size_t a; char *r; @@ -1207,3 +1132,30 @@ int string_extract_line(const char *s, size_t i, char **ret) { c++; } } + +int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word) { + /* In the default mode with no separators specified, we split on whitespace and + * don't coalesce separators. */ + const ExtractFlags flags = separators ? EXTRACT_DONT_COALESCE_SEPARATORS : 0; + + const char *found = NULL; + + for (const char *p = string;;) { + _cleanup_free_ char *w = NULL; + int r; + + r = extract_first_word(&p, &w, separators, flags); + if (r < 0) + return r; + if (r == 0) + break; + + found = strv_find(words, w); + if (found) + break; + } + + if (ret_word) + *ret_word = found; + return !!found; +} diff --git a/src/basic/string-util.h b/src/basic/string-util.h index cf8c74b822..cefbda3577 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -108,24 +108,6 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_; char *first_word(const char *s, const char *word) _pure_; -typedef enum SplitFlags { - SPLIT_QUOTES = 0x01 << 0, - SPLIT_RELAX = 0x01 << 1, -} SplitFlags; - -/* Smelly. Do not use this anymore. Use extract_first_word() instead! */ -const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags); - -/* Similar, don't use this anymore */ -#define FOREACH_WORD(word, length, s, state) \ - _FOREACH_WORD(word, length, s, WHITESPACE, 0, state) - -#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ - _FOREACH_WORD(word, length, s, separator, 0, state) - -#define _FOREACH_WORD(word, length, s, separator, flags, state) \ - for ((state) = (s), (word) = split(&(state), &(length), (separator), (flags)); (word); (word) = split(&(state), &(length), (separator), (flags))) - char *strnappend(const char *s, const char *suffix, size_t length); char *strjoin_real(const char *x, ...) _sentinel_; @@ -280,3 +262,8 @@ char* string_erase(char *x); int string_truncate_lines(const char *s, size_t n_lines, char **ret); int string_extract_line(const char *s, size_t i, char **ret); + +int string_contains_word_strv(const char *string, const char *separators, char **words, const char **ret_word); +static inline int string_contains_word(const char *string, const char *separators, const char *word) { + return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL); +} diff --git a/src/basic/strv.c b/src/basic/strv.c index a172ca2fe9..e4ecf405b7 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -256,44 +256,6 @@ int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) { return 0; } -char **strv_split_full(const char *s, const char *separator, SplitFlags flags) { - const char *word, *state; - size_t l; - size_t n, i; - char **r; - - assert(s); - - if (!separator) - separator = WHITESPACE; - - s += strspn(s, separator); - if (isempty(s)) - return new0(char*, 1); - - n = 0; - _FOREACH_WORD(word, l, s, separator, flags, state) - n++; - - r = new(char*, n+1); - if (!r) - return NULL; - - i = 0; - _FOREACH_WORD(word, l, s, separator, flags, state) { - r[i] = strndup(word, l); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - } - - r[i] = NULL; - return r; -} - char **strv_split_newlines(const char *s) { char **l; size_t n; @@ -317,7 +279,7 @@ char **strv_split_newlines(const char *s) { return l; } -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) { +int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags) { _cleanup_strv_free_ char **l = NULL; size_t n = 0, allocated = 0; int r; diff --git a/src/basic/strv.h b/src/basic/strv.h index e57dfff69b..9468edc6a6 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -72,13 +72,19 @@ static inline bool strv_isempty(char * const *l) { return !l || !*l; } -char **strv_split_full(const char *s, const char *separator, SplitFlags flags); -static inline char **strv_split(const char *s, const char *separator) { - return strv_split_full(s, separator, 0); -} char **strv_split_newlines(const char *s); -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); +int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags); +static inline char **strv_split(const char *s, const char *separators) { + char **ret; + int r; + + r = strv_split_full(&ret, s, separators, 0); + if (r < 0) + return NULL; + + return ret; +} /* Given a string containing white-space separated tuples of words themselves separated by ':', * returns a vector of strings. If the second element in a tuple is missing, the corresponding @@ -123,10 +129,6 @@ bool strv_overlap(char * const *a, char * const *b) _pure_; char **strv_sort(char **l); void strv_print(char * const *l); -#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) - -#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) - #define strv_from_stdarg_alloca(first) \ ({ \ char **_l; \ diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 633c7ca106..404a85e1ae 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -4438,15 +4438,13 @@ int config_parse_set_status( void *data, void *userdata) { - size_t l; - const char *word, *state; - int r; ExitStatusSet *status_set = data; + int r; assert(filename); assert(lvalue); assert(rvalue); - assert(data); + assert(status_set); /* Empty assignment resets the list */ if (isempty(rvalue)) { @@ -4454,25 +4452,26 @@ int config_parse_set_status( return 0; } - FOREACH_WORD(word, l, rvalue, state) { - _cleanup_free_ char *temp; + for (const char *p = rvalue;;) { + _cleanup_free_ char *word = NULL; Bitmap *bitmap; - temp = strndup(word, l); - if (!temp) - return log_oom(); + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + return log_error_errno(r, "Failed to parse %s: %m", lvalue); + if (r == 0) + return 0; /* We need to call exit_status_from_string() first, because we want * to parse numbers as exit statuses, not signals. */ - r = exit_status_from_string(temp); + r = exit_status_from_string(word); if (r >= 0) { assert(r >= 0 && r < 256); bitmap = &status_set->status; } else { - r = signal_from_string(temp); - - if (r <= 0) { + r = signal_from_string(word); + if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse value, ignoring: %s", word); continue; @@ -4484,10 +4483,6 @@ int config_parse_set_status( if (r < 0) return log_error_errno(r, "Failed to set signal or status %s: %m", word); } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); - - return 0; } int config_parse_namespace_path_strv( diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index f9e627da46..7d0571f147 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -288,19 +288,19 @@ static int parse_one_option(const char *option) { } static int parse_options(const char *options) { - const char *word, *state; - size_t l; - int r; - assert(options); - FOREACH_WORD_SEPARATOR(word, l, options, ",", state) { - _cleanup_free_ char *o; + for (;;) { + _cleanup_free_ char *word = NULL; + int r; + + r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return log_debug_errno(r, "Failed to parse options: %m"); + if (r == 0) + break; - o = strndup(word, l); - if (!o) - return -ENOMEM; - r = parse_one_option(o); + r = parse_one_option(word); if (r < 0) return r; } diff --git a/src/delta/delta.c b/src/delta/delta.c index acb6020715..a88eed098d 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -541,28 +541,33 @@ static int help(void) { } static int parse_flags(const char *flag_str, int flags) { - const char *word, *state; - size_t l; + for (;;) { + _cleanup_free_ char *word = NULL; + int r; - FOREACH_WORD_SEPARATOR(word, l, flag_str, ",", state) { - if (strneq("masked", word, l)) + r = extract_first_word(&flag_str, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + return flags; + + if (streq(word, "masked")) flags |= SHOW_MASKED; - else if (strneq ("equivalent", word, l)) + else if (streq(word, "equivalent")) flags |= SHOW_EQUIVALENT; - else if (strneq("redirected", word, l)) + else if (streq(word, "redirected")) flags |= SHOW_REDIRECTED; - else if (strneq("overridden", word, l)) + else if (streq(word, "overridden")) flags |= SHOW_OVERRIDDEN; - else if (strneq("unchanged", word, l)) + else if (streq(word, "unchanged")) flags |= SHOW_UNCHANGED; - else if (strneq("extended", word, l)) + else if (streq(word, "extended")) flags |= SHOW_EXTENDED; - else if (strneq("default", word, l)) + else if (streq(word, "default")) flags |= SHOW_DEFAULTS; else return -EINVAL; } - return flags; } static int parse_argv(int argc, char *argv[]) { diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index be38612060..04dcacfd59 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -99,89 +99,85 @@ static int verify_tty(const char *name) { return 0; } -static int run(const char *dest, const char *dest_early, const char *dest_late) { - _cleanup_free_ char *active = NULL; - const char *j; +static int run_container(void) { + _cleanup_free_ char *container_ttys = NULL; int r; - assert_se(arg_dest = dest); + log_debug("Automatically adding console shell."); - if (detect_container() > 0) { - _cleanup_free_ char *container_ttys = NULL; + r = add_symlink("console-getty.service", "console-getty.service"); + if (r < 0) + return r; - log_debug("Automatically adding console shell."); + /* When $container_ttys is set for PID 1, spawn gettys on all ptys named therein. + * Note that despite the variable name we only support ptys here. */ - r = add_symlink("console-getty.service", "console-getty.service"); - if (r < 0) - return r; + (void) getenv_for_pid(1, "container_ttys", &container_ttys); - /* When $container_ttys is set for PID 1, spawn - * gettys on all ptys named therein. Note that despite - * the variable name we only support ptys here. */ + for (const char *p = container_ttys;;) { + _cleanup_free_ char *word = NULL; - r = getenv_for_pid(1, "container_ttys", &container_ttys); - if (r > 0) { - const char *word, *state; - size_t l; + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + return log_error_errno(r, "Failed to parse $container_ttys: %m"); + if (r == 0) + return 0; - FOREACH_WORD(word, l, container_ttys, state) { - const char *t; - char tty[l + 1]; + const char *tty = word; - memcpy(tty, word, l); - tty[l] = 0; + /* First strip off /dev/ if it is specified */ + tty = path_startswith(tty, "/dev/") ?: tty; - /* First strip off /dev/ if it is specified */ - t = path_startswith(tty, "/dev/"); - if (!t) - t = tty; + /* Then, make sure it's actually a pty */ + tty = path_startswith(tty, "pts/"); + if (!tty) + continue; - /* Then, make sure it's actually a pty */ - t = path_startswith(t, "pts/"); - if (!t) - continue; + r = add_container_getty(tty); + if (r < 0) + return r; + } +} - r = add_container_getty(t); - if (r < 0) - return r; - } - } +static int run(const char *dest, const char *dest_early, const char *dest_late) { + int r; - /* Don't add any further magic if we are in a container */ - return 0; - } + assert_se(arg_dest = dest); - if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) { - const char *word, *state; - size_t l; + if (detect_container() > 0) + /* Add console shell and look at $container_ttys, but don't do add any + * further magic if we are in a container. */ + return run_container(); - /* Automatically add in a serial getty on all active - * kernel consoles */ - FOREACH_WORD(word, l, active, state) { - _cleanup_free_ char *tty = NULL; + /* Automatically add in a serial getty on all active kernel consoles */ + _cleanup_free_ char *active = NULL; + (void) read_one_line_file("/sys/class/tty/console/active", &active); + for (const char *p = active;;) { + _cleanup_free_ char *tty = NULL; - tty = strndup(word, l); - if (!tty) - return log_oom(); + r = extract_first_word(&p, &tty, NULL, 0); + if (r < 0) + return log_error_errno(r, "Failed to parse /sys/class/tty/console/active: %m"); + if (r == 0) + break; - /* We assume that gettys on virtual terminals are - * started via manual configuration and do this magic - * only for non-VC terminals. */ + /* We assume that gettys on virtual terminals are started via manual configuration and do + * this magic only for non-VC terminals. */ - if (isempty(tty) || tty_is_vc(tty)) - continue; + if (isempty(tty) || tty_is_vc(tty)) + continue; - if (verify_tty(tty) < 0) - continue; + if (verify_tty(tty) < 0) + continue; - r = add_serial_getty(tty); - if (r < 0) - return r; - } + r = add_serial_getty(tty); + if (r < 0) + return r; } /* Automatically add in a serial getty on the first * virtualizer console */ + const char *j; FOREACH_STRING(j, "hvc0", "xvc0", diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index 77dfdefd64..0e028a9e4a 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -124,7 +124,7 @@ static int spawn_getter(const char *getter) { _cleanup_strv_free_ char **words = NULL; assert(getter); - r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_UNQUOTE); + r = strv_split_full(&words, getter, WHITESPACE, EXTRACT_UNQUOTE); if (r < 0) return log_error_errno(r, "Failed to split getter option: %m"); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 234e81445b..731fc51212 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -950,74 +950,69 @@ _public_ int sd_journal_get_cursor(sd_journal *j, char **cursor) { } _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { - const char *word, *state; - size_t l; unsigned long long seqnum, monotonic, realtime, xor_hash; - bool - seqnum_id_set = false, - seqnum_set = false, - boot_id_set = false, - monotonic_set = false, - realtime_set = false, - xor_hash_set = false; + bool seqnum_id_set = false, + seqnum_set = false, + boot_id_set = false, + monotonic_set = false, + realtime_set = false, + xor_hash_set = false; sd_id128_t seqnum_id, boot_id; + int r; assert_return(j, -EINVAL); assert_return(!journal_pid_changed(j), -ECHILD); assert_return(!isempty(cursor), -EINVAL); - FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) { - char *item; - int k = 0; + for (const char *p = cursor;;) { + _cleanup_free_ char *word = NULL; - if (l < 2 || word[1] != '=') - return -EINVAL; + r = extract_first_word(&p, &word, ";", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + break; - item = strndup(word, l); - if (!item) - return -ENOMEM; + if (word[0] == '\0' || word[1] != '=') + return -EINVAL; switch (word[0]) { - case 's': seqnum_id_set = true; - k = sd_id128_from_string(item+2, &seqnum_id); + r = sd_id128_from_string(word + 2, &seqnum_id); + if (r < 0) + return r; break; case 'i': seqnum_set = true; - if (sscanf(item+2, "%llx", &seqnum) != 1) - k = -EINVAL; + if (sscanf(word + 2, "%llx", &seqnum) != 1) + return -EINVAL; break; case 'b': boot_id_set = true; - k = sd_id128_from_string(item+2, &boot_id); + r = sd_id128_from_string(word + 2, &boot_id); break; case 'm': monotonic_set = true; - if (sscanf(item+2, "%llx", &monotonic) != 1) - k = -EINVAL; + if (sscanf(word + 2, "%llx", &monotonic) != 1) + return -EINVAL; break; case 't': realtime_set = true; - if (sscanf(item+2, "%llx", &realtime) != 1) - k = -EINVAL; + if (sscanf(word + 2, "%llx", &realtime) != 1) + return -EINVAL; break; case 'x': xor_hash_set = true; - if (sscanf(item+2, "%llx", &xor_hash) != 1) - k = -EINVAL; + if (sscanf(word + 2, "%llx", &xor_hash) != 1) + return -EINVAL; break; } - - free(item); - - if (k < 0) - return k; } if ((!seqnum_set || !seqnum_id_set) && diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index bbe7cd76d5..bdcbb106ce 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -101,7 +101,7 @@ _public_ int sd_listen_fds_with_names(int unset_environment, char ***names) { e = getenv("LISTEN_FDNAMES"); if (e) { - n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + n_names = strv_split_full(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS); if (n_names < 0) { unsetenv_all(unset_environment); return n_names; diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index 44d1fd9839..dc16d555a1 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -316,34 +316,33 @@ static int device_amend(sd_device *device, const char *key, const char *value) { if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set SEQNUM to '%s': %m", value); } else if (streq(key, "DEVLINKS")) { - const char *word, *state; - size_t l; + for (const char *p = value;;) { + _cleanup_free_ char *word = NULL; - FOREACH_WORD(word, l, value, state) { - char devlink[l + 1]; - - strncpy(devlink, word, l); - devlink[l] = '\0'; + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; - r = device_add_devlink(device, devlink); + r = device_add_devlink(device, word); if (r < 0) - return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", devlink); + return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", word); } } else if (STR_IN_SET(key, "TAGS", "CURRENT_TAGS")) { - const char *word, *state; - size_t l; - - FOREACH_WORD_SEPARATOR(word, l, value, ":", state) { - char tag[l + 1]; + for (const char *p = value;;) { + _cleanup_free_ char *word = NULL; - (void) strncpy(tag, word, l); - tag[l] = '\0'; + r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + break; - r = device_add_tag(device, tag, streq(key, "CURRENT_TAGS")); + r = device_add_tag(device, word, streq(key, "CURRENT_TAGS")); if (r < 0) - return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", tag); + return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", word); } - } else { r = device_add_property_internal(device, key, value); if (r < 0) diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 746c895b61..601a27ab57 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -12,6 +12,7 @@ #include "dirent-util.h" #include "env-file.h" #include "escape.h" +#include "extract-word.h" #include "fd-util.h" #include "format-util.h" #include "fs-util.h" @@ -22,6 +23,7 @@ #include "parse-util.h" #include "path-util.h" #include "socket-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "user-util.h" @@ -331,35 +333,29 @@ static int file_of_seat(const char *seat, char **_p) { } _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) { - _cleanup_free_ char *t = NULL, *s = NULL, *p = NULL; - size_t l; + _cleanup_free_ char *filename = NULL, *content = NULL; int r; - const char *word, *variable, *state; assert_return(uid_is_valid(uid), -EINVAL); - r = file_of_seat(seat, &p); + r = file_of_seat(seat, &filename); if (r < 0) return r; - variable = require_active ? "ACTIVE_UID" : "UIDS"; - - r = parse_env_file(NULL, p, variable, &s); + r = parse_env_file(NULL, filename, + require_active ? "ACTIVE_UID" : "UIDS", + &content); if (r == -ENOENT) return 0; if (r < 0) return r; - if (isempty(s)) + if (isempty(content)) return 0; - if (asprintf(&t, UID_FMT, uid) < 0) - return -ENOMEM; - - FOREACH_WORD(word, l, s, state) - if (strneq(t, word, l)) - return 1; + char t[DECIMAL_STR_MAX(uid_t)]; + xsprintf(t, UID_FMT, uid); - return 0; + return string_contains_word(content, ",", t); } static int uid_get_array(uid_t uid, const char *variable, char ***array) { @@ -382,7 +378,7 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) { if (r < 0) return r; - a = strv_split(s, " "); + a = strv_split(s, NULL); if (!a) return -ENOMEM; @@ -654,73 +650,70 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) { return 0; } -_public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) { - _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; - _cleanup_strv_free_ char **a = NULL; - _cleanup_free_ uid_t *b = NULL; - unsigned n = 0; +_public_ int sd_seat_get_sessions( + const char *seat, + char ***ret_sessions, + uid_t **ret_uids, + unsigned *ret_n_uids) { + + _cleanup_free_ char *fname = NULL, *session_line = NULL, *uid_line = NULL; + _cleanup_strv_free_ char **sessions = NULL; + _cleanup_free_ uid_t *uids = NULL; + unsigned n_sessions = 0; int r; - r = file_of_seat(seat, &p); + r = file_of_seat(seat, &fname); if (r < 0) return r; - r = parse_env_file(NULL, p, - "SESSIONS", &s, - "UIDS", &t); + r = parse_env_file(NULL, fname, + "SESSIONS", &session_line, + "UIDS", &uid_line); if (r == -ENOENT) return -ENXIO; if (r < 0) return r; - if (s) { - a = strv_split(s, " "); - if (!a) + if (session_line) { + sessions = strv_split(session_line, NULL); + if (!sessions) return -ENOMEM; - } - - if (uids && t) { - const char *word, *state; - size_t l; - - FOREACH_WORD(word, l, t, state) - n++; - - if (n > 0) { - unsigned i = 0; - b = new(uid_t, n); - if (!b) - return -ENOMEM; + n_sessions = strv_length(sessions); + }; - FOREACH_WORD(word, l, t, state) { - _cleanup_free_ char *k = NULL; + if (ret_uids && uid_line) { + uids = new(uid_t, n_sessions); + if (!uids) + return -ENOMEM; - k = strndup(word, l); - if (!k) - return -ENOMEM; + size_t n = 0; + for (const char *p = uid_line;;) { + _cleanup_free_ char *word = NULL; - r = parse_uid(k, b + i); - if (r < 0) - return r; + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; - i++; - } + r = parse_uid(word, &uids[n++]); + if (r < 0) + return r; } - } - r = (int) strv_length(a); - - if (sessions) - *sessions = TAKE_PTR(a); - - if (uids) - *uids = TAKE_PTR(b); + if (n != n_sessions) + return -EUCLEAN; + } - if (n_uids) - *n_uids = n; + if (ret_sessions) + *ret_sessions = TAKE_PTR(sessions); + if (ret_uids) + *ret_uids = TAKE_PTR(uids); + if (ret_n_uids) + *ret_n_uids = n_sessions; - return r; + return n_sessions; } static int seat_get_can(const char *seat, const char *variable) { @@ -901,47 +894,52 @@ _public_ int sd_machine_get_class(const char *machine, char **class) { return 0; } -_public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) { - _cleanup_free_ char *netif = NULL; - size_t l, allocated = 0, nr = 0; - int *ni = NULL; - const char *p, *word, *state; +_public_ int sd_machine_get_ifindices(const char *machine, int **ret_ifindices) { + _cleanup_free_ char *netif_line = NULL; + const char *p; int r; assert_return(machine_name_is_valid(machine), -EINVAL); - assert_return(ifindices, -EINVAL); p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(NULL, p, "NETIF", &netif); + r = parse_env_file(NULL, p, "NETIF", &netif_line); if (r == -ENOENT) return -ENXIO; if (r < 0) return r; - if (!netif) { - *ifindices = NULL; + if (!netif_line) { + *ret_ifindices = NULL; return 0; } - FOREACH_WORD(word, l, netif, state) { - char buf[l+1]; - int ifi; + _cleanup_strv_free_ char **tt = strv_split(netif_line, NULL); + if (!tt) + return -ENOMEM; - *(char*) (mempcpy(buf, word, l)) = 0; + size_t n = 0; + int *ifindices; + if (ret_ifindices) { + ifindices = new(int, strv_length(tt)); + if (!ifindices) + return -ENOMEM; + } - ifi = parse_ifindex(buf); - if (ifi < 0) - continue; + for (size_t i = 0; tt[i]; i++) { + int ind; - if (!GREEDY_REALLOC(ni, allocated, nr+1)) { - free(ni); - return -ENOMEM; - } + ind = parse_ifindex(tt[i]); + if (ind < 0) + /* Return -EUCLEAN to distinguish from -EINVAL for invalid args */ + return ind == -EINVAL ? -EUCLEAN : ind; - ni[nr++] = ifi; + if (ret_ifindices) + ifindices[n] = ind; + n++; } - *ifindices = ni; - return nr; + if (ret_ifindices) + *ret_ifindices = ifindices; + return n; } static int MONITOR_TO_FD(sd_login_monitor *m) { diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c index 233d081300..8e0cb74775 100644 --- a/src/locale/keymap-util.c +++ b/src/locale/keymap-util.c @@ -251,7 +251,7 @@ int x11_read_data(Context *c, sd_bus_message *m) { if (in_section && first_word(l, "Option")) { _cleanup_strv_free_ char **a = NULL; - r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_UNQUOTE); + r = strv_split_full(&a, l, WHITESPACE, EXTRACT_UNQUOTE); if (r < 0) return r; @@ -274,7 +274,7 @@ int x11_read_data(Context *c, sd_bus_message *m) { } else if (!in_section && first_word(l, "Section")) { _cleanup_strv_free_ char **a = NULL; - r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_UNQUOTE); + r = strv_split_full(&a, l, WHITESPACE, EXTRACT_UNQUOTE); if (r < 0) return -ENOMEM; @@ -489,7 +489,7 @@ static int read_next_mapping(const char* filename, if (IN_SET(l[0], 0, '#')) continue; - r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_UNQUOTE); + r = strv_split_full(&b, l, WHITESPACE, EXTRACT_UNQUOTE); if (r < 0) return r; diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index ff16bc4b3d..7a6fba4d9b 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -8,6 +8,7 @@ #include "alloc-util.h" #include "env-file.h" +#include "errno-list.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" @@ -481,31 +482,39 @@ const char *inhibit_what_to_string(InhibitWhat w) { return buffer; } -InhibitWhat inhibit_what_from_string(const char *s) { +int inhibit_what_from_string(const char *s) { InhibitWhat what = 0; - const char *word, *state; - size_t l; - FOREACH_WORD_SEPARATOR(word, l, s, ":", state) { - if (l == 8 && strneq(word, "shutdown", l)) + for (const char *p = s;;) { + _cleanup_free_ char *word = NULL; + int r; + + /* A sanity check that our return values fit in an int */ + assert_cc((int) _INHIBIT_WHAT_MAX == _INHIBIT_WHAT_MAX); + + r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + return what; + + if (streq(word, "shutdown")) what |= INHIBIT_SHUTDOWN; - else if (l == 5 && strneq(word, "sleep", l)) + else if (streq(word, "sleep")) what |= INHIBIT_SLEEP; - else if (l == 4 && strneq(word, "idle", l)) + else if (streq(word, "idle")) what |= INHIBIT_IDLE; - else if (l == 16 && strneq(word, "handle-power-key", l)) + else if (streq(word, "handle-power-key")) what |= INHIBIT_HANDLE_POWER_KEY; - else if (l == 18 && strneq(word, "handle-suspend-key", l)) + else if (streq(word, "handle-suspend-key")) what |= INHIBIT_HANDLE_SUSPEND_KEY; - else if (l == 20 && strneq(word, "handle-hibernate-key", l)) + else if (streq(word, "handle-hibernate-key")) what |= INHIBIT_HANDLE_HIBERNATE_KEY; - else if (l == 17 && strneq(word, "handle-lid-switch", l)) + else if (streq(word, "handle-lid-switch")) what |= INHIBIT_HANDLE_LID_SWITCH; else return _INHIBIT_WHAT_INVALID; } - - return what; } static const char* const inhibit_mode_table[_INHIBIT_MODE_MAX] = { diff --git a/src/login/logind-inhibit.h b/src/login/logind-inhibit.h index cea67a08c5..7eaecee0b4 100644 --- a/src/login/logind-inhibit.h +++ b/src/login/logind-inhibit.h @@ -66,7 +66,7 @@ InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm); bool manager_is_inhibited(Manager *m, InhibitWhat w, InhibitMode mm, dual_timestamp *since, bool ignore_inactive, bool ignore_uid, uid_t uid, Inhibitor **offending); const char *inhibit_what_to_string(InhibitWhat k); -InhibitWhat inhibit_what_from_string(const char *s); +int inhibit_what_from_string(const char *s); const char *inhibit_mode_to_string(InhibitMode k); InhibitMode inhibit_mode_from_string(const char *s); diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 5599c6a1b3..c49ed76979 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -301,7 +301,7 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl CustomMount *m; int k; - k = strv_split_extract(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + k = strv_split_full(&lower, s, ":", EXTRACT_DONT_COALESCE_SEPARATORS); if (k < 0) return k; if (k < 2) diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c index d0e575fef2..fa2002d578 100644 --- a/src/nspawn/nspawn-setuid.c +++ b/src/nspawn/nspawn-setuid.c @@ -88,13 +88,12 @@ int change_uid_gid_raw( int change_uid_gid(const char *user, char **_home) { char *x, *u, *g, *h; - const char *word, *state; _cleanup_free_ gid_t *gids = NULL; _cleanup_free_ char *home = NULL, *line = NULL; _cleanup_fclose_ FILE *f = NULL; _cleanup_close_ int fd = -1; unsigned n_gids = 0; - size_t sz = 0, l; + size_t sz = 0; uid_t uid; gid_t gid; pid_t pid; @@ -208,16 +207,19 @@ int change_uid_gid(const char *user, char **_home) { x += strcspn(x, WHITESPACE); x += strspn(x, WHITESPACE); - FOREACH_WORD(word, l, x, state) { - char c[l+1]; + for (const char *p = x;;) { + _cleanup_free_ char *word = NULL; - memcpy(c, word, l); - c[l] = 0; + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + return log_error_errno(r, "Failed to parse group data from getent: %m"); + if (r == 0) + break; if (!GREEDY_REALLOC(gids, sz, n_gids+1)) return log_oom(); - r = parse_gid(c, &gids[n_gids++]); + r = parse_gid(word, &gids[n_gids++]); if (r < 0) return log_error_errno(r, "Failed to parse group data from getent: %m"); } diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index e579d606c3..c72c9791c0 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -340,7 +340,7 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c return log_error_errno(r, "Failed to parse path: %m"); } - r = strv_split_extract(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE); + r = strv_split_full(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE); if (r < 0) return log_error_errno(r, "Failed to parse command line: %m"); diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 7c9f5531b0..57787ea033 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -239,10 +239,10 @@ typedef enum Disabled { #define DEFINE_CONFIG_PARSE_ENUMV(function, name, type, invalid, msg) \ CONFIG_PARSER_PROTOTYPE(function) { \ - type **enums = data, x, *ys; \ + type **enums = data; \ _cleanup_free_ type *xs = NULL; \ - const char *word, *state; \ - size_t l, i = 0; \ + size_t i = 0; \ + int r; \ \ assert(filename); \ assert(lvalue); \ @@ -255,29 +255,32 @@ typedef enum Disabled { \ *xs = invalid; \ \ - FOREACH_WORD(word, l, rvalue, state) { \ + for (const char *p = rvalue;;) { \ _cleanup_free_ char *en = NULL; \ - type *new_xs; \ + type x, *new_xs; \ \ - en = strndup(word, l); \ - if (!en) \ + r = extract_first_word(&p, &en, NULL, 0); \ + if (r == -ENOMEM) \ return log_oom(); \ + if (r < 0) \ + return log_syntax(unit, LOG_ERR, filename, line, 0, \ + msg ": %s", en); \ + if (r == 0) \ + break; \ \ if ((x = name##_from_string(en)) < 0) { \ - log_syntax(unit, LOG_WARNING, filename, line, 0, \ + log_syntax(unit, LOG_WARNING, filename, line, 0, \ msg ", ignoring: %s", en); \ continue; \ } \ \ - for (ys = xs; x != invalid && *ys != invalid; ys++) { \ - if (*ys == x) { \ - log_syntax(unit, LOG_NOTICE, filename, \ - line, 0, \ - "Duplicate entry, ignoring: %s", \ + for (type *ys = xs; x != invalid && *ys != invalid; ys++) \ + if (*ys == x) { \ + log_syntax(unit, LOG_NOTICE, filename, line, 0, \ + "Duplicate entry, ignoring: %s", \ en); \ x = invalid; \ } \ - } \ \ if (x == invalid) \ continue; \ @@ -292,6 +295,5 @@ typedef enum Disabled { *(xs + i) = invalid; \ } \ \ - free_and_replace(*enums, xs); \ - return 0; \ + return free_and_replace(*enums, xs); \ } diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index 806dda8475..d883eca5c7 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -81,50 +81,53 @@ int fstab_is_mount_point(const char *mount) { int fstab_filter_options(const char *opts, const char *names, const char **ret_namefound, char **ret_value, char **ret_filtered) { - const char *name, *n = NULL, *x; + const char *name, *namefound = NULL, *x; _cleanup_strv_free_ char **stor = NULL; _cleanup_free_ char *v = NULL, **strv = NULL; + int r; assert(names && *names); if (!opts) goto answer; - /* If !value and !filtered, this function is not allowed to fail. */ + /* If !ret_value and !ret_filtered, this function is not allowed to fail. */ if (!ret_filtered) { - const char *word, *state; - size_t l; + for (const char *word = opts;;) { + const char *end = word + strcspn(word, ","); - FOREACH_WORD_SEPARATOR(word, l, opts, ",", state) NULSTR_FOREACH(name, names) { - if (l < strlen(name)) + if (end < word + strlen(name)) continue; if (!strneq(word, name, strlen(name))) continue; - /* we know that the string is NUL - * terminated, so *x is valid */ + /* We know that the string is NUL terminated, so *x is valid */ x = word + strlen(name); if (IN_SET(*x, '\0', '=', ',')) { - n = name; + namefound = name; if (ret_value) { - free(v); - if (IN_SET(*x, '\0', ',')) - v = NULL; - else { - assert(*x == '='); - x++; - v = strndup(x, l - strlen(name) - 1); - if (!v) - return -ENOMEM; - } + bool eq = *x == '='; + assert(eq || IN_SET(*x, ',', '\0')); + + r = free_and_strndup(&v, + eq ? x + 1 : NULL, + eq ? end - x - 1 : 0); + if (r < 0) + return r; } + + break; } } - } else { - char **t, **s; + if (*end) + word = end + 1; + else + break; + } + } else { stor = strv_split(opts, ","); if (!stor) return -ENOMEM; @@ -132,7 +135,8 @@ int fstab_filter_options(const char *opts, const char *names, if (!strv) return -ENOMEM; - for (s = t = strv; *s; s++) { + char **t = strv; + for (char **s = strv; *s; s++) { NULSTR_FOREACH(name, names) { x = startswith(*s, name); if (x && IN_SET(*x, '\0', '=')) @@ -144,18 +148,12 @@ int fstab_filter_options(const char *opts, const char *names, continue; found: /* Keep the last occurrence found */ - n = name; + namefound = name; if (ret_value) { - free(v); - if (*x == '\0') - v = NULL; - else { - assert(*x == '='); - x++; - v = strdup(x); - if (!v) - return -ENOMEM; - } + assert(IN_SET(*x, '=', '\0')); + r = free_and_strdup(&v, *x == '=' ? x + 1 : NULL); + if (r < 0) + return r; } } *t = NULL; @@ -163,7 +161,7 @@ int fstab_filter_options(const char *opts, const char *names, answer: if (ret_namefound) - *ret_namefound = n; + *ret_namefound = namefound; if (ret_filtered) { char *f; @@ -176,7 +174,7 @@ answer: if (ret_value) *ret_value = TAKE_PTR(v); - return !!n; + return !!namefound; } int fstab_extract_values(const char *opts, const char *name, char ***values) { diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 96c125b993..134cedaf8b 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -100,9 +100,8 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) { } int can_sleep_state(char **types) { - char **type; + _cleanup_free_ char *text = NULL; int r; - _cleanup_free_ char *p = NULL; if (strv_isempty(types)) return true; @@ -113,34 +112,27 @@ int can_sleep_state(char **types) { return false; } - r = read_one_line_file("/sys/power/state", &p); + r = read_one_line_file("/sys/power/state", &text); if (r < 0) { log_debug_errno(r, "Failed to read /sys/power/state, cannot sleep: %m"); return false; } - STRV_FOREACH(type, types) { - const char *word, *state; - size_t l, k; - - k = strlen(*type); - FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) - if (l == k && memcmp(word, *type, l) == 0) { - log_debug("Sleep mode \"%s\" is supported by the kernel.", *type); - return true; - } - } - - if (DEBUG_LOGGING) { + const char *found; + r = string_contains_word_strv(text, NULL, types, &found); + if (r < 0) + return log_debug_errno(r, "Failed to parse /sys/power/state: %m"); + if (r > 0) + log_debug("Sleep mode \"%s\" is supported by the kernel.", found); + else if (DEBUG_LOGGING) { _cleanup_free_ char *t = strv_join(types, "/"); log_debug("Sleep mode %s not supported by the kernel, sorry.", strnull(t)); } - return false; + return r; } int can_sleep_disk(char **types) { - _cleanup_free_ char *p = NULL; - char **type; + _cleanup_free_ char *text = NULL; int r; if (strv_isempty(types)) @@ -152,29 +144,38 @@ int can_sleep_disk(char **types) { return false; } - r = read_one_line_file("/sys/power/disk", &p); + r = read_one_line_file("/sys/power/disk", &text); if (r < 0) { log_debug_errno(r, "Couldn't read /sys/power/disk: %m"); return false; } - STRV_FOREACH(type, types) { - const char *word, *state; - size_t l, k; + for (const char *p = text;;) { + _cleanup_free_ char *word = NULL; - k = strlen(*type); - FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state) { - if (l == k && memcmp(word, *type, l) == 0) - return true; + r = extract_first_word(&p, &word, NULL, 0); + if (r < 0) + return log_debug_errno(r, "Failed to parse /sys/power/disk: %m"); + if (r == 0) + break; + + char *s = word; + size_t l = strlen(s); + if (s[0] == '[' && s[l-1] == ']') { + s[l-1] = '\0'; + s++; + } - if (l == k + 2 && - word[0] == '[' && - memcmp(word + 1, *type, l - 2) == 0 && - word[l-1] == ']') - return true; + if (strv_contains(types, s)) { + log_debug("Disk sleep mode \"%s\" is supported by the kernel.", s); + return true; } } + if (DEBUG_LOGGING) { + _cleanup_free_ char *t = strv_join(types, "/"); + log_debug("Disk sleep mode %s not supported by the kernel, sorry.", strnull(t)); + } return false; } diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h index e18f01bb67..6a8c206259 100644 --- a/src/systemd/sd-login.h +++ b/src/systemd/sd-login.h @@ -180,7 +180,11 @@ int sd_seat_get_active(const char *seat, char **session, uid_t *uid); /* Return sessions and users on seat. Returns number of sessions. * If sessions is NULL, this returns only the number of sessions. */ -int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uid, unsigned *n_uids); +int sd_seat_get_sessions( + const char *seat, + char ***ret_sessions, + uid_t **ret_uids, + unsigned *ret_n_uids); /* Return whether the seat is multi-session capable */ int sd_seat_can_multi_session(const char *seat) _sd_deprecated_; @@ -195,7 +199,7 @@ int sd_seat_can_graphical(const char *seat); int sd_machine_get_class(const char *machine, char **clazz); /* Return the list if host-side network interface indices of a machine */ -int sd_machine_get_ifindices(const char *machine, int **ifindices); +int sd_machine_get_ifindices(const char *machine, int **ret_ifindices); /* Get all seats, store in *seats. Returns the number of seats. If * seats is NULL, this only returns the number of seats. */ diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c index c71e4d32bf..cc1f29385f 100644 --- a/src/test/test-extract-word.c +++ b/src/test/test-extract-word.c @@ -11,6 +11,8 @@ static void test_extract_first_word(void) { const char *p, *original; char *t; + log_info("/* %s */", __func__); + p = original = "foobar waldo"; assert_se(extract_first_word(&p, &t, NULL, 0) > 0); assert_se(streq(t, "foobar")); @@ -387,6 +389,8 @@ static void test_extract_first_word_and_warn(void) { const char *p, *original; char *t; + log_info("/* %s */", __func__); + p = original = "foobar waldo"; assert_se(extract_first_word_and_warn(&p, &t, NULL, 0, NULL, "fake", 1, original) > 0); assert_se(streq(t, "foobar")); @@ -531,6 +535,8 @@ static void test_extract_many_words(void) { const char *p, *original; char *a, *b, *c, *d, *e, *f; + log_info("/* %s */", __func__); + p = original = "foobar waldi piep"; assert_se(extract_many_words(&p, NULL, 0, &a, &b, &c, NULL) == 3); assert_se(isempty(p)); diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 1127d398a5..196c96aa8c 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -10,8 +10,9 @@ #include "util.h" static void test_string_erase(void) { - char *x; + log_info("/* %s */", __func__); + char *x; x = strdupa(""); assert_se(streq(string_erase(x), "")); @@ -33,17 +34,17 @@ static void test_string_erase(void) { } static void test_free_and_strndup_one(char **t, const char *src, size_t l, const char *expected, bool change) { - int r; - log_debug("%s: \"%s\", \"%s\", %zd (expect \"%s\", %s)", __func__, strnull(*t), strnull(src), l, strnull(expected), yes_no(change)); - r = free_and_strndup(t, src, l); + int r = free_and_strndup(t, src, l); assert_se(streq_ptr(*t, expected)); assert_se(r == change); /* check that change occurs only when necessary */ } static void test_free_and_strndup(void) { + log_info("/* %s */", __func__); + static const struct test_case { const char *src; size_t len; @@ -91,6 +92,7 @@ static void test_free_and_strndup(void) { } static void test_ascii_strcasecmp_n(void) { + log_info("/* %s */", __func__); assert_se(ascii_strcasecmp_n("", "", 0) == 0); assert_se(ascii_strcasecmp_n("", "", 1) == 0); @@ -118,6 +120,8 @@ static void test_ascii_strcasecmp_n(void) { } static void test_ascii_strcasecmp_nn(void) { + log_info("/* %s */", __func__); + assert_se(ascii_strcasecmp_nn("", 0, "", 0) == 0); assert_se(ascii_strcasecmp_nn("", 0, "", 1) < 0); assert_se(ascii_strcasecmp_nn("", 1, "", 0) > 0); @@ -137,6 +141,8 @@ static void test_ascii_strcasecmp_nn(void) { static void test_cellescape(void) { char buf[40]; + log_info("/* %s */", __func__); + assert_se(streq(cellescape(buf, 1, ""), "")); assert_se(streq(cellescape(buf, 1, "1"), "")); assert_se(streq(cellescape(buf, 1, "12"), "")); @@ -216,19 +222,24 @@ static void test_cellescape(void) { } static void test_streq_ptr(void) { + log_info("/* %s */", __func__); + assert_se(streq_ptr(NULL, NULL)); assert_se(!streq_ptr("abc", "cdef")); } static void test_strstrip(void) { - char *r; - char input[] = " hello, waldo. "; + log_info("/* %s */", __func__); + + char *ret, input[] = " hello, waldo. "; - r = strstrip(input); - assert_se(streq(r, "hello, waldo.")); + ret = strstrip(input); + assert_se(streq(ret, "hello, waldo.")); } static void test_strextend(void) { + log_info("/* %s */", __func__); + _cleanup_free_ char *str = NULL; assert_se(strextend(&str, NULL)); @@ -240,6 +251,8 @@ static void test_strextend(void) { } static void test_strextend_with_separator(void) { + log_info("/* %s */", __func__); + _cleanup_free_ char *str = NULL; assert_se(strextend_with_separator(&str, NULL, NULL)); @@ -263,6 +276,8 @@ static void test_strextend_with_separator(void) { } static void test_strrep(void) { + log_info("/* %s */", __func__); + _cleanup_free_ char *one, *three, *zero; one = strrep("waldo", 1); three = strrep("waldo", 3); @@ -288,11 +303,15 @@ static void test_string_has_cc(void) { } static void test_ascii_strlower(void) { + log_info("/* %s */", __func__); + char a[] = "AabBcC Jk Ii Od LKJJJ kkd LK"; assert_se(streq(ascii_strlower(a), "aabbcc jk ii od lkjjj kkd lk")); } static void test_strshorten(void) { + log_info("/* %s */", __func__); + char s[] = "foobar"; assert_se(strlen(strshorten(s, 6)) == 6); @@ -302,6 +321,8 @@ static void test_strshorten(void) { } static void test_strjoina(void) { + log_info("/* %s */", __func__); + char *actual; actual = strjoina("", "foo", "bar"); @@ -359,6 +380,8 @@ static void test_strjoin(void) { } static void test_strcmp_ptr(void) { + log_info("/* %s */", __func__); + assert_se(strcmp_ptr(NULL, NULL) == 0); assert_se(strcmp_ptr("", NULL) > 0); assert_se(strcmp_ptr("foo", NULL) > 0); @@ -371,26 +394,36 @@ static void test_strcmp_ptr(void) { } static void test_foreach_word(void) { - const char *word, *state; - size_t l; - int i = 0; - const char test[] = "test abc d\te f "; + log_info("/* %s */", __func__); + + const char *test = "test abc d\te f "; const char * const expected[] = { "test", "abc", "d", "e", "f", - "", - NULL }; - FOREACH_WORD(word, l, test, state) - assert_se(strneq(expected[i++], word, l)); + size_t i = 0; + int r; + for (const char *p = test;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, 0); + if (r == 0) { + assert_se(i == ELEMENTSOF(expected)); + break; + } + assert_se(r > 0); + + assert_se(streq(expected[i++], word)); + } } static void check(const char *test, char** expected, bool trailing) { - int i = 0, r; + size_t i = 0; + int r; printf("<<<%s>>>\n", test); for (;;) { @@ -412,6 +445,8 @@ static void check(const char *test, char** expected, bool trailing) { } static void test_foreach_word_quoted(void) { + log_info("/* %s */", __func__); + check("test a b c 'd' e '' '' hhh '' '' \"a b c\"", STRV_MAKE("test", "a", @@ -437,6 +472,8 @@ static void test_foreach_word_quoted(void) { } static void test_endswith(void) { + log_info("/* %s */", __func__); + assert_se(endswith("foobar", "bar")); assert_se(endswith("foobar", "")); assert_se(endswith("foobar", "foobar")); @@ -447,6 +484,8 @@ static void test_endswith(void) { } static void test_endswith_no_case(void) { + log_info("/* %s */", __func__); + assert_se(endswith_no_case("fooBAR", "bar")); assert_se(endswith_no_case("foobar", "")); assert_se(endswith_no_case("foobar", "FOOBAR")); @@ -457,6 +496,8 @@ static void test_endswith_no_case(void) { } static void test_delete_chars(void) { + log_info("/* %s */", __func__); + char *s, input[] = " hello, waldo. abc"; s = delete_chars(input, WHITESPACE); @@ -465,6 +506,7 @@ static void test_delete_chars(void) { } static void test_delete_trailing_chars(void) { + log_info("/* %s */", __func__); char *s, input1[] = " \n \r k \n \r ", @@ -489,6 +531,8 @@ static void test_delete_trailing_chars(void) { } static void test_delete_trailing_slashes(void) { + log_info("/* %s */", __func__); + char s1[] = "foobar//", s2[] = "foobar/", s3[] = "foobar", @@ -502,6 +546,8 @@ static void test_delete_trailing_slashes(void) { } static void test_skip_leading_chars(void) { + log_info("/* %s */", __func__); + char input1[] = " \n \r k \n \r ", input2[] = "kkkkthiskkkiskkkaktestkkk", input3[] = "abcdef"; @@ -514,11 +560,15 @@ static void test_skip_leading_chars(void) { } static void test_in_charset(void) { + log_info("/* %s */", __func__); + assert_se(in_charset("dddaaabbbcccc", "abcd")); assert_se(!in_charset("dddaaabbbcccc", "abc f")); } static void test_split_pair(void) { + log_info("/* %s */", __func__); + _cleanup_free_ char *a = NULL, *b = NULL; assert_se(split_pair("", "", &a, &b) == -EINVAL); @@ -541,6 +591,8 @@ static void test_split_pair(void) { } static void test_first_word(void) { + log_info("/* %s */", __func__); + assert_se(first_word("Hello", "")); assert_se(first_word("Hello", "Hello")); assert_se(first_word("Hello world", "Hello")); @@ -555,12 +607,16 @@ static void test_first_word(void) { } static void test_strlen_ptr(void) { + log_info("/* %s */", __func__); + assert_se(strlen_ptr("foo") == 3); assert_se(strlen_ptr("") == 0); assert_se(strlen_ptr(NULL) == 0); } static void test_memory_startswith(void) { + log_info("/* %s */", __func__); + assert_se(streq(memory_startswith("", 0, ""), "")); assert_se(streq(memory_startswith("", 1, ""), "")); assert_se(streq(memory_startswith("x", 2, ""), "x")); @@ -573,6 +629,8 @@ static void test_memory_startswith(void) { } static void test_memory_startswith_no_case(void) { + log_info("/* %s */", __func__); + assert_se(streq(memory_startswith_no_case("", 0, ""), "")); assert_se(streq(memory_startswith_no_case("", 1, ""), "")); assert_se(streq(memory_startswith_no_case("x", 2, ""), "x")); @@ -605,6 +663,8 @@ static void test_string_truncate_lines_one(const char *input, size_t n_lines, co } static void test_string_truncate_lines(void) { + log_info("/* %s */", __func__); + test_string_truncate_lines_one("", 0, "", false); test_string_truncate_lines_one("", 1, "", false); test_string_truncate_lines_one("", 2, "", false); @@ -676,6 +736,8 @@ static void test_string_extract_lines_one(const char *input, size_t i, const cha } static void test_string_extract_line(void) { + log_info("/* %s */", __func__); + test_string_extract_lines_one("", 0, "", false); test_string_extract_lines_one("", 1, "", false); test_string_extract_lines_one("", 2, "", false); @@ -742,6 +804,88 @@ static void test_string_extract_line(void) { test_string_extract_lines_one("\n\n\nx\n", 3, "x", false); } +static void test_string_contains_word_strv(void) { + log_info("/* %s */", __func__); + + const char *w; + + assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("a", "b"), NULL)); + + assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("a", "b"), &w)); + assert_se(streq(w, "a")); + + assert_se(!string_contains_word_strv("a b cc", NULL, STRV_MAKE("d"), &w)); + assert_se(w == NULL); + + assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("b", "a"), &w)); + assert_se(streq(w, "a")); + + assert_se(string_contains_word_strv("b a b cc", NULL, STRV_MAKE("b", "a", "b"), &w)); + assert_se(streq(w, "b")); + + assert_se(string_contains_word_strv("a b cc", NULL, STRV_MAKE("b", ""), &w)); + assert_se(streq(w, "b")); + + assert_se(!string_contains_word_strv("a b cc", NULL, STRV_MAKE(""), &w)); + assert_se(w == NULL); + + assert_se(string_contains_word_strv("a b cc", " ", STRV_MAKE(""), &w)); + assert_se(streq(w, "")); +} + +static void test_string_contains_word(void) { + log_info("/* %s */", __func__); + + assert_se( string_contains_word("a b cc", NULL, "a")); + assert_se( string_contains_word("a b cc", NULL, "b")); + assert_se(!string_contains_word("a b cc", NULL, "c")); + assert_se( string_contains_word("a b cc", NULL, "cc")); + assert_se(!string_contains_word("a b cc", NULL, "d")); + assert_se(!string_contains_word("a b cc", NULL, "a b")); + assert_se(!string_contains_word("a b cc", NULL, "a b c")); + assert_se(!string_contains_word("a b cc", NULL, "b c")); + assert_se(!string_contains_word("a b cc", NULL, "b cc")); + assert_se(!string_contains_word("a b cc", NULL, "a ")); + assert_se(!string_contains_word("a b cc", NULL, " b ")); + assert_se(!string_contains_word("a b cc", NULL, " cc")); + + assert_se( string_contains_word(" a b\t\tcc", NULL, "a")); + assert_se( string_contains_word(" a b\t\tcc", NULL, "b")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, "c")); + assert_se( string_contains_word(" a b\t\tcc", NULL, "cc")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, "d")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, "a b")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, "a b\t\tc")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, "b\t\tc")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, "b\t\tcc")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, "a ")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, " b ")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, " cc")); + + assert_se(!string_contains_word(" a b\t\tcc", NULL, "")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, " ")); + assert_se(!string_contains_word(" a b\t\tcc", NULL, " ")); + assert_se( string_contains_word(" a b\t\tcc", " ", "")); + assert_se( string_contains_word(" a b\t\tcc", "\t", "")); + assert_se( string_contains_word(" a b\t\tcc", WHITESPACE, "")); + + assert_se( string_contains_word("a:b:cc", ":#", "a")); + assert_se( string_contains_word("a:b:cc", ":#", "b")); + assert_se(!string_contains_word("a:b:cc", ":#", "c")); + assert_se( string_contains_word("a:b:cc", ":#", "cc")); + assert_se(!string_contains_word("a:b:cc", ":#", "d")); + assert_se(!string_contains_word("a:b:cc", ":#", "a:b")); + assert_se(!string_contains_word("a:b:cc", ":#", "a:b:c")); + assert_se(!string_contains_word("a:b:cc", ":#", "b:c")); + assert_se(!string_contains_word("a#b#cc", ":#", "b:cc")); + assert_se( string_contains_word("a#b#cc", ":#", "b")); + assert_se( string_contains_word("a#b#cc", ":#", "cc")); + assert_se(!string_contains_word("a:b:cc", ":#", "a:")); + assert_se(!string_contains_word("a:b cc", ":#", "b")); + assert_se( string_contains_word("a:b cc", ":#", "b cc")); + assert_se(!string_contains_word("a:b:cc", ":#", ":cc")); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -777,6 +921,8 @@ int main(int argc, char *argv[]) { test_memory_startswith_no_case(); test_string_truncate_lines(); test_string_extract_line(); + test_string_contains_word_strv(); + test_string_contains_word(); return 0; } diff --git a/src/test/test-strv.c b/src/test/test-strv.c index fda5948f49..558ffeef51 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -100,6 +100,12 @@ static const char* const input_table_quoted[] = { NULL, }; +static const char* const input_table_quoted_joined[] = { + "one", + " two\t three " " four five", + NULL, +}; + static const char* const input_table_one[] = { "one", NULL, @@ -232,7 +238,7 @@ static void test_strv_unquote(const char *quoted, char **list) { log_info("/* %s */", __func__); - r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE); + r = strv_split_full(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE); assert_se(r == (int) strv_length(list)); assert_se(s); j = strv_join(s, " | "); @@ -251,7 +257,7 @@ static void test_invalid_unquote(const char *quoted) { log_info("/* %s */", __func__); - r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE); + r = strv_split_full(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE); assert_se(s == NULL); assert_se(r == -EINVAL); } @@ -281,47 +287,39 @@ static void test_strv_split(void) { strv_free_erase(l); - l = strv_split_full(" one two\t three", NULL, 0); - assert_se(l); + assert_se(strv_split_full(&l, " one two\t three", NULL, 0) == 3); assert_se(strv_equal(l, (char**) input_table_multiple)); strv_free_erase(l); - l = strv_split_full(" 'one' \" two\t three \" ' four five'", NULL, SPLIT_QUOTES); - assert_se(l); + assert_se(strv_split_full(&l, " 'one' \" two\t three \" ' four five'", NULL, EXTRACT_UNQUOTE) == 3); assert_se(strv_equal(l, (char**) input_table_quoted)); - strv_free_erase(l); + l = strv_free_erase(l); - /* missing last quote ignores the last element. */ - l = strv_split_full(" 'one' \" two\t three \" ' four five' ' ignored element ", NULL, SPLIT_QUOTES); - assert_se(l); - assert_se(strv_equal(l, (char**) input_table_quoted)); + /* missing last quote causes extraction to fail. */ + assert_se(strv_split_full(&l, " 'one' \" two\t three \" ' four five", NULL, EXTRACT_UNQUOTE) == -EINVAL); + assert_se(!l); - strv_free_erase(l); - - /* missing last quote, but the last element is _not_ ignored with SPLIT_RELAX. */ - l = strv_split_full(" 'one' \" two\t three \" ' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX); - assert_se(l); + /* missing last quote, but the last element is _not_ ignored with EXTRACT_RELAX. */ + assert_se(strv_split_full(&l, " 'one' \" two\t three \" ' four five", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 3); assert_se(strv_equal(l, (char**) input_table_quoted)); - strv_free_erase(l); + l = strv_free_erase(l); - /* missing separator between */ - l = strv_split_full(" 'one' \" two\t three \"' four five'", NULL, SPLIT_QUOTES | SPLIT_RELAX); - assert_se(l); - assert_se(strv_equal(l, (char**) input_table_quoted)); + /* missing separator between items */ + assert_se(strv_split_full(&l, " 'one' \" two\t three \"' four five'", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 2); + assert_se(strv_equal(l, (char**) input_table_quoted_joined)); - strv_free_erase(l); + l = strv_free_erase(l); - l = strv_split_full(" 'one' \" two\t three \"' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX); - assert_se(l); - assert_se(strv_equal(l, (char**) input_table_quoted)); + assert_se(strv_split_full(&l, " 'one' \" two\t three \"' four five", NULL, + EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_CUNESCAPE_RELAX) == 2); + assert_se(strv_equal(l, (char**) input_table_quoted_joined)); - strv_free_erase(l); + l = strv_free_erase(l); - l = strv_split_full("\\", NULL, SPLIT_QUOTES | SPLIT_RELAX); - assert_se(l); + assert_se(strv_split_full(&l, "\\", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_CUNESCAPE_RELAX) == 1); assert_se(strv_equal(l, STRV_MAKE("\\"))); } @@ -333,71 +331,70 @@ static void test_strv_split_empty(void) { l = strv_split("", WHITESPACE); assert_se(l); assert_se(strv_isempty(l)); + l = strv_free(l); - strv_free(l); - l = strv_split("", NULL); - assert_se(l); + assert_se(l = strv_split("", NULL)); assert_se(strv_isempty(l)); + l = strv_free(l); - strv_free(l); - l = strv_split_full("", NULL, 0); + assert_se(strv_split_full(&l, "", NULL, 0) == 0); assert_se(l); assert_se(strv_isempty(l)); + l = strv_free(l); - strv_free(l); - l = strv_split_full("", NULL, SPLIT_QUOTES); + assert_se(strv_split_full(&l, "", NULL, EXTRACT_UNQUOTE) == 0); assert_se(l); assert_se(strv_isempty(l)); + l = strv_free(l); - strv_free(l); - l = strv_split_full("", WHITESPACE, SPLIT_QUOTES); + assert_se(strv_split_full(&l, "", WHITESPACE, EXTRACT_UNQUOTE) == 0); assert_se(l); assert_se(strv_isempty(l)); + l = strv_free(l); - strv_free(l); - l = strv_split_full("", WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX); + assert_se(strv_split_full(&l, "", WHITESPACE, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 0); assert_se(l); assert_se(strv_isempty(l)); - strv_free(l); + l = strv_split(" ", WHITESPACE); assert_se(l); assert_se(strv_isempty(l)); - strv_free(l); + l = strv_split(" ", NULL); assert_se(l); assert_se(strv_isempty(l)); + l = strv_free(l); - strv_free(l); - l = strv_split_full(" ", NULL, 0); + assert_se(strv_split_full(&l, " ", NULL, 0) == 0); assert_se(l); assert_se(strv_isempty(l)); + l = strv_free(l); - strv_free(l); - l = strv_split_full(" ", WHITESPACE, SPLIT_QUOTES); + assert_se(strv_split_full(&l, " ", WHITESPACE, EXTRACT_UNQUOTE) == 0); assert_se(l); assert_se(strv_isempty(l)); + l = strv_free(l); - strv_free(l); - l = strv_split_full(" ", NULL, SPLIT_QUOTES); + assert_se(strv_split_full(&l, " ", NULL, EXTRACT_UNQUOTE) == 0); assert_se(l); assert_se(strv_isempty(l)); + l = strv_free(l); - strv_free(l); - l = strv_split_full(" ", NULL, SPLIT_QUOTES | SPLIT_RELAX); + assert_se(strv_split_full(&l, " ", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX) == 0); assert_se(l); assert_se(strv_isempty(l)); } -static void test_strv_split_extract(void) { +static void test_strv_split_full(void) { _cleanup_strv_free_ char **l = NULL; const char *str = ":foo\\:bar::waldo:"; int r; log_info("/* %s */", __func__); - r = strv_split_extract(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + r = strv_split_full(&l, str, ":", EXTRACT_DONT_COALESCE_SEPARATORS); assert_se(r == (int) strv_length(l)); assert_se(streq_ptr(l[0], "")); assert_se(streq_ptr(l[1], "foo:bar")); @@ -1026,7 +1023,7 @@ int main(int argc, char *argv[]) { test_strv_split(); test_strv_split_empty(); - test_strv_split_extract(); + test_strv_split_full(); test_strv_split_colon_pairs(); test_strv_split_newlines(); test_strv_split_nulstr(); diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c index b41fbfc39a..fdb98101d3 100644 --- a/src/udev/udev-builtin.c +++ b/src/udev/udev-builtin.c @@ -109,6 +109,7 @@ UdevBuiltinCommand udev_builtin_lookup(const char *command) { int udev_builtin_run(sd_device *dev, UdevBuiltinCommand cmd, const char *command, bool test) { _cleanup_strv_free_ char **argv = NULL; + int r; assert(dev); assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX); @@ -117,9 +118,10 @@ int udev_builtin_run(sd_device *dev, UdevBuiltinCommand cmd, const char *command if (!builtins[cmd]) return -EOPNOTSUPP; - argv = strv_split_full(command, NULL, SPLIT_QUOTES | SPLIT_RELAX); - if (!argv) - return -ENOMEM; + r = strv_split_full(&argv, command, NULL, + EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return r; /* we need '0' here to reset the internal state */ optind = 0; diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index e7208c62b0..7c78b4c680 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -747,9 +747,9 @@ int udev_event_spawn(UdevEvent *event, return log_device_error_errno(event->dev, errno, "Failed to create pipe for command '%s': %m", cmd); - argv = strv_split_full(cmd, NULL, SPLIT_QUOTES|SPLIT_RELAX); - if (!argv) - return log_oom(); + r = strv_split_full(&argv, cmd, NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return log_device_error_errno(event->dev, r, "Failed to split command: %m"); if (isempty(argv[0])) return log_device_error_errno(event->dev, SYNTHETIC_ERRNO(EINVAL), diff --git a/src/xdg-autostart-generator/xdg-autostart-service.c b/src/xdg-autostart-generator/xdg-autostart-service.c index 4a30f9e433..80643236d5 100644 --- a/src/xdg-autostart-generator/xdg-autostart-service.c +++ b/src/xdg-autostart-generator/xdg-autostart-service.c @@ -385,9 +385,9 @@ int xdg_autostart_format_exec_start( * NOTE: Technically, XDG only specifies " as quotes, while this also * accepts '. */ - exec_split = strv_split_full(exec, WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX); - if (!exec_split) - return -ENOMEM; + r = strv_split_full(&exec_split, exec, NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX); + if (r < 0) + return r; if (strv_isempty(exec_split)) return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Exec line is empty"); |