2 * libudev - interface to udev device information
4 * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
5 * Copyright (C) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
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.
14 * DISCLAIMER - The file format mentioned here is private to udev/libudev,
15 * and may be changed without notice.
18 * The udev event queue is exported as a binary log file.
19 * Each log record consists of a sequence number followed by the device path.
21 * When a new event is queued, its details are appended to the log.
22 * When the event finishes, a second record is appended to the log
23 * with the same sequence number but a null devpath.
26 * {1, "/devices/virtual/mem/null" },
27 * {2, "/devices/virtual/mem/zero" },
29 * Event 2 is still queued, but event 1 has been finished
31 * The queue does not grow indefinitely. It is periodically re-created
32 * to remove finished events. Atomic rename() makes this transparent to readers.
35 * The queue file starts with a single sequence number which specifies the
36 * minimum sequence number in the log that follows. Any events prior to this
37 * sequence number have already finished.
48 #include <sys/types.h>
53 static int rebuild_queue_file(struct udev_queue_export *udev_queue_export);
55 struct udev_queue_export {
57 int failed_count; /* number of failed events exported */
58 int queued_count; /* number of unfinished events exported in queue file */
60 unsigned long long int seqnum_max; /* earliest sequence number in queue file */
61 unsigned long long int seqnum_min; /* latest sequence number in queue file */
62 int waste_bytes; /* queue file bytes wasted on finished events */
65 struct udev_queue_export *udev_queue_export_new(struct udev *udev)
67 struct udev_queue_export *udev_queue_export;
68 unsigned long long int initial_seqnum;
73 udev_queue_export = calloc(1, sizeof(struct udev_queue_export));
74 if (udev_queue_export == NULL)
76 udev_queue_export->udev = udev;
78 initial_seqnum = udev_get_kernel_seqnum(udev);
79 udev_queue_export->seqnum_min = initial_seqnum;
80 udev_queue_export->seqnum_max = initial_seqnum;
82 udev_queue_export_cleanup(udev_queue_export);
83 if (rebuild_queue_file(udev_queue_export) != 0) {
84 free(udev_queue_export);
88 return udev_queue_export;
91 void udev_queue_export_unref(struct udev_queue_export *udev_queue_export)
93 if (udev_queue_export == NULL)
95 if (udev_queue_export->queue_file != NULL)
96 fclose(udev_queue_export->queue_file);
97 free(udev_queue_export);
100 void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export)
102 char filename[UTIL_PATH_SIZE];
104 util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
107 util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
112 static int skip_to(FILE *file, long offset)
116 /* fseek may drop buffered data, avoid it for small seeks */
117 old_offset = ftell(file);
118 if (offset > old_offset && offset - old_offset <= BUFSIZ) {
119 size_t skip_bytes = offset - old_offset;
120 char buf[skip_bytes];
122 if (fread(buf, skip_bytes, 1, file) != skip_bytes)
126 return fseek(file, offset, SEEK_SET);
129 struct queue_devpaths {
130 unsigned int devpaths_first; /* index of first queued event */
131 unsigned int devpaths_size;
132 long devpaths[]; /* seqnum -> offset of devpath in queue file (or 0) */
136 * Returns a table mapping seqnum to devpath file offset for currently queued events.
137 * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min.
139 static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export)
141 struct queue_devpaths *devpaths;
142 unsigned long long int range;
145 unsigned long long int seqnum;
146 unsigned long long int n;
149 /* seek to the first event in the file */
150 rewind(udev_queue_export->queue_file);
151 udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum);
153 /* allocate the table */
154 range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max;
155 if (range - 1 > INT_MAX) {
156 err(udev_queue_export->udev, "queue file overflow\n");
159 devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long));
162 devpaths->devpaths_size = range + 1;
164 /* read all records and populate the table */
166 if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0)
168 n = seqnum - udev_queue_export->seqnum_max;
169 if (n >= devpaths->devpaths_size)
172 devpath_offset = ftell(udev_queue_export->queue_file);
173 devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file);
178 devpaths->devpaths[n] = devpath_offset;
180 devpaths->devpaths[n] = 0;
183 /* find first queued event */
184 for (i = 0; i < devpaths->devpaths_size; i++) {
185 if (devpaths->devpaths[i] != 0)
188 devpaths->devpaths_first = i;
193 err(udev_queue_export->udev, "queue file corrupted\n");
198 static int rebuild_queue_file(struct udev_queue_export *udev_queue_export)
200 unsigned long long int seqnum;
201 struct queue_devpaths *devpaths = NULL;
202 char filename[UTIL_PATH_SIZE];
203 char filename_tmp[UTIL_PATH_SIZE];
204 FILE *new_queue_file = NULL;
207 /* read old queue file */
208 if (udev_queue_export->queue_file != NULL) {
209 dbg(udev_queue_export->udev, "compacting queue file, freeing %d bytes\n",
210 udev_queue_export->waste_bytes);
212 devpaths = build_index(udev_queue_export);
213 if (devpaths != NULL)
214 udev_queue_export->seqnum_max += devpaths->devpaths_first;
216 if (devpaths == NULL) {
217 dbg(udev_queue_export->udev, "creating empty queue file\n");
218 udev_queue_export->queued_count = 0;
219 udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
222 /* create new queue file */
223 util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
224 new_queue_file = fopen(filename_tmp, "w+");
225 if (new_queue_file == NULL)
227 seqnum = udev_queue_export->seqnum_max;
228 fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file);
230 /* copy unfinished events only to the new file */
231 if (devpaths != NULL) {
232 for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) {
233 char devpath[UTIL_PATH_SIZE];
235 unsigned short devpath_len;
237 if (devpaths->devpaths[i] != 0)
239 skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]);
240 err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath));
243 fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file);
244 fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file);
245 fwrite(devpath, 1, devpath_len, new_queue_file);
252 fflush(new_queue_file);
253 if (ferror(new_queue_file))
256 /* rename the new file on top of the old one */
257 util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
258 if (rename(filename_tmp, filename) != 0)
261 if (udev_queue_export->queue_file != NULL)
262 fclose(udev_queue_export->queue_file);
263 udev_queue_export->queue_file = new_queue_file;
264 udev_queue_export->waste_bytes = 0;
269 err(udev_queue_export->udev, "failed to create queue file: %m\n");
270 udev_queue_export_cleanup(udev_queue_export);
272 if (udev_queue_export->queue_file != NULL) {
273 fclose(udev_queue_export->queue_file);
274 udev_queue_export->queue_file = NULL;
276 if (new_queue_file != NULL)
277 fclose(new_queue_file);
279 if (devpaths != NULL)
281 udev_queue_export->queued_count = 0;
282 udev_queue_export->waste_bytes = 0;
283 udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
288 static int write_queue_record(struct udev_queue_export *udev_queue_export,
289 unsigned long long int seqnum, const char *devpath, size_t devpath_len)
293 if (udev_queue_export->queue_file == NULL) {
294 dbg(udev_queue_export->udev, "can't record event: queue file not available\n");
298 if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1)
301 len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX;
302 if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1)
304 if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len)
307 /* *must* flush output; caller may fork */
308 if (fflush(udev_queue_export->queue_file) != 0)
314 /* if we failed half way through writing a record to a file,
315 we should not try to write any further records to it. */
316 err(udev_queue_export->udev, "error writing to queue file: %m\n");
317 fclose(udev_queue_export->queue_file);
318 udev_queue_export->queue_file = NULL;
330 static inline size_t queue_record_size(size_t devpath_len)
332 return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len;
335 static int update_queue(struct udev_queue_export *udev_queue_export,
336 struct udev_device *udev_device, enum device_state state)
338 unsigned long long int seqnum = udev_device_get_seqnum(udev_device);
339 const char *devpath = NULL;
340 size_t devpath_len = 0;
344 if (state == DEVICE_QUEUED) {
345 devpath = udev_device_get_devpath(udev_device);
346 devpath_len = strlen(devpath);
349 /* recover from an earlier failed rebuild */
350 if (udev_queue_export->queue_file == NULL) {
351 if (rebuild_queue_file(udev_queue_export) != 0)
355 /* when the queue files grow too large, they must be garbage collected and rebuilt */
356 bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len);
358 /* if we're removing the last event from the queue, that's the best time to rebuild it */
359 if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1 && bytes > 2048) {
360 /* because we don't need to read the old queue file */
361 fclose(udev_queue_export->queue_file);
362 udev_queue_export->queue_file = NULL;
363 rebuild_queue_file(udev_queue_export);
367 /* try to rebuild the queue files before they grow larger than one page. */
368 if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096)
369 rebuild_queue_file(udev_queue_export);
371 /* don't record a finished event, if we already dropped the event in a failed rebuild */
372 if (seqnum < udev_queue_export->seqnum_max)
375 /* now write to the queue */
376 if (state == DEVICE_QUEUED) {
377 udev_queue_export->queued_count++;
378 udev_queue_export->seqnum_min = seqnum;
380 udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0);
381 udev_queue_export->queued_count--;
383 err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len);
385 /* try to handle ENOSPC */
386 if (err != 0 && udev_queue_export->queued_count == 0) {
387 udev_queue_export_cleanup(udev_queue_export);
388 err = rebuild_queue_file(udev_queue_export);
394 static void update_failed(struct udev_queue_export *udev_queue_export,
395 struct udev_device *udev_device, enum device_state state)
397 struct udev *udev = udev_device_get_udev(udev_device);
398 char filename[UTIL_PATH_SIZE];
402 if (state != DEVICE_FAILED && udev_queue_export->failed_count == 0)
405 /* location of failed file */
407 l = util_strpcpyl(&s, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL);
408 util_path_encode(udev_device_get_devpath(udev_device), s, l);
412 /* record event in the failed directory */
413 if (udev_queue_export->failed_count == 0)
414 util_create_path(udev, filename);
415 udev_queue_export->failed_count++;
417 udev_selinux_setfscreatecon(udev, filename, S_IFLNK);
418 symlink(udev_device_get_devpath(udev_device), filename);
419 udev_selinux_resetfscreatecon(udev);
423 /* delete failed file */
424 if (unlink(filename) == 0) {
425 util_delete_path(udev, filename);
426 udev_queue_export->failed_count--;
430 case DEVICE_FINISHED:
431 if (udev_device_get_devpath_old(udev_device) != NULL) {
432 /* "move" event - rename failed file to current name, do not delete failed */
433 char filename_old[UTIL_PATH_SIZE];
436 l = util_strpcpyl(&s, sizeof(filename_old), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL);
437 util_path_encode(udev_device_get_devpath_old(udev_device), s, l);
439 if (rename(filename_old, filename) == 0)
440 info(udev, "renamed devpath, moved failed state of '%s' to %s'\n",
441 udev_device_get_devpath_old(udev_device), udev_device_get_devpath(udev_device));
449 static int update(struct udev_queue_export *udev_queue_export,
450 struct udev_device *udev_device, enum device_state state)
452 update_failed(udev_queue_export, udev_device, state);
454 if (update_queue(udev_queue_export, udev_device, state) != 0)
460 int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
462 return update(udev_queue_export, udev_device, DEVICE_QUEUED);
465 int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
467 return update(udev_queue_export, udev_device, DEVICE_FINISHED);
470 int udev_queue_export_device_failed(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
472 return update(udev_queue_export, udev_device, DEVICE_FAILED);