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
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2020-01-12 12:14:31 +01:00
parent 0e05be8405
commit 0ef84b80c5
4 changed files with 50 additions and 13 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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[]) {