X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibudev%2Flibudev-device.c;h=9f80f56d4206b9b5e90156fb44c80615c88900ae;hb=7619683b46bddcf753786fd20581322da9825f99;hp=9fe3d633d4dbaa5ccedbabe7bf96a4de53cf0a3b;hpb=d5a89d7dc17a5ba5cf4fc71f82963c5c94a31c3d;p=elogind.git diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c index 9fe3d633d..9f80f56d4 100644 --- a/src/libudev/libudev-device.c +++ b/src/libudev/libudev-device.c @@ -36,13 +36,15 @@ #include "libudev.h" #include "libudev-private.h" +static int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode); + /** * SECTION:libudev-device * @short_description: kernel sys devices * * Representation of kernel sys devices. Devices are uniquely identified * by their syspath, every device has exactly one path in the kernel sys - * filesystem. Devices usually belong to a kernel subsystem, and and have + * filesystem. Devices usually belong to a kernel subsystem, and have * a unique name inside that subsystem. */ @@ -534,8 +536,10 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) f = fopen(dbfile, "re"); if (f == NULL) { udev_dbg(udev_device->udev, "no db file to read %s: %m\n", dbfile); - return -1; + return -errno; } + + /* devices with a database entry are initialized */ udev_device->is_initialized = true; while (fgets(line, sizeof(line), f)) { @@ -591,7 +595,7 @@ int udev_device_read_uevent_file(struct udev_device *udev_device) strscpyl(filename, sizeof(filename), udev_device->syspath, "/uevent", NULL); f = fopen(filename, "re"); if (f == NULL) - return -1; + return -errno; udev_device->uevent_loaded = true; while (fgets(line, sizeof(line), f)) { @@ -643,7 +647,7 @@ struct udev_device *udev_device_new(struct udev *udev) if (udev == NULL) return NULL; - udev_device = calloc(1, sizeof(struct udev_device)); + udev_device = new0(struct udev_device, 1); if (udev_device == NULL) return NULL; udev_device->refcount = 1; @@ -780,7 +784,7 @@ _public_ struct udev_device *udev_device_new_from_devnum(struct udev *udev, char * * Returns: a new udev device, or #NULL, if it does not exist **/ -_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, char *id) +_public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id) { char type; int maj, min; @@ -806,7 +810,7 @@ _public_ struct udev_device *udev_device_new_from_device_id(struct udev *udev, c sk = socket(PF_INET, SOCK_DGRAM, 0); if (sk < 0) return NULL; - memset(&ifr, 0x00, sizeof(struct ifreq)); + memzero(&ifr, sizeof(struct ifreq)); ifr.ifr_ifindex = ifindex; if (ioctl(sk, SIOCGIFNAME, &ifr) != 0) { close(sk); @@ -980,9 +984,8 @@ static struct udev_device *device_new_from_parent(struct udev_device *udev_devic * Find the next parent device, and fill in information from the sys * device and the udev database entry. * - * The returned the device is not referenced. It is attached to the - * child device, and will be cleaned up when the child device - * is cleaned up. + * Returned device is not referenced. It is attached to the child + * device, and will be cleaned up when the child device is cleaned up. * * It is not necessarily just the upper level directory, empty or not * recognized sys directories are ignored. @@ -1016,9 +1019,8 @@ _public_ struct udev_device *udev_device_get_parent(struct udev_device *udev_dev * If devtype is #NULL, only subsystem is checked, and any devtype will * match. * - * The returned the device is not referenced. It is attached to the - * child device, and will be cleaned up when the child device - * is cleaned up. + * Returned device is not referenced. It is attached to the child + * device, and will be cleaned up when the child device is cleaned up. * * It can be called as many times as needed, without caring about * references. @@ -1088,7 +1090,7 @@ _public_ struct udev_device *udev_device_ref(struct udev_device *udev_device) * Drop a reference of a udev device. If the refcount reaches zero, * the resources of the device will be released. * - * Returns: the passed udev device if it has still an active reference, or #NULL otherwise. + * Returns: #NULL **/ _public_ struct udev_device *udev_device_unref(struct udev_device *udev_device) { @@ -1096,7 +1098,7 @@ _public_ struct udev_device *udev_device_unref(struct udev_device *udev_device) return NULL; udev_device->refcount--; if (udev_device->refcount > 0) - return udev_device; + return NULL; if (udev_device->parent_device != NULL) udev_device_unref(udev_device->parent_device); free(udev_device->syspath); @@ -1381,8 +1383,6 @@ _public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_devi } if (S_ISLNK(statbuf.st_mode)) { - struct udev_device *dev; - /* * Some core links return only the last element of the target path, * these are just values, the paths should not be exposed. @@ -1398,17 +1398,6 @@ _public_ const char *udev_device_get_sysattr_value(struct udev_device *udev_devi goto out; } - /* resolve custom link to a device and return its syspath */ - if (!streq(sysattr, "device")) { - strscpyl(path, sizeof(path), udev_device->syspath, "/", sysattr, NULL); - dev = udev_device_new_from_syspath(udev_device->udev, path); - if (dev != NULL) { - list_entry = udev_list_entry_add(&udev_device->sysattr_value_list, sysattr, - udev_device_get_syspath(dev)); - val = udev_list_entry_get_value(list_entry); - udev_device_unref(dev); - } - } goto out; } @@ -1440,6 +1429,91 @@ 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); + + 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)) { + 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; @@ -1447,13 +1521,13 @@ static int udev_device_sysattr_list_read(struct udev_device *udev_device) int num = 0; if (udev_device == NULL) - return -1; + return -EINVAL; if (udev_device->sysattr_list_read) return 0; dir = opendir(udev_device_get_syspath(udev_device)); if (!dir) - return -1; + return -errno; for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { char path[UTIL_PATH_SIZE]; @@ -1539,7 +1613,7 @@ int udev_device_set_syspath(struct udev_device *udev_device, const char *syspath return 0; } -int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode) +static int udev_device_set_devnode(struct udev_device *udev_device, const char *devnode) { free(udev_device->devnode); if (devnode[0] != '/') {