]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libudev/libudev-queue.c
bfb48073831bb75cd46266df043091f95b840640
[thirdparty/systemd.git] / src / libudev / libudev-queue.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2008-2012 Kay Sievers <kay@vrfy.org>
5 Copyright 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stddef.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <dirent.h>
28 #include <fcntl.h>
29 #include <limits.h>
30 #include <sys/stat.h>
31
32 #include "libudev.h"
33 #include "libudev-private.h"
34
35 /**
36 * SECTION:libudev-queue
37 * @short_description: access to currently active events
38 *
39 * The udev daemon processes events asynchronously. All events which do not have
40 * interdependencies run in parallel. This exports the current state of the
41 * event processing queue, and the current event sequence numbers from the kernel
42 * and the udev daemon.
43 */
44
45 /**
46 * udev_queue:
47 *
48 * Opaque object representing the current event queue in the udev daemon.
49 */
50 struct udev_queue {
51 struct udev *udev;
52 int refcount;
53 struct udev_list queue_list;
54 };
55
56 /**
57 * udev_queue_new:
58 * @udev: udev library context
59 *
60 * The initial refcount is 1, and needs to be decremented to
61 * release the resources of the udev queue context.
62 *
63 * Returns: the udev queue context, or #NULL on error.
64 **/
65 _public_ struct udev_queue *udev_queue_new(struct udev *udev)
66 {
67 struct udev_queue *udev_queue;
68
69 if (udev == NULL)
70 return NULL;
71
72 udev_queue = calloc(1, sizeof(struct udev_queue));
73 if (udev_queue == NULL)
74 return NULL;
75 udev_queue->refcount = 1;
76 udev_queue->udev = udev;
77 udev_list_init(udev, &udev_queue->queue_list, false);
78 return udev_queue;
79 }
80
81 /**
82 * udev_queue_ref:
83 * @udev_queue: udev queue context
84 *
85 * Take a reference of a udev queue context.
86 *
87 * Returns: the same udev queue context.
88 **/
89 _public_ struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue)
90 {
91 if (udev_queue == NULL)
92 return NULL;
93 udev_queue->refcount++;
94 return udev_queue;
95 }
96
97 /**
98 * udev_queue_unref:
99 * @udev_queue: udev queue context
100 *
101 * Drop a reference of a udev queue context. If the refcount reaches zero,
102 * the resources of the queue context will be released.
103 *
104 * Returns: the passed queue context if it has still an active reference, or #NULL otherwise.
105 **/
106 _public_ struct udev_queue *udev_queue_unref(struct udev_queue *udev_queue)
107 {
108 if (udev_queue == NULL)
109 return NULL;
110 udev_queue->refcount--;
111 if (udev_queue->refcount > 0)
112 return udev_queue;
113 udev_list_cleanup(&udev_queue->queue_list);
114 free(udev_queue);
115 return NULL;
116 }
117
118 /**
119 * udev_queue_get_udev:
120 * @udev_queue: udev queue context
121 *
122 * Retrieve the udev library context the queue context was created with.
123 *
124 * Returns: the udev library context.
125 **/
126 _public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
127 {
128 if (udev_queue == NULL)
129 return NULL;
130 return udev_queue->udev;
131 }
132
133 unsigned long long int udev_get_kernel_seqnum(struct udev *udev)
134 {
135 unsigned long long int seqnum;
136 int fd;
137 char buf[32];
138 ssize_t len;
139
140 fd = open("/sys/kernel/uevent_seqnum", O_RDONLY|O_CLOEXEC);
141 if (fd < 0)
142 return 0;
143 len = read(fd, buf, sizeof(buf));
144 close(fd);
145 if (len <= 2)
146 return 0;
147 buf[len-1] = '\0';
148 seqnum = strtoull(buf, NULL, 10);
149 return seqnum;
150 }
151
152 /**
153 * udev_queue_get_kernel_seqnum:
154 * @udev_queue: udev queue context
155 *
156 * Get the current kernel event sequence number.
157 *
158 * Returns: the sequence number.
159 **/
160 _public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
161 {
162 unsigned long long int seqnum;
163
164 if (udev_queue == NULL)
165 return -EINVAL;
166
167 seqnum = udev_get_kernel_seqnum(udev_queue->udev);
168 return seqnum;
169 }
170
171 int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum)
172 {
173 if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1)
174 return -1;
175
176 return 0;
177 }
178
179 ssize_t udev_queue_skip_devpath(FILE *queue_file)
180 {
181 unsigned short int len;
182
183 if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) {
184 char *devpath = alloca(len);
185
186 /* use fread to skip, fseek might drop buffered data */
187 if (fread(devpath, 1, len, queue_file) == len)
188 return len;
189 }
190
191 return -1;
192 }
193
194 ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size)
195 {
196 unsigned short int read_bytes = 0;
197 unsigned short int len;
198
199 if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1)
200 return -1;
201
202 read_bytes = (len < size - 1) ? len : size - 1;
203 if (fread(devpath, 1, read_bytes, queue_file) != read_bytes)
204 return -1;
205 devpath[read_bytes] = '\0';
206
207 /* if devpath was too long, skip unread characters */
208 if (read_bytes != len) {
209 unsigned short int skip_bytes = len - read_bytes;
210 char *buf = alloca(skip_bytes);
211
212 if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes)
213 return -1;
214 }
215
216 return read_bytes;
217 }
218
219 static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start)
220 {
221 FILE *queue_file;
222
223 queue_file = fopen("/run/udev/queue.bin", "re");
224 if (queue_file == NULL)
225 return NULL;
226
227 if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) {
228 udev_err(udev_queue->udev, "corrupt queue file\n");
229 fclose(queue_file);
230 return NULL;
231 }
232
233 return queue_file;
234 }
235
236 /**
237 * udev_queue_get_udev_seqnum:
238 * @udev_queue: udev queue context
239 *
240 * Get the last known udev event sequence number.
241 *
242 * Returns: the sequence number.
243 **/
244 _public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
245 {
246 unsigned long long int seqnum_udev;
247 FILE *queue_file;
248
249 queue_file = open_queue_file(udev_queue, &seqnum_udev);
250 if (queue_file == NULL)
251 return 0;
252
253 for (;;) {
254 unsigned long long int seqnum;
255 ssize_t devpath_len;
256
257 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
258 break;
259 devpath_len = udev_queue_skip_devpath(queue_file);
260 if (devpath_len < 0)
261 break;
262 if (devpath_len > 0)
263 seqnum_udev = seqnum;
264 }
265
266 fclose(queue_file);
267 return seqnum_udev;
268 }
269
270 /**
271 * udev_queue_get_udev_is_active:
272 * @udev_queue: udev queue context
273 *
274 * Check if udev is active on the system.
275 *
276 * Returns: a flag indicating if udev is active.
277 **/
278 _public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
279 {
280 unsigned long long int seqnum_start;
281 FILE *queue_file;
282
283 queue_file = open_queue_file(udev_queue, &seqnum_start);
284 if (queue_file == NULL)
285 return 0;
286
287 fclose(queue_file);
288 return 1;
289 }
290
291 /**
292 * udev_queue_get_queue_is_empty:
293 * @udev_queue: udev queue context
294 *
295 * Check if udev is currently processing any events.
296 *
297 * Returns: a flag indicating if udev is currently handling events.
298 **/
299 _public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
300 {
301 unsigned long long int seqnum_kernel;
302 unsigned long long int seqnum_udev = 0;
303 int queued = 0;
304 int is_empty = 0;
305 FILE *queue_file;
306
307 if (udev_queue == NULL)
308 return -EINVAL;
309 queue_file = open_queue_file(udev_queue, &seqnum_udev);
310 if (queue_file == NULL)
311 return 1;
312
313 for (;;) {
314 unsigned long long int seqnum;
315 ssize_t devpath_len;
316
317 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
318 break;
319 devpath_len = udev_queue_skip_devpath(queue_file);
320 if (devpath_len < 0)
321 break;
322
323 if (devpath_len > 0) {
324 queued++;
325 seqnum_udev = seqnum;
326 } else {
327 queued--;
328 }
329 }
330
331 if (queued > 0)
332 goto out;
333
334 seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue);
335 if (seqnum_udev < seqnum_kernel)
336 goto out;
337
338 is_empty = 1;
339
340 out:
341 fclose(queue_file);
342 return is_empty;
343 }
344
345 /**
346 * udev_queue_get_seqnum_sequence_is_finished:
347 * @udev_queue: udev queue context
348 * @start: first event sequence number
349 * @end: last event sequence number
350 *
351 * Check if udev is currently processing any events in a given sequence number range.
352 *
353 * Returns: a flag indicating if any of the sequence numbers in the given range is currently active.
354 **/
355 _public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
356 unsigned long long int start, unsigned long long int end)
357 {
358 unsigned long long int seqnum;
359 ssize_t devpath_len;
360 int unfinished;
361 FILE *queue_file;
362
363 if (udev_queue == NULL)
364 return -EINVAL;
365 queue_file = open_queue_file(udev_queue, &seqnum);
366 if (queue_file == NULL)
367 return 1;
368 if (start < seqnum)
369 start = seqnum;
370 if (start > end) {
371 fclose(queue_file);
372 return 1;
373 }
374 if (end - start > INT_MAX - 1) {
375 fclose(queue_file);
376 return -EOVERFLOW;
377 }
378
379 /*
380 * we might start with 0, and handle the initial seqnum
381 * only when we find an entry in the queue file
382 **/
383 unfinished = end - start;
384
385 do {
386 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
387 break;
388 devpath_len = udev_queue_skip_devpath(queue_file);
389 if (devpath_len < 0)
390 break;
391
392 /*
393 * we might start with an empty or re-build queue file, where
394 * the initial seqnum is not recorded as finished
395 */
396 if (start == seqnum && devpath_len > 0)
397 unfinished++;
398
399 if (devpath_len == 0) {
400 if (seqnum >= start && seqnum <= end)
401 unfinished--;
402 }
403 } while (unfinished > 0);
404
405 fclose(queue_file);
406
407 return (unfinished == 0);
408 }
409
410 /**
411 * udev_queue_get_seqnum_is_finished:
412 * @udev_queue: udev queue context
413 * @seqnum: sequence number
414 *
415 * Check if udev is currently processing a given sequence number.
416 *
417 * Returns: a flag indicating if the given sequence number is currently active.
418 **/
419 _public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
420 {
421 if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum))
422 return 0;
423
424 return 1;
425 }
426
427 /**
428 * udev_queue_get_queued_list_entry:
429 * @udev_queue: udev queue context
430 *
431 * Get the first entry of the list of queued events.
432 *
433 * Returns: a udev_list_entry.
434 **/
435 _public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
436 {
437 unsigned long long int seqnum;
438 FILE *queue_file;
439
440 if (udev_queue == NULL)
441 return NULL;
442 udev_list_cleanup(&udev_queue->queue_list);
443
444 queue_file = open_queue_file(udev_queue, &seqnum);
445 if (queue_file == NULL)
446 return NULL;
447
448 for (;;) {
449 char syspath[UTIL_PATH_SIZE];
450 char *s;
451 size_t l;
452 ssize_t len;
453 char seqnum_str[32];
454 struct udev_list_entry *list_entry;
455
456 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
457 break;
458 snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum);
459
460 s = syspath;
461 l = strpcpy(&s, sizeof(syspath), "/sys");
462 len = udev_queue_read_devpath(queue_file, s, l);
463 if (len < 0)
464 break;
465
466 if (len > 0) {
467 udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str);
468 } else {
469 udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) {
470 if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) {
471 udev_list_entry_delete(list_entry);
472 break;
473 }
474 }
475 }
476 }
477 fclose(queue_file);
478
479 return udev_list_get_entry(&udev_queue->queue_list);
480 }