chiark / gitweb /
udevd: simplify udev database and fix DEVNAME handling
[elogind.git] / libudev / libudev-device.c
index a6a1ecb4e90f84651b26ea0c5cb345928bbeb082..ac206a0e16700b14c156483516957490149c69bf 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;
@@ -67,6 +68,7 @@ struct udev_device {
        int devlink_priority;
        int refcount;
        dev_t devnum;
+       int ifindex;
        int watch_handle;
        int maj, min;
        bool parent_set;
@@ -187,6 +189,8 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device,
                udev_device_set_seqnum(udev_device, strtoull(&property[7], NULL, 10));
        } else if (strncmp(property, "TIMEOUT=", 8) == 0) {
                udev_device_set_timeout(udev_device, strtoull(&property[8], NULL, 10));
+       } else if (strncmp(property, "IFINDEX=", 8) == 0) {
+               udev_device_set_ifindex(udev_device, strtoull(&property[8], NULL, 10));
        } else {
                udev_device_add_property_from_string(udev_device, property);
        }
@@ -227,68 +231,29 @@ const char *udev_device_get_property_value(struct udev_device *udev_device, cons
 
 int udev_device_read_db(struct udev_device *udev_device)
 {
-       struct stat stats;
+       const char *id;
        char filename[UTIL_PATH_SIZE];
        char line[UTIL_LINE_SIZE];
        FILE *f;
 
        if (udev_device->db_loaded)
                return 0;
+       udev_device->db_loaded = true;
 
-       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);
-
-       if (lstat(filename, &stats) != 0) {
-               dbg(udev_device->udev, "no db file to read %s: %m\n", filename);
+       id = udev_device_get_id_filename(udev_device);
+       if (id == NULL)
                return -1;
-       }
-       if ((stats.st_mode & S_IFMT) == S_IFLNK) {
-               char target[UTIL_PATH_SIZE];
-               char devnode[UTIL_PATH_SIZE];
-               int target_len;
-               char *next;
-
-               target_len = readlink(filename, target, sizeof(target));
-               if (target_len <= 0 || target_len == sizeof(target)) {
-                       info(udev_device->udev, "error reading db link %s: %m\n", filename);
-                       return -1;
-               }
-               target[target_len] = '\0';
-
-               next = strchr(target, ' ');
-               if (next != NULL) {
-                       next[0] = '\0';
-                       next = &next[1];
-               }
-               util_strscpyl(devnode, sizeof(devnode), udev_get_dev_path(udev_device->udev), "/", target, NULL);
-               udev_device_set_devnode(udev_device, devnode);
-               while (next != NULL) {
-                       char devlink[UTIL_PATH_SIZE];
-                       const char *lnk;
-
-                       lnk = next;
-                       next = strchr(next, ' ');
-                       if (next != NULL) {
-                               next[0] = '\0';
-                               next = &next[1];
-                       }
-                       util_strscpyl(devlink, sizeof(devlink), udev_get_dev_path(udev_device->udev), "/", lnk, NULL);
-                       udev_device_add_devlink(udev_device, devlink, 0);
-               }
-               info(udev_device->udev, "device %p filled with db symlink data '%s'\n", udev_device, udev_device->devnode);
-               return 0;
-       }
-
-       f = fopen(filename, "r");
+       util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/.udev/db/", id, NULL);
+       f = fopen(filename, "re");
        if (f == NULL) {
-               dbg(udev_device->udev, "error reading db file %s: %m\n", filename);
+               info(udev_device->udev, "no db file to read %s: %m\n", filename);
                return -1;
        }
-       udev_device->db_loaded = true;
 
        while (fgets(line, sizeof(line), f)) {
                ssize_t len;
                const char *val;
+               struct udev_list_entry *entry;
 
                len = strlen(line);
                if (len < 4)
@@ -307,11 +272,9 @@ int udev_device_read_db(struct udev_device *udev_device)
                case 'L':
                        udev_device_set_devlink_priority(udev_device, atoi(val));
                        break;
-               case 'T':
-                       udev_device_set_event_timeout(udev_device, atoi(val));
-                       break;
                case 'E':
-                       udev_device_add_property_from_string(udev_device, val);
+                       entry = udev_device_add_property_from_string(udev_device, val);
+                       udev_list_entry_set_flags(entry, 1);
                        break;
                case 'G':
                        udev_device_add_tag(udev_device, val);
@@ -339,7 +302,7 @@ int udev_device_read_uevent_file(struct udev_device *udev_device)
                return 0;
 
        util_strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL);
-       f = fopen(filename, "r");
+       f = fopen(filename, "re");
        if (f == NULL)
                return -1;
        udev_device->uevent_loaded = true;
@@ -358,6 +321,8 @@ int udev_device_read_uevent_file(struct udev_device *udev_device)
                        maj = strtoull(&line[6], NULL, 10);
                else if (strncmp(line, "MINOR=", 6) == 0)
                        min = strtoull(&line[6], NULL, 10);
+               else if (strncmp(line, "IFINDEX=", 8) == 0)
+                       udev_device_set_ifindex(udev_device, strtoull(&line[8], NULL, 10));
                else if (strncmp(line, "DEVNAME=", 8) == 0)
                        udev_device_set_knodename(udev_device, &line[8]);
 
@@ -794,6 +759,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);
@@ -871,8 +837,21 @@ const char *udev_device_get_devnode(struct udev_device *udev_device)
 {
        if (udev_device == NULL)
                return NULL;
-       if (!udev_device->info_loaded)
+       if (!udev_device->info_loaded) {
+               udev_device_read_uevent_file(udev_device);
                udev_device_read_db(udev_device);
+       }
+
+       /* we might get called before we handled an event and have a db, use the kernel-provided name */
+       if (udev_device->devnode == NULL && udev_device_get_knodename(udev_device) != NULL) {
+               char filename[UTIL_NAME_SIZE];
+
+               util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev_device->udev), "/",
+                             udev_device_get_knodename(udev_device), NULL);
+               udev_device_set_devnode(udev_device, filename);
+               return udev_device->devnode;
+       }
+
        return udev_device->devnode;
 }
 
@@ -1164,7 +1143,7 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const
                goto out;
 
        /* read attribute value */
-       fd = open(path, O_RDONLY);
+       fd = open(path, O_RDONLY|O_CLOEXEC);
        if (fd < 0) {
                dbg(udev_device->udev, "attribute '%s' can not be opened\n", path);
                goto out;
@@ -1250,8 +1229,6 @@ int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode
 {
        free(udev_device->devnode);
        udev_device->devnode = strdup(devnode);
-       if (devnode == NULL)
-               return 0;
        if (udev_device->devnode == NULL)
                return -ENOMEM;
        udev_device_add_property(udev_device, "DEVNAME", udev_device->devnode);
@@ -1271,6 +1248,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)
@@ -1287,6 +1298,17 @@ void udev_device_cleanup_tags_list(struct udev_device *udev_device)
        udev_list_cleanup_entries(udev_device->udev, &udev_device->tags_list);
 }
 
+/**
+ * udev_device_get_tags_list_entry:
+ * @udev_device: udev device
+ *
+ * Retrieve the list of tags attached to the udev device. The next
+ * list entry can be retrieved with udev_list_entry_next(),
+ * which returns #NULL if no more entries exist. The tag string
+ * can be retrieved from the list entry by udev_list_get_name().
+ *
+ * Returns: the first entry of the tag list
+ **/
 struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_device *udev_device)
 {
        if (udev_device == NULL)
@@ -1450,7 +1472,9 @@ int udev_device_set_knodename(struct udev_device *udev_device, const char *knode
        udev_device->knodename = strdup(knodename);
        if (udev_device->knodename == NULL)
                return -ENOMEM;
-       udev_device_add_property(udev_device, "DEVNAME", udev_device->knodename);
+       /* do not overwrite the udev property with the kernel property */
+       if (udev_device->devnode == NULL)
+               udev_device_add_property(udev_device, "DEVNAME", udev_device->knodename);
        return 0;
 }
 
@@ -1473,7 +1497,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;
 }
 
@@ -1525,3 +1553,18 @@ int udev_device_set_watch_handle(struct udev_device *udev_device, int handle)
        udev_device->watch_handle = handle;
        return 0;
 }
+
+int udev_device_get_ifindex(struct udev_device *udev_device)
+{
+       return udev_device->ifindex;
+}
+
+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;
+}