Assorted libsystemd/network hardening fixes flagged by kres (#42865)
This commit is contained in:
@@ -1330,6 +1330,9 @@ int set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key) {
|
||||
if (r <= 0) {
|
||||
if (hash_ops && hash_ops->free_key)
|
||||
hash_ops->free_key(key);
|
||||
else if (hash_ops && hash_ops->free_value)
|
||||
/* Sets store their element in the key slot but may carry a value destructor. */
|
||||
hash_ops->free_value(key);
|
||||
else
|
||||
free(key);
|
||||
}
|
||||
|
||||
@@ -391,6 +391,9 @@ int bus_match_run(
|
||||
r = bus_match_run(bus, found, m);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (bus && bus->match_callbacks_modified)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2099,6 +2099,37 @@ static int add_object_vtable_internal(
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
/* Remove members we already inserted into the global tables before failing (identified by
|
||||
* parent), so they don't dangle once the slot and node are freed below. */
|
||||
if (s && s->node_vtable.interface)
|
||||
for (v = bus_vtable_next(vtable, vtable); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) {
|
||||
BusVTableMember key, *x;
|
||||
Set *set;
|
||||
|
||||
if (v->type == _SD_BUS_VTABLE_METHOD) {
|
||||
set = bus->vtable_methods;
|
||||
key.member = v->x.method.member;
|
||||
} else if (IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY)) {
|
||||
set = bus->vtable_properties;
|
||||
key.member = v->x.property.member;
|
||||
} else
|
||||
continue;
|
||||
|
||||
/* The entry that failed validation may carry a NULL member, which was never
|
||||
* inserted and would crash the hashmap lookup below. Skip it. */
|
||||
if (isempty(key.member))
|
||||
continue;
|
||||
|
||||
key.path = n->path;
|
||||
key.interface = s->node_vtable.interface;
|
||||
|
||||
x = set_get(set, &key);
|
||||
if (x && x->parent == &s->node_vtable) {
|
||||
assert_se(set_remove(set, &key) == x);
|
||||
free(x);
|
||||
}
|
||||
}
|
||||
|
||||
sd_bus_slot_unref(s);
|
||||
bus_node_gc(bus, n);
|
||||
|
||||
|
||||
@@ -67,6 +67,63 @@ static void test_match_scope(const char *match, BusMatchScope scope) {
|
||||
assert_se(bus_match_get_scope(components, n_components) == scope);
|
||||
}
|
||||
|
||||
static sd_bus *modify_bus;
|
||||
static BusMatchNode *modify_root;
|
||||
static sd_bus_slot *modify_slots;
|
||||
|
||||
static int strv_uaf_modify_filter(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
|
||||
/* Remove both argNhas matches from within the callback (as bus_slot_disconnect() would), which
|
||||
* frees the argNhas node that bus_match_run() is still iterating over the array values of. */
|
||||
assert_se(bus_match_remove(modify_root, &modify_slots[0].match_callback) >= 0);
|
||||
assert_se(bus_match_remove(modify_root, &modify_slots[1].match_callback) >= 0);
|
||||
modify_bus->match_callbacks_modified = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int strv_uaf_noop_filter(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_cb_match(BusMatchNode *root, sd_bus_slot *slot, const char *match, sd_bus_message_handler_t cb) {
|
||||
BusMatchComponent *components = NULL;
|
||||
size_t n_components = 0;
|
||||
|
||||
CLEANUP_ARRAY(components, n_components, bus_match_parse_free);
|
||||
|
||||
assert_se(bus_match_parse(match, &components, &n_components) >= 0);
|
||||
slot->n_ref = 1; /* bus_match_run() refs the slot around each callback */
|
||||
slot->match_callback.callback = cb;
|
||||
assert_se(bus_match_add(root, components, n_components, &slot->match_callback) >= 0);
|
||||
}
|
||||
|
||||
/* Two argNhas= matches under the same node; the first callback frees that node. bus_match_run() must
|
||||
* not dereference it for the next array value. */
|
||||
static void test_match_run_strv_modified(sd_bus *bus) {
|
||||
BusMatchNode root = { .type = BUS_MATCH_ROOT };
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
sd_bus_slot slots[2] = {};
|
||||
|
||||
modify_bus = bus;
|
||||
modify_root = &root;
|
||||
modify_slots = slots;
|
||||
|
||||
add_cb_match(&root, &slots[0], "arg0has='pi'", strv_uaf_modify_filter);
|
||||
add_cb_match(&root, &slots[1], "arg0has='pa'", strv_uaf_noop_filter);
|
||||
|
||||
assert_se(sd_bus_message_new_signal(bus, &m, "/", "a.b", "c") >= 0);
|
||||
assert_se(sd_bus_message_append(m, "as", 2, "pi", "pa") >= 0);
|
||||
assert_se(sd_bus_message_seal(m, 1, 0) >= 0);
|
||||
|
||||
/* Let the leaf gating pass for our synthetic message/matches. */
|
||||
m->read_counter = 1;
|
||||
bus->iteration_counter = 1;
|
||||
bus->match_callbacks_modified = false;
|
||||
|
||||
assert_se(bus_match_run(bus, &root, m) == 0);
|
||||
|
||||
bus_match_free(&root);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
BusMatchNode root = {
|
||||
.type = BUS_MATCH_ROOT,
|
||||
@@ -142,5 +199,7 @@ int main(int argc, char *argv[]) {
|
||||
test_match_scope("member='gurke',path='/org/freedesktop/DBus/Local'", BUS_MATCH_LOCAL);
|
||||
test_match_scope("arg2='piep',sender='org.freedesktop.DBus',member='waldo'", BUS_MATCH_DRIVER);
|
||||
|
||||
test_match_run_strv_modified(bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "bus-internal.h"
|
||||
#include "bus-message.h"
|
||||
#include "log.h"
|
||||
#include "set.h"
|
||||
#include "strv.h"
|
||||
#include "tests.h"
|
||||
|
||||
@@ -566,6 +567,25 @@ static int client(void *p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const sd_bus_vtable partial_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("Foo", NULL, NULL, NULL, 0),
|
||||
SD_BUS_METHOD("has space", NULL, NULL, NULL, 0), /* invalid member name, registration fails here */
|
||||
SD_BUS_VTABLE_END
|
||||
};
|
||||
|
||||
static void test_vtable_partial_failure(void) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
|
||||
ASSERT_OK(sd_bus_new(&bus));
|
||||
|
||||
/* Registration fails at the invalid member, after "Foo" was already inserted into the global vtable
|
||||
* member tables. Check that the fail path removes it again. */
|
||||
ASSERT_ERROR(sd_bus_add_object_vtable(bus, NULL, "/x", "org.test.iface", partial_vtable, NULL), EINVAL);
|
||||
|
||||
ASSERT_TRUE(set_isempty(bus->vtable_methods));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
|
||||
_cleanup_(sd_future_unrefp) sd_future *f_server = NULL, *f_client = NULL;
|
||||
@@ -573,6 +593,8 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
test_vtable_partial_failure();
|
||||
|
||||
c.automatic_integer_property = 4711;
|
||||
ASSERT_NOT_NULL(c.automatic_string_property = strdup("dudeldu"));
|
||||
|
||||
|
||||
@@ -15,3 +15,24 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group,
|
||||
int device_monitor_get_address(sd_device_monitor *m, union sockaddr_union *ret);
|
||||
int device_monitor_allow_unicast_sender(sd_device_monitor *m, sd_device_monitor *sender);
|
||||
int device_monitor_send(sd_device_monitor *m, const union sockaddr_union *destination, sd_device *device);
|
||||
|
||||
#define UDEV_MONITOR_MAGIC 0xfeedcafe
|
||||
|
||||
typedef struct monitor_netlink_header {
|
||||
/* "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 magic;
|
||||
/* Total length of header structure known to the sender */
|
||||
unsigned header_size;
|
||||
/* Properties string buffer */
|
||||
unsigned properties_off;
|
||||
unsigned properties_len;
|
||||
/* Hashes of primary device properties strings, to let libudev subscribers
|
||||
* use in-kernel socket filters; values need to be stored in network order */
|
||||
unsigned filter_subsystem_hash;
|
||||
unsigned filter_devtype_hash;
|
||||
unsigned filter_tag_bloom_hi;
|
||||
unsigned filter_tag_bloom_lo;
|
||||
} monitor_netlink_header;
|
||||
|
||||
@@ -67,27 +67,6 @@ struct sd_device_monitor {
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
#define UDEV_MONITOR_MAGIC 0xfeedcafe
|
||||
|
||||
typedef struct monitor_netlink_header {
|
||||
/* "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 magic;
|
||||
/* Total length of header structure known to the sender */
|
||||
unsigned header_size;
|
||||
/* Properties string buffer */
|
||||
unsigned properties_off;
|
||||
unsigned properties_len;
|
||||
/* Hashes of primary device properties strings, to let libudev subscribers
|
||||
* use in-kernel socket filters; values need to be stored in network order */
|
||||
unsigned filter_subsystem_hash;
|
||||
unsigned filter_devtype_hash;
|
||||
unsigned filter_tag_bloom_hi;
|
||||
unsigned filter_tag_bloom_lo;
|
||||
} monitor_netlink_header;
|
||||
|
||||
static int monitor_set_nl_address(sd_device_monitor *m) {
|
||||
union sockaddr_union snl;
|
||||
socklen_t addrlen;
|
||||
@@ -642,10 +621,10 @@ _public_ int sd_device_monitor_receive(sd_device_monitor *m, sd_device **ret) {
|
||||
"Invalid message signature (%x != %x).",
|
||||
message.nlh->magic, htobe32(UDEV_MONITOR_MAGIC));
|
||||
|
||||
if (message.nlh->properties_off + 32 > (size_t) n)
|
||||
if (message.nlh->properties_off > LESS_BY((size_t) n, 32u))
|
||||
return log_monitor_errno(m, SYNTHETIC_ERRNO(EAGAIN),
|
||||
"Invalid offset for properties (%u > %zi).",
|
||||
message.nlh->properties_off + 32, n);
|
||||
"Invalid properties offset (%u) for message of length %zi.",
|
||||
message.nlh->properties_off, n);
|
||||
|
||||
offset = message.nlh->properties_off;
|
||||
|
||||
@@ -781,34 +760,57 @@ int device_monitor_send(
|
||||
return count;
|
||||
}
|
||||
|
||||
static void bpf_stmt(struct sock_filter *ins, unsigned *i,
|
||||
unsigned short code, unsigned data) {
|
||||
static int bpf_stmt_impl(struct sock_filter *ins, size_t *i, size_t n_ins,
|
||||
unsigned short code, unsigned data) {
|
||||
assert(ins);
|
||||
assert(i);
|
||||
|
||||
if (*i >= n_ins)
|
||||
return -E2BIG;
|
||||
|
||||
ins[(*i)++] = (struct sock_filter) {
|
||||
.code = code,
|
||||
.k = data,
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bpf_jmp(struct sock_filter *ins, unsigned *i,
|
||||
unsigned short code, unsigned data,
|
||||
unsigned short jt, unsigned short jf) {
|
||||
#define bpf_stmt(ins, i, code, data) \
|
||||
bpf_stmt_impl((ins), (i), ELEMENTSOF(ins), (code), (data))
|
||||
|
||||
static int bpf_jmp_impl(struct sock_filter *ins, size_t *i, size_t n_ins,
|
||||
unsigned short code, unsigned data,
|
||||
unsigned jt, unsigned jf) {
|
||||
assert(ins);
|
||||
assert(i);
|
||||
|
||||
if (*i >= n_ins)
|
||||
return -E2BIG;
|
||||
|
||||
/* The jump offsets are stored in single bytes (struct sock_filter.jt/.jf are __u8). A larger
|
||||
* offset would be silently truncated and make the filter branch to the wrong instruction, i.e.
|
||||
* drop events that should match. */
|
||||
if (jt > UINT8_MAX || jf > UINT8_MAX)
|
||||
return -E2BIG;
|
||||
|
||||
ins[(*i)++] = (struct sock_filter) {
|
||||
.code = code,
|
||||
.jt = jt,
|
||||
.jf = jf,
|
||||
.k = data,
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define bpf_jmp(ins, i, code, data, jt, jf) \
|
||||
bpf_jmp_impl((ins), (i), ELEMENTSOF(ins), (code), (data), (jt), (jf))
|
||||
|
||||
_public_ int sd_device_monitor_filter_update(sd_device_monitor *m) {
|
||||
struct sock_filter ins[512] = {};
|
||||
struct sock_fprog filter;
|
||||
const char *subsystem, *devtype, *tag;
|
||||
unsigned i = 0;
|
||||
size_t i = 0;
|
||||
int r;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
|
||||
@@ -823,11 +825,11 @@ _public_ int sd_device_monitor_filter_update(sd_device_monitor *m) {
|
||||
}
|
||||
|
||||
/* load magic in A */
|
||||
bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, magic));
|
||||
r = bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, magic));
|
||||
/* jump if magic matches */
|
||||
bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0);
|
||||
RET_GATHER(r, bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0));
|
||||
/* wrong magic, pass packet */
|
||||
bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
|
||||
RET_GATHER(r, bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff));
|
||||
|
||||
if (!set_isempty(m->tag_filter)) {
|
||||
int tag_matches = set_size(m->tag_filter);
|
||||
@@ -839,23 +841,23 @@ _public_ int sd_device_monitor_filter_update(sd_device_monitor *m) {
|
||||
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(monitor_netlink_header, filter_tag_bloom_hi));
|
||||
RET_GATHER(r, bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(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);
|
||||
RET_GATHER(r, 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);
|
||||
RET_GATHER(r, 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(monitor_netlink_header, filter_tag_bloom_lo));
|
||||
RET_GATHER(r, bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(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);
|
||||
RET_GATHER(r, 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);
|
||||
RET_GATHER(r, bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, tag_bloom_lo, 1 + (tag_matches * 6), 0));
|
||||
}
|
||||
|
||||
/* nothing matched, drop packet */
|
||||
bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
|
||||
RET_GATHER(r, bpf_stmt(ins, &i, BPF_RET|BPF_K, 0));
|
||||
}
|
||||
|
||||
/* add all subsystem matches */
|
||||
@@ -864,33 +866,33 @@ _public_ int sd_device_monitor_filter_update(sd_device_monitor *m) {
|
||||
uint32_t hash = string_hash32(subsystem);
|
||||
|
||||
/* load device subsystem value in A */
|
||||
bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_subsystem_hash));
|
||||
RET_GATHER(r, bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_subsystem_hash)));
|
||||
if (!devtype) {
|
||||
/* jump if subsystem does not match */
|
||||
bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
|
||||
RET_GATHER(r, 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);
|
||||
RET_GATHER(r, 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(monitor_netlink_header, filter_devtype_hash));
|
||||
RET_GATHER(r, bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(monitor_netlink_header, filter_devtype_hash)));
|
||||
/* jump if value does not match */
|
||||
hash = string_hash32(devtype);
|
||||
bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, hash, 0, 1);
|
||||
RET_GATHER(r, 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 >= ELEMENTSOF(ins))
|
||||
return -E2BIG;
|
||||
RET_GATHER(r, bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff));
|
||||
}
|
||||
|
||||
/* nothing matched, drop packet */
|
||||
bpf_stmt(ins, &i, BPF_RET|BPF_K, 0);
|
||||
RET_GATHER(r, bpf_stmt(ins, &i, BPF_RET|BPF_K, 0));
|
||||
}
|
||||
|
||||
/* matched, pass packet */
|
||||
bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
|
||||
RET_GATHER(r, bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff));
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* install filter */
|
||||
filter = (struct sock_fprog) {
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
#include "device-private.h"
|
||||
#include "device-util.h"
|
||||
#include "io-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "path-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tests.h"
|
||||
#include "time-util.h"
|
||||
@@ -282,6 +284,21 @@ TEST(sd_device_monitor_filter_add_match_tag) {
|
||||
ASSERT_EQ(sd_event_loop(sd_device_monitor_get_event(monitor_client)), 100);
|
||||
}
|
||||
|
||||
TEST(sd_device_monitor_filter_update_bounds) {
|
||||
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
|
||||
|
||||
ASSERT_OK(device_monitor_new_full(&m, MONITOR_GROUP_NONE, -EBADF));
|
||||
|
||||
/* Each tag emits 6 BPF instructions into a fixed 512-entry array, too many must be refused */
|
||||
for (unsigned u = 0; u < 200; u++) {
|
||||
char t[32];
|
||||
xsprintf(t, "tag%u", u);
|
||||
ASSERT_OK(sd_device_monitor_filter_add_match_tag(m, t));
|
||||
}
|
||||
|
||||
ASSERT_ERROR(sd_device_monitor_filter_update(m), E2BIG);
|
||||
}
|
||||
|
||||
TEST(sd_device_monitor_filter_add_match_sysattr) {
|
||||
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
|
||||
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
|
||||
@@ -408,6 +425,31 @@ TEST(sd_device_monitor_receive) {
|
||||
ASSERT_STREQ(s, syspath);
|
||||
}
|
||||
|
||||
TEST(sd_device_monitor_receive_bad_properties_off) {
|
||||
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL;
|
||||
union sockaddr_union sa;
|
||||
|
||||
prepare_monitor(&monitor_server, &monitor_client, &sa);
|
||||
|
||||
monitor_netlink_header nlh = {
|
||||
.prefix = "libudev",
|
||||
.magic = htobe32(UDEV_MONITOR_MAGIC),
|
||||
.header_size = sizeof nlh,
|
||||
.properties_off = UINT32_MAX - 10,
|
||||
};
|
||||
struct iovec iov = IOVEC_MAKE(&nlh, sizeof nlh);
|
||||
struct msghdr smsg = {
|
||||
.msg_iov = &iov,
|
||||
.msg_iovlen = 1,
|
||||
.msg_name = &sa,
|
||||
.msg_namelen = sizeof(struct sockaddr_nl),
|
||||
};
|
||||
ASSERT_OK_ERRNO(sendmsg(sd_device_monitor_get_fd(monitor_server), &smsg, 0));
|
||||
|
||||
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
||||
ASSERT_ERROR(sd_device_monitor_receive(monitor_client, &dev), EAGAIN);
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
if (getuid() != 0)
|
||||
return log_tests_skipped("not root");
|
||||
|
||||
@@ -548,7 +548,7 @@ int sd_netlink_call_async(
|
||||
uint64_t usec,
|
||||
const char *description) {
|
||||
|
||||
_cleanup_free_ sd_netlink_slot *slot = NULL;
|
||||
_cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL;
|
||||
int r, k;
|
||||
|
||||
assert_return(nl, -EINVAL);
|
||||
@@ -859,34 +859,39 @@ int netlink_add_match_internal(
|
||||
void *userdata,
|
||||
const char *description) {
|
||||
|
||||
_cleanup_free_ sd_netlink_slot *slot = NULL;
|
||||
_cleanup_(sd_netlink_slot_unrefp) sd_netlink_slot *slot = NULL;
|
||||
int r;
|
||||
|
||||
assert(groups);
|
||||
assert(n_groups > 0);
|
||||
|
||||
for (size_t i = 0; i < n_groups; i++) {
|
||||
r = socket_broadcast_group_ref(nl, groups[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = netlink_slot_allocate(nl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback),
|
||||
userdata, description, &slot);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
slot->match_callback.groups = newdup(uint32_t, groups, n_groups);
|
||||
if (!slot->match_callback.groups)
|
||||
return -ENOMEM;
|
||||
|
||||
slot->match_callback.n_groups = n_groups;
|
||||
slot->match_callback.callback = callback;
|
||||
slot->match_callback.type = type;
|
||||
slot->match_callback.cmd = cmd;
|
||||
|
||||
slot->match_callback.groups = newdup(uint32_t, groups, n_groups);
|
||||
if (!slot->match_callback.groups)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Link the callback before taking the broadcast group references, so that on a partial failure the
|
||||
* cleanup path (sd_netlink_slot_unref() -> netlink_slot_disconnect()) can unlink it again and drop
|
||||
* the references we already took. n_groups is bumped as each reference is acquired, so the disconnect
|
||||
* only releases what we actually got. */
|
||||
LIST_PREPEND(match_callbacks, nl->match_callbacks, &slot->match_callback);
|
||||
|
||||
for (size_t i = 0; i < n_groups; i++) {
|
||||
r = socket_broadcast_group_ref(nl, groups[i]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
slot->match_callback.n_groups = i + 1;
|
||||
}
|
||||
|
||||
/* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
|
||||
slot->destroy_callback = destroy_callback;
|
||||
|
||||
|
||||
@@ -98,11 +98,12 @@ int link_request_sr_iov_vfs(Link *link) {
|
||||
if (!sr_iov_has_config(sr_iov, attr))
|
||||
continue;
|
||||
|
||||
/* The request takes a reference on the configuration. */
|
||||
r = link_queue_request_safe(
|
||||
link,
|
||||
_REQUEST_TYPE_SRIOV_BASE + attr,
|
||||
sr_iov,
|
||||
NULL,
|
||||
sr_iov_unref,
|
||||
sr_iov_hash_func,
|
||||
sr_iov_compare_func,
|
||||
sr_iov_process_request,
|
||||
@@ -113,6 +114,8 @@ int link_request_sr_iov_vfs(Link *link) {
|
||||
return log_link_warning_errno(link, r,
|
||||
"Failed to request to set up %s for SR-IOV virtual function %"PRIu32": %m",
|
||||
sr_iov_attribute_to_string(attr), sr_iov->vf);
|
||||
if (r > 0)
|
||||
sr_iov_ref(sr_iov);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ static int link_put_dns(Link *link, OrderedSet **s) {
|
||||
|
||||
r = sd_dhcp_lease_get_dnr(link->dhcp_lease, &resolvers);
|
||||
if (r >= 0) {
|
||||
struct in_addr_full **dot_servers;
|
||||
struct in_addr_full **dot_servers = NULL;
|
||||
size_t n = 0;
|
||||
CLEANUP_ARRAY(dot_servers, n, in_addr_full_free_array);
|
||||
|
||||
@@ -163,7 +163,7 @@ static int link_put_dns(Link *link, OrderedSet **s) {
|
||||
|
||||
r = sd_dhcp6_lease_get_dnr(link->dhcp6_lease, &resolvers);
|
||||
if (r >= 0) {
|
||||
struct in_addr_full **dot_servers;
|
||||
struct in_addr_full **dot_servers = NULL;
|
||||
size_t n = 0;
|
||||
CLEANUP_ARRAY(dot_servers, n, in_addr_full_free_array);
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ static int mdns_maintenance_query(sd_event_source *s, uint64_t usec, void *userd
|
||||
return log_error_errno(r, "Failed to create mDNS query for maintenance: %m");
|
||||
|
||||
q->complete = mdns_maintenance_query_complete;
|
||||
q->varlink_request = sd_varlink_ref(service->service_browser->link);
|
||||
q->service_browser_request = dns_service_browser_ref(service->service_browser);
|
||||
q->dnsservice_request = dnssd_discovered_service_ref(service);
|
||||
|
||||
/* Schedule the next maintenance query based on the TTL */
|
||||
|
||||
+31
-12
@@ -16,24 +16,42 @@
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
|
||||
static SRIOV* sr_iov_detach_impl(SRIOV *sr_iov) {
|
||||
assert(sr_iov);
|
||||
|
||||
if (!sr_iov->sr_iov_by_section)
|
||||
return NULL;
|
||||
|
||||
assert(sr_iov->section);
|
||||
ordered_hashmap_remove(sr_iov->sr_iov_by_section, sr_iov->section);
|
||||
sr_iov->sr_iov_by_section = NULL;
|
||||
|
||||
return sr_iov;
|
||||
}
|
||||
|
||||
static SRIOV* sr_iov_free(SRIOV *sr_iov) {
|
||||
if (!sr_iov)
|
||||
return NULL;
|
||||
|
||||
if (sr_iov->sr_iov_by_section && sr_iov->section)
|
||||
ordered_hashmap_remove(sr_iov->sr_iov_by_section, sr_iov->section);
|
||||
sr_iov_detach_impl(sr_iov);
|
||||
|
||||
config_section_free(sr_iov->section);
|
||||
|
||||
return mfree(sr_iov);
|
||||
}
|
||||
|
||||
DEFINE_SECTION_CLEANUP_FUNCTIONS(SRIOV, sr_iov_free);
|
||||
DEFINE_TRIVIAL_REF_UNREF_FUNC(SRIOV, sr_iov, sr_iov_free);
|
||||
DEFINE_SECTION_CLEANUP_FUNCTIONS(SRIOV, sr_iov_unref);
|
||||
|
||||
static void sr_iov_detach(SRIOV *sr_iov) {
|
||||
assert(sr_iov);
|
||||
sr_iov_unref(sr_iov_detach_impl(sr_iov));
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
sr_iov_hash_ops_by_section,
|
||||
ConfigSection, config_section_hash_func, config_section_compare_func,
|
||||
SRIOV, sr_iov_free);
|
||||
SRIOV, sr_iov_detach);
|
||||
|
||||
static int sr_iov_new(SRIOV **ret) {
|
||||
SRIOV *sr_iov;
|
||||
@@ -45,6 +63,7 @@ static int sr_iov_new(SRIOV **ret) {
|
||||
return -ENOMEM;
|
||||
|
||||
*sr_iov = (SRIOV) {
|
||||
.n_ref = 1,
|
||||
.vf = UINT32_MAX,
|
||||
.vlan_proto = ETH_P_8021Q,
|
||||
.vf_spoof_check_setting = -1,
|
||||
@@ -60,7 +79,7 @@ static int sr_iov_new(SRIOV **ret) {
|
||||
|
||||
static int sr_iov_new_static(OrderedHashmap **sr_iov_by_section, const char *filename, unsigned section_line, SRIOV **ret) {
|
||||
_cleanup_(config_section_freep) ConfigSection *n = NULL;
|
||||
_cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL;
|
||||
_cleanup_(sr_iov_unrefp) SRIOV *sr_iov = NULL;
|
||||
SRIOV *existing = NULL;
|
||||
int r;
|
||||
|
||||
@@ -368,7 +387,7 @@ int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_sec
|
||||
SRIOV *dup;
|
||||
|
||||
if (sr_iov_section_verify(num_vfs, sr_iov) < 0) {
|
||||
sr_iov_free(sr_iov);
|
||||
sr_iov_detach(sr_iov);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -378,7 +397,7 @@ int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_sec
|
||||
"dropping the [SR-IOV] section specified at line %u.",
|
||||
dup->section->filename, sr_iov->section->line,
|
||||
dup->section->line, dup->section->line);
|
||||
sr_iov_free(dup);
|
||||
sr_iov_detach(dup);
|
||||
}
|
||||
|
||||
r = set_ensure_put(&set, &sr_iov_hash_ops, sr_iov);
|
||||
@@ -402,7 +421,7 @@ int config_parse_sr_iov_uint32(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
|
||||
_cleanup_(sr_iov_unref_or_set_invalidp) SRIOV *sr_iov = NULL;
|
||||
OrderedHashmap **sr_iov_by_section = ASSERT_PTR(data);
|
||||
uint32_t k;
|
||||
int r;
|
||||
@@ -469,7 +488,7 @@ int config_parse_sr_iov_vlan_proto(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
|
||||
_cleanup_(sr_iov_unref_or_set_invalidp) SRIOV *sr_iov = NULL;
|
||||
OrderedHashmap **sr_iov_by_section = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
@@ -507,7 +526,7 @@ int config_parse_sr_iov_link_state(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
|
||||
_cleanup_(sr_iov_unref_or_set_invalidp) SRIOV *sr_iov = NULL;
|
||||
OrderedHashmap **sr_iov_by_section = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
@@ -558,7 +577,7 @@ int config_parse_sr_iov_boolean(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
|
||||
_cleanup_(sr_iov_unref_or_set_invalidp) SRIOV *sr_iov = NULL;
|
||||
OrderedHashmap **sr_iov_by_section = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
@@ -615,7 +634,7 @@ int config_parse_sr_iov_mac(
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
_cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
|
||||
_cleanup_(sr_iov_unref_or_set_invalidp) SRIOV *sr_iov = NULL;
|
||||
OrderedHashmap **sr_iov_by_section = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ typedef enum SRIOVLinkState {
|
||||
} SRIOVLinkState;
|
||||
|
||||
typedef struct SRIOV {
|
||||
unsigned n_ref;
|
||||
|
||||
ConfigSection *section;
|
||||
OrderedHashmap *sr_iov_by_section;
|
||||
|
||||
@@ -43,6 +45,8 @@ typedef struct SRIOV {
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP_TO_STRING(sr_iov_attribute, SRIOVAttribute);
|
||||
|
||||
DECLARE_TRIVIAL_REF_UNREF_FUNC(SRIOV, sr_iov);
|
||||
|
||||
void sr_iov_hash_func(const SRIOV *sr_iov, struct siphash *state);
|
||||
int sr_iov_compare_func(const SRIOV *s1, const SRIOV *s2);
|
||||
bool sr_iov_has_config(SRIOV *sr_iov, SRIOVAttribute attr);
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "random-util.h"
|
||||
#include "set.h"
|
||||
#include "siphash24.h"
|
||||
#include "strv.h"
|
||||
#include "tests.h"
|
||||
|
||||
@@ -51,6 +53,46 @@ TEST(set_free_with_hash_ops) {
|
||||
assert_se(items[3].seen == 0);
|
||||
}
|
||||
|
||||
static unsigned set_ensure_consume_freed = 0;
|
||||
|
||||
typedef struct ValItem {
|
||||
int key;
|
||||
} ValItem;
|
||||
|
||||
static void val_item_free(ValItem *i) {
|
||||
set_ensure_consume_freed++;
|
||||
free(i);
|
||||
}
|
||||
|
||||
static void val_item_hash_func(const ValItem *i, struct siphash *state) {
|
||||
siphash24_compress_typesafe(i->key, state);
|
||||
}
|
||||
|
||||
static int val_item_compare_func(const ValItem *a, const ValItem *b) {
|
||||
return CMP(a->key, b->key);
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
|
||||
val_item_hash_ops, ValItem, val_item_hash_func, val_item_compare_func,
|
||||
ValItem, val_item_free);
|
||||
|
||||
TEST(set_ensure_consume_value_destructor) {
|
||||
_cleanup_set_free_ Set *s = NULL;
|
||||
ValItem *a, *b;
|
||||
|
||||
ASSERT_NOT_NULL(a = new0(ValItem, 1));
|
||||
a->key = 1;
|
||||
ASSERT_OK_POSITIVE(set_ensure_consume(&s, &val_item_hash_ops, a));
|
||||
|
||||
ASSERT_NOT_NULL(b = new0(ValItem, 1));
|
||||
b->key = 1; /* same key, different pointer -> duplicate */
|
||||
|
||||
set_ensure_consume_freed = 0;
|
||||
/* The rejected duplicate must be released via the value destructor, not plain free(). */
|
||||
ASSERT_OK_ZERO(set_ensure_consume(&s, &val_item_hash_ops, b));
|
||||
ASSERT_EQ(set_ensure_consume_freed, 1u);
|
||||
}
|
||||
|
||||
TEST(set_put) {
|
||||
_cleanup_set_free_ Set *m = NULL;
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "device-private.h"
|
||||
#include "log.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tests.h"
|
||||
#include "udev-event.h"
|
||||
#include "udev-format.h"
|
||||
|
||||
static void test_udev_resolve_subsys_kernel_one(const char *str, bool read_value, int retval, const char *expected) {
|
||||
@@ -36,6 +40,26 @@ TEST(udev_resolve_subsys_kernel) {
|
||||
test_udev_resolve_subsys_kernel_one("[net/lo]/address", true, 0, "00:00:00:00:00:00");
|
||||
}
|
||||
|
||||
TEST(udev_event_apply_format_links) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
|
||||
_cleanup_(udev_event_unrefp) UdevEvent *event = NULL;
|
||||
char dest[64];
|
||||
bool truncated = false;
|
||||
|
||||
ASSERT_OK(sd_device_new_from_syspath(&dev, "/sys/class/net/lo"));
|
||||
|
||||
for (unsigned u = 0; u < 32; u++) {
|
||||
_cleanup_free_ char *l = NULL;
|
||||
ASSERT_OK(asprintf(&l, "/dev/link-that-is-long-%u", u));
|
||||
ASSERT_OK(device_add_devlink(dev, l));
|
||||
}
|
||||
|
||||
ASSERT_NOT_NULL((event = udev_event_new(dev, NULL, EVENT_TEST_SPAWN)));
|
||||
|
||||
udev_event_apply_format(event, "$links", dest, sizeof dest, false, &truncated);
|
||||
ASSERT_TRUE(truncated);
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
if (path_is_mount_point("/sys") <= 0)
|
||||
return log_tests_skipped("/sys is not mounted");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "tests.h"
|
||||
#include "udev-manager.h"
|
||||
|
||||
@@ -16,4 +17,22 @@ TEST(devpath_conflict) {
|
||||
"/devices/pci0000:00/0000:00:1c.4/0000:3c:00.0/nvme/nvme0/nvme0n1/nvme0n1p1"));
|
||||
}
|
||||
|
||||
TEST(worker_free_detaches_event) {
|
||||
/* worker_free() must clear the back-pointer of the event it is processing, otherwise the event is
|
||||
* left referencing the freed worker. This happens e.g. on manager_free(), which frees the workers
|
||||
* before the events still referencing them. */
|
||||
|
||||
Worker *worker = ASSERT_PTR(new0(Worker, 1));
|
||||
worker->pidref = PIDREF_NULL;
|
||||
|
||||
_cleanup_free_ Event *event = ASSERT_PTR(new0(Event, 1));
|
||||
|
||||
worker->event = event;
|
||||
event->worker = worker;
|
||||
|
||||
worker_free(worker);
|
||||
|
||||
ASSERT_NULL(event->worker);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
@@ -334,9 +334,9 @@ static ssize_t udev_event_subst_format(
|
||||
case FORMAT_SUBST_LINKS:
|
||||
FOREACH_DEVICE_DEVLINK(dev, link) {
|
||||
if (s == dest)
|
||||
strpcpy_full(&s, l, link + STRLEN("/dev/"), &truncated);
|
||||
l = strpcpy_full(&s, l, link + STRLEN("/dev/"), &truncated);
|
||||
else
|
||||
strpcpyl_full(&s, l, &truncated, " ", link + STRLEN("/dev/"), NULL);
|
||||
l = strpcpyl_full(&s, l, &truncated, " ", link + STRLEN("/dev/"), NULL);
|
||||
if (truncated)
|
||||
break;
|
||||
}
|
||||
|
||||
+6
-58
@@ -50,63 +50,6 @@
|
||||
#define EVENT_REQUEUE_INTERVAL_USEC (200 * USEC_PER_MSEC)
|
||||
#define EVENT_REQUEUE_TIMEOUT_USEC (3 * USEC_PER_MINUTE)
|
||||
|
||||
typedef enum EventState {
|
||||
EVENT_UNDEF,
|
||||
EVENT_QUEUED,
|
||||
EVENT_RUNNING,
|
||||
EVENT_LOCKED,
|
||||
EVENT_PROCESSED,
|
||||
} EventState;
|
||||
|
||||
typedef struct Event {
|
||||
/* All events that have not been processed (state != EVENT_PROCESSED) are referenced by the Manager.
|
||||
* Additionally, an event may be referenced by events blocked by this event. See event_find_blocker(). */
|
||||
unsigned n_ref;
|
||||
|
||||
Manager *manager;
|
||||
Worker *worker;
|
||||
EventState state;
|
||||
|
||||
sd_device *dev;
|
||||
|
||||
sd_device_action_t action;
|
||||
uint64_t seqnum;
|
||||
const char *id;
|
||||
const char *devpath;
|
||||
const char *devpath_old;
|
||||
const char *devnode;
|
||||
|
||||
/* Used when the device is locked by another program. */
|
||||
usec_t requeue_next_usec;
|
||||
usec_t requeue_timeout_usec;
|
||||
unsigned locked_event_prioq_index;
|
||||
char *whole_disk;
|
||||
LIST_FIELDS(Event, same_disk);
|
||||
|
||||
/* The last blocker for this event. This event must not be processed before the blocker is processed. */
|
||||
Event *blocker;
|
||||
|
||||
LIST_FIELDS(Event, event);
|
||||
} Event;
|
||||
|
||||
typedef enum WorkerState {
|
||||
WORKER_UNDEF,
|
||||
WORKER_RUNNING,
|
||||
WORKER_IDLE,
|
||||
WORKER_KILLED,
|
||||
} WorkerState;
|
||||
|
||||
typedef struct Worker {
|
||||
Manager *manager;
|
||||
PidRef pidref;
|
||||
sd_event_source *child_event_source;
|
||||
sd_event_source *timeout_warning_event_source;
|
||||
sd_event_source *timeout_kill_event_source;
|
||||
union sockaddr_union address;
|
||||
WorkerState state;
|
||||
Event *event;
|
||||
} Worker;
|
||||
|
||||
static void event_unset_whole_disk(Event *event) {
|
||||
Manager *manager = ASSERT_PTR(ASSERT_PTR(event)->manager);
|
||||
|
||||
@@ -171,13 +114,18 @@ static Event* event_enter_processed(Event *event) {
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Event*, event_enter_processed);
|
||||
|
||||
static Worker* worker_free(Worker *worker) {
|
||||
Worker* worker_free(Worker *worker) {
|
||||
if (!worker)
|
||||
return NULL;
|
||||
|
||||
if (worker->manager)
|
||||
hashmap_remove(worker->manager->workers, &worker->pidref);
|
||||
|
||||
/* If an event is still attached, clear its back-pointer so it doesn't dangle (mirrors event_free(),
|
||||
* which clears the worker's pointer). */
|
||||
if (worker->event)
|
||||
worker->event->worker = NULL;
|
||||
|
||||
sd_event_source_disable_unref(worker->child_event_source);
|
||||
sd_event_source_unref(worker->timeout_warning_event_source);
|
||||
sd_event_source_unref(worker->timeout_kill_event_source);
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-device.h"
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "list.h"
|
||||
#include "pidref.h"
|
||||
#include "socket-util.h"
|
||||
#include "udev-config.h"
|
||||
#include "udev-forward.h"
|
||||
|
||||
@@ -76,10 +79,69 @@ typedef struct Manager {
|
||||
bool exit;
|
||||
} Manager;
|
||||
|
||||
typedef enum EventState {
|
||||
EVENT_UNDEF,
|
||||
EVENT_QUEUED,
|
||||
EVENT_RUNNING,
|
||||
EVENT_LOCKED,
|
||||
EVENT_PROCESSED,
|
||||
} EventState;
|
||||
|
||||
typedef struct Event {
|
||||
/* All events that have not been processed (state != EVENT_PROCESSED) are referenced by the Manager.
|
||||
* Additionally, an event may be referenced by events blocked by this event. See event_find_blocker(). */
|
||||
unsigned n_ref;
|
||||
|
||||
Manager *manager;
|
||||
Worker *worker;
|
||||
EventState state;
|
||||
|
||||
sd_device *dev;
|
||||
|
||||
sd_device_action_t action;
|
||||
uint64_t seqnum;
|
||||
const char *id;
|
||||
const char *devpath;
|
||||
const char *devpath_old;
|
||||
const char *devnode;
|
||||
|
||||
/* Used when the device is locked by another program. */
|
||||
usec_t requeue_next_usec;
|
||||
usec_t requeue_timeout_usec;
|
||||
unsigned locked_event_prioq_index;
|
||||
char *whole_disk;
|
||||
LIST_FIELDS(Event, same_disk);
|
||||
|
||||
/* The last blocker for this event. This event must not be processed before the blocker is processed. */
|
||||
Event *blocker;
|
||||
|
||||
LIST_FIELDS(Event, event);
|
||||
} Event;
|
||||
|
||||
typedef enum WorkerState {
|
||||
WORKER_UNDEF,
|
||||
WORKER_RUNNING,
|
||||
WORKER_IDLE,
|
||||
WORKER_KILLED,
|
||||
} WorkerState;
|
||||
|
||||
typedef struct Worker {
|
||||
Manager *manager;
|
||||
PidRef pidref;
|
||||
sd_event_source *child_event_source;
|
||||
sd_event_source *timeout_warning_event_source;
|
||||
sd_event_source *timeout_kill_event_source;
|
||||
union sockaddr_union address;
|
||||
WorkerState state;
|
||||
Event *event;
|
||||
} Worker;
|
||||
|
||||
Manager* manager_new(void);
|
||||
Manager* manager_free(Manager *manager);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
|
||||
|
||||
Worker* worker_free(Worker *worker);
|
||||
|
||||
int manager_main(Manager *manager);
|
||||
void manager_reload(Manager *manager, bool force);
|
||||
void manager_revert(Manager *manager);
|
||||
|
||||
Reference in New Issue
Block a user