]> git.ipfire.org Git - thirdparty/systemd.git/blame - libudev/libudev-queue.c
libudev: update documentation
[thirdparty/systemd.git] / libudev / libudev-queue.c
CommitLineData
64ccdf82
KS
1/*
2 * libudev - interface to udev device information
3 *
4 * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
f503f6b2 5 * Copyright (C) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
64ccdf82 6 *
4061ab9f
KS
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.
64ccdf82
KS
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>
f503f6b2 21#include <limits.h>
64ccdf82
KS
22#include <sys/stat.h>
23
24#include "libudev.h"
25#include "libudev-private.h"
26
27struct udev_queue {
28 struct udev *udev;
29 int refcount;
8cd2e972
KS
30 struct udev_list_node queue_list;
31 struct udev_list_node failed_list;
64ccdf82
KS
32};
33
34struct udev_queue *udev_queue_new(struct udev *udev)
35{
36 struct udev_queue *udev_queue;
37
38 if (udev == NULL)
39 return NULL;
40
b29a5e4a 41 udev_queue = calloc(1, sizeof(struct udev_queue));
64ccdf82
KS
42 if (udev_queue == NULL)
43 return NULL;
64ccdf82
KS
44 udev_queue->refcount = 1;
45 udev_queue->udev = udev;
8cd2e972
KS
46 udev_list_init(&udev_queue->queue_list);
47 udev_list_init(&udev_queue->failed_list);
64ccdf82
KS
48 return udev_queue;
49}
50
51struct 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
59void 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;
eb8837e1
KS
66 udev_list_cleanup_entries(udev_queue->udev, &udev_queue->queue_list);
67 udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list);
64ccdf82
KS
68 free(udev_queue);
69}
70
71struct 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
f503f6b2 78unsigned long long int udev_get_kernel_seqnum(struct udev *udev)
64ccdf82
KS
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
f503f6b2 86 util_strscpyl(filename, sizeof(filename), udev_get_sys_path(udev), "/kernel/uevent_seqnum", NULL);
64ccdf82
KS
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);
64ccdf82
KS
96 return seqnum;
97}
98
f503f6b2 99unsigned long long int udev_queue_get_kernel_seqnum(struct udev_queue *udev_queue)
64ccdf82 100{
64ccdf82 101 unsigned long long int seqnum;
64ccdf82
KS
102
103 if (udev_queue == NULL)
104 return -EINVAL;
f503f6b2
AJ
105
106 seqnum = udev_get_kernel_seqnum(udev_queue->udev);
86b57788 107 dbg(udev_queue->udev, "seqnum=%llu\n", seqnum);
64ccdf82
KS
108 return seqnum;
109}
110
f503f6b2
AJ
111int 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
119ssize_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
134ssize_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
159static FILE *open_queue_file(struct udev_queue *udev_queue, unsigned long long int *seqnum_start)
11d5eec2
KS
160{
161 char filename[UTIL_PATH_SIZE];
f503f6b2 162 FILE *queue_file;
11d5eec2 163
f503f6b2
AJ
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
179unsigned 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)
11d5eec2 186 return 0;
f503f6b2
AJ
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
205int 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;
11d5eec2
KS
216}
217
64ccdf82
KS
218int udev_queue_get_queue_is_empty(struct udev_queue *udev_queue)
219{
64ccdf82 220 unsigned long long int seqnum_kernel;
f503f6b2
AJ
221 unsigned long long int seqnum_udev = 0;
222 int queued = 0;
223 int is_empty = 0;
224 FILE *queue_file;
64ccdf82
KS
225
226 if (udev_queue == NULL)
227 return -EINVAL;
f503f6b2
AJ
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) {
86b57788 251 dbg(udev_queue->udev, "queue is not empty\n");
f503f6b2 252 goto out;
64ccdf82 253 }
f503f6b2 254
64ccdf82 255 seqnum_kernel = udev_queue_get_kernel_seqnum(udev_queue);
f503f6b2
AJ
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;
64ccdf82 260 }
f503f6b2
AJ
261
262 dbg(udev_queue->udev, "queue is empty\n");
263 is_empty = 1;
264
265out:
266 fclose(queue_file);
267 return is_empty;
64ccdf82
KS
268}
269
f503f6b2
AJ
270int udev_queue_get_seqnum_sequence_is_finished(struct udev_queue *udev_queue,
271 unsigned long long int start, unsigned long long int end)
64ccdf82 272{
f503f6b2
AJ
273 unsigned long long int seqnum = 0;
274 ssize_t devpath_len;
275 int unfinished;
276 FILE *queue_file;
64ccdf82
KS
277
278 if (udev_queue == NULL)
279 return -EINVAL;
f503f6b2
AJ
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
308int 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))
64ccdf82 311 return 0;
f503f6b2 312
86b57788 313 dbg(udev_queue->udev, "seqnum: %llu finished\n", seqnum);
64ccdf82
KS
314 return 1;
315}
316
317struct udev_list_entry *udev_queue_get_queued_list_entry(struct udev_queue *udev_queue)
318{
f503f6b2
AJ
319 unsigned long long int seqnum;
320 FILE *queue_file;
64ccdf82
KS
321
322 if (udev_queue == NULL)
323 return NULL;
eb8837e1 324 udev_list_cleanup_entries(udev_queue->udev, &udev_queue->queue_list);
f503f6b2
AJ
325
326 queue_file = open_queue_file(udev_queue, &seqnum);
327 if (queue_file == NULL)
64ccdf82 328 return NULL;
f503f6b2
AJ
329
330 while (1) {
64ccdf82 331 char syspath[UTIL_PATH_SIZE];
065db052
KS
332 char *s;
333 size_t l;
64ccdf82 334 ssize_t len;
f503f6b2
AJ
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);
64ccdf82 341
065db052
KS
342 s = syspath;
343 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
f503f6b2
AJ
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 }
64ccdf82 358 }
f503f6b2
AJ
359 fclose(queue_file);
360
8cd2e972 361 return udev_list_get_entry(&udev_queue->queue_list);
64ccdf82
KS
362}
363
364struct 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;
eb8837e1 372 udev_list_cleanup_entries(udev_queue->udev, &udev_queue->failed_list);
065db052 373 util_strscpyl(path, sizeof(path), udev_get_dev_path(udev_queue->udev), "/.udev/failed", NULL);
64ccdf82
KS
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];
065db052
KS
380 char *s;
381 size_t l;
64ccdf82 382 ssize_t len;
065db052 383 struct stat statbuf;
64ccdf82
KS
384
385 if (dent->d_name[0] == '.')
386 continue;
065db052
KS
387 s = syspath;
388 l = util_strpcpyl(&s, sizeof(syspath), udev_get_sys_path(udev_queue->udev), NULL);
e6c1a2bd 389 len = readlinkat(dirfd(dir), dent->d_name, s, l);
065db052 390 if (len < 0 || (size_t)len >= l)
64ccdf82 391 continue;
065db052 392 s[len] = '\0';
86b57788 393 dbg(udev_queue->udev, "found '%s' [%s]\n", syspath, dent->d_name);
065db052 394 util_strscpyl(filename, sizeof(filename), syspath, "/uevent", NULL);
64ccdf82
KS
395 if (stat(filename, &statbuf) != 0)
396 continue;
8cd2e972 397 udev_list_entry_add(udev_queue->udev, &udev_queue->failed_list, syspath, NULL, 0, 0);
64ccdf82
KS
398 }
399 closedir(dir);
8cd2e972 400 return udev_list_get_entry(&udev_queue->failed_list);
64ccdf82 401}