]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libudev-queue.c
add test/src to .gitignore
[thirdparty/systemd.git] / src / 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 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 57UDEV_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 81UDEV_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 96UDEV_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 115UDEV_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 122unsigned 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 149UDEV_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
161int 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
169ssize_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
184ssize_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
209static 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 234UDEV_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 266UDEV_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 285UDEV_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
332out:
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 345UDEV_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 407UDEV_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 422UDEV_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 469struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue);
666fcf03 470UDEV_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}