summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHannes Reinecke <hare@suse.de>2013-06-02 11:40:15 -0400
committerAnthony G. Basile <blueness@gentoo.org>2013-06-02 11:42:02 -0400
commitebcfcd41acc4a7b10155662f72a173b77842aa16 (patch)
tree9e8c4cfab75ad63a2755b37d2decd991436d50ed
parentebbcb55a2d488668cf779fab6cb1100e9a3f2298 (diff)
libudev: implement udev_device_set_attribute_value()
Signed-off-by: Anthony G. Basile <blueness@gentoo.org>
-rw-r--r--src/libudev/Makefile.am4
-rw-r--r--src/libudev/libudev-device.c102
2 files changed, 104 insertions, 2 deletions
diff --git a/src/libudev/Makefile.am b/src/libudev/Makefile.am
index 98d6e4e0f8..683944a671 100644
--- a/src/libudev/Makefile.am
+++ b/src/libudev/Makefile.am
@@ -1,8 +1,8 @@
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
-LIBUDEV_CURRENT=3
+LIBUDEV_CURRENT=4
LIBUDEV_REVISION=0
-LIBUDEV_AGE=2
+LIBUDEV_AGE=3
define move-to-rootlibdir
if test "$(libdir)" != "$(rootlibdir)"; then \
diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c
index 3c9783ffd8..fd3e694ef6 100644
--- a/src/libudev/libudev-device.c
+++ b/src/libudev/libudev-device.c
@@ -1535,6 +1535,108 @@ out:
return ret;
}
+/**
+ * udev_device_set_sysattr_value:
+ * @udev_device: udev device
+ * @sysattr: attribute name
+ * @value: new value to be set
+ *
+ * Update the contents of the sys attribute and the cached value of the device.
+ *
+ * Returns: Negative error code on failure or 0 on success.
+ **/
+_public_ int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value)
+{
+ struct udev_device *dev;
+ char path[UTIL_PATH_SIZE];
+ struct stat statbuf;
+ int fd;
+ ssize_t size, value_len;
+ int ret = 0;
+
+ if (udev_device == NULL)
+ return -EINVAL;
+ dev = udev_device;
+ if (sysattr == NULL)
+ return -EINVAL;
+ if (value == NULL)
+ value_len = 0;
+ else
+ value_len = strlen(value);
+restart:
+ strscpyl(path, sizeof(path), udev_device_get_syspath(dev), "/", sysattr, NULL);
+ if (lstat(path, &statbuf) != 0) {
+ udev_list_entry_add(&dev->sysattr_value_list, sysattr, NULL);
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (S_ISLNK(statbuf.st_mode)) {
+ /*
+ * Cannot modify core link values
+ */
+ if (streq(sysattr, "driver") ||
+ streq(sysattr, "subsystem") ||
+ streq(sysattr, "module")) {
+ ret = -EPERM;
+ } else if (!streq(sysattr, "device")) {
+ /* resolve custom link to a device */
+ strscpyl(path, sizeof(path), udev_device->syspath, "/", sysattr, NULL);
+ dev = udev_device_new_from_syspath(udev_device->udev, path);
+ if (dev != NULL)
+ goto restart;
+ ret = -ENXIO;
+ } else {
+ /* Unhandled, to not try to modify anything */
+ ret = -EINVAL;
+ }
+ goto out;
+ }
+
+ /* skip directories */
+ if (S_ISDIR(statbuf.st_mode)) {
+ ret = -EISDIR;
+ goto out;
+ }
+
+ /* skip non-readable files */
+ if ((statbuf.st_mode & S_IRUSR) == 0) {
+ ret = -EACCES;
+ goto out;
+ }
+
+ /* Value is limited to 4k */
+ if (value_len > 4096) {
+ ret = -EINVAL;
+ goto out;
+ }
+ util_remove_trailing_chars(value, '\n');
+
+ /* write attribute value */
+ fd = open(path, O_WRONLY|O_CLOEXEC);
+ if (fd < 0) {
+ ret = -errno;
+ goto out;
+ }
+ size = write(fd, value, value_len);
+ close(fd);
+ if (size < 0) {
+ ret = -errno;
+ goto out;
+ }
+ if (size < value_len) {
+ ret = -EIO;
+ goto out;
+ }
+
+ /* wrote a valid value, store it in cache and return it */
+ udev_list_entry_add(&dev->sysattr_value_list, sysattr, value);
+out:
+ if (dev != udev_device)
+ udev_device_unref(dev);
+ return ret;
+}
+
static int udev_device_sysattr_list_read(struct udev_device *udev_device)
{
struct dirent *dent;