chiark / gitweb /
systemd: bind udev control socket in systemd and split udev.service
[elogind.git] / 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_node queue_list;
46         struct udev_list_node failed_list;
47 };
48
49 /**
50  * udev_queue_new:
51  * @udev: udev library context
52  *
53  * The initial refcount is 1, and needs to be decremented to
54  * release the resources of the udev queue context.
55  *
56  * Returns: the udev queue context, or #NULL on error.
57  **/
58 struct udev_queue *udev_queue_new(struct udev *udev)
59 {
60         struct udev_queue *udev_queue;
61
62         if (udev == NULL)
63                 return NULL;
64
65         udev_queue = calloc(1, sizeof(struct udev_queue));
66         if (udev_queue == NULL)
67                 return NULL;
68         udev_queue->refcount = 1;
69         udev_queue->udev = udev;
70         udev_list_init(&udev_queue->queue_list);
71         udev_list_init(&udev_queue->failed_list);
72         return udev_queue;
73 }
74
75 /**
76  * udev_queue_ref:
77  * @udev_queue: udev queue context
78  *
79  * Take a reference of a udev queue context.
80  *
81  * Returns: the same udev queue context.
82  **/
83 struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue)
84 {
85         if (udev_queue == NULL)
86                 return NULL;
87         udev_queue->refcount++;
88         return udev_queue;
89 }
90
91 /**
92  * udev_queue_unref:
93  * @udev_queue: udev queue context
94  *
95  * Drop a reference of a udev queue context. If the refcount reaches zero,
96  * the resources of the queue context will be released.
97  **/
98 void udev_queue_unref(struct udev_queue *udev_queue)
99 {
100         if (udev_queue == NULL)
101                 return;
102         udev_queue->refcount--;
103         if (udev_queue->refcount > 0)
104                 return;
105         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->queue_list);
106         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list);
107         free(udev_queue);
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 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         char filename[UTIL_PATH_SIZE];
128         unsigned long long int seqnum;
129         int fd;
130         char buf[32];
131         ssize_t len;
132
133         util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL);
134         fd = open(filename, O_RDONLY|O_CLOEXEC);
135         if (fd < 0)
136                 return 0;
137         len = read(fd, buf, sizeof(buf));
138         close(fd);
139         if (len <= 2)
140                 return 0;
141         buf[len-1] = '\0';
142         seqnum = strtoull(buf, NULL, 10);
143         return seqnum;
144 }
145
146 /**
147  * udev_queue_get_kernel_seqnum:
148  * @udev_queue: udev queue context
149  *
150  * Returns: the current kernel event sequence number.
151  **/
152 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         dbg(udev_queue->udev, "seqnum=%llu\n", seqnum);
161         return seqnum;
162 }
163
164 int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum)
165 {
166         if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1)
167                 return -1;
168
169         return 0;
170 }
171
172 ssize_t udev_queue_skip_devpath(FILE *queue_file)
173 {
174         unsigned short int len;
175
176         if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) {
177                 char devpath[len];
178
179                 /* use fread to skip, fseek might drop buffered data */
180                 if (fread(devpath, 1, len, queue_file) == len)
181                         return len;
182         }
183
184         return -1;
185 }
186
187 ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size)
188 {
189         unsigned short int read_bytes = 0;
190         unsigned short int len;
191
192         if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1)
193                 return -1;
194
195         read_bytes = (len < size - 1) ? len : size - 1;
196         if (fread(devpath, 1, read_bytes, queue_file) != read_bytes)
197                 return -1;
198         devpath[read_bytes] = '\0';
199
200         /* if devpath was too long, skip unread characters */
201         if (read_bytes != len) {
202                 unsigned short int skip_bytes = len - read_bytes;
203                 char buf[skip_bytes];
204
205                 if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes)
206                         return -1;
207         }
208
209         return read_bytes;
210 }
211
212 static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start)
213 {
214         char filename[UTIL_PATH_SIZE];
215         FILE *queue_file;
216
217         util_strscpyl(filename, sizeof(filename), udev_get_run_path(udev_queue->udev), "/queue.bin", NULL);
218         queue_file = fopen(filename, "re");
219         if (queue_file == NULL)
220                 return NULL;
221
222         if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) {
223                 err(udev_queue->udev, "corrupt queue file\n");
224                 fclose(queue_file);
225                 return NULL;
226         }
227
228         return queue_file;
229 }
230
231 /**
232  * udev_queue_get_udev_seqnum:
233  * @udev_queue: udev queue context
234  *
235  * Returns: the last known udev event sequence number.
236  **/
237 unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
238 {
239         unsigned long long int seqnum_udev;
240         FILE *queue_file;
241
242         queue_file = open_queue_file(udev_queue, &seqnum_udev);
243         if (queue_file == NULL)
244                 return 0;
245
246         for (;;) {
247                 unsigned long long int seqnum;
248                 ssize_t devpath_len;
249
250                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
251                         break;
252                 devpath_len = udev_queue_skip_devpath(queue_file);
253                 if (devpath_len < 0)
254                         break;
255                 if (devpath_len > 0)
256                         seqnum_udev = seqnum;
257         }
258
259         fclose(queue_file);
260         return seqnum_udev;
261 }
262
263 /**
264  * udev_queue_get_udev_is_active:
265  * @udev_queue: udev queue context
266  *
267  * Returns: a flag indicating if udev is active.
268  **/
269 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  * Returns: a flag indicating if udev is currently handling events.
287  **/
288 int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
289 {
290         unsigned long long int seqnum_kernel;
291         unsigned long long int seqnum_udev = 0;
292         int queued = 0;
293         int is_empty = 0;
294         FILE *queue_file;
295
296         if (udev_queue == NULL)
297                 return -EINVAL;
298         queue_file = open_queue_file(udev_queue, &seqnum_udev);
299         if (queue_file == NULL)
300                 return 1;
301
302         for (;;) {
303                 unsigned long long int seqnum;
304                 ssize_t devpath_len;
305
306                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
307                         break;
308                 devpath_len = udev_queue_skip_devpath(queue_file);
309                 if (devpath_len < 0)
310                         break;
311
312                 if (devpath_len > 0) {
313                         queued++;
314                         seqnum_udev = seqnum;
315                 } else {
316                         queued--;
317                 }
318         }
319
320         if (queued > 0) {
321                 dbg(udev_queue->udev, "queue is not empty\n");
322                 goto out;
323         }
324
325         seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue);
326         if (seqnum_udev < seqnum_kernel) {
327                 dbg(udev_queue->udev, "queue is empty but kernel events still pending [%llu]<->[%llu]\n",
328                                         seqnum_kernel, seqnum_udev);
329                 goto out;
330         }
331
332         dbg(udev_queue->udev, "queue is empty\n");
333         is_empty = 1;
334
335 out:
336         fclose(queue_file);
337         return is_empty;
338 }
339
340 /**
341  * udev_queue_get_seqnum_sequence_is_finished:
342  * @udev_queue: udev queue context
343  * @start: first event sequence number
344  * @end: last event sequence number
345  *
346  * Returns: if any of the sequence numbers in the given range is currently active.
347  **/
348 int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
349                                                unsigned long long int start, unsigned long long int end)
350 {
351         unsigned long long int seqnum;
352         ssize_t devpath_len;
353         int unfinished;
354         FILE *queue_file;
355
356         if (udev_queue == NULL)
357                 return -EINVAL;
358         queue_file = open_queue_file(udev_queue, &seqnum);
359         if (queue_file == NULL)
360                 return 1;
361         if (start < seqnum)
362                 start = seqnum;
363         if (start > end) {
364                 fclose(queue_file);
365                 return 1;
366         }
367         if (end - start > INT_MAX - 1) {
368                 fclose(queue_file);
369                 return -EOVERFLOW;
370         }
371
372         /*
373          * we might start with 0, and handle the initial seqnum
374          * only when we find an entry in the queue file
375          **/
376         unfinished = end - start;
377
378         do {
379                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
380                         break;
381                 devpath_len = udev_queue_skip_devpath(queue_file);
382                 if (devpath_len < 0)
383                         break;
384
385                 /*
386                  * we might start with an empty or re-build queue file, where
387                  * the initial seqnum is not recorded as finished
388                  */
389                 if (start == seqnum && devpath_len > 0)
390                         unfinished++;
391
392                 if (devpath_len == 0) {
393                         if (seqnum >= start && seqnum <= end)
394                                 unfinished--;
395                 }
396         } while (unfinished > 0);
397
398         fclose(queue_file);
399
400         return (unfinished == 0);
401 }
402
403 /**
404  * udev_queue_get_seqnum_is_finished:
405  * @udev_queue: udev queue context
406  * @seqnum: sequence number
407  *
408  * Returns: a flag indicating if the given sequence number is handled.
409  **/
410 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         dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum);
416         return 1;
417 }
418
419 /**
420  * udev_queue_get_queued_list_entry:
421  * @udev_queue: udev queue context
422  *
423  * Returns: the first entry of the list of queued events.
424  **/
425 struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
426 {
427         unsigned long long int seqnum;
428         FILE *queue_file;
429
430         if (udev_queue == NULL)
431                 return NULL;
432         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->queue_list);
433
434         queue_file = open_queue_file(udev_queue, &seqnum);
435         if (queue_file == NULL)
436                 return NULL;
437
438         for (;;) {
439                 char syspath[UTIL_PATH_SIZE];
440                 char *s;
441                 size_t l;
442                 ssize_t len;
443                 char seqnum_str[32];
444                 struct udev_list_entry *list_entry;
445
446                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
447                         break;
448                 snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum);
449
450                 s = syspath;
451                 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
452                 len = udev_queue_read_devpath(queue_file, s, l);
453                 if (len < 0)
454                         break;
455
456                 if (len > 0) {
457                         udev_list_entry_add(udev_queue->udev, &udev_queue->queue_list, syspath, seqnum_str, 0, 0);
458                 } else {
459                         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) {
460                                 if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) {
461                                         udev_list_entry_delete(list_entry);
462                                         break;
463                                 }
464                         }
465                 }
466         }
467         fclose(queue_file);
468
469         return udev_list_get_entry(&udev_queue->queue_list);
470 }
471
472 /**
473  * udev_queue_get_failed_list_entry:
474  * @udev_queue: udev queue context
475  *
476  * Returns: the first entry of the list of recorded failed events.
477  **/
478 struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue)
479 {
480         char path[UTIL_PATH_SIZE];
481         DIR *dir;
482         struct dirent *dent;
483
484         if (udev_queue == NULL)
485                 return NULL;
486         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list);
487         util_strscpyl(path, sizeof(path), udev_get_run_path(udev_queue->udev), "/failed", NULL);
488         dir = opendir(path);
489         if (dir == NULL)
490                 return NULL;
491         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
492                 char filename[UTIL_PATH_SIZE];
493                 char syspath[UTIL_PATH_SIZE];
494                 char *s;
495                 size_t l;
496                 ssize_t len;
497                 struct stat statbuf;
498
499                 if (dent->d_name[0] == '.')
500                         continue;
501                 s = syspath;
502                 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
503                 len = readlinkat(dirfd(dir), dent->d_name, s, l);
504                 if (len <= 0 || (size_t)len == l)
505                         continue;
506                 s[len] = '\0';
507                 dbg(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name);
508                 util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
509                 if (stat(filename, &statbuf) != 0)
510                         continue;
511                 udev_list_entry_add(udev_queue->udev, &udev_queue->failed_list, syspath, NULL, 0, 0);
512         }
513         closedir(dir);
514         return udev_list_get_entry(&udev_queue->failed_list);
515 }