X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=libsysfs%2Fsysfs_class.c;h=cd86912142791246a846ef7c3932b18cb2e83632;hb=616a7078071362b32a4db73fe3314feb46438258;hp=169600d5fd21f5812a8c68c8a810beea631615e4;hpb=5d4754f19521568b775ba7a31465d3af192ce382;p=elogind.git diff --git a/libsysfs/sysfs_class.c b/libsysfs/sysfs_class.c index 169600d5f..cd8691214 100644 --- a/libsysfs/sysfs_class.c +++ b/libsysfs/sysfs_class.c @@ -130,11 +130,12 @@ static void set_classdev_classname(struct sysfs_class_device *cdev) } /** - * sysfs_open_class_device: Opens and populates class device + * sysfs_open_class_device_path: Opens and populates class device * @path: path to class device. * returns struct sysfs_class_device with success and NULL with error. */ -struct sysfs_class_device *sysfs_open_class_device(const unsigned char *path) +struct sysfs_class_device *sysfs_open_class_device_path + (const unsigned char *path) { struct sysfs_class_device *cdev = NULL; @@ -159,6 +160,11 @@ struct sysfs_class_device *sysfs_open_class_device(const unsigned char *path) } strcpy(cdev->path, path); + if ((sysfs_remove_trailing_slash(cdev->path)) != 0) { + dprintf("Invalid path to class device %s\n", cdev->path); + sysfs_close_class_device(cdev); + return NULL; + } set_classdev_classname(cdev); return cdev; @@ -178,28 +184,34 @@ struct dlist *sysfs_get_class_devices(struct sysfs_class *cls) errno = EINVAL; return NULL; } + + if (cls->devices != NULL) + return cls->devices; + if (cls->directory == NULL) { cls->directory = sysfs_open_directory(cls->path); if (cls->directory == NULL) return NULL; } - if ((sysfs_read_dir_subdirs(cls->directory) != 0) - || cls->directory->subdirs == NULL) + if ((sysfs_read_dir_subdirs(cls->directory)) != 0) return NULL; - dlist_for_each_data(cls->directory->subdirs, cur, - struct sysfs_directory) { - dev = sysfs_open_class_device(cur->path); - if (dev == NULL) { - dprintf("Error opening device at %s\n", cur->path); - continue; - } - if (cls->devices == NULL) - cls->devices = dlist_new_with_delete + if (cls->directory->subdirs != NULL) { + dlist_for_each_data(cls->directory->subdirs, cur, + struct sysfs_directory) { + dev = sysfs_open_class_device_path(cur->path); + if (dev == NULL) { + dprintf("Error opening device at %s\n", + cur->path); + continue; + } + if (cls->devices == NULL) + cls->devices = dlist_new_with_delete (sizeof(struct sysfs_class_device), sysfs_close_cls_dev); - dlist_unshift(cls->devices, dev); + dlist_unshift(cls->devices, dev); + } } return cls->devices; } @@ -224,16 +236,15 @@ struct sysfs_class *sysfs_open_class(const unsigned char *name) return NULL; } - if (sysfs_trailing_slash(classpath) == 0) - strcat(classpath, "/"); - /* * We shall now treat "block" also as a class. Hence, check here * if "name" is "block" and proceed accordingly */ if (strcmp(name, SYSFS_BLOCK_NAME) == 0) { + strcat(classpath, "/"); strcat(classpath, SYSFS_BLOCK_NAME); } else { + strcat(classpath, "/"); strcat(classpath, SYSFS_CLASS_NAME); strcat(classpath, "/"); strcat(classpath, name); @@ -250,6 +261,11 @@ struct sysfs_class *sysfs_open_class(const unsigned char *name) } strcpy(cls->name, name); strcpy(cls->path, classpath); + if ((sysfs_remove_trailing_slash(cls->path)) != 0) { + dprintf("Invalid path to class device %s\n", cls->path); + sysfs_close_class(cls); + return NULL; + } return cls; } @@ -262,8 +278,6 @@ struct sysfs_class *sysfs_open_class(const unsigned char *name) struct sysfs_class_device *sysfs_get_class_device(struct sysfs_class *class, unsigned char *name) { - struct dlist *devlist = NULL; - if (class == NULL || name == NULL) { errno = EINVAL; return NULL; @@ -271,7 +285,7 @@ struct sysfs_class_device *sysfs_get_class_device(struct sysfs_class *class, if (class->devices == NULL) { class->devices = sysfs_get_class_devices(class); - if (devlist == NULL) + if (class->devices == NULL) return NULL; } return (struct sysfs_class_device *)dlist_find_custom(class->devices, @@ -289,14 +303,21 @@ struct sysfs_device *sysfs_get_classdev_device (struct sysfs_class_device *clsdev) { struct sysfs_link *devlink = NULL; + unsigned char devpath[SYSFS_PATH_MAX]; if (clsdev == NULL) { errno = EINVAL; return NULL; } - - if (clsdev->sysdevice != NULL) - return (clsdev->sysdevice); + strcpy(devpath, clsdev->path); + strcat(devpath, "/device"); + if ((sysfs_path_is_link(devpath)) != 0) { + if (clsdev->sysdevice != NULL) { + sysfs_close_device(clsdev->sysdevice); + clsdev->sysdevice = NULL; + } + return NULL; + } if (clsdev->directory == NULL) { clsdev->directory = sysfs_open_directory(clsdev->path); @@ -304,10 +325,26 @@ struct sysfs_device *sysfs_get_classdev_device return NULL; } devlink = sysfs_get_directory_link(clsdev->directory, "device"); - if (devlink == NULL) + if (devlink == NULL) { + if (clsdev->sysdevice != NULL) { + dprintf("Device link no longer exists\n"); + sysfs_close_device(clsdev->sysdevice); + clsdev->sysdevice = NULL; + } return NULL; + } - clsdev->sysdevice = sysfs_open_device(devlink->target); + if (clsdev->sysdevice != NULL) { + if (!strncmp(devlink->target, clsdev->sysdevice->path, + SYSFS_PATH_MAX)) + /* sysdevice hasn't changed */ + return (clsdev->sysdevice); + else + /* come here only if the device link for has changed */ + sysfs_close_device(clsdev->sysdevice); + } + + clsdev->sysdevice = sysfs_open_device_path(devlink->target); if (clsdev->sysdevice == NULL) return NULL; if (clsdev->driver != NULL) @@ -327,31 +364,56 @@ struct sysfs_driver *sysfs_get_classdev_driver (struct sysfs_class_device *clsdev) { struct sysfs_link *drvlink = NULL; + unsigned char drvpath[SYSFS_PATH_MAX]; if (clsdev == NULL) { errno = EINVAL; return NULL; } - - if (clsdev->driver != NULL) - return (clsdev->driver); - + strcpy(drvpath, clsdev->path); + strcat(drvpath, "/driver"); + if ((sysfs_path_is_link(drvpath)) != 0) { + if (clsdev->driver != NULL) { + sysfs_close_driver(clsdev->driver); + clsdev->driver = NULL; + } + return NULL; + } + if (clsdev->directory == NULL) { clsdev->directory = sysfs_open_directory(clsdev->path); if (clsdev->directory == NULL) return NULL; } drvlink = sysfs_get_directory_link(clsdev->directory, "driver"); - if (drvlink != NULL) { - clsdev->driver = sysfs_open_driver(drvlink->target); - if (clsdev->driver == NULL) - return NULL; - + if (drvlink == NULL) { + if (clsdev->driver != NULL) { + dprintf("Driver link no longer exists\n"); + sysfs_close_driver(clsdev->driver); + clsdev->driver = NULL; + } + return NULL; + } + if (clsdev->driver != NULL) { + if (!strncmp(drvlink->target, clsdev->driver->path, + SYSFS_PATH_MAX)) + /* driver hasn't changed */ + return (clsdev->driver); + else + /* come here only if the device link for has changed */ + sysfs_close_driver(clsdev->driver); } + + clsdev->driver = sysfs_open_driver_path(drvlink->target); + if (clsdev->driver == NULL) + return NULL; + if (clsdev->sysdevice != NULL) + strcpy(clsdev->sysdevice->driver_name, clsdev->driver->name); + return (clsdev->driver); } - -/* + +/** * get_blockdev_parent: Get the parent class device for a "block" subsystem * device if present * @clsdev: block subsystem class device whose parent needs to be found @@ -359,49 +421,35 @@ struct sysfs_driver *sysfs_get_classdev_driver */ static int get_blockdev_parent(struct sysfs_class_device *clsdev) { - unsigned char parent_path[SYSFS_PATH_MAX], value[256], *c = NULL; - - memset(parent_path, 0, SYSFS_PATH_MAX); - strcpy(parent_path, clsdev->path); + unsigned char parent_path[SYSFS_PATH_MAX], *c = NULL; + strcpy(parent_path, clsdev->path); c = strstr(parent_path, SYSFS_BLOCK_NAME); if (c == NULL) { - dprintf("Class device %s does not belong to BLOCK subsystem", + dprintf("Class device %s does not belong to BLOCK subsystem\n", clsdev->name); return 1; } - + c += strlen(SYSFS_BLOCK_NAME); if (*c == '/') c++; else goto errout; - - /* validate whether the given class device is a partition or not */ - if ((strncmp(c, clsdev->name, strlen(clsdev->name))) == 0) { - dprintf("%s not a partition\n", clsdev->name); - return 1; - } - c = strchr(c, '/'); - if (c == NULL) - goto errout; - *c = '\0'; - - /* Now validate if the parent has the "dev" attribute */ - memset(value, 0, 256); - strcat(parent_path, "/dev"); - if ((sysfs_read_attribute_value(parent_path, value, 256)) != 0) { - dprintf("Block device %s does not have a parent\n", - clsdev->name); - return 1; - } - - c = strrchr(parent_path, '/'); + + /* validate whether the given class device is a partition or not */ + if ((strncmp(c, clsdev->name, strlen(clsdev->name))) == 0) { + dprintf("%s not a partition\n", clsdev->name); + return 1; + } + + c = strchr(c, '/'); if (c == NULL) goto errout; *c = '\0'; - clsdev->parent = sysfs_open_class_device(parent_path); + + clsdev->parent = sysfs_open_class_device_path(parent_path); if (clsdev->parent == NULL) { dprintf("Error opening the parent class device at %s\n", parent_path); @@ -465,13 +513,11 @@ static int get_classdev_path(const unsigned char *classname, dprintf("Error getting sysfs mount path\n"); return -1; } - - if (sysfs_trailing_slash(path) == 0) - strcat(path, "/"); - if (strcmp(classname, SYSFS_BLOCK_NAME) == 0) { + strcat(path, "/"); strcat(path, SYSFS_BLOCK_NAME); } else { + strcat(path, "/"); strcat(path, SYSFS_CLASS_NAME); strcat(path, "/"); strcat(path, classname); @@ -482,7 +528,7 @@ static int get_classdev_path(const unsigned char *classname, } /** - * sysfs_open_class_device_by_name: Locates a specific class_device and returns it. + * sysfs_open_class_device: Locates a specific class_device and returns it. * Class_device must be closed using sysfs_close_class_device * @classname: Class to search * @name: name of the class_device @@ -490,7 +536,7 @@ static int get_classdev_path(const unsigned char *classname, * NOTE: * Call sysfs_close_class_device() to close the class device */ -struct sysfs_class_device *sysfs_open_class_device_by_name +struct sysfs_class_device *sysfs_open_class_device (const unsigned char *classname, const unsigned char *name) { unsigned char devpath[SYSFS_PATH_MAX]; @@ -509,7 +555,7 @@ struct sysfs_class_device *sysfs_open_class_device_by_name return NULL; } - cdev = sysfs_open_class_device(devpath); + cdev = sysfs_open_class_device_path(devpath); if (cdev == NULL) { dprintf("Error getting class device %s from class %s\n", name, classname); @@ -535,26 +581,40 @@ struct dlist *sysfs_get_classdev_attributes(struct sysfs_class_device *cdev) return NULL; } if (cdev->directory->attributes == NULL) { - if ((sysfs_read_dir_attributes(cdev->directory)) != 0) { - dprintf("Error reading attributes for directory %s\n", - cdev->directory->path); - return NULL; - } - } else { - if ((sysfs_path_is_dir(cdev->path)) != 0) { - dprintf("Class device at %s no longer exists\n", - cdev->path); - return NULL; - } - if ((sysfs_refresh_attributes - (cdev->directory->attributes)) != 0) { - dprintf("Error refreshing classdev attributes\n"); + if ((sysfs_read_dir_attributes(cdev->directory)) != 0) return NULL; - } } return (cdev->directory->attributes); } +/** + * sysfs_refresh_clsassdev_attributes: refreshes the driver's list of attributes + * @clsdev: sysfs_class_device whose attributes to refresh + * + * NOTE: Upon return, prior references to sysfs_attributes for this classdev + * _may_ not be valid + * + * Returns list of attributes on success and NULL on failure + */ +struct dlist *sysfs_refresh_classdev_attributes + (struct sysfs_class_device *clsdev) +{ + if (clsdev == NULL) { + errno = EINVAL; + return NULL; + } + + if (clsdev->directory == NULL) + return (sysfs_get_classdev_attributes(clsdev)); + + if ((sysfs_refresh_dir_attributes(clsdev->directory)) != 0) { + dprintf("Error refreshing class_device attributes\n"); + return NULL; + } + + return (clsdev->directory->attributes); +} + /** * sysfs_get_classdev_attr: searches class device's attributes by name * @clsdev: class device to look through @@ -572,32 +632,36 @@ struct sysfs_attribute *sysfs_get_classdev_attr errno = EINVAL; return NULL; } + /* * First, see if it's in the current directory. Then look at * subdirs since class devices can have subdirs of attributes. */ attrlist = sysfs_get_classdev_attributes(clsdev); - if (attrlist == NULL) - return NULL; - cur = sysfs_get_directory_attribute(clsdev->directory, + if (attrlist != NULL) { + cur = sysfs_get_directory_attribute(clsdev->directory, (unsigned char *)name); - if (cur != NULL) - return cur; + if (cur != NULL) + return cur; + } if (clsdev->directory->subdirs == NULL) if ((sysfs_read_dir_subdirs(clsdev->directory)) != 0 || clsdev->directory->subdirs == NULL) return NULL; - dlist_for_each_data(clsdev->directory->subdirs, sdir, - struct sysfs_directory) { - cur = sysfs_get_directory_attribute(sdir, - (unsigned char *)name); - if (cur != NULL) - return cur; + if (clsdev->directory->subdirs != NULL) { + dlist_for_each_data(clsdev->directory->subdirs, sdir, + struct sysfs_directory) { + if ((sysfs_path_is_dir(sdir->path)) != 0) + continue; + cur = sysfs_get_directory_attribute(sdir, + (unsigned char *)name); + if (cur == NULL) + continue; + } } - - return NULL; + return cur; } /** @@ -643,3 +707,4 @@ struct sysfs_attribute *sysfs_open_classdev_attr(const unsigned char *classname, } return attribute; } +