chiark / gitweb /
udevd: use dev_t or netif ifindex as database key
authorKay Sievers <kay.sievers@vrfy.org>
Fri, 10 Dec 2010 00:13:35 +0000 (01:13 +0100)
committerKay Sievers <kay.sievers@vrfy.org>
Fri, 10 Dec 2010 00:18:15 +0000 (01:18 +0100)
We need to preserve the database of network interfaces while we
rename them. Use the kernel's numbers wherever possible, instead
of the device names.

Fix wrong database filenames which contain a '/', translated
from '!' in the kernel name.

Fix segfault for kobject pathes where the subsystem can not be
determined from sysfs.

Makefile.am
libudev/libudev-device-private.c
libudev/libudev-device.c
libudev/libudev-private.h
udev/udev-event.c
udev/udev-node.c
udev/udev-watch.c
udev/udevadm-info.c
udev/udevadm.xml

index 891440c..9fe4a3a 100644 (file)
@@ -35,7 +35,7 @@ DISTCHECK_HOOKS =
 # libudev
 # ------------------------------------------------------------------------------
 LIBUDEV_CURRENT=9
-LIBUDEV_REVISION=3
+LIBUDEV_REVISION=4
 LIBUDEV_AGE=9
 
 SUBDIRS += libudev/docs
index a4b2fb0..63f947e 100644 (file)
 
 static void udev_device_tag(struct udev_device *dev, const char *tag, bool add)
 {
+       const char *id;
        struct udev *udev = udev_device_get_udev(dev);
        char filename[UTIL_PATH_SIZE];
 
-       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/tags/", tag, "/",
-                     udev_device_get_subsystem(dev), ":", udev_device_get_sysname(dev), NULL);
+       id = udev_device_get_id_filename(dev);
+       if (id == NULL)
+               return;
+       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/tags/", tag, "/", id, NULL);
 
        if (add) {
                util_create_path(udev, filename);
                symlink(udev_device_get_devpath(dev), filename);
-               /* possibly cleanup old entries after a device renaming */
-               if (udev_device_get_sysname_old(dev) != NULL) {
-                       char filename_old[UTIL_PATH_SIZE];
-
-                       util_strscpyl(filename_old, sizeof(filename_old), udev_get_dev_path(udev), "/.udev/tags/", tag, "/",
-                                     udev_device_get_subsystem(dev), ":", udev_device_get_sysname_old(dev), NULL);
-                       unlink(filename_old);
-               }
        } else {
                unlink(filename);
        }
@@ -79,6 +74,7 @@ int udev_device_tag_index(struct udev_device *dev, struct udev_device *dev_old,
 
 int udev_device_update_db(struct udev_device *udev_device)
 {
+       const char *id;
        struct udev *udev = udev_device_get_udev(udev_device);
        char filename[UTIL_PATH_SIZE];
        char filename_tmp[UTIL_PATH_SIZE];
@@ -90,8 +86,10 @@ int udev_device_update_db(struct udev_device *udev_device)
        struct udev_list_entry *list_entry;
        int ret;
 
-       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db/",
-                     udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname(udev_device), NULL);
+       id = udev_device_get_id_filename(udev_device);
+       if (id == NULL)
+               return -1;
+       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db/", id, NULL);
        util_strscpyl(filename_tmp, sizeof(filename_tmp), filename, ".tmp", NULL);
 
        udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev_device))
@@ -169,11 +167,14 @@ out:
 
 int udev_device_delete_db(struct udev_device *udev_device)
 {
+       const char *id;
        struct udev *udev = udev_device_get_udev(udev_device);
        char filename[UTIL_PATH_SIZE];
 
-       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db/",
-                     udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname(udev_device), NULL);
+       id = udev_device_get_id_filename(udev_device);
+       if (id == NULL)
+               return -1;
+       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/db/", id, NULL);
        unlink(filename);
        return 0;
 }
index 9b5d79f..d87b0c6 100644 (file)
@@ -54,6 +54,7 @@ struct udev_device {
        char *devpath_old;
        char *sysname_old;
        char *knodename;
+       char *id_filename;
        char **envp;
        char *monitor_buf;
        size_t monitor_buf_len;
@@ -230,6 +231,7 @@ const char *udev_device_get_property_value(struct udev_device *udev_device, cons
 
 int udev_device_read_db(struct udev_device *udev_device)
 {
+       const char *id;
        struct stat stats;
        char filename[UTIL_PATH_SIZE];
        char line[UTIL_LINE_SIZE];
@@ -238,8 +240,10 @@ int udev_device_read_db(struct udev_device *udev_device)
        if (udev_device->db_loaded)
                return 0;
 
-       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/.udev/db/",
-                     udev_device_get_subsystem(udev_device), ":", udev_device_get_sysname(udev_device), NULL);
+       id = udev_device_get_id_filename(udev_device);
+       if (id == NULL)
+               return -1;
+       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/.udev/db/", id, NULL);
 
        if (lstat(filename, &stats) != 0) {
                dbg(udev_device->udev, "no db file to read %s: %m\n", filename);
@@ -799,6 +803,7 @@ void udev_device_unref(struct udev_device *udev_device)
        free(udev_device->devpath_old);
        free(udev_device->sysname_old);
        free(udev_device->knodename);
+       free(udev_device->id_filename);
        free(udev_device->envp);
        free(udev_device->monitor_buf);
        dbg(udev_device->udev, "udev_device: %p released\n", udev_device);
@@ -1287,6 +1292,40 @@ int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink
        return 0;
 }
 
+const char *udev_device_get_id_filename(struct udev_device *udev_device)
+{
+       if (udev_device->id_filename == NULL) {
+               if (udev_device_get_subsystem(udev_device) == NULL)
+                       return NULL;
+
+               if (major(udev_device_get_devnum(udev_device)) > 0) {
+                       /* use dev_t -- b259:131072, c254:0 */
+                       if (asprintf(&udev_device->id_filename, "%c%u:%u",
+                                    strcmp(udev_device_get_subsystem(udev_device), "block") == 0 ? 'b' : 'c',
+                                    major(udev_device_get_devnum(udev_device)),
+                                    minor(udev_device_get_devnum(udev_device))) < 0)
+                               udev_device->id_filename = NULL;
+               } else if (strcmp(udev_device_get_subsystem(udev_device), "net") == 0) {
+                       /* use netdev ifindex -- n3 */
+                       if (asprintf(&udev_device->id_filename, "n%u", udev_device_get_ifindex(udev_device)) < 0)
+                               udev_device->id_filename = NULL;
+               } else {
+                       /*
+                        * use $subsys:$syname -- pci:0000:00:1f.2
+                        * sysname() has '!' translated, get it from devpath
+                        */
+                       const char *sysname;
+                       sysname = strrchr(udev_device->devpath, '/');
+                       if (sysname == NULL)
+                               return NULL;
+                       sysname = &sysname[1];
+                       if (asprintf(&udev_device->id_filename, "+%s:%s", udev_device_get_subsystem(udev_device), sysname) < 0)
+                               udev_device->id_filename = NULL;
+               }
+       }
+       return udev_device->id_filename;
+}
+
 int udev_device_add_tag(struct udev_device *udev_device, const char *tag)
 {
        if (strchr(tag, ':') != NULL || strchr(tag, ' ') != NULL)
@@ -1500,7 +1539,11 @@ int udev_device_get_event_timeout(struct udev_device *udev_device)
 
 int udev_device_set_event_timeout(struct udev_device *udev_device, int event_timeout)
 {
+       char num[32];
+
        udev_device->event_timeout = event_timeout;
+       snprintf(num, sizeof(num), "%u", event_timeout);
+       udev_device_add_property(udev_device, "TIMEOUT", num);
        return 0;
 }
 
@@ -1560,6 +1603,10 @@ int udev_device_get_ifindex(struct udev_device *udev_device)
 
 int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex)
 {
+       char num[32];
+
        udev_device->ifindex = ifindex;
+       snprintf(num, sizeof(num), "%u", ifindex);
+       udev_device_add_property(udev_device, "IFINDEX", num);
        return 0;
 }
index c9ed462..d09a9a5 100644 (file)
@@ -85,6 +85,7 @@ const char *udev_device_get_devpath_old(struct udev_device *udev_device);
 const char *udev_device_get_sysname_old(struct udev_device *udev_device);
 int udev_device_set_devpath_old(struct udev_device *udev_device, const char *devpath_old);
 const char *udev_device_get_knodename(struct udev_device *udev_device);
+const char *udev_device_get_id_filename(struct udev_device *udev_device);
 int udev_device_add_tag(struct udev_device *udev_device, const char *tag);
 void udev_device_cleanup_tags_list(struct udev_device *udev_device);
 int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
index 0648735..f41f06b 100644 (file)
@@ -535,6 +535,9 @@ int udev_event_execute_rules(struct udev_event *event, struct udev_rules *rules)
        struct udev_device *dev = event->dev;
        int err = 0;
 
+       if (udev_device_get_subsystem(dev) == NULL)
+               return -1;
+
        if (strcmp(udev_device_get_action(dev), "remove") == 0) {
                udev_device_read_db(dev);
                udev_device_delete_db(dev);
index c8113f1..9263442 100644 (file)
@@ -298,10 +298,8 @@ static void link_update(struct udev_device *dev, const char *slink, bool add)
        dbg(udev, "update symlink '%s' of '%s'\n", slink, udev_device_get_syspath(dev));
 
        util_path_encode(&slink[strlen(udev_get_dev_path(udev))+1], name_enc, sizeof(name_enc));
-       snprintf(dirname, sizeof(dirname), "%s/.udev/links/%s", udev_get_dev_path(udev), name_enc);
-       snprintf(filename, sizeof(filename), "%s/%c%u:%u", dirname,
-                strcmp(udev_device_get_subsystem(dev), "block") == 0 ? 'b' : 'c',
-                major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
+       util_strscpyl(dirname, sizeof(dirname), udev_get_dev_path(udev), "/.udev/links/", name_enc, NULL);
+       util_strscpyl(filename, sizeof(filename), dirname, "/", udev_device_get_id_filename(dev), NULL);
 
        if (!add) {
                dbg(udev, "removing index: '%s'\n", filename);
index 5fa6010..9e1b8d8 100644 (file)
@@ -109,7 +109,6 @@ unlink:
 void udev_watch_begin(struct udev *udev, struct udev_device *dev)
 {
        char filename[UTIL_PATH_SIZE];
-       char majmin[UTIL_PATH_SIZE];
        int wd;
 
        if (inotify_fd < 0)
@@ -123,13 +122,10 @@ void udev_watch_begin(struct udev *udev, struct udev_device *dev)
                return;
        }
 
-       snprintf(majmin, sizeof(majmin), "%c%i:%i",
-                strcmp(udev_device_get_subsystem(dev), "block") == 0 ? 'b' : 'c',
-                major(udev_device_get_devnum(dev)), minor(udev_device_get_devnum(dev)));
        snprintf(filename, sizeof(filename), "%s/.udev/watch/%d", udev_get_dev_path(udev), wd);
        util_create_path(udev, filename);
        unlink(filename);
-       symlink(majmin, filename);
+       symlink(udev_device_get_id_filename(dev), filename);
 
        udev_device_set_watch_handle(dev, wd);
 }
index b3b31eb..7206f4f 100644 (file)
@@ -195,6 +195,60 @@ static int export_devices(struct udev *udev)
        return 0;
 }
 
+static int convert_db(struct udev *udev)
+{
+       struct udev_enumerate *udev_enumerate;
+       struct udev_list_entry *list_entry;
+
+       udev_enumerate = udev_enumerate_new(udev);
+       if (udev_enumerate == NULL)
+               return -1;
+       udev_enumerate_scan_devices(udev_enumerate);
+       udev_list_entry_foreach(list_entry, udev_enumerate_get_list_entry(udev_enumerate)) {
+               struct udev_device *device;
+
+               device = udev_device_new_from_syspath(udev, udev_list_entry_get_name(list_entry));
+               if (device != NULL) {
+                       const char *id;
+                       struct stat statbuf;
+                       char to[UTIL_PATH_SIZE];
+                       char devpath[UTIL_PATH_SIZE];
+                       char from[UTIL_PATH_SIZE];
+
+                       id = udev_device_get_id_filename(device);
+                       if (id == NULL)
+                               goto next;
+                       util_strscpyl(to, sizeof(to), udev_get_dev_path(udev), "/.udev/db/", id, NULL);
+
+                       /* do not overwrite a new database file */
+                       if (lstat(to, &statbuf) == 0)
+                               goto next;
+
+                       /* find old database with $subsys:$sysname */
+                       util_strscpyl(from, sizeof(from), udev_get_dev_path(udev),
+                                    "/.udev/db/", udev_device_get_subsystem(device), ":",
+                                    udev_device_get_sysname(device), NULL);
+                       if (lstat(from, &statbuf) == 0) {
+                               rename(from, to);
+                               goto next;
+                       }
+
+                       /* find old database with the encoded devpath */
+                       util_path_encode(udev_device_get_devpath(device), devpath, sizeof(devpath));
+                       util_strscpyl(from, sizeof(from), udev_get_dev_path(udev),
+                                     "/.udev/db/", devpath, NULL);
+                       if (lstat(from, &statbuf) == 0) {
+                               rename(from, to);
+                               goto next;
+                       }
+next:
+                       udev_device_unref(device);
+               }
+       }
+       udev_enumerate_unref(udev_enumerate);
+       return 0;
+}
+
 int udevadm_info(struct udev *udev, int argc, char *argv[])
 {
        struct udev_device *device = NULL;
@@ -212,6 +266,7 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
                { "query", required_argument, NULL, 'q' },
                { "attribute-walk", no_argument, NULL, 'a' },
                { "export-db", no_argument, NULL, 'e' },
+               { "convert-db", no_argument, NULL, 'C' },
                { "root", no_argument, NULL, 'r' },
                { "device-id-of-file", required_argument, NULL, 'd' },
                { "export", no_argument, NULL, 'x' },
@@ -336,6 +391,9 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
                case 'e':
                        export_devices(udev);
                        goto exit;
+               case 'C':
+                       convert_db(udev);
+                       goto exit;
                case 'x':
                        export = true;
                        break;
@@ -359,7 +417,10 @@ int udevadm_info(struct udev *udev, int argc, char *argv[])
                               "  --attribute-walk           print all key matches while walking along the chain\n"
                               "                             of parent devices\n"
                               "  --device-id-of-file=<file> print major:minor of device containing this file\n"
+                              "  --export                   export key/value pairs\n"
+                              "  --export-prefix            export the key name with a prefix\n"
                               "  --export-db                export the content of the udev database\n"
+                              "  --convert-db               convert older version of database without a reboot\n"
                               "  --help\n\n");
                        goto exit;
                default:
index cefd776..00f299f 100644 (file)
           </listitem>
         </varlistentry>
         <varlistentry>
+          <term><option>--convert-db</option></term>
+          <listitem>
+            <para>Convert the database of an earlier udev version to the current format. This
+            is only useful on udev version upgrades, where the content of the old database might
+            be needed for the running system, and it is not sufficient for it, to be re-created
+            with the next bootup.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
           <term><option>--version</option></term>
           <listitem>
             <para>Print version.</para>