]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libudev/libudev-queue.c
udev: libudev - bump soname, remove deprecated functions, introduce symbol versions
[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.
95 **/
20bbd54f 96_public_ struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue)
64ccdf82 97{
912541b0 98 if (udev_queue == NULL)
20bbd54f 99 return NULL;
912541b0
KS
100 udev_queue->refcount--;
101 if (udev_queue->refcount > 0)
20bbd54f 102 return udev_queue;
912541b0
KS
103 udev_list_cleanup(&udev_queue->queue_list);
104 free(udev_queue);
20bbd54f 105 return NULL;
64ccdf82
KS
106}
107
8d6bc73a
KS
108/**
109 * udev_queue_get_udev:
110 * @udev_queue: udev queue context
111 *
112 * Retrieve the udev library context the queue context was created with.
113 *
114 * Returns: the udev library context.
115 **/
54cf0b7f 116_public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
64ccdf82 117{
912541b0
KS
118 if (udev_queue == NULL)
119 return NULL;
120 return udev_queue->udev;
64ccdf82
KS
121}
122
f503f6b2 123unsigned long long int udev_get_kernel_seqnum(struct udev *udev)
64ccdf82 124{
912541b0
KS
125 unsigned long long int seqnum;
126 int fd;
127 char buf[32];
128 ssize_t len;
129
6ada823a 130 fd = open("/sys/kernel/uevent_seqnum", O_RDONLY|O_CLOEXEC);
912541b0
KS
131 if (fd < 0)
132 return 0;
133 len = read(fd, buf, sizeof(buf));
134 close(fd);
135 if (len <= 2)
136 return 0;
137 buf[len-1] = '\0';
138 seqnum = strtoull(buf, NULL, 10);
139 return seqnum;
64ccdf82
KS
140}
141
8d6bc73a
KS
142/**
143 * udev_queue_get_kernel_seqnum:
144 * @udev_queue: udev queue context
145 *
21dbe43a
KS
146 * Get the current kernel event sequence number.
147 *
148 * Returns: the sequence number.
8d6bc73a 149 **/
54cf0b7f 150_public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
64ccdf82 151{
912541b0 152 unsigned long long int seqnum;
64ccdf82 153
912541b0
KS
154 if (udev_queue == NULL)
155 return -EINVAL;
f503f6b2 156
912541b0 157 seqnum = udev_get_kernel_seqnum(udev_queue->udev);
912541b0 158 return seqnum;
64ccdf82
KS
159}
160
f503f6b2
AJ
161int 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
169ssize_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
184ssize_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
209static 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
6ada823a 214 util_strscpyl(filename, sizeof(filename), "/run/udev/queue.bin", NULL);
912541b0
KS
215 queue_file = fopen(filename, "re");
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}