summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKay Sievers <kay.sievers@vrfy.org>2010-04-22 18:12:36 +0200
committerKay Sievers <kay.sievers@vrfy.org>2010-04-22 18:12:36 +0200
commit28460195c2ae90892bf556aff2b80705a8f37795 (patch)
tree8c3783342e1bf033b6f0e596985b250c2f2e5d4c
parente7964b93e826274d0e92d58e458decb49e502bf5 (diff)
add TAG= to improve event filtering and device enumeration
-rw-r--r--Makefile.am4
-rw-r--r--libudev/docs/libudev-sections.txt2
-rw-r--r--libudev/exported_symbols1
-rw-r--r--libudev/libudev-device-private.c46
-rw-r--r--libudev/libudev-device.c159
-rw-r--r--libudev/libudev-enumerate.c197
-rw-r--r--libudev/libudev-monitor.c218
-rw-r--r--libudev/libudev-private.h8
-rw-r--r--libudev/libudev-util.c75
-rw-r--r--libudev/libudev.h4
-rw-r--r--udev/udev-event.c1
-rw-r--r--udev/udev-rules.c14
-rw-r--r--udev/udev.xml13
-rw-r--r--udev/udevadm-monitor.c17
-rw-r--r--udev/udevadm-trigger.c7
-rw-r--r--udev/udevadm.xml13
16 files changed, 595 insertions, 184 deletions
diff --git a/Makefile.am b/Makefile.am
index 7403949cf3..9d3fefd702 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -28,9 +28,9 @@ CLEANFILES =
# ------------------------------------------------------------------------------
# libudev
# ------------------------------------------------------------------------------
-LIBUDEV_CURRENT=7
+LIBUDEV_CURRENT=8
LIBUDEV_REVISION=0
-LIBUDEV_AGE=7
+LIBUDEV_AGE=8
SUBDIRS += libudev/docs
diff --git a/libudev/docs/libudev-sections.txt b/libudev/docs/libudev-sections.txt
index ca781fff23..3f8c107a1f 100644
--- a/libudev/docs/libudev-sections.txt
+++ b/libudev/docs/libudev-sections.txt
@@ -68,6 +68,7 @@ udev_monitor_enable_receiving
udev_monitor_get_fd
udev_monitor_receive_device
udev_monitor_filter_add_match_subsystem_devtype
+udev_monitor_filter_add_match_tag
udev_monitor_filter_update
udev_monitor_filter_remove
</SECTION>
@@ -85,6 +86,7 @@ udev_enumerate_add_nomatch_subsystem
udev_enumerate_add_match_sysattr
udev_enumerate_add_nomatch_sysattr
udev_enumerate_add_match_property
+udev_enumerate_add_match_tag
udev_enumerate_add_match_sysname
udev_enumerate_add_syspath
udev_enumerate_scan_devices
diff --git a/libudev/exported_symbols b/libudev/exported_symbols
index 61486c6f48..c0ca4b9c6d 100644
--- a/libudev/exported_symbols
+++ b/libudev/exported_symbols
@@ -60,6 +60,7 @@ udev_monitor_get_udev
udev_monitor_get_fd
udev_monitor_receive_device
udev_monitor_filter_add_match_subsystem_devtype
+udev_monitor_filter_add_match_tag
udev_monitor_filter_update
udev_monitor_filter_remove
udev_queue_new
diff --git a/libudev/libudev-device-private.c b/libudev/libudev-device-private.c
index 5e4381ec27..13f1ebf883 100644
--- a/libudev/libudev-device-private.c
+++ b/libudev/libudev-device-private.c
@@ -1,7 +1,7 @@
/*
* libudev - interface to udev device information
*
- * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -13,6 +13,7 @@
#include <stdio.h>
#include <string.h>
#include <stddef.h>
+#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
@@ -21,6 +22,37 @@
#include "libudev.h"
#include "libudev-private.h"
+static int udev_device_tag_index(struct udev_device *udev_device, bool add)
+{
+ struct udev *udev = udev_device_get_udev(udev_device);
+ struct udev_list_entry *list_entry;
+
+ udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device)) {
+ char filename[UTIL_PATH_SIZE];
+
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/tags/",
+ udev_list_entry_get_name(list_entry), "/",
+ udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname(udev_device), NULL);
+
+ if (add) {
+ util_create_path(udev, filename);
+ symlink(udev_device_get_devpath(udev_device), filename);
+ if (udev_device_get_sysname_old(udev_device) != NULL) {
+ char filename_old[UTIL_PATH_SIZE];
+
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/tags/",
+ udev_list_entry_get_name(list_entry),
+ udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname_old(udev_device), NULL);
+ unlink(filename_old);
+ }
+ } else {
+ unlink(filename);
+ util_delete_path(udev, filename);
+ }
+ }
+ return 0;
+}
+
int udev_device_update_db(struct udev_device *udev_device)
{
struct udev *udev = udev_device_get_udev(udev_device);
@@ -41,6 +73,8 @@ int udev_device_update_db(struct udev_device *udev_device)
udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device))
if (udev_list_entry_get_flags(list_entry))
goto file;
+ if (udev_device_get_tags_list_entry(udev_device) != NULL)
+ goto file;
if (udev_device_get_devlink_priority(udev_device) != 0)
goto file;
if (udev_device_get_event_timeout(udev_device) >= 0)
@@ -80,7 +114,7 @@ file:
if (f == NULL) {
err(udev, "unable to create temporary db file '%s': %m\n", filename_tmp);
return -1;
- }
+ }
if (udev_device_get_devnode(udev_device) != NULL) {
fprintf(f, "N:%s\n", &udev_device_get_devnode(udev_device)[devlen]);
@@ -100,10 +134,13 @@ file:
udev_list_entry_get_name(list_entry),
udev_list_entry_get_value(list_entry));
}
+ udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))
+ fprintf(f, "G:%s\n", udev_list_entry_get_name(list_entry));
fclose(f);
rename(filename_tmp, filename);
info(udev, "created db file for '%s' in '%s'\n", udev_device_get_devpath(udev_device), filename);
out:
+ udev_device_tag_index(udev_device, true);
return 0;
}
@@ -112,6 +149,7 @@ int udev_device_delete_db(struct udev_device *udev_device)
struct udev *udev = udev_device_get_udev(udev_device);
char filename[UTIL_PATH_SIZE];
+ udev_device_tag_index(udev_device, false);
util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db/",
udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname(udev_device), NULL);
unlink(filename);
@@ -124,9 +162,13 @@ int udev_device_rename_db(struct udev_device *udev_device)
char filename_old[UTIL_PATH_SIZE];
char filename[UTIL_PATH_SIZE];
+ if (strcmp(udev_device_get_sysname(udev_device), udev_device_get_sysname_old(udev_device)) == 0)
+ return 0;
+
util_strscpyl(filename_old, sizeof(filename_old), udev_get_dev_path(udev), "/.udev/db/",
udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname_old(udev_device), NULL);
util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db/",
udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname(udev_device), NULL);
+ udev_device_tag_index(udev_device, true);
return rename(filename_old, filename);
}
diff --git a/libudev/libudev-device.c b/libudev/libudev-device.c
index 400354539b..478fdcb92d 100644
--- a/libudev/libudev-device.c
+++ b/libudev/libudev-device.c
@@ -60,6 +60,7 @@ struct udev_device {
struct udev_list_node devlinks_list;
struct udev_list_node properties_list;
struct udev_list_node sysattr_list;
+ struct udev_list_node tags_list;
unsigned long long int seqnum;
int event_timeout;
int timeout;
@@ -68,18 +69,21 @@ struct udev_device {
dev_t devnum;
int watch_handle;
int maj, min;
- unsigned int parent_set:1;
- unsigned int subsystem_set:1;
- unsigned int devtype_set:1;
- unsigned int devlinks_uptodate:1;
- unsigned int envp_uptodate:1;
- unsigned int driver_set:1;
- unsigned int info_loaded:1;
+ bool parent_set;
+ bool subsystem_set;
+ bool devtype_set;
+ bool devlinks_uptodate;
+ bool envp_uptodate;
+ bool tags_uptodate;
+ bool driver_set;
+ bool info_loaded;
+ bool db_loaded;
+ bool uevent_loaded;
};
struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value)
{
- udev_device->envp_uptodate = 0;
+ udev_device->envp_uptodate = false;
if (value == NULL) {
struct udev_list_entry *list_entry;
@@ -149,6 +153,26 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device,
}
if (slink[0] != '\0')
udev_device_add_devlink(udev_device, slink, 0);
+ } else if (strncmp(property, "TAGS=", 5) == 0) {
+ char tags[UTIL_PATH_SIZE];
+ char *next;
+
+ util_strscpy(tags, sizeof(tags), &property[5]);
+ next = strchr(tags, ':');
+ if (next != NULL) {
+ next++;
+ while (next[0] != '\0') {
+ char *tag;
+
+ tag = next;
+ next = strchr(tag, ':');
+ if (next == NULL)
+ break;
+ next[0] = '\0';
+ next++;
+ udev_device_add_tag(udev_device, tag);
+ }
+ }
} else if (strncmp(property, "DRIVER=", 7) == 0) {
udev_device_set_driver(udev_device, &property[7]);
} else if (strncmp(property, "ACTION=", 7) == 0) {
@@ -208,6 +232,9 @@ int udev_device_read_db(struct udev_device *udev_device)
char line[UTIL_LINE_SIZE];
FILE *f;
+ if (udev_device->db_loaded)
+ return 0;
+
util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/.udev/db/",
udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname(udev_device), NULL);
@@ -284,6 +311,9 @@ int udev_device_read_db(struct udev_device *udev_device)
case 'E':
udev_device_add_property_from_string(udev_device, val);
break;
+ case 'G':
+ udev_device_add_tag(udev_device, val);
+ break;
case 'W':
udev_device_set_watch_handle(udev_device, atoi(val));
break;
@@ -292,6 +322,7 @@ int udev_device_read_db(struct udev_device *udev_device)
fclose(f);
info(udev_device->udev, "device %p filled with db file data\n", udev_device);
+ udev_device->db_loaded = true;
return 0;
}
@@ -303,6 +334,9 @@ int udev_device_read_uevent_file(struct udev_device *udev_device)
int maj = 0;
int min = 0;
+ if (udev_device->uevent_loaded)
+ return 0;
+
util_strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL);
f = fopen(filename, "r");
if (f == NULL)
@@ -329,21 +363,14 @@ int udev_device_read_uevent_file(struct udev_device *udev_device)
}
udev_device->devnum = makedev(maj, min);
-
fclose(f);
+ udev_device->uevent_loaded = true;
return 0;
}
-static void device_load_info(struct udev_device *device)
-{
- device->info_loaded = 1;
- udev_device_read_uevent_file(device);
- udev_device_read_db(device);
-}
-
void udev_device_set_info_loaded(struct udev_device *device)
{
- device->info_loaded = 1;
+ device->info_loaded = true;
}
struct udev_device *udev_device_new(struct udev *udev)
@@ -362,6 +389,7 @@ struct udev_device *udev_device_new(struct udev *udev)
udev_list_init(&udev_device->devlinks_list);
udev_list_init(&udev_device->properties_list);
udev_list_init(&udev_device->sysattr_list);
+ udev_list_init(&udev_device->tags_list);
udev_device->event_timeout = -1;
udev_device->watch_handle = -1;
/* copy global properties */
@@ -420,7 +448,7 @@ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *
util_strscpy(path, sizeof(path), syspath);
util_resolve_sys_link(udev, path, sizeof(path));
- if (strncmp(&syspath[len], "/devices/", 9) == 0) {
+ if (strncmp(&path[len], "/devices/", 9) == 0) {
char file[UTIL_PATH_SIZE];
/* all "devices" require a "uevent" file */
@@ -648,7 +676,7 @@ struct udev_device *udev_device_get_parent(struct udev_device *udev_device)
if (udev_device == NULL)
return NULL;
if (!udev_device->parent_set) {
- udev_device->parent_set = 1;
+ udev_device->parent_set = true;
udev_device->parent_device = device_new_from_parent(udev_device);
}
if (udev_device->parent_device != NULL)
@@ -758,12 +786,13 @@ void udev_device_unref(struct udev_device *udev_device)
free(udev_device->devtype);
udev_list_cleanup_entries(udev_device->udev, &udev_device->devlinks_list);
udev_list_cleanup_entries(udev_device->udev, &udev_device->properties_list);
+ udev_list_cleanup_entries(udev_device->udev, &udev_device->sysattr_list);
+ udev_list_cleanup_entries(udev_device->udev, &udev_device->tags_list);
free(udev_device->action);
free(udev_device->driver);
free(udev_device->devpath_old);
free(udev_device->sysname_old);
free(udev_device->knodename);
- udev_list_cleanup_entries(udev_device->udev, &udev_device->sysattr_list);
free(udev_device->envp);
free(udev_device->monitor_buf);
dbg(udev_device->udev, "udev_device: %p released\n", udev_device);
@@ -842,7 +871,7 @@ const char *udev_device_get_devnode(struct udev_device *udev_device)
if (udev_device == NULL)
return NULL;
if (!udev_device->info_loaded)
- device_load_info(udev_device);
+ udev_device_read_db(udev_device);
return udev_device->devnode;
}
@@ -862,7 +891,7 @@ const char *udev_device_get_subsystem(struct udev_device *udev_device)
if (udev_device == NULL)
return NULL;
if (!udev_device->subsystem_set) {
- udev_device->subsystem_set = 1;
+ udev_device->subsystem_set = true;
/* read "subsystem" link */
if (util_get_sys_subsystem(udev_device->udev, udev_device->syspath, subsystem, sizeof(subsystem)) > 0) {
udev_device_set_subsystem(udev_device, subsystem);
@@ -900,9 +929,8 @@ const char *udev_device_get_devtype(struct udev_device *udev_device)
if (udev_device == NULL)
return NULL;
if (!udev_device->devtype_set) {
- udev_device->devtype_set = 1;
- if (!udev_device->info_loaded)
- udev_device_read_uevent_file(udev_device);
+ udev_device->devtype_set = true;
+ udev_device_read_uevent_file(udev_device);
}
return udev_device->devtype;
}
@@ -925,13 +953,13 @@ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *
if (udev_device == NULL)
return NULL;
if (!udev_device->info_loaded)
- device_load_info(udev_device);
+ udev_device_read_db(udev_device);
return udev_list_get_entry(&udev_device->devlinks_list);
}
void udev_device_cleanup_devlinks_list(struct udev_device *udev_device)
{
- udev_device->devlinks_uptodate = 0;
+ udev_device->devlinks_uptodate = false;
udev_list_cleanup_entries(udev_device->udev, &udev_device->devlinks_list);
}
@@ -951,13 +979,15 @@ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device
{
if (udev_device == NULL)
return NULL;
- if (!udev_device->info_loaded)
- device_load_info(udev_device);
+ if (!udev_device->info_loaded) {
+ udev_device_read_uevent_file(udev_device);
+ udev_device_read_db(udev_device);
+ }
if (!udev_device->devlinks_uptodate) {
char symlinks[UTIL_PATH_SIZE];
struct udev_list_entry *list_entry;
- udev_device->devlinks_uptodate = 1;
+ udev_device->devlinks_uptodate = true;
list_entry = udev_device_get_devlinks_list_entry(udev_device);
if (list_entry != NULL) {
char *s;
@@ -970,6 +1000,21 @@ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device
udev_device_add_property(udev_device, "DEVLINKS", symlinks);
}
}
+ if (!udev_device->tags_uptodate) {
+ udev_device->tags_uptodate = true;
+ if (udev_device_get_tags_list_entry(udev_device) != NULL) {
+ char tags[UTIL_PATH_SIZE];
+ struct udev_list_entry *list_entry;
+ char *s;
+ size_t l;
+
+ s = tags;
+ l = util_strpcpyl(&s, sizeof(tags), ":", NULL);
+ udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))
+ l = util_strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL);
+ udev_device_add_property(udev_device, "TAGS", tags);
+ }
+ }
return udev_list_get_entry(&udev_device->properties_list);
}
@@ -986,7 +1031,7 @@ const char *udev_device_get_driver(struct udev_device *udev_device)
if (udev_device == NULL)
return NULL;
if (!udev_device->driver_set) {
- udev_device->driver_set = 1;
+ udev_device->driver_set = true;
if (util_get_sys_driver(udev_device->udev, udev_device->syspath, driver, sizeof(driver)) > 0)
udev_device->driver = strdup(driver);
}
@@ -1004,7 +1049,7 @@ dev_t udev_device_get_devnum(struct udev_device *udev_device)
if (udev_device == NULL)
return makedev(0, 0);
if (!udev_device->info_loaded)
- device_load_info(udev_device);
+ udev_device_read_uevent_file(udev_device);
return udev_device->devnum;
}
@@ -1184,7 +1229,7 @@ int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsy
udev_device->subsystem = strdup(subsystem);
if (udev_device->subsystem == NULL)
return -ENOMEM;
- udev_device->subsystem_set = 1;
+ udev_device->subsystem_set = true;
udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem);
return 0;
}
@@ -1195,7 +1240,7 @@ int udev_device_set_devtype(struct udev_device *udev_device, const char *devtype
udev_device->devtype = strdup(devtype);
if (udev_device->devtype == NULL)
return -ENOMEM;
- udev_device->devtype_set = 1;
+ udev_device->devtype_set = true;
udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype);
return 0;
}
@@ -1216,7 +1261,7 @@ int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink
{
struct udev_list_entry *list_entry;
- udev_device->devlinks_uptodate = 0;
+ udev_device->devlinks_uptodate = false;
list_entry = udev_list_entry_add(udev_device->udev, &udev_device->devlinks_list, devlink, NULL, 1, 0);
if (list_entry == NULL)
return -ENOMEM;
@@ -1225,6 +1270,40 @@ int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink
return 0;
}
+int udev_device_add_tag(struct udev_device *udev_device, const char *tag)
+{
+ if (strchr(tag, ':') != NULL || strchr(tag, ' ') != NULL)
+ return -EINVAL;
+ udev_device->tags_uptodate = false;
+ if (udev_list_entry_add(udev_device->udev, &udev_device->tags_list, tag, NULL, 1, 0) != NULL)
+ return 0;
+ return -ENOMEM;
+}
+
+void udev_device_cleanup_tags_list(struct udev_device *udev_device)
+{
+ udev_device->tags_uptodate = false;
+ udev_list_cleanup_entries(udev_device->udev, &udev_device->tags_list);
+}
+
+struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device)
+{
+ return udev_list_get_entry(&udev_device->tags_list);
+}
+
+int udev_device_has_tag(struct udev_device *udev_device, const char *tag)
+{
+ struct udev_list_entry *list_entry;
+
+ if (!udev_device->info_loaded)
+ udev_device_read_db(udev_device);
+ list_entry = udev_device_get_tags_list_entry(udev_device);
+ list_entry = udev_list_entry_get_by_name(list_entry, tag);
+ if (list_entry != NULL)
+ return 1;
+ return 0;
+}
+
#define ENVP_SIZE 128
#define MONITOR_BUF_SIZE 4096
static int update_envp_monitor_buf(struct udev_device *udev_device)
@@ -1273,7 +1352,7 @@ static int update_envp_monitor_buf(struct udev_device *udev_device)
}
udev_device->envp[i] = NULL;
udev_device->monitor_buf_len = s - udev_device->monitor_buf;
- udev_device->envp_uptodate = 1;
+ udev_device->envp_uptodate = true;
dbg(udev_device->udev, "filled envp/monitor buffer, %u properties, %zu bytes\n",
i, udev_device->monitor_buf_len);
return 0;
@@ -1312,7 +1391,7 @@ int udev_device_set_driver(struct udev_device *udev_device, const char *driver)
udev_device->driver = strdup(driver);
if (udev_device->driver == NULL)
return -ENOMEM;
- udev_device->driver_set = 1;
+ udev_device->driver_set = true;
udev_device_add_property(udev_device, "DRIVER", udev_device->driver);
return 0;
}
@@ -1385,7 +1464,7 @@ int udev_device_set_timeout(struct udev_device *udev_device, int timeout)
int udev_device_get_event_timeout(struct udev_device *udev_device)
{
if (!udev_device->info_loaded)
- device_load_info(udev_device);
+ udev_device_read_db(udev_device);
return udev_device->event_timeout;
}
@@ -1421,7 +1500,7 @@ int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum)
int udev_device_get_devlink_priority(struct udev_device *udev_device)
{
if (!udev_device->info_loaded)
- device_load_info(udev_device);
+ udev_device_read_db(udev_device);
return udev_device->devlink_priority;
}
@@ -1434,7 +1513,7 @@ int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio)
int udev_device_get_watch_handle(struct udev_device *udev_device)
{
if (!udev_device->info_loaded)
- device_load_info(udev_device);
+ udev_device_read_db(udev_device);
return udev_device->watch_handle;
}
diff --git a/libudev/libudev-enumerate.c b/libudev/libudev-enumerate.c
index 9a61a61f0d..da831449dc 100644
--- a/libudev/libudev-enumerate.c
+++ b/libudev/libudev-enumerate.c
@@ -1,7 +1,7 @@
/*
* libudev - interface to udev device information
*
- * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -51,6 +51,7 @@ struct udev_enumerate {
struct udev_list_node subsystem_nomatch_list;
struct udev_list_node sysname_match_list;
struct udev_list_node properties_match_list;
+ struct udev_list_node tags_match_list;
struct udev_list_node devices_list;
struct syspath *devices;
unsigned int devices_cur;
@@ -79,6 +80,7 @@ struct udev_enumerate *udev_enumerate_new(struct udev *udev)
udev_list_init(&udev_enumerate->subsystem_nomatch_list);
udev_list_init(&udev_enumerate->sysname_match_list);
udev_list_init(&udev_enumerate->properties_match_list);
+ udev_list_init(&udev_enumerate->tags_match_list);
udev_list_init(&udev_enumerate->devices_list);
return udev_enumerate;
}
@@ -121,6 +123,7 @@ void udev_enumerate_unref(struct udev_enumerate *udev_enumerate)
udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->subsystem_nomatch_list);
udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->sysname_match_list);
udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->properties_match_list);
+ udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->tags_match_list);
udev_list_cleanup_entries(udev_enumerate->udev, &udev_enumerate->devices_list);
for (i = 0; i < udev_enumerate->devices_cur; i++)
free(udev_enumerate->devices[i].syspath);
@@ -191,7 +194,7 @@ static int syspath_cmp(const void *p1, const void *p2)
}
/* For devices that should be moved to the absolute end of the list */
-static int devices_delay_end(struct udev *udev, const char *syspath)
+static bool devices_delay_end(struct udev *udev, const char *syspath)
{
static const char *delay_device_list[] = {
"/block/md",
@@ -205,10 +208,10 @@ static int devices_delay_end(struct udev *udev, const char *syspath)
for (i = 0; delay_device_list[i] != NULL; i++) {
if (strstr(&syspath[len], delay_device_list[i]) != NULL) {
dbg(udev, "delaying: %s\n", syspath);
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
/* For devices that should just be moved a little bit later, just
@@ -394,16 +397,12 @@ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, co
return 0;
}
-static int match_sysattr_value(struct udev *udev, const char *syspath, const char *sysattr, const char *match_val)
+static int match_sysattr_value(struct udev_device *dev, const char *sysattr, const char *match_val)
{
- struct udev_device *device;
const char *val = NULL;
bool match = false;
- device = udev_device_new_from_syspath(udev, syspath);
- if (device == NULL)
- return -EINVAL;
- val = udev_device_get_sysattr_value(device, sysattr);
+ val = udev_device_get_sysattr_value(dev, sysattr);
if (val == NULL)
goto exit;
if (match_val == NULL) {
@@ -415,7 +414,6 @@ static int match_sysattr_value(struct udev *udev, const char *syspath, const cha
goto exit;
}
exit:
- udev_device_unref(device);
return match;
}
@@ -440,6 +438,25 @@ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, con
}
/**
+ * udev_enumerate_add_match_tag:
+ * @udev_enumerate: context
+ * @tag: filter for a tag of the device to include in the list
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag)
+{
+ if (udev_enumerate == NULL)
+ return -EINVAL;
+ if (tag == NULL)
+ return 0;
+ if (udev_list_entry_add(udev_enumerate_get_udev(udev_enumerate),
+ &udev_enumerate->tags_match_list, tag, NULL, 1, 0) == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
* udev_enumerate_add_match_sysname:
* @udev_enumerate: context
* @sysname: filter for the name of the device to include in the list
@@ -458,46 +475,37 @@ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, cons
return 0;
}
-static int match_sysattr(struct udev_enumerate *udev_enumerate, const char *syspath)
+static bool match_sysattr(struct udev_enumerate *udev_enumerate, struct udev_device *dev)
{
- struct udev *udev = udev_enumerate_get_udev(udev_enumerate);
struct udev_list_entry *list_entry;
/* skip list */
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_nomatch_list)) {
- if (match_sysattr_value(udev, syspath,
- udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry)))
- return 0;
+ if (match_sysattr_value(dev, udev_list_entry_get_name(list_entry),
+ udev_list_entry_get_value(list_entry)))
+ return false;
}
/* include list */
if (udev_list_get_entry(&udev_enumerate->sysattr_match_list) != NULL) {
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysattr_match_list)) {
/* anything that does not match, will make it FALSE */
- if (!match_sysattr_value(udev, syspath,
- udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry)))
- return 0;
+ if (!match_sysattr_value(dev, udev_list_entry_get_name(list_entry),
+ udev_list_entry_get_value(list_entry)))
+ return false;
}
- return 1;
+ return true;
}
- return 1;
+ return true;
}
-static int match_property(struct udev_enumerate *udev_enumerate, const char *syspath)
+static bool match_property(struct udev_enumerate *udev_enumerate, struct udev_device *dev)
{
- struct udev_device *dev;
struct udev_list_entry *list_entry;
- int match = false;
+ bool match = false;
/* no match always matches */
if (udev_list_get_entry(&udev_enumerate->properties_match_list) == NULL)
- return 1;
-
- /* no device does not match */
- dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
- if (dev == NULL)
- return 0;
+ return true;
/* loop over matches */
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->properties_match_list)) {
@@ -525,23 +533,38 @@ static int match_property(struct udev_enumerate *udev_enumerate, const char *sys
}
}
out:
- udev_device_unref(dev);
return match;
}
-static int match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname)
+static bool match_tag(struct udev_enumerate *udev_enumerate, struct udev_device *dev)
+{
+ struct udev_list_entry *list_entry;
+
+ /* no match always matches */
+ if (udev_list_get_entry(&udev_enumerate->tags_match_list) == NULL)
+ return true;
+
+ /* loop over matches */
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list))
+ if (!udev_device_has_tag(dev, udev_list_entry_get_name(list_entry)))
+ return false;
+
+ return true;
+}
+
+static bool match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname)
{
struct udev_list_entry *list_entry;
if (udev_list_get_entry(&udev_enumerate->sysname_match_list) == NULL)
- return 1;
+ return true;
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->sysname_match_list)) {
if (fnmatch(udev_list_entry_get_name(list_entry), sysname, 0) != 0)
continue;
- return 1;
+ return true;
}
- return 0;
+ return false;
}
static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate,
@@ -562,54 +585,53 @@ static int scan_dir_and_add_devices(struct udev_enumerate *udev_enumerate,
util_strpcpyl(&s, l, "/", subdir2, NULL);
dir = opendir(path);
if (dir == NULL)
- return -1;
+ return -ENOENT;
for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
char syspath[UTIL_PATH_SIZE];
- char filename[UTIL_PATH_SIZE];
- struct stat statbuf;
+ struct udev_device *dev;
if (dent->d_name[0] == '.')
continue;
+
if (!match_sysname(udev_enumerate, dent->d_name))
continue;
util_strscpyl(syspath, sizeof(syspath), path, "/", dent->d_name, NULL);
- if (!match_property(udev_enumerate, syspath))
- continue;
- if (lstat(syspath, &statbuf) != 0)
- continue;
- if (S_ISREG(statbuf.st_mode))
+ dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
+ if (dev == NULL)
continue;
- if (S_ISLNK(statbuf.st_mode))
- util_resolve_sys_link(udev, syspath, sizeof(syspath));
- util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
- if (stat(filename, &statbuf) != 0)
- continue;
- if (!match_sysattr(udev_enumerate, syspath))
- continue;
- syspath_add(udev_enumerate, syspath);
+ if (!match_tag(udev_enumerate, dev))
+ goto nomatch;
+ if (!match_property(udev_enumerate, dev))
+ goto nomatch;
+ if (!match_sysattr(udev_enumerate, dev))
+ goto nomatch;
+
+ syspath_add(udev_enumerate, udev_device_get_syspath(dev));
+nomatch:
+ udev_device_unref(dev);
}
closedir(dir);
return 0;
}
-static int match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
+static bool match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem)
{
struct udev_list_entry *list_entry;
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_nomatch_list)) {
if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
- return 0;
+ return false;
}
if (udev_list_get_entry(&udev_enumerate->subsystem_match_list) != NULL) {
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->subsystem_match_list)) {
if (fnmatch(udev_list_entry_get_name(list_entry), subsystem, 0) == 0)
- return 1;
+ return true;
}
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static int scan_dir(struct udev_enumerate *udev_enumerate, const char *basedir, const char *subdir, const char *subsystem)
@@ -675,17 +697,59 @@ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
if (udev_enumerate == NULL)
return -EINVAL;
- util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL);
- if (stat(base, &statbuf) == 0) {
- /* we have /subsystem/, forget all the old stuff */
- dbg(udev, "searching '/subsystem/*/devices/*' dir\n");
- scan_dir(udev_enumerate, "subsystem", "devices", NULL);
+
+ if (udev_list_get_entry(&udev_enumerate->tags_match_list) != NULL) {
+ struct udev_list_entry *list_entry;
+
+ /* scan only tagged devices, use tags reverse-index, instead of searching all devices in /sys */
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_enumerate->tags_match_list)) {
+ DIR *dir;
+ struct dirent *dent;
+ char path[UTIL_PATH_SIZE];
+
+ util_strscpyl(path, sizeof(path), udev_get_dev_path(udev), "/.udev/tags/",
+ udev_list_entry_get_name(list_entry), NULL);
+ dir = opendir(path);
+ if (dir == NULL)
+ continue;
+ for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
+ struct udev_device *dev;
+ char syspath[UTIL_PATH_SIZE];
+ char *s;
+ size_t l;
+ ssize_t len;
+
+ if (dent->d_name[0] == '.')
+ continue;
+
+ s = syspath;
+ l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev), NULL);
+ len = readlinkat(dirfd(dir), dent->d_name, s, l);
+ if (len <= 0 || (size_t)len == l)
+ continue;
+ s[len] = '\0';
+
+ dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
+ if (dev == NULL)
+ continue;
+ syspath_add(udev_enumerate, udev_device_get_syspath(dev));
+ udev_device_unref(dev);
+ }
+ }
} else {
+ util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL);
+ if (stat(base, &statbuf) == 0) {
+ /* we have /subsystem/, forget all the old stuff */
+ dbg(udev, "searching '/subsystem/*/devices/*' dir\n");
+ scan_dir(udev_enumerate, "subsystem", "devices", NULL);
+ } else {
dbg(udev, "searching '/bus/*/devices/*' dir\n");
- scan_dir(udev_enumerate, "bus", "devices", NULL);
- dbg(udev, "searching '/class/*' dir\n");
- scan_dir(udev_enumerate, "class", NULL, NULL);
+ scan_dir(udev_enumerate, "bus", "devices", NULL);
+ dbg(udev, "searching '/class/*' dir\n");
+ scan_dir(udev_enumerate, "class", NULL, NULL);
+ }
}
+
return 0;
}
@@ -704,6 +768,7 @@ int udev_enumerate_scan_subsystems(struct udev_enumerate *udev_enumerate)
if (udev_enumerate == NULL)
return -EINVAL;
+
util_strscpyl(base, sizeof(base), udev_get_sys_path(udev), "/subsystem", NULL);
if (stat(base, &statbuf) == 0)
subsysdir = "subsystem";
diff --git a/libudev/libudev-monitor.c b/libudev/libudev-monitor.c
index 97e52c42db..24e8aead26 100644
--- a/libudev/libudev-monitor.c
+++ b/libudev/libudev-monitor.c
@@ -1,7 +1,7 @@
/*
* libudev - interface to udev device information
*
- * Copyright (C) 2008-2009 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -49,6 +49,7 @@ struct udev_monitor {
struct sockaddr_un sun;
socklen_t addrlen;
struct udev_list_node filter_subsystem_list;
+ struct udev_list_node filter_tag_list;
};
enum udev_monitor_netlink_group {
@@ -57,25 +58,28 @@ enum udev_monitor_netlink_group {
UDEV_MONITOR_UDEV,
};
-#define UDEV_MONITOR_MAGIC 0xcafe1dea
+#define UDEV_MONITOR_MAGIC 0xfeedcafe
struct udev_monitor_netlink_header {
- /* udev version text */
- char version[16];
+ /* "libudev" prefix to distinguish libudev and kernel messages */
+ char prefix[8];
/*
* magic to protect against daemon <-> library message format mismatch
* used in the kernel from socket filter rules; needs to be stored in network order
*/
unsigned int magic;
- /* properties buffer */
- unsigned short properties_off;
- unsigned short properties_len;
+ /* total length of header structure known to the sender */
+ unsigned int header_size;
+ /* properties string buffer */
+ unsigned int properties_off;
+ unsigned int properties_len;
/*
- * hashes of some common device properties strings to filter with socket filters in
- * the client used in the kernel from socket filter rules; needs to be stored in
- * network order
+ * hashes of primary device properties strings, to let libudev subscribers
+ * use in-kernel socket filters; values need to be stored in network order
*/
- unsigned int filter_subsystem;
- unsigned int filter_devtype;
+ unsigned int filter_subsystem_hash;
+ unsigned int filter_devtype_hash;
+ unsigned int filter_tag_bloom_hi;
+ unsigned int filter_tag_bloom_lo;
};
static struct udev_monitor *udev_monitor_new(struct udev *udev)
@@ -88,6 +92,7 @@ static struct udev_monitor *udev_monitor_new(struct udev *udev)
udev_monitor->refcount = 1;
udev_monitor->udev = udev;
udev_list_init(&udev_monitor->filter_subsystem_list);
+ udev_list_init(&udev_monitor->filter_tag_list);
return udev_monitor;
}
@@ -247,13 +252,14 @@ static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i,
*/
int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
{
- static struct sock_filter ins[256];
- static struct sock_fprog filter;
+ struct sock_filter ins[512];
+ struct sock_fprog filter;
unsigned int i;
struct udev_list_entry *list_entry;
int err;
- if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL)
+ if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL &&
+ udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL)
return 0;
memset(ins, 0x00, sizeof(ins));
@@ -266,35 +272,74 @@ int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
/* wrong magic, pass packet */
bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
- /* add all subsystem match values */
- udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) {
- unsigned int hash;
-
- /* load filter_subsystem value in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem));
- hash = util_string_hash32(udev_list_entry_get_name(list_entry));
- if (udev_list_entry_get_value(list_entry) == NULL) {
- /* jump if subsystem does not match */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
- } else {
- /* jump if subsystem does not match */
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3);
-
- /* load filter_devtype value in A */
- bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_devtype));
- /* jump if value does not match */
- hash = util_string_hash32(udev_list_entry_get_value(list_entry));
- bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL) {
+ int tag_matches;
+
+ /* count tag matches, to calculate end of tag match block */
+ tag_matches = 0;
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list))
+ tag_matches++;
+
+ /* add all tags matches */
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) {
+ uint64_t tag_bloom_bits = util_string_bloom64(udev_list_entry_get_name(list_entry));
+ uint32_t tag_bloom_hi = tag_bloom_bits >> 32;
+ uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff;
+
+ /* load device bloom bits in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_hi));
+ /* clear bits (tag bits & bloom bits) */
+ bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_hi);
+ /* jump to next tag if it does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_hi, 0, 3);
+
+ /* load device bloom bits in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_lo));
+ /* clear bits (tag bits & bloom bits) */
+ bpf_stmt(ins, &i, BPF_ALU|BPF_AND|BPF_K, tag_bloom_lo);
+ /* jump behind end of tag match block if tag matches */
+ tag_matches--;
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0);
}
- /* matched, pass packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
+ /* nothing matched, drop packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
+ }
- if (i+1 >= ARRAY_SIZE(ins))
- return -1;
+ /* add all subsystem matches */
+ if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) != NULL) {
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) {
+ unsigned int hash = util_string_hash32(udev_list_entry_get_name(list_entry));
+
+ /* load device subsystem value in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem_hash));
+ if (udev_list_entry_get_value(list_entry) == NULL) {
+ /* jump if subsystem does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ } else {
+ /* jump if subsystem does not match */
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3);
+
+ /* load device devtype value in A */
+ bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_devtype_hash));
+ /* jump if value does not match */
+ hash = util_string_hash32(udev_list_entry_get_value(list_entry));
+ bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
+ }
+
+ /* matched, pass packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
+
+ if (i+1 >= ARRAY_SIZE(ins))
+ return -1;
+ }
+
+ /* nothing matched, drop packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
}
- /* nothing matched, drop packet */
- bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
+
+ /* matched, pass packet */
+ bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
/* install filter */
filter.len = i;
@@ -406,6 +451,7 @@ void udev_monitor_unref(struct udev_monitor *udev_monitor)
if (udev_monitor->sock >= 0)
close(udev_monitor->sock);
udev_list_cleanup_entries(udev_monitor->udev, &udev_monitor->filter_subsystem_list);
+ udev_list_cleanup_entries(udev_monitor->udev, &udev_monitor->filter_tag_list);
dbg(udev_monitor->udev, "monitor %p released\n", udev_monitor);
free(udev_monitor);
}
@@ -445,8 +491,7 @@ static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *
struct udev_list_entry *list_entry;
if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL)
- return 1;
-
+ goto tag;
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) {
const char *subsys = udev_list_entry_get_name(list_entry);
const char *dsubsys = udev_device_get_subsystem(udev_device);
@@ -458,11 +503,22 @@ static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *
devtype = udev_list_entry_get_value(list_entry);
if (devtype == NULL)
- return 1;
+ goto tag;
ddevtype = udev_device_get_devtype(udev_device);
if (ddevtype == NULL)
continue;
if (strcmp(ddevtype, devtype) == 0)
+ goto tag;
+ }
+ return 0;
+
+tag:
+ if (udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL)
+ return 1;
+ udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) {
+ const char *tag = udev_list_entry_get_name(list_entry);
+
+ if (udev_device_has_tag(udev_device, tag))
return 1;
}
return 0;
@@ -556,13 +612,11 @@ retry:
return NULL;
}
- if (strncmp(buf, "udev-", 5) == 0) {
+ if (memcmp(buf, "libudev", 8) == 0) {
/* udev message needs proper version magic */
nlh = (struct udev_monitor_netlink_header *) buf;
if (nlh->magic != htonl(UDEV_MONITOR_MAGIC))
return NULL;
- if (nlh->properties_off < sizeof(struct udev_monitor_netlink_header))
- return NULL;
if (nlh->properties_off+32 > buflen)
return NULL;
bufpos = nlh->properties_off;
@@ -626,17 +680,17 @@ retry:
int udev_monitor_send_device(struct udev_monitor *udev_monitor,
struct udev_monitor *destination, struct udev_device *udev_device)
{
- struct msghdr smsg;
- struct iovec iov[2];
const char *buf;
ssize_t blen;
ssize_t count;
blen = udev_device_get_properties_monitor_buf(udev_device, &buf);
if (blen < 32)
- return -1;
+ return -EINVAL;
if (udev_monitor->sun.sun_family != 0) {
+ struct msghdr smsg;
+ struct iovec iov[2];
const char *action;
char header[2048];
char *s;
@@ -660,23 +714,41 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor,
smsg.msg_iovlen = 2;
smsg.msg_name = &udev_monitor->sun;
smsg.msg_namelen = udev_monitor->addrlen;
- } else if (udev_monitor->snl.nl_family != 0) {
+ count = sendmsg(udev_monitor->sock, &smsg, 0);
+ info(udev_monitor->udev, "passed %zi bytes to socket monitor %p\n", count, udev_monitor);
+ return count;
+ }
+
+ if (udev_monitor->snl.nl_family != 0) {
+ struct msghdr smsg;
+ struct iovec iov[2];
const char *val;
struct udev_monitor_netlink_header nlh;
-
+ struct udev_list_entry *list_entry;
+ uint64_t tag_bloom_bits;
/* add versioned header */
memset(&nlh, 0x00, sizeof(struct udev_monitor_netlink_header));
- util_strscpy(nlh.version, sizeof(nlh.version), "udev-" VERSION);
+ memcpy(nlh.prefix, "libudev", 8);
nlh.magic = htonl(UDEV_MONITOR_MAGIC);
+ nlh.header_size = sizeof(struct udev_monitor_netlink_header);
val = udev_device_get_subsystem(udev_device);
- nlh.filter_subsystem = htonl(util_string_hash32(val));
+ nlh.filter_subsystem_hash = htonl(util_string_hash32(val));
val = udev_device_get_devtype(udev_device);
if (val != NULL)
- nlh.filter_devtype = htonl(util_string_hash32(val));
+ nlh.filter_devtype_hash = htonl(util_string_hash32(val));
iov[0].iov_base = &nlh;
iov[0].iov_len = sizeof(struct udev_monitor_netlink_header);
+ /* add tag bloom filter */
+ tag_bloom_bits = 0;
+ udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))
+ tag_bloom_bits |= util_string_bloom64(udev_list_entry_get_name(list_entry));
+ if (tag_bloom_bits > 0) {
+ nlh.filter_tag_bloom_hi = htonl(tag_bloom_bits >> 32);
+ nlh.filter_tag_bloom_lo = htonl(tag_bloom_bits & 0xffffffff);
+ }
+
/* add properties list */
nlh.properties_off = iov[0].iov_len;
nlh.properties_len = blen;
@@ -697,13 +769,12 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor,
else
smsg.msg_name = &udev_monitor->snl_destination;
smsg.msg_namelen = sizeof(struct sockaddr_nl);
- } else {
- return -1;
+ count = sendmsg(udev_monitor->sock, &smsg, 0);
+ info(udev_monitor->udev, "passed %zi bytes to netlink monitor %p\n", count, udev_monitor);
+ return count;
}
- count = sendmsg(udev_monitor->sock, &smsg, 0);
- info(udev_monitor->udev, "passed %zi bytes to monitor %p\n", count, udev_monitor);
- return count;
+ return -EINVAL;
}
/**
@@ -712,6 +783,9 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor,
* @subsystem: the subsystem value to match the incoming devices against
* @devtype: the devtype value to match the incoming devices against
*
+ * This filer is efficiently executed inside the kernel, and libudev subscribers
+ * will usually not be woken up for devices which do not match.
+ *
* The filter must be installed before the monitor is switched to listening mode.
*
* Returns: 0 on success, otherwise a negative error value.
@@ -721,7 +795,7 @@ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_mo
if (udev_monitor == NULL)
return -EINVAL;
if (subsystem == NULL)
- return 0;
+ return -EINVAL;
if (udev_list_entry_add(udev_monitor->udev,
&udev_monitor->filter_subsystem_list, subsystem, devtype, 0, 0) == NULL)
return -ENOMEM;
@@ -729,6 +803,30 @@ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_mo
}
/**
+ * udev_monitor_filter_add_match_tag:
+ * @udev_monitor: the monitor
+ * @tag: the name of a tag
+ *
+ * This filer is efficiently executed inside the kernel, and libudev subscribers
+ * will usually not be woken up for devices which do not match.
+ *
+ * The filter must be installed before the monitor is switched to listening mode.
+ *
+ * Returns: 0 on success, otherwise a negative error value.
+ */
+int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag)
+{
+ if (udev_monitor == NULL)
+ return -EINVAL;
+ if (tag == NULL)
+ return -EINVAL;
+ if (udev_list_entry_add(udev_monitor->udev,
+ &udev_monitor->filter_tag_list, tag, NULL, 0, 0) == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
* udev_monitor_filter_remove:
* @udev_monitor: monitor
*
diff --git a/libudev/libudev-private.h b/libudev/libudev-private.h
index 8dc469ec9b..548f4adbef 100644
--- a/libudev/libudev-private.h
+++ b/libudev/libudev-private.h
@@ -14,6 +14,8 @@
#include <syslog.h>
#include <signal.h>
+#include <stdint.h>
+#include <stdbool.h>
#include "libudev.h"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
@@ -84,7 +86,8 @@ const char *udev_device_get_sysname_old(struct udev_device *udev_device);
int udev_device_set_devpath_old(struct udev_device *udev_device, const char *devpath_old);
const char *udev_device_get_knodename(struct udev_device *udev_device);
int udev_device_add_tag(struct udev_device *udev_device, const char *tag);
-struct udev_list_entry *udev_device_get_tag_list_entry(struct udev_device *udev_device);
+void udev_device_cleanup_tags_list(struct udev_device *udev_device);
+struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device);
int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
int udev_device_set_knodename(struct udev_device *udev_device, const char *knodename);
int udev_device_get_timeout(struct udev_device *udev_device);
@@ -203,7 +206,8 @@ size_t util_strscpyl(char *dest, size_t size, const char *src, ...) __attribute_
int udev_util_replace_whitespace(const char *str, char *to, size_t len);
int udev_util_replace_chars(char *str, const char *white);
int udev_util_encode_string(const char *str, char *str_enc, size_t len);
-unsigned int util_string_hash32(const char *str);
+unsigned int util_string_hash32(const char *key);
+uint64_t util_string_bloom64(const char *str);
/* libudev-util-private.c */
int util_create_path(struct udev *udev, const char *path);
diff --git a/libudev/libudev-util.c b/libudev/libudev-util.c
index c0209f9cc6..3a67b0cd5d 100644
--- a/libudev/libudev-util.c
+++ b/libudev/libudev-util.c
@@ -481,15 +481,74 @@ err:
return -1;
}
+/*
+ * http://sites.google.com/site/murmurhash/
+ *
+ * All code is released to the public domain. For business purposes,
+ * Murmurhash is under the MIT license.
+ *
+ */
+static unsigned int murmur_hash2(const char *key, int len, unsigned int seed)
+{
+ /*
+ * 'm' and 'r' are mixing constants generated offline.
+ * They're not really 'magic', they just happen to work well.
+ */
+ const unsigned int m = 0x5bd1e995;
+ const int r = 24;
+
+ /* initialize the hash to a 'random' value */
+ unsigned int h = seed ^ len;
+
+ /* mix 4 bytes at a time into the hash */
+ const unsigned char * data = (const unsigned char *)key;
+
+ while(len >= 4) {
+ unsigned int k = *(unsigned int *)data;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h *= m;
+ h ^= k;
+
+ data += 4;
+ len -= 4;
+ }
+
+ /* handle the last few bytes of the input array */
+ switch(len) {
+ case 3:
+ h ^= data[2] << 16;
+ case 2:
+ h ^= data[1] << 8;
+ case 1:
+ h ^= data[0];
+ h *= m;
+ };
+
+ /* do a few final mixes of the hash to ensure the last few bytes are well-incorporated */
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return h;
+}
+
unsigned int util_string_hash32(const char *str)
{
- unsigned int hash = 0;
+ return murmur_hash2(str, strlen(str), 0);
+}
- while (str[0] != '\0') {
- hash += str[0] << 4;
- hash += str[0] >> 4;
- hash *= 11;
- str++;
- }
- return hash;
+/* get a bunch of bit numbers out of the hash, and set the bits in our bit field */
+uint64_t util_string_bloom64(const char *str)
+{
+ uint64_t bits = 0;
+ unsigned int hash = util_string_hash32(str);
+
+ bits |= 1LLU << (hash & 63);
+ bits |= 1LLU << ((hash >> 6) & 63);
+ bits |= 1LLU << ((hash >> 12) & 63);
+ bits |= 1LLU << ((hash >> 18) & 63);
+ return bits;
}
diff --git a/libudev/libudev.h b/libudev/libudev.h
index 750664f43c..3b73fbd61b 100644
--- a/libudev/libudev.h
+++ b/libudev/libudev.h
@@ -1,7 +1,7 @@
/*
* libudev - interface to udev device information
*
- * Copyright (C) 2008-2009 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008-2010 Kay Sievers <kay.sievers@vrfy.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -118,6 +118,7 @@ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monito
/* in-kernel socket filters to select messages that get delivered to a listener */
int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor,
const char *subsystem, const char *devtype);
+int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag);
int udev_monitor_filter_update(struct udev_monitor *udev_monitor);
int udev_monitor_filter_remove(struct udev_monitor *udev_monitor);
@@ -138,6 +139,7 @@ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, cons
int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value);
int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value);
int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname);
+int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag);
int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, const char *syspath);
/* run enumeration with active filters */
int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate);
diff --git a/udev/udev-event.c b/udev/udev-event.c
index b2e1baee1f..212ccc7950 100644
--- a/udev/udev-event.c
+++ b/udev/udev-event.c
@@ -543,7 +543,6 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
if (strcmp(udev_device_get_action(dev), "remove") == 0) {
udev_device_read_db(dev);
- udev_device_set_info_loaded(dev);
udev_device_delete_db(dev);
if (major(udev_device_get_devnum(dev)) != 0)
diff --git a/udev/udev-rules.c b/udev/udev-rules.c
index 6eb8350343..b5016d0bc3 100644
--- a/udev/udev-rules.c
+++ b/udev/udev-rules.c
@@ -157,6 +157,7 @@ enum token_type {
TK_A_GROUP_ID, /* gid_t */
TK_A_MODE_ID, /* mode_t */
TK_A_ENV, /* val, attr */
+ TK_A_TAG, /* val */
TK_A_NAME, /* val */
TK_A_DEVLINK, /* val */
TK_A_EVENT_TIMEOUT, /* int */
@@ -285,6 +286,7 @@ static const char *token_str(enum token_type type)
[TK_A_GROUP_ID] = "A GROUP_ID",
[TK_A_MODE_ID] = "A MODE_ID",
[TK_A_ENV] = "A ENV",
+ [TK_A_TAG] = "A ENV",
[TK_A_NAME] = "A NAME",
[TK_A_DEVLINK] = "A DEVLINK",
[TK_A_EVENT_TIMEOUT] = "A EVENT_TIMEOUT",
@@ -354,6 +356,9 @@ static void dump_token(struct udev_rules *rules, struct token *token)
dbg(rules->udev, "%s %s '%s' '%s'(%s)\n",
token_str(type), operation_str(op), attr, value, string_glob_str(glob));
break;
+ case TK_A_TAG:
+ dbg(rules->udev, "%s %s '%s'\n", token_str(type), operation_str(op), value);
+ break;
case TK_A_STRING_ESCAPE_NONE:
case TK_A_STRING_ESCAPE_REPLACE:
dbg(rules->udev, "%s\n", token_str(type));
@@ -1003,6 +1008,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type,
case TK_A_MODE:
case TK_A_NAME:
case TK_A_GOTO:
+ case TK_A_TAG:
token->key.value_off = add_string(rule_tmp->rules, value);
break;
case TK_M_ENV:
@@ -1350,6 +1356,11 @@ static int add_rule(struct udev_rules *rules, char *line,
continue;
}
+ if (strcmp(key, "TAG") == 0) {
+ rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL);
+ continue;
+ }
+
if (strcmp(key, "PROGRAM") == 0) {
rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL);
continue;
@@ -2408,6 +2419,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
}
break;
}
+ case TK_A_TAG:
+ udev_device_add_tag(event->dev, &rules->buf[cur->key.value_off]);
+ break;
case TK_A_NAME:
{
const char *name = &rules->buf[cur->key.value_off];
diff --git a/udev/udev.xml b/udev/udev.xml
index 192a6f1238..842fd5d52a 100644
--- a/udev/udev.xml
+++ b/udev/udev.xml
@@ -349,6 +349,19 @@
</varlistentry>
<varlistentry>
+ <term><option>TAG</option></term>
+ <listitem>
+ <para>Attach a tag to a device. This is used to filter events for users
+ of libudev's monitor functionality, or to enumerate a group of tagged
+ devices. The implementation can only work efficiently if only a few
+ tags are attached to a device. It is only meant to be used in
+ contexts with specific device filter requirements, and not as a
+ general-purpose flag. Excessive use might result in inefficient event
+ handling.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>RUN</option></term>
<listitem>
<para>Add a program to the list of programs to be executed for a specific
diff --git a/udev/udevadm-monitor.c b/udev/udevadm-monitor.c
index d136c6070a..fb650846bc 100644
--- a/udev/udevadm-monitor.c
+++ b/udev/udevadm-monitor.c
@@ -73,6 +73,7 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
int print_kernel = 0;
int print_udev = 0;
struct udev_list_node subsystem_match_list;
+ struct udev_list_node tag_match_list;
struct udev_monitor *udev_monitor = NULL;
struct udev_monitor *kernel_monitor = NULL;
fd_set readfds;
@@ -84,13 +85,15 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
{ "kernel", no_argument, NULL, 'k' },
{ "udev", no_argument, NULL, 'u' },
{ "subsystem-match", required_argument, NULL, 's' },
+ { "tag-match", required_argument, NULL, 't' },
{ "help", no_argument, NULL, 'h' },
{}
};
udev_list_init(&subsystem_match_list);
+ udev_list_init(&tag_match_list);
while (1) {
- option = getopt_long(argc, argv, "epkus:h", options, NULL);
+ option = getopt_long(argc, argv, "pekus:t:h", options, NULL);
if (option == -1)
break;
@@ -119,12 +122,16 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
udev_list_entry_add(udev, &subsystem_match_list, subsys, devtype, 0, 0);
break;
}
+ case 't':
+ udev_list_entry_add(udev, &tag_match_list, optarg, NULL, 0, 0);
+ break;
case 'h':
printf("Usage: udevadm monitor [--property] [--kernel] [--udev] [--help]\n"
" --property print the event properties\n"
" --kernel print kernel uevents\n"
" --udev print udev events\n"
" --subsystem-match=<subsystem[/devtype]> filter events by subsystem\n"
+ " --tag-match=<tag> filter events by tag\n"
" --help\n\n");
default:
goto out;
@@ -168,6 +175,13 @@ int udevadm_monitor(struct udev *udev, int argc, char *argv[])
fprintf(stderr, "error: unable to apply subsystem filter '%s'\n", subsys);
}
+ udev_list_entry_foreach(entry, udev_list_get_entry(&tag_match_list)) {
+ const char *tag = udev_list_entry_get_name(entry);
+
+ if (udev_monitor_filter_add_match_tag(udev_monitor, tag) < 0)
+ fprintf(stderr, "error: unable to apply tag filter '%s'\n", tag);
+ }
+
if (udev_monitor_enable_receiving(udev_monitor) < 0) {
fprintf(stderr, "error: unable to subscribe to udev events\n");
rc = 2;
@@ -244,5 +258,6 @@ out:
udev_monitor_unref(udev_monitor);
udev_monitor_unref(kernel_monitor);
udev_list_cleanup_entries(udev, &subsystem_match_list);
+ udev_list_cleanup_entries(udev, &tag_match_list);
return rc;
}
diff --git a/udev/udevadm-trigger.c b/udev/udevadm-trigger.c
index 03aa53437f..3cb07dda9a 100644
--- a/udev/udevadm-trigger.c
+++ b/udev/udevadm-trigger.c
@@ -101,6 +101,7 @@ int udevadm_trigger(struct udev *udev, int argc, char *argv[])
{ "attr-match", required_argument, NULL, 'a' },
{ "attr-nomatch", required_argument, NULL, 'A' },
{ "property-match", required_argument, NULL, 'p' },
+ { "tag-match", required_argument, NULL, 'g' },
{ "sysname-match", required_argument, NULL, 'y' },
{ "help", no_argument, NULL, 'h' },
{}
@@ -127,7 +128,7 @@ int udevadm_trigger(struct udev *udev, int argc, char *argv[])
const char *val;
char buf[UTIL_PATH_SIZE];
- option = getopt_long(argc, argv, "vnFo:t:hcp:s:S:a:A:y:", options, NULL);
+ option = getopt_long(argc, argv, "vng:o:t:hcp:s:S:a:A:y:", options, NULL);
if (option == -1)
break;
@@ -172,6 +173,9 @@ int udevadm_trigger(struct udev *udev, int argc, char *argv[])
key = keyval(optarg, &val, buf, sizeof(buf));
udev_enumerate_add_match_property(udev_enumerate, key, val);
break;
+ case 'g':
+ udev_enumerate_add_match_tag(udev_enumerate, optarg);
+ break;
case 'y':
udev_enumerate_add_match_sysname(udev_enumerate, optarg);
break;
@@ -190,6 +194,7 @@ int udevadm_trigger(struct udev *udev, int argc, char *argv[])
" --attr-match=<file[=<value>]> trigger devices with a matching attribute\n"
" --attr-nomatch=<file[=<value>]> exclude devices with a matching attribute\n"
" --property-match=<key>=<value> trigger devices with a matching property\n"
+ " --tag-match=<key>=<value> trigger devices with a matching property\n"
" --sysname-match=<name> trigger devices with a matching name\n"
" --help\n\n");
goto exit;
diff --git a/udev/udevadm.xml b/udev/udevadm.xml
index fa1742bada..73e6f110a6 100644
--- a/udev/udevadm.xml
+++ b/udev/udevadm.xml
@@ -217,6 +217,13 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><option>--tag-match=<replaceable>property</replaceable></option></term>
+ <listitem>
+ <para>Trigger events for devices with a matching tag. This option can be
+ specified multiple times.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><option>--sysname-match=<replaceable>name</replaceable></option></term>
<listitem>
<para>Trigger events for devices with a matching sys device name. This option can be
@@ -356,6 +363,12 @@
</listitem>
</varlistentry>
<varlistentry>
+ <term><option>--tag-match=<replaceable>string</replaceable></option></term>
+ <listitem>
+ <para>Filter events by property. Only udev events with a given tag attached will pass.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term><option>--help</option></term>
<listitem>
<para>Print help text.</para>