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