]>
Commit | Line | Data |
---|---|---|
64ccdf82 KS |
1 | /* |
2 | * libudev - interface to udev device information | |
3 | * | |
4 | * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org> | |
5 | * | |
4061ab9f KS |
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. | |
64ccdf82 KS |
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; | |
8cd2e972 KS |
29 | struct udev_list_node queue_list; |
30 | struct udev_list_node failed_list; | |
64ccdf82 KS |
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 | ||
b29a5e4a | 40 | udev_queue = calloc(1, sizeof(struct udev_queue)); |
64ccdf82 KS |
41 | if (udev_queue == NULL) |
42 | return NULL; | |
64ccdf82 KS |
43 | udev_queue->refcount = 1; |
44 | udev_queue->udev = udev; | |
8cd2e972 KS |
45 | udev_list_init(&udev_queue->queue_list); |
46 | udev_list_init(&udev_queue->failed_list); | |
64ccdf82 KS |
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; | |
eb8837e1 KS |
65 | udev_list_cleanup_entries(udev_queue->udev, &udev_queue->queue_list); |
66 | udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list); | |
64ccdf82 KS |
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; | |
065db052 | 87 | util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev_queue->udev), "/kernel/uevent_seqnum", NULL); |
64ccdf82 KS |
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); | |
86b57788 | 97 | dbg(udev_queue->udev, "seqnum=%llu\n", seqnum); |
64ccdf82 KS |
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; | |
065db052 | 111 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue->udev), "/.udev/uevent_seqnum", NULL); |
64ccdf82 KS |
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); | |
86b57788 | 121 | dbg(udev_queue->udev, "seqnum=%llu\n", seqnum); |
64ccdf82 KS |
122 | udev_queue->last_seen_udev_seqnum = seqnum; |
123 | return seqnum; | |
124 | } | |
125 | ||
11d5eec2 KS |
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; | |
065db052 | 133 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue->udev), "/.udev/uevent_seqnum", NULL); |
11d5eec2 KS |
134 | if (stat(filename, &statbuf) == 0) |
135 | return 1; | |
136 | return 0; | |
137 | } | |
138 | ||
64ccdf82 KS |
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; | |
065db052 | 147 | util_strscpyl(queuename, sizeof(queuename), udev_get_dev_path(udev_queue->udev), "/.udev/queue", NULL); |
64ccdf82 | 148 | if (stat(queuename, &statbuf) == 0) { |
86b57788 | 149 | dbg(udev_queue->udev, "queue is not empty\n"); |
64ccdf82 KS |
150 | return 0; |
151 | } | |
152 | seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue); | |
153 | if (seqnum_kernel <= udev_queue->last_seen_udev_seqnum) { | |
86b57788 | 154 | dbg(udev_queue->udev, "queue is empty\n"); |
64ccdf82 KS |
155 | return 1; |
156 | } | |
11d5eec2 KS |
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; | |
64ccdf82 | 161 | if (seqnum_kernel <= udev_queue->last_seen_udev_seqnum) { |
86b57788 | 162 | dbg(udev_queue->udev, "queue is empty\n"); |
64ccdf82 KS |
163 | return 1; |
164 | } | |
86b57788 | 165 | dbg(udev_queue->udev, "queue is empty, but kernel events still pending [%llu]<->[%llu]\n", |
64ccdf82 KS |
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; | |
34f55e1d | 177 | /* did it reach the queue? */ |
8c3ae785 KS |
178 | if (seqnum > udev_queue->last_seen_udev_seqnum) |
179 | if (seqnum > udev_queue_get_udev_seqnum(udev_queue)) | |
64ccdf82 | 180 | return 0; |
34f55e1d | 181 | /* is it still in the queue? */ |
64ccdf82 KS |
182 | snprintf(filename, sizeof(filename), "%s/.udev/queue/%llu", |
183 | udev_get_dev_path(udev_queue->udev), seqnum); | |
a5d8cffa | 184 | if (lstat(filename, &statbuf) == 0) |
64ccdf82 | 185 | return 0; |
86b57788 | 186 | dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum); |
64ccdf82 KS |
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; | |
eb8837e1 | 198 | udev_list_cleanup_entries(udev_queue->udev, &udev_queue->queue_list); |
065db052 | 199 | util_strscpyl(path, sizeof(path), udev_get_dev_path(udev_queue->udev), "/.udev/queue", NULL); |
64ccdf82 KS |
200 | dir = opendir(path); |
201 | if (dir == NULL) | |
202 | return NULL; | |
203 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | |
64ccdf82 | 204 | char syspath[UTIL_PATH_SIZE]; |
065db052 KS |
205 | char *s; |
206 | size_t l; | |
64ccdf82 KS |
207 | ssize_t len; |
208 | ||
209 | if (dent->d_name[0] == '.') | |
210 | continue; | |
065db052 KS |
211 | s = syspath; |
212 | l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL); | |
e6c1a2bd | 213 | len = readlinkat(dirfd(dir), dent->d_name, s, l); |
065db052 | 214 | if (len < 0 || (size_t)len >= l) |
64ccdf82 | 215 | continue; |
065db052 | 216 | s[len] = '\0'; |
86b57788 | 217 | dbg(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name); |
8cd2e972 | 218 | udev_list_entry_add(udev_queue->udev, &udev_queue->queue_list, syspath, dent->d_name, 0, 0); |
64ccdf82 KS |
219 | } |
220 | closedir(dir); | |
8cd2e972 | 221 | return udev_list_get_entry(&udev_queue->queue_list); |
64ccdf82 KS |
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; | |
eb8837e1 | 232 | udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list); |
065db052 | 233 | util_strscpyl(path, sizeof(path), udev_get_dev_path(udev_queue->udev), "/.udev/failed", NULL); |
64ccdf82 KS |
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]; | |
065db052 KS |
240 | char *s; |
241 | size_t l; | |
64ccdf82 | 242 | ssize_t len; |
065db052 | 243 | struct stat statbuf; |
64ccdf82 KS |
244 | |
245 | if (dent->d_name[0] == '.') | |
246 | continue; | |
065db052 KS |
247 | s = syspath; |
248 | l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL); | |
e6c1a2bd | 249 | len = readlinkat(dirfd(dir), dent->d_name, s, l); |
065db052 | 250 | if (len < 0 || (size_t)len >= l) |
64ccdf82 | 251 | continue; |
065db052 | 252 | s[len] = '\0'; |
86b57788 | 253 | dbg(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name); |
065db052 | 254 | util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL); |
64ccdf82 KS |
255 | if (stat(filename, &statbuf) != 0) |
256 | continue; | |
8cd2e972 | 257 | udev_list_entry_add(udev_queue->udev, &udev_queue->failed_list, syspath, NULL, 0, 0); |
64ccdf82 KS |
258 | } |
259 | closedir(dir); | |
8cd2e972 | 260 | return udev_list_get_entry(&udev_queue->failed_list); |
64ccdf82 KS |
261 | } |
262 | ||
8cd2e972 | 263 | int udev_queue_export_udev_seqnum(struct udev_queue *udev_queue, unsigned long long int seqnum) |
64ccdf82 | 264 | { |
8cd2e972 | 265 | return -1; |
64ccdf82 KS |
266 | } |
267 | ||
37ed4f56 | 268 | int udev_queue_export_device_queued(struct udev_queue *udev_queue, struct udev_device *udev_device) |
64ccdf82 | 269 | { |
8cd2e972 | 270 | return -1; |
64ccdf82 KS |
271 | } |
272 | ||
37ed4f56 | 273 | int udev_queue_export_device_finished(struct udev_queue *udev_queue, struct udev_device *udev_device) |
64ccdf82 | 274 | { |
8cd2e972 | 275 | return -1; |
64ccdf82 KS |
276 | } |
277 | ||
37ed4f56 | 278 | int udev_queue_export_device_failed(struct udev_queue *udev_queue, struct udev_device *udev_device) |
64ccdf82 | 279 | { |
8cd2e972 | 280 | return -1; |
64ccdf82 | 281 | } |