chiark / gitweb /
libudev: introduce clone_with_db()
[elogind.git] / src / libudev / libudev-device.c
index 193c706d89d185e2d65152005ccd45a0ea51af7a..c0a061af6c808d6d505fc13f0182fcf41d4145c9 100644 (file)
@@ -37,6 +37,7 @@
 #include "libudev-private.h"
 
 static int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode);
+static struct udev_list_entry *udev_device_add_property_internal(struct udev_device *udev_device, const char *key, const char *value);
 
 /**
  * SECTION:libudev-device
@@ -123,7 +124,7 @@ static int udev_device_set_seqnum(struct udev_device *udev_device, unsigned long
 
         udev_device->seqnum = seqnum;
         snprintf(num, sizeof(num), "%llu", seqnum);
-        udev_device_add_property(udev_device, "SEQNUM", num);
+        udev_device_add_property_internal(udev_device, "SEQNUM", num);
         return 0;
 }
 
@@ -140,7 +141,7 @@ static int udev_device_set_ifindex(struct udev_device *udev_device, int ifindex)
 
         udev_device->ifindex = ifindex;
         snprintf(num, sizeof(num), "%d", ifindex);
-        udev_device_add_property(udev_device, "IFINDEX", num);
+        udev_device_add_property_internal(udev_device, "IFINDEX", num);
         return 0;
 }
 
@@ -161,16 +162,16 @@ _public_ dev_t udev_device_get_devnum(struct udev_device *udev_device)
         return udev_device->devnum;
 }
 
-int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum)
+static int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum)
 {
         char num[32];
 
         udev_device->devnum = devnum;
 
         snprintf(num, sizeof(num), "%u", major(devnum));
-        udev_device_add_property(udev_device, "MAJOR", num);
+        udev_device_add_property_internal(udev_device, "MAJOR", num);
         snprintf(num, sizeof(num), "%u", minor(devnum));
-        udev_device_add_property(udev_device, "MINOR", num);
+        udev_device_add_property_internal(udev_device, "MINOR", num);
         return 0;
 }
 
@@ -187,7 +188,7 @@ static int udev_device_set_devpath_old(struct udev_device *udev_device, const ch
         udev_device->devpath_old = strdup(devpath_old);
         if (udev_device->devpath_old == NULL)
                 return -ENOMEM;
-        udev_device_add_property(udev_device, "DEVPATH_OLD", udev_device->devpath_old);
+        udev_device_add_property_internal(udev_device, "DEVPATH_OLD", udev_device->devpath_old);
 
         pos = strrchr(udev_device->devpath_old, '/');
         if (pos == NULL)
@@ -224,7 +225,7 @@ static int udev_device_set_driver(struct udev_device *udev_device, const char *d
         if (udev_device->driver == NULL)
                 return -ENOMEM;
         udev_device->driver_set = true;
-        udev_device_add_property(udev_device, "DRIVER", udev_device->driver);
+        udev_device_add_property_internal(udev_device, "DRIVER", udev_device->driver);
         return 0;
 }
 
@@ -254,18 +255,18 @@ static int udev_device_set_devtype(struct udev_device *udev_device, const char *
         if (udev_device->devtype == NULL)
                 return -ENOMEM;
         udev_device->devtype_set = true;
-        udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype);
+        udev_device_add_property_internal(udev_device, "DEVTYPE", udev_device->devtype);
         return 0;
 }
 
-int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem)
+static int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsystem)
 {
         free(udev_device->subsystem);
         udev_device->subsystem = strdup(subsystem);
         if (udev_device->subsystem == NULL)
                 return -ENOMEM;
         udev_device->subsystem_set = true;
-        udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem);
+        udev_device_add_property_internal(udev_device, "SUBSYSTEM", udev_device->subsystem);
         return 0;
 }
 
@@ -323,7 +324,7 @@ static int udev_device_set_devnode_mode(struct udev_device *udev_device, mode_t
 
         udev_device->devnode_mode = mode;
         snprintf(num, sizeof(num), "%#o", mode);
-        udev_device_add_property(udev_device, "DEVMODE", num);
+        udev_device_add_property_internal(udev_device, "DEVMODE", num);
         return 0;
 }
 
@@ -340,7 +341,7 @@ static int udev_device_set_devnode_uid(struct udev_device *udev_device, uid_t ui
 
         udev_device->devnode_uid = uid;
         snprintf(num, sizeof(num), "%u", uid);
-        udev_device_add_property(udev_device, "DEVUID", num);
+        udev_device_add_property_internal(udev_device, "DEVUID", num);
         return 0;
 }
 
@@ -357,11 +358,11 @@ static int udev_device_set_devnode_gid(struct udev_device *udev_device, gid_t gi
 
         udev_device->devnode_gid = gid;
         snprintf(num, sizeof(num), "%u", gid);
-        udev_device_add_property(udev_device, "DEVGID", num);
+        udev_device_add_property_internal(udev_device, "DEVGID", num);
         return 0;
 }
 
-struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value)
+static struct udev_list_entry *udev_device_add_property_internal(struct udev_device *udev_device, const char *key, const char *value)
 {
         udev_device->envp_uptodate = false;
         if (value == NULL) {
@@ -376,6 +377,20 @@ struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device
         return udev_list_entry_add(&udev_device->properties_list, key, value);
 }
 
+
+int udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value)
+{
+        struct udev_list_entry *property;
+
+        property = udev_device_add_property_internal(udev_device, key, value);
+
+        /* store in db, skip private keys */
+        if (key[0] != '.')
+                udev_list_entry_set_num(property, true);
+
+        return 0;
+}
+
 static struct udev_list_entry *udev_device_add_property_from_string(struct udev_device *udev_device, const char *property)
 {
         char name[UTIL_LINE_SIZE];
@@ -389,7 +404,45 @@ static struct udev_list_entry *udev_device_add_property_from_string(struct udev_
         val = &val[1];
         if (val[0] == '\0')
                 val = NULL;
-        return udev_device_add_property(udev_device, name, val);
+        return udev_device_add_property_internal(udev_device, name, val);
+}
+
+static int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath)
+{
+        const char *pos;
+        size_t len;
+
+        free(udev_device->syspath);
+        udev_device->syspath = strdup(syspath);
+        if (udev_device->syspath ==  NULL)
+                return -ENOMEM;
+        udev_device->devpath = udev_device->syspath + strlen("/sys");
+        udev_device_add_property_internal(udev_device, "DEVPATH", udev_device->devpath);
+
+        pos = strrchr(udev_device->syspath, '/');
+        if (pos == NULL)
+                return -EINVAL;
+        udev_device->sysname = strdup(&pos[1]);
+        if (udev_device->sysname == NULL)
+                return -ENOMEM;
+
+        /* some devices have '!' in their name, change that to '/' */
+        len = 0;
+        while (udev_device->sysname[len] != '\0') {
+                if (udev_device->sysname[len] == '!')
+                        udev_device->sysname[len] = '/';
+                len++;
+        }
+
+        /* trailing number */
+        while (len > 0 && isdigit(udev_device->sysname[--len]))
+                udev_device->sysnum = &udev_device->sysname[len];
+
+        /* sysname is completely numeric */
+        if (len == 0)
+                udev_device->sysnum = NULL;
+
+        return 0;
 }
 
 /*
@@ -401,7 +454,7 @@ static struct udev_list_entry *udev_device_add_property_from_string(struct udev_
  * udev_device_set_info_loaded() needs to be set, to avoid trying
  * to use a device without a DEVPATH set
  */
-void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property)
+static void udev_device_add_property_from_string_parse(struct udev_device *udev_device, const char *property)
 {
         if (startswith(property, "DEVPATH=")) {
                 char path[UTIL_PATH_SIZE];
@@ -477,7 +530,7 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device,
         }
 }
 
-int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device)
+static int udev_device_add_property_from_string_parse_finish(struct udev_device *udev_device)
 {
         if (udev_device->maj > 0)
                 udev_device_set_devnum(udev_device, makedev(udev_device->maj, udev_device->min));
@@ -512,30 +565,27 @@ _public_ const char *udev_device_get_property_value(struct udev_device *udev_dev
         return udev_list_entry_get_value(list_entry);
 }
 
-int udev_device_read_db(struct udev_device *udev_device, const char *dbfile)
+int udev_device_read_db(struct udev_device *udev_device)
 {
         char filename[UTIL_PATH_SIZE];
         char line[UTIL_LINE_SIZE];
+        const char *id;
         FILE *f;
 
-        /* providing a database file will always force-load it */
-        if (dbfile == NULL) {
-                const char *id;
+        if (udev_device->db_loaded)
+                return 0;
 
-                if (udev_device->db_loaded)
-                        return 0;
-                udev_device->db_loaded = true;
+        udev_device->db_loaded = true;
 
-                id = udev_device_get_id_filename(udev_device);
-                if (id == NULL)
-                        return -1;
-                strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
-                dbfile = filename;
-        }
+        id = udev_device_get_id_filename(udev_device);
+        if (id == NULL)
+                return -1;
+
+        strscpyl(filename, sizeof(filename), "/run/udev/data/", id, NULL);
 
-        f = fopen(dbfile, "re");
+        f = fopen(filename, "re");
         if (f == NULL)
-                return log_debug_errno(errno, "no db file to read %s: %m", dbfile);
+                return log_debug_errno(errno, "no db file to read %s: %m", filename);
 
         /* devices with a database entry are initialized */
         udev_device->is_initialized = true;
@@ -637,7 +687,7 @@ void udev_device_set_info_loaded(struct udev_device *device)
         device->info_loaded = true;
 }
 
-struct udev_device *udev_device_new(struct udev *udev)
+static struct udev_device *udev_device_new(struct udev *udev)
 {
         struct udev_device *udev_device;
 
@@ -1253,7 +1303,7 @@ _public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev
         if (udev_device == NULL)
                 return NULL;
         if (!udev_device->info_loaded)
-                udev_device_read_db(udev_device, NULL);
+                udev_device_read_db(udev_device);
         return udev_list_get_entry(&udev_device->devlinks_list);
 }
 
@@ -1281,7 +1331,7 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud
                 return NULL;
         if (!udev_device->info_loaded) {
                 udev_device_read_uevent_file(udev_device);
-                udev_device_read_db(udev_device, NULL);
+                udev_device_read_db(udev_device);
         }
         if (!udev_device->devlinks_uptodate) {
                 char symlinks[UTIL_PATH_SIZE];
@@ -1297,7 +1347,7 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud
                         l = strpcpyl(&s, sizeof(symlinks), udev_list_entry_get_name(list_entry), NULL);
                         udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry))
                                 l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry), NULL);
-                        udev_device_add_property(udev_device, "DEVLINKS", symlinks);
+                        udev_device_add_property_internal(udev_device, "DEVLINKS", symlinks);
                 }
         }
         if (!udev_device->tags_uptodate) {
@@ -1312,7 +1362,7 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud
                         l = strpcpyl(&s, sizeof(tags), ":", NULL);
                         udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))
                                 l = strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL);
-                        udev_device_add_property(udev_device, "TAGS", tags);
+                        udev_device_add_property_internal(udev_device, "TAGS", tags);
                 }
         }
         return udev_list_get_entry(&udev_device->properties_list);
@@ -1354,7 +1404,7 @@ _public_ unsigned long long int udev_device_get_usec_since_initialized(struct ud
         if (udev_device == NULL)
                 return 0;
         if (!udev_device->info_loaded)
-                udev_device_read_db(udev_device, NULL);
+                udev_device_read_db(udev_device);
         if (udev_device->usec_initialized == 0)
                 return 0;
         now_ts = now(CLOCK_MONOTONIC);
@@ -1374,7 +1424,7 @@ void udev_device_set_usec_initialized(struct udev_device *udev_device, usec_t us
 
         udev_device->usec_initialized = usec_initialized;
         snprintf(num, sizeof(num), USEC_FMT, usec_initialized);
-        udev_device_add_property(udev_device, "USEC_INITIALIZED", num);
+        udev_device_add_property_internal(udev_device, "USEC_INITIALIZED", num);
 }
 
 /**
@@ -1607,44 +1657,6 @@ _public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_
         return udev_list_get_entry(&udev_device->sysattr_list);
 }
 
-int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath)
-{
-        const char *pos;
-        size_t len;
-
-        free(udev_device->syspath);
-        udev_device->syspath = strdup(syspath);
-        if (udev_device->syspath ==  NULL)
-                return -ENOMEM;
-        udev_device->devpath = udev_device->syspath + strlen("/sys");
-        udev_device_add_property(udev_device, "DEVPATH", udev_device->devpath);
-
-        pos = strrchr(udev_device->syspath, '/');
-        if (pos == NULL)
-                return -EINVAL;
-        udev_device->sysname = strdup(&pos[1]);
-        if (udev_device->sysname == NULL)
-                return -ENOMEM;
-
-        /* some devices have '!' in their name, change that to '/' */
-        len = 0;
-        while (udev_device->sysname[len] != '\0') {
-                if (udev_device->sysname[len] == '!')
-                        udev_device->sysname[len] = '/';
-                len++;
-        }
-
-        /* trailing number */
-        while (len > 0 && isdigit(udev_device->sysname[--len]))
-                udev_device->sysnum = &udev_device->sysname[len];
-
-        /* sysname is completely numeric */
-        if (len == 0)
-                udev_device->sysnum = NULL;
-
-        return 0;
-}
-
 static int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode)
 {
         free(udev_device->devnode);
@@ -1656,7 +1668,7 @@ static int udev_device_set_devnode(struct udev_device *udev_device, const char *
         }
         if (udev_device->devnode == NULL)
                 return -ENOMEM;
-        udev_device_add_property(udev_device, "DEVNAME", udev_device->devnode);
+        udev_device_add_property_internal(udev_device, "DEVNAME", udev_device->devnode);
         return 0;
 }
 
@@ -1721,7 +1733,7 @@ const char *udev_device_get_id_filename(struct udev_device *udev_device)
 _public_ int udev_device_get_is_initialized(struct udev_device *udev_device)
 {
         if (!udev_device->info_loaded)
-                udev_device_read_db(udev_device, NULL);
+                udev_device_read_db(udev_device);
         return udev_device->is_initialized;
 }
 
@@ -1781,7 +1793,7 @@ _public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_dev
         if (udev_device == NULL)
                 return NULL;
         if (!udev_device->info_loaded)
-                udev_device_read_db(udev_device, NULL);
+                udev_device_read_db(udev_device);
         return udev_list_get_entry(&udev_device->tags_list);
 }
 
@@ -1801,7 +1813,7 @@ _public_ int udev_device_has_tag(struct udev_device *udev_device, const char *ta
         if (udev_device == NULL)
                 return false;
         if (!udev_device->info_loaded)
-                udev_device_read_db(udev_device, NULL);
+                udev_device_read_db(udev_device);
         list_entry = udev_device_get_tags_list_entry(udev_device);
         if (udev_list_entry_get_by_name(list_entry, tag) != NULL)
                 return true;
@@ -1883,14 +1895,14 @@ int udev_device_set_action(struct udev_device *udev_device, const char *action)
         udev_device->action = strdup(action);
         if (udev_device->action == NULL)
                 return -ENOMEM;
-        udev_device_add_property(udev_device, "ACTION", udev_device->action);
+        udev_device_add_property_internal(udev_device, "ACTION", udev_device->action);
         return 0;
 }
 
 int udev_device_get_devlink_priority(struct udev_device *udev_device)
 {
         if (!udev_device->info_loaded)
-                udev_device_read_db(udev_device, NULL);
+                udev_device_read_db(udev_device);
         return udev_device->devlink_priority;
 }
 
@@ -1903,7 +1915,7 @@ int udev_device_set_devlink_priority(struct udev_device *udev_device, int prio)
 int udev_device_get_watch_handle(struct udev_device *udev_device)
 {
         if (!udev_device->info_loaded)
-                udev_device_read_db(udev_device, NULL);
+                udev_device_read_db(udev_device);
         return udev_device->watch_handle;
 }
 
@@ -1926,6 +1938,7 @@ void udev_device_set_db_persist(struct udev_device *udev_device)
 int udev_device_rename(struct udev_device *udev_device, const char *name)
 {
         _cleanup_free_ char *dirname = NULL;
+        const char *interface;
         char *new_syspath;
         int r;
 
@@ -1936,11 +1949,113 @@ int udev_device_rename(struct udev_device *udev_device, const char *name)
         if (!dirname)
                 return -ENOMEM;
 
-        new_syspath = strappenda(dirname, "/", name);
+        new_syspath = strjoina(dirname, "/", name);
 
         r = udev_device_set_syspath(udev_device, new_syspath);
         if (r < 0)
                 return r;
 
+        interface = udev_device_get_property_value(udev_device, "INTERFACE");
+        if (interface) {
+                /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */
+                udev_device_add_property_internal(udev_device, "INTERFACE_OLD", interface);
+                udev_device_add_property_internal(udev_device, "INTERFACE", name);
+        }
+
+        return 0;
+}
+
+struct udev_device *udev_device_shallow_clone(struct udev_device *old_device)
+{
+        struct udev_device *device;
+
+        if (old_device == NULL)
+                return NULL;
+
+        device = udev_device_new(old_device->udev);
+        if (!device) {
+                errno = ENOMEM;
+
+                return NULL;
+        }
+
+        udev_device_set_syspath(device, udev_device_get_syspath(old_device));
+        udev_device_set_subsystem(device, udev_device_get_subsystem(old_device));
+        udev_device_set_devnum(device, udev_device_get_devnum(old_device));
+
+        return device;
+}
+
+struct udev_device *udev_device_clone_with_db(struct udev_device *old_device)
+{
+        struct udev_device *device;
+
+        device = udev_device_shallow_clone(old_device);
+        if (!device)
+                return NULL;
+
+        udev_device_read_db(device);
+        udev_device_set_info_loaded(device);
+
+        return device;
+}
+
+struct udev_device *udev_device_new_from_nulstr(struct udev *udev, char *nulstr, ssize_t buflen) {
+        struct udev_device *device;
+        ssize_t bufpos = 0;
+
+        if (nulstr == NULL || buflen <= 0) {
+                errno = EINVAL;
+
+                return NULL;
+        }
+
+        device = udev_device_new(udev);
+        if (!device) {
+                errno = ENOMEM;
+
+                return NULL;
+        }
+
+        udev_device_set_info_loaded(device);
+
+        while (bufpos < buflen) {
+                char *key;
+                size_t keylen;
+
+                key = nulstr + bufpos;
+                keylen = strlen(key);
+                if (keylen == 0)
+                        break;
+
+                bufpos += keylen + 1;
+                udev_device_add_property_from_string_parse(device, key);
+        }
+
+        if (udev_device_add_property_from_string_parse_finish(device) < 0) {
+                log_debug("missing values, invalid device");
+
+                udev_device_unref(device);
+
+                errno = EINVAL;
+
+                return NULL;
+        }
+
+        return device;
+}
+
+int udev_device_copy_properties(struct udev_device *dst, struct udev_device *src) {
+        struct udev_list_entry *entry;
+
+        for ((entry = udev_device_get_properties_list_entry(src)); entry; entry = udev_list_entry_get_next(entry)) {
+                const char *key, *value;
+
+                key = udev_list_entry_get_name(entry);
+                value = udev_list_entry_get_value(entry);
+
+                udev_device_add_property(dst, key, value);
+        }
+
         return 0;
 }