1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-23 21:35:11 +03:00

add TAG= to improve event filtering and device enumeration

This commit is contained in:
Kay Sievers 2010-04-22 18:12:36 +02:00
parent e7964b93e8
commit 28460195c2
16 changed files with 595 additions and 184 deletions

View File

@ -28,9 +28,9 @@ CLEANFILES =
# ------------------------------------------------------------------------------
# libudev
# ------------------------------------------------------------------------------
LIBUDEV_CURRENT=7
LIBUDEV_CURRENT=8
LIBUDEV_REVISION=0
LIBUDEV_AGE=7
LIBUDEV_AGE=8
SUBDIRS += libudev/docs

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
}
@ -439,6 +437,25 @@ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, con
return 0;
}
/**
* 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
@ -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))
dev = udev_device_new_from_syspath(udev_enumerate->udev, syspath);
if (dev == NULL)
continue;
if (lstat(syspath, &statbuf) != 0)
continue;
if (S_ISREG(statbuf.st_mode))
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";

View File

@ -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;
if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL) {
int tag_matches;
/* 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);
/* 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++;
/* 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);
/* 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);
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);
/* 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);
}
/* 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,13 +795,37 @@ 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;
return 0;
}
/**
* 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

View File

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

View File

@ -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;
while (str[0] != '\0') {
hash += str[0] << 4;
hash += str[0] >> 4;
hash *= 11;
str++;
}
return hash;
return murmur_hash2(str, strlen(str), 0);
}
/* 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;
}

View File

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

View File

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

View File

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

View File

@ -348,6 +348,19 @@
</listitem>
</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>

View File

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

View File

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

View File

@ -216,6 +216,13 @@
specified multiple times and supports shell style pattern matching.</para>
</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>
@ -355,6 +362,12 @@
<para>Filter events by subsystem[/devtype]. Only udev events with a matching subsystem value will pass.</para>
</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>