]> git.ipfire.org Git - thirdparty/systemd.git/blame - libudev/libudev-queue.c
libudev: list - use binary search for list lookup
[thirdparty/systemd.git] / libudev / libudev-queue.c
CommitLineData
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
42struct udev_queue {
43 struct udev *udev;
44 int refcount;
869c9031
KS
45 struct udev_list queue_list;
46 struct udev_list failed_list;
64ccdf82
KS
47};
48
8d6bc73a
KS
49/**
50 * udev_queue_new:
51 * @udev: udev library context
52 *
53 * The initial refcount is 1, and needs to be decremented to
54 * release the resources of the udev queue context.
55 *
56 * Returns: the udev queue context, or #NULL on error.
57 **/
666fcf03 58UDEV_EXPORT struct udev_queue *udev_queue_new(struct udev *udev)
64ccdf82
KS
59{
60 struct udev_queue *udev_queue;
61
62 if (udev == NULL)
63 return NULL;
64
b29a5e4a 65 udev_queue = calloc(1, sizeof(struct udev_queue));
64ccdf82
KS
66 if (udev_queue == NULL)
67 return NULL;
64ccdf82
KS
68 udev_queue->refcount = 1;
69 udev_queue->udev = udev;
869c9031
KS
70 udev_list_init(udev, &udev_queue->queue_list, false);
71 udev_list_init(udev, &udev_queue->failed_list, false);
64ccdf82
KS
72 return udev_queue;
73}
74
8d6bc73a
KS
75/**
76 * udev_queue_ref:
77 * @udev_queue: udev queue context
78 *
79 * Take a reference of a udev queue context.
80 *
81 * Returns: the same udev queue context.
82 **/
666fcf03 83UDEV_EXPORT struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue)
64ccdf82
KS
84{
85 if (udev_queue == NULL)
86 return NULL;
87 udev_queue->refcount++;
88 return udev_queue;
89}
90
8d6bc73a
KS
91/**
92 * udev_queue_unref:
93 * @udev_queue: udev queue context
94 *
95 * Drop a reference of a udev queue context. If the refcount reaches zero,
96 * the resources of the queue context will be released.
97 **/
666fcf03 98UDEV_EXPORT void udev_queue_unref(struct udev_queue *udev_queue)
64ccdf82
KS
99{
100 if (udev_queue == NULL)
101 return;
102 udev_queue->refcount--;
103 if (udev_queue->refcount > 0)
104 return;
869c9031
KS
105 udev_list_cleanup(&udev_queue->queue_list);
106 udev_list_cleanup(&udev_queue->failed_list);
64ccdf82
KS
107 free(udev_queue);
108}
109
8d6bc73a
KS
110/**
111 * udev_queue_get_udev:
112 * @udev_queue: udev queue context
113 *
114 * Retrieve the udev library context the queue context was created with.
115 *
116 * Returns: the udev library context.
117 **/
666fcf03 118UDEV_EXPORT struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
64ccdf82
KS
119{
120 if (udev_queue == NULL)
121 return NULL;
122 return udev_queue->udev;
123}
124
f503f6b2 125unsigned long long int udev_get_kernel_seqnum(struct udev *udev)
64ccdf82
KS
126{
127 char filename[UTIL_PATH_SIZE];
128 unsigned long long int seqnum;
129 int fd;
130 char buf[32];
131 ssize_t len;
132
f503f6b2 133 util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL);
0c5c4804 134 fd = open(filename, O_RDONLY|O_CLOEXEC);
64ccdf82
KS
135 if (fd < 0)
136 return 0;
137 len = read(fd, buf, sizeof(buf));
138 close(fd);
139 if (len <= 2)
140 return 0;
141 buf[len-1] = '\0';
142 seqnum = strtoull(buf, NULL, 10);
64ccdf82
KS
143 return seqnum;
144}
145
8d6bc73a
KS
146/**
147 * udev_queue_get_kernel_seqnum:
148 * @udev_queue: udev queue context
149 *
150 * Returns: the current kernel event sequence number.
151 **/
666fcf03 152UDEV_EXPORT unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
64ccdf82 153{
64ccdf82 154 unsigned long long int seqnum;
64ccdf82
KS
155
156 if (udev_queue == NULL)
157 return -EINVAL;
f503f6b2
AJ
158
159 seqnum = udev_get_kernel_seqnum(udev_queue->udev);
86b57788 160 dbg(udev_queue->udev, "seqnum=%llu\n", seqnum);
64ccdf82
KS
161 return seqnum;
162}
163
f503f6b2
AJ
164int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum)
165{
166 if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1)
167 return -1;
168
169 return 0;
170}
171
172ssize_t udev_queue_skip_devpath(FILE *queue_file)
173{
174 unsigned short int len;
175
176 if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) {
177 char devpath[len];
178
179 /* use fread to skip, fseek might drop buffered data */
180 if (fread(devpath, 1, len, queue_file) == len)
181 return len;
182 }
183
184 return -1;
185}
186
187ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size)
188{
189 unsigned short int read_bytes = 0;
190 unsigned short int len;
191
192 if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1)
193 return -1;
194
195 read_bytes = (len < size - 1) ? len : size - 1;
196 if (fread(devpath, 1, read_bytes, queue_file) != read_bytes)
197 return -1;
198 devpath[read_bytes] = '\0';
199
200 /* if devpath was too long, skip unread characters */
201 if (read_bytes != len) {
202 unsigned short int skip_bytes = len - read_bytes;
203 char buf[skip_bytes];
204
205 if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes)
206 return -1;
207 }
208
209 return read_bytes;
210}
211
212static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start)
11d5eec2
KS
213{
214 char filename[UTIL_PATH_SIZE];
f503f6b2 215 FILE *queue_file;
11d5eec2 216
4ec9c3e7 217 util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue->udev), "/queue.bin", NULL);
0c5c4804 218 queue_file = fopen(filename, "re");
f503f6b2
AJ
219 if (queue_file == NULL)
220 return NULL;
221
222 if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) {
223 err(udev_queue->udev, "corrupt queue file\n");
224 fclose(queue_file);
225 return NULL;
226 }
227
228 return queue_file;
229}
230
8d6bc73a
KS
231/**
232 * udev_queue_get_udev_seqnum:
233 * @udev_queue: udev queue context
234 *
235 * Returns: the last known udev event sequence number.
236 **/
666fcf03 237UDEV_EXPORT unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
f503f6b2
AJ
238{
239 unsigned long long int seqnum_udev;
240 FILE *queue_file;
241
242 queue_file = open_queue_file(udev_queue, &seqnum_udev);
243 if (queue_file == NULL)
11d5eec2 244 return 0;
f503f6b2 245
88cbfb09 246 for (;;) {
f503f6b2
AJ
247 unsigned long long int seqnum;
248 ssize_t devpath_len;
249
250 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
251 break;
252 devpath_len = udev_queue_skip_devpath(queue_file);
253 if (devpath_len < 0)
254 break;
255 if (devpath_len > 0)
256 seqnum_udev = seqnum;
257 }
258
259 fclose(queue_file);
260 return seqnum_udev;
261}
262
8d6bc73a
KS
263/**
264 * udev_queue_get_udev_is_active:
265 * @udev_queue: udev queue context
266 *
267 * Returns: a flag indicating if udev is active.
268 **/
666fcf03 269UDEV_EXPORT int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
f503f6b2
AJ
270{
271 unsigned long long int seqnum_start;
272 FILE *queue_file;
273
274 queue_file = open_queue_file(udev_queue, &seqnum_start);
275 if (queue_file == NULL)
276 return 0;
277
278 fclose(queue_file);
279 return 1;
11d5eec2
KS
280}
281
8d6bc73a
KS
282/**
283 * udev_queue_get_queue_is_empty:
284 * @udev_queue: udev queue context
285 *
286 * Returns: a flag indicating if udev is currently handling events.
287 **/
666fcf03 288UDEV_EXPORT int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
64ccdf82 289{
64ccdf82 290 unsigned long long int seqnum_kernel;
f503f6b2
AJ
291 unsigned long long int seqnum_udev = 0;
292 int queued = 0;
293 int is_empty = 0;
294 FILE *queue_file;
64ccdf82
KS
295
296 if (udev_queue == NULL)
297 return -EINVAL;
f503f6b2
AJ
298 queue_file = open_queue_file(udev_queue, &seqnum_udev);
299 if (queue_file == NULL)
300 return 1;
301
e0d9c042 302 for (;;) {
f503f6b2
AJ
303 unsigned long long int seqnum;
304 ssize_t devpath_len;
305
306 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
307 break;
308 devpath_len = udev_queue_skip_devpath(queue_file);
309 if (devpath_len < 0)
310 break;
311
312 if (devpath_len > 0) {
313 queued++;
314 seqnum_udev = seqnum;
315 } else {
316 queued--;
317 }
318 }
319
320 if (queued > 0) {
86b57788 321 dbg(udev_queue->udev, "queue is not empty\n");
f503f6b2 322 goto out;
64ccdf82 323 }
f503f6b2 324
64ccdf82 325 seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue);
f503f6b2
AJ
326 if (seqnum_udev < seqnum_kernel) {
327 dbg(udev_queue->udev, "queue is empty but kernel events still pending [%llu]<->[%llu]\n",
328 seqnum_kernel, seqnum_udev);
329 goto out;
64ccdf82 330 }
f503f6b2
AJ
331
332 dbg(udev_queue->udev, "queue is empty\n");
333 is_empty = 1;
334
335out:
336 fclose(queue_file);
337 return is_empty;
64ccdf82
KS
338}
339
8d6bc73a
KS
340/**
341 * udev_queue_get_seqnum_sequence_is_finished:
342 * @udev_queue: udev queue context
343 * @start: first event sequence number
344 * @end: last event sequence number
345 *
2906cbba 346 * Returns: a flag indicating if any of the sequence numbers in the given range is currently active.
8d6bc73a 347 **/
666fcf03 348UDEV_EXPORT int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
f503f6b2 349 unsigned long long int start, unsigned long long int end)
64ccdf82 350{
e0d9c042 351 unsigned long long int seqnum;
f503f6b2
AJ
352 ssize_t devpath_len;
353 int unfinished;
354 FILE *queue_file;
64ccdf82
KS
355
356 if (udev_queue == NULL)
357 return -EINVAL;
f503f6b2
AJ
358 queue_file = open_queue_file(udev_queue, &seqnum);
359 if (queue_file == NULL)
360 return 1;
361 if (start < seqnum)
362 start = seqnum;
9e6273c7
FZ
363 if (start > end) {
364 fclose(queue_file);
f503f6b2 365 return 1;
9e6273c7
FZ
366 }
367 if (end - start > INT_MAX - 1) {
368 fclose(queue_file);
f503f6b2 369 return -EOVERFLOW;
9e6273c7 370 }
f503f6b2 371
e0d9c042
KS
372 /*
373 * we might start with 0, and handle the initial seqnum
374 * only when we find an entry in the queue file
375 **/
376 unfinished = end - start;
377
378 do {
f503f6b2
AJ
379 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
380 break;
381 devpath_len = udev_queue_skip_devpath(queue_file);
382 if (devpath_len < 0)
383 break;
384
e0d9c042
KS
385 /*
386 * we might start with an empty or re-build queue file, where
387 * the initial seqnum is not recorded as finished
388 */
389 if (start == seqnum && devpath_len > 0)
390 unfinished++;
391
f503f6b2
AJ
392 if (devpath_len == 0) {
393 if (seqnum >= start && seqnum <= end)
394 unfinished--;
395 }
e0d9c042
KS
396 } while (unfinished > 0);
397
f503f6b2
AJ
398 fclose(queue_file);
399
400 return (unfinished == 0);
401}
402
8d6bc73a
KS
403/**
404 * udev_queue_get_seqnum_is_finished:
405 * @udev_queue: udev queue context
406 * @seqnum: sequence number
407 *
2906cbba 408 * Returns: a flag indicating if the given sequence number is currently active.
8d6bc73a 409 **/
666fcf03 410UDEV_EXPORT int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
f503f6b2
AJ
411{
412 if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum))
64ccdf82 413 return 0;
f503f6b2 414
86b57788 415 dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum);
64ccdf82
KS
416 return 1;
417}
418
8d6bc73a
KS
419/**
420 * udev_queue_get_queued_list_entry:
421 * @udev_queue: udev queue context
422 *
423 * Returns: the first entry of the list of queued events.
424 **/
666fcf03 425UDEV_EXPORT struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
64ccdf82 426{
f503f6b2
AJ
427 unsigned long long int seqnum;
428 FILE *queue_file;
64ccdf82
KS
429
430 if (udev_queue == NULL)
431 return NULL;
869c9031 432 udev_list_cleanup(&udev_queue->queue_list);
f503f6b2
AJ
433
434 queue_file = open_queue_file(udev_queue, &seqnum);
435 if (queue_file == NULL)
64ccdf82 436 return NULL;
f503f6b2 437
88cbfb09 438 for (;;) {
64ccdf82 439 char syspath[UTIL_PATH_SIZE];
065db052
KS
440 char *s;
441 size_t l;
64ccdf82 442 ssize_t len;
f503f6b2
AJ
443 char seqnum_str[32];
444 struct udev_list_entry *list_entry;
445
446 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
447 break;
448 snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum);
64ccdf82 449
065db052
KS
450 s = syspath;
451 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
f503f6b2
AJ
452 len = udev_queue_read_devpath(queue_file, s, l);
453 if (len < 0)
454 break;
455
456 if (len > 0) {
869c9031 457 udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str);
f503f6b2
AJ
458 } else {
459 udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) {
460 if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) {
461 udev_list_entry_delete(list_entry);
462 break;
463 }
464 }
465 }
64ccdf82 466 }
f503f6b2
AJ
467 fclose(queue_file);
468
8cd2e972 469 return udev_list_get_entry(&udev_queue->queue_list);
64ccdf82
KS
470}
471
8d6bc73a
KS
472/**
473 * udev_queue_get_failed_list_entry:
474 * @udev_queue: udev queue context
475 *
476 * Returns: the first entry of the list of recorded failed events.
477 **/
666fcf03 478UDEV_EXPORT struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue)
64ccdf82
KS
479{
480 char path[UTIL_PATH_SIZE];
481 DIR *dir;
482 struct dirent *dent;
483
484 if (udev_queue == NULL)
485 return NULL;
869c9031 486 udev_list_cleanup(&udev_queue->failed_list);
4ec9c3e7 487 util_strscpyl(path, sizeof(path), udev_get_run_path(udev_queue->udev), "/failed", NULL);
64ccdf82
KS
488 dir = opendir(path);
489 if (dir == NULL)
490 return NULL;
491 for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
492 char filename[UTIL_PATH_SIZE];
493 char syspath[UTIL_PATH_SIZE];
065db052
KS
494 char *s;
495 size_t l;
64ccdf82 496 ssize_t len;
065db052 497 struct stat statbuf;
64ccdf82
KS
498
499 if (dent->d_name[0] == '.')
500 continue;
065db052
KS
501 s = syspath;
502 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
e6c1a2bd 503 len = readlinkat(dirfd(dir), dent->d_name, s, l);
60067cc7 504 if (len <= 0 || (size_t)len == l)
64ccdf82 505 continue;
065db052 506 s[len] = '\0';
86b57788 507 dbg(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name);
065db052 508 util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
64ccdf82
KS
509 if (stat(filename, &statbuf) != 0)
510 continue;
869c9031 511 udev_list_entry_add(&udev_queue->failed_list, syspath, NULL);
64ccdf82
KS
512 }
513 closedir(dir);
8cd2e972 514 return udev_list_get_entry(&udev_queue->failed_list);
64ccdf82 515}