]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libudev/libudev-queue.c
TODO: update
[thirdparty/systemd.git] / src / 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 42struct 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 **/
54cf0b7f 57_public_ 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 **/
54cf0b7f 81_public_ 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.
c1959569
KS
95 *
96 * Returns: the passed queue context if it has still an active reference, or #NULL otherwise.
8d6bc73a 97 **/
20bbd54f 98_public_ struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue)
64ccdf82 99{
912541b0 100 if (udev_queue == NULL)
20bbd54f 101 return NULL;
912541b0
KS
102 udev_queue->refcount--;
103 if (udev_queue->refcount > 0)
20bbd54f 104 return udev_queue;
912541b0
KS
105 udev_list_cleanup(&udev_queue->queue_list);
106 free(udev_queue);
20bbd54f 107 return NULL;
64ccdf82
KS
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 **/
54cf0b7f 118_public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
64ccdf82 119{
912541b0
KS
120 if (udev_queue == NULL)
121 return NULL;
122 return udev_queue->udev;
64ccdf82
KS
123}
124
f503f6b2 125unsigned long long int udev_get_kernel_seqnum(struct udev *udev)
64ccdf82 126{
912541b0
KS
127 unsigned long long int seqnum;
128 int fd;
129 char buf[32];
130 ssize_t len;
131
6ada823a 132 fd = open("/sys/kernel/uevent_seqnum", O_RDONLY|O_CLOEXEC);
912541b0
KS
133 if (fd < 0)
134 return 0;
135 len = read(fd, buf, sizeof(buf));
136 close(fd);
137 if (len <= 2)
138 return 0;
139 buf[len-1] = '\0';
140 seqnum = strtoull(buf, NULL, 10);
141 return seqnum;
64ccdf82
KS
142}
143
8d6bc73a
KS
144/**
145 * udev_queue_get_kernel_seqnum:
146 * @udev_queue: udev queue context
147 *
21dbe43a
KS
148 * Get the current kernel event sequence number.
149 *
150 * Returns: the sequence number.
8d6bc73a 151 **/
54cf0b7f 152_public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
64ccdf82 153{
912541b0 154 unsigned long long int seqnum;
64ccdf82 155
912541b0
KS
156 if (udev_queue == NULL)
157 return -EINVAL;
f503f6b2 158
912541b0 159 seqnum = udev_get_kernel_seqnum(udev_queue->udev);
912541b0 160 return seqnum;
64ccdf82
KS
161}
162
f503f6b2
AJ
163int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum)
164{
912541b0
KS
165 if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1)
166 return -1;
f503f6b2 167
912541b0 168 return 0;
f503f6b2
AJ
169}
170
171ssize_t udev_queue_skip_devpath(FILE *queue_file)
172{
912541b0 173 unsigned short int len;
f503f6b2 174
912541b0 175 if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) {
fc863dea 176 char *devpath = alloca(len);
f503f6b2 177
912541b0
KS
178 /* use fread to skip, fseek might drop buffered data */
179 if (fread(devpath, 1, len, queue_file) == len)
180 return len;
181 }
f503f6b2 182
912541b0 183 return -1;
f503f6b2
AJ
184}
185
186ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size)
187{
912541b0
KS
188 unsigned short int read_bytes = 0;
189 unsigned short int len;
f503f6b2 190
912541b0
KS
191 if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1)
192 return -1;
f503f6b2 193
912541b0
KS
194 read_bytes = (len < size - 1) ? len : size - 1;
195 if (fread(devpath, 1, read_bytes, queue_file) != read_bytes)
196 return -1;
197 devpath[read_bytes] = '\0';
f503f6b2 198
912541b0
KS
199 /* if devpath was too long, skip unread characters */
200 if (read_bytes != len) {
201 unsigned short int skip_bytes = len - read_bytes;
fc863dea 202 char *buf = alloca(skip_bytes);
f503f6b2 203
912541b0
KS
204 if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes)
205 return -1;
206 }
f503f6b2 207
912541b0 208 return read_bytes;
f503f6b2
AJ
209}
210
211static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start)
11d5eec2 212{
912541b0 213 FILE *queue_file;
11d5eec2 214
a267bebf 215 queue_file = fopen("/run/udev/queue.bin", "re");
912541b0
KS
216 if (queue_file == NULL)
217 return NULL;
f503f6b2 218
912541b0 219 if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) {
c8f8394a 220 udev_err(udev_queue->udev, "corrupt queue file\n");
912541b0
KS
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 *
21dbe43a
KS
232 * Get the last known udev event sequence number.
233 *
234 * Returns: the sequence number.
8d6bc73a 235 **/
54cf0b7f 236_public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
f503f6b2 237{
912541b0
KS
238 unsigned long long int seqnum_udev;
239 FILE *queue_file;
240
241 queue_file = open_queue_file(udev_queue, &seqnum_udev);
242 if (queue_file == NULL)
243 return 0;
244
245 for (;;) {
246 unsigned long long int seqnum;
247 ssize_t devpath_len;
248
249 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
250 break;
251 devpath_len = udev_queue_skip_devpath(queue_file);
252 if (devpath_len < 0)
253 break;
254 if (devpath_len > 0)
255 seqnum_udev = seqnum;
256 }
257
258 fclose(queue_file);
259 return seqnum_udev;
f503f6b2
AJ
260}
261
8d6bc73a
KS
262/**
263 * udev_queue_get_udev_is_active:
264 * @udev_queue: udev queue context
265 *
21dbe43a
KS
266 * Check if udev is active on the system.
267 *
8d6bc73a
KS
268 * Returns: a flag indicating if udev is active.
269 **/
54cf0b7f 270_public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
f503f6b2 271{
912541b0
KS
272 unsigned long long int seqnum_start;
273 FILE *queue_file;
f503f6b2 274
912541b0
KS
275 queue_file = open_queue_file(udev_queue, &seqnum_start);
276 if (queue_file == NULL)
277 return 0;
f503f6b2 278
912541b0
KS
279 fclose(queue_file);
280 return 1;
11d5eec2
KS
281}
282
8d6bc73a
KS
283/**
284 * udev_queue_get_queue_is_empty:
285 * @udev_queue: udev queue context
286 *
21dbe43a
KS
287 * Check if udev is currently processing any events.
288 *
8d6bc73a
KS
289 * Returns: a flag indicating if udev is currently handling events.
290 **/
54cf0b7f 291_public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
64ccdf82 292{
912541b0
KS
293 unsigned long long int seqnum_kernel;
294 unsigned long long int seqnum_udev = 0;
295 int queued = 0;
296 int is_empty = 0;
297 FILE *queue_file;
298
299 if (udev_queue == NULL)
300 return -EINVAL;
301 queue_file = open_queue_file(udev_queue, &seqnum_udev);
302 if (queue_file == NULL)
303 return 1;
304
305 for (;;) {
306 unsigned long long int seqnum;
307 ssize_t devpath_len;
308
309 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
310 break;
311 devpath_len = udev_queue_skip_devpath(queue_file);
312 if (devpath_len < 0)
313 break;
314
315 if (devpath_len > 0) {
316 queued++;
317 seqnum_udev = seqnum;
318 } else {
319 queued--;
320 }
321 }
322
baa30fbc 323 if (queued > 0)
912541b0 324 goto out;
912541b0
KS
325
326 seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue);
baa30fbc 327 if (seqnum_udev < seqnum_kernel)
912541b0 328 goto out;
912541b0 329
912541b0 330 is_empty = 1;
f503f6b2
AJ
331
332out:
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 *
21dbe43a
KS
343 * Check if udev is currently processing any events in a given sequence number range.
344 *
2906cbba 345 * Returns: a flag indicating if any of the sequence numbers in the given range is currently active.
8d6bc73a 346 **/
54cf0b7f 347_public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
912541b0 348 unsigned long long int start, unsigned long long int end)
64ccdf82 349{
912541b0
KS
350 unsigned long long int seqnum;
351 ssize_t devpath_len;
352 int unfinished;
353 FILE *queue_file;
354
355 if (udev_queue == NULL)
356 return -EINVAL;
357 queue_file = open_queue_file(udev_queue, &seqnum);
358 if (queue_file == NULL)
359 return 1;
360 if (start < seqnum)
361 start = seqnum;
362 if (start > end) {
363 fclose(queue_file);
364 return 1;
365 }
366 if (end - start > INT_MAX - 1) {
367 fclose(queue_file);
368 return -EOVERFLOW;
369 }
370
371 /*
372 * we might start with 0, and handle the initial seqnum
373 * only when we find an entry in the queue file
374 **/
375 unfinished = end - start;
376
377 do {
378 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
379 break;
380 devpath_len = udev_queue_skip_devpath(queue_file);
381 if (devpath_len < 0)
382 break;
383
384 /*
385 * we might start with an empty or re-build queue file, where
386 * the initial seqnum is not recorded as finished
387 */
388 if (start == seqnum && devpath_len > 0)
389 unfinished++;
390
391 if (devpath_len == 0) {
392 if (seqnum >= start && seqnum <= end)
393 unfinished--;
394 }
395 } while (unfinished > 0);
396
397 fclose(queue_file);
398
399 return (unfinished == 0);
f503f6b2
AJ
400}
401
8d6bc73a
KS
402/**
403 * udev_queue_get_seqnum_is_finished:
404 * @udev_queue: udev queue context
405 * @seqnum: sequence number
406 *
21dbe43a
KS
407 * Check if udev is currently processing a given sequence number.
408 *
2906cbba 409 * Returns: a flag indicating if the given sequence number is currently active.
8d6bc73a 410 **/
54cf0b7f 411_public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
f503f6b2 412{
912541b0
KS
413 if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum))
414 return 0;
f503f6b2 415
912541b0 416 return 1;
64ccdf82
KS
417}
418
8d6bc73a
KS
419/**
420 * udev_queue_get_queued_list_entry:
421 * @udev_queue: udev queue context
422 *
21dbe43a
KS
423 * Get the first entry of the list of queued events.
424 *
425 * Returns: a udev_list_entry.
8d6bc73a 426 **/
54cf0b7f 427_public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
64ccdf82 428{
912541b0
KS
429 unsigned long long int seqnum;
430 FILE *queue_file;
431
432 if (udev_queue == NULL)
433 return NULL;
434 udev_list_cleanup(&udev_queue->queue_list);
435
436 queue_file = open_queue_file(udev_queue, &seqnum);
437 if (queue_file == NULL)
438 return NULL;
439
440 for (;;) {
441 char syspath[UTIL_PATH_SIZE];
442 char *s;
443 size_t l;
444 ssize_t len;
445 char seqnum_str[32];
446 struct udev_list_entry *list_entry;
447
448 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
449 break;
450 snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum);
451
452 s = syspath;
6ada823a 453 l = util_strpcpy(&s, sizeof(syspath), "/sys");
912541b0
KS
454 len = udev_queue_read_devpath(queue_file, s, l);
455 if (len < 0)
456 break;
457
458 if (len > 0) {
459 udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str);
460 } else {
461 udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) {
462 if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) {
463 udev_list_entry_delete(list_entry);
464 break;
465 }
466 }
467 }
468 }
469 fclose(queue_file);
470
471 return udev_list_get_entry(&udev_queue->queue_list);
64ccdf82 472}