chiark / gitweb /
ca230023072065cb9c1c837fff01ce60c080eb35
[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  * Returns: the current kernel event sequence number.
146  **/
147 _public_ unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
148 {
149         unsigned long long int seqnum;
150
151         if (udev_queue == NULL)
152                 return -EINVAL;
153
154         seqnum = udev_get_kernel_seqnum(udev_queue->udev);
155         return seqnum;
156 }
157
158 int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum)
159 {
160         if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1)
161                 return -1;
162
163         return 0;
164 }
165
166 ssize_t udev_queue_skip_devpath(FILE *queue_file)
167 {
168         unsigned short int len;
169
170         if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) {
171                 char *devpath = alloca(len);
172
173                 /* use fread to skip, fseek might drop buffered data */
174                 if (fread(devpath, 1, len, queue_file) == len)
175                         return len;
176         }
177
178         return -1;
179 }
180
181 ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size)
182 {
183         unsigned short int read_bytes = 0;
184         unsigned short int len;
185
186         if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1)
187                 return -1;
188
189         read_bytes = (len < size - 1) ? len : size - 1;
190         if (fread(devpath, 1, read_bytes, queue_file) != read_bytes)
191                 return -1;
192         devpath[read_bytes] = '\0';
193
194         /* if devpath was too long, skip unread characters */
195         if (read_bytes != len) {
196                 unsigned short int skip_bytes = len - read_bytes;
197                 char *buf = alloca(skip_bytes);
198
199                 if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes)
200                         return -1;
201         }
202
203         return read_bytes;
204 }
205
206 static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start)
207 {
208         char filename[UTIL_PATH_SIZE];
209         FILE *queue_file;
210
211         util_strscpyl(filename, sizeof(filename), "/run/udev/queue.bin", NULL);
212         queue_file = fopen(filename, "re");
213         if (queue_file == NULL)
214                 return NULL;
215
216         if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) {
217                 err(udev_queue->udev, "corrupt queue file\n");
218                 fclose(queue_file);
219                 return NULL;
220         }
221
222         return queue_file;
223 }
224
225 /**
226  * udev_queue_get_udev_seqnum:
227  * @udev_queue: udev queue context
228  *
229  * Returns: the last known udev event sequence number.
230  **/
231 _public_ unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
232 {
233         unsigned long long int seqnum_udev;
234         FILE *queue_file;
235
236         queue_file = open_queue_file(udev_queue, &seqnum_udev);
237         if (queue_file == NULL)
238                 return 0;
239
240         for (;;) {
241                 unsigned long long int seqnum;
242                 ssize_t devpath_len;
243
244                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
245                         break;
246                 devpath_len = udev_queue_skip_devpath(queue_file);
247                 if (devpath_len < 0)
248                         break;
249                 if (devpath_len > 0)
250                         seqnum_udev = seqnum;
251         }
252
253         fclose(queue_file);
254         return seqnum_udev;
255 }
256
257 /**
258  * udev_queue_get_udev_is_active:
259  * @udev_queue: udev queue context
260  *
261  * Returns: a flag indicating if udev is active.
262  **/
263 _public_ int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
264 {
265         unsigned long long int seqnum_start;
266         FILE *queue_file;
267
268         queue_file = open_queue_file(udev_queue, &seqnum_start);
269         if (queue_file == NULL)
270                 return 0;
271
272         fclose(queue_file);
273         return 1;
274 }
275
276 /**
277  * udev_queue_get_queue_is_empty:
278  * @udev_queue: udev queue context
279  *
280  * Returns: a flag indicating if udev is currently handling events.
281  **/
282 _public_ int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
283 {
284         unsigned long long int seqnum_kernel;
285         unsigned long long int seqnum_udev = 0;
286         int queued = 0;
287         int is_empty = 0;
288         FILE *queue_file;
289
290         if (udev_queue == NULL)
291                 return -EINVAL;
292         queue_file = open_queue_file(udev_queue, &seqnum_udev);
293         if (queue_file == NULL)
294                 return 1;
295
296         for (;;) {
297                 unsigned long long int seqnum;
298                 ssize_t devpath_len;
299
300                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
301                         break;
302                 devpath_len = udev_queue_skip_devpath(queue_file);
303                 if (devpath_len < 0)
304                         break;
305
306                 if (devpath_len > 0) {
307                         queued++;
308                         seqnum_udev = seqnum;
309                 } else {
310                         queued--;
311                 }
312         }
313
314         if (queued > 0)
315                 goto out;
316
317         seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue);
318         if (seqnum_udev < seqnum_kernel)
319                 goto out;
320
321         is_empty = 1;
322
323 out:
324         fclose(queue_file);
325         return is_empty;
326 }
327
328 /**
329  * udev_queue_get_seqnum_sequence_is_finished:
330  * @udev_queue: udev queue context
331  * @start: first event sequence number
332  * @end: last event sequence number
333  *
334  * Returns: a flag indicating if any of the sequence numbers in the given range is currently active.
335  **/
336 _public_ int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
337                                                unsigned long long int start, unsigned long long int end)
338 {
339         unsigned long long int seqnum;
340         ssize_t devpath_len;
341         int unfinished;
342         FILE *queue_file;
343
344         if (udev_queue == NULL)
345                 return -EINVAL;
346         queue_file = open_queue_file(udev_queue, &seqnum);
347         if (queue_file == NULL)
348                 return 1;
349         if (start < seqnum)
350                 start = seqnum;
351         if (start > end) {
352                 fclose(queue_file);
353                 return 1;
354         }
355         if (end - start > INT_MAX - 1) {
356                 fclose(queue_file);
357                 return -EOVERFLOW;
358         }
359
360         /*
361          * we might start with 0, and handle the initial seqnum
362          * only when we find an entry in the queue file
363          **/
364         unfinished = end - start;
365
366         do {
367                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
368                         break;
369                 devpath_len = udev_queue_skip_devpath(queue_file);
370                 if (devpath_len < 0)
371                         break;
372
373                 /*
374                  * we might start with an empty or re-build queue file, where
375                  * the initial seqnum is not recorded as finished
376                  */
377                 if (start == seqnum && devpath_len > 0)
378                         unfinished++;
379
380                 if (devpath_len == 0) {
381                         if (seqnum >= start && seqnum <= end)
382                                 unfinished--;
383                 }
384         } while (unfinished > 0);
385
386         fclose(queue_file);
387
388         return (unfinished == 0);
389 }
390
391 /**
392  * udev_queue_get_seqnum_is_finished:
393  * @udev_queue: udev queue context
394  * @seqnum: sequence number
395  *
396  * Returns: a flag indicating if the given sequence number is currently active.
397  **/
398 _public_ int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
399 {
400         if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum))
401                 return 0;
402
403         return 1;
404 }
405
406 /**
407  * udev_queue_get_queued_list_entry:
408  * @udev_queue: udev queue context
409  *
410  * Returns: the first entry of the list of queued events.
411  **/
412 _public_ struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
413 {
414         unsigned long long int seqnum;
415         FILE *queue_file;
416
417         if (udev_queue == NULL)
418                 return NULL;
419         udev_list_cleanup(&udev_queue->queue_list);
420
421         queue_file = open_queue_file(udev_queue, &seqnum);
422         if (queue_file == NULL)
423                 return NULL;
424
425         for (;;) {
426                 char syspath[UTIL_PATH_SIZE];
427                 char *s;
428                 size_t l;
429                 ssize_t len;
430                 char seqnum_str[32];
431                 struct udev_list_entry *list_entry;
432
433                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
434                         break;
435                 snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum);
436
437                 s = syspath;
438                 l = util_strpcpy(&s, sizeof(syspath), "/sys");
439                 len = udev_queue_read_devpath(queue_file, s, l);
440                 if (len < 0)
441                         break;
442
443                 if (len > 0) {
444                         udev_list_entry_add(&udev_queue->queue_list, syspath, seqnum_str);
445                 } else {
446                         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) {
447                                 if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) {
448                                         udev_list_entry_delete(list_entry);
449                                         break;
450                                 }
451                         }
452                 }
453         }
454         fclose(queue_file);
455
456         return udev_list_get_entry(&udev_queue->queue_list);
457 }
458
459 struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue);
460 _public_ struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue)
461 {
462         err(udev_queue->udev, "udev_queue_get_failed_list_entry() does not return anything; failed events are not recorded\n");
463         errno = ENOSYS;
464         return NULL;
465 }