chiark / gitweb /
libudev: udev_device - add attribute cache
[elogind.git] / udev / lib / libudev-device.c
index 0405e1c98a2fce4002a1945da7a915f81d565727..cda2de7490e6ef309d94b65a59c4e8c9800b2cdf 100644 (file)
@@ -26,6 +26,7 @@
 #include <errno.h>
 #include <string.h>
 #include <dirent.h>
+#include <fcntl.h>
 #include <sys/stat.h>
 
 #include "libudev.h"
 struct udev_device {
        int refcount;
        struct udev *udev;
-       char *devpath;
+       struct udev_device *parent_device;
        char *syspath;
+       const char *devpath;
+       const char *sysname;
        char *devname;
        char *subsystem;
        struct list_head link_list;
@@ -50,6 +53,7 @@ struct udev_device {
        int num_fake_partitions;
        int devlink_priority;
        int ignore_remove;
+       struct list_head attr_list;
 };
 
 static size_t devpath_to_db_path(struct udev *udev, const char *devpath, char *filename, size_t len)
@@ -162,6 +166,7 @@ struct udev_device *device_init(struct udev *udev)
        udev_device->udev = udev;
        INIT_LIST_HEAD(&udev_device->link_list);
        INIT_LIST_HEAD(&udev_device->env_list);
+       INIT_LIST_HEAD(&udev_device->attr_list);
        info(udev_device->udev, "udev_device: %p created\n", udev_device);
        return udev_device;
 }
@@ -193,10 +198,11 @@ struct udev_device *udev_device_new_from_devpath(struct udev *udev, const char *
 
        util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
        util_strlcat(path, devpath, sizeof(path));
-       if (stat(path, &statbuf) != 0)
-               return NULL;
-       if (!S_ISDIR(statbuf.st_mode))
+       util_strlcat(path, "/uevent", sizeof(path));
+       if (stat(path, &statbuf) != 0) {
+               info(udev, "not a device :%s\n", devpath);
                return NULL;
+       }
 
        udev_device = device_init(udev);
        if (udev_device == NULL)
@@ -210,10 +216,38 @@ struct udev_device *udev_device_new_from_devpath(struct udev *udev, const char *
 
        if (device_read_db(udev_device) >= 0)
                info(udev, "device %p filled with udev database data\n", udev_device);
-
        return udev_device;
 }
 
+static struct udev_device *device_new_from_parent(struct udev_device *udev_device)
+{
+       struct udev_device *udev_device_parent = NULL;
+       char path[UTIL_PATH_SIZE];
+       char *pos;
+
+       if (udev_device == NULL)
+               return NULL;
+
+       util_strlcpy(path, udev_device_get_devpath(udev_device), sizeof(path));
+       while (1) {
+               pos = strrchr(path, '/');
+               if (pos == path || pos == NULL)
+                       break;
+               pos[0] = '\0';
+               udev_device_parent = udev_device_new_from_devpath(udev_device->udev, path);
+               if (udev_device_parent != NULL)
+                       break;
+       }
+       return udev_device_parent;
+}
+
+struct udev_device *udev_device_get_parent(struct udev_device *udev_device)
+{
+       if (udev_device->parent_device == NULL)
+               udev_device->parent_device = device_new_from_parent(udev_device);
+       return udev_device->parent_device;
+}
+
 /**
  * udev_device_get_udev:
  * @udev_device: udev device
@@ -260,6 +294,8 @@ void udev_device_unref(struct udev_device *udev_device)
        udev_device->refcount--;
        if (udev_device->refcount > 0)
                return;
+       if (udev_device->parent_device != NULL)
+               udev_device_unref(udev_device->parent_device);
        free(udev_device->syspath);
        free(udev_device->devname);
        free(udev_device->subsystem);
@@ -269,6 +305,7 @@ void udev_device_unref(struct udev_device *udev_device)
        free(udev_device->driver);
        free(udev_device->devpath_old);
        free(udev_device->physdevpath);
+       util_name_list_cleanup(udev_device->udev, &udev_device->attr_list);
        info(udev_device->udev, "udev_device: %p released\n", udev_device);
        free(udev_device);
 }
@@ -305,6 +342,13 @@ const char *udev_device_get_syspath(struct udev_device *udev_device)
        return udev_device->syspath;
 }
 
+const char *udev_device_get_sysname(struct udev_device *udev_device)
+{
+       if (udev_device == NULL)
+               return NULL;
+       return udev_device->sysname;
+}
+
 /**
  * udev_device_get_devname:
  * @udev_device: udev device
@@ -449,11 +493,80 @@ unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device)
        return udev_device->seqnum;
 }
 
+const char *udev_device_get_attr_value(struct udev_device *udev_device, const char *attr)
+{
+       char path[UTIL_PATH_SIZE];
+       char value[UTIL_NAME_SIZE];
+       struct stat statbuf;
+       int fd;
+       ssize_t size;
+       const char *val = NULL;
+
+       util_strlcpy(path, udev_device_get_syspath(udev_device), sizeof(path));
+       util_strlcat(path, "/", sizeof(path));
+       util_strlcat(path, attr, sizeof(path));
+
+       if (lstat(path, &statbuf) != 0) {
+               info(udev_device->udev, "stat '%s' failed: %s\n", path, strerror(errno));
+               goto out;
+       }
+
+       if (S_ISLNK(statbuf.st_mode)) {
+               /* links return the last element of the target path */
+               char target[UTIL_NAME_SIZE];
+               int len;
+               char *pos;
+
+               len = readlink(path, target, sizeof(target));
+               if (len > 0) {
+                       target[len] = '\0';
+                       pos = strrchr(target, '/');
+                       if (pos != NULL) {
+                               pos = &pos[1];
+                               info(udev_device->udev, "cache '%s' with link value '%s'\n", attr, pos);
+                               val = util_name_list_add(udev_device->udev, &udev_device->attr_list, attr, pos, 0)->value;
+                       }
+               }
+               goto out;
+       }
+
+       /* skip directories */
+       if (S_ISDIR(statbuf.st_mode))
+               goto out;
+
+       /* skip non-readable files */
+       if ((statbuf.st_mode & S_IRUSR) == 0)
+               goto out;
+
+       /* read attribute value */
+       fd = open(path, O_RDONLY);
+       if (fd < 0) {
+               info(udev_device->udev, "attribute '%s' can not be opened\n", path);
+               goto out;
+       }
+       size = read(fd, value, sizeof(value));
+       close(fd);
+       if (size < 0)
+               goto out;
+       if (size == sizeof(value))
+               goto out;
+
+       /* got a valid value, store and return it */
+       value[size] = '\0';
+       util_remove_trailing_chars(value, '\n');
+       info(udev_device->udev, "'%s' has attribute value '%s'\n", path, value);
+       val = util_name_list_add(udev_device->udev, &udev_device->attr_list, attr, value, 0)->value;
+out:
+       return val;
+}
 int device_set_devpath(struct udev_device *udev_device, const char *devpath)
 {
        if (asprintf(&udev_device->syspath, "%s%s", udev_get_sys_path(udev_device->udev), devpath) < 0)
                return -ENOMEM;
        udev_device->devpath = &udev_device->syspath[strlen(udev_get_sys_path(udev_device->udev))];
+       udev_device->sysname = strrchr(udev_device->syspath, '/');
+       if (udev_device->sysname != NULL)
+               udev_device->sysname = &udev_device->sysname[1];
        return 0;
 }
 
@@ -475,14 +588,14 @@ int device_set_devname(struct udev_device *udev_device, const char *devname)
 
 int device_add_devlink(struct udev_device *udev_device, const char *devlink)
 {
-       if (util_name_list_add(udev_device->udev, &udev_device->link_list, devlink, 0) == NULL)
+       if (util_name_list_add(udev_device->udev, &udev_device->link_list, devlink, NULL, 0) == NULL)
                return -ENOMEM;
        return 0;
 }
 
 int device_add_property(struct udev_device *udev_device, const char *property)
 {
-       if (util_name_list_add(udev_device->udev, &udev_device->env_list, property, 0) == NULL)
+       if (util_name_list_add(udev_device->udev, &udev_device->env_list, property, NULL, 0) == NULL)
                return -ENOMEM;
        return 0;
 }