chiark / gitweb /
libudev: add missing 'global' to symbol export
[elogind.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 (streq(seqnum_str, udev_list_entry_get_value(list_entry))) {
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 }