From 0ef84b80c59b6d15c344b96e3040b2ee367e33ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 12 Jan 2020 12:14:31 +0100 Subject: [PATCH 1/2] 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[]) { From 191a3f1634512f37ff01d63f8a4c2e6be41be350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 12 Jan 2020 12:26:26 +0100 Subject: [PATCH 2/2] basic/strv: drop flags argument from strv_fnmatch() --- src/analyze/analyze.c | 8 ++++---- src/basic/strv.h | 6 +++--- src/network/networkd-link.c | 4 ++-- src/network/wait-online/manager.c | 2 +- src/test/test-strv.c | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 991e61de7e7..9674a8af87d 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -1162,9 +1162,9 @@ static int graph_one_property( assert(prop); assert(color); - match_patterns = strv_fnmatch(patterns, u->id, 0); + match_patterns = strv_fnmatch(patterns, u->id); - if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id, 0)) + if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id)) return 0; r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units); @@ -1174,9 +1174,9 @@ static int graph_one_property( STRV_FOREACH(unit, units) { bool match_patterns2; - match_patterns2 = strv_fnmatch(patterns, *unit, 0); + match_patterns2 = strv_fnmatch(patterns, *unit); - if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit, 0)) + if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit)) continue; if (!strv_isempty(patterns) && !match_patterns && !match_patterns2) diff --git a/src/basic/strv.h b/src/basic/strv.h index 1cae0ae09fd..16faf9e7f25 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -178,14 +178,14 @@ char **strv_reverse(char **l); char **strv_shell_escape(char **l, const char *bad); 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(char* const* patterns, const char *s) { + return strv_fnmatch_full(patterns, s, 0, NULL); } static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) { assert(s); return strv_isempty(patterns) || - strv_fnmatch(patterns, s, flags); + strv_fnmatch_full(patterns, s, flags, NULL); } char ***strv_free_free(char ***l); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index d9a695b3a41..07c6cf34510 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1875,7 +1875,7 @@ static int link_new_bound_by_list(Link *link) { if (strv_isempty(carrier->network->bind_carrier)) continue; - if (strv_fnmatch(carrier->network->bind_carrier, link->ifname, 0)) { + if (strv_fnmatch(carrier->network->bind_carrier, link->ifname)) { r = link_put_carrier(link, carrier, &link->bound_by_links); if (r < 0) return r; @@ -1917,7 +1917,7 @@ static int link_new_bound_to_list(Link *link) { m = link->manager; HASHMAP_FOREACH (carrier, m->links, i) { - if (strv_fnmatch(link->network->bind_carrier, carrier->ifname, 0)) { + if (strv_fnmatch(link->network->bind_carrier, carrier->ifname)) { r = link_put_carrier(link, carrier, &link->bound_to_links); if (r < 0) return r; diff --git a/src/network/wait-online/manager.c b/src/network/wait-online/manager.c index 6de3df94e1f..0e6a284da2f 100644 --- a/src/network/wait-online/manager.c +++ b/src/network/wait-online/manager.c @@ -29,7 +29,7 @@ static bool manager_ignore_link(Manager *m, Link *link) { return true; /* ignore interfaces we explicitly are asked to ignore */ - return strv_fnmatch(m->ignore, link->ifname, 0); + return strv_fnmatch(m->ignore, link->ifname); } static int manager_link_is_online(Manager *m, Link *l, LinkOperationalState s) { diff --git a/src/test/test-strv.c b/src/test/test-strv.c index b8001dfe70a..b63cb4b63e9 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -928,7 +928,7 @@ static void test_strv_fnmatch(void) { log_info("/* %s */", __func__); - assert_se(!strv_fnmatch(STRV_MAKE_EMPTY, "a", 0)); + assert_se(!strv_fnmatch(STRV_MAKE_EMPTY, "a")); v = strv_new("xxx", "*\\*", "yyy"); assert_se(!strv_fnmatch_full(v, "\\", 0, NULL));