mirror of
https://github.com/systemd/systemd.git
synced 2024-12-25 01:34:28 +03:00
libudev-monitor: use Hashmap or Set to store filters
This commit is contained in:
parent
5e1e4c247b
commit
759d9f3f8d
@ -17,10 +17,12 @@
|
|||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "format-util.h"
|
#include "format-util.h"
|
||||||
|
#include "hashmap.h"
|
||||||
#include "libudev-device-internal.h"
|
#include "libudev-device-internal.h"
|
||||||
#include "libudev-private.h"
|
#include "libudev-private.h"
|
||||||
#include "missing.h"
|
#include "missing.h"
|
||||||
#include "mount-util.h"
|
#include "mount-util.h"
|
||||||
|
#include "set.h"
|
||||||
#include "socket-util.h"
|
#include "socket-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
@ -45,8 +47,8 @@ struct udev_monitor {
|
|||||||
union sockaddr_union snl_trusted_sender;
|
union sockaddr_union snl_trusted_sender;
|
||||||
union sockaddr_union snl_destination;
|
union sockaddr_union snl_destination;
|
||||||
socklen_t addrlen;
|
socklen_t addrlen;
|
||||||
struct udev_list filter_subsystem_list;
|
Hashmap *subsystem_filter;
|
||||||
struct udev_list filter_tag_list;
|
Set *tag_filter;
|
||||||
bool bound;
|
bool bound;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -164,9 +166,6 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
udev_list_init(udev, &udev_monitor->filter_subsystem_list, false);
|
|
||||||
udev_list_init(udev, &udev_monitor->filter_tag_list, true);
|
|
||||||
|
|
||||||
return TAKE_PTR(udev_monitor);
|
return TAKE_PTR(udev_monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,21 +223,19 @@ static void bpf_jmp(struct sock_filter *ins, unsigned *i,
|
|||||||
*
|
*
|
||||||
* Returns: 0 on success, otherwise a negative error value.
|
* Returns: 0 on success, otherwise a negative error value.
|
||||||
*/
|
*/
|
||||||
_public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
|
_public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor) {
|
||||||
{
|
struct sock_filter ins[512] = {};
|
||||||
struct sock_filter ins[512];
|
|
||||||
struct sock_fprog filter;
|
struct sock_fprog filter;
|
||||||
unsigned i;
|
const char *subsystem, *devtype, *tag;
|
||||||
struct udev_list_entry *list_entry;
|
unsigned i = 0;
|
||||||
int err;
|
Iterator it;
|
||||||
|
|
||||||
if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL &&
|
assert_return(udev_monitor, -EINVAL);
|
||||||
udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL)
|
|
||||||
|
if (hashmap_isempty(udev_monitor->subsystem_filter) &&
|
||||||
|
set_isempty(udev_monitor->tag_filter))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
memzero(ins, sizeof(ins));
|
|
||||||
i = 0;
|
|
||||||
|
|
||||||
/* load magic in A */
|
/* load magic in A */
|
||||||
bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic));
|
bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic));
|
||||||
/* jump if magic matches */
|
/* jump if magic matches */
|
||||||
@ -246,17 +243,12 @@ _public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
|
|||||||
/* wrong magic, pass packet */
|
/* wrong magic, pass packet */
|
||||||
bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
|
bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
|
||||||
|
|
||||||
if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL) {
|
if (!set_isempty(udev_monitor->tag_filter)) {
|
||||||
int tag_matches;
|
int tag_matches = set_size(udev_monitor->tag_filter);
|
||||||
|
|
||||||
/* 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 */
|
/* add all tags matches */
|
||||||
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list)) {
|
SET_FOREACH(tag, udev_monitor->tag_filter, it) {
|
||||||
uint64_t tag_bloom_bits = util_string_bloom64(udev_list_entry_get_name(list_entry));
|
uint64_t tag_bloom_bits = util_string_bloom64(tag);
|
||||||
uint32_t tag_bloom_hi = tag_bloom_bits >> 32;
|
uint32_t tag_bloom_hi = tag_bloom_bits >> 32;
|
||||||
uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff;
|
uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff;
|
||||||
|
|
||||||
@ -281,23 +273,23 @@ _public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* add all subsystem matches */
|
/* add all subsystem matches */
|
||||||
if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) != NULL) {
|
if (!hashmap_isempty(udev_monitor->subsystem_filter)) {
|
||||||
udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) {
|
HASHMAP_FOREACH_KEY(devtype, subsystem, udev_monitor->subsystem_filter, it) {
|
||||||
uint32_t hash = util_string_hash32(udev_list_entry_get_name(list_entry));
|
uint32_t hash = util_string_hash32(subsystem);
|
||||||
|
|
||||||
/* load device subsystem value in A */
|
/* 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));
|
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) {
|
if (!devtype) {
|
||||||
/* jump if subsystem does not match */
|
/* jump if subsystem does not match */
|
||||||
bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
|
bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
|
||||||
} else {
|
} else {
|
||||||
|
hash = util_string_hash32(devtype);
|
||||||
|
|
||||||
/* jump if subsystem does not match */
|
/* jump if subsystem does not match */
|
||||||
bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3);
|
bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 3);
|
||||||
|
|
||||||
/* load device devtype value in A */
|
/* 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));
|
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 */
|
/* 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);
|
bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,11 +308,14 @@ _public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
|
|||||||
bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
|
bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
|
||||||
|
|
||||||
/* install filter */
|
/* install filter */
|
||||||
memzero(&filter, sizeof(filter));
|
filter = (struct sock_fprog) {
|
||||||
filter.len = i;
|
.len = i,
|
||||||
filter.filter = ins;
|
.filter = ins,
|
||||||
err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
|
};
|
||||||
return err < 0 ? -errno : 0;
|
if (setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender)
|
int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender)
|
||||||
@ -395,8 +390,8 @@ static struct udev_monitor *udev_monitor_free(struct udev_monitor *udev_monitor)
|
|||||||
assert(udev_monitor);
|
assert(udev_monitor);
|
||||||
|
|
||||||
udev_monitor_disconnect(udev_monitor);
|
udev_monitor_disconnect(udev_monitor);
|
||||||
udev_list_cleanup(&udev_monitor->filter_subsystem_list);
|
hashmap_free_free_free(udev_monitor->subsystem_filter);
|
||||||
udev_list_cleanup(&udev_monitor->filter_tag_list);
|
set_free_free(udev_monitor->tag_filter);
|
||||||
return mfree(udev_monitor);
|
return mfree(udev_monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,41 +446,49 @@ _public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
|
|||||||
return udev_monitor->sock;
|
return udev_monitor->sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device)
|
static int passes_filter(struct udev_monitor *udev_monitor, sd_device *device) {
|
||||||
{
|
const char *tag, *subsystem, *devtype, *s, *d = NULL;
|
||||||
struct udev_list_entry *list_entry;
|
Iterator i;
|
||||||
|
int r;
|
||||||
|
|
||||||
if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL)
|
assert_return(udev_monitor, -EINVAL);
|
||||||
|
assert_return(device, -EINVAL);
|
||||||
|
|
||||||
|
if (hashmap_isempty(udev_monitor->subsystem_filter))
|
||||||
goto tag;
|
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);
|
|
||||||
const char *devtype;
|
|
||||||
const char *ddevtype;
|
|
||||||
|
|
||||||
if (!streq(dsubsys, subsys))
|
r = sd_device_get_subsystem(device, &s);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_device_get_devtype(device, &d);
|
||||||
|
if (r < 0 && r != -ENOENT)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
HASHMAP_FOREACH_KEY(devtype, subsystem, udev_monitor->subsystem_filter, i) {
|
||||||
|
if (!streq(s, subsystem))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
devtype = udev_list_entry_get_value(list_entry);
|
if (!devtype)
|
||||||
if (devtype == NULL)
|
|
||||||
goto tag;
|
goto tag;
|
||||||
ddevtype = udev_device_get_devtype(udev_device);
|
|
||||||
if (ddevtype == NULL)
|
if (!d)
|
||||||
continue;
|
continue;
|
||||||
if (streq(ddevtype, devtype))
|
|
||||||
|
if (streq(d, devtype))
|
||||||
goto tag;
|
goto tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
tag:
|
tag:
|
||||||
if (udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL)
|
if (set_isempty(udev_monitor->tag_filter))
|
||||||
return 1;
|
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))
|
SET_FOREACH(tag, udev_monitor->tag_filter, i)
|
||||||
|
if (sd_device_has_tag(device, tag) > 0)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,7 +633,7 @@ retry:
|
|||||||
udev_device_set_is_initialized(udev_device);
|
udev_device_set_is_initialized(udev_device);
|
||||||
|
|
||||||
/* skip device, if it does not pass the current filter */
|
/* skip device, if it does not pass the current filter */
|
||||||
if (!passes_filter(udev_monitor, udev_device)) {
|
if (!passes_filter(udev_monitor, udev_device->device)) {
|
||||||
struct pollfd pfd[1];
|
struct pollfd pfd[1];
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
@ -749,14 +752,32 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor,
|
|||||||
*
|
*
|
||||||
* Returns: 0 on success, otherwise a negative error value.
|
* Returns: 0 on success, otherwise a negative error value.
|
||||||
*/
|
*/
|
||||||
_public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype)
|
_public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) {
|
||||||
{
|
_cleanup_free_ char *s = NULL, *d = NULL;
|
||||||
if (udev_monitor == NULL)
|
int r;
|
||||||
return -EINVAL;
|
|
||||||
if (subsystem == NULL)
|
assert_return(udev_monitor, -EINVAL);
|
||||||
return -EINVAL;
|
assert_return(subsystem, -EINVAL);
|
||||||
if (udev_list_entry_add(&udev_monitor->filter_subsystem_list, subsystem, devtype) == NULL)
|
|
||||||
|
s = strdup(subsystem);
|
||||||
|
if (!s)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (devtype) {
|
||||||
|
d = strdup(devtype);
|
||||||
|
if (!d)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = hashmap_ensure_allocated(&udev_monitor->subsystem_filter, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = hashmap_put(udev_monitor->subsystem_filter, s, d);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
s = d = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,14 +793,28 @@ _public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor
|
|||||||
*
|
*
|
||||||
* Returns: 0 on success, otherwise a negative error value.
|
* Returns: 0 on success, otherwise a negative error value.
|
||||||
*/
|
*/
|
||||||
_public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag)
|
_public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) {
|
||||||
{
|
_cleanup_free_ char *t = NULL;
|
||||||
if (udev_monitor == NULL)
|
int r;
|
||||||
return -EINVAL;
|
|
||||||
if (tag == NULL)
|
assert_return(udev_monitor, -EINVAL);
|
||||||
return -EINVAL;
|
assert_return(tag, -EINVAL);
|
||||||
if (udev_list_entry_add(&udev_monitor->filter_tag_list, tag, NULL) == NULL)
|
|
||||||
|
t = strdup(tag);
|
||||||
|
if (!t)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = set_ensure_allocated(&udev_monitor->tag_filter, &string_hash_ops);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = set_put(udev_monitor->tag_filter, t);
|
||||||
|
if (r == -EEXIST)
|
||||||
|
return 0;
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
TAKE_PTR(t);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -791,11 +826,14 @@ _public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor
|
|||||||
*
|
*
|
||||||
* Returns: 0 on success, otherwise a negative error value.
|
* Returns: 0 on success, otherwise a negative error value.
|
||||||
*/
|
*/
|
||||||
_public_ int udev_monitor_filter_remove(struct udev_monitor *udev_monitor)
|
_public_ int udev_monitor_filter_remove(struct udev_monitor *udev_monitor) {
|
||||||
{
|
|
||||||
static const struct sock_fprog filter = { 0, NULL };
|
static const struct sock_fprog filter = { 0, NULL };
|
||||||
|
|
||||||
udev_list_cleanup(&udev_monitor->filter_subsystem_list);
|
assert_return(udev_monitor, -EINVAL);
|
||||||
|
|
||||||
|
udev_monitor->subsystem_filter = hashmap_free_free_free(udev_monitor->subsystem_filter);
|
||||||
|
udev_monitor->tag_filter = set_free_free(udev_monitor->tag_filter);
|
||||||
|
|
||||||
if (setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0)
|
if (setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user