[PATCH 1/2] libudev: modify sysfs attr caching behavior

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In order to get a complete list of sysfs attributes for a given
udev_device a complete list of all sysfs attributes is created on first
access (with values being empty). Values themselves are filled in, as
soon as a particular sysfs attribute is read.

Signed-off-by: Thomas Egerer <thomas.egerer@xxxxxxxxxxx>
---
 libudev/libudev-device.c |  145 ++++++++++++++++++++++++++++++++--------------
 1 files changed, 101 insertions(+), 44 deletions(-)

diff --git a/libudev/libudev-device.c b/libudev/libudev-device.c
index 16bee19..5baff49 100644
--- a/libudev/libudev-device.c
+++ b/libudev/libudev-device.c
@@ -78,6 +78,7 @@ struct udev_device {
 	bool devlinks_uptodate;
 	bool envp_uptodate;
 	bool tags_uptodate;
+	bool sysattrs_cached;
 	bool driver_set;
 	bool info_loaded;
 	bool db_loaded;
@@ -1136,6 +1137,79 @@ void udev_device_set_usec_initialized(struct udev_device
*udev_device, unsigned
 	udev_device->usec_initialized = usec_initialized;
 }

+static int cache_sysattrs(struct udev_device *udev_device)
+{
+	struct udev_list_entry *list_entry;
+	struct dirent *entry;
+	DIR *dir;
+	int num = 0;
+
+	if (udev_device == NULL)
+		return -1;
+	/* caching already done? */
+	if (true == udev_device->sysattrs_cached)
+		return 0;
+
+	dir = opendir(udev_device_get_syspath(udev_device));
+	if (!dir) {
+		dbg(udev_device->udev, "sysfs dir '%s' can not be opened\n",
+				udev_device_get_syspath(udev_device));
+		return -1;
+	}
+
+	while (NULL != (entry = readdir(dir))) {
+		char path[UTIL_PATH_SIZE];
+		struct stat statbuf;
+
+		/* only handle symlinks and regular files */
+		if (DT_LNK != entry->d_type && DT_REG != entry->d_type)
+			continue;
+
+		util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device),
+				"/", entry->d_name, NULL);
+		if (0 != lstat(path, &statbuf))
+			continue;
+
+		if (0 == (statbuf.st_mode & S_IRUSR))
+			continue;
+
+		if (DT_LNK == entry->d_type) {
+			/* symlink */
+			char target[UTIL_NAME_SIZE];
+			int len;
+			char *pos;
+
+			/* these values are cached immediately */
+			if (strcmp(entry->d_name, "driver") != 0 &&
+				strcmp(entry->d_name, "subsystem") != 0 &&
+				strcmp(entry->d_name, "module") != 0)
+				continue;
+
+			len = readlinkat(dirfd(dir), entry->d_name, target, sizeof(target));
+			if (len <= 0 || len == sizeof(target))
+				continue;
+			target[len] = '\0';
+
+			pos = strrchr(target, '/');
+			if (pos != NULL) {
+				pos = &pos[1];
+				dbg(udev_device->udev, "cache '%s' with link value '%s'\n", entry->d_name,
pos);
+				list_entry = udev_list_entry_add(udev_device->udev,
+						&udev_device->sysattr_list, entry->d_name, pos, 1, 0);
+			}
+		} else {
+			/* regular file */
+			dbg(udev_device->udev, "cache '%s' empty sysattr\n", entry->d_name);
+			list_entry = udev_list_entry_add(udev_device->udev,
+					&udev_device->sysattr_list, entry->d_name, NULL, 0, 0);
+		}
+		++num;
+	}
+
+	udev_device->sysattrs_cached = true;
+	return num;
+}
+
 /**
  * udev_device_get_sysattr_value:
  * @udev_device: udev device
@@ -1143,6 +1217,9 @@ void udev_device_set_usec_initialized(struct udev_device
*udev_device, unsigned
  *
  * The retrieved value is cached in the device. Repeated calls will return the same
  * value and not open the attribute again.
+ * Upon first call a list of available sysfs attributes is assembled and saved in
+ * sysattr_list -- while the actual value of the sysattr is -- except for
symlinks --
+ * not read.
  *
  * Returns: the content of a sys attribute file, or #NULL if there is no sys
attribute value.
  **/
@@ -1151,66 +1228,46 @@ const char *udev_device_get_sysattr_value(struct
udev_device *udev_device, const
 	struct udev_list_entry *list_entry;
 	char path[UTIL_PATH_SIZE];
 	char value[4096];
-	struct stat statbuf;
 	int fd;
 	ssize_t size;
 	const char *val = NULL;
+	bool found = false;

 	if (udev_device == NULL)
 		return NULL;
 	if (sysattr == NULL)
 		return NULL;

-	/* look for possibly already cached result */
+	/* perform initial caching of sysattr list */
+	if (!udev_device->sysattrs_cached) {
+		int ret;
+		ret = cache_sysattrs(udev_device);
+		if (0 > ret)
+			return NULL;
+	}
+
+	/* does the desired sysattr exist? */
 	udev_list_entry_foreach(list_entry,
udev_list_get_entry(&udev_device->sysattr_list)) {
 		if (strcmp(udev_list_entry_get_name(list_entry), sysattr) == 0) {
-			dbg(udev_device->udev, "got '%s' (%s) from cache\n",
-			    sysattr, udev_list_entry_get_value(list_entry));
-			return udev_list_entry_get_value(list_entry);
+			found = true;
+			val = udev_list_entry_get_value(list_entry);
+			break;
 		}
 	}

-	util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/",
sysattr, NULL);
-	if (lstat(path, &statbuf) != 0) {
-		dbg(udev_device->udev, "no attribute '%s', keep negative entry\n", path);
-		udev_list_entry_add(udev_device->udev, &udev_device->sysattr_list, sysattr,
NULL, 0, 0);
-		goto out;
+	if (false == found) {
+		dbg(udev_device->udev, "no attribute '%s/%s' found\n",
+				udev_device_get_syspath(udev_device), sysattr);
+		return NULL;
 	}

-	if (S_ISLNK(statbuf.st_mode)) {
-		char target[UTIL_NAME_SIZE];
-		int len;
-		char *pos;
-
-		/* some core links return the last element of the target path */
-		if (strcmp(sysattr, "driver") != 0 &&
-		    strcmp(sysattr, "subsystem") != 0 &&
-		    strcmp(sysattr, "module") != 0)
-			goto out;
-
-		len = readlink(path, target, sizeof(target));
-		if (len <= 0 || len == sizeof(target))
-			goto out;
-		target[len] = '\0';
-
-		pos = strrchr(target, '/');
-		if (pos != NULL) {
-			pos = &pos[1];
-			dbg(udev_device->udev, "cache '%s' with link value '%s'\n", sysattr, pos);
-			list_entry = udev_list_entry_add(udev_device->udev,
&udev_device->sysattr_list, sysattr, pos, 0, 0);
-			val = udev_list_entry_get_value(list_entry);
-		}
-
-		goto out;
+	if (NULL != val) {
+		dbg(udev_device->udev, "got '%s' (%s) from cache\n",
+				sysattr, udev_list_entry_get_value(list_entry));
+		return val;
 	}

-	/* skip directories */
-	if (S_ISDIR(statbuf.st_mode))
-		goto out;
-
-	/* skip non-readable files */
-	if ((statbuf.st_mode & S_IRUSR) == 0)
-		goto out;
+	util_strscpyl(path, sizeof(path), udev_device_get_syspath(udev_device), "/",
sysattr, NULL);

 	/* read attribute value */
 	fd = open(path, O_RDONLY|O_CLOEXEC);
@@ -1225,11 +1282,11 @@ const char *udev_device_get_sysattr_value(struct
udev_device *udev_device, const
 	if (size == sizeof(value))
 		goto out;

-	/* got a valid value, store it in cache and return it */
+	/* got a valid value, update in cache and return it */
 	value[size] = '\0';
 	util_remove_trailing_chars(value, '\n');
 	dbg(udev_device->udev, "'%s' has attribute value '%s'\n", path, value);
-	list_entry = udev_list_entry_add(udev_device->udev,
&udev_device->sysattr_list, sysattr, value, 0, 0);
+	list_entry = udev_list_entry_add(udev_device->udev,
&udev_device->sysattr_list, sysattr, value, 1, 0);
 	val = udev_list_entry_get_value(list_entry);
 out:
 	return val;
-- 
1.7.2.2


--
To unsubscribe from this list: send the line "unsubscribe linux-hotplug" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Kernel]     [Linux DVB]     [Asterisk Internet PBX]     [DCCP]     [Netdev]     [X.org]     [Util Linux NG]     [Fedora Women]     [ALSA Devel]     [Linux USB]

  Powered by Linux