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;
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;
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 */
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;
(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)
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++;
}
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++;
}
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)
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);
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);
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; \
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);
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);
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;
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;
(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;
}
(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->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->tags, &device->tags_iterator, &v);
+ (void) set_iterate(device->current_tags, &device->current_tags_iterator, &v);
return v;
}
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;
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;
}
(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) {
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);
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);
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,
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);
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);
}