chiark / gitweb /
libudev: return kernel provided devnode when asked before we handled any rules
[elogind.git] / libudev / libudev-device.c
index 400354539b4d849c9454e4c435a27526302aa181..9b5d79ff4bf7e7caadd179794fa810f2497c16e5 100644 (file)
@@ -60,26 +60,31 @@ struct udev_device {
        struct udev_list_node devlinks_list;
        struct udev_list_node properties_list;
        struct udev_list_node sysattr_list;
+       struct udev_list_node tags_list;
        unsigned long long int seqnum;
        int event_timeout;
        int timeout;
        int devlink_priority;
        int refcount;
        dev_t devnum;
+       int ifindex;
        int watch_handle;
        int maj, min;
-       unsigned int parent_set:1;
-       unsigned int subsystem_set:1;
-       unsigned int devtype_set:1;
-       unsigned int devlinks_uptodate:1;
-       unsigned int envp_uptodate:1;
-       unsigned int driver_set:1;
-       unsigned int info_loaded:1;
+       bool parent_set;
+       bool subsystem_set;
+       bool devtype_set;
+       bool devlinks_uptodate;
+       bool envp_uptodate;
+       bool tags_uptodate;
+       bool driver_set;
+       bool info_loaded;
+       bool db_loaded;
+       bool uevent_loaded;
 };
 
 struct udev_list_entry *udev_device_add_property(struct udev_device *udev_device, const char *key, const char *value)
 {
-       udev_device->envp_uptodate = 0;
+       udev_device->envp_uptodate = false;
        if (value == NULL) {
                struct udev_list_entry *list_entry;
 
@@ -149,6 +154,26 @@ void udev_device_add_property_from_string_parse(struct udev_device *udev_device,
                }
                if (slink[0] != '\0')
                        udev_device_add_devlink(udev_device, slink, 0);
+       } else if (strncmp(property, "TAGS=", 5) == 0) {
+               char tags[UTIL_PATH_SIZE];
+               char *next;
+
+               util_strscpy(tags, sizeof(tags), &property[5]);
+               next = strchr(tags, ':');
+               if (next != NULL) {
+                       next++;
+                       while (next[0] != '\0') {
+                               char *tag;
+
+                               tag = next;
+                               next = strchr(tag, ':');
+                               if (next == NULL)
+                                       break;
+                               next[0] = '\0';
+                               next++;
+                               udev_device_add_tag(udev_device, tag);
+                       }
+               }
        } else if (strncmp(property, "DRIVER=", 7) == 0) {
                udev_device_set_driver(udev_device, &property[7]);
        } else if (strncmp(property, "ACTION=", 7) == 0) {
@@ -163,6 +188,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);
        }
@@ -208,6 +235,9 @@ int udev_device_read_db(struct udev_device *udev_device)
        char line[UTIL_LINE_SIZE];
        FILE *f;
 
+       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);
 
@@ -252,11 +282,13 @@ int udev_device_read_db(struct udev_device *udev_device)
                return 0;
        }
 
-       f = fopen(filename, "r");
+       f = fopen(filename, "re");
        if (f == NULL) {
                dbg(udev_device->udev, "error reading db file %s: %m\n", filename);
                return -1;
        }
+       udev_device->db_loaded = true;
+
        while (fgets(line, sizeof(line), f)) {
                ssize_t len;
                const char *val;
@@ -284,6 +316,9 @@ int udev_device_read_db(struct udev_device *udev_device)
                case 'E':
                        udev_device_add_property_from_string(udev_device, val);
                        break;
+               case 'G':
+                       udev_device_add_tag(udev_device, val);
+                       break;
                case 'W':
                        udev_device_set_watch_handle(udev_device, atoi(val));
                        break;
@@ -303,10 +338,14 @@ int udev_device_read_uevent_file(struct udev_device *udev_device)
        int maj = 0;
        int min = 0;
 
+       if (udev_device->uevent_loaded)
+               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;
 
        while (fgets(line, sizeof(line), f)) {
                char *pos;
@@ -322,6 +361,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]);
 
@@ -329,21 +370,13 @@ int udev_device_read_uevent_file(struct udev_device *udev_device)
        }
 
        udev_device->devnum = makedev(maj, min);
-
        fclose(f);
        return 0;
 }
 
-static void device_load_info(struct udev_device *device)
-{
-       device->info_loaded = 1;
-       udev_device_read_uevent_file(device);
-       udev_device_read_db(device);
-}
-
 void udev_device_set_info_loaded(struct udev_device *device)
 {
-       device->info_loaded = 1;
+       device->info_loaded = true;
 }
 
 struct udev_device *udev_device_new(struct udev *udev)
@@ -362,6 +395,7 @@ struct udev_device *udev_device_new(struct udev *udev)
        udev_list_init(&udev_device->devlinks_list);
        udev_list_init(&udev_device->properties_list);
        udev_list_init(&udev_device->sysattr_list);
+       udev_list_init(&udev_device->tags_list);
        udev_device->event_timeout = -1;
        udev_device->watch_handle = -1;
        /* copy global properties */
@@ -420,7 +454,7 @@ struct udev_device *udev_device_new_from_syspath(struct udev *udev, const char *
        util_strscpy(path, sizeof(path), syspath);
        util_resolve_sys_link(udev, path, sizeof(path));
 
-       if (strncmp(&syspath[len], "/devices/", 9) == 0) {
+       if (strncmp(&path[len], "/devices/", 9) == 0) {
                char file[UTIL_PATH_SIZE];
 
                /* all "devices" require a "uevent" file */
@@ -648,7 +682,7 @@ struct udev_device *udev_device_get_parent(struct udev_device *udev_device)
        if (udev_device == NULL)
                return NULL;
        if (!udev_device->parent_set) {
-               udev_device->parent_set = 1;
+               udev_device->parent_set = true;
                udev_device->parent_device = device_new_from_parent(udev_device);
        }
        if (udev_device->parent_device != NULL)
@@ -758,12 +792,13 @@ void udev_device_unref(struct udev_device *udev_device)
        free(udev_device->devtype);
        udev_list_cleanup_entries(udev_device->udev, &udev_device->devlinks_list);
        udev_list_cleanup_entries(udev_device->udev, &udev_device->properties_list);
+       udev_list_cleanup_entries(udev_device->udev, &udev_device->sysattr_list);
+       udev_list_cleanup_entries(udev_device->udev, &udev_device->tags_list);
        free(udev_device->action);
        free(udev_device->driver);
        free(udev_device->devpath_old);
        free(udev_device->sysname_old);
        free(udev_device->knodename);
-       udev_list_cleanup_entries(udev_device->udev, &udev_device->sysattr_list);
        free(udev_device->envp);
        free(udev_device->monitor_buf);
        dbg(udev_device->udev, "udev_device: %p released\n", udev_device);
@@ -841,8 +876,19 @@ const char *udev_device_get_devnode(struct udev_device *udev_device)
 {
        if (udev_device == NULL)
                return NULL;
-       if (!udev_device->info_loaded)
-               device_load_info(udev_device);
+       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) {
+               if (asprintf(&udev_device->devnode, "%s/%s",
+                            udev_get_dev_path(udev_device->udev), udev_device_get_knodename(udev_device)) < 0)
+                       return NULL;
+               return udev_device->devnode;
+       }
+
        return udev_device->devnode;
 }
 
@@ -862,7 +908,7 @@ const char *udev_device_get_subsystem(struct udev_device *udev_device)
        if (udev_device == NULL)
                return NULL;
        if (!udev_device->subsystem_set) {
-               udev_device->subsystem_set = 1;
+               udev_device->subsystem_set = true;
                /* read "subsystem" link */
                if (util_get_sys_subsystem(udev_device->udev, udev_device->syspath, subsystem, sizeof(subsystem)) > 0) {
                        udev_device_set_subsystem(udev_device, subsystem);
@@ -900,9 +946,8 @@ const char *udev_device_get_devtype(struct udev_device *udev_device)
        if (udev_device == NULL)
                return NULL;
        if (!udev_device->devtype_set) {
-               udev_device->devtype_set = 1;
-               if (!udev_device->info_loaded)
-                       udev_device_read_uevent_file(udev_device);
+               udev_device->devtype_set = true;
+               udev_device_read_uevent_file(udev_device);
        }
        return udev_device->devtype;
 }
@@ -925,13 +970,13 @@ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev_device *
        if (udev_device == NULL)
                return NULL;
        if (!udev_device->info_loaded)
-               device_load_info(udev_device);
+               udev_device_read_db(udev_device);
        return udev_list_get_entry(&udev_device->devlinks_list);
 }
 
 void udev_device_cleanup_devlinks_list(struct udev_device *udev_device)
 {
-       udev_device->devlinks_uptodate = 0;
+       udev_device->devlinks_uptodate = false;
        udev_list_cleanup_entries(udev_device->udev, &udev_device->devlinks_list);
 }
 
@@ -951,13 +996,15 @@ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device
 {
        if (udev_device == NULL)
                return NULL;
-       if (!udev_device->info_loaded)
-               device_load_info(udev_device);
+       if (!udev_device->info_loaded) {
+               udev_device_read_uevent_file(udev_device);
+               udev_device_read_db(udev_device);
+       }
        if (!udev_device->devlinks_uptodate) {
                char symlinks[UTIL_PATH_SIZE];
                struct udev_list_entry *list_entry;
 
-               udev_device->devlinks_uptodate = 1;
+               udev_device->devlinks_uptodate = true;
                list_entry = udev_device_get_devlinks_list_entry(udev_device);
                if (list_entry != NULL) {
                        char *s;
@@ -970,6 +1017,21 @@ struct udev_list_entry *udev_device_get_properties_list_entry(struct udev_device
                        udev_device_add_property(udev_device, "DEVLINKS", symlinks);
                }
        }
+       if (!udev_device->tags_uptodate) {
+               udev_device->tags_uptodate = true;
+               if (udev_device_get_tags_list_entry(udev_device) != NULL) {
+                       char tags[UTIL_PATH_SIZE];
+                       struct udev_list_entry *list_entry;
+                       char *s;
+                       size_t l;
+
+                       s = tags;
+                       l = util_strpcpyl(&s, sizeof(tags), ":", NULL);
+                       udev_list_entry_foreach(list_entry, udev_device_get_tags_list_entry(udev_device))
+                               l = util_strpcpyl(&s, l, udev_list_entry_get_name(list_entry), ":", NULL);
+                       udev_device_add_property(udev_device, "TAGS", tags);
+               }
+       }
        return udev_list_get_entry(&udev_device->properties_list);
 }
 
@@ -986,7 +1048,7 @@ const char *udev_device_get_driver(struct udev_device *udev_device)
        if (udev_device == NULL)
                return NULL;
        if (!udev_device->driver_set) {
-               udev_device->driver_set = 1;
+               udev_device->driver_set = true;
                if (util_get_sys_driver(udev_device->udev, udev_device->syspath, driver, sizeof(driver)) > 0)
                        udev_device->driver = strdup(driver);
        }
@@ -1004,7 +1066,7 @@ dev_t udev_device_get_devnum(struct udev_device *udev_device)
        if (udev_device == NULL)
                return makedev(0, 0);
        if (!udev_device->info_loaded)
-               device_load_info(udev_device);
+               udev_device_read_uevent_file(udev_device);
        return udev_device->devnum;
 }
 
@@ -1118,7 +1180,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;
@@ -1184,7 +1246,7 @@ int udev_device_set_subsystem(struct udev_device *udev_device, const char *subsy
        udev_device->subsystem = strdup(subsystem);
        if (udev_device->subsystem == NULL)
                return -ENOMEM;
-       udev_device->subsystem_set = 1;
+       udev_device->subsystem_set = true;
        udev_device_add_property(udev_device, "SUBSYSTEM", udev_device->subsystem);
        return 0;
 }
@@ -1195,7 +1257,7 @@ int udev_device_set_devtype(struct udev_device *udev_device, const char *devtype
        udev_device->devtype = strdup(devtype);
        if (udev_device->devtype == NULL)
                return -ENOMEM;
-       udev_device->devtype_set = 1;
+       udev_device->devtype_set = true;
        udev_device_add_property(udev_device, "DEVTYPE", udev_device->devtype);
        return 0;
 }
@@ -1216,7 +1278,7 @@ int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink
 {
        struct udev_list_entry *list_entry;
 
-       udev_device->devlinks_uptodate = 0;
+       udev_device->devlinks_uptodate = false;
        list_entry = udev_list_entry_add(udev_device->udev, &udev_device->devlinks_list, devlink, NULL, 1, 0);
        if (list_entry == NULL)
                return -ENOMEM;
@@ -1225,6 +1287,53 @@ int udev_device_add_devlink(struct udev_device *udev_device, const char *devlink
        return 0;
 }
 
+int udev_device_add_tag(struct udev_device *udev_device, const char *tag)
+{
+       if (strchr(tag, ':') != NULL || strchr(tag, ' ') != NULL)
+               return -EINVAL;
+       udev_device->tags_uptodate = false;
+       if (udev_list_entry_add(udev_device->udev, &udev_device->tags_list, tag, NULL, 1, 0) != NULL)
+               return 0;
+       return -ENOMEM;
+}
+
+void udev_device_cleanup_tags_list(struct udev_device *udev_device)
+{
+       udev_device->tags_uptodate = false;
+       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)
+               return NULL;
+       return udev_list_get_entry(&udev_device->tags_list);
+}
+
+int udev_device_has_tag(struct udev_device *udev_device, const char *tag)
+{
+       struct udev_list_entry *list_entry;
+
+       if (!udev_device->info_loaded)
+               udev_device_read_db(udev_device);
+       list_entry = udev_device_get_tags_list_entry(udev_device);
+       list_entry =  udev_list_entry_get_by_name(list_entry, tag);
+       if (list_entry != NULL)
+               return 1;
+       return 0;
+}
+
 #define ENVP_SIZE                      128
 #define MONITOR_BUF_SIZE               4096
 static int update_envp_monitor_buf(struct udev_device *udev_device)
@@ -1273,7 +1382,7 @@ static int update_envp_monitor_buf(struct udev_device *udev_device)
        }
        udev_device->envp[i] = NULL;
        udev_device->monitor_buf_len = s - udev_device->monitor_buf;
-       udev_device->envp_uptodate = 1;
+       udev_device->envp_uptodate = true;
        dbg(udev_device->udev, "filled envp/monitor buffer, %u properties, %zu bytes\n",
            i, udev_device->monitor_buf_len);
        return 0;
@@ -1312,7 +1421,7 @@ int udev_device_set_driver(struct udev_device *udev_device, const char *driver)
        udev_device->driver = strdup(driver);
        if (udev_device->driver == NULL)
                return -ENOMEM;
-       udev_device->driver_set = 1;
+       udev_device->driver_set = true;
        udev_device_add_property(udev_device, "DRIVER", udev_device->driver);
        return 0;
 }
@@ -1385,7 +1494,7 @@ int udev_device_set_timeout(struct udev_device *udev_device, int timeout)
 int udev_device_get_event_timeout(struct udev_device *udev_device)
 {
        if (!udev_device->info_loaded)
-               device_load_info(udev_device);
+               udev_device_read_db(udev_device);
        return udev_device->event_timeout;
 }
 
@@ -1421,7 +1530,7 @@ int udev_device_set_devnum(struct udev_device *udev_device, dev_t devnum)
 int udev_device_get_devlink_priority(struct udev_device *udev_device)
 {
        if (!udev_device->info_loaded)
-               device_load_info(udev_device);
+               udev_device_read_db(udev_device);
        return udev_device->devlink_priority;
 }
 
@@ -1434,7 +1543,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)
-               device_load_info(udev_device);
+               udev_device_read_db(udev_device);
        return udev_device->watch_handle;
 }
 
@@ -1443,3 +1552,14 @@ 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)
+{
+       udev_device->ifindex = ifindex;
+       return 0;
+}