chiark / gitweb /
use openat(), unlinkat(), fstatat()
[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 syspath[UTIL_PATH_SIZE];
205                 char *s;
206                 size_t l;
207                 ssize_t len;
208
209                 if (dent->d_name[0] == '.')
210                         continue;
211                 s = syspath;
212                 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
213                 len = readlinkat(dirfd(dir), dent->d_name, s, l);
214                 if (len < 0 || (size_t)len >= l)
215                         continue;
216                 s[len] = '\0';
217                 dbg(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name);
218                 udev_list_entry_add(udev_queue->udev, &udev_queue->queue_list, syspath, dent->d_name, 0, 0);
219         }
220         closedir(dir);
221         return udev_list_get_entry(&udev_queue->queue_list);
222 }
223
224 struct udev_list_entry *udev_queue_get_failed_list_entry(struct udev_queue *udev_queue)
225 {
226         char path[UTIL_PATH_SIZE];
227         DIR *dir;
228         struct dirent *dent;
229
230         if (udev_queue == NULL)
231                 return NULL;
232         udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list);
233         util_strscpyl(path, sizeof(path), udev_get_dev_path(udev_queue->udev), "/.udev/failed", NULL);
234         dir = opendir(path);
235         if (dir == NULL)
236                 return NULL;
237         for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) {
238                 char filename[UTIL_PATH_SIZE];
239                 char syspath[UTIL_PATH_SIZE];
240                 char *s;
241                 size_t l;
242                 ssize_t len;
243                 struct stat statbuf;
244
245                 if (dent->d_name[0] == '.')
246                         continue;
247                 s = syspath;
248                 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
249                 len = readlinkat(dirfd(dir), dent->d_name, s, l);
250                 if (len < 0 || (size_t)len >= l)
251                         continue;
252                 s[len] = '\0';
253                 dbg(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name);
254                 util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
255                 if (stat(filename, &statbuf) != 0)
256                         continue;
257                 udev_list_entry_add(udev_queue->udev, &udev_queue->failed_list, syspath, NULL, 0, 0);
258         }
259         closedir(dir);
260         return udev_list_get_entry(&udev_queue->failed_list);
261 }
262
263 int udev_queue_export_udev_seqnum(struct udev_queue *udev_queue, unsigned long long int seqnum)
264 {
265         return -1;
266 }
267
268 int udev_queue_export_device_queued(struct udev_queue *udev_queue, struct udev_device *udev_device)
269 {
270         return -1;
271 }
272
273 int udev_queue_export_device_finished(struct udev_queue *udev_queue, struct udev_device *udev_device)
274 {
275         return -1;
276 }
277
278 int udev_queue_export_device_failed(struct udev_queue *udev_queue, struct udev_device *udev_device)
279 {
280         return -1;
281 }