diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c index 6addb76f1b..5fac467185 100644 --- a/src/basic/hash-funcs.c +++ b/src/basic/hash-funcs.c @@ -4,6 +4,7 @@ #include "hash-funcs.h" #include "path-util.h" +#include "strv.h" void string_hash_func(const char *p, struct siphash *state) { siphash24_compress(p, strlen(p) + 1, state); @@ -15,6 +16,9 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free, DEFINE_HASH_OPS_FULL(string_hash_ops_free_free, char, string_hash_func, string_compare_func, free, void, free); +DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free, + char, string_hash_func, string_compare_func, free, + char*, strv_free); void path_hash_func(const char *q, struct siphash *state) { bool add_slash = false; diff --git a/src/basic/hash-funcs.h b/src/basic/hash-funcs.h index c537c6af7e..c14302ec72 100644 --- a/src/basic/hash-funcs.h +++ b/src/basic/hash-funcs.h @@ -78,6 +78,7 @@ void string_hash_func(const char *p, struct siphash *state); extern const struct hash_ops string_hash_ops; extern const struct hash_ops string_hash_ops_free; extern const struct hash_ops string_hash_ops_free_free; +extern const struct hash_ops string_hash_ops_free_strv_free; void path_hash_func(const char *p, struct siphash *state); extern const struct hash_ops path_hash_ops; diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c index 39f769c35c..01d45a941f 100644 --- a/src/libsystemd/sd-device/device-enumerator.c +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -157,9 +157,7 @@ _public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumer else hashmap = &enumerator->nomatch_sysattr; - /* Do not use string_has_ops_free_free or hashmap_put_strdup() here, as this may be called - * multiple times with the same sysattr but different value. */ - r = hashmap_put_strdup_full(hashmap, &trivial_hash_ops_free_free, sysattr, value); + r = update_match_strv(hashmap, sysattr, value, /* clear_on_null = */ true); if (r <= 0) return r; @@ -174,9 +172,7 @@ _public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enume assert_return(enumerator, -EINVAL); assert_return(property, -EINVAL); - /* Do not use string_has_ops_free_free or hashmap_put_strdup() here, as this may be called - * multiple times with the same property but different value. */ - r = hashmap_put_strdup_full(&enumerator->match_property, &trivial_hash_ops_free_free, property, value); + r = update_match_strv(&enumerator->match_property, property, value, /* clear_on_null = */ false); if (r <= 0) return r; @@ -466,29 +462,25 @@ int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *de } static bool match_property(sd_device_enumerator *enumerator, sd_device *device) { - const char *property; - const char *value; + const char *property_pattern; + char * const *value_patterns; assert(enumerator); assert(device); + /* Unlike device_match_sysattr(), this accepts device that has at least one matching property. */ + if (hashmap_isempty(enumerator->match_property)) return true; - HASHMAP_FOREACH_KEY(value, property, enumerator->match_property) { - const char *property_dev, *value_dev; + HASHMAP_FOREACH_KEY(value_patterns, property_pattern, enumerator->match_property) { + const char *property, *value; - FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) { - if (fnmatch(property, property_dev, 0) != 0) + FOREACH_DEVICE_PROPERTY(device, property, value) { + if (fnmatch(property_pattern, property, 0) != 0) continue; - if (!value && !value_dev) - return true; - - if (!value || !value_dev) - continue; - - if (fnmatch(value, value_dev, 0) == 0) + if (strv_fnmatch(value_patterns, value)) return true; } } diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c index e8913c3d1f..459dbdf4e4 100644 --- a/src/libsystemd/sd-device/device-monitor.c +++ b/src/libsystemd/sd-device/device-monitor.c @@ -787,7 +787,7 @@ _public_ int sd_device_monitor_filter_add_match_sysattr(sd_device_monitor *m, co hashmap = &m->nomatch_sysattr_filter; /* TODO: unset m->filter_uptodate on success when we support this filter on BPF. */ - return hashmap_put_strdup_full(hashmap, &trivial_hash_ops_free_free, sysattr, value); + return update_match_strv(hashmap, sysattr, value, /* clear_on_null = */ true); } _public_ int sd_device_monitor_filter_add_match_parent(sd_device_monitor *m, sd_device *device, int match) { diff --git a/src/libsystemd/sd-device/device-util.c b/src/libsystemd/sd-device/device-util.c index 616c16c1fc..3b8689e0d6 100644 --- a/src/libsystemd/sd-device/device-util.c +++ b/src/libsystemd/sd-device/device-util.c @@ -5,7 +5,65 @@ #include "device-util.h" #include "path-util.h" -static bool device_match_sysattr_value(sd_device *device, const char *sysattr, const char *match_value) { +int update_match_strv(Hashmap **match_strv, const char *key, const char *value, bool clear_on_null) { + char **strv; + int r; + + assert(match_strv); + assert(key); + + strv = hashmap_get(*match_strv, key); + if (strv) { + if (!value) { + char **v; + + if (strv_isempty(strv) || !clear_on_null) + return 0; + + /* Accept all value. Clear previous assignment. */ + + v = new0(char*, 1); + if (!v) + return -ENOMEM; + + strv_free_and_replace(strv, v); + } else { + if (strv_contains(strv, value)) + return 0; + + r = strv_extend(&strv, value); + if (r < 0) + return r; + } + + r = hashmap_update(*match_strv, key, strv); + if (r < 0) + return r; + + } else { + _cleanup_strv_free_ char **strv_alloc = NULL; + _cleanup_free_ char *key_alloc = NULL; + + key_alloc = strdup(key); + if (!key_alloc) + return -ENOMEM; + + strv_alloc = strv_new(value); + if (!strv_alloc) + return -ENOMEM; + + r = hashmap_ensure_put(match_strv, &string_hash_ops_free_strv_free, key_alloc, strv_alloc); + if (r < 0) + return r; + + TAKE_PTR(key_alloc); + TAKE_PTR(strv_alloc); + } + + return 1; +} + +static bool device_match_sysattr_value(sd_device *device, const char *sysattr, char * const *patterns) { const char *value; assert(device); @@ -14,27 +72,21 @@ static bool device_match_sysattr_value(sd_device *device, const char *sysattr, c if (sd_device_get_sysattr_value(device, sysattr, &value) < 0) return false; - if (!match_value) - return true; - - if (fnmatch(match_value, value, 0) == 0) - return true; - - return false; + return strv_fnmatch_or_empty(patterns, value, 0); } bool device_match_sysattr(sd_device *device, Hashmap *match_sysattr, Hashmap *nomatch_sysattr) { + char * const *patterns; const char *sysattr; - const char *value; assert(device); - HASHMAP_FOREACH_KEY(value, sysattr, match_sysattr) - if (!device_match_sysattr_value(device, sysattr, value)) + HASHMAP_FOREACH_KEY(patterns, sysattr, match_sysattr) + if (!device_match_sysattr_value(device, sysattr, patterns)) return false; - HASHMAP_FOREACH_KEY(value, sysattr, nomatch_sysattr) - if (device_match_sysattr_value(device, sysattr, value)) + HASHMAP_FOREACH_KEY(patterns, sysattr, nomatch_sysattr) + if (device_match_sysattr_value(device, sysattr, patterns)) return false; return true; diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h index 4eda2abaf3..d9e9f1e8c4 100644 --- a/src/libsystemd/sd-device/device-util.h +++ b/src/libsystemd/sd-device/device-util.h @@ -82,5 +82,6 @@ #define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__) #define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__) +int update_match_strv(Hashmap **match_strv, const char *key, const char *value, bool clear_on_null); bool device_match_sysattr(sd_device *device, Hashmap *match_sysattr, Hashmap *nomatch_sysattr); bool device_match_parent(sd_device *device, Set *match_parent, Set *nomatch_parent); diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c index 91e358e464..d625dde85d 100644 --- a/src/libsystemd/sd-device/test-sd-device.c +++ b/src/libsystemd/sd-device/test-sd-device.c @@ -310,6 +310,49 @@ TEST(sd_device_enumerator_filter_subsystem) { assert_se(n_new_dev + n_removed_dev <= 10); } +TEST(sd_device_enumerator_add_match_sysattr) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *dev; + int ifindex; + + assert_se(sd_device_enumerator_new(&e) >= 0); + assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0); + assert_se(sd_device_enumerator_add_match_subsystem(e, "net", true) >= 0); + assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true) >= 0); + assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "hoge", true) >= 0); + assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "foo", true) >= 0); + assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "bar", false) >= 0); + assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "baz", false) >= 0); + + dev = sd_device_enumerator_get_device_first(e); + assert_se(dev); + assert_se(sd_device_get_ifindex(dev, &ifindex) >= 0); + assert_se(ifindex == 1); + + assert_se(!sd_device_enumerator_get_device_next(e)); +} + +TEST(sd_device_enumerator_add_match_property) { + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *dev; + int ifindex; + + assert_se(sd_device_enumerator_new(&e) >= 0); + assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0); + assert_se(sd_device_enumerator_add_match_subsystem(e, "net", true) >= 0); + assert_se(sd_device_enumerator_add_match_sysattr(e, "ifindex", "1", true) >= 0); + assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", "1*") >= 0); + assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", "hoge") >= 0); + assert_se(sd_device_enumerator_add_match_property(e, "IFINDE*", NULL) >= 0); + assert_se(sd_device_enumerator_add_match_property(e, "AAAAA", "BBBB") >= 0); + assert_se(sd_device_enumerator_add_match_property(e, "FOOOO", NULL) >= 0); + + dev = sd_device_enumerator_get_device_first(e); + assert_se(dev); + assert_se(sd_device_get_ifindex(dev, &ifindex) >= 0); + assert_se(ifindex == 1); +} + TEST(sd_device_new_from_nulstr) { const char *devlinks = "/dev/disk/by-partuuid/1290d63a-42cc-4c71-b87c-xxxxxxxxxxxx\0"