]>
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 KS |
42 | struct udev_queue { |
43 | struct udev *udev; | |
44 | int refcount; | |
869c9031 | 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 KS |
58 | { |
59 | struct udev_queue *udev_queue; | |
60 | ||
61 | if (udev == NULL) | |
62 | return NULL; | |
63 | ||
b29a5e4a | 64 | udev_queue = calloc(1, sizeof(struct udev_queue)); |
64ccdf82 KS |
65 | if (udev_queue == NULL) |
66 | return NULL; | |
64ccdf82 KS |
67 | udev_queue->refcount = 1; |
68 | udev_queue->udev = udev; | |
869c9031 | 69 | udev_list_init(udev, &udev_queue->queue_list, false); |
64ccdf82 KS |
70 | return udev_queue; |
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 KS |
82 | { |
83 | if (udev_queue == NULL) | |
84 | return NULL; | |
85 | udev_queue->refcount++; | |
86 | return udev_queue; | |
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 KS |
97 | { |
98 | if (udev_queue == NULL) | |
99 | return; | |
100 | udev_queue->refcount--; | |
101 | if (udev_queue->refcount > 0) | |
102 | return; | |
869c9031 | 103 | udev_list_cleanup(&udev_queue->queue_list); |
64ccdf82 KS |
104 | free(udev_queue); |
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 KS |
116 | { |
117 | if (udev_queue == NULL) | |
118 | return NULL; | |
119 | return udev_queue->udev; | |
120 | } | |
121 | ||
f503f6b2 | 122 | unsigned long long int udev_get_kernel_seqnum(struct udev *udev) |
64ccdf82 KS |
123 | { |
124 | char filename[UTIL_PATH_SIZE]; | |
125 | unsigned long long int seqnum; | |
126 | int fd; | |
127 | char buf[32]; | |
128 | ssize_t len; | |
129 | ||
f503f6b2 | 130 | util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL); |
0c5c4804 | 131 | fd = open(filename, O_RDONLY|O_CLOEXEC); |
64ccdf82 KS |
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); | |
64ccdf82 KS |
140 | return seqnum; |
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 | { |
64ccdf82 | 151 | unsigned long long int seqnum; |
64ccdf82 KS |
152 | |
153 | if (udev_queue == NULL) | |
154 | return -EINVAL; | |
f503f6b2 AJ |
155 | |
156 | seqnum = udev_get_kernel_seqnum(udev_queue->udev); | |
86b57788 | 157 | dbg(udev_queue->udev, "seqnum=%llu\n", seqnum); |
64ccdf82 KS |
158 | return seqnum; |
159 | } | |
160 | ||
f503f6b2 AJ |
161 | int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum) |
162 | { | |
163 | if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1) | |
164 | return -1; | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
169 | ssize_t udev_queue_skip_devpath(FILE *queue_file) | |
170 | { | |
171 | unsigned short int len; | |
172 | ||
173 | if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) { | |
174 | char devpath[len]; | |
175 | ||
176 | /* use fread to skip, fseek might drop buffered data */ | |
177 | if (fread(devpath, 1, len, queue_file) == len) | |
178 | return len; | |
179 | } | |
180 | ||
181 | return -1; | |
182 | } | |
183 | ||
184 | ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size) | |
185 | { | |
186 | unsigned short int read_bytes = 0; | |
187 | unsigned short int len; | |
188 | ||
189 | if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1) | |
190 | return -1; | |
191 | ||
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'; | |
196 | ||
197 | /* if devpath was too long, skip unread characters */ | |
198 | if (read_bytes != len) { | |
199 | unsigned short int skip_bytes = len - read_bytes; | |
200 | char buf[skip_bytes]; | |
201 | ||
202 | if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes) | |
203 | return -1; | |
204 | } | |
205 | ||
206 | return read_bytes; | |
207 | } | |
208 | ||
209 | static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start) | |
11d5eec2 KS |
210 | { |
211 | char filename[UTIL_PATH_SIZE]; | |
f503f6b2 | 212 | FILE *queue_file; |
11d5eec2 | 213 | |
4ec9c3e7 | 214 | util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue->udev), "/queue.bin", NULL); |
0c5c4804 | 215 | queue_file = fopen(filename, "re"); |
f503f6b2 AJ |
216 | if (queue_file == NULL) |
217 | return NULL; | |
218 | ||
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 | } | |
224 | ||
225 | return queue_file; | |
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 AJ |
235 | { |
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) | |
11d5eec2 | 241 | return 0; |
f503f6b2 | 242 | |
88cbfb09 | 243 | for (;;) { |
f503f6b2 AJ |
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; | |
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 AJ |
267 | { |
268 | unsigned long long int seqnum_start; | |
269 | FILE *queue_file; | |
270 | ||
271 | queue_file = open_queue_file(udev_queue, &seqnum_start); | |
272 | if (queue_file == NULL) | |
273 | return 0; | |
274 | ||
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 | { |
64ccdf82 | 287 | unsigned long long int seqnum_kernel; |
f503f6b2 AJ |
288 | unsigned long long int seqnum_udev = 0; |
289 | int queued = 0; | |
290 | int is_empty = 0; | |
291 | FILE *queue_file; | |
64ccdf82 KS |
292 | |
293 | if (udev_queue == NULL) | |
294 | return -EINVAL; | |
f503f6b2 AJ |
295 | queue_file = open_queue_file(udev_queue, &seqnum_udev); |
296 | if (queue_file == NULL) | |
297 | return 1; | |
298 | ||
e0d9c042 | 299 | for (;;) { |
f503f6b2 AJ |
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) { | |
86b57788 | 318 | dbg(udev_queue->udev, "queue is not empty\n"); |
f503f6b2 | 319 | goto out; |
64ccdf82 | 320 | } |
f503f6b2 | 321 | |
64ccdf82 | 322 | seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue); |
f503f6b2 AJ |
323 | if (seqnum_udev < seqnum_kernel) { |
324 | dbg(udev_queue->udev, "queue is empty but kernel events still pending [%llu]<->[%llu]\n", | |
325 | seqnum_kernel, seqnum_udev); | |
326 | goto out; | |
64ccdf82 | 327 | } |
f503f6b2 AJ |
328 | |
329 | dbg(udev_queue->udev, "queue is empty\n"); | |
330 | is_empty = 1; | |
331 | ||
332 | out: | |
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, |
f503f6b2 | 346 | unsigned long long int start, unsigned long long int end) |
64ccdf82 | 347 | { |
e0d9c042 | 348 | unsigned long long int seqnum; |
f503f6b2 AJ |
349 | ssize_t devpath_len; |
350 | int unfinished; | |
351 | FILE *queue_file; | |
64ccdf82 KS |
352 | |
353 | if (udev_queue == NULL) | |
354 | return -EINVAL; | |
f503f6b2 AJ |
355 | queue_file = open_queue_file(udev_queue, &seqnum); |
356 | if (queue_file == NULL) | |
357 | return 1; | |
358 | if (start < seqnum) | |
359 | start = seqnum; | |
9e6273c7 FZ |
360 | if (start > end) { |
361 | fclose(queue_file); | |
f503f6b2 | 362 | return 1; |
9e6273c7 FZ |
363 | } |
364 | if (end - start > INT_MAX - 1) { | |
365 | fclose(queue_file); | |
f503f6b2 | 366 | return -EOVERFLOW; |
9e6273c7 | 367 | } |
f503f6b2 | 368 | |
e0d9c042 KS |
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 { | |
f503f6b2 AJ |
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 | ||
e0d9c042 KS |
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 | ||
f503f6b2 AJ |
389 | if (devpath_len == 0) { |
390 | if (seqnum >= start && seqnum <= end) | |
391 | unfinished--; | |
392 | } | |
e0d9c042 KS |
393 | } while (unfinished > 0); |
394 | ||
f503f6b2 AJ |
395 | fclose(queue_file); |
396 | ||
397 | return (unfinished == 0); | |
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 AJ |
408 | { |
409 | if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum)) | |
64ccdf82 | 410 | return 0; |
f503f6b2 | 411 | |
86b57788 | 412 | dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum); |
64ccdf82 KS |
413 | return 1; |
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 | { |
f503f6b2 AJ |
424 | unsigned long long int seqnum; |
425 | FILE *queue_file; | |
64ccdf82 KS |
426 | |
427 | if (udev_queue == NULL) | |
428 | return NULL; | |
869c9031 | 429 | udev_list_cleanup(&udev_queue->queue_list); |
f503f6b2 AJ |
430 | |
431 | queue_file = open_queue_file(udev_queue, &seqnum); | |
432 | if (queue_file == NULL) | |
64ccdf82 | 433 | return NULL; |
f503f6b2 | 434 | |
88cbfb09 | 435 | for (;;) { |
64ccdf82 | 436 | char syspath[UTIL_PATH_SIZE]; |
065db052 KS |
437 | char *s; |
438 | size_t l; | |
64ccdf82 | 439 | ssize_t len; |
f503f6b2 AJ |
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); | |
64ccdf82 | 446 | |
065db052 KS |
447 | s = syspath; |
448 | l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL); | |
f503f6b2 AJ |
449 | len = udev_queue_read_devpath(queue_file, s, l); |
450 | if (len < 0) | |
451 | break; | |
452 | ||
453 | if (len > 0) { | |
869c9031 | 454 | udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str); |
f503f6b2 AJ |
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 | } | |
64ccdf82 | 463 | } |
f503f6b2 AJ |
464 | fclose(queue_file); |
465 | ||
8cd2e972 | 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 | { |
289a1821 KS |
472 | errno = ENOSYS; |
473 | return NULL; | |
64ccdf82 | 474 | } |