chiark / gitweb /
libudev: doc - add section headers
[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 event asynchronously. All events wich do not have
32  * interdependencies are run in parallel. This exports the current state of the
33  * event processing queue, and the currently 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 struct udev_queue *udev_queue_new(struct udev *udev)
50 {
51         struct udev_queue *udev_queue;
52
53         if (udev == NULL)
54                 return NULL;
55
56         udev_queue = calloc(1, sizeof(struct udev_queue));
57         if (udev_queue == NULL)
58                 return NULL;
59         udev_queue->refcount = 1;
60         udev_queue->udev = udev;
61         udev_list_init(&udev_queue->queue_list);
62         udev_list_init(&udev_queue->failed_list);
63         return udev_queue;
64 }
65
66 struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue)
67 {
68         if (udev_queue == NULL)
69                 return NULL;
70         udev_queue->refcount++;
71         return udev_queue;
72 }
73
74 void udev_queue_unref(struct udev_queue *udev_queue)
75 {
76         if (udev_queue == NULL)
77                 return;
78         udev_queue->refcount--;
79         if (udev_queue->refcount > 0)
80                 return;
81         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->queue_list);
82         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list);
83         free(udev_queue);
84 }
85
86 struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
87 {
88         if (udev_queue == NULL)
89                 return NULL;
90         return udev_queue->udev;
91 }
92
93 unsigned long long int udev_get_kernel_seqnum(struct udev *udev)
94 {
95         char filename[UTIL_PATH_SIZE];
96         unsigned long long int seqnum;
97         int fd;
98         char buf[32];
99         ssize_t len;
100
101         util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL);
102         fd = open(filename, O_RDONLY);
103         if (fd < 0)
104                 return 0;
105         len = read(fd, buf, sizeof(buf));
106         close(fd);
107         if (len <= 2)
108                 return 0;
109         buf[len-1] = '\0';
110         seqnum = strtoull(buf, NULL, 10);
111         return seqnum;
112 }
113
114 unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
115 {
116         unsigned long long int seqnum;
117
118         if (udev_queue == NULL)
119                 return -EINVAL;
120
121         seqnum = udev_get_kernel_seqnum(udev_queue->udev);
122         dbg(udev_queue->udev, "seqnum=%llu\n", seqnum);
123         return seqnum;
124 }
125
126 int udev_queue_read_seqnum(FILE *queue_file, unsigned long long int *seqnum)
127 {
128         if (fread(seqnum, sizeof(unsigned long long int), 1, queue_file) != 1)
129                 return -1;
130
131         return 0;
132 }
133
134 ssize_t udev_queue_skip_devpath(FILE *queue_file)
135 {
136         unsigned short int len;
137
138         if (fread(&len, sizeof(unsigned short int), 1, queue_file) == 1) {
139                 char devpath[len];
140
141                 /* use fread to skip, fseek might drop buffered data */
142                 if (fread(devpath, 1, len, queue_file) == len)
143                         return len;
144         }
145
146         return -1;
147 }
148
149 ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size)
150 {
151         unsigned short int read_bytes = 0;
152         unsigned short int len;
153
154         if (fread(&len, sizeof(unsigned short int), 1, queue_file) != 1)
155                 return -1;
156
157         read_bytes = (len < size - 1) ? len : size - 1;
158         if (fread(devpath, 1, read_bytes, queue_file) != read_bytes)
159                 return -1;
160         devpath[read_bytes] = '\0';
161
162         /* if devpath was too long, skip unread characters */
163         if (read_bytes != len) {
164                 unsigned short int skip_bytes = len - read_bytes;
165                 char buf[skip_bytes];
166
167                 if (fread(buf, 1, skip_bytes, queue_file) != skip_bytes)
168                         return -1;
169         }
170
171         return read_bytes;
172 }
173
174 static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start)
175 {
176         char filename[UTIL_PATH_SIZE];
177         FILE *queue_file;
178
179         util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue->udev), "/.udev/queue.bin", NULL);
180         queue_file = fopen(filename, "r");
181         if (queue_file == NULL)
182                 return NULL;
183
184         if (udev_queue_read_seqnum(queue_file, seqnum_start) < 0) {
185                 err(udev_queue->udev, "corrupt queue file\n");
186                 fclose(queue_file);
187                 return NULL;
188         }
189
190         return queue_file;
191 }
192
193
194 unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
195 {
196         unsigned long long int seqnum_udev;
197         FILE *queue_file;
198
199         queue_file = open_queue_file(udev_queue, &seqnum_udev);
200         if (queue_file == NULL)
201                 return 0;
202
203         while (1) {
204                 unsigned long long int seqnum;
205                 ssize_t devpath_len;
206
207                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
208                         break;
209                 devpath_len = udev_queue_skip_devpath(queue_file);
210                 if (devpath_len < 0)
211                         break;
212                 if (devpath_len > 0)
213                         seqnum_udev = seqnum;
214         }
215
216         fclose(queue_file);
217         return seqnum_udev;
218 }
219
220 int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
221 {
222         unsigned long long int seqnum_start;
223         FILE *queue_file;
224
225         queue_file = open_queue_file(udev_queue, &seqnum_start);
226         if (queue_file == NULL)
227                 return 0;
228
229         fclose(queue_file);
230         return 1;
231 }
232
233 int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
234 {
235         unsigned long long int seqnum_kernel;
236         unsigned long long int seqnum_udev = 0;
237         int queued = 0;
238         int is_empty = 0;
239         FILE *queue_file;
240
241         if (udev_queue == NULL)
242                 return -EINVAL;
243         queue_file = open_queue_file(udev_queue, &seqnum_udev);
244         if (queue_file == NULL)
245                 return 1;
246
247         while (1) {
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
257                 if (devpath_len > 0) {
258                         queued++;
259                         seqnum_udev = seqnum;
260                 } else {
261                         queued--;
262                 }
263         }
264
265         if (queued > 0) {
266                 dbg(udev_queue->udev, "queue is not empty\n");
267                 goto out;
268         }
269
270         seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue);
271         if (seqnum_udev < seqnum_kernel) {
272                 dbg(udev_queue->udev, "queue is empty but kernel events still pending [%llu]<->[%llu]\n",
273                                         seqnum_kernel, seqnum_udev);
274                 goto out;
275         }
276
277         dbg(udev_queue->udev, "queue is empty\n");
278         is_empty = 1;
279
280 out:
281         fclose(queue_file);
282         return is_empty;
283 }
284
285 int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
286                                                unsigned long long int start, unsigned long long int end)
287 {
288         unsigned long long int seqnum = 0;
289         ssize_t devpath_len;
290         int unfinished;
291         FILE *queue_file;
292
293         if (udev_queue == NULL)
294                 return -EINVAL;
295         queue_file = open_queue_file(udev_queue, &seqnum);
296         if (queue_file == NULL)
297                 return 1;
298         if (start < seqnum)
299                 start = seqnum;
300         if (start > end)
301                 return 1;
302         if (end - start > INT_MAX - 1)
303                 return -EOVERFLOW;
304         unfinished = (end - start) + 1;
305
306         while (unfinished > 0) {
307                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
308                         break;
309                 devpath_len = udev_queue_skip_devpath(queue_file);
310                 if (devpath_len < 0)
311                         break;
312
313                 if (devpath_len == 0) {
314                         if (seqnum >= start && seqnum <= end)
315                                 unfinished--;
316                 }
317         }
318         fclose(queue_file);
319
320         return (unfinished == 0);
321 }
322
323 int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
324 {
325         if (!udev_queue_get_seqnum_sequence_is_finished(udev_queue, seqnum, seqnum))
326                 return 0;
327
328         dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum);
329         return 1;
330 }
331
332 struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
333 {
334         unsigned long long int seqnum;
335         FILE *queue_file;
336
337         if (udev_queue == NULL)
338                 return NULL;
339         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->queue_list);
340
341         queue_file = open_queue_file(udev_queue, &seqnum);
342         if (queue_file == NULL)
343                 return NULL;
344
345         while (1) {
346                 char syspath[UTIL_PATH_SIZE];
347                 char *s;
348                 size_t l;
349                 ssize_t len;
350                 char seqnum_str[32];
351                 struct udev_list_entry *list_entry;
352
353                 if (udev_queue_read_seqnum(queue_file, &seqnum) < 0)
354                         break;
355                 snprintf(seqnum_str, sizeof(seqnum_str), "%llu", seqnum);
356
357                 s = syspath;
358                 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
359                 len = udev_queue_read_devpath(queue_file, s, l);
360                 if (len < 0)
361                         break;
362
363                 if (len > 0) {
364                         udev_list_entry_add(udev_queue->udev, &udev_queue->queue_list, syspath, seqnum_str, 0, 0);
365                 } else {
366                         udev_list_entry_foreach(list_entry, udev_list_get_entry(&udev_queue->queue_list)) {
367                                 if (strcmp(seqnum_str, udev_list_entry_get_value(list_entry)) == 0) {
368                                         udev_list_entry_delete(list_entry);
369                                         break;
370                                 }
371                         }
372                 }
373         }
374         fclose(queue_file);
375
376         return udev_list_get_entry(&udev_queue->queue_list);
377 }
378
379 struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue)
380 {
381         char path[UTIL_PATH_SIZE];
382         DIR *dir;
383         struct dirent *dent;
384
385         if (udev_queue == NULL)
386                 return NULL;
387         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list);
388         util_strscpyl(path, sizeof(path), udev_get_dev_path(udev_queue->udev), "/.udev/failed", NULL);
389         dir = opendir(path);
390         if (dir == NULL)
391                 return NULL;
392         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
393                 char filename[UTIL_PATH_SIZE];
394                 char syspath[UTIL_PATH_SIZE];
395                 char *s;
396                 size_t l;
397                 ssize_t len;
398                 struct stat statbuf;
399
400                 if (dent->d_name[0] == '.')
401                         continue;
402                 s = syspath;
403                 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
404                 len = readlinkat(dirfd(dir), dent->d_name, s, l);
405                 if (len < 0 || (size_t)len >= l)
406                         continue;
407                 s[len] = '\0';
408                 dbg(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name);
409                 util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
410                 if (stat(filename, &statbuf) != 0)
411                         continue;
412                 udev_list_entry_add(udev_queue->udev, &udev_queue->failed_list, syspath, NULL, 0, 0);
413         }
414         closedir(dir);
415         return udev_list_get_entry(&udev_queue->failed_list);
416 }