]>
Commit | Line | Data |
---|---|---|
ba6929f6 KS |
1 | /* |
2 | * libudev - interface to udev device information | |
3 | * | |
4 | * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org> | |
5 | * | |
4061ab9f KS |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
ba6929f6 KS |
10 | */ |
11 | ||
ba6929f6 KS |
12 | #include <stdio.h> |
13 | #include <stdlib.h> | |
14 | #include <stddef.h> | |
15 | #include <unistd.h> | |
16 | #include <errno.h> | |
17 | #include <string.h> | |
18 | #include <dirent.h> | |
e14bdd88 | 19 | #include <sys/poll.h> |
ba6929f6 KS |
20 | #include <sys/stat.h> |
21 | #include <sys/socket.h> | |
22 | #include <sys/un.h> | |
e14bdd88 | 23 | #include <arpa/inet.h> |
1c7047ea | 24 | #include <linux/netlink.h> |
e14bdd88 | 25 | #include <linux/filter.h> |
ba6929f6 KS |
26 | |
27 | #include "libudev.h" | |
28 | #include "libudev-private.h" | |
ba6929f6 KS |
29 | |
30 | struct udev_monitor { | |
31 | struct udev *udev; | |
32 | int refcount; | |
d59f11e1 | 33 | int sock; |
1c7047ea | 34 | struct sockaddr_nl snl; |
11625409 | 35 | struct sockaddr_nl snl_peer; |
1c7047ea | 36 | struct sockaddr_un sun; |
d59f11e1 | 37 | socklen_t addrlen; |
e14bdd88 | 38 | struct udev_list_node filter_subsystem_list; |
ba6929f6 KS |
39 | }; |
40 | ||
f2b93744 KS |
41 | enum udev_monitor_netlink_group { |
42 | UDEV_MONITOR_KERNEL = 1, | |
43 | UDEV_MONITOR_UDEV = 2, | |
44 | }; | |
45 | ||
e14bdd88 KS |
46 | #define UDEV_MONITOR_MAGIC 0xcafe1dea |
47 | struct udev_monitor_netlink_header { | |
48 | /* udev version text */ | |
49 | char version[16]; | |
50 | /* | |
51 | * magic to protect against daemon <-> library message format mismatch | |
52 | * used in the kernel from socket filter rules; needs to be stored in network order | |
53 | */ | |
54 | unsigned int magic; | |
55 | /* properties buffer */ | |
56 | unsigned short properties_off; | |
57 | unsigned short properties_len; | |
58 | /* | |
59 | * crc32 of some common device properties to filter with socket filters in the client | |
60 | * used in the kernel from socket filter rules; needs to be stored in network order | |
61 | */ | |
62 | unsigned int filter_subsystem; | |
63 | }; | |
64 | ||
65 | static struct udev_monitor *udev_monitor_new(struct udev *udev) | |
66 | { | |
67 | struct udev_monitor *udev_monitor; | |
68 | ||
69 | udev_monitor = calloc(1, sizeof(struct udev_monitor)); | |
70 | if (udev_monitor == NULL) | |
71 | return NULL; | |
72 | udev_monitor->refcount = 1; | |
73 | udev_monitor->udev = udev; | |
74 | udev_list_init(&udev_monitor->filter_subsystem_list); | |
75 | return udev_monitor; | |
76 | } | |
77 | ||
7d8787b3 KS |
78 | /** |
79 | * udev_monitor_new_from_socket: | |
80 | * @udev: udev library context | |
81 | * @socket_path: unix socket path | |
82 | * | |
ff109b8d KS |
83 | * Create new udev monitor and connect to a specified socket. The |
84 | * path to a socket either points to an existing socket file, or if | |
85 | * the socket path starts with a '@' character, an abstract namespace | |
7d8787b3 KS |
86 | * socket will be used. |
87 | * | |
ff109b8d KS |
88 | * A socket file will not be created. If it does not already exist, |
89 | * it will fall-back and connect to an abstract namespace socket with | |
90 | * the given path. The permissions adjustment of a socket file, as | |
91 | * well as the later cleanup, needs to be done by the caller. | |
92 | * | |
7d8787b3 | 93 | * The initial refcount is 1, and needs to be decremented to |
be7de409 | 94 | * release the resources of the udev monitor. |
7d8787b3 KS |
95 | * |
96 | * Returns: a new udev monitor, or #NULL, in case of an error | |
97 | **/ | |
ba6929f6 KS |
98 | struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path) |
99 | { | |
100 | struct udev_monitor *udev_monitor; | |
9925ab04 | 101 | struct stat statbuf; |
ba6929f6 KS |
102 | |
103 | if (udev == NULL) | |
104 | return NULL; | |
105 | if (socket_path == NULL) | |
106 | return NULL; | |
e14bdd88 | 107 | udev_monitor = udev_monitor_new(udev); |
ba6929f6 KS |
108 | if (udev_monitor == NULL) |
109 | return NULL; | |
ba6929f6 | 110 | |
1c7047ea | 111 | udev_monitor->sun.sun_family = AF_LOCAL; |
81d9e221 | 112 | if (socket_path[0] == '@') { |
9925ab04 KS |
113 | /* translate leading '@' to abstract namespace */ |
114 | util_strlcpy(udev_monitor->sun.sun_path, socket_path, sizeof(udev_monitor->sun.sun_path)); | |
1c7047ea | 115 | udev_monitor->sun.sun_path[0] = '\0'; |
9925ab04 | 116 | udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path); |
81d9e221 | 117 | } else if (stat(socket_path, &statbuf) == 0 && S_ISSOCK(statbuf.st_mode)) { |
9925ab04 KS |
118 | /* existing socket file */ |
119 | util_strlcpy(udev_monitor->sun.sun_path, socket_path, sizeof(udev_monitor->sun.sun_path)); | |
120 | udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path); | |
121 | } else { | |
122 | /* no socket file, assume abstract namespace socket */ | |
123 | util_strlcpy(&udev_monitor->sun.sun_path[1], socket_path, sizeof(udev_monitor->sun.sun_path)-1); | |
124 | udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path)+1; | |
125 | } | |
d59f11e1 KS |
126 | udev_monitor->sock = socket(AF_LOCAL, SOCK_DGRAM, 0); |
127 | if (udev_monitor->sock == -1) { | |
659353f5 | 128 | err(udev, "error getting socket: %m\n"); |
ba6929f6 KS |
129 | free(udev_monitor); |
130 | return NULL; | |
131 | } | |
4b09a2fc AJ |
132 | util_set_fd_cloexec(udev_monitor->sock); |
133 | ||
86b57788 | 134 | dbg(udev, "monitor %p created with '%s'\n", udev_monitor, socket_path); |
d59f11e1 KS |
135 | return udev_monitor; |
136 | } | |
ba6929f6 | 137 | |
ff109b8d KS |
138 | /** |
139 | * udev_monitor_new_from_netlink: | |
140 | * @udev: udev library context | |
141 | * @name: name of event source | |
142 | * | |
143 | * Create new udev monitor and connect to a specified event | |
144 | * source. Valid sources identifiers are "udev" and "kernel". | |
145 | * | |
146 | * Applications should usually not connect directly to the | |
147 | * "kernel" events, because the devices might not be useable | |
148 | * at that time, before udev has configured them, and created | |
149 | * device nodes. | |
150 | * | |
151 | * Accessing devices at the same time as udev, might result | |
152 | * in unpredictable behavior. | |
153 | * | |
154 | * The "udev" events are sent out after udev has finished its | |
155 | * event processing, all rules have been processed, and needed | |
156 | * device nodes are created. | |
157 | * | |
158 | * The initial refcount is 1, and needs to be decremented to | |
159 | * release the resources of the udev monitor. | |
160 | * | |
161 | * Returns: a new udev monitor, or #NULL, in case of an error | |
162 | **/ | |
f2b93744 | 163 | struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) |
1c7047ea KS |
164 | { |
165 | struct udev_monitor *udev_monitor; | |
f2b93744 | 166 | unsigned int group; |
1c7047ea KS |
167 | |
168 | if (udev == NULL) | |
169 | return NULL; | |
f2b93744 KS |
170 | |
171 | if (name == NULL) | |
172 | return NULL; | |
173 | if (strcmp(name, "kernel") == 0) | |
174 | group = UDEV_MONITOR_KERNEL; | |
175 | else if (strcmp(name, "udev") == 0) | |
176 | group = UDEV_MONITOR_UDEV; | |
177 | else | |
178 | return NULL; | |
179 | ||
e14bdd88 | 180 | udev_monitor = udev_monitor_new(udev); |
1c7047ea KS |
181 | if (udev_monitor == NULL) |
182 | return NULL; | |
1c7047ea KS |
183 | |
184 | udev_monitor->sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); | |
185 | if (udev_monitor->sock == -1) { | |
659353f5 | 186 | err(udev, "error getting socket: %m\n"); |
1c7047ea KS |
187 | free(udev_monitor); |
188 | return NULL; | |
189 | } | |
4b09a2fc | 190 | util_set_fd_cloexec(udev_monitor->sock); |
1c7047ea KS |
191 | |
192 | udev_monitor->snl.nl_family = AF_NETLINK; | |
11625409 KS |
193 | udev_monitor->snl.nl_groups = group; |
194 | udev_monitor->snl_peer.nl_family = AF_NETLINK; | |
195 | udev_monitor->snl_peer.nl_groups = UDEV_MONITOR_UDEV; | |
1c7047ea | 196 | |
11625409 | 197 | dbg(udev, "monitor %p created with NETLINK_KOBJECT_UEVENT (%u)\n", udev_monitor, group); |
1c7047ea KS |
198 | return udev_monitor; |
199 | } | |
200 | ||
e14bdd88 KS |
201 | static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i, |
202 | unsigned short code, unsigned int data) | |
203 | { | |
204 | struct sock_filter *ins = &inss[*i]; | |
205 | ||
206 | ins->code = code; | |
207 | ins->k = data; | |
208 | (*i)++; | |
209 | } | |
210 | ||
211 | static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i, | |
212 | unsigned short code, unsigned int data, | |
213 | unsigned short jt, unsigned short jf) | |
214 | { | |
215 | struct sock_filter *ins = &inss[*i]; | |
216 | ||
217 | ins->code = code; | |
218 | ins->jt = jt; | |
219 | ins->jf = jf; | |
220 | ins->k = data; | |
221 | (*i)++; | |
222 | } | |
223 | ||
224 | static int filter_apply(struct udev_monitor *udev_monitor) | |
225 | { | |
226 | static struct sock_filter ins[256]; | |
227 | static struct sock_fprog filter; | |
228 | unsigned int i; | |
229 | struct udev_list_entry *list_entry; | |
230 | int err; | |
231 | ||
232 | if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL) | |
233 | return 0; | |
234 | ||
235 | memset(ins, 0x00, sizeof(ins)); | |
236 | i = 0; | |
237 | ||
238 | /* load magic in accu */ | |
239 | bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, magic)); | |
240 | /* jump if magic matches */ | |
241 | bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, UDEV_MONITOR_MAGIC, 1, 0); | |
242 | /* wrong magic, drop packet */ | |
243 | bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); | |
244 | ||
245 | /* load filter_subsystem value in accu */ | |
246 | bpf_stmt(ins, &i, BPF_LD|BPF_W|BPF_ABS, offsetof(struct udev_monitor_netlink_header, filter_subsystem)); | |
247 | /* add all subsystem match values */ | |
248 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { | |
249 | const char *subsys = udev_list_entry_get_name(list_entry); | |
250 | unsigned int crc; | |
251 | ||
252 | crc = util_crc32((unsigned char *)subsys, strlen(subsys)); | |
253 | /* jump if value does not match */ | |
254 | bpf_jmp(ins, &i, BPF_JMP|BPF_JEQ|BPF_K, crc, 0, 1); | |
255 | /* matched, pass packet */ | |
256 | bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff); | |
257 | ||
258 | if (i+1 >= ARRAY_SIZE(ins)) | |
259 | return -1; | |
260 | } | |
261 | /* nothing matched, drop packet */ | |
262 | bpf_stmt(ins, &i, BPF_RET|BPF_K, 0); | |
263 | ||
264 | /* install filter */ | |
265 | filter.len = i; | |
266 | filter.filter = ins; | |
267 | err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)); | |
268 | return err; | |
269 | } | |
270 | ||
d59f11e1 KS |
271 | int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) |
272 | { | |
273 | int err; | |
274 | const int on = 1; | |
275 | ||
e14bdd88 | 276 | if (udev_monitor->sun.sun_family != 0) { |
11625409 KS |
277 | err = bind(udev_monitor->sock, |
278 | (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen); | |
e14bdd88 KS |
279 | } else if (udev_monitor->snl.nl_family != 0) { |
280 | filter_apply(udev_monitor); | |
e2b362d9 KS |
281 | err = bind(udev_monitor->sock, |
282 | (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl)); | |
e14bdd88 | 283 | } else { |
e2b362d9 | 284 | return -EINVAL; |
e14bdd88 | 285 | } |
e2b362d9 KS |
286 | |
287 | if (err < 0) { | |
288 | err(udev_monitor->udev, "bind failed: %m\n"); | |
289 | return err; | |
ba6929f6 | 290 | } |
e2b362d9 KS |
291 | |
292 | /* enable receiving of sender credentials */ | |
293 | setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); | |
d59f11e1 | 294 | return 0; |
ba6929f6 KS |
295 | } |
296 | ||
cb25a958 KS |
297 | int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) |
298 | { | |
299 | if (udev_monitor == NULL) | |
300 | return -1; | |
301 | return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size)); | |
302 | } | |
303 | ||
7d8787b3 KS |
304 | /** |
305 | * udev_monitor_ref: | |
306 | * @udev_monitor: udev monitor | |
307 | * | |
308 | * Take a reference of a udev monitor. | |
309 | * | |
310 | * Returns: the passed udev monitor | |
311 | **/ | |
ba6929f6 KS |
312 | struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor) |
313 | { | |
314 | if (udev_monitor == NULL) | |
315 | return NULL; | |
316 | udev_monitor->refcount++; | |
317 | return udev_monitor; | |
318 | } | |
319 | ||
7d8787b3 KS |
320 | /** |
321 | * udev_monitor_unref: | |
322 | * @udev_monitor: udev monitor | |
323 | * | |
ff109b8d | 324 | * Drop a reference of a udev monitor. If the refcount reaches zero, |
be7de409 | 325 | * the bound socket will be closed, and the resources of the monitor |
7d8787b3 KS |
326 | * will be released. |
327 | * | |
328 | **/ | |
ba6929f6 KS |
329 | void udev_monitor_unref(struct udev_monitor *udev_monitor) |
330 | { | |
331 | if (udev_monitor == NULL) | |
332 | return; | |
333 | udev_monitor->refcount--; | |
334 | if (udev_monitor->refcount > 0) | |
335 | return; | |
d59f11e1 KS |
336 | if (udev_monitor->sock >= 0) |
337 | close(udev_monitor->sock); | |
e14bdd88 | 338 | udev_list_cleanup_entries(udev_monitor->udev, &udev_monitor->filter_subsystem_list); |
86b57788 | 339 | dbg(udev_monitor->udev, "monitor %p released\n", udev_monitor); |
ba6929f6 KS |
340 | free(udev_monitor); |
341 | } | |
342 | ||
7d8787b3 KS |
343 | /** |
344 | * udev_monitor_get_udev: | |
345 | * @udev_monitor: udev monitor | |
346 | * | |
b98fd840 | 347 | * Retrieve the udev library context the monitor was created with. |
7d8787b3 KS |
348 | * |
349 | * Returns: the udev library context | |
350 | **/ | |
ba6929f6 KS |
351 | struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) |
352 | { | |
353 | if (udev_monitor == NULL) | |
354 | return NULL; | |
355 | return udev_monitor->udev; | |
356 | } | |
357 | ||
7d8787b3 KS |
358 | /** |
359 | * udev_monitor_get_fd: | |
360 | * @udev_monitor: udev monitor | |
361 | * | |
362 | * Retrieve the socket file descriptor associated with the monitor. | |
363 | * | |
364 | * Returns: the socket file descriptor | |
365 | **/ | |
ba6929f6 KS |
366 | int udev_monitor_get_fd(struct udev_monitor *udev_monitor) |
367 | { | |
368 | if (udev_monitor == NULL) | |
369 | return -1; | |
d59f11e1 | 370 | return udev_monitor->sock; |
ba6929f6 KS |
371 | } |
372 | ||
e14bdd88 KS |
373 | static int passes_filter(struct udev_monitor *udev_monitor, struct udev_device *udev_device) |
374 | { | |
375 | struct udev_list_entry *list_entry; | |
376 | int pass = 0; | |
377 | ||
378 | if (udev_list_get_entry(&udev_monitor->filter_subsystem_list) == NULL) | |
379 | return 1; | |
380 | ||
381 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_monitor->filter_subsystem_list)) { | |
382 | const char *subsys = udev_device_get_subsystem(udev_device); | |
383 | ||
384 | if (strcmp(udev_list_entry_get_name(list_entry), subsys) == 0) { | |
385 | pass= 1; | |
386 | break; | |
387 | } | |
388 | } | |
389 | return pass; | |
390 | } | |
391 | ||
7d8787b3 | 392 | /** |
d59f11e1 | 393 | * udev_monitor_receive_device: |
7d8787b3 KS |
394 | * @udev_monitor: udev monitor |
395 | * | |
d59f11e1 | 396 | * Receive data from the udev monitor socket, allocate a new udev |
b98fd840 | 397 | * device, fill in the received data, and return the device. |
7d8787b3 KS |
398 | * |
399 | * Only socket connections with uid=0 are accepted. The caller | |
be7de409 AJ |
400 | * needs to make sure that there is data to read from the socket. |
401 | * The call will block until the socket becomes readable. | |
7d8787b3 KS |
402 | * |
403 | * The initial refcount is 1, and needs to be decremented to | |
be7de409 | 404 | * release the resources of the udev device. |
7d8787b3 KS |
405 | * |
406 | * Returns: a new udev device, or #NULL, in case of an error | |
407 | **/ | |
d59f11e1 | 408 | struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) |
ba6929f6 KS |
409 | { |
410 | struct udev_device *udev_device; | |
411 | struct msghdr smsg; | |
ba6929f6 | 412 | struct iovec iov; |
ba6929f6 | 413 | char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; |
e2b362d9 | 414 | struct cmsghdr *cmsg; |
e86a923d | 415 | struct sockaddr_nl snl; |
e2b362d9 | 416 | struct ucred *cred; |
ba6929f6 KS |
417 | char buf[4096]; |
418 | size_t bufpos; | |
e14bdd88 | 419 | struct udev_monitor_netlink_header *nlh; |
81d9e221 | 420 | int devpath_set = 0; |
31f4b036 KS |
421 | int subsystem_set = 0; |
422 | int action_set = 0; | |
37372bbc KS |
423 | int maj = 0; |
424 | int min = 0; | |
ba6929f6 | 425 | |
e14bdd88 | 426 | retry: |
ba6929f6 KS |
427 | if (udev_monitor == NULL) |
428 | return NULL; | |
429 | memset(buf, 0x00, sizeof(buf)); | |
430 | iov.iov_base = &buf; | |
431 | iov.iov_len = sizeof(buf); | |
432 | memset (&smsg, 0x00, sizeof(struct msghdr)); | |
433 | smsg.msg_iov = &iov; | |
434 | smsg.msg_iovlen = 1; | |
435 | smsg.msg_control = cred_msg; | |
436 | smsg.msg_controllen = sizeof(cred_msg); | |
437 | ||
e86a923d SJR |
438 | if (udev_monitor->snl.nl_family != 0) { |
439 | smsg.msg_name = &snl; | |
e14bdd88 | 440 | smsg.msg_namelen = sizeof(snl); |
e86a923d SJR |
441 | } |
442 | ||
d59f11e1 | 443 | if (recvmsg(udev_monitor->sock, &smsg, 0) < 0) { |
ba6929f6 | 444 | if (errno != EINTR) |
e86a923d | 445 | info(udev_monitor->udev, "unable to receive message\n"); |
ba6929f6 KS |
446 | return NULL; |
447 | } | |
ba6929f6 | 448 | |
e86a923d SJR |
449 | if (udev_monitor->snl.nl_family != 0) { |
450 | if (snl.nl_groups == 0) { | |
451 | info(udev_monitor->udev, "unicast netlink message ignored\n"); | |
452 | return NULL; | |
453 | } | |
454 | if ((snl.nl_groups == UDEV_MONITOR_KERNEL) && (snl.nl_pid > 0)) { | |
455 | info(udev_monitor->udev, "multicast kernel netlink message from pid %d ignored\n", snl.nl_pid); | |
456 | return NULL; | |
457 | } | |
458 | } | |
459 | ||
e2b362d9 KS |
460 | cmsg = CMSG_FIRSTHDR(&smsg); |
461 | if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { | |
e86a923d | 462 | info(udev_monitor->udev, "no sender credentials received, message ignored\n"); |
e2b362d9 KS |
463 | return NULL; |
464 | } | |
1c7047ea | 465 | |
e2b362d9 KS |
466 | cred = (struct ucred *)CMSG_DATA(cmsg); |
467 | if (cred->uid != 0) { | |
e86a923d | 468 | info(udev_monitor->udev, "sender uid=%d, message ignored\n", cred->uid); |
e2b362d9 | 469 | return NULL; |
ba6929f6 KS |
470 | } |
471 | ||
e14bdd88 KS |
472 | nlh = (struct udev_monitor_netlink_header *) buf; |
473 | if (nlh->magic == ntohl(UDEV_MONITOR_MAGIC)) { | |
474 | /* udev message with version magic */ | |
475 | if (nlh->properties_off < sizeof(struct udev_monitor_netlink_header)) | |
476 | return NULL; | |
477 | if (nlh->properties_off+32U > sizeof(buf)) | |
478 | return NULL; | |
479 | bufpos = nlh->properties_off; | |
480 | } else { | |
481 | /* kernel message with header */ | |
482 | bufpos = strlen(buf) + 1; | |
483 | if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) { | |
484 | info(udev_monitor->udev, "invalid message length\n"); | |
485 | return NULL; | |
486 | } | |
ba6929f6 | 487 | |
e14bdd88 KS |
488 | /* check message header */ |
489 | if (strstr(buf, "@/") == NULL) { | |
490 | info(udev_monitor->udev, "unrecognized message header\n"); | |
491 | return NULL; | |
492 | } | |
ba6929f6 KS |
493 | } |
494 | ||
e0083e8e | 495 | udev_device = device_new(udev_monitor->udev); |
ba6929f6 KS |
496 | if (udev_device == NULL) { |
497 | return NULL; | |
498 | } | |
499 | ||
500 | while (bufpos < sizeof(buf)) { | |
501 | char *key; | |
502 | size_t keylen; | |
503 | ||
504 | key = &buf[bufpos]; | |
505 | keylen = strlen(key); | |
506 | if (keylen == 0) | |
507 | break; | |
508 | bufpos += keylen + 1; | |
509 | ||
510 | if (strncmp(key, "DEVPATH=", 8) == 0) { | |
8753fadf KS |
511 | char path[UTIL_PATH_SIZE]; |
512 | ||
513 | util_strlcpy(path, udev_get_sys_path(udev_monitor->udev), sizeof(path)); | |
514 | util_strlcat(path, &key[8], sizeof(path)); | |
8cd2e972 | 515 | udev_device_set_syspath(udev_device, path); |
81d9e221 | 516 | devpath_set = 1; |
ba6929f6 | 517 | } else if (strncmp(key, "SUBSYSTEM=", 10) == 0) { |
8cd2e972 | 518 | udev_device_set_subsystem(udev_device, &key[10]); |
31f4b036 | 519 | subsystem_set = 1; |
bf8b2ae1 MH |
520 | } else if (strncmp(key, "DEVTYPE=", 8) == 0) { |
521 | udev_device_set_devtype(udev_device, &key[8]); | |
ba6929f6 | 522 | } else if (strncmp(key, "DEVNAME=", 8) == 0) { |
8cd2e972 | 523 | udev_device_set_devnode(udev_device, &key[8]); |
ba6929f6 | 524 | } else if (strncmp(key, "DEVLINKS=", 9) == 0) { |
1e61ff54 KS |
525 | char devlinks[UTIL_PATH_SIZE]; |
526 | char *slink; | |
527 | char *next; | |
ba6929f6 | 528 | |
1e61ff54 KS |
529 | util_strlcpy(devlinks, &key[9], sizeof(devlinks)); |
530 | slink = devlinks; | |
531 | next = strchr(slink, ' '); | |
ba6929f6 KS |
532 | while (next != NULL) { |
533 | next[0] = '\0'; | |
8cd2e972 | 534 | udev_device_add_devlink(udev_device, slink); |
ba6929f6 KS |
535 | slink = &next[1]; |
536 | next = strchr(slink, ' '); | |
537 | } | |
538 | if (slink[0] != '\0') | |
8cd2e972 | 539 | udev_device_add_devlink(udev_device, slink); |
37372bbc | 540 | } else if (strncmp(key, "DRIVER=", 7) == 0) { |
8cd2e972 | 541 | udev_device_set_driver(udev_device, &key[7]); |
37372bbc | 542 | } else if (strncmp(key, "ACTION=", 7) == 0) { |
8cd2e972 | 543 | udev_device_set_action(udev_device, &key[7]); |
31f4b036 | 544 | action_set = 1; |
37372bbc KS |
545 | } else if (strncmp(key, "MAJOR=", 6) == 0) { |
546 | maj = strtoull(&key[6], NULL, 10); | |
547 | } else if (strncmp(key, "MINOR=", 6) == 0) { | |
548 | min = strtoull(&key[6], NULL, 10); | |
549 | } else if (strncmp(key, "DEVPATH_OLD=", 12) == 0) { | |
8cd2e972 | 550 | udev_device_set_devpath_old(udev_device, &key[12]); |
37372bbc | 551 | } else if (strncmp(key, "PHYSDEVPATH=", 12) == 0) { |
8cd2e972 | 552 | udev_device_set_physdevpath(udev_device, &key[12]); |
37372bbc | 553 | } else if (strncmp(key, "SEQNUM=", 7) == 0) { |
8cd2e972 | 554 | udev_device_set_seqnum(udev_device, strtoull(&key[7], NULL, 10)); |
37372bbc | 555 | } else if (strncmp(key, "TIMEOUT=", 8) == 0) { |
8cd2e972 | 556 | udev_device_set_timeout(udev_device, strtoull(&key[8], NULL, 10)); |
31f4b036 KS |
557 | } else if (strncmp(key, "PHYSDEV", 7) == 0) { |
558 | /* skip deprecated values */ | |
8753fadf | 559 | continue; |
31f4b036 KS |
560 | } else { |
561 | udev_device_add_property_from_string(udev_device, key); | |
562 | } | |
ba6929f6 | 563 | } |
31f4b036 KS |
564 | if (!devpath_set || !subsystem_set || !action_set) { |
565 | info(udev_monitor->udev, "missing values, skip\n"); | |
81d9e221 KS |
566 | udev_device_unref(udev_device); |
567 | return NULL; | |
568 | } | |
e14bdd88 KS |
569 | |
570 | /* skip device, if it does not pass the current filter */ | |
571 | if (!passes_filter(udev_monitor, udev_device)) { | |
572 | struct pollfd pfd[1]; | |
573 | int rc; | |
574 | ||
575 | udev_device_unref(udev_device); | |
576 | ||
577 | /* if something is queued, get next device */ | |
578 | pfd[0].fd = udev_monitor->sock; | |
579 | pfd[0].events = POLLIN; | |
580 | rc = poll(pfd, 1, 0); | |
581 | if (rc > 0) | |
582 | goto retry; | |
583 | return NULL; | |
584 | } | |
585 | ||
3361a0f1 KS |
586 | if (maj > 0) |
587 | udev_device_set_devnum(udev_device, makedev(maj, min)); | |
8cd2e972 | 588 | udev_device_set_info_loaded(udev_device); |
ba6929f6 KS |
589 | return udev_device; |
590 | } | |
9925ab04 KS |
591 | |
592 | int udev_monitor_send_device(struct udev_monitor *udev_monitor, struct udev_device *udev_device) | |
593 | { | |
e14bdd88 KS |
594 | struct msghdr smsg; |
595 | struct iovec iov[2]; | |
c2654402 | 596 | const char *buf; |
e14bdd88 | 597 | ssize_t blen; |
9925ab04 KS |
598 | ssize_t count; |
599 | ||
e14bdd88 KS |
600 | blen = udev_device_get_properties_monitor_buf(udev_device, &buf); |
601 | if (blen < 32) | |
3c67f7d2 | 602 | return -1; |
e14bdd88 KS |
603 | |
604 | if (udev_monitor->sun.sun_family != 0) { | |
605 | const char *action; | |
606 | char header[2048]; | |
607 | size_t hlen; | |
608 | ||
609 | /* header <action>@<devpath> */ | |
610 | action = udev_device_get_action(udev_device); | |
611 | if (action == NULL) | |
612 | return -EINVAL; | |
613 | util_strlcpy(header, action, sizeof(header)); | |
614 | util_strlcat(header, "@", sizeof(header)); | |
615 | hlen = util_strlcat(header, udev_device_get_devpath(udev_device), sizeof(header))+1; | |
616 | if (hlen >= sizeof(header)) | |
617 | return -EINVAL; | |
618 | iov[0].iov_base = header; | |
619 | iov[0].iov_len = hlen; | |
620 | ||
621 | /* add properties list */ | |
622 | iov[1].iov_base = (char *)buf; | |
623 | iov[1].iov_len = blen; | |
624 | ||
625 | memset(&smsg, 0x00, sizeof(struct msghdr)); | |
626 | smsg.msg_iov = iov; | |
627 | smsg.msg_iovlen = 2; | |
628 | smsg.msg_name = &udev_monitor->sun; | |
629 | smsg.msg_namelen = udev_monitor->addrlen; | |
630 | } else if (udev_monitor->snl.nl_family != 0) { | |
631 | const char *val; | |
632 | size_t vlen; | |
633 | struct udev_monitor_netlink_header nlh; | |
634 | ||
635 | ||
636 | /* add versioned header */ | |
637 | memset(&nlh, 0x00, sizeof(struct udev_monitor_netlink_header)); | |
638 | util_strlcpy(nlh.version, "udev-" VERSION, sizeof(nlh.version)); | |
639 | nlh.magic = htonl(UDEV_MONITOR_MAGIC); | |
640 | val = udev_device_get_subsystem(udev_device); | |
641 | vlen = strlen(val); | |
642 | nlh.filter_subsystem = htonl(util_crc32((unsigned char *)val, vlen)); | |
643 | iov[0].iov_base = &nlh; | |
644 | iov[0].iov_len = sizeof(struct udev_monitor_netlink_header); | |
645 | ||
646 | /* add properties list */ | |
647 | nlh.properties_off = iov[0].iov_len; | |
648 | nlh.properties_len = blen; | |
649 | iov[1].iov_base = (char *)buf; | |
650 | iov[1].iov_len = blen; | |
651 | ||
652 | memset(&smsg, 0x00, sizeof(struct msghdr)); | |
653 | smsg.msg_iov = iov; | |
654 | smsg.msg_iovlen = 2; | |
e2b362d9 | 655 | /* no destination besides the muticast group, we will always get ECONNREFUSED */ |
e14bdd88 KS |
656 | smsg.msg_name = &udev_monitor->snl_peer; |
657 | smsg.msg_namelen = sizeof(struct sockaddr_nl); | |
658 | } else { | |
e2b362d9 | 659 | return -1; |
e14bdd88 | 660 | } |
e2b362d9 | 661 | |
e14bdd88 | 662 | count = sendmsg(udev_monitor->sock, &smsg, 0); |
e86a923d | 663 | info(udev_monitor->udev, "passed %zi bytes to monitor %p\n", count, udev_monitor); |
9925ab04 KS |
664 | return count; |
665 | } | |
e14bdd88 KS |
666 | |
667 | int udev_monitor_filter_add_match_subsystem(struct udev_monitor *udev_monitor, const char *subsystem) | |
668 | { | |
669 | if (udev_monitor == NULL) | |
670 | return -EINVAL; | |
671 | if (subsystem == NULL) | |
672 | return 0; | |
673 | if (udev_list_entry_add(udev_monitor->udev, | |
674 | &udev_monitor->filter_subsystem_list, subsystem, NULL, 1, 0) == NULL) | |
675 | return -ENOMEM; | |
676 | return 0; | |
677 | } |