File multipath-tools-implement-sysfs-attr-set-value of Package multipath-tools

From e3354354518377b34fa3b501c11a62b61a54b0d4 Mon Sep 17 00:00:00 2001
From: Hannes Reinecke <hare@suse.de>
Date: Tue, 13 Jan 2009 15:34:55 +0100
Subject: [PATCH] Implement sysfs_attr_set_value()

Complement to sysfs_attr_get_value(). We might want to modify some
sysfs values from multipathing.

References: bnc#464155

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 libmultipath/sysfs.c |  100 ++++++++++++++++++++++++++++++++++++++++++++++++++
 libmultipath/sysfs.h |    2 +
 2 files changed, 102 insertions(+), 0 deletions(-)

diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c
index e00a101..4a472ae 100644
--- a/libmultipath/sysfs.c
+++ b/libmultipath/sysfs.c
@@ -461,6 +461,106 @@ out:
 	return attr && attr->value && strlen(attr->value) ? attr->value : NULL;
 }
 
+int sysfs_attr_set_value(const char *devpath, const char *attr_name,
+			 char *value, int value_len)
+{
+	char path_full[PATH_SIZE];
+	const char *path;
+	struct sysfs_attr *attr_loop;
+	struct sysfs_attr *attr = NULL;
+	struct stat statbuf;
+	int fd;
+	ssize_t size = 0;
+	size_t sysfs_len;
+
+	dbg("open '%s'/'%s'", devpath, attr_name);
+	sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
+	if(sysfs_len >= sizeof(path_full))
+		sysfs_len = sizeof(path_full) - 1;
+	path = &path_full[sysfs_len];
+	strlcat(path_full, devpath, sizeof(path_full));
+	strlcat(path_full, "/", sizeof(path_full));
+	strlcat(path_full, attr_name, sizeof(path_full));
+
+	/* look for attribute in cache */
+	list_for_each_entry(attr_loop, &attr_list, node) {
+		if (strcmp(attr_loop->path, path) == 0) {
+			dbg("found in cache '%s'", attr_loop->path);
+			attr = attr_loop;
+		}
+	}
+	if (!attr) {
+		/* store attribute in cache */
+		dbg("new uncached attribute '%s'", path_full);
+		attr = malloc(sizeof(struct sysfs_attr));
+		if (attr == NULL)
+			return -1;
+		memset(attr, 0x00, sizeof(struct sysfs_attr));
+		strlcpy(attr->path, path, sizeof(attr->path));
+		dbg("add to cache '%s'", path_full);
+		list_add(&attr->node, &attr_list);
+	} else {
+		/* clear old value */
+		if(attr->value)
+			memset(attr->value, 0x00, sizeof(attr->value));
+	}
+
+	if (lstat(path_full, &statbuf) != 0) {
+		dbg("stat '%s' failed: %s", path_full, strerror(errno));
+		goto out;
+	}
+
+	if (S_ISLNK(statbuf.st_mode)) {
+		/* links return the last element of the target path */
+		char link_target[PATH_SIZE];
+		int len;
+		const char *pos;
+
+		len = readlink(path_full, link_target, sizeof(link_target));
+		if (len > 0) {
+			link_target[len] = '\0';
+			pos = strrchr(link_target, '/');
+			if (pos != NULL) {
+				dbg("cache '%s' with link value '%s'",
+				    path_full, value);
+				strlcpy(attr->value_local, &pos[1],
+					sizeof(attr->value_local));
+				attr->value = attr->value_local;
+			}
+		}
+		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;
+
+	/* write attribute value */
+	fd = open(path_full, O_WRONLY);
+	if (fd < 0) {
+		dbg("attribute '%s' can not be opened: %s",
+		    path_full, strerror(errno));
+		goto out;
+	}
+	size = write(fd, value, value_len);
+	close(fd);
+	if (size < 0)
+		goto out;
+
+	/* successfully wrote new value, store it */
+	remove_trailing_chars(value, '\n');
+	dbg("cache '%s' with attribute value '%s'", path_full, value);
+	strlcpy(attr->value_local, value, sizeof(attr->value_local));
+	attr->value = attr->value_local;
+
+out:
+	return size;
+}
+
 int sysfs_lookup_devpath_by_subsys_id(char *devpath_full, size_t len,
 				      const char *subsystem, const char *id)
 {
diff --git a/libmultipath/sysfs.h b/libmultipath/sysfs.h
index 620962c..ac76acf 100644
--- a/libmultipath/sysfs.h
+++ b/libmultipath/sysfs.h
@@ -20,6 +20,8 @@ struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev);
 struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem);
 void sysfs_device_put(struct sysfs_device *dev);
 char *sysfs_attr_get_value(const char *devpath, const char *attr_name);
+int sysfs_attr_set_value(const char *devpath, const char *attr_name,
+			 char *value, int value_len);
 int sysfs_resolve_link(char *path, size_t size);
 int sysfs_get_size (struct sysfs_device * dev, unsigned long long * size);
 
-- 
1.5.3.2

openSUSE Build Service is sponsored by