diff options
Diffstat (limited to 'libsysfs/sysfs_attr.c')
-rw-r--r-- | libsysfs/sysfs_attr.c | 632 |
1 files changed, 632 insertions, 0 deletions
diff --git a/libsysfs/sysfs_attr.c b/libsysfs/sysfs_attr.c new file mode 100644 index 0000000..8819926 --- /dev/null +++ b/libsysfs/sysfs_attr.c @@ -0,0 +1,632 @@ +/* + * sysfs_dir.c + * + * Directory utility functions for libsysfs + * + * Copyright (C) IBM Corp. 2003-2005 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "libsysfs.h" +#include "sysfs.h" + +static int sort_char(void *new, void *old) +{ + return ((strncmp((char *)new, (char *)old, + strlen((char *)new))) < 0 ? 1 : 0); +} + +/** + * sysfs_del_name: free function for sysfs_open_subsystem_list + * @name: memory area to be freed + */ +static void sysfs_del_name(void *name) +{ + free(name); +} + +/** + * sysfs_del_attribute: routine for dlist integration + */ +static void sysfs_del_attribute(void *attr) +{ + sysfs_close_attribute((struct sysfs_attribute *)attr); +} + +/** + * attr_name_equal: compares attributes by name + * @a: attribute name for comparison + * @b: sysfs_attribute to be compared. + * returns 1 if a==b->name or 0 if not equal + */ +static int attr_name_equal(void *a, void *b) +{ + if (!a || !b) + return 0; + + if (strcmp(((char *)a), ((struct sysfs_attribute *)b)->name) == 0) + return 1; + + return 0; +} + +/** + * sysfs_close_attribute: closes and cleans up attribute + * @sysattr: attribute to close. + */ +void sysfs_close_attribute(struct sysfs_attribute *sysattr) +{ + if (sysattr) { + if (sysattr->value) + free(sysattr->value); + free(sysattr); + } +} + +/** + * alloc_attribute: allocates and initializes attribute structure + * returns struct sysfs_attribute with success and NULL with error. + */ +static struct sysfs_attribute *alloc_attribute(void) +{ + return (struct sysfs_attribute *) + calloc(1, sizeof(struct sysfs_attribute)); +} + +/** + * sysfs_open_attribute: creates sysfs_attribute structure + * @path: path to attribute. + * returns sysfs_attribute struct with success and NULL with error. + */ +struct sysfs_attribute *sysfs_open_attribute(const char *path) +{ + struct sysfs_attribute *sysattr = NULL; + struct stat fileinfo; + + if (!path) { + errno = EINVAL; + return NULL; + } + sysattr = alloc_attribute(); + if (!sysattr) { + dprintf("Error allocating attribute at %s\n", path); + return NULL; + } + if (sysfs_get_name_from_path(path, sysattr->name, + SYSFS_NAME_LEN) != 0) { + dprintf("Error retrieving attrib name from path: %s\n", path); + sysfs_close_attribute(sysattr); + return NULL; + } + safestrcpy(sysattr->path, path); + if ((stat(sysattr->path, &fileinfo)) != 0) { + dprintf("Stat failed: No such attribute?\n"); + sysattr->method = 0; + free(sysattr); + sysattr = NULL; + } else { + if (fileinfo.st_mode & S_IRUSR) + sysattr->method |= SYSFS_METHOD_SHOW; + if (fileinfo.st_mode & S_IWUSR) + sysattr->method |= SYSFS_METHOD_STORE; + } + + return sysattr; +} + +/** + * sysfs_read_attribute: reads value from attribute + * @sysattr: attribute to read + * returns 0 with success and -1 with error. + */ +int sysfs_read_attribute(struct sysfs_attribute *sysattr) +{ + char *fbuf = NULL; + char *vbuf = NULL; + ssize_t length = 0; + long pgsize = 0; + int fd; + + if (!sysattr) { + errno = EINVAL; + return -1; + } + if (!(sysattr->method & SYSFS_METHOD_SHOW)) { + dprintf("Show method not supported for attribute %s\n", + sysattr->path); + errno = EACCES; + return -1; + } + pgsize = getpagesize(); + fbuf = (char *)calloc(1, pgsize+1); + if (!fbuf) { + dprintf("calloc failed\n"); + return -1; + } + if ((fd = open(sysattr->path, O_RDONLY)) < 0) { + dprintf("Error reading attribute %s\n", sysattr->path); + free(fbuf); + return -1; + } + length = read(fd, fbuf, pgsize); + if (length < 0) { + dprintf("Error reading from attribute %s\n", sysattr->path); + close(fd); + free(fbuf); + return -1; + } + if (sysattr->len > 0) { + if ((sysattr->len == length) && + (!(strncmp(sysattr->value, fbuf, length)))) { + close(fd); + free(fbuf); + return 0; + } + free(sysattr->value); + } + sysattr->len = length; + close(fd); + vbuf = (char *)realloc(fbuf, length+1); + if (!vbuf) { + dprintf("realloc failed\n"); + free(fbuf); + return -1; + } + sysattr->value = vbuf; + + return 0; +} + +/** + * sysfs_write_attribute: write value to the attribute + * @sysattr: attribute to write + * @new_value: value to write + * @len: length of "new_value" + * returns 0 with success and -1 with error. + */ +int sysfs_write_attribute(struct sysfs_attribute *sysattr, + const char *new_value, size_t len) +{ + int fd; + int length; + + if (!sysattr || !new_value || len == 0) { + errno = EINVAL; + return -1; + } + + if (!(sysattr->method & SYSFS_METHOD_STORE)) { + dprintf ("Store method not supported for attribute %s\n", + sysattr->path); + errno = EACCES; + return -1; + } + if (sysattr->method & SYSFS_METHOD_SHOW) { + /* + * read attribute again to see if we can get an updated value + */ + if ((sysfs_read_attribute(sysattr))) { + dprintf("Error reading attribute\n"); + return -1; + } + if ((strncmp(sysattr->value, new_value, sysattr->len)) == 0 && + (len == sysattr->len)) { + dprintf("Attr %s already has the requested value %s\n", + sysattr->name, new_value); + return 0; + } + } + /* + * open O_WRONLY since some attributes have no "read" but only + * "write" permission + */ + if ((fd = open(sysattr->path, O_WRONLY)) < 0) { + dprintf("Error reading attribute %s\n", sysattr->path); + return -1; + } + + length = write(fd, new_value, len); + if (length < 0) { + dprintf("Error writing to the attribute %s - invalid value?\n", + sysattr->name); + close(fd); + return -1; + } else if ((unsigned int)length != len) { + dprintf("Could not write %zd bytes to attribute %s\n", + len, sysattr->name); + /* + * since we could not write user supplied number of bytes, + * restore the old value if one available + */ + if (sysattr->method & SYSFS_METHOD_SHOW) { + length = write(fd, sysattr->value, sysattr->len); + close(fd); + return -1; + } + } + + /* + * Validate length that has been copied. Alloc appropriate area + * in sysfs_attribute. Verify first if the attribute supports reading + * (show method). If it does not, do not bother + */ + if (sysattr->method & SYSFS_METHOD_SHOW) { + if (length != sysattr->len) { + sysattr->value = (char *)realloc + (sysattr->value, length); + sysattr->len = length; + safestrcpymax(sysattr->value, new_value, length); + } else { + /*"length" of the new value is same as old one */ + safestrcpymax(sysattr->value, new_value, length); + } + } + + close(fd); + return 0; +} + +/** + * add_attribute_to_list: open and add attribute at path to given dlist + * @list: dlist attribute is to be added + * @path: path to attribute + * returns pointer to attr added with success and NULL with error. + */ +static struct sysfs_attribute *add_attribute_to_list(struct dlist *alist, + const char *path) +{ + struct sysfs_attribute *attr; + + attr = sysfs_open_attribute(path); + if (!attr) { + dprintf("Error opening attribute %s\n", path); + return NULL; + } + if (attr->method & SYSFS_METHOD_SHOW) { + if (sysfs_read_attribute(attr)) { + dprintf("Error reading attribute %s\n", path); + sysfs_close_attribute(attr); + return NULL; + } + } + + if (!alist) { + alist = dlist_new_with_delete + (sizeof(struct sysfs_attribute), sysfs_del_attribute); + } + dlist_unshift_sorted(alist, attr, sort_list); + return attr; +} + +/** + * add_attribute: open and add attribute at path to given directory + * @dev: device whose attribute is to be added + * @path: path to attribute + * returns pointer to attr added with success and NULL with error. + */ +static struct sysfs_attribute *add_attribute(void *dev, const char *path) +{ + struct sysfs_attribute *attr; + + attr = sysfs_open_attribute(path); + if (!attr) { + dprintf("Error opening attribute %s\n", path); + return NULL; + } + if (attr->method & SYSFS_METHOD_SHOW) { + if (sysfs_read_attribute(attr)) { + dprintf("Error reading attribute %s\n", path); + sysfs_close_attribute(attr); + return NULL; + } + } + + if (!((struct sysfs_device *)dev)->attrlist) { + ((struct sysfs_device *)dev)->attrlist = dlist_new_with_delete + (sizeof(struct sysfs_attribute), sysfs_del_attribute); + } + dlist_unshift_sorted(((struct sysfs_device *)dev)->attrlist, + attr, sort_list); + + return attr; +} + +/* + * get_attribute - given a sysfs_* struct and a name, return the + * sysfs_attribute corresponding to "name" + * returns sysfs_attribute on success and NULL on error + */ +struct sysfs_attribute *get_attribute(void *dev, const char *name) +{ + struct sysfs_attribute *cur = NULL; + char path[SYSFS_PATH_MAX]; + + if (!dev || !name) { + errno = EINVAL; + return NULL; + } + + if (((struct sysfs_device *)dev)->attrlist) { + /* check if attr is already in the list */ + cur = (struct sysfs_attribute *)dlist_find_custom + ((((struct sysfs_device *)dev)->attrlist), + (void *)name, attr_name_equal); + if (cur) + return cur; + } + safestrcpymax(path, ((struct sysfs_device *)dev)->path, + SYSFS_PATH_MAX); + safestrcatmax(path, "/", SYSFS_PATH_MAX); + safestrcatmax(path, name, SYSFS_PATH_MAX); + if (!sysfs_path_is_file(path)) + cur = add_attribute((void *)dev, path); + return cur; +} + +/** + * read_dir_links: grabs links in a specific directory + * @sysdir: sysfs directory to read + * returns list of link names with success and NULL with error. + */ +struct dlist *read_dir_links(const char *path) +{ + DIR *dir = NULL; + struct dirent *dirent = NULL; + char file_path[SYSFS_PATH_MAX], *linkname; + struct dlist *linklist = NULL; + + if (!path) { + errno = EINVAL; + return NULL; + } + dir = opendir(path); + if (!dir) { + dprintf("Error opening directory %s\n", path); + return NULL; + } + while ((dirent = readdir(dir)) != NULL) { + if (0 == strcmp(dirent->d_name, ".")) + continue; + if (0 == strcmp(dirent->d_name, "..")) + continue; + memset(file_path, 0, SYSFS_PATH_MAX); + safestrcpy(file_path, path); + safestrcat(file_path, "/"); + safestrcat(file_path, dirent->d_name); + if (!sysfs_path_is_link(file_path)) { + if (!linklist) { + linklist = dlist_new_with_delete + (SYSFS_NAME_LEN, sysfs_del_name); + if (!linklist) { + dprintf("Error creating list\n"); + return NULL; + } + } + linkname = (char *)calloc(1, SYSFS_NAME_LEN); + safestrcpymax(linkname, dirent->d_name, SYSFS_NAME_LEN); + dlist_unshift_sorted(linklist, linkname, sort_char); + } + } + closedir(dir); + return linklist; +} + +void sysfs_close_dev_tree(void *dev); + +int add_subdirectory(struct sysfs_device *dev, char *path) +{ + struct sysfs_device *newdev; + + if (!path) + return -1; + + newdev = sysfs_open_device_path(path); + if (newdev == NULL) + return -1; + + if (dev->children == NULL) + dev->children = dlist_new_with_delete( + sizeof(struct sysfs_device), sysfs_close_dev_tree); + + dlist_unshift_sorted(dev->children, newdev, sort_list); + return 0; +} + +/** + * read_dir_subdirs: grabs subdirs in a specific directory + * @sysdir: sysfs directory to read + * returns list of directory names with success and NULL with error. + */ +struct sysfs_device *sysfs_read_dir_subdirs(const char *path) +{ + DIR *dir = NULL; + struct dirent *dirent = NULL; + char file_path[SYSFS_PATH_MAX]; + struct sysfs_device *dev = NULL; + + if (!path) { + errno = EINVAL; + return NULL; + } + + dev = sysfs_open_device_path(path); + + dir = opendir(path); + if (!dir) { + dprintf("Error opening directory %s\n", path); + return NULL; + } + while ((dirent = readdir(dir)) != NULL) { + if (0 == strcmp(dirent->d_name, ".")) + continue; + if (0 == strcmp(dirent->d_name, "..")) + continue; + memset(file_path, 0, SYSFS_PATH_MAX); + safestrcpy(file_path, path); + safestrcat(file_path, "/"); + safestrcat(file_path, dirent->d_name); + if (!sysfs_path_is_dir(file_path)) + add_subdirectory(dev, file_path); + } + closedir(dir); + return dev; +} + +/** + * read_dir_subdirs: grabs subdirs in a specific directory + * @sysdir: sysfs directory to read + * returns list of directory names with success and NULL with error. + */ +struct dlist *read_dir_subdirs(const char *path) +{ + DIR *dir = NULL; + struct dirent *dirent = NULL; + char file_path[SYSFS_PATH_MAX], *dir_name; + struct dlist *dirlist = NULL; + + if (!path) { + errno = EINVAL; + return NULL; + } + dir = opendir(path); + if (!dir) { + dprintf("Error opening directory %s\n", path); + return NULL; + } + while ((dirent = readdir(dir)) != NULL) { + if (0 == strcmp(dirent->d_name, ".")) + continue; + if (0 == strcmp(dirent->d_name, "..")) + continue; + memset(file_path, 0, SYSFS_PATH_MAX); + safestrcpy(file_path, path); + safestrcat(file_path, "/"); + safestrcat(file_path, dirent->d_name); + if (!sysfs_path_is_dir(file_path)) { + if (!dirlist) { + dirlist = dlist_new_with_delete + (SYSFS_NAME_LEN, sysfs_del_name); + if (!dirlist) { + dprintf("Error creating list\n"); + return NULL; + } + } + dir_name = (char *)calloc(1, SYSFS_NAME_LEN); + safestrcpymax(dir_name, dirent->d_name, SYSFS_NAME_LEN); + dlist_unshift_sorted(dirlist, dir_name, sort_char); + } + } + closedir(dir); + return dirlist; +} + +/** + * get_attributes_list: build a list of attributes for the given path + * @path: grab attributes at the given path + * returns dlist of attributes on success and NULL on failure + */ +struct dlist *get_attributes_list(struct dlist *alist, const char *path) +{ + DIR *dir = NULL; + struct dirent *dirent = NULL; + char file_path[SYSFS_PATH_MAX]; + + if (!path) { + errno = EINVAL; + return NULL; + } + + dir = opendir(path); + if (!dir) { + dprintf("Error opening directory %s\n", path); + return NULL; + } + while ((dirent = readdir(dir)) != NULL) { + if (0 == strcmp(dirent->d_name, ".")) + continue; + if (0 == strcmp(dirent->d_name, "..")) + continue; + memset(file_path, 0, SYSFS_PATH_MAX); + safestrcpy(file_path, path); + safestrcat(file_path, "/"); + safestrcat(file_path, dirent->d_name); + if (!sysfs_path_is_file(file_path)) { + if (!alist) { + alist = dlist_new_with_delete + (sizeof(struct sysfs_attribute), + sysfs_del_attribute); + if (!alist) { + dprintf("Error creating list\n"); + return NULL; + } + } + add_attribute_to_list(alist, file_path); + } + } + closedir(dir); + return alist; +} + +/** + * get_dev_attributes_list: build a list of attributes for the given device + * @dev: devices whose attributes list is required + * returns dlist of attributes on success and NULL on failure + */ +struct dlist *get_dev_attributes_list(void *dev) +{ + DIR *dir = NULL; + struct dirent *dirent = NULL; + struct sysfs_attribute *attr = NULL; + char file_path[SYSFS_PATH_MAX], path[SYSFS_PATH_MAX]; + + if (!dev) { + errno = EINVAL; + return NULL; + } + memset(path, 0, SYSFS_PATH_MAX); + safestrcpy(path, ((struct sysfs_device *)dev)->path); + dir = opendir(path); + if (!dir) { + dprintf("Error opening directory %s\n", path); + return NULL; + } + while ((dirent = readdir(dir)) != NULL) { + if (0 == strcmp(dirent->d_name, ".")) + continue; + if (0 == strcmp(dirent->d_name, "..")) + continue; + memset(file_path, 0, SYSFS_PATH_MAX); + safestrcpy(file_path, path); + safestrcat(file_path, "/"); + safestrcat(file_path, dirent->d_name); + if (!sysfs_path_is_file(file_path)) { + if (((struct sysfs_device *)dev)->attrlist) { + /* check if attr is already in the list */ + attr = (struct sysfs_attribute *) + dlist_find_custom + ((((struct sysfs_device *)dev)->attrlist), + (void *)dirent->d_name, attr_name_equal); + if (attr) + continue; + else + add_attribute(dev, file_path); + } else + attr = add_attribute(dev, file_path); + } + } + closedir(dir); + return ((struct sysfs_device *)dev)->attrlist; +} |