]>
Commit | Line | Data |
---|---|---|
64ccdf82 KS |
1 | /* |
2 | * libudev - interface to udev device information | |
3 | * | |
4 | * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org> | |
f503f6b2 | 5 | * Copyright (C) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk> |
64ccdf82 | 6 | * |
4061ab9f KS |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2.1 of the License, or (at your option) any later version. | |
64ccdf82 KS |
11 | */ |
12 | ||
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <stddef.h> | |
16 | #include <unistd.h> | |
17 | #include <errno.h> | |
18 | #include <string.h> | |
19 | #include <dirent.h> | |
20 | #include <fcntl.h> | |
f503f6b2 | 21 | #include <limits.h> |
64ccdf82 KS |
22 | #include <sys/stat.h> |
23 | ||
24 | #include "libudev.h" | |
25 | #include "libudev-private.h" | |
26 | ||
ce1d6d7f KS |
27 | /** |
28 | * SECTION:libudev-queue | |
29 | * @short_description: access to currently active events | |
30 | * | |
a7d29d15 KS |
31 | * The udev daemon processes events asynchronously. All events which do not have |
32 | * interdependencies run in parallel. This exports the current state of the | |
33 | * event processing queue, and the current event sequence numbers from the kernel | |
ce1d6d7f KS |
34 | * and the udev daemon. |
35 | */ | |
36 | ||
37 | /** | |
38 | * udev_queue: | |
39 | * | |
40 | * Opaque object representing the current event queue in the udev daemon. | |
41 | */ | |
64ccdf82 | 42 | struct udev_queue { |
912541b0 KS |
43 | struct udev *udev; |
44 | int refcount; | |
45 | struct udev_list queue_list; | |
64ccdf82 KS |
46 | }; |
47 | ||
8d6bc73a KS |
48 | /** |
49 | * udev_queue_new: | |
50 | * @udev: udev library context | |
51 | * | |
52 | * The initial refcount is 1, and needs to be decremented to | |
53 | * release the resources of the udev queue context. | |
54 | * | |
55 | * Returns: the udev queue context, or #NULL on error. | |
56 | **/ | |
666fcf03 | 57 | UDEV_EXPORT struct udev_queue *udev_queue_new(struct udev *udev) |
64ccdf82 | 58 | { |
912541b0 KS |
59 | struct udev_queue *udev_queue; |
60 | ||
61 | if (udev == NULL) | |
62 | return NULL; | |
63 | ||
64 | udev_queue = calloc(1, sizeof(struct udev_queue)); | |
65 | if (udev_queue == NULL) | |
66 | return NULL; | |
67 | udev_queue->refcount = 1; | |
68 | udev_queue->udev = udev; | |
69 | udev_list_init(udev, &udev_queue->queue_list, false); | |
70 | return udev_queue; | |
64ccdf82 KS |
71 | } |
72 | ||
8d6bc73a KS |
73 | /** |
74 | * udev_queue_ref: | |
75 | * @udev_queue: udev queue context | |
76 | * | |
77 | * Take a reference of a udev queue context. | |
78 | * | |
79 | * Returns: the same udev queue context. | |
80 | **/ | |
666fcf03 | 81 | UDEV_EXPORT struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue) |
64ccdf82 | 82 | { |
912541b0 KS |
83 | if (udev_queue == NULL) |
84 | return NULL; | |
85 | udev_queue->refcount++; | |
86 | return udev_queue; | |
64ccdf82 KS |
87 | } |
88 | ||
8d6bc73a KS |
89 | /** |
90 | * udev_queue_unref: | |
91 | * @udev_queue: udev queue context | |
92 | * | |
93 | * Drop a reference of a udev queue context. If the refcount reaches zero, | |
94 | * the resources of the queue context will be released. | |
95 | **/ | |
666fcf03 | 96 | UDEV_EXPORT void udev_queue_unref(struct udev_queue *udev_queue) |
64ccdf82 | 97 | { |
912541b0 KS |
98 | if (udev_queue == NULL) |
99 | return; | |
100 | udev_queue->refcount--; | |
101 | if (udev_queue->refcount > 0) | |
102 | return; | |
103 | udev_list_cleanup(&udev_queue->queue_list); | |
104 | free(udev_queue); | |
64ccdf82 KS |
105 | } |
106 | ||
8d6bc73a KS |
107 | /** |
108 | * udev_queue_get_udev: | |
109 | * @udev_queue: udev queue context | |
110 | * | |
111 | * Retrieve the udev library context the queue context was created with. | |
112 | * | |
113 | * Returns: the udev library context. | |
114 | **/ | |
666fcf03 | 115 | UDEV_EXPORT struct udev *udev_queue_get_udev(struct udev_queue *udev_queue) |
64ccdf82 | 116 | { |
912541b0 KS |
117 | if (udev_queue == NULL) |
118 | return NULL; | |
119 | return udev_queue->udev; | |
64ccdf82 KS |
120 | } |
121 | ||
f503f6b2 | 122 | unsigned long long int udev_get_kernel_seqnum(struct udev *udev) |
64ccdf82 | 123 | { |
912541b0 KS |
124 | char filename[UTIL_PATH_SIZE]; |
125 | unsigned long long int seqnum; | |
126 | int fd; | |
127 | char buf[32]; | |
128 | ssize_t len; | |
129 | ||
130 | util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL); | |
131 | fd = open(filename, O_RDONLY|O_CLOEXEC); | |
132 | if (fd < 0) | |
133 | return 0; | |
134 | len = read(fd, buf, sizeof(buf)); | |
135 | close(fd); | |
136 | if (len <= 2) | |
137 | return 0; | |
138 | buf[len-1] = '\0'; | |
139 | seqnum = strtoull(buf, NULL, 10); | |
140 | return seqnum; | |
64ccdf82 KS |
141 | } |
142 | ||
8d6bc73a KS |
143 | /** |
144 | * udev_queue_get_kernel_seqnum: | |
145 | * @udev_queue: udev queue context | |
146 | * | |
147 | * Returns: the current kernel event sequence number. | |
148 | **/ | |
666fcf03 | 149 | UDEV_EXPORT unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue) |
64ccdf82 | 150 | { |
912541b0 | 151 | unsigned long long int seqnum; |
64ccdf82 | 152 | |
912541b0 KS |
153 | if (udev_queue == NULL) |
154 | return -EINVAL; | |
f503f6b2 | 155 | |
912541b0 KS |
156 | seqnum = udev_get_kernel_seqnum(udev_queue->udev); |
157 | dbg(udev_queue->udev, "seqnum=%llu\n", seqnum); | |
158 | return seqnum; | |
64ccdf82 KS |
159 | } |
160 | ||
f503f6b2 AJ |
161 | int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum) |
162 | { | |
912541b0 KS |
163 | if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1) |
164 | return -1; | |
f503f6b2 | 165 | |
912541b0 | 166 | return 0; |
f503f6b2 AJ |
167 | } |
168 | ||
169 | ssize_t udev_queue_skip_devpath(FILE *queue_file) | |
170 | { | |
912541b0 | 171 | unsigned short int len; |
f503f6b2 | 172 | |
912541b0 | 173 | if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) { |
fc863dea | 174 | char *devpath = alloca(len); |
f503f6b2 | 175 | |
912541b0 KS |
176 | /* use fread to skip, fseek might drop buffered data */ |
177 | if (fread(devpath, 1, len, queue_file) == len) | |
178 | return len; | |
179 | } | |
f503f6b2 | 180 | |
912541b0 | 181 | return -1; |
f503f6b2 AJ |
182 | } |
183 | ||
184 | ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size) | |
185 | { | |
912541b0 KS |
186 | unsigned short int read_bytes = 0; |
187 | unsigned short int len; | |
f503f6b2 | 188 | |
912541b0 KS |
189 | if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1) |
190 | return -1; | |
f503f6b2 | 191 | |
912541b0 KS |
192 | read_bytes = (len < size - 1) ? len : size - 1; |
193 | if (fread(devpath, 1, read_bytes, queue_file) != read_bytes) | |
194 | return -1; | |
195 | devpath[read_bytes] = '\0'; | |
f503f6b2 | 196 | |
912541b0 KS |
197 | /* if devpath was too long, skip unread characters */ |
198 | if (read_bytes != len) { | |
199 | unsigned short int skip_bytes = len - read_bytes; | |
fc863dea | 200 | char *buf = alloca(skip_bytes); |
f503f6b2 | 201 | |
912541b0 KS |
202 | if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes) |
203 | return -1; | |
204 | } | |
f503f6b2 | 205 | |
912541b0 | 206 | return read_bytes; |
f503f6b2 AJ |
207 | } |
208 | ||
209 | static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start) | |
11d5eec2 | 210 | { |
912541b0 KS |
211 | char filename[UTIL_PATH_SIZE]; |
212 | FILE *queue_file; | |
11d5eec2 | 213 | |
912541b0 KS |
214 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue->udev), "/queue.bin", NULL); |
215 | queue_file = fopen(filename, "re"); | |
216 | if (queue_file == NULL) | |
217 | return NULL; | |
f503f6b2 | 218 | |
912541b0 KS |
219 | if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) { |
220 | err(udev_queue->udev, "corrupt queue file\n"); | |
221 | fclose(queue_file); | |
222 | return NULL; | |
223 | } | |
f503f6b2 | 224 | |
912541b0 | 225 | return queue_file; |
f503f6b2 AJ |
226 | } |
227 | ||
8d6bc73a KS |
228 | /** |
229 | * udev_queue_get_udev_seqnum: | |
230 | * @udev_queue: udev queue context | |
231 | * | |
232 | * Returns: the last known udev event sequence number. | |
233 | **/ | |
666fcf03 | 234 | UDEV_EXPORT unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue) |
f503f6b2 | 235 | { |
912541b0 KS |
236 | unsigned long long int seqnum_udev; |
237 | FILE *queue_file; | |
238 | ||
239 | queue_file = open_queue_file(udev_queue, &seqnum_udev); | |
240 | if (queue_file == NULL) | |
241 | return 0; | |
242 | ||
243 | for (;;) { | |
244 | unsigned long long int seqnum; | |
245 | ssize_t devpath_len; | |
246 | ||
247 | if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) | |
248 | break; | |
249 | devpath_len = udev_queue_skip_devpath(queue_file); | |
250 | if (devpath_len < 0) | |
251 | break; | |
252 | if (devpath_len > 0) | |
253 | seqnum_udev = seqnum; | |
254 | } | |
255 | ||
256 | fclose(queue_file); | |
257 | return seqnum_udev; | |
f503f6b2 AJ |
258 | } |
259 | ||
8d6bc73a KS |
260 | /** |
261 | * udev_queue_get_udev_is_active: | |
262 | * @udev_queue: udev queue context | |
263 | * | |
264 | * Returns: a flag indicating if udev is active. | |
265 | **/ | |
666fcf03 | 266 | UDEV_EXPORT int udev_queue_get_udev_is_active(struct udev_queue *udev_queue) |
f503f6b2 | 267 | { |
912541b0 KS |
268 | unsigned long long int seqnum_start; |
269 | FILE *queue_file; | |
f503f6b2 | 270 | |
912541b0 KS |
271 | queue_file = open_queue_file(udev_queue, &seqnum_start); |
272 | if (queue_file == NULL) | |
273 | return 0; | |
f503f6b2 | 274 | |
912541b0 KS |
275 | fclose(queue_file); |
276 | return 1; | |
11d5eec2 KS |
277 | } |
278 | ||
8d6bc73a KS |
279 | /** |
280 | * udev_queue_get_queue_is_empty: | |
281 | * @udev_queue: udev queue context | |
282 | * | |
283 | * Returns: a flag indicating if udev is currently handling events. | |
284 | **/ | |
666fcf03 | 285 | UDEV_EXPORT int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue) |
64ccdf82 | 286 | { |
912541b0 KS |
287 | unsigned long long int seqnum_kernel; |
288 | unsigned long long int seqnum_udev = 0; | |
289 | int queued = 0; | |
290 | int is_empty = 0; | |
291 | FILE *queue_file; | |
292 | ||
293 | if (udev_queue == NULL) | |
294 | return -EINVAL; | |
295 | queue_file = open_queue_file(udev_queue, &seqnum_udev); | |
296 | if (queue_file == NULL) | |
297 | return 1; | |
298 | ||
299 | for (;;) { | |
300 | unsigned long long int seqnum; | |
301 | ssize_t devpath_len; | |
302 | ||
303 | if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) | |
304 | break; | |
305 | devpath_len = udev_queue_skip_devpath(queue_file); | |
306 | if (devpath_len < 0) | |
307 | break; | |
308 | ||
309 | if (devpath_len > 0) { | |
310 | queued++; | |
311 | seqnum_udev = seqnum; | |
312 | } else { | |
313 | queued--; | |
314 | } | |
315 | } | |
316 | ||
317 | if (queued > 0) { | |
318 | dbg(udev_queue->udev, "queue is not empty\n"); | |
319 | goto out; | |
320 | } | |
321 | ||
322 | seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue); | |
323 | if (seqnum_udev < seqnum_kernel) { | |
324 | dbg(udev_queue->udev, "queue is empty but kernel events still pending [%llu]<->[%llu]\n", | |
6997e3b2 | 325 | seqnum_kernel, seqnum_udev); |
912541b0 KS |
326 | goto out; |
327 | } | |
328 | ||
329 | dbg(udev_queue->udev, "queue is empty\n"); | |
330 | is_empty = 1; | |
f503f6b2 AJ |
331 | |
332 | out: | |
912541b0 KS |
333 | fclose(queue_file); |
334 | return is_empty; | |
64ccdf82 KS |
335 | } |
336 | ||
8d6bc73a KS |
337 | /** |
338 | * udev_queue_get_seqnum_sequence_is_finished: | |
339 | * @udev_queue: udev queue context | |
340 | * @start: first event sequence number | |
341 | * @end: last event sequence number | |
342 | * | |
2906cbba | 343 | * Returns: a flag indicating if any of the sequence numbers in the given range is currently active. |
8d6bc73a | 344 | **/ |
666fcf03 | 345 | UDEV_EXPORT int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue, |
912541b0 | 346 | unsigned long long int start, unsigned long long int end) |
64ccdf82 | 347 | { |
912541b0 KS |
348 | unsigned long long int seqnum; |
349 | ssize_t devpath_len; | |
350 | int unfinished; | |
351 | FILE *queue_file; | |
352 | ||
353 | if (udev_queue == NULL) | |
354 | return -EINVAL; | |
355 | queue_file = open_queue_file(udev_queue, &seqnum); | |
356 | if (queue_file == NULL) | |
357 | return 1; | |
358 | if (start < seqnum) | |
359 | start = seqnum; | |
360 | if (start > end) { | |
361 | fclose(queue_file); | |
362 | return 1; | |
363 | } | |
364 | if (end - start > INT_MAX - 1) { | |
365 | fclose(queue_file); | |
366 | return -EOVERFLOW; | |
367 | } | |
368 | ||
369 | /* | |
370 | * we might start with 0, and handle the initial seqnum | |
371 | * only when we find an entry in the queue file | |
372 | **/ | |
373 | unfinished = end - start; | |
374 | ||
375 | do { | |
376 | if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) | |
377 | break; | |
378 | devpath_len = udev_queue_skip_devpath(queue_file); | |
379 | if (devpath_len < 0) | |
380 | break; | |
381 | ||
382 | /* | |
383 | * we might start with an empty or re-build queue file, where | |
384 | * the initial seqnum is not recorded as finished | |
385 | */ | |
386 | if (start == seqnum && devpath_len > 0) | |
387 | unfinished++; | |
388 | ||
389 | if (devpath_len == 0) { | |
390 | if (seqnum >= start && seqnum <= end) | |
391 | unfinished--; | |
392 | } | |
393 | } while (unfinished > 0); | |
394 | ||
395 | fclose(queue_file); | |
396 | ||
397 | return (unfinished == 0); | |
f503f6b2 AJ |
398 | } |
399 | ||
8d6bc73a KS |
400 | /** |
401 | * udev_queue_get_seqnum_is_finished: | |
402 | * @udev_queue: udev queue context | |
403 | * @seqnum: sequence number | |
404 | * | |
2906cbba | 405 | * Returns: a flag indicating if the given sequence number is currently active. |
8d6bc73a | 406 | **/ |
666fcf03 | 407 | UDEV_EXPORT int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum) |
f503f6b2 | 408 | { |
912541b0 KS |
409 | if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum)) |
410 | return 0; | |
f503f6b2 | 411 | |
912541b0 KS |
412 | dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum); |
413 | return 1; | |
64ccdf82 KS |
414 | } |
415 | ||
8d6bc73a KS |
416 | /** |
417 | * udev_queue_get_queued_list_entry: | |
418 | * @udev_queue: udev queue context | |
419 | * | |
420 | * Returns: the first entry of the list of queued events. | |
421 | **/ | |
666fcf03 | 422 | UDEV_EXPORT struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue) |
64ccdf82 | 423 | { |
912541b0 KS |
424 | unsigned long long int seqnum; |
425 | FILE *queue_file; | |
426 | ||
427 | if (udev_queue == NULL) | |
428 | return NULL; | |
429 | udev_list_cleanup(&udev_queue->queue_list); | |
430 | ||
431 | queue_file = open_queue_file(udev_queue, &seqnum); | |
432 | if (queue_file == NULL) | |
433 | return NULL; | |
434 | ||
435 | for (;;) { | |
436 | char syspath[UTIL_PATH_SIZE]; | |
437 | char *s; | |
438 | size_t l; | |
439 | ssize_t len; | |
440 | char seqnum_str[32]; | |
441 | struct udev_list_entry *list_entry; | |
442 | ||
443 | if (udev_queue_read_seqnum(queue_file, &seqnum) < 0) | |
444 | break; | |
445 | snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum); | |
446 | ||
447 | s = syspath; | |
448 | l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL); | |
449 | len = udev_queue_read_devpath(queue_file, s, l); | |
450 | if (len < 0) | |
451 | break; | |
452 | ||
453 | if (len > 0) { | |
454 | udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str); | |
455 | } else { | |
456 | udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) { | |
457 | if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) { | |
458 | udev_list_entry_delete(list_entry); | |
459 | break; | |
460 | } | |
461 | } | |
462 | } | |
463 | } | |
464 | fclose(queue_file); | |
465 | ||
466 | return udev_list_get_entry(&udev_queue->queue_list); | |
64ccdf82 KS |
467 | } |
468 | ||
289a1821 | 469 | struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue); |
666fcf03 | 470 | UDEV_EXPORT struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue) |
64ccdf82 | 471 | { |
912541b0 KS |
472 | errno = ENOSYS; |
473 | return NULL; | |
64ccdf82 | 474 | } |