chiark / gitweb /
docs: hook-up gtk-doc to 'make check'
[elogind.git] / src / libudev / libudev-queue.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
5  * Copyright (C) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
6  *
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.
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>
21 #include <limits.h>
22 #include <sys/stat.h>
23
24 #include "libudev.h"
25 #include "libudev-private.h"
26
27 /**
28  * SECTION:libudev-queue
29  * @short_description: access to currently active events
30  *
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
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  */
42 struct udev_queue {
43         struct udev *udev;
44         int refcount;
45         struct udev_list queue_list;
46 };
47
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  **/
57 _public_ struct udev_queue *udev_queue_new(struct udev *udev)
58 {
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;
71 }
72
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  **/
81 _public_ struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue)
82 {
83         if (udev_queue == NULL)
84                 return NULL;
85         udev_queue->refcount++;
86         return udev_queue;
87 }
88
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  **/
96 _public_ void udev_queue_unref(struct udev_queue *udev_queue)
97 {
98         if (udev_queue == NULL)
99                 return;
100         udev_queue->refcount--;
101         if (udev_queue->refcount > 0)
102                 return;
103         udev_list_cleanup(&udev_queue->queue_list);
104         free(udev_queue);
105 }
106
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  **/
115 _public_ struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
116 {
117         if (udev_queue == NULL)
118                 return NULL;
119         return udev_queue->udev;
120 }
121
122 unsigned long long int udev_get_kernel_seqnum(struct udev *udev)
123 {
124         unsigned long long int seqnum;
125         int fd;
126         char buf[32];
127         ssize_t len;
128
129         fd = open("/sys/kernel/uevent_seqnum", O_RDONLY|O_CLOEXEC);
130         if (fd < 0)
131                 return 0;
132         len = read(fd, buf, sizeof(buf));
133         close(fd);
134         if (len <= 2)
135                 return 0;
136         buf[len-1] = '\0';
137         seqnum = strtoull(buf, NULL, 10);
138         return seqnum;
139 }
140
141 /**
142  * udev_queue_get_kernel_seqnum:
143  * @udev_queue: udev queue context
144  *
145  * Get the current kernel event sequence number.
146  *
147  * Returns: the sequence number.
148  **/
149 _public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
150 {
151         unsigned long long int seqnum;
152
153         if (udev_queue == NULL)
154                 return -EINVAL;
155
156         seqnum = udev_get_kernel_seqnum(udev_queue->udev);
157         return seqnum;
158 }
159
160 int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum)
161 {
162         if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1)
163                 return -1;
164
165         return 0;
166 }
167
168 ssize_t udev_queue_skip_devpath(FILE *queue_file)
169 {
170         unsigned short int len;
171
172         if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) {
173                 char *devpath = alloca(len);
174
175                 /* use fread to skip, fseek might drop buffered data */
176                 if (fread(devpath, 1, len, queue_file) == len)
177                         return len;
178         }
179
180         return -1;
181 }
182
183 ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size)
184 {
185         unsigned short int read_bytes = 0;
186         unsigned short int len;
187
188         if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1)
189                 return -1;
190
191         read_bytes = (len < size - 1) ? len : size - 1;
192         if (fread(devpath, 1, read_bytes, queue_file) != read_bytes)
193                 return -1;
194         devpath[read_bytes] = '\0';
195
196         /* if devpath was too long, skip unread characters */
197         if (read_bytes != len) {
198                 unsigned short int skip_bytes = len - read_bytes;
199                 char *buf = alloca(skip_bytes);
200
201                 if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes)
202                         return -1;
203         }
204
205         return read_bytes;
206 }
207
208 static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start)
209 {
210         char filename[UTIL_PATH_SIZE];
211         FILE *queue_file;
212
213         util_strscpyl(filename, sizeof(filename), "/run/udev/queue.bin", NULL);
214         queue_file = fopen(filename, "re");
215         if (queue_file == NULL)
216                 return NULL;
217
218         if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) {
219                 err(udev_queue->udev, "corrupt queue file\n");
220                 fclose(queue_file);
221                 return NULL;
222         }
223
224         return queue_file;
225 }
226
227 /**
228  * udev_queue_get_udev_seqnum:
229  * @udev_queue: udev queue context
230  *
231  * Get the last known udev event sequence number.
232  *
233  * Returns: the sequence number.
234  **/
235 _public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
236 {
237         unsigned long long int seqnum_udev;
238         FILE *queue_file;
239
240         queue_file = open_queue_file(udev_queue, &seqnum_udev);
241         if (queue_file == NULL)
242                 return 0;
243
244         for (;;) {
245                 unsigned long long int seqnum;
246                 ssize_t devpath_len;
247
248                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
249                         break;
250                 devpath_len = udev_queue_skip_devpath(queue_file);
251                 if (devpath_len < 0)
252                         break;
253                 if (devpath_len > 0)
254                         seqnum_udev = seqnum;
255         }
256
257         fclose(queue_file);
258         return seqnum_udev;
259 }
260
261 /**
262  * udev_queue_get_udev_is_active:
263  * @udev_queue: udev queue context
264  *
265  * Check if udev is active on the system.
266  *
267  * Returns: a flag indicating if udev is active.
268  **/
269 _public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
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;
280 }
281
282 /**
283  * udev_queue_get_queue_is_empty:
284  * @udev_queue: udev queue context
285  *
286  * Check if udev is currently processing any events.
287  *
288  * Returns: a flag indicating if udev is currently handling events.
289  **/
290 _public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
291 {
292         unsigned long long int seqnum_kernel;
293         unsigned long long int seqnum_udev = 0;
294         int queued = 0;
295         int is_empty = 0;
296         FILE *queue_file;
297
298         if (udev_queue == NULL)
299                 return -EINVAL;
300         queue_file = open_queue_file(udev_queue, &seqnum_udev);
301         if (queue_file == NULL)
302                 return 1;
303
304         for (;;) {
305                 unsigned long long int seqnum;
306                 ssize_t devpath_len;
307
308                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
309                         break;
310                 devpath_len = udev_queue_skip_devpath(queue_file);
311                 if (devpath_len < 0)
312                         break;
313
314                 if (devpath_len > 0) {
315                         queued++;
316                         seqnum_udev = seqnum;
317                 } else {
318                         queued--;
319                 }
320         }
321
322         if (queued > 0)
323                 goto out;
324
325         seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue);
326         if (seqnum_udev < seqnum_kernel)
327                 goto out;
328
329         is_empty = 1;
330
331 out:
332         fclose(queue_file);
333         return is_empty;
334 }
335
336 /**
337  * udev_queue_get_seqnum_sequence_is_finished:
338  * @udev_queue: udev queue context
339  * @start: first event sequence number
340  * @end: last event sequence number
341  *
342  * Check if udev is currently processing any events in a given sequence number range.
343  *
344  * Returns: a flag indicating if any of the sequence numbers in the given range is currently active.
345  **/
346 _public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
347                                                unsigned long long int start, unsigned long long int end)
348 {
349         unsigned long long int seqnum;
350         ssize_t devpath_len;
351         int unfinished;
352         FILE *queue_file;
353
354         if (udev_queue == NULL)
355                 return -EINVAL;
356         queue_file = open_queue_file(udev_queue, &seqnum);
357         if (queue_file == NULL)
358                 return 1;
359         if (start < seqnum)
360                 start = seqnum;
361         if (start > end) {
362                 fclose(queue_file);
363                 return 1;
364         }
365         if (end - start > INT_MAX - 1) {
366                 fclose(queue_file);
367                 return -EOVERFLOW;
368         }
369
370         /*
371          * we might start with 0, and handle the initial seqnum
372          * only when we find an entry in the queue file
373          **/
374         unfinished = end - start;
375
376         do {
377                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
378                         break;
379                 devpath_len = udev_queue_skip_devpath(queue_file);
380                 if (devpath_len < 0)
381                         break;
382
383                 /*
384                  * we might start with an empty or re-build queue file, where
385                  * the initial seqnum is not recorded as finished
386                  */
387                 if (start == seqnum && devpath_len > 0)
388                         unfinished++;
389
390                 if (devpath_len == 0) {
391                         if (seqnum >= start && seqnum <= end)
392                                 unfinished--;
393                 }
394         } while (unfinished > 0);
395
396         fclose(queue_file);
397
398         return (unfinished == 0);
399 }
400
401 /**
402  * udev_queue_get_seqnum_is_finished:
403  * @udev_queue: udev queue context
404  * @seqnum: sequence number
405  *
406  * Check if udev is currently processing a given sequence number.
407  *
408  * Returns: a flag indicating if the given sequence number is currently active.
409  **/
410 _public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
411 {
412         if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum))
413                 return 0;
414
415         return 1;
416 }
417
418 /**
419  * udev_queue_get_queued_list_entry:
420  * @udev_queue: udev queue context
421  *
422  * Get the first entry of the list of queued events.
423  *
424  * Returns: a udev_list_entry.
425  **/
426 _public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
427 {
428         unsigned long long int seqnum;
429         FILE *queue_file;
430
431         if (udev_queue == NULL)
432                 return NULL;
433         udev_list_cleanup(&udev_queue->queue_list);
434
435         queue_file = open_queue_file(udev_queue, &seqnum);
436         if (queue_file == NULL)
437                 return NULL;
438
439         for (;;) {
440                 char syspath[UTIL_PATH_SIZE];
441                 char *s;
442                 size_t l;
443                 ssize_t len;
444                 char seqnum_str[32];
445                 struct udev_list_entry *list_entry;
446
447                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
448                         break;
449                 snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum);
450
451                 s = syspath;
452                 l = util_strpcpy(&s, sizeof(syspath), "/sys");
453                 len = udev_queue_read_devpath(queue_file, s, l);
454                 if (len < 0)
455                         break;
456
457                 if (len > 0) {
458                         udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str);
459                 } else {
460                         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) {
461                                 if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) {
462                                         udev_list_entry_delete(list_entry);
463                                         break;
464                                 }
465                         }
466                 }
467         }
468         fclose(queue_file);
469
470         return udev_list_get_entry(&udev_queue->queue_list);
471 }
472
473 struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue);
474 _public_ struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue)
475 {
476         err(udev_queue->udev, "udev_queue_get_failed_list_entry() does not return anything; failed events are not recorded\n");
477         errno = ENOSYS;
478         return NULL;
479 }