chiark / gitweb /
use more efficient string copying
[elogind.git] / udev / lib / libudev-queue.c
1 /*
2  * libudev - interface to udev device information
3  *
4  * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <string.h>
18 #include <dirent.h>
19 #include <fcntl.h>
20 #include <sys/stat.h>
21
22 #include "libudev.h"
23 #include "libudev-private.h"
24
25 struct udev_queue {
26         struct udev *udev;
27         int refcount;
28         unsigned long long int last_seen_udev_seqnum;
29         struct udev_list_node queue_list;
30         struct udev_list_node failed_list;
31 };
32
33 struct udev_queue *udev_queue_new(struct udev *udev)
34 {
35         struct udev_queue *udev_queue;
36
37         if (udev == NULL)
38                 return NULL;
39
40         udev_queue = calloc(1, sizeof(struct udev_queue));
41         if (udev_queue == NULL)
42                 return NULL;
43         udev_queue->refcount = 1;
44         udev_queue->udev = udev;
45         udev_list_init(&udev_queue->queue_list);
46         udev_list_init(&udev_queue->failed_list);
47         return udev_queue;
48 }
49
50 struct udev_queue *udev_queue_ref(struct udev_queue *udev_queue)
51 {
52         if (udev_queue == NULL)
53                 return NULL;
54         udev_queue->refcount++;
55         return udev_queue;
56 }
57
58 void udev_queue_unref(struct udev_queue *udev_queue)
59 {
60         if (udev_queue == NULL)
61                 return;
62         udev_queue->refcount--;
63         if (udev_queue->refcount > 0)
64                 return;
65         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->queue_list);
66         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list);
67         free(udev_queue);
68 }
69
70 struct udev *udev_queue_get_udev(struct udev_queue *udev_queue)
71 {
72         if (udev_queue == NULL)
73                 return NULL;
74         return udev_queue->udev;
75 }
76
77 unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
78 {
79         char filename[UTIL_PATH_SIZE];
80         unsigned long long int seqnum;
81         int fd;
82         char buf[32];
83         ssize_t len;
84
85         if (udev_queue == NULL)
86                 return -EINVAL;
87         util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev_queue->udev), "/kernel/uevent_seqnum", NULL);
88         fd = open(filename, O_RDONLY);
89         if (fd < 0)
90                 return 0;
91         len = read(fd, buf, sizeof(buf));
92         close(fd);
93         if (len <= 2)
94                 return 0;
95         buf[len-1] = '\0';
96         seqnum = strtoull(buf, NULL, 10);
97         dbg(udev_queue->udev, "seqnum=%llu\n", seqnum);
98         return seqnum;
99 }
100
101 unsigned long long int udev_queue_get_udev_seqnum(struct udev_queue *udev_queue)
102 {
103         char filename[UTIL_PATH_SIZE];
104         unsigned long long int seqnum;
105         int fd;
106         char buf[32];
107         ssize_t len;
108
109         if (udev_queue == NULL)
110                 return -EINVAL;
111         util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue->udev), "/.udev/uevent_seqnum", NULL);
112         fd = open(filename, O_RDONLY);
113         if (fd < 0)
114                 return 0;
115         len = read(fd, buf, sizeof(buf));
116         close(fd);
117         if (len <= 2)
118                 return 0;
119         buf[len-1] = '\0';
120         seqnum = strtoull(buf, NULL, 10);
121         dbg(udev_queue->udev, "seqnum=%llu\n", seqnum);
122         udev_queue->last_seen_udev_seqnum = seqnum;
123         return seqnum;
124 }
125
126 int udev_queue_get_udev_is_active(struct udev_queue *udev_queue)
127 {
128         char filename[UTIL_PATH_SIZE];
129         struct stat statbuf;
130
131         if (udev_queue == NULL)
132                 return 0;
133         util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue->udev), "/.udev/uevent_seqnum", NULL);
134         if (stat(filename, &statbuf) == 0)
135                 return 1;
136         return 0;
137 }
138
139 int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
140 {
141         char queuename[UTIL_PATH_SIZE];
142         struct stat statbuf;
143         unsigned long long int seqnum_kernel;
144
145         if (udev_queue == NULL)
146                 return -EINVAL;
147         util_strscpyl(queuename, sizeof(queuename), udev_get_dev_path(udev_queue->udev), "/.udev/queue", NULL);
148         if (stat(queuename, &statbuf) == 0) {
149                 dbg(udev_queue->udev, "queue is not empty\n");
150                 return 0;
151         }
152         seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue);
153         if (seqnum_kernel <= udev_queue->last_seen_udev_seqnum) {
154                 dbg(udev_queue->udev, "queue is empty\n");
155                 return 1;
156         }
157         /* update udev seqnum, and check if udev is still running */
158         if (udev_queue_get_udev_seqnum(udev_queue) == 0)
159                 if (!udev_queue_get_udev_is_active(udev_queue))
160                         return 1;
161         if (seqnum_kernel <= udev_queue->last_seen_udev_seqnum) {
162                 dbg(udev_queue->udev, "queue is empty\n");
163                 return 1;
164         }
165         dbg(udev_queue->udev, "queue is empty, but kernel events still pending [%llu]<->[%llu]\n",
166              seqnum_kernel, udev_queue->last_seen_udev_seqnum);
167         return 0;
168 }
169
170 int udev_queue_get_seqnum_is_finished(struct udev_queue *udev_queue, unsigned long long int seqnum)
171 {
172         char filename[UTIL_PATH_SIZE];
173         struct stat statbuf;
174
175         if (udev_queue == NULL)
176                 return -EINVAL;
177         /* did it reach the queue? */
178         if (seqnum > udev_queue->last_seen_udev_seqnum)
179                 if (seqnum > udev_queue_get_udev_seqnum(udev_queue))
180                         return 0;
181         /* is it still in the queue? */
182         snprintf(filename, sizeof(filename), "%s/.udev/queue/%llu",
183                  udev_get_dev_path(udev_queue->udev), seqnum);
184         if (lstat(filename, &statbuf) == 0)
185                 return 0;
186         dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum);
187         return 1;
188 }
189
190 struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
191 {
192         char path[UTIL_PATH_SIZE];
193         DIR *dir;
194         struct dirent *dent;
195
196         if (udev_queue == NULL)
197                 return NULL;
198         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->queue_list);
199         util_strscpyl(path, sizeof(path), udev_get_dev_path(udev_queue->udev), "/.udev/queue", NULL);
200         dir = opendir(path);
201         if (dir == NULL)
202                 return NULL;
203         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
204                 char filename[UTIL_PATH_SIZE];
205                 char syspath[UTIL_PATH_SIZE];
206                 char *s;
207                 size_t l;
208                 ssize_t len;
209
210                 if (dent->d_name[0] == '.')
211                         continue;
212                 util_strscpyl(filename, sizeof(filename), path, "/", dent->d_name, NULL);
213                 s = syspath;
214                 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
215                 len = readlink(filename, s, l);
216                 if (len < 0 || (size_t)len >= l)
217                         continue;
218                 s[len] = '\0';
219                 dbg(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name);
220                 udev_list_entry_add(udev_queue->udev, &udev_queue->queue_list, syspath, dent->d_name, 0, 0);
221         }
222         closedir(dir);
223         return udev_list_get_entry(&udev_queue->queue_list);
224 }
225
226 struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue)
227 {
228         char path[UTIL_PATH_SIZE];
229         DIR *dir;
230         struct dirent *dent;
231
232         if (udev_queue == NULL)
233                 return NULL;
234         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list);
235         util_strscpyl(path, sizeof(path), udev_get_dev_path(udev_queue->udev), "/.udev/failed", NULL);
236         dir = opendir(path);
237         if (dir == NULL)
238                 return NULL;
239         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
240                 char filename[UTIL_PATH_SIZE];
241                 char syspath[UTIL_PATH_SIZE];
242                 char *s;
243                 size_t l;
244                 ssize_t len;
245                 struct stat statbuf;
246
247                 if (dent->d_name[0] == '.')
248                         continue;
249                 util_strscpyl(filename, sizeof(filename), path, "/", dent->d_name, NULL);
250                 s = syspath;
251                 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
252                 len = readlink(filename, s, l);
253                 if (len < 0 || (size_t)len >= l)
254                         continue;
255                 s[len] = '\0';
256                 dbg(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name);
257                 util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
258                 if (stat(filename, &statbuf) != 0)
259                         continue;
260                 udev_list_entry_add(udev_queue->udev, &udev_queue->failed_list, syspath, NULL, 0, 0);
261         }
262         closedir(dir);
263         return udev_list_get_entry(&udev_queue->failed_list);
264 }
265
266 int udev_queue_export_udev_seqnum(struct udev_queue *udev_queue, unsigned long long int seqnum)
267 {
268         return -1;
269 }
270
271 int udev_queue_export_device_queued(struct udev_queue *udev_queue, struct udev_device *udev_device)
272 {
273         return -1;
274 }
275
276 int udev_queue_export_device_finished(struct udev_queue *udev_queue, struct udev_device *udev_device)
277 {
278         return -1;
279 }
280
281 int udev_queue_export_device_failed(struct udev_queue *udev_queue, struct udev_device *udev_device)
282 {
283         return -1;
284 }