+++ /dev/null
-/*
- * libudev - interface to udev device information
- *
- * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include "libudev.h"
-#include "libudev-private.h"
-
-static size_t devpath_to_db_path(struct udev *udev, const char *devpath, char *filename, size_t len)
-{
- char *s;
- size_t l;
-
- s = filename;
- l = util_strpcpyl(&s, len, udev_get_dev_path(udev), "/.udev/db/", NULL);
- return util_path_encode(devpath, s, l);
-}
-
-int udev_device_update_db(struct udev_device *udev_device)
-{
- struct udev *udev = udev_device_get_udev(udev_device);
- char filename[UTIL_PATH_SIZE];
- FILE *f;
- char target[232]; /* on 64bit, tmpfs inlines up to 239 bytes */
- size_t devlen = strlen(udev_get_dev_path(udev))+1;
- char *s;
- size_t l;
- struct udev_list_entry *list_entry;
- int ret;
-
- devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename));
- util_create_path(udev, filename);
- unlink(filename);
-
- udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device))
- if (udev_list_entry_get_flag(list_entry))
- goto file;
- if (udev_device_get_num_fake_partitions(udev_device) != 0)
- goto file;
- if (udev_device_get_ignore_remove(udev_device))
- goto file;
- if (udev_device_get_devlink_priority(udev_device) != 0)
- goto file;
- if (udev_device_get_event_timeout(udev_device) >= 0)
- goto file;
- if (udev_device_get_watch_handle(udev_device) >= 0)
- goto file;
- if (udev_device_get_devnode(udev_device) == NULL)
- goto out;
-
- /*
- * if we have only the node and symlinks to store, try not to waste
- * tmpfs memory -- store values, if they fit, in a symlink target
- */
- s = target;
- l = util_strpcpy(&s, sizeof(target), &udev_device_get_devnode(udev_device)[devlen]);
- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device)) {
- l = util_strpcpyl(&s, l, " ", &udev_list_entry_get_name(list_entry)[devlen], NULL);
- if (l == 0) {
- info(udev, "size of links too large, create file\n");
- goto file;
- }
- }
- info(udev, "create db link (%s)\n", target);
- udev_selinux_setfscreatecon(udev, filename, S_IFLNK);
- ret = symlink(target, filename);
- udev_selinux_resetfscreatecon(udev);
- if (ret == 0)
- goto out;
-file:
- f = fopen(filename, "w");
- if (f == NULL) {
- err(udev, "unable to create db file '%s': %m\n", filename);
- return -1;
- }
- info(udev, "created db file for '%s' in '%s'\n", udev_device_get_devpath(udev_device), filename);
-
- if (udev_device_get_devnode(udev_device) != NULL) {
- fprintf(f, "N:%s\n", &udev_device_get_devnode(udev_device)[devlen]);
- udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device))
- fprintf(f, "S:%s\n", &udev_list_entry_get_name(list_entry)[devlen]);
- }
- if (udev_device_get_devlink_priority(udev_device) != 0)
- fprintf(f, "L:%u\n", udev_device_get_devlink_priority(udev_device));
- if (udev_device_get_event_timeout(udev_device) >= 0)
- fprintf(f, "T:%u\n", udev_device_get_event_timeout(udev_device));
- if (udev_device_get_num_fake_partitions(udev_device) != 0)
- fprintf(f, "A:%u\n", udev_device_get_num_fake_partitions(udev_device));
- if (udev_device_get_ignore_remove(udev_device))
- fprintf(f, "R:%u\n", udev_device_get_ignore_remove(udev_device));
- if (udev_device_get_watch_handle(udev_device) >= 0)
- fprintf(f, "W:%u\n", udev_device_get_watch_handle(udev_device));
- udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) {
- if (!udev_list_entry_get_flag(list_entry))
- continue;
- fprintf(f, "E:%s=%s\n",
- udev_list_entry_get_name(list_entry),
- udev_list_entry_get_value(list_entry));
- }
- fclose(f);
-out:
- return 0;
-}
-
-int udev_device_delete_db(struct udev_device *udev_device)
-{
- struct udev *udev = udev_device_get_udev(udev_device);
- char filename[UTIL_PATH_SIZE];
-
- devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename));
- unlink(filename);
- return 0;
-}
-
-int udev_device_rename_db(struct udev_device *udev_device, const char *devpath_old)
-{
- struct udev *udev = udev_device_get_udev(udev_device);
- char filename_old[UTIL_PATH_SIZE];
- char filename[UTIL_PATH_SIZE];
-
- devpath_to_db_path(udev, devpath_old, filename_old, sizeof(filename_old));
- devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename));
- return rename(filename_old, filename);
-}
--- /dev/null
+/*
+ * libudev - interface to udev device information
+ *
+ * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "libudev.h"
+#include "libudev-private.h"
+
+static size_t devpath_to_db_path(struct udev *udev, const char *devpath, char *filename, size_t len)
+{
+ char *s;
+ size_t l;
+
+ s = filename;
+ l = util_strpcpyl(&s, len, udev_get_dev_path(udev), "/.udev/db/", NULL);
+ return util_path_encode(devpath, s, l);
+}
+
+int udev_device_update_db(struct udev_device *udev_device)
+{
+ struct udev *udev = udev_device_get_udev(udev_device);
+ char filename[UTIL_PATH_SIZE];
+ FILE *f;
+ char target[232]; /* on 64bit, tmpfs inlines up to 239 bytes */
+ size_t devlen = strlen(udev_get_dev_path(udev))+1;
+ char *s;
+ size_t l;
+ struct udev_list_entry *list_entry;
+ int ret;
+
+ devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename));
+ util_create_path(udev, filename);
+ unlink(filename);
+
+ udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device))
+ if (udev_list_entry_get_flag(list_entry))
+ goto file;
+ if (udev_device_get_num_fake_partitions(udev_device) != 0)
+ goto file;
+ if (udev_device_get_ignore_remove(udev_device))
+ goto file;
+ if (udev_device_get_devlink_priority(udev_device) != 0)
+ goto file;
+ if (udev_device_get_event_timeout(udev_device) >= 0)
+ goto file;
+ if (udev_device_get_watch_handle(udev_device) >= 0)
+ goto file;
+ if (udev_device_get_devnode(udev_device) == NULL)
+ goto out;
+
+ /*
+ * if we have only the node and symlinks to store, try not to waste
+ * tmpfs memory -- store values, if they fit, in a symlink target
+ */
+ s = target;
+ l = util_strpcpy(&s, sizeof(target), &udev_device_get_devnode(udev_device)[devlen]);
+ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device)) {
+ l = util_strpcpyl(&s, l, " ", &udev_list_entry_get_name(list_entry)[devlen], NULL);
+ if (l == 0) {
+ info(udev, "size of links too large, create file\n");
+ goto file;
+ }
+ }
+ info(udev, "create db link (%s)\n", target);
+ udev_selinux_setfscreatecon(udev, filename, S_IFLNK);
+ ret = symlink(target, filename);
+ udev_selinux_resetfscreatecon(udev);
+ if (ret == 0)
+ goto out;
+file:
+ f = fopen(filename, "w");
+ if (f == NULL) {
+ err(udev, "unable to create db file '%s': %m\n", filename);
+ return -1;
+ }
+ info(udev, "created db file for '%s' in '%s'\n", udev_device_get_devpath(udev_device), filename);
+
+ if (udev_device_get_devnode(udev_device) != NULL) {
+ fprintf(f, "N:%s\n", &udev_device_get_devnode(udev_device)[devlen]);
+ udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(udev_device))
+ fprintf(f, "S:%s\n", &udev_list_entry_get_name(list_entry)[devlen]);
+ }
+ if (udev_device_get_devlink_priority(udev_device) != 0)
+ fprintf(f, "L:%u\n", udev_device_get_devlink_priority(udev_device));
+ if (udev_device_get_event_timeout(udev_device) >= 0)
+ fprintf(f, "T:%u\n", udev_device_get_event_timeout(udev_device));
+ if (udev_device_get_num_fake_partitions(udev_device) != 0)
+ fprintf(f, "A:%u\n", udev_device_get_num_fake_partitions(udev_device));
+ if (udev_device_get_ignore_remove(udev_device))
+ fprintf(f, "R:%u\n", udev_device_get_ignore_remove(udev_device));
+ if (udev_device_get_watch_handle(udev_device) >= 0)
+ fprintf(f, "W:%u\n", udev_device_get_watch_handle(udev_device));
+ udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device)) {
+ if (!udev_list_entry_get_flag(list_entry))
+ continue;
+ fprintf(f, "E:%s=%s\n",
+ udev_list_entry_get_name(list_entry),
+ udev_list_entry_get_value(list_entry));
+ }
+ fclose(f);
+out:
+ return 0;
+}
+
+int udev_device_delete_db(struct udev_device *udev_device)
+{
+ struct udev *udev = udev_device_get_udev(udev_device);
+ char filename[UTIL_PATH_SIZE];
+
+ devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename));
+ unlink(filename);
+ return 0;
+}
+
+int udev_device_rename_db(struct udev_device *udev_device, const char *devpath_old)
+{
+ struct udev *udev = udev_device_get_udev(udev_device);
+ char filename_old[UTIL_PATH_SIZE];
+ char filename[UTIL_PATH_SIZE];
+
+ devpath_to_db_path(udev, devpath_old, filename_old, sizeof(filename_old));
+ devpath_to_db_path(udev, udev_device_get_devpath(udev_device), filename, sizeof(filename));
+ return rename(filename_old, filename);
+}
int udev_device_set_watch_handle(struct udev_device *udev_device, int handle);
void udev_device_set_info_loaded(struct udev_device *device);
-/* libudev-device-db-write.c */
+/* libudev-device-private.c */
int udev_device_update_db(struct udev_device *udev_device);
int udev_device_delete_db(struct udev_device *udev_device);
int udev_device_rename_db(struct udev_device *udev_device, const char *devpath);
ssize_t udev_queue_read_devpath(FILE *queue_file, char *devpath, size_t size);
ssize_t udev_queue_skip_devpath(FILE *queue_file);
-/* libudev-queue-export.c */
+/* libudev-queue-private.c */
struct udev_queue_export *udev_queue_export_new(struct udev *udev);
void udev_queue_export_unref(struct udev_queue_export *udev_queue_export);
void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export);
+++ /dev/null
-/*
- * libudev - interface to udev device information
- *
- * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
- * Copyright (C) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- */
-
-/*
- * DISCLAIMER - The file format mentioned here is private to udev/libudev,
- * and may be changed without notice.
- *
- * The udev event queue is exported as a binary log file.
- * Each log record consists of a sequence number followed by the device path.
- *
- * When a new event is queued, its details are appended to the log.
- * When the event finishes, a second record is appended to the log
- * with the same sequence number but a null devpath.
- *
- * Example:
- * {1, "/devices/virtual/mem/null" },
- * {2, "/devices/virtual/mem/zero" },
- * {1, "" },
- * Event 2 is still queued, but event 1 has been finished
- *
- * The queue does not grow indefinitely. It is periodically re-created
- * to remove finished events. Atomic rename() makes this transparent to readers.
- *
- * The queue file starts with a single sequence number which specifies the
- * minimum sequence number in the log that follows. Any events prior to this
- * sequence number have already finished.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <limits.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <assert.h>
-
-#include "libudev.h"
-#include "libudev-private.h"
-
-static int rebuild_queue_file(struct udev_queue_export *udev_queue_export);
-
-struct udev_queue_export {
- struct udev *udev;
- int failed_count; /* number of failed events exported */
- int queued_count; /* number of unfinished events exported in queue file */
- FILE *queue_file;
- unsigned long long int seqnum_max; /* earliest sequence number in queue file */
- unsigned long long int seqnum_min; /* latest sequence number in queue file */
- int waste_bytes; /* queue file bytes wasted on finished events */
-};
-
-struct udev_queue_export *udev_queue_export_new(struct udev *udev)
-{
- struct udev_queue_export *udev_queue_export;
- unsigned long long int initial_seqnum;
-
- if (udev == NULL)
- return NULL;
-
- udev_queue_export = calloc(1, sizeof(struct udev_queue_export));
- if (udev_queue_export == NULL)
- return NULL;
- udev_queue_export->udev = udev;
-
- initial_seqnum = udev_get_kernel_seqnum(udev);
- udev_queue_export->seqnum_min = initial_seqnum;
- udev_queue_export->seqnum_max = initial_seqnum;
-
- udev_queue_export_cleanup(udev_queue_export);
- if (rebuild_queue_file(udev_queue_export) != 0) {
- free(udev_queue_export);
- return NULL;
- }
-
- return udev_queue_export;
-}
-
-void udev_queue_export_unref(struct udev_queue_export *udev_queue_export)
-{
- if (udev_queue_export == NULL)
- return;
- if (udev_queue_export->queue_file != NULL)
- fclose(udev_queue_export->queue_file);
- free(udev_queue_export);
-}
-
-void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export)
-{
- char filename[UTIL_PATH_SIZE];
-
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
- unlink(filename);
-
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
- unlink(filename);
-}
-
-static int skip_to(FILE *file, long offset)
-{
- long old_offset;
-
- /* fseek may drop buffered data, avoid it for small seeks */
- old_offset = ftell(file);
- if (offset > old_offset && offset - old_offset <= BUFSIZ) {
- size_t skip_bytes = offset - old_offset;
- char buf[skip_bytes];
-
- if (fread(buf, skip_bytes, 1, file) != skip_bytes)
- return -1;
- }
-
- return fseek(file, offset, SEEK_SET);
-}
-
-struct queue_devpaths {
- unsigned int devpaths_first; /* index of first queued event */
- unsigned int devpaths_size;
- long devpaths[]; /* seqnum -> offset of devpath in queue file (or 0) */
-};
-
-/*
- * Returns a table mapping seqnum to devpath file offset for currently queued events.
- * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min.
- */
-static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export)
-{
- struct queue_devpaths *devpaths;
- unsigned long long int range;
- long devpath_offset;
- ssize_t devpath_len;
- unsigned long long int seqnum;
- unsigned long long int n;
- unsigned int i;
-
- /* seek to the first event in the file */
- rewind(udev_queue_export->queue_file);
- udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum);
-
- /* allocate the table */
- range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max;
- if (range - 1 > INT_MAX) {
- err(udev_queue_export->udev, "queue file overflow\n");
- return NULL;
- }
- devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long));
- if (index == NULL)
- return NULL;
- devpaths->devpaths_size = range + 1;
-
- /* read all records and populate the table */
- while(1) {
- if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0)
- break;
- n = seqnum - udev_queue_export->seqnum_max;
- if (n >= devpaths->devpaths_size)
- goto read_error;
-
- devpath_offset = ftell(udev_queue_export->queue_file);
- devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file);
- if (devpath_len < 0)
- goto read_error;
-
- if (devpath_len > 0)
- devpaths->devpaths[n] = devpath_offset;
- else
- devpaths->devpaths[n] = 0;
- }
-
- /* find first queued event */
- for (i = 0; i < devpaths->devpaths_size; i++) {
- if (devpaths->devpaths[i] != 0)
- break;
- }
- devpaths->devpaths_first = i;
-
- return devpaths;
-
-read_error:
- err(udev_queue_export->udev, "queue file corrupted\n");
- free(devpaths);
- return NULL;
-}
-
-static int rebuild_queue_file(struct udev_queue_export *udev_queue_export)
-{
- unsigned long long int seqnum;
- struct queue_devpaths *devpaths = NULL;
- char filename[UTIL_PATH_SIZE];
- char filename_tmp[UTIL_PATH_SIZE];
- FILE *new_queue_file = NULL;
- unsigned int i;
-
- /* read old queue file */
- if (udev_queue_export->queue_file != NULL) {
- dbg(udev_queue_export->udev, "compacting queue file, freeing %d bytes\n",
- udev_queue_export->waste_bytes);
-
- devpaths = build_index(udev_queue_export);
- if (devpaths != NULL)
- udev_queue_export->seqnum_max += devpaths->devpaths_first;
- }
- if (devpaths == NULL) {
- dbg(udev_queue_export->udev, "creating empty queue file\n");
- udev_queue_export->queued_count = 0;
- udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
- }
-
- /* create new queue file */
- util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
- new_queue_file = fopen(filename_tmp, "w+");
- if (new_queue_file == NULL)
- goto error;
- seqnum = udev_queue_export->seqnum_max;
- fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file);
-
- /* copy unfinished events only to the new file */
- if (devpaths != NULL) {
- for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) {
- char devpath[UTIL_PATH_SIZE];
- int err;
- unsigned short devpath_len;
-
- if (devpaths->devpaths[i] != 0)
- {
- skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]);
- err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath));
- devpath_len = err;
-
- fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file);
- fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file);
- fwrite(devpath, 1, devpath_len, new_queue_file);
- }
- seqnum++;
- }
- free(devpaths);
- devpaths = NULL;
- }
- fflush(new_queue_file);
- if (ferror(new_queue_file))
- goto error;
-
- /* rename the new file on top of the old one */
- util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
- if (rename(filename_tmp, filename) != 0)
- goto error;
-
- if (udev_queue_export->queue_file != NULL)
- fclose(udev_queue_export->queue_file);
- udev_queue_export->queue_file = new_queue_file;
- udev_queue_export->waste_bytes = 0;
-
- return 0;
-
-error:
- err(udev_queue_export->udev, "failed to create queue file: %m\n");
- udev_queue_export_cleanup(udev_queue_export);
-
- if (udev_queue_export->queue_file != NULL) {
- fclose(udev_queue_export->queue_file);
- udev_queue_export->queue_file = NULL;
- }
- if (new_queue_file != NULL)
- fclose(new_queue_file);
-
- if (devpaths != NULL)
- free(devpaths);
- udev_queue_export->queued_count = 0;
- udev_queue_export->waste_bytes = 0;
- udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
-
- return -1;
-}
-
-static int write_queue_record(struct udev_queue_export *udev_queue_export,
- unsigned long long int seqnum, const char *devpath, size_t devpath_len)
-{
- unsigned short len;
-
- if (udev_queue_export->queue_file == NULL) {
- dbg(udev_queue_export->udev, "can't record event: queue file not available\n");
- return -1;
- }
-
- if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1)
- goto write_error;
-
- len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX;
- if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1)
- goto write_error;
- if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len)
- goto write_error;
-
- /* *must* flush output; caller may fork */
- if (fflush(udev_queue_export->queue_file) != 0)
- goto write_error;
-
- return 0;
-
-write_error:
- /* if we failed half way through writing a record to a file,
- we should not try to write any further records to it. */
- err(udev_queue_export->udev, "error writing to queue file: %m\n");
- fclose(udev_queue_export->queue_file);
- udev_queue_export->queue_file = NULL;
-
- return -1;
-}
-
-enum device_state {
- DEVICE_QUEUED,
- DEVICE_FINISHED,
- DEVICE_FAILED,
-};
-
-static inline size_t queue_record_size(size_t devpath_len)
-{
- return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len;
-}
-
-static int update_queue(struct udev_queue_export *udev_queue_export,
- struct udev_device *udev_device, enum device_state state)
-{
- unsigned long long int seqnum = udev_device_get_seqnum(udev_device);
- const char *devpath = NULL;
- size_t devpath_len = 0;
- int bytes;
- int err;
-
- if (state == DEVICE_QUEUED) {
- devpath = udev_device_get_devpath(udev_device);
- devpath_len = strlen(devpath);
- }
-
- /* recover from an earlier failed rebuild */
- if (udev_queue_export->queue_file == NULL) {
- if (rebuild_queue_file(udev_queue_export) != 0)
- return -1;
- }
-
- /* when the queue files grow too large, they must be garbage collected and rebuilt */
- bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len);
-
- /* if we're removing the last event from the queue, that's the best time to rebuild it */
- if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1 && bytes > 2048) {
- /* because we don't need to read the old queue file */
- fclose(udev_queue_export->queue_file);
- udev_queue_export->queue_file = NULL;
- rebuild_queue_file(udev_queue_export);
- return 0;
- }
-
- /* try to rebuild the queue files before they grow larger than one page. */
- if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096)
- rebuild_queue_file(udev_queue_export);
-
- /* don't record a finished event, if we already dropped the event in a failed rebuild */
- if (seqnum < udev_queue_export->seqnum_max)
- return 0;
-
- /* now write to the queue */
- if (state == DEVICE_QUEUED) {
- udev_queue_export->queued_count++;
- udev_queue_export->seqnum_min = seqnum;
- } else {
- udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0);
- udev_queue_export->queued_count--;
- }
- err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len);
-
- /* try to handle ENOSPC */
- if (err != 0 && udev_queue_export->queued_count == 0) {
- udev_queue_export_cleanup(udev_queue_export);
- err = rebuild_queue_file(udev_queue_export);
- }
-
- return err;
-}
-
-static void update_failed(struct udev_queue_export *udev_queue_export,
- struct udev_device *udev_device, enum device_state state)
-{
- struct udev *udev = udev_device_get_udev(udev_device);
- char filename[UTIL_PATH_SIZE];
- char *s;
- size_t l;
-
- if (state != DEVICE_FAILED && udev_queue_export->failed_count == 0)
- return;
-
- /* location of failed file */
- s = filename;
- l = util_strpcpyl(&s, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL);
- util_path_encode(udev_device_get_devpath(udev_device), s, l);
-
- switch (state) {
- case DEVICE_FAILED:
- /* record event in the failed directory */
- if (udev_queue_export->failed_count == 0)
- util_create_path(udev, filename);
- udev_queue_export->failed_count++;
-
- udev_selinux_setfscreatecon(udev, filename, S_IFLNK);
- symlink(udev_device_get_devpath(udev_device), filename);
- udev_selinux_resetfscreatecon(udev);
- break;
-
- case DEVICE_QUEUED:
- /* delete failed file */
- if (unlink(filename) == 0) {
- util_delete_path(udev, filename);
- udev_queue_export->failed_count--;
- }
- break;
-
- case DEVICE_FINISHED:
- if (udev_device_get_devpath_old(udev_device) != NULL) {
- /* "move" event - rename failed file to current name, do not delete failed */
- char filename_old[UTIL_PATH_SIZE];
-
- s = filename_old;
- l = util_strpcpyl(&s, sizeof(filename_old), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL);
- util_path_encode(udev_device_get_devpath_old(udev_device), s, l);
-
- if (rename(filename_old, filename) == 0)
- info(udev, "renamed devpath, moved failed state of '%s' to %s'\n",
- udev_device_get_devpath_old(udev_device), udev_device_get_devpath(udev_device));
- }
- break;
- }
-
- return;
-}
-
-static int update(struct udev_queue_export *udev_queue_export,
- struct udev_device *udev_device, enum device_state state)
-{
- update_failed(udev_queue_export, udev_device, state);
-
- if (update_queue(udev_queue_export, udev_device, state) != 0)
- return -1;
-
- return 0;
-}
-
-int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
-{
- return update(udev_queue_export, udev_device, DEVICE_QUEUED);
-}
-
-int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
-{
- return update(udev_queue_export, udev_device, DEVICE_FINISHED);
-}
-
-int udev_queue_export_device_failed(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
-{
- return update(udev_queue_export, udev_device, DEVICE_FAILED);
-}
--- /dev/null
+/*
+ * libudev - interface to udev device information
+ *
+ * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2009 Alan Jenkins <alan-jenkins@tuffmail.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+/*
+ * DISCLAIMER - The file format mentioned here is private to udev/libudev,
+ * and may be changed without notice.
+ *
+ * The udev event queue is exported as a binary log file.
+ * Each log record consists of a sequence number followed by the device path.
+ *
+ * When a new event is queued, its details are appended to the log.
+ * When the event finishes, a second record is appended to the log
+ * with the same sequence number but a null devpath.
+ *
+ * Example:
+ * {1, "/devices/virtual/mem/null" },
+ * {2, "/devices/virtual/mem/zero" },
+ * {1, "" },
+ * Event 2 is still queued, but event 1 has been finished
+ *
+ * The queue does not grow indefinitely. It is periodically re-created
+ * to remove finished events. Atomic rename() makes this transparent to readers.
+ *
+ * The queue file starts with a single sequence number which specifies the
+ * minimum sequence number in the log that follows. Any events prior to this
+ * sequence number have already finished.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <assert.h>
+
+#include "libudev.h"
+#include "libudev-private.h"
+
+static int rebuild_queue_file(struct udev_queue_export *udev_queue_export);
+
+struct udev_queue_export {
+ struct udev *udev;
+ int failed_count; /* number of failed events exported */
+ int queued_count; /* number of unfinished events exported in queue file */
+ FILE *queue_file;
+ unsigned long long int seqnum_max; /* earliest sequence number in queue file */
+ unsigned long long int seqnum_min; /* latest sequence number in queue file */
+ int waste_bytes; /* queue file bytes wasted on finished events */
+};
+
+struct udev_queue_export *udev_queue_export_new(struct udev *udev)
+{
+ struct udev_queue_export *udev_queue_export;
+ unsigned long long int initial_seqnum;
+
+ if (udev == NULL)
+ return NULL;
+
+ udev_queue_export = calloc(1, sizeof(struct udev_queue_export));
+ if (udev_queue_export == NULL)
+ return NULL;
+ udev_queue_export->udev = udev;
+
+ initial_seqnum = udev_get_kernel_seqnum(udev);
+ udev_queue_export->seqnum_min = initial_seqnum;
+ udev_queue_export->seqnum_max = initial_seqnum;
+
+ udev_queue_export_cleanup(udev_queue_export);
+ if (rebuild_queue_file(udev_queue_export) != 0) {
+ free(udev_queue_export);
+ return NULL;
+ }
+
+ return udev_queue_export;
+}
+
+void udev_queue_export_unref(struct udev_queue_export *udev_queue_export)
+{
+ if (udev_queue_export == NULL)
+ return;
+ if (udev_queue_export->queue_file != NULL)
+ fclose(udev_queue_export->queue_file);
+ free(udev_queue_export);
+}
+
+void udev_queue_export_cleanup(struct udev_queue_export *udev_queue_export)
+{
+ char filename[UTIL_PATH_SIZE];
+
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
+ unlink(filename);
+
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
+ unlink(filename);
+}
+
+static int skip_to(FILE *file, long offset)
+{
+ long old_offset;
+
+ /* fseek may drop buffered data, avoid it for small seeks */
+ old_offset = ftell(file);
+ if (offset > old_offset && offset - old_offset <= BUFSIZ) {
+ size_t skip_bytes = offset - old_offset;
+ char buf[skip_bytes];
+
+ if (fread(buf, skip_bytes, 1, file) != skip_bytes)
+ return -1;
+ }
+
+ return fseek(file, offset, SEEK_SET);
+}
+
+struct queue_devpaths {
+ unsigned int devpaths_first; /* index of first queued event */
+ unsigned int devpaths_size;
+ long devpaths[]; /* seqnum -> offset of devpath in queue file (or 0) */
+};
+
+/*
+ * Returns a table mapping seqnum to devpath file offset for currently queued events.
+ * devpaths[i] represents the event with seqnum = i + udev_queue_export->seqnum_min.
+ */
+static struct queue_devpaths *build_index(struct udev_queue_export *udev_queue_export)
+{
+ struct queue_devpaths *devpaths;
+ unsigned long long int range;
+ long devpath_offset;
+ ssize_t devpath_len;
+ unsigned long long int seqnum;
+ unsigned long long int n;
+ unsigned int i;
+
+ /* seek to the first event in the file */
+ rewind(udev_queue_export->queue_file);
+ udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum);
+
+ /* allocate the table */
+ range = udev_queue_export->seqnum_min - udev_queue_export->seqnum_max;
+ if (range - 1 > INT_MAX) {
+ err(udev_queue_export->udev, "queue file overflow\n");
+ return NULL;
+ }
+ devpaths = calloc(1, sizeof(struct queue_devpaths) + (range + 1) * sizeof(long));
+ if (index == NULL)
+ return NULL;
+ devpaths->devpaths_size = range + 1;
+
+ /* read all records and populate the table */
+ while(1) {
+ if (udev_queue_read_seqnum(udev_queue_export->queue_file, &seqnum) < 0)
+ break;
+ n = seqnum - udev_queue_export->seqnum_max;
+ if (n >= devpaths->devpaths_size)
+ goto read_error;
+
+ devpath_offset = ftell(udev_queue_export->queue_file);
+ devpath_len = udev_queue_skip_devpath(udev_queue_export->queue_file);
+ if (devpath_len < 0)
+ goto read_error;
+
+ if (devpath_len > 0)
+ devpaths->devpaths[n] = devpath_offset;
+ else
+ devpaths->devpaths[n] = 0;
+ }
+
+ /* find first queued event */
+ for (i = 0; i < devpaths->devpaths_size; i++) {
+ if (devpaths->devpaths[i] != 0)
+ break;
+ }
+ devpaths->devpaths_first = i;
+
+ return devpaths;
+
+read_error:
+ err(udev_queue_export->udev, "queue file corrupted\n");
+ free(devpaths);
+ return NULL;
+}
+
+static int rebuild_queue_file(struct udev_queue_export *udev_queue_export)
+{
+ unsigned long long int seqnum;
+ struct queue_devpaths *devpaths = NULL;
+ char filename[UTIL_PATH_SIZE];
+ char filename_tmp[UTIL_PATH_SIZE];
+ FILE *new_queue_file = NULL;
+ unsigned int i;
+
+ /* read old queue file */
+ if (udev_queue_export->queue_file != NULL) {
+ dbg(udev_queue_export->udev, "compacting queue file, freeing %d bytes\n",
+ udev_queue_export->waste_bytes);
+
+ devpaths = build_index(udev_queue_export);
+ if (devpaths != NULL)
+ udev_queue_export->seqnum_max += devpaths->devpaths_first;
+ }
+ if (devpaths == NULL) {
+ dbg(udev_queue_export->udev, "creating empty queue file\n");
+ udev_queue_export->queued_count = 0;
+ udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
+ }
+
+ /* create new queue file */
+ util_strscpyl(filename_tmp, sizeof(filename_tmp), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.tmp", NULL);
+ new_queue_file = fopen(filename_tmp, "w+");
+ if (new_queue_file == NULL)
+ goto error;
+ seqnum = udev_queue_export->seqnum_max;
+ fwrite(&seqnum, 1, sizeof(unsigned long long int), new_queue_file);
+
+ /* copy unfinished events only to the new file */
+ if (devpaths != NULL) {
+ for (i = devpaths->devpaths_first; i < devpaths->devpaths_size; i++) {
+ char devpath[UTIL_PATH_SIZE];
+ int err;
+ unsigned short devpath_len;
+
+ if (devpaths->devpaths[i] != 0)
+ {
+ skip_to(udev_queue_export->queue_file, devpaths->devpaths[i]);
+ err = udev_queue_read_devpath(udev_queue_export->queue_file, devpath, sizeof(devpath));
+ devpath_len = err;
+
+ fwrite(&seqnum, sizeof(unsigned long long int), 1, new_queue_file);
+ fwrite(&devpath_len, sizeof(unsigned short), 1, new_queue_file);
+ fwrite(devpath, 1, devpath_len, new_queue_file);
+ }
+ seqnum++;
+ }
+ free(devpaths);
+ devpaths = NULL;
+ }
+ fflush(new_queue_file);
+ if (ferror(new_queue_file))
+ goto error;
+
+ /* rename the new file on top of the old one */
+ util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/queue.bin", NULL);
+ if (rename(filename_tmp, filename) != 0)
+ goto error;
+
+ if (udev_queue_export->queue_file != NULL)
+ fclose(udev_queue_export->queue_file);
+ udev_queue_export->queue_file = new_queue_file;
+ udev_queue_export->waste_bytes = 0;
+
+ return 0;
+
+error:
+ err(udev_queue_export->udev, "failed to create queue file: %m\n");
+ udev_queue_export_cleanup(udev_queue_export);
+
+ if (udev_queue_export->queue_file != NULL) {
+ fclose(udev_queue_export->queue_file);
+ udev_queue_export->queue_file = NULL;
+ }
+ if (new_queue_file != NULL)
+ fclose(new_queue_file);
+
+ if (devpaths != NULL)
+ free(devpaths);
+ udev_queue_export->queued_count = 0;
+ udev_queue_export->waste_bytes = 0;
+ udev_queue_export->seqnum_max = udev_queue_export->seqnum_min;
+
+ return -1;
+}
+
+static int write_queue_record(struct udev_queue_export *udev_queue_export,
+ unsigned long long int seqnum, const char *devpath, size_t devpath_len)
+{
+ unsigned short len;
+
+ if (udev_queue_export->queue_file == NULL) {
+ dbg(udev_queue_export->udev, "can't record event: queue file not available\n");
+ return -1;
+ }
+
+ if (fwrite(&seqnum, sizeof(unsigned long long int), 1, udev_queue_export->queue_file) != 1)
+ goto write_error;
+
+ len = (devpath_len < USHRT_MAX) ? devpath_len : USHRT_MAX;
+ if (fwrite(&len, sizeof(unsigned short), 1, udev_queue_export->queue_file) != 1)
+ goto write_error;
+ if (fwrite(devpath, 1, len, udev_queue_export->queue_file) != len)
+ goto write_error;
+
+ /* *must* flush output; caller may fork */
+ if (fflush(udev_queue_export->queue_file) != 0)
+ goto write_error;
+
+ return 0;
+
+write_error:
+ /* if we failed half way through writing a record to a file,
+ we should not try to write any further records to it. */
+ err(udev_queue_export->udev, "error writing to queue file: %m\n");
+ fclose(udev_queue_export->queue_file);
+ udev_queue_export->queue_file = NULL;
+
+ return -1;
+}
+
+enum device_state {
+ DEVICE_QUEUED,
+ DEVICE_FINISHED,
+ DEVICE_FAILED,
+};
+
+static inline size_t queue_record_size(size_t devpath_len)
+{
+ return sizeof(unsigned long long int) + sizeof(unsigned short int) + devpath_len;
+}
+
+static int update_queue(struct udev_queue_export *udev_queue_export,
+ struct udev_device *udev_device, enum device_state state)
+{
+ unsigned long long int seqnum = udev_device_get_seqnum(udev_device);
+ const char *devpath = NULL;
+ size_t devpath_len = 0;
+ int bytes;
+ int err;
+
+ if (state == DEVICE_QUEUED) {
+ devpath = udev_device_get_devpath(udev_device);
+ devpath_len = strlen(devpath);
+ }
+
+ /* recover from an earlier failed rebuild */
+ if (udev_queue_export->queue_file == NULL) {
+ if (rebuild_queue_file(udev_queue_export) != 0)
+ return -1;
+ }
+
+ /* when the queue files grow too large, they must be garbage collected and rebuilt */
+ bytes = ftell(udev_queue_export->queue_file) + queue_record_size(devpath_len);
+
+ /* if we're removing the last event from the queue, that's the best time to rebuild it */
+ if (state != DEVICE_QUEUED && udev_queue_export->queued_count == 1 && bytes > 2048) {
+ /* because we don't need to read the old queue file */
+ fclose(udev_queue_export->queue_file);
+ udev_queue_export->queue_file = NULL;
+ rebuild_queue_file(udev_queue_export);
+ return 0;
+ }
+
+ /* try to rebuild the queue files before they grow larger than one page. */
+ if ((udev_queue_export->waste_bytes > bytes / 2) && bytes > 4096)
+ rebuild_queue_file(udev_queue_export);
+
+ /* don't record a finished event, if we already dropped the event in a failed rebuild */
+ if (seqnum < udev_queue_export->seqnum_max)
+ return 0;
+
+ /* now write to the queue */
+ if (state == DEVICE_QUEUED) {
+ udev_queue_export->queued_count++;
+ udev_queue_export->seqnum_min = seqnum;
+ } else {
+ udev_queue_export->waste_bytes += queue_record_size(devpath_len) + queue_record_size(0);
+ udev_queue_export->queued_count--;
+ }
+ err = write_queue_record(udev_queue_export, seqnum, devpath, devpath_len);
+
+ /* try to handle ENOSPC */
+ if (err != 0 && udev_queue_export->queued_count == 0) {
+ udev_queue_export_cleanup(udev_queue_export);
+ err = rebuild_queue_file(udev_queue_export);
+ }
+
+ return err;
+}
+
+static void update_failed(struct udev_queue_export *udev_queue_export,
+ struct udev_device *udev_device, enum device_state state)
+{
+ struct udev *udev = udev_device_get_udev(udev_device);
+ char filename[UTIL_PATH_SIZE];
+ char *s;
+ size_t l;
+
+ if (state != DEVICE_FAILED && udev_queue_export->failed_count == 0)
+ return;
+
+ /* location of failed file */
+ s = filename;
+ l = util_strpcpyl(&s, sizeof(filename), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL);
+ util_path_encode(udev_device_get_devpath(udev_device), s, l);
+
+ switch (state) {
+ case DEVICE_FAILED:
+ /* record event in the failed directory */
+ if (udev_queue_export->failed_count == 0)
+ util_create_path(udev, filename);
+ udev_queue_export->failed_count++;
+
+ udev_selinux_setfscreatecon(udev, filename, S_IFLNK);
+ symlink(udev_device_get_devpath(udev_device), filename);
+ udev_selinux_resetfscreatecon(udev);
+ break;
+
+ case DEVICE_QUEUED:
+ /* delete failed file */
+ if (unlink(filename) == 0) {
+ util_delete_path(udev, filename);
+ udev_queue_export->failed_count--;
+ }
+ break;
+
+ case DEVICE_FINISHED:
+ if (udev_device_get_devpath_old(udev_device) != NULL) {
+ /* "move" event - rename failed file to current name, do not delete failed */
+ char filename_old[UTIL_PATH_SIZE];
+
+ s = filename_old;
+ l = util_strpcpyl(&s, sizeof(filename_old), udev_get_dev_path(udev_queue_export->udev), "/.udev/failed/", NULL);
+ util_path_encode(udev_device_get_devpath_old(udev_device), s, l);
+
+ if (rename(filename_old, filename) == 0)
+ info(udev, "renamed devpath, moved failed state of '%s' to %s'\n",
+ udev_device_get_devpath_old(udev_device), udev_device_get_devpath(udev_device));
+ }
+ break;
+ }
+
+ return;
+}
+
+static int update(struct udev_queue_export *udev_queue_export,
+ struct udev_device *udev_device, enum device_state state)
+{
+ update_failed(udev_queue_export, udev_device, state);
+
+ if (update_queue(udev_queue_export, udev_device, state) != 0)
+ return -1;
+
+ return 0;
+}
+
+int udev_queue_export_device_queued(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
+{
+ return update(udev_queue_export, udev_device, DEVICE_QUEUED);
+}
+
+int udev_queue_export_device_finished(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
+{
+ return update(udev_queue_export, udev_device, DEVICE_FINISHED);
+}
+
+int udev_queue_export_device_failed(struct udev_queue_export *udev_queue_export, struct udev_device *udev_device)
+{
+ return update(udev_queue_export, udev_device, DEVICE_FAILED);
+}
../libudev/libudev-util.c \
../libudev/libudev-util-private.c \
../libudev/libudev-device.c \
- ../libudev/libudev-device-db-write.c \
+ ../libudev/libudev-device-private.c \
../libudev/libudev-monitor.c \
../libudev/libudev-enumerate.c \
../libudev/libudev-queue.c \
- ../libudev/libudev-queue-export.c \
+ ../libudev/libudev-queue-private.c \
../libudev/libudev-ctrl.c
if USE_SELINUX