From af0b60b37c8bc5e5206d41e00f31f2682a51f21e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Sep 2018 19:17:51 +0900 Subject: [PATCH 1/8] strv: introduce strv_split_full() which optionally handle quotes --- src/basic/strv.c | 6 +++--- src/basic/strv.h | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/basic/strv.c b/src/basic/strv.c index dc72f036ac..ffc7b98d70 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -245,7 +245,7 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { return 0; } -char **strv_split(const char *s, const char *separator) { +char **strv_split_full(const char *s, const char *separator, bool quoted) { const char *word, *state; size_t l; size_t n, i; @@ -258,7 +258,7 @@ char **strv_split(const char *s, const char *separator) { return new0(char*, 1); n = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) + _FOREACH_WORD(word, l, s, separator, quoted, state) n++; r = new(char*, n+1); @@ -266,7 +266,7 @@ char **strv_split(const char *s, const char *separator) { return NULL; i = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { + _FOREACH_WORD(word, l, s, separator, quoted, state) { r[i] = strndup(word, l); if (!r[i]) { strv_free(r); diff --git a/src/basic/strv.h b/src/basic/strv.h index 34a660cb92..03fb5cc2b2 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -66,7 +66,10 @@ static inline bool strv_isempty(char * const *l) { return !l || !*l; } -char **strv_split(const char *s, const char *separator); +char **strv_split_full(const char *s, const char *separator, bool quoted); +static inline char **strv_split(const char *s, const char *separator) { + return strv_split_full(s, separator, false); +} char **strv_split_newlines(const char *s); int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); From 2c3a11d86efdf54493ac18719af5aa76b0483d51 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Sep 2018 19:20:43 +0900 Subject: [PATCH 2/8] strv: allow to set separator NULL in strv_split() or strv_split_full() --- src/basic/strv.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/basic/strv.c b/src/basic/strv.c index ffc7b98d70..0647a472d7 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -253,6 +253,9 @@ char **strv_split_full(const char *s, const char *separator, bool quoted) { assert(s); + if (!separator) + separator = WHITESPACE; + s += strspn(s, separator); if (isempty(s)) return new0(char*, 1); From 8059aa9c92fe1f9847fc33b11670d3f48ad92fb4 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Sep 2018 22:17:40 +0900 Subject: [PATCH 3/8] strv: introduce 'relax' mode to strv_split_full() If SPLIT_RELAX is specified, then it accepts unfinished quotes or missing separator after right quote. --- src/basic/string-util.c | 12 ++++++++---- src/basic/string-util.h | 15 ++++++++++----- src/basic/strv.c | 6 +++--- src/basic/strv.h | 5 +++-- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 0a40683493..07b11d4fc8 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -128,7 +128,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) { } /* Split a string into words. */ -const char* split(const char **state, size_t *l, const char *separator, bool quoted) { +const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) { const char *current; current = *state; @@ -144,20 +144,24 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo return NULL; } - if (quoted && strchr("\'\"", *current)) { + if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) { char quotechars[2] = {*current, '\0'}; *l = strcspn_escaped(current + 1, quotechars); if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] || (current[*l + 2] && !strchr(separator, current[*l + 2]))) { /* right quote missing or garbage at the end */ + if (flags & SPLIT_RELAX) { + *state = current + *l + 1 + (current[*l + 1] != '\0'); + return current + 1; + } *state = current; return NULL; } *state = current++ + *l + 2; - } else if (quoted) { + } else if (flags & SPLIT_QUOTES) { *l = strcspn_escaped(current, separator); - if (current[*l] && !strchr(separator, current[*l])) { + if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) { /* unfinished escape */ *state = current; return NULL; diff --git a/src/basic/string-util.h b/src/basic/string-util.h index fcd12f3a30..a337dbc35f 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -81,16 +81,21 @@ char *endswith_no_case(const char *s, const char *postfix) _pure_; char *first_word(const char *s, const char *word) _pure_; -const char* split(const char **state, size_t *l, const char *separator, bool quoted); +typedef enum SplitFlags { + SPLIT_QUOTES = 0x01 << 0, + SPLIT_RELAX = 0x01 << 1, +} SplitFlags; + +const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags); #define FOREACH_WORD(word, length, s, state) \ - _FOREACH_WORD(word, length, s, WHITESPACE, false, state) + _FOREACH_WORD(word, length, s, WHITESPACE, 0, state) #define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \ - _FOREACH_WORD(word, length, s, separator, false, state) + _FOREACH_WORD(word, length, s, separator, 0, state) -#define _FOREACH_WORD(word, length, s, separator, quoted, state) \ - for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) +#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 *strappend(const char *s, const char *suffix); char *strnappend(const char *s, const char *suffix, size_t length); diff --git a/src/basic/strv.c b/src/basic/strv.c index 0647a472d7..1bab723e88 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -245,7 +245,7 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { return 0; } -char **strv_split_full(const char *s, const char *separator, bool quoted) { +char **strv_split_full(const char *s, const char *separator, SplitFlags flags) { const char *word, *state; size_t l; size_t n, i; @@ -261,7 +261,7 @@ char **strv_split_full(const char *s, const char *separator, bool quoted) { return new0(char*, 1); n = 0; - _FOREACH_WORD(word, l, s, separator, quoted, state) + _FOREACH_WORD(word, l, s, separator, flags, state) n++; r = new(char*, n+1); @@ -269,7 +269,7 @@ char **strv_split_full(const char *s, const char *separator, bool quoted) { return NULL; i = 0; - _FOREACH_WORD(word, l, s, separator, quoted, state) { + _FOREACH_WORD(word, l, s, separator, flags, state) { r[i] = strndup(word, l); if (!r[i]) { strv_free(r); diff --git a/src/basic/strv.h b/src/basic/strv.h index 03fb5cc2b2..e9e6063f58 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "extract-word.h" #include "macro.h" +#include "string-util.h" #include "util.h" char *strv_find(char **l, const char *name) _pure_; @@ -66,9 +67,9 @@ static inline bool strv_isempty(char * const *l) { return !l || !*l; } -char **strv_split_full(const char *s, const char *separator, bool quoted); +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, false); + return strv_split_full(s, separator, 0); } char **strv_split_newlines(const char *s); From 1f938af8117df784fad2c8f890da4acaee9d1fd3 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Sep 2018 22:50:02 +0900 Subject: [PATCH 4/8] test: add tests for strv_split_full() --- src/test/test-strv.c | 111 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 7 deletions(-) diff --git a/src/test/test-strv.c b/src/test/test-strv.c index 5b0b9547b7..5e392c75ac 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -63,6 +63,13 @@ static const char* const input_table_multiple[] = { NULL, }; +static const char* const input_table_quoted[] = { + "one", + " two\t three ", + " four five", + NULL, +}; + static const char* const input_table_one[] = { "one", NULL, @@ -206,23 +213,64 @@ static void test_invalid_unquote(const char *quoted) { } static void test_strv_split(void) { - char **s; - unsigned i = 0; _cleanup_strv_free_ char **l = NULL; const char str[] = "one,two,three"; l = strv_split(str, ","); assert_se(l); - STRV_FOREACH(s, l) - assert_se(streq(*s, input_table_multiple[i++])); + assert_se(strv_equal(l, (char**) input_table_multiple)); - i = 0; strv_free(l); l = strv_split(" one two\t three", WHITESPACE); assert_se(l); - STRV_FOREACH(s, l) - assert_se(streq(*s, input_table_multiple[i++])); + assert_se(strv_equal(l, (char**) input_table_multiple)); + + strv_free(l); + + /* Setting NULL for separator is equivalent to WHITESPACE */ + l = strv_split(" one two\t three", NULL); + assert_se(l); + assert_se(strv_equal(l, (char**) input_table_multiple)); + + strv_free(l); + + l = strv_split_full(" one two\t three", NULL, 0); + assert_se(l); + assert_se(strv_equal(l, (char**) input_table_multiple)); + + strv_free(l); + + l = strv_split_full(" 'one' \" two\t three \" ' four five'", NULL, SPLIT_QUOTES); + assert_se(l); + assert_se(strv_equal(l, (char**) input_table_quoted)); + + strv_free(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)); + + strv_free(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); + assert_se(strv_equal(l, (char**) input_table_quoted)); + + strv_free(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)); + + strv_free(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)); } static void test_strv_split_empty(void) { @@ -232,11 +280,60 @@ static void test_strv_split_empty(void) { assert_se(l); assert_se(strv_isempty(l)); + strv_free(l); + l = strv_split("", NULL); + assert_se(l); + assert_se(strv_isempty(l)); + + strv_free(l); + l = strv_split_full("", NULL, 0); + assert_se(l); + assert_se(strv_isempty(l)); + + strv_free(l); + l = strv_split_full("", NULL, SPLIT_QUOTES); + assert_se(l); + assert_se(strv_isempty(l)); + + strv_free(l); + l = strv_split_full("", WHITESPACE, SPLIT_QUOTES); + assert_se(l); + assert_se(strv_isempty(l)); + + strv_free(l); + l = strv_split_full("", WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX); + 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)); + + strv_free(l); + l = strv_split_full(" ", NULL, 0); + assert_se(l); + assert_se(strv_isempty(l)); + + strv_free(l); + l = strv_split_full(" ", WHITESPACE, SPLIT_QUOTES); + assert_se(l); + assert_se(strv_isempty(l)); + + strv_free(l); + l = strv_split_full(" ", NULL, SPLIT_QUOTES); + assert_se(l); + assert_se(strv_isempty(l)); + + strv_free(l); + l = strv_split_full(" ", NULL, SPLIT_QUOTES | SPLIT_RELAX); + assert_se(l); + assert_se(strv_isempty(l)); } static void test_strv_split_extract(void) { From bd206af40242c62cbae708fa0405f083eb266720 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 26 Sep 2018 22:51:57 +0900 Subject: [PATCH 5/8] test: introduce test-udev-build-argv This intends to clarify the rule udev_build_argv() uses. --- src/test/meson.build | 12 +++++ src/test/test-udev-build-argv.c | 78 +++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/test/test-udev-build-argv.c diff --git a/src/test/meson.build b/src/test/meson.build index 21a8c3b12b..7e603cf1c0 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -632,6 +632,18 @@ tests += [ libacl], '', 'manual', '-DLOG_REALM=LOG_REALM_UDEV'], + [['src/test/test-udev-build-argv.c'], + [libudev_core, + libudev_static, + libsystemd_network, + libshared], + [threads, + librt, + libblkid, + libkmod, + libacl], + '', '', '-DLOG_REALM=LOG_REALM_UDEV'], + [['src/test/test-id128.c'], [], []], diff --git a/src/test/test-udev-build-argv.c b/src/test/test-udev-build-argv.c new file mode 100644 index 0000000000..c71c706e2e --- /dev/null +++ b/src/test/test-udev-build-argv.c @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "string-util.h" +#include "strv.h" +#include "tests.h" +#include "udev.h" + +static void test_udev_build_argv_one(const char *c) { + _cleanup_strv_free_ char **a = NULL; + _cleanup_free_ char *arg = NULL; + char *argv[128], **p; + int argc; + size_t i; + + assert_se(a = strv_split_full(c, NULL, SPLIT_QUOTES | SPLIT_RELAX)); + + assert_se(arg = strdup(c)); + assert_se(udev_build_argv(arg, &argc, argv) >= 0); + + log_info("command: %s", c); + + i = 0; + log_info("strv_split:"); + STRV_FOREACH(p, a) + log_info("argv[%zu] = '%s'", i++, *p); + + i = 0; + log_info("udev_build_argv:"); + STRV_FOREACH(p, argv) + log_info("argv[%zu] = '%s'", i++, *p); + + assert_se(strv_equal(argv, a)); + assert_se(argc == (int) strv_length(a)); + +} + +static void test_udev_build_argv(void) { + test_udev_build_argv_one("one two three"); + test_udev_build_argv_one("one 'two three ' \" four five \" 'aaa bbb "); + test_udev_build_argv_one("/bin/echo -e \\101"); + test_udev_build_argv_one("/bin/echo -n special-device"); + test_udev_build_argv_one("/bin/echo -n special-device"); + test_udev_build_argv_one("/bin/echo test"); + test_udev_build_argv_one("/bin/echo -n test-%b"); + test_udev_build_argv_one("/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9"); + test_udev_build_argv_one("/bin/sh -c 'echo foo3 foo4 foo5 foo6 foo7 foo8 foo9 | sed s/foo9/bar9/'"); + test_udev_build_argv_one("/bin/echo -n 'foo3 foo4' 'foo5 foo6 foo7 foo8'"); + test_udev_build_argv_one("/bin/sh -c 'printf %%s \\\"foo1 foo2\\\" | grep \\\"foo1 foo2\\\"'"); + test_udev_build_argv_one("/bin/sh -c \\\"printf %%s 'foo1 foo2' | grep 'foo1 foo2'\\\""); + test_udev_build_argv_one("/bin/sh -c 'printf \\\"%%s %%s\\\" \\\"foo1 foo2\\\" \\\"foo3\\\"| grep \\\"foo1 foo2\\\"'"); + test_udev_build_argv_one("/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9"); + test_udev_build_argv_one("/bin/echo -n foo3 foo4 foo5 foo6 foo7 foo8 foo9"); + test_udev_build_argv_one("/bin/echo -n foo"); + test_udev_build_argv_one("/bin/echo -n usb-%b"); + test_udev_build_argv_one("/bin/echo -n scsi-%b"); + test_udev_build_argv_one("/bin/echo -n foo-%b"); + test_udev_build_argv_one("/bin/echo test"); + test_udev_build_argv_one("/bin/echo symlink test this"); + test_udev_build_argv_one("/bin/echo symlink test this"); + test_udev_build_argv_one("/bin/echo link test this"); + test_udev_build_argv_one("/bin/echo -n node link1 link2"); + test_udev_build_argv_one("/bin/echo -n node link1 link2 link3 link4"); + test_udev_build_argv_one("/usr/bin/test -b %N"); + test_udev_build_argv_one("/bin/echo -e name; (/usr/bin/badprogram)"); + test_udev_build_argv_one("/bin/echo -e \\xc3\\xbcber"); + test_udev_build_argv_one("/bin/echo -e \\xef\\xe8garbage"); + test_udev_build_argv_one("/bin/echo 1 1 0400"); + test_udev_build_argv_one("/bin/echo 0 0 0400letsdoabuffferoverflow0123456789012345789012345678901234567890"); +} + +int main(int argc, char *argv[]) { + test_setup_logging(LOG_DEBUG); + + test_udev_build_argv(); + + return 0; +} From feaa6db7ac287b34887f75c27a7efc2107a7b691 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 23 Sep 2018 15:41:39 +0900 Subject: [PATCH 6/8] udev-event: replace udev_build_argv() by strv_split_full() This also modernize udev_event_spawn() a bit. --- src/udev/udev-event.c | 77 +++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index 45ebdfc51f..e46cf1563f 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -19,6 +19,7 @@ #include "fd-util.h" #include "format-util.h" #include "netlink-util.h" +#include "path-util.h" #include "process-util.h" #include "signal-util.h" #include "string-util.h" @@ -737,47 +738,44 @@ int udev_event_spawn(struct udev_event *event, bool accept_failure, const char *cmd, char *result, size_t ressize) { - int outpipe[2] = {-1, -1}; - int errpipe[2] = {-1, -1}; + _cleanup_close_pair_ int outpipe[2] = {-1, -1}, errpipe[2] = {-1, -1}; + _cleanup_strv_free_ char **argv = NULL; pid_t pid; - int err = 0; + int r; /* pipes from child to parent */ - if (result != NULL || log_get_max_level() >= LOG_INFO) { - if (pipe2(outpipe, O_NONBLOCK) != 0) { - err = log_error_errno(errno, "pipe failed: %m"); - goto out; - } - } - if (log_get_max_level() >= LOG_INFO) { - if (pipe2(errpipe, O_NONBLOCK) != 0) { - err = log_error_errno(errno, "pipe failed: %m"); - goto out; - } + if (!result || log_get_max_level() >= LOG_INFO) + if (pipe2(outpipe, O_NONBLOCK) != 0) + return log_error_errno(errno, "Failed to create pipe for command '%s': %m", cmd); + + if (log_get_max_level() >= LOG_INFO) + if (pipe2(errpipe, O_NONBLOCK) != 0) + return log_error_errno(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(); + + /* allow programs in /usr/lib/udev/ to be called without the path */ + if (!path_is_absolute(argv[0])) { + char *program; + + program = path_join(NULL, UDEVLIBEXECDIR, argv[0]); + if (!program) + return log_oom(); + + free_and_replace(argv[0], program); } - err = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_LOG, &pid); - if (err < 0) - goto out; - if (err == 0) { - char arg[UTIL_PATH_SIZE]; - char *argv[128]; - char program[UTIL_PATH_SIZE]; - + r = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_LOG, &pid); + if (r < 0) + return log_error_errno(r, "Failed to fork() to execute command '%s': %m", cmd); + if (r == 0) { /* child closes parent's ends of pipes */ outpipe[READ_END] = safe_close(outpipe[READ_END]); errpipe[READ_END] = safe_close(errpipe[READ_END]); - strscpy(arg, sizeof(arg), cmd); - udev_build_argv(arg, NULL, argv); - - /* allow programs in /usr/lib/udev/ to be called without the path */ - if (argv[0][0] != '/') { - strscpyl(program, sizeof(program), UDEVLIBEXECDIR "/", argv[0], NULL); - argv[0] = program; - } - - log_debug("starting '%s'", cmd); + log_debug("Starting '%s'", cmd); spawn_exec(event, cmd, argv, udev_device_get_properties_envp(event->dev), outpipe[WRITE_END], errpipe[WRITE_END]); @@ -795,18 +793,11 @@ int udev_event_spawn(struct udev_event *event, outpipe[READ_END], errpipe[READ_END], result, ressize); - err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure); + r = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure); + if (r < 0) + return log_error_errno(r, "Failed to wait spawned command '%s': %m", cmd); -out: - if (outpipe[READ_END] >= 0) - close(outpipe[READ_END]); - if (outpipe[WRITE_END] >= 0) - close(outpipe[WRITE_END]); - if (errpipe[READ_END] >= 0) - close(errpipe[READ_END]); - if (errpipe[WRITE_END] >= 0) - close(errpipe[WRITE_END]); - return err; + return r; } static int rename_netif(struct udev_event *event) { From 6d93eeb7c8b3cfa104ed2247fa457fbb55b3f1e1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 23 Sep 2018 15:49:49 +0900 Subject: [PATCH 7/8] udev-builtin: replace udev_build_argv() by strv_split_extract() --- src/udev/udev-builtin.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c index be7e995933..956dc30ed2 100644 --- a/src/udev/udev-builtin.c +++ b/src/udev/udev-builtin.c @@ -102,18 +102,18 @@ enum udev_builtin_cmd udev_builtin_lookup(const char *command) { } int udev_builtin_run(struct udev_device *dev, enum udev_builtin_cmd cmd, const char *command, bool test) { - char arg[UTIL_PATH_SIZE]; - int argc; - char *argv[128]; + _cleanup_strv_free_ char **argv = NULL; if (!builtins[cmd]) return -EOPNOTSUPP; + argv = strv_split_full(command, NULL, SPLIT_QUOTES | SPLIT_RELAX); + if (!argv) + return -ENOMEM; + /* we need '0' here to reset the internal state */ optind = 0; - strscpy(arg, sizeof(arg), command); - udev_build_argv(arg, &argc, argv); - return builtins[cmd]->cmd(dev, argc, argv, test); + return builtins[cmd]->cmd(dev, strv_length(argv), argv, test); } int udev_builtin_add_property(struct udev_device *dev, bool test, const char *key, const char *val) { From c1ee2674894a3856726e61ee891002ac6d7d3734 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sun, 23 Sep 2018 16:31:12 +0900 Subject: [PATCH 8/8] udev: use path_make_relative() --- src/udev/udev-node.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index 4080471a51..be253ccefe 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -20,34 +20,19 @@ #include "udev.h" static int node_symlink(struct udev_device *dev, const char *node, const char *slink) { - struct stat stats; - char target[UTIL_PATH_SIZE]; - char *s; - size_t l; + _cleanup_free_ char *slink_dirname = NULL, *target = NULL; char slink_tmp[UTIL_PATH_SIZE + 32]; - int i = 0; - int tail = 0; - int err = 0; + struct stat stats; + int r, err = 0; + + slink_dirname = dirname_malloc(slink); + if (!slink_dirname) + return log_oom(); /* use relative link */ - target[0] = '\0'; - while (node[i] && (node[i] == slink[i])) { - if (node[i] == '/') - tail = i+1; - i++; - } - s = target; - l = sizeof(target); - while (slink[i] != '\0') { - if (slink[i] == '/') - l = strpcpy(&s, l, "../"); - i++; - } - l = strscpy(s, l, &node[tail]); - if (l == 0) { - err = -EINVAL; - goto exit; - } + r = path_make_relative(slink_dirname, node, &target); + if (r < 0) + return log_error_errno(r, "Failed to get relative path from '%s' to '%s': %m", slink, node); /* preserve link with correct target, do not replace node of other device */ if (lstat(slink, &stats) == 0) {