| File: | build-scan/../src/libudev/libudev-monitor.c |
| Warning: | line 149, column 45 Assigned value is garbage or undefined |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | |||
| 2 | ||||
| 3 | #include <errno(*__errno_location ()).h> | |||
| 4 | #include <linux1/filter.h> | |||
| 5 | #include <linux1/netlink.h> | |||
| 6 | #include <poll.h> | |||
| 7 | #include <stddef.h> | |||
| 8 | #include <stdio.h> | |||
| 9 | #include <stdlib.h> | |||
| 10 | #include <string.h> | |||
| 11 | #include <sys/socket.h> | |||
| 12 | #include <unistd.h> | |||
| 13 | ||||
| 14 | #include "libudev.h" | |||
| 15 | ||||
| 16 | #include "alloc-util.h" | |||
| 17 | #include "fd-util.h" | |||
| 18 | #include "fileio.h" | |||
| 19 | #include "format-util.h" | |||
| 20 | #include "libudev-private.h" | |||
| 21 | #include "missing.h" | |||
| 22 | #include "mount-util.h" | |||
| 23 | #include "socket-util.h" | |||
| 24 | #include "string-util.h" | |||
| 25 | ||||
| 26 | /** | |||
| 27 | * SECTION:libudev-monitor | |||
| 28 | * @short_description: device event source | |||
| 29 | * | |||
| 30 | * Connects to a device event source. | |||
| 31 | */ | |||
| 32 | ||||
| 33 | /** | |||
| 34 | * udev_monitor: | |||
| 35 | * | |||
| 36 | * Opaque object handling an event source. | |||
| 37 | */ | |||
| 38 | struct udev_monitor { | |||
| 39 | struct udev *udev; | |||
| 40 | int refcount; | |||
| 41 | int sock; | |||
| 42 | union sockaddr_union snl; | |||
| 43 | union sockaddr_union snl_trusted_sender; | |||
| 44 | union sockaddr_union snl_destination; | |||
| 45 | socklen_t addrlen; | |||
| 46 | struct udev_list filter_subsystem_list; | |||
| 47 | struct udev_list filter_tag_list; | |||
| 48 | bool_Bool bound; | |||
| 49 | }; | |||
| 50 | ||||
| 51 | enum udev_monitor_netlink_group { | |||
| 52 | UDEV_MONITOR_NONE, | |||
| 53 | UDEV_MONITOR_KERNEL, | |||
| 54 | UDEV_MONITOR_UDEV, | |||
| 55 | }; | |||
| 56 | ||||
| 57 | #define UDEV_MONITOR_MAGIC0xfeedcafe 0xfeedcafe | |||
| 58 | struct udev_monitor_netlink_header { | |||
| 59 | /* "libudev" prefix to distinguish libudev and kernel messages */ | |||
| 60 | char prefix[8]; | |||
| 61 | /* | |||
| 62 | * magic to protect against daemon <-> library message format mismatch | |||
| 63 | * used in the kernel from socket filter rules; needs to be stored in network order | |||
| 64 | */ | |||
| 65 | unsigned int magic; | |||
| 66 | /* total length of header structure known to the sender */ | |||
| 67 | unsigned int header_size; | |||
| 68 | /* properties string buffer */ | |||
| 69 | unsigned int properties_off; | |||
| 70 | unsigned int properties_len; | |||
| 71 | /* | |||
| 72 | * hashes of primary device properties strings, to let libudev subscribers | |||
| 73 | * use in-kernel socket filters; values need to be stored in network order | |||
| 74 | */ | |||
| 75 | unsigned int filter_subsystem_hash; | |||
| 76 | unsigned int filter_devtype_hash; | |||
| 77 | unsigned int filter_tag_bloom_hi; | |||
| 78 | unsigned int filter_tag_bloom_lo; | |||
| 79 | }; | |||
| 80 | ||||
| 81 | static struct udev_monitor *udev_monitor_new(struct udev *udev) { | |||
| 82 | struct udev_monitor *udev_monitor; | |||
| 83 | ||||
| 84 | udev_monitor = new0(struct udev_monitor, 1)((struct udev_monitor*) calloc((1), sizeof(struct udev_monitor ))); | |||
| 85 | if (udev_monitor == NULL((void*)0)) { | |||
| 86 | errno(*__errno_location ()) = ENOMEM12; | |||
| 87 | return NULL((void*)0); | |||
| 88 | } | |||
| 89 | udev_monitor->refcount = 1; | |||
| 90 | udev_monitor->udev = udev; | |||
| 91 | udev_list_init(udev, &udev_monitor->filter_subsystem_list, false0); | |||
| 92 | udev_list_init(udev, &udev_monitor->filter_tag_list, true1); | |||
| 93 | return udev_monitor; | |||
| 94 | } | |||
| 95 | ||||
| 96 | /* we consider udev running when /dev is on devtmpfs */ | |||
| 97 | static bool_Bool udev_has_devtmpfs(struct udev *udev) { | |||
| 98 | ||||
| 99 | _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0); | |||
| 100 | char line[LINE_MAX2048], *e; | |||
| 101 | int mount_id, r; | |||
| 102 | ||||
| 103 | r = path_get_mnt_id("/dev", &mount_id); | |||
| 104 | if (r < 0) { | |||
| 105 | if (r != -EOPNOTSUPP95) | |||
| 106 | log_debug_errno(r, "name_to_handle_at on /dev: %m")({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 106, __func__, "name_to_handle_at on /dev: %m" ) : -abs(_e); }); | |||
| 107 | ||||
| 108 | return false0; | |||
| 109 | } | |||
| 110 | ||||
| 111 | f = fopen("/proc/self/mountinfo", "re"); | |||
| 112 | if (!f) | |||
| 113 | return false0; | |||
| 114 | ||||
| 115 | FOREACH_LINE(line, f, return false)for (;;) if (!fgets(line, sizeof(line), f)) { if (ferror(f)) { return 0; } break; } else { | |||
| 116 | int mid; | |||
| 117 | ||||
| 118 | if (sscanf(line, "%i", &mid) != 1) | |||
| 119 | continue; | |||
| 120 | ||||
| 121 | if (mid != mount_id) | |||
| 122 | continue; | |||
| 123 | ||||
| 124 | e = strstr(line, " - "); | |||
| 125 | if (!e) | |||
| 126 | continue; | |||
| 127 | ||||
| 128 | /* accept any name that starts with the currently expected type */ | |||
| 129 | if (startswith(e + 3, "devtmpfs")) | |||
| 130 | return true1; | |||
| 131 | } | |||
| 132 | ||||
| 133 | return false0; | |||
| 134 | } | |||
| 135 | ||||
| 136 | static void monitor_set_nl_address(struct udev_monitor *udev_monitor) { | |||
| 137 | union sockaddr_union snl; | |||
| 138 | socklen_t addrlen; | |||
| 139 | int r; | |||
| 140 | ||||
| 141 | assert(udev_monitor)do { if ((__builtin_expect(!!(!(udev_monitor)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("udev_monitor"), "../src/libudev/libudev-monitor.c" , 141, __PRETTY_FUNCTION__); } while (0); | |||
| 142 | ||||
| 143 | /* get the address the kernel has assigned us | |||
| 144 | * it is usually, but not necessarily the pid | |||
| 145 | */ | |||
| 146 | addrlen = sizeof(struct sockaddr_nl); | |||
| 147 | r = getsockname(udev_monitor->sock, &snl.sa, &addrlen); | |||
| 148 | if (r >= 0) | |||
| 149 | udev_monitor->snl.nl.nl_pid = snl.nl.nl_pid; | |||
| ||||
| 150 | } | |||
| 151 | ||||
| 152 | struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd) { | |||
| 153 | struct udev_monitor *udev_monitor; | |||
| 154 | unsigned int group; | |||
| 155 | ||||
| 156 | if (udev == NULL((void*)0)) { | |||
| 157 | errno(*__errno_location ()) = EINVAL22; | |||
| 158 | return NULL((void*)0); | |||
| 159 | } | |||
| 160 | ||||
| 161 | if (name == NULL((void*)0)) | |||
| 162 | group = UDEV_MONITOR_NONE; | |||
| 163 | else if (streq(name, "udev")(strcmp((name),("udev")) == 0)) { | |||
| 164 | /* | |||
| 165 | * We do not support subscribing to uevents if no instance of | |||
| 166 | * udev is running. Uevents would otherwise broadcast the | |||
| 167 | * processing data of the host into containers, which is not | |||
| 168 | * desired. | |||
| 169 | * | |||
| 170 | * Containers will currently not get any udev uevents, until | |||
| 171 | * a supporting infrastructure is available. | |||
| 172 | * | |||
| 173 | * We do not set a netlink multicast group here, so the socket | |||
| 174 | * will not receive any messages. | |||
| 175 | */ | |||
| 176 | if (access("/run/udev/control", F_OK0) < 0 && !udev_has_devtmpfs(udev)) { | |||
| 177 | log_debug("the udev service seems not to be active, disable the monitor")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 177, __func__, "the udev service seems not to be active, disable the monitor" ) : -abs(_e); }); | |||
| 178 | group = UDEV_MONITOR_NONE; | |||
| 179 | } else | |||
| 180 | group = UDEV_MONITOR_UDEV; | |||
| 181 | } else if (streq(name, "kernel")(strcmp((name),("kernel")) == 0)) | |||
| 182 | group = UDEV_MONITOR_KERNEL; | |||
| 183 | else { | |||
| 184 | errno(*__errno_location ()) = EINVAL22; | |||
| 185 | return NULL((void*)0); | |||
| 186 | } | |||
| 187 | ||||
| 188 | udev_monitor = udev_monitor_new(udev); | |||
| 189 | if (udev_monitor == NULL((void*)0)) | |||
| 190 | return NULL((void*)0); | |||
| 191 | ||||
| 192 | if (fd < 0) { | |||
| 193 | udev_monitor->sock = socket(PF_NETLINK16, SOCK_RAWSOCK_RAW|SOCK_CLOEXECSOCK_CLOEXEC|SOCK_NONBLOCKSOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT15); | |||
| 194 | if (udev_monitor->sock < 0) { | |||
| 195 | log_debug_errno(errno, "error getting socket: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/libudev/libudev-monitor.c", 195, __func__, "error getting socket: %m") : -abs(_e); }); | |||
| 196 | return mfree(udev_monitor); | |||
| 197 | } | |||
| 198 | } else { | |||
| 199 | udev_monitor->bound = true1; | |||
| 200 | udev_monitor->sock = fd; | |||
| 201 | monitor_set_nl_address(udev_monitor); | |||
| 202 | } | |||
| 203 | ||||
| 204 | udev_monitor->snl.nl.nl_family = AF_NETLINK16; | |||
| 205 | udev_monitor->snl.nl.nl_groups = group; | |||
| 206 | ||||
| 207 | /* default destination for sending */ | |||
| 208 | udev_monitor->snl_destination.nl.nl_family = AF_NETLINK16; | |||
| 209 | udev_monitor->snl_destination.nl.nl_groups = UDEV_MONITOR_UDEV; | |||
| 210 | ||||
| 211 | return udev_monitor; | |||
| 212 | } | |||
| 213 | ||||
| 214 | /** | |||
| 215 | * udev_monitor_new_from_netlink: | |||
| 216 | * @udev: udev library context | |||
| 217 | * @name: name of event source | |||
| 218 | * | |||
| 219 | * Create new udev monitor and connect to a specified event | |||
| 220 | * source. Valid sources identifiers are "udev" and "kernel". | |||
| 221 | * | |||
| 222 | * Applications should usually not connect directly to the | |||
| 223 | * "kernel" events, because the devices might not be useable | |||
| 224 | * at that time, before udev has configured them, and created | |||
| 225 | * device nodes. Accessing devices at the same time as udev, | |||
| 226 | * might result in unpredictable behavior. The "udev" events | |||
| 227 | * are sent out after udev has finished its event processing, | |||
| 228 | * all rules have been processed, and needed device nodes are | |||
| 229 | * created. | |||
| 230 | * | |||
| 231 | * The initial refcount is 1, and needs to be decremented to | |||
| 232 | * release the resources of the udev monitor. | |||
| 233 | * | |||
| 234 | * Returns: a new udev monitor, or #NULL, in case of an error | |||
| 235 | **/ | |||
| 236 | _public___attribute__ ((visibility("default"))) struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) { | |||
| 237 | return udev_monitor_new_from_netlink_fd(udev, name, -1); | |||
| 238 | } | |||
| 239 | ||||
| 240 | static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i, | |||
| 241 | unsigned short code, unsigned int data) | |||
| 242 | { | |||
| 243 | struct sock_filter *ins = &inss[*i]; | |||
| 244 | ||||
| 245 | ins->code = code; | |||
| 246 | ins->k = data; | |||
| 247 | (*i)++; | |||
| 248 | } | |||
| 249 | ||||
| 250 | static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i, | |||
| 251 | unsigned short code, unsigned int data, | |||
| 252 | unsigned short jt, unsigned short jf) | |||
| 253 | { | |||
| 254 | struct sock_filter *ins = &inss[*i]; | |||
| 255 | ||||
| 256 | ins->code = code; | |||
| 257 | ins->jt = jt; | |||
| 258 | ins->jf = jf; | |||
| 259 | ins->k = data; | |||
| 260 | (*i)++; | |||
| 261 | } | |||
| 262 | ||||
| 263 | /** | |||
| 264 | * udev_monitor_filter_update: | |||
| 265 | * @udev_monitor: monitor | |||
| 266 | * | |||
| 267 | * Update the installed socket filter. This is only needed, | |||
| 268 | * if the filter was removed or changed. | |||
| 269 | * | |||
| 270 | * Returns: 0 on success, otherwise a negative error value. | |||
| 271 | */ | |||
| 272 | _public___attribute__ ((visibility("default"))) int udev_monitor_filter_update(struct udev_monitor *udev_monitor) | |||
| 273 | { | |||
| 274 | struct sock_filter ins[512]; | |||
| 275 | struct sock_fprog filter; | |||
| 276 | unsigned int i; | |||
| 277 | struct udev_list_entry *list_entry; | |||
| 278 | int err; | |||
| 279 | ||||
| 280 | if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL((void*)0) && | |||
| 281 | udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL((void*)0)) | |||
| 282 | return 0; | |||
| 283 | ||||
| 284 | memzero(ins, sizeof(ins))({ size_t _l_ = (sizeof(ins)); void *_x_ = (ins); _l_ == 0 ? _x_ : memset(_x_, 0, _l_); }); | |||
| 285 | i = 0; | |||
| 286 | ||||
| 287 | /* load magic in A */ | |||
| 288 | bpf_stmt(ins, &i, BPF_LD0x00|BPF_W0x00|BPF_ABS0x20, offsetof(struct udev_monitor_netlink_header, magic)__builtin_offsetof(struct udev_monitor_netlink_header, magic)); | |||
| 289 | /* jump if magic matches */ | |||
| 290 | bpf_jmp(ins, &i, BPF_JMP0x05|BPF_JEQ0x10|BPF_K0x00, UDEV_MONITOR_MAGIC0xfeedcafe, 1, 0); | |||
| 291 | /* wrong magic, pass packet */ | |||
| 292 | bpf_stmt(ins, &i, BPF_RET0x06|BPF_K0x00, 0xffffffff); | |||
| 293 | ||||
| 294 | if (udev_list_get_entry(&udev_monitor->filter_tag_list) != NULL((void*)0)) { | |||
| 295 | int tag_matches; | |||
| 296 | ||||
| 297 | /* count tag matches, to calculate end of tag match block */ | |||
| 298 | tag_matches = 0; | |||
| 299 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list))for (list_entry = udev_list_get_entry(&udev_monitor->filter_tag_list ); list_entry != ((void*)0); list_entry = udev_list_entry_get_next (list_entry)) | |||
| 300 | tag_matches++; | |||
| 301 | ||||
| 302 | /* add all tags matches */ | |||
| 303 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list))for (list_entry = udev_list_get_entry(&udev_monitor->filter_tag_list ); list_entry != ((void*)0); list_entry = udev_list_entry_get_next (list_entry)) { | |||
| 304 | uint64_t tag_bloom_bits = util_string_bloom64(udev_list_entry_get_name(list_entry)); | |||
| 305 | uint32_t tag_bloom_hi = tag_bloom_bits >> 32; | |||
| 306 | uint32_t tag_bloom_lo = tag_bloom_bits & 0xffffffff; | |||
| 307 | ||||
| 308 | /* load device bloom bits in A */ | |||
| 309 | bpf_stmt(ins, &i, BPF_LD0x00|BPF_W0x00|BPF_ABS0x20, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_hi)__builtin_offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_hi )); | |||
| 310 | /* clear bits (tag bits & bloom bits) */ | |||
| 311 | bpf_stmt(ins, &i, BPF_ALU0x04|BPF_AND0x50|BPF_K0x00, tag_bloom_hi); | |||
| 312 | /* jump to next tag if it does not match */ | |||
| 313 | bpf_jmp(ins, &i, BPF_JMP0x05|BPF_JEQ0x10|BPF_K0x00, tag_bloom_hi, 0, 3); | |||
| 314 | ||||
| 315 | /* load device bloom bits in A */ | |||
| 316 | bpf_stmt(ins, &i, BPF_LD0x00|BPF_W0x00|BPF_ABS0x20, offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_lo)__builtin_offsetof(struct udev_monitor_netlink_header, filter_tag_bloom_lo )); | |||
| 317 | /* clear bits (tag bits & bloom bits) */ | |||
| 318 | bpf_stmt(ins, &i, BPF_ALU0x04|BPF_AND0x50|BPF_K0x00, tag_bloom_lo); | |||
| 319 | /* jump behind end of tag match block if tag matches */ | |||
| 320 | tag_matches--; | |||
| 321 | bpf_jmp(ins, &i, BPF_JMP0x05|BPF_JEQ0x10|BPF_K0x00, tag_bloom_lo, 1 + (tag_matches * 6), 0); | |||
| 322 | } | |||
| 323 | ||||
| 324 | /* nothing matched, drop packet */ | |||
| 325 | bpf_stmt(ins, &i, BPF_RET0x06|BPF_K0x00, 0); | |||
| 326 | } | |||
| 327 | ||||
| 328 | /* add all subsystem matches */ | |||
| 329 | if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) != NULL((void*)0)) { | |||
| 330 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list))for (list_entry = udev_list_get_entry(&udev_monitor->filter_subsystem_list ); list_entry != ((void*)0); list_entry = udev_list_entry_get_next (list_entry)) { | |||
| 331 | unsigned int hash = util_string_hash32(udev_list_entry_get_name(list_entry)); | |||
| 332 | ||||
| 333 | /* load device subsystem value in A */ | |||
| 334 | bpf_stmt(ins, &i, BPF_LD0x00|BPF_W0x00|BPF_ABS0x20, offsetof(struct udev_monitor_netlink_header, filter_subsystem_hash)__builtin_offsetof(struct udev_monitor_netlink_header, filter_subsystem_hash )); | |||
| 335 | if (udev_list_entry_get_value(list_entry) == NULL((void*)0)) { | |||
| 336 | /* jump if subsystem does not match */ | |||
| 337 | bpf_jmp(ins, &i, BPF_JMP0x05|BPF_JEQ0x10|BPF_K0x00, hash, 0, 1); | |||
| 338 | } else { | |||
| 339 | /* jump if subsystem does not match */ | |||
| 340 | bpf_jmp(ins, &i, BPF_JMP0x05|BPF_JEQ0x10|BPF_K0x00, hash, 0, 3); | |||
| 341 | ||||
| 342 | /* load device devtype value in A */ | |||
| 343 | bpf_stmt(ins, &i, BPF_LD0x00|BPF_W0x00|BPF_ABS0x20, offsetof(struct udev_monitor_netlink_header, filter_devtype_hash)__builtin_offsetof(struct udev_monitor_netlink_header, filter_devtype_hash )); | |||
| 344 | /* jump if value does not match */ | |||
| 345 | hash = util_string_hash32(udev_list_entry_get_value(list_entry)); | |||
| 346 | bpf_jmp(ins, &i, BPF_JMP0x05|BPF_JEQ0x10|BPF_K0x00, hash, 0, 1); | |||
| 347 | } | |||
| 348 | ||||
| 349 | /* matched, pass packet */ | |||
| 350 | bpf_stmt(ins, &i, BPF_RET0x06|BPF_K0x00, 0xffffffff); | |||
| 351 | ||||
| 352 | if (i+1 >= ELEMENTSOF(ins)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(ins), typeof(&*(ins))), sizeof(ins)/sizeof((ins)[ 0]), ((void)0)))) | |||
| 353 | return -E2BIG7; | |||
| 354 | } | |||
| 355 | ||||
| 356 | /* nothing matched, drop packet */ | |||
| 357 | bpf_stmt(ins, &i, BPF_RET0x06|BPF_K0x00, 0); | |||
| 358 | } | |||
| 359 | ||||
| 360 | /* matched, pass packet */ | |||
| 361 | bpf_stmt(ins, &i, BPF_RET0x06|BPF_K0x00, 0xffffffff); | |||
| 362 | ||||
| 363 | /* install filter */ | |||
| 364 | memzero(&filter, sizeof(filter))({ size_t _l_ = (sizeof(filter)); void *_x_ = (&filter); _l_ == 0 ? _x_ : memset(_x_, 0, _l_); }); | |||
| 365 | filter.len = i; | |||
| 366 | filter.filter = ins; | |||
| 367 | err = setsockopt(udev_monitor->sock, SOL_SOCKET1, SO_ATTACH_FILTER26, &filter, sizeof(filter)); | |||
| 368 | return err < 0 ? -errno(*__errno_location ()) : 0; | |||
| 369 | } | |||
| 370 | ||||
| 371 | int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender) | |||
| 372 | { | |||
| 373 | udev_monitor->snl_trusted_sender.nl.nl_pid = sender->snl.nl.nl_pid; | |||
| 374 | return 0; | |||
| 375 | } | |||
| 376 | ||||
| 377 | /** | |||
| 378 | * udev_monitor_enable_receiving: | |||
| 379 | * @udev_monitor: the monitor which should receive events | |||
| 380 | * | |||
| 381 | * Binds the @udev_monitor socket to the event source. | |||
| 382 | * | |||
| 383 | * Returns: 0 on success, otherwise a negative error value. | |||
| 384 | */ | |||
| 385 | _public___attribute__ ((visibility("default"))) int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) | |||
| 386 | { | |||
| 387 | int err = 0; | |||
| 388 | const int on = 1; | |||
| 389 | ||||
| 390 | udev_monitor_filter_update(udev_monitor); | |||
| 391 | ||||
| 392 | if (!udev_monitor->bound) { | |||
| ||||
| 393 | err = bind(udev_monitor->sock, | |||
| 394 | &udev_monitor->snl.sa, sizeof(struct sockaddr_nl)); | |||
| 395 | if (err == 0) | |||
| 396 | udev_monitor->bound = true1; | |||
| 397 | } | |||
| 398 | ||||
| 399 | if (err
| |||
| 400 | monitor_set_nl_address(udev_monitor); | |||
| 401 | else | |||
| 402 | return log_debug_errno(errno, "bind failed: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/libudev/libudev-monitor.c", 402, __func__, "bind failed: %m") : -abs(_e); }); | |||
| 403 | ||||
| 404 | /* enable receiving of sender credentials */ | |||
| 405 | err = setsockopt(udev_monitor->sock, SOL_SOCKET1, SO_PASSCRED16, &on, sizeof(on)); | |||
| 406 | if (err < 0) | |||
| 407 | log_debug_errno(errno, "setting SO_PASSCRED failed: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/libudev/libudev-monitor.c", 407, __func__, "setting SO_PASSCRED failed: %m") : -abs(_e); }); | |||
| 408 | ||||
| 409 | return 0; | |||
| 410 | } | |||
| 411 | ||||
| 412 | /** | |||
| 413 | * udev_monitor_set_receive_buffer_size: | |||
| 414 | * @udev_monitor: the monitor which should receive events | |||
| 415 | * @size: the size in bytes | |||
| 416 | * | |||
| 417 | * Set the size of the kernel socket buffer. This call needs the | |||
| 418 | * appropriate privileges to succeed. | |||
| 419 | * | |||
| 420 | * Returns: 0 on success, otherwise -1 on error. | |||
| 421 | */ | |||
| 422 | _public___attribute__ ((visibility("default"))) int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) | |||
| 423 | { | |||
| 424 | if (udev_monitor == NULL((void*)0)) | |||
| 425 | return -EINVAL22; | |||
| 426 | if (setsockopt(udev_monitor->sock, SOL_SOCKET1, SO_RCVBUFFORCE33, &size, sizeof(size)) < 0) | |||
| 427 | return -errno(*__errno_location ()); | |||
| 428 | ||||
| 429 | return 0; | |||
| 430 | } | |||
| 431 | ||||
| 432 | int udev_monitor_disconnect(struct udev_monitor *udev_monitor) | |||
| 433 | { | |||
| 434 | int err; | |||
| 435 | ||||
| 436 | err = close(udev_monitor->sock); | |||
| 437 | udev_monitor->sock = -1; | |||
| 438 | return err < 0 ? -errno(*__errno_location ()) : 0; | |||
| 439 | } | |||
| 440 | ||||
| 441 | /** | |||
| 442 | * udev_monitor_ref: | |||
| 443 | * @udev_monitor: udev monitor | |||
| 444 | * | |||
| 445 | * Take a reference of a udev monitor. | |||
| 446 | * | |||
| 447 | * Returns: the passed udev monitor | |||
| 448 | **/ | |||
| 449 | _public___attribute__ ((visibility("default"))) struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor) | |||
| 450 | { | |||
| 451 | if (udev_monitor == NULL((void*)0)) | |||
| 452 | return NULL((void*)0); | |||
| 453 | udev_monitor->refcount++; | |||
| 454 | return udev_monitor; | |||
| 455 | } | |||
| 456 | ||||
| 457 | /** | |||
| 458 | * udev_monitor_unref: | |||
| 459 | * @udev_monitor: udev monitor | |||
| 460 | * | |||
| 461 | * Drop a reference of a udev monitor. If the refcount reaches zero, | |||
| 462 | * the bound socket will be closed, and the resources of the monitor | |||
| 463 | * will be released. | |||
| 464 | * | |||
| 465 | * Returns: #NULL | |||
| 466 | **/ | |||
| 467 | _public___attribute__ ((visibility("default"))) struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor) | |||
| 468 | { | |||
| 469 | if (udev_monitor == NULL((void*)0)) | |||
| 470 | return NULL((void*)0); | |||
| 471 | udev_monitor->refcount--; | |||
| 472 | if (udev_monitor->refcount > 0) | |||
| 473 | return NULL((void*)0); | |||
| 474 | if (udev_monitor->sock >= 0) | |||
| 475 | close(udev_monitor->sock); | |||
| 476 | udev_list_cleanup(&udev_monitor->filter_subsystem_list); | |||
| 477 | udev_list_cleanup(&udev_monitor->filter_tag_list); | |||
| 478 | return mfree(udev_monitor); | |||
| 479 | } | |||
| 480 | ||||
| 481 | /** | |||
| 482 | * udev_monitor_get_udev: | |||
| 483 | * @udev_monitor: udev monitor | |||
| 484 | * | |||
| 485 | * Retrieve the udev library context the monitor was created with. | |||
| 486 | * | |||
| 487 | * Returns: the udev library context | |||
| 488 | **/ | |||
| 489 | _public___attribute__ ((visibility("default"))) struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) | |||
| 490 | { | |||
| 491 | if (udev_monitor == NULL((void*)0)) | |||
| 492 | return NULL((void*)0); | |||
| 493 | return udev_monitor->udev; | |||
| 494 | } | |||
| 495 | ||||
| 496 | /** | |||
| 497 | * udev_monitor_get_fd: | |||
| 498 | * @udev_monitor: udev monitor | |||
| 499 | * | |||
| 500 | * Retrieve the socket file descriptor associated with the monitor. | |||
| 501 | * | |||
| 502 | * Returns: the socket file descriptor | |||
| 503 | **/ | |||
| 504 | _public___attribute__ ((visibility("default"))) int udev_monitor_get_fd(struct udev_monitor *udev_monitor) | |||
| 505 | { | |||
| 506 | if (udev_monitor == NULL((void*)0)) | |||
| 507 | return -EINVAL22; | |||
| 508 | return udev_monitor->sock; | |||
| 509 | } | |||
| 510 | ||||
| 511 | static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device) | |||
| 512 | { | |||
| 513 | struct udev_list_entry *list_entry; | |||
| 514 | ||||
| 515 | if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL((void*)0)) | |||
| 516 | goto tag; | |||
| 517 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list))for (list_entry = udev_list_get_entry(&udev_monitor->filter_subsystem_list ); list_entry != ((void*)0); list_entry = udev_list_entry_get_next (list_entry)) { | |||
| 518 | const char *subsys = udev_list_entry_get_name(list_entry); | |||
| 519 | const char *dsubsys = udev_device_get_subsystem(udev_device); | |||
| 520 | const char *devtype; | |||
| 521 | const char *ddevtype; | |||
| 522 | ||||
| 523 | if (!streq(dsubsys, subsys)(strcmp((dsubsys),(subsys)) == 0)) | |||
| 524 | continue; | |||
| 525 | ||||
| 526 | devtype = udev_list_entry_get_value(list_entry); | |||
| 527 | if (devtype == NULL((void*)0)) | |||
| 528 | goto tag; | |||
| 529 | ddevtype = udev_device_get_devtype(udev_device); | |||
| 530 | if (ddevtype == NULL((void*)0)) | |||
| 531 | continue; | |||
| 532 | if (streq(ddevtype, devtype)(strcmp((ddevtype),(devtype)) == 0)) | |||
| 533 | goto tag; | |||
| 534 | } | |||
| 535 | return 0; | |||
| 536 | ||||
| 537 | tag: | |||
| 538 | if (udev_list_get_entry(&udev_monitor->filter_tag_list) == NULL((void*)0)) | |||
| 539 | return 1; | |||
| 540 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_tag_list))for (list_entry = udev_list_get_entry(&udev_monitor->filter_tag_list ); list_entry != ((void*)0); list_entry = udev_list_entry_get_next (list_entry)) { | |||
| 541 | const char *tag = udev_list_entry_get_name(list_entry); | |||
| 542 | ||||
| 543 | if (udev_device_has_tag(udev_device, tag)) | |||
| 544 | return 1; | |||
| 545 | } | |||
| 546 | return 0; | |||
| 547 | } | |||
| 548 | ||||
| 549 | /** | |||
| 550 | * udev_monitor_receive_device: | |||
| 551 | * @udev_monitor: udev monitor | |||
| 552 | * | |||
| 553 | * Receive data from the udev monitor socket, allocate a new udev | |||
| 554 | * device, fill in the received data, and return the device. | |||
| 555 | * | |||
| 556 | * Only socket connections with uid=0 are accepted. | |||
| 557 | * | |||
| 558 | * The monitor socket is by default set to NONBLOCK. A variant of poll() on | |||
| 559 | * the file descriptor returned by udev_monitor_get_fd() should to be used to | |||
| 560 | * wake up when new devices arrive, or alternatively the file descriptor | |||
| 561 | * switched into blocking mode. | |||
| 562 | * | |||
| 563 | * The initial refcount is 1, and needs to be decremented to | |||
| 564 | * release the resources of the udev device. | |||
| 565 | * | |||
| 566 | * Returns: a new udev device, or #NULL, in case of an error | |||
| 567 | **/ | |||
| 568 | _public___attribute__ ((visibility("default"))) struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) | |||
| 569 | { | |||
| 570 | struct udev_device *udev_device; | |||
| 571 | struct msghdr smsg; | |||
| 572 | struct iovec iov; | |||
| 573 | char cred_msg[CMSG_SPACE(sizeof(struct ucred))((((sizeof(struct ucred)) + sizeof (size_t) - 1) & (size_t ) ~(sizeof (size_t) - 1)) + (((sizeof (struct cmsghdr)) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)))]; | |||
| 574 | struct cmsghdr *cmsg; | |||
| 575 | union sockaddr_union snl; | |||
| 576 | struct ucred *cred; | |||
| 577 | union { | |||
| 578 | struct udev_monitor_netlink_header nlh; | |||
| 579 | char raw[8192]; | |||
| 580 | } buf; | |||
| 581 | ssize_t buflen; | |||
| 582 | ssize_t bufpos; | |||
| 583 | bool_Bool is_initialized = false0; | |||
| 584 | ||||
| 585 | retry: | |||
| 586 | if (udev_monitor == NULL((void*)0)) { | |||
| 587 | errno(*__errno_location ()) = EINVAL22; | |||
| 588 | return NULL((void*)0); | |||
| 589 | } | |||
| 590 | iov.iov_base = &buf; | |||
| 591 | iov.iov_len = sizeof(buf); | |||
| 592 | memzero(&smsg, sizeof(struct msghdr))({ size_t _l_ = (sizeof(struct msghdr)); void *_x_ = (&smsg ); _l_ == 0 ? _x_ : memset(_x_, 0, _l_); }); | |||
| 593 | smsg.msg_iov = &iov; | |||
| 594 | smsg.msg_iovlen = 1; | |||
| 595 | smsg.msg_control = cred_msg; | |||
| 596 | smsg.msg_controllen = sizeof(cred_msg); | |||
| 597 | smsg.msg_name = &snl; | |||
| 598 | smsg.msg_namelen = sizeof(snl); | |||
| 599 | ||||
| 600 | buflen = recvmsg(udev_monitor->sock, &smsg, 0); | |||
| 601 | if (buflen < 0) { | |||
| 602 | if (errno(*__errno_location ()) != EINTR4) | |||
| 603 | log_debug("unable to receive message")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 603, __func__, "unable to receive message" ) : -abs(_e); }); | |||
| 604 | return NULL((void*)0); | |||
| 605 | } | |||
| 606 | ||||
| 607 | if (buflen < 32 || (smsg.msg_flags & MSG_TRUNCMSG_TRUNC)) { | |||
| 608 | log_debug("invalid message length")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 608, __func__, "invalid message length" ) : -abs(_e); }); | |||
| 609 | errno(*__errno_location ()) = EINVAL22; | |||
| 610 | return NULL((void*)0); | |||
| 611 | } | |||
| 612 | ||||
| 613 | if (snl.nl.nl_groups == 0) { | |||
| 614 | /* unicast message, check if we trust the sender */ | |||
| 615 | if (udev_monitor->snl_trusted_sender.nl.nl_pid == 0 || | |||
| 616 | snl.nl.nl_pid != udev_monitor->snl_trusted_sender.nl.nl_pid) { | |||
| 617 | log_debug("unicast netlink message ignored")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 617, __func__, "unicast netlink message ignored" ) : -abs(_e); }); | |||
| 618 | errno(*__errno_location ()) = EAGAIN11; | |||
| 619 | return NULL((void*)0); | |||
| 620 | } | |||
| 621 | } else if (snl.nl.nl_groups == UDEV_MONITOR_KERNEL) { | |||
| 622 | if (snl.nl.nl_pid > 0) { | |||
| 623 | log_debug("multicast kernel netlink message from PID %"PRIu32" ignored",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 624, __func__, "multicast kernel netlink message from PID %" "u"" ignored", snl.nl.nl_pid) : -abs(_e); }) | |||
| 624 | snl.nl.nl_pid)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 624, __func__, "multicast kernel netlink message from PID %" "u"" ignored", snl.nl.nl_pid) : -abs(_e); }); | |||
| 625 | errno(*__errno_location ()) = EAGAIN11; | |||
| 626 | return NULL((void*)0); | |||
| 627 | } | |||
| 628 | } | |||
| 629 | ||||
| 630 | cmsg = CMSG_FIRSTHDR(&smsg)((size_t) (&smsg)->msg_controllen >= sizeof (struct cmsghdr) ? (struct cmsghdr *) (&smsg)->msg_control : ( struct cmsghdr *) 0); | |||
| 631 | if (cmsg == NULL((void*)0) || cmsg->cmsg_type != SCM_CREDENTIALSSCM_CREDENTIALS) { | |||
| 632 | log_debug("no sender credentials received, message ignored")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 632, __func__, "no sender credentials received, message ignored" ) : -abs(_e); }); | |||
| 633 | errno(*__errno_location ()) = EAGAIN11; | |||
| 634 | return NULL((void*)0); | |||
| 635 | } | |||
| 636 | ||||
| 637 | cred = (struct ucred *)CMSG_DATA(cmsg)((cmsg)->__cmsg_data); | |||
| 638 | if (cred->uid != 0) { | |||
| 639 | log_debug("sender uid="UID_FMT", message ignored", cred->uid)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 639, __func__, "sender uid=" "%" "u"", message ignored", cred->uid) : -abs(_e); }); | |||
| 640 | errno(*__errno_location ()) = EAGAIN11; | |||
| 641 | return NULL((void*)0); | |||
| 642 | } | |||
| 643 | ||||
| 644 | if (memcmp(buf.raw, "libudev", 8) == 0) { | |||
| 645 | /* udev message needs proper version magic */ | |||
| 646 | if (buf.nlh.magic != htobe32(UDEV_MONITOR_MAGIC)__bswap_32 (0xfeedcafe)) { | |||
| 647 | log_debug("unrecognized message signature (%x != %x)",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 648, __func__, "unrecognized message signature (%x != %x)" , buf.nlh.magic, __bswap_32 (0xfeedcafe)) : -abs(_e); }) | |||
| 648 | buf.nlh.magic, htobe32(UDEV_MONITOR_MAGIC))({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 648, __func__, "unrecognized message signature (%x != %x)" , buf.nlh.magic, __bswap_32 (0xfeedcafe)) : -abs(_e); }); | |||
| 649 | errno(*__errno_location ()) = EAGAIN11; | |||
| 650 | return NULL((void*)0); | |||
| 651 | } | |||
| 652 | if (buf.nlh.properties_off+32 > (size_t)buflen) { | |||
| 653 | log_debug("message smaller than expected (%u > %zd)",({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 654, __func__, "message smaller than expected (%u > %zd)" , buf.nlh.properties_off+32, buflen) : -abs(_e); }) | |||
| 654 | buf.nlh.properties_off+32, buflen)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 654, __func__, "message smaller than expected (%u > %zd)" , buf.nlh.properties_off+32, buflen) : -abs(_e); }); | |||
| 655 | errno(*__errno_location ()) = EAGAIN11; | |||
| 656 | return NULL((void*)0); | |||
| 657 | } | |||
| 658 | ||||
| 659 | bufpos = buf.nlh.properties_off; | |||
| 660 | ||||
| 661 | /* devices received from udev are always initialized */ | |||
| 662 | is_initialized = true1; | |||
| 663 | } else { | |||
| 664 | /* kernel message with header */ | |||
| 665 | bufpos = strlen(buf.raw) + 1; | |||
| 666 | if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) { | |||
| 667 | log_debug("invalid message length")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 667, __func__, "invalid message length" ) : -abs(_e); }); | |||
| 668 | errno(*__errno_location ()) = EAGAIN11; | |||
| 669 | return NULL((void*)0); | |||
| 670 | } | |||
| 671 | ||||
| 672 | /* check message header */ | |||
| 673 | if (strstr(buf.raw, "@/") == NULL((void*)0)) { | |||
| 674 | log_debug("unrecognized message header")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 674, __func__, "unrecognized message header" ) : -abs(_e); }); | |||
| 675 | errno(*__errno_location ()) = EAGAIN11; | |||
| 676 | return NULL((void*)0); | |||
| 677 | } | |||
| 678 | } | |||
| 679 | ||||
| 680 | udev_device = udev_device_new_from_nulstr(udev_monitor->udev, &buf.raw[bufpos], buflen - bufpos); | |||
| 681 | if (!udev_device) { | |||
| 682 | log_debug_errno(errno, "could not create device: %m")({ int _level = ((7)), _e = (((*__errno_location ()))), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/libudev/libudev-monitor.c", 682, __func__, "could not create device: %m") : -abs(_e); }); | |||
| 683 | return NULL((void*)0); | |||
| 684 | } | |||
| 685 | ||||
| 686 | if (is_initialized) | |||
| 687 | udev_device_set_is_initialized(udev_device); | |||
| 688 | ||||
| 689 | /* skip device, if it does not pass the current filter */ | |||
| 690 | if (!passes_filter(udev_monitor, udev_device)) { | |||
| 691 | struct pollfd pfd[1]; | |||
| 692 | int rc; | |||
| 693 | ||||
| 694 | udev_device_unref(udev_device); | |||
| 695 | ||||
| 696 | /* if something is queued, get next device */ | |||
| 697 | pfd[0].fd = udev_monitor->sock; | |||
| 698 | pfd[0].events = POLLIN0x001; | |||
| 699 | rc = poll(pfd, 1, 0); | |||
| 700 | if (rc > 0) | |||
| 701 | goto retry; | |||
| 702 | ||||
| 703 | errno(*__errno_location ()) = EAGAIN11; | |||
| 704 | return NULL((void*)0); | |||
| 705 | } | |||
| 706 | ||||
| 707 | return udev_device; | |||
| 708 | } | |||
| 709 | ||||
| 710 | int udev_monitor_send_device(struct udev_monitor *udev_monitor, | |||
| 711 | struct udev_monitor *destination, struct udev_device *udev_device) | |||
| 712 | { | |||
| 713 | const char *buf, *val; | |||
| 714 | ssize_t blen, count; | |||
| 715 | struct udev_monitor_netlink_header nlh = { | |||
| 716 | .prefix = "libudev", | |||
| 717 | .magic = htobe32(UDEV_MONITOR_MAGIC)__bswap_32 (0xfeedcafe), | |||
| 718 | .header_size = sizeof nlh, | |||
| 719 | }; | |||
| 720 | struct iovec iov[2] = { | |||
| 721 | { .iov_base = &nlh, .iov_len = sizeof nlh }, | |||
| 722 | }; | |||
| 723 | struct msghdr smsg = { | |||
| 724 | .msg_iov = iov, | |||
| 725 | .msg_iovlen = 2, | |||
| 726 | }; | |||
| 727 | struct udev_list_entry *list_entry; | |||
| 728 | uint64_t tag_bloom_bits; | |||
| 729 | ||||
| 730 | blen = udev_device_get_properties_monitor_buf(udev_device, &buf); | |||
| 731 | if (blen < 32) { | |||
| 732 | log_debug("device buffer is too small to contain a valid device")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 732, __func__, "device buffer is too small to contain a valid device" ) : -abs(_e); }); | |||
| 733 | return -EINVAL22; | |||
| 734 | } | |||
| 735 | ||||
| 736 | /* fill in versioned header */ | |||
| 737 | val = udev_device_get_subsystem(udev_device); | |||
| 738 | nlh.filter_subsystem_hash = htobe32(util_string_hash32(val))__bswap_32 (util_string_hash32(val)); | |||
| 739 | ||||
| 740 | val = udev_device_get_devtype(udev_device); | |||
| 741 | if (val != NULL((void*)0)) | |||
| 742 | nlh.filter_devtype_hash = htobe32(util_string_hash32(val))__bswap_32 (util_string_hash32(val)); | |||
| 743 | ||||
| 744 | /* add tag bloom filter */ | |||
| 745 | tag_bloom_bits = 0; | |||
| 746 | udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))for (list_entry = udev_device_get_tags_list_entry(udev_device ); list_entry != ((void*)0); list_entry = udev_list_entry_get_next (list_entry)) | |||
| 747 | tag_bloom_bits |= util_string_bloom64(udev_list_entry_get_name(list_entry)); | |||
| 748 | if (tag_bloom_bits > 0) { | |||
| 749 | nlh.filter_tag_bloom_hi = htobe32(tag_bloom_bits >> 32)__bswap_32 (tag_bloom_bits >> 32); | |||
| 750 | nlh.filter_tag_bloom_lo = htobe32(tag_bloom_bits & 0xffffffff)__bswap_32 (tag_bloom_bits & 0xffffffff); | |||
| 751 | } | |||
| 752 | ||||
| 753 | /* add properties list */ | |||
| 754 | nlh.properties_off = iov[0].iov_len; | |||
| 755 | nlh.properties_len = blen; | |||
| 756 | iov[1].iov_base = (char *)buf; | |||
| 757 | iov[1].iov_len = blen; | |||
| 758 | ||||
| 759 | /* | |||
| 760 | * Use custom address for target, or the default one. | |||
| 761 | * | |||
| 762 | * If we send to a multicast group, we will get | |||
| 763 | * ECONNREFUSED, which is expected. | |||
| 764 | */ | |||
| 765 | if (destination) | |||
| 766 | smsg.msg_name = &destination->snl; | |||
| 767 | else | |||
| 768 | smsg.msg_name = &udev_monitor->snl_destination; | |||
| 769 | smsg.msg_namelen = sizeof(struct sockaddr_nl); | |||
| 770 | count = sendmsg(udev_monitor->sock, &smsg, 0); | |||
| 771 | if (count < 0) { | |||
| 772 | if (!destination && errno(*__errno_location ()) == ECONNREFUSED111) { | |||
| 773 | log_debug("passed device to netlink monitor %p", udev_monitor)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 773, __func__, "passed device to netlink monitor %p" , udev_monitor) : -abs(_e); }); | |||
| 774 | return 0; | |||
| 775 | } else | |||
| 776 | return -errno(*__errno_location ()); | |||
| 777 | } | |||
| 778 | ||||
| 779 | log_debug("passed %zi byte device to netlink monitor %p", count, udev_monitor)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/libudev/libudev-monitor.c", 779, __func__, "passed %zi byte device to netlink monitor %p" , count, udev_monitor) : -abs(_e); }); | |||
| 780 | return count; | |||
| 781 | } | |||
| 782 | ||||
| 783 | /** | |||
| 784 | * udev_monitor_filter_add_match_subsystem_devtype: | |||
| 785 | * @udev_monitor: the monitor | |||
| 786 | * @subsystem: the subsystem value to match the incoming devices against | |||
| 787 | * @devtype: the devtype value to match the incoming devices against | |||
| 788 | * | |||
| 789 | * This filter is efficiently executed inside the kernel, and libudev subscribers | |||
| 790 | * will usually not be woken up for devices which do not match. | |||
| 791 | * | |||
| 792 | * The filter must be installed before the monitor is switched to listening mode. | |||
| 793 | * | |||
| 794 | * Returns: 0 on success, otherwise a negative error value. | |||
| 795 | */ | |||
| 796 | _public___attribute__ ((visibility("default"))) int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) | |||
| 797 | { | |||
| 798 | if (udev_monitor == NULL((void*)0)) | |||
| 799 | return -EINVAL22; | |||
| 800 | if (subsystem == NULL((void*)0)) | |||
| 801 | return -EINVAL22; | |||
| 802 | if (udev_list_entry_add(&udev_monitor->filter_subsystem_list, subsystem, devtype) == NULL((void*)0)) | |||
| 803 | return -ENOMEM12; | |||
| 804 | return 0; | |||
| 805 | } | |||
| 806 | ||||
| 807 | /** | |||
| 808 | * udev_monitor_filter_add_match_tag: | |||
| 809 | * @udev_monitor: the monitor | |||
| 810 | * @tag: the name of a tag | |||
| 811 | * | |||
| 812 | * This filter is efficiently executed inside the kernel, and libudev subscribers | |||
| 813 | * will usually not be woken up for devices which do not match. | |||
| 814 | * | |||
| 815 | * The filter must be installed before the monitor is switched to listening mode. | |||
| 816 | * | |||
| 817 | * Returns: 0 on success, otherwise a negative error value. | |||
| 818 | */ | |||
| 819 | _public___attribute__ ((visibility("default"))) int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) | |||
| 820 | { | |||
| 821 | if (udev_monitor == NULL((void*)0)) | |||
| 822 | return -EINVAL22; | |||
| 823 | if (tag == NULL((void*)0)) | |||
| 824 | return -EINVAL22; | |||
| 825 | if (udev_list_entry_add(&udev_monitor->filter_tag_list, tag, NULL((void*)0)) == NULL((void*)0)) | |||
| 826 | return -ENOMEM12; | |||
| 827 | return 0; | |||
| 828 | } | |||
| 829 | ||||
| 830 | /** | |||
| 831 | * udev_monitor_filter_remove: | |||
| 832 | * @udev_monitor: monitor | |||
| 833 | * | |||
| 834 | * Remove all filters from monitor. | |||
| 835 | * | |||
| 836 | * Returns: 0 on success, otherwise a negative error value. | |||
| 837 | */ | |||
| 838 | _public___attribute__ ((visibility("default"))) int udev_monitor_filter_remove(struct udev_monitor *udev_monitor) | |||
| 839 | { | |||
| 840 | static const struct sock_fprog filter = { 0, NULL((void*)0) }; | |||
| 841 | ||||
| 842 | udev_list_cleanup(&udev_monitor->filter_subsystem_list); | |||
| 843 | if (setsockopt(udev_monitor->sock, SOL_SOCKET1, SO_ATTACH_FILTER26, &filter, sizeof(filter)) < 0) | |||
| 844 | return -errno(*__errno_location ()); | |||
| 845 | ||||
| 846 | return 0; | |||
| 847 | } |