diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index a3659d00b3..1bee5318d6 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -728,4 +728,8 @@ global: sd_event_source_set_time_relative; sd_bus_error_has_names_sentinel; + + sd_device_get_current_tag_first; + sd_device_get_current_tag_next; + sd_device_has_current_tag; } LIBSYSTEMD_246; diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h index 1fe5c1a6bf..9c6b8a345f 100644 --- a/src/libsystemd/sd-device/device-internal.h +++ b/src/libsystemd/sd-device/device-internal.h @@ -28,10 +28,10 @@ struct sd_device { Set *sysattrs; /* names of sysattrs */ Iterator sysattrs_iterator; - Set *tags; - Iterator tags_iterator; + Set *all_tags, *current_tags; + Iterator all_tags_iterator, current_tags_iterator; + uint64_t all_tags_iterator_generation, current_tags_iterator_generation; /* generation when iteration was started */ uint64_t tags_generation; /* changes whenever the tags are changed */ - uint64_t tags_iterator_generation; /* generation when iteration was started */ Set *devlinks; Iterator devlinks_iterator; @@ -71,7 +71,7 @@ struct sd_device { bool parent_set:1; /* no need to try to reload parent */ bool sysattrs_read:1; /* don't try to re-read sysattrs once read */ - bool property_tags_outdated:1; /* need to update TAGS= property */ + bool property_tags_outdated:1; /* need to update TAGS= or CURRENT_TAGS= property */ bool property_devlinks_outdated:1; /* need to update DEVLINKS= property */ bool properties_buf_outdated:1; /* need to reread hashmap */ bool sysname_set:1; /* don't reread sysname */ diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index 1e61732dfe..1ad7713ec7 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -329,7 +329,7 @@ static int device_amend(sd_device *device, const char *key, const char *value) { if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", devlink); } - } else if (streq(key, "TAGS")) { + } else if (STR_IN_SET(key, "TAGS", "CURRENT_TAGS")) { const char *word, *state; size_t l; @@ -339,10 +339,11 @@ static int device_amend(sd_device *device, const char *key, const char *value) { (void) strncpy(tag, word, l); tag[l] = '\0'; - r = device_add_tag(device, tag); + r = device_add_tag(device, tag, streq(key, "CURRENT_TAGS")); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", tag); } + } else { r = device_add_property_internal(device, key, value); if (r < 0) @@ -759,8 +760,8 @@ int device_copy_properties(sd_device *device_dst, sd_device *device_src) { void device_cleanup_tags(sd_device *device) { assert(device); - set_free_free(device->tags); - device->tags = NULL; + device->all_tags = set_free_free(device->all_tags); + device->current_tags = set_free_free(device->current_tags); device->property_tags_outdated = true; device->tags_generation++; } @@ -778,7 +779,7 @@ void device_remove_tag(sd_device *device, const char *tag) { assert(device); assert(tag); - free(set_remove(device->tags, tag)); + free(set_remove(device->current_tags, tag)); device->property_tags_outdated = true; device->tags_generation++; } @@ -846,7 +847,10 @@ static bool device_has_info(sd_device *device) { if (!ordered_hashmap_isempty(device->properties_db)) return true; - if (!set_isempty(device->tags)) + if (!set_isempty(device->all_tags)) + return true; + + if (!set_isempty(device->current_tags)) return true; if (device->watch_handle >= 0) @@ -939,7 +943,10 @@ int device_update_db(sd_device *device) { fprintf(f, "E:%s=%s\n", property, value); FOREACH_DEVICE_TAG(device, tag) - fprintf(f, "G:%s\n", tag); + fprintf(f, "G:%s\n", tag); /* Any tag */ + + SET_FOREACH(tag, device->current_tags, i) + fprintf(f, "Q:%s\n", tag); /* Current tag */ } r = fflush_and_check(f); diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h index 2bde53e969..1f1c4ca107 100644 --- a/src/libsystemd/sd-device/device-private.h +++ b/src/libsystemd/sd-device/device-private.h @@ -45,7 +45,7 @@ void device_set_devlink_priority(sd_device *device, int priority); int device_ensure_usec_initialized(sd_device *device, sd_device *device_old); int device_add_devlink(sd_device *device, const char *devlink); int device_add_property(sd_device *device, const char *property, const char *value); -int device_add_tag(sd_device *device, const char *tag); +int device_add_tag(sd_device *device, const char *tag, bool both); void device_remove_tag(sd_device *device, const char *tag); void device_cleanup_tags(sd_device *device); void device_cleanup_devlinks(sd_device *device); diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h index 1a1795d974..eda0f2f49e 100644 --- a/src/libsystemd/sd-device/device-util.h +++ b/src/libsystemd/sd-device/device-util.h @@ -11,6 +11,11 @@ tag; \ tag = sd_device_get_tag_next(device)) +#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \ + for (tag = sd_device_get_current_tag_first(device); \ + tag; \ + tag = sd_device_get_current_tag_next(device)) + #define FOREACH_DEVICE_SYSATTR(device, attr) \ for (attr = sd_device_get_sysattr_first(device); \ attr; \ diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 3bba17aff8..3041ce2e9c 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -69,7 +69,8 @@ static sd_device *device_free(sd_device *device) { ordered_hashmap_free_free_free(device->properties_db); hashmap_free_free_free(device->sysattr_values); set_free(device->sysattrs); - set_free(device->tags); + set_free(device->all_tags); + set_free(device->current_tags); set_free(device->devlinks); return mfree(device); @@ -1062,8 +1063,8 @@ static bool is_valid_tag(const char *tag) { return !strchr(tag, ':') && !strchr(tag, ' '); } -int device_add_tag(sd_device *device, const char *tag) { - int r; +int device_add_tag(sd_device *device, const char *tag, bool both) { + int r, added; assert(device); assert(tag); @@ -1071,9 +1072,21 @@ int device_add_tag(sd_device *device, const char *tag) { if (!is_valid_tag(tag)) return -EINVAL; - r = set_put_strdup(&device->tags, tag); - if (r < 0) - return r; + /* Definitely add to the "all" list of tags (i.e. the sticky list) */ + added = set_put_strdup(&device->all_tags, tag); + if (added < 0) + return added; + + /* And optionally, also add it to the current list of tags */ + if (both) { + r = set_put_strdup(&device->current_tags, tag); + if (r < 0) { + if (added > 0) + (void) set_remove(device->all_tags, tag); + + return r; + } + } device->tags_generation++; device->property_tags_outdated = true; @@ -1151,8 +1164,9 @@ static int handle_db_line(sd_device *device, char key, const char *value) { assert(value); switch (key) { - case 'G': - r = device_add_tag(device, value); + case 'G': /* Any tag */ + case 'Q': /* Current tag */ + r = device_add_tag(device, value, key == 'Q'); if (r < 0) return r; @@ -1407,10 +1421,10 @@ _public_ const char *sd_device_get_tag_first(sd_device *device) { (void) device_read_db(device); - device->tags_iterator_generation = device->tags_generation; - device->tags_iterator = ITERATOR_FIRST; + device->all_tags_iterator_generation = device->tags_generation; + device->all_tags_iterator = ITERATOR_FIRST; - (void) set_iterate(device->tags, &device->tags_iterator, &v); + (void) set_iterate(device->all_tags, &device->all_tags_iterator, &v); return v; } @@ -1421,10 +1435,38 @@ _public_ const char *sd_device_get_tag_next(sd_device *device) { (void) device_read_db(device); - if (device->tags_iterator_generation != device->tags_generation) + if (device->all_tags_iterator_generation != device->tags_generation) return NULL; - (void) set_iterate(device->tags, &device->tags_iterator, &v); + (void) set_iterate(device->all_tags, &device->all_tags_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_current_tag_first(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + device->current_tags_iterator_generation = device->tags_generation; + device->current_tags_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->current_tags, &device->current_tags_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_current_tag_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + if (device->current_tags_iterator_generation != device->tags_generation) + return NULL; + + (void) set_iterate(device->current_tags, &device->current_tags_iterator, &v); return v; } @@ -1456,6 +1498,31 @@ _public_ const char *sd_device_get_devlink_next(sd_device *device) { return v; } +static char *join_string_set(Set *s) { + size_t ret_allocated = 0, ret_len; + _cleanup_free_ char *ret = NULL; + const char *tag; + Iterator i; + + if (!GREEDY_REALLOC(ret, ret_allocated, 2)) + return NULL; + + strcpy(ret, ":"); + ret_len = 1; + + SET_FOREACH(tag, s, i) { + char *e; + + if (!GREEDY_REALLOC(ret, ret_allocated, ret_len + strlen(tag) + 2)) + return NULL; + + e = stpcpy(stpcpy(ret + ret_len, tag), ":"); + ret_len = e - ret; + } + + return TAKE_PTR(ret); +} + int device_properties_prepare(sd_device *device) { int r; @@ -1494,26 +1561,27 @@ int device_properties_prepare(sd_device *device) { if (device->property_tags_outdated) { _cleanup_free_ char *tags = NULL; - size_t tags_allocated = 0, tags_len = 0; - const char *tag; - if (!GREEDY_REALLOC(tags, tags_allocated, 2)) + tags = join_string_set(device->all_tags); + if (!tags) return -ENOMEM; - stpcpy(tags, ":"); - tags_len++; - for (tag = sd_device_get_tag_first(device); tag; tag = sd_device_get_tag_next(device)) { - char *e; - - if (!GREEDY_REALLOC(tags, tags_allocated, tags_len + strlen(tag) + 2)) - return -ENOMEM; - e = stpcpy(stpcpy(tags + tags_len, tag), ":"); - tags_len = e - tags; + if (!streq(tags, ":")) { + r = device_add_property_internal(device, "TAGS", tags); + if (r < 0) + return r; } - r = device_add_property_internal(device, "TAGS", tags); - if (r < 0) - return r; + free(tags); + tags = join_string_set(device->current_tags); + if (!tags) + return -ENOMEM; + + if (!streq(tags, ":")) { + r = device_add_property_internal(device, "CURRENT_TAGS", tags); + if (r < 0) + return r; + } device->property_tags_outdated = false; } @@ -1689,7 +1757,16 @@ _public_ int sd_device_has_tag(sd_device *device, const char *tag) { (void) device_read_db(device); - return !!set_contains(device->tags, tag); + return set_contains(device->all_tags, tag); +} + +_public_ int sd_device_has_current_tag(sd_device *device, const char *tag) { + assert_return(device, -EINVAL); + assert_return(tag, -EINVAL); + + (void) device_read_db(device); + + return set_contains(device->current_tags, tag); } _public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **_value) { diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h index 3c5c88c56b..d720ce50da 100644 --- a/src/systemd/sd-device.h +++ b/src/systemd/sd-device.h @@ -64,6 +64,8 @@ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec); const char *sd_device_get_tag_first(sd_device *device); const char *sd_device_get_tag_next(sd_device *device); +const char *sd_device_get_current_tag_first(sd_device *device); +const char *sd_device_get_current_tag_next(sd_device *device); const char *sd_device_get_devlink_first(sd_device *device); const char *sd_device_get_devlink_next(sd_device *device); const char *sd_device_get_property_first(sd_device *device, const char **value); @@ -72,6 +74,7 @@ const char *sd_device_get_sysattr_first(sd_device *device); const char *sd_device_get_sysattr_next(sd_device *device); int sd_device_has_tag(sd_device *device, const char *tag); +int sd_device_has_current_tag(sd_device *device, const char *tag); int sd_device_get_property_value(sd_device *device, const char *key, const char **value); int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index e1c2baf7f2..e1daac21ed 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -958,6 +958,24 @@ static int udev_event_on_move(UdevEvent *event) { return 0; } +static int copy_all_tags(sd_device *d, sd_device *s) { + const char *tag; + int r; + + assert(d); + + if (!s) + return 0; + + for (tag = sd_device_get_tag_first(s); tag; tag = sd_device_get_tag_next(s)) { + r = device_add_tag(d, tag, false); + if (r < 0) + return r; + } + + return 0; +} + int udev_event_execute_rules(UdevEvent *event, usec_t timeout_usec, int timeout_signal, @@ -990,6 +1008,10 @@ int udev_event_execute_rules(UdevEvent *event, if (r < 0) return log_device_debug_errno(dev, r, "Failed to clone sd_device object: %m"); + r = copy_all_tags(dev, event->dev_db_clone); + if (r < 0) + log_device_warning_errno(dev, r, "Failed to copy all tags from old database entry, ignoring: %m"); + if (sd_device_get_devnum(dev, NULL) >= 0) /* Disable watch during event processing. */ (void) udev_watch_end(event->dev_db_clone); diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index eb28431325..018478c986 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -2017,7 +2017,7 @@ static int udev_rule_apply_token_to_event( if (token->op == OP_REMOVE) device_remove_tag(dev, buf); else { - r = device_add_tag(dev, buf); + r = device_add_tag(dev, buf, true); if (r < 0) return log_rule_error_errno(dev, rules, r, "Failed to add tag '%s': %m", buf); }