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