summaryrefslogtreecommitdiff
path: root/src/libudev
diff options
context:
space:
mode:
Diffstat (limited to 'src/libudev')
-rw-r--r--src/libudev/libudev-device.c102
-rw-r--r--src/libudev/libudev.h1
-rw-r--r--src/libudev/libudev.sym4
3 files changed, 107 insertions, 0 deletions
diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c
index 9fe3d633d4..05c5fbce98 100644
--- a/src/libudev/libudev-device.c
+++ b/src/libudev/libudev-device.c
@@ -1440,6 +1440,108 @@ out:
return val;
}
+/**
+ * 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;
diff --git a/src/libudev/libudev.h b/src/libudev/libudev.h
index bb41532a21..61567b1d67 100644
--- a/src/libudev/libudev.h
+++ b/src/libudev/libudev.h
@@ -107,6 +107,7 @@ const char *udev_device_get_action(struct udev_device *udev_device);
unsigned long long int udev_device_get_seqnum(struct udev_device *udev_device);
unsigned long long int udev_device_get_usec_since_initialized(struct udev_device *udev_device);
const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const char *sysattr);
+int udev_device_set_sysattr_value(struct udev_device *udev_device, const char *sysattr, char *value);
int udev_device_has_tag(struct udev_device *udev_device, const char *tag);
/*
diff --git a/src/libudev/libudev.sym b/src/libudev/libudev.sym
index df6a1aeddf..c232fbfdd5 100644
--- a/src/libudev/libudev.sym
+++ b/src/libudev/libudev.sym
@@ -108,3 +108,7 @@ global:
udev_hwdb_unref;
udev_hwdb_get_properties_list_entry;
} LIBUDEV_189;
+
+LIBUDEV_199 {
+ udev_device_set_sysattr_value;
+} LIBUDEV_196;