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