From 0ef84b80c59b6d15c344b96e3040b2ee367e33ac Mon Sep 17 00:00:00 2001 From: =?utf8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 12 Jan 2020 12:14:31 +0100 Subject: [PATCH] networkctl: return error or warning when interfaces are not matched We'd just print nothing and exit with 0. If the user gave an explicit name, we should fail. If a pattern didn't match, we should at least warn. $ networkctl status enx54ee75cb1dc0a* --no-pager && echo $? No interfaces matched. 0 $ networkctl status enx54ee75cb1dc0a --no-pager Interface "enx54ee75cb1dc0a" not found. 1 --- src/basic/strv.c | 11 ++++++----- src/basic/strv.h | 5 ++++- src/network/networkctl.c | 39 +++++++++++++++++++++++++++++++++++---- src/test/test-strv.c | 8 +++++--- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/basic/strv.c b/src/basic/strv.c index 99bcfde089d..303d9798594 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -800,12 +800,13 @@ char **strv_shell_escape(char **l, const char *bad) { return l; } -bool strv_fnmatch(char* const* patterns, const char *s, int flags) { - char* const* p; - - STRV_FOREACH(p, patterns) - if (fnmatch(*p, s, flags) == 0) +bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos) { + for (size_t i = 0; patterns && patterns[i]; i++) + if (fnmatch(patterns[i], s, flags) == 0) { + if (matched_pos) + *matched_pos = i; return true; + } return false; } diff --git a/src/basic/strv.h b/src/basic/strv.h index f335aeee32f..1cae0ae09fd 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -177,7 +177,10 @@ void strv_print(char * const *l); char **strv_reverse(char **l); char **strv_shell_escape(char **l, const char *bad); -bool strv_fnmatch(char* const* patterns, const char *s, int flags); +bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos); +static inline bool strv_fnmatch(char* const* patterns, const char *s, int flags) { + return strv_fnmatch_full(patterns, s, flags, NULL); +} static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) { assert(s); diff --git a/src/network/networkctl.c b/src/network/networkctl.c index e6dc70a0e2b..284a59bb6f4 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -27,6 +27,7 @@ #include "fd-util.h" #include "format-table.h" #include "format-util.h" +#include "glob-util.h" #include "hwdb-util.h" #include "local-addresses.h" #include "locale-util.h" @@ -266,7 +267,7 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) { return 0; } -static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) { +static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, bool matched_patterns[]) { _cleanup_strv_free_ char **altnames = NULL; const char *name; int ifindex, r; @@ -296,20 +297,26 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) { if (patterns) { char str[DECIMAL_STR_MAX(int)]; + size_t pos; + + assert(matched_patterns); xsprintf(str, "%i", ifindex); - if (!strv_fnmatch(patterns, str, 0) && !strv_fnmatch(patterns, name, 0)) { + if (!strv_fnmatch_full(patterns, str, 0, &pos) && + !strv_fnmatch_full(patterns, name, 0, &pos)) { bool match = false; char **p; STRV_FOREACH(p, altnames) - if (strv_fnmatch(patterns, *p, 0)) { + if (strv_fnmatch_full(patterns, *p, 0, &pos)) { match = true; break; } if (!match) return 0; } + + matched_patterns[pos] = true; } r = sd_rtnl_message_link_get_type(m, &info->iftype); @@ -464,11 +471,18 @@ static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, Lin if (r < 0) return log_error_errno(r, "Failed to enumerate links: %m"); + _cleanup_free_ bool *matched_patterns = NULL; + if (patterns) { + matched_patterns = new0(bool, strv_length(patterns)); + if (!matched_patterns) + return log_oom(); + } + for (i = reply; i; i = sd_netlink_message_next(i)) { if (!GREEDY_REALLOC0(links, allocated, c + 2)) /* We keep one trailing one as marker */ return -ENOMEM; - r = decode_link(i, links + c, patterns); + r = decode_link(i, links + c, patterns, matched_patterns); if (r < 0) return r; if (r == 0) @@ -486,6 +500,20 @@ static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, Lin c++; } + /* Look if we matched all our arguments that are not globs. It + * is OK for a glob to match nothing, but not for an exact argument. */ + for (size_t pos = 0; pos < strv_length(patterns); pos++) { + if (matched_patterns[pos]) + continue; + + if (string_is_glob(patterns[pos])) + log_debug("Pattern \"%s\" doesn't match any interface, ignoring.", + patterns[pos]); + else + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), + "Interface \"%s\" not found.", patterns[pos]); + } + typesafe_qsort(links, c, link_info_compare); if (bus) @@ -494,6 +522,9 @@ static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, Lin *ret = TAKE_PTR(links); + if (patterns && c == 0) + log_warning("No interfaces matched."); + return (int) c; } diff --git a/src/test/test-strv.c b/src/test/test-strv.c index f31ea6f8c61..b8001dfe70a 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -924,14 +924,16 @@ static void test_foreach_string(void) { static void test_strv_fnmatch(void) { _cleanup_strv_free_ char **v = NULL; + size_t pos; log_info("/* %s */", __func__); assert_se(!strv_fnmatch(STRV_MAKE_EMPTY, "a", 0)); - v = strv_new("*\\*"); - assert_se(!strv_fnmatch(v, "\\", 0)); - assert_se(strv_fnmatch(v, "\\", FNM_NOESCAPE)); + v = strv_new("xxx", "*\\*", "yyy"); + assert_se(!strv_fnmatch_full(v, "\\", 0, NULL)); + assert_se(strv_fnmatch_full(v, "\\", FNM_NOESCAPE, &pos)); + assert(pos == 1); } int main(int argc, char *argv[]) { -- 2.47.3