diff --git a/libsysfs/libsysfs.h b/libsysfs/libsysfs.h index 6d8e58de21..aca25772b5 100644 --- a/libsysfs/libsysfs.h +++ b/libsysfs/libsysfs.h @@ -31,15 +31,10 @@ */ #define SYSFS_FSTYPE_NAME "sysfs" #define SYSFS_PROC_MNTS "/proc/mounts" -#define SYSFS_BUS_DIR "/bus" #define SYSFS_BUS_NAME "bus" -#define SYSFS_CLASS_DIR "/class" #define SYSFS_CLASS_NAME "class" -#define SYSFS_BLOCK_DIR "/block" #define SYSFS_BLOCK_NAME "block" -#define SYSFS_DEVICES_DIR "/devices" #define SYSFS_DEVICES_NAME "devices" -#define SYSFS_DRIVERS_DIR "/drivers" #define SYSFS_DRIVERS_NAME "drivers" #define SYSFS_NAME_ATTRIBUTE "name" #define SYSFS_UNKNOWN "unknown" @@ -75,17 +70,15 @@ struct sysfs_directory { }; struct sysfs_driver { - struct dlist *devices; unsigned char name[SYSFS_NAME_LEN]; unsigned char path[SYSFS_PATH_MAX]; /* for internal use only */ + struct dlist *devices; struct sysfs_directory *directory; }; struct sysfs_device { - struct sysfs_device *parent; - struct dlist *children; unsigned char name[SYSFS_NAME_LEN]; unsigned char bus_id[SYSFS_NAME_LEN]; unsigned char bus[SYSFS_NAME_LEN]; @@ -93,45 +86,48 @@ struct sysfs_device { unsigned char path[SYSFS_PATH_MAX]; /* for internal use only */ + struct sysfs_device *parent; + struct dlist *children; struct sysfs_directory *directory; }; struct sysfs_root_device { - struct dlist *devices; unsigned char name[SYSFS_NAME_LEN]; unsigned char path[SYSFS_PATH_MAX]; /* for internal use only */ + struct dlist *devices; struct sysfs_directory *directory; }; struct sysfs_bus { - struct dlist *drivers; - struct dlist *devices; unsigned char name[SYSFS_NAME_LEN]; unsigned char path[SYSFS_PATH_MAX]; /* internal use only */ + struct dlist *drivers; + struct dlist *devices; struct sysfs_directory *directory; }; struct sysfs_class_device { - struct sysfs_device *sysdevice; /* NULL if virtual */ - struct sysfs_driver *driver; /* NULL if not implemented */ unsigned char name[SYSFS_NAME_LEN]; unsigned char classname[SYSFS_NAME_LEN]; unsigned char path[SYSFS_PATH_MAX]; /* for internal use only */ + struct sysfs_class_device *parent; + struct sysfs_device *sysdevice; /* NULL if virtual */ + struct sysfs_driver *driver; /* NULL if not implemented */ struct sysfs_directory *directory; }; struct sysfs_class { - struct dlist *devices; unsigned char name[SYSFS_NAME_LEN]; unsigned char path[SYSFS_PATH_MAX]; /* for internal use only */ + struct dlist *devices; struct sysfs_directory *directory; }; @@ -146,6 +142,9 @@ extern int sysfs_trailing_slash(unsigned char *path); extern int sysfs_get_mnt_path(unsigned char *mnt_path, size_t len); extern int sysfs_get_name_from_path(const unsigned char *path, unsigned char *name, size_t len); +extern int sysfs_path_is_dir(const unsigned char *path); +extern int sysfs_path_is_link(const unsigned char *path); +extern int sysfs_path_is_file(const unsigned char *path); extern int sysfs_get_link(const unsigned char *path, unsigned char *target, size_t len); extern struct dlist *sysfs_open_subsystem_list(unsigned char *name); @@ -162,8 +161,12 @@ extern int sysfs_write_attribute(struct sysfs_attribute *sysattr, const unsigned char *new_value, size_t len); extern unsigned char *sysfs_get_value_from_attributes(struct dlist *attr, const unsigned char * name); +extern int sysfs_refresh_attributes(struct dlist *attrlist); extern void sysfs_close_directory(struct sysfs_directory *sysdir); extern struct sysfs_directory *sysfs_open_directory(const unsigned char *path); +extern int sysfs_read_dir_attributes(struct sysfs_directory *sysdir); +extern int sysfs_read_dir_links(struct sysfs_directory *sysdir); +extern int sysfs_read_dir_subdirs(struct sysfs_directory *sysdir); extern int sysfs_read_directory(struct sysfs_directory *sysdir); extern int sysfs_read_all_subdirs(struct sysfs_directory *sysdir); extern struct sysfs_directory *sysfs_get_subdirectory @@ -183,10 +186,10 @@ extern struct sysfs_driver *sysfs_open_driver(const unsigned char *path); extern struct sysfs_attribute *sysfs_get_driver_attr (struct sysfs_driver *drv, const unsigned char *name); extern struct dlist *sysfs_get_driver_attributes(struct sysfs_driver *driver); +extern struct dlist *sysfs_get_driver_devices(struct sysfs_driver *driver); extern struct dlist *sysfs_get_driver_links(struct sysfs_driver *driver); -extern void sysfs_close_driver_by_name(struct sysfs_driver *driver); -extern struct sysfs_driver *sysfs_open_driver_by_name - (const unsigned char *drv_name, const unsigned char *bus, size_t bsize); +extern struct sysfs_device *sysfs_get_driver_device + (struct sysfs_driver *driver, const unsigned char *name); extern struct sysfs_attribute *sysfs_open_driver_attr(const unsigned char *bus, const unsigned char *drv, const unsigned char *attrib); @@ -194,13 +197,14 @@ extern struct sysfs_attribute *sysfs_open_driver_attr(const unsigned char *bus, extern void sysfs_close_root_device(struct sysfs_root_device *root); extern struct sysfs_root_device *sysfs_open_root_device (const unsigned char *name); +extern struct dlist *sysfs_get_root_devices(struct sysfs_root_device *root); extern void sysfs_close_device(struct sysfs_device *dev); extern struct sysfs_device *sysfs_open_device(const unsigned char *path); extern struct sysfs_attribute *sysfs_get_device_attr (struct sysfs_device *dev, const unsigned char *name); extern struct dlist *sysfs_get_device_attributes(struct sysfs_device *device); extern struct sysfs_device *sysfs_open_device_by_id - (const unsigned char *bus_id, const unsigned char *bus, size_t bsize); + (const unsigned char *bus_id, const unsigned char *bus); extern struct sysfs_attribute *sysfs_open_device_attr(const unsigned char *bus, const unsigned char *bus_id, const unsigned char *attrib); @@ -211,6 +215,8 @@ extern struct sysfs_device *sysfs_get_bus_device(struct sysfs_bus *bus, unsigned char *id); extern struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus, unsigned char *drvname); +extern struct dlist *sysfs_get_bus_drivers(struct sysfs_bus *bus); +extern struct dlist *sysfs_get_bus_devices(struct sysfs_bus *bus); extern struct dlist *sysfs_get_bus_attributes(struct sysfs_bus *bus); extern struct sysfs_attribute *sysfs_get_bus_attribute(struct sysfs_bus *bus, unsigned char *attrname); @@ -223,8 +229,15 @@ extern int sysfs_find_driver_bus(const unsigned char *driver, extern void sysfs_close_class_device(struct sysfs_class_device *dev); extern struct sysfs_class_device *sysfs_open_class_device (const unsigned char *path); +extern struct sysfs_device *sysfs_get_classdev_device + (struct sysfs_class_device *clsdev); +extern struct sysfs_driver *sysfs_get_classdev_driver + (struct sysfs_class_device *clsdev); +extern struct sysfs_class_device *sysfs_get_classdev_parent + (struct sysfs_class_device *clsdev); extern void sysfs_close_class(struct sysfs_class *cls); extern struct sysfs_class *sysfs_open_class(const unsigned char *name); +extern struct dlist *sysfs_get_class_devices(struct sysfs_class *cls); extern struct sysfs_class_device *sysfs_get_class_device (struct sysfs_class *class, unsigned char *name); extern struct sysfs_class_device *sysfs_open_class_device_by_name diff --git a/libsysfs/sysfs_bus.c b/libsysfs/sysfs_bus.c index 639acef4ee..3e6c22bbb1 100644 --- a/libsysfs/sysfs_bus.c +++ b/libsysfs/sysfs_bus.c @@ -93,12 +93,100 @@ static struct sysfs_bus *alloc_bus(void) } /** - * open_bus_dir: opens up sysfs bus directory - * returns sysfs_directory struct with success and NULL with error + * sysfs_get_bus_devices: gets all devices for bus + * @bus: bus to get devices for + * returns dlist of devices with success and NULL with failure */ -static struct sysfs_directory *open_bus_dir(const unsigned char *name) +struct dlist *sysfs_get_bus_devices(struct sysfs_bus *bus) { - struct sysfs_directory *busdir = NULL; + struct sysfs_device *bdev = NULL; + struct sysfs_directory *devdir = NULL; + struct sysfs_link *curl = NULL; + unsigned char path[SYSFS_PATH_MAX]; + + if (bus == NULL) { + errno = EINVAL; + return NULL; + } + memset(path, 0, SYSFS_PATH_MAX); + strcpy(path, bus->path); + strcat(path, "/"); + strcat(path, SYSFS_DEVICES_NAME); + devdir = sysfs_open_directory(path); + if (devdir == NULL) + return NULL; + + if (sysfs_read_dir_links(devdir) != 0) { + sysfs_close_directory(devdir); + return NULL; + } + + dlist_for_each_data(devdir->links, curl, struct sysfs_link) { + bdev = sysfs_open_device(curl->target); + if (bdev == NULL) { + dprintf("Error opening device at %s\n", curl->target); + continue; + } + if (bus->devices == NULL) + bus->devices = dlist_new_with_delete + (sizeof(struct sysfs_device), sysfs_close_dev); + dlist_unshift(bus->devices, bdev); + } + sysfs_close_directory(devdir); + + return (bus->devices); +} + +/** + * sysfs_get_bus_drivers: get all pci drivers + * @bus: pci bus to add drivers to + * returns dlist of drivers with success and NULL with error + */ +struct dlist *sysfs_get_bus_drivers(struct sysfs_bus *bus) +{ + struct sysfs_driver *driver = NULL; + struct sysfs_directory *drvdir = NULL; + struct sysfs_directory *cursub = NULL; + unsigned char path[SYSFS_PATH_MAX]; + + if (bus == NULL) { + errno = EINVAL; + return NULL; + } + memset(path, 0, SYSFS_PATH_MAX); + strcpy(path, bus->path); + strcat(path, "/"); + strcat(path, SYSFS_DRIVERS_NAME); + drvdir = sysfs_open_directory(path); + if (drvdir == NULL) + return NULL; + + if (sysfs_read_dir_subdirs(drvdir) != 0) { + sysfs_close_directory(drvdir); + return NULL; + } + dlist_for_each_data(drvdir->subdirs, cursub, struct sysfs_directory) { + driver = sysfs_open_driver(cursub->path); + if (driver == NULL) { + dprintf("Error opening driver at %s\n", cursub->path); + continue; + } + if (bus->drivers == NULL) + bus->drivers = dlist_new_with_delete + (sizeof(struct sysfs_driver), sysfs_close_drv); + dlist_unshift(bus->drivers, driver); + } + sysfs_close_directory(drvdir); + return (bus->drivers); +} + +/** + * sysfs_open_bus: opens specific bus and all its devices on system + * returns sysfs_bus structure with success or NULL with error. + */ +struct sysfs_bus *sysfs_open_bus(const unsigned char *name) +{ + struct sysfs_bus *bus = NULL; unsigned char buspath[SYSFS_PATH_MAX]; if (name == NULL) { @@ -114,208 +202,21 @@ static struct sysfs_directory *open_bus_dir(const unsigned char *name) if (sysfs_trailing_slash(buspath) == 0) strcat(buspath, "/"); - + strcat(buspath, SYSFS_BUS_NAME); strcat(buspath, "/"); strcat(buspath, name); - busdir = sysfs_open_directory(buspath); - if (busdir == NULL) { - errno = EINVAL; - dprintf("Bus %s not supported on this system\n", - name); + if ((sysfs_path_is_dir(buspath)) != 0) { + dprintf("Invalid path to bus: %s\n", buspath); return NULL; } - if ((sysfs_read_directory(busdir)) != 0) { - dprintf("Error reading %s bus dir %s\n", name, - buspath); - sysfs_close_directory(busdir); - return NULL; - } - /* read in devices and drivers subdirs */ - sysfs_read_all_subdirs(busdir); - - return busdir; -} - -/** - * get_all_bus_devices: gets all devices for bus - * @bus: bus to get devices for - * returns 0 with success and -1 with failure - */ -static int get_all_bus_devices(struct sysfs_bus *bus) -{ - struct sysfs_device *bdev = NULL; - struct sysfs_directory *cur = NULL; - struct sysfs_link *curl = NULL; - - if (bus == NULL || bus->directory == NULL) { - errno = EINVAL; - return -1; - } - if (bus->directory->subdirs == NULL) - return 0; - - dlist_for_each_data(bus->directory->subdirs, cur, - struct sysfs_directory) { - if (strcmp(cur->name, SYSFS_DEVICES_NAME) != 0) - continue; - if (cur->links == NULL) - continue; - dlist_for_each_data(cur->links, curl, struct sysfs_link) { - bdev = sysfs_open_device(curl->target); - if (bdev == NULL) { - dprintf("Error opening device at %s\n", - curl->target); - continue; - } - if (bus->devices == NULL) - bus->devices = dlist_new_with_delete - (sizeof(struct sysfs_device), - sysfs_close_dev); - dlist_unshift(bus->devices, bdev); - } - } - - return 0; -} - -/** - * get_all_bus_drivers: get all pci drivers - * @bus: pci bus to add drivers to - * returns 0 with success and -1 with error - */ -static int get_all_bus_drivers(struct sysfs_bus *bus) -{ - struct sysfs_driver *driver = NULL; - struct sysfs_directory *cur = NULL; - struct sysfs_directory *cursub = NULL; - - if (bus == NULL || bus->directory == NULL) { - errno = EINVAL; - return -1; - } - if (bus->directory->subdirs == NULL) - return 0; - - dlist_for_each_data(bus->directory->subdirs, cur, - struct sysfs_directory) { - if (strcmp(cur->name, SYSFS_DRIVERS_NAME) != 0) - continue; - if (cur->subdirs == NULL) - continue; - dlist_for_each_data(cur->subdirs, cursub, - struct sysfs_directory) { - driver = sysfs_open_driver(cursub->path); - if (driver == NULL) { - dprintf("Error opening driver at %s\n", - cursub->path); - continue; - } - if (bus->drivers == NULL) - bus->drivers = dlist_new_with_delete - (sizeof(struct sysfs_driver), - sysfs_close_drv); - dlist_unshift(bus->drivers, driver); - } - } - - return 0; -} - -/** - * match_bus_device_to_driver: returns 1 if device is bound to driver - * @driver: driver to match - * @busid: busid of device to match - * returns 1 if found and 0 if not found - */ -static int match_bus_device_to_driver(struct sysfs_driver *driver, - unsigned char *busid) -{ - struct sysfs_link *cur = NULL; - int found = 0; - - if (driver == NULL || driver->directory == NULL || busid == NULL) { - errno = EINVAL; - return found; - } - if (driver->directory->links != NULL) { - dlist_for_each_data(driver->directory->links, cur, - struct sysfs_link) { - if ((strcmp(cur->name, busid)) == 0) - found++; - } - } - return found; -} - -/** - * link_bus_devices_to_drivers: goes through and links devices to drivers - * @bus: bus to link - */ -static void link_bus_devices_to_drivers(struct sysfs_bus *bus) -{ - struct sysfs_device *dev = NULL; - struct sysfs_driver *drv = NULL; - - if (bus != NULL && bus->devices != NULL && bus->drivers != NULL) { - dlist_for_each_data(bus->devices, dev, struct sysfs_device) { - dlist_for_each_data(bus->drivers, drv, - struct sysfs_driver) { - if ((match_bus_device_to_driver(drv, - dev->bus_id)) != 0) { - strncpy(dev->driver_name, drv->name, - SYSFS_NAME_LEN); - if (drv->devices == NULL) - drv->devices = dlist_new - (sizeof(struct - sysfs_device)); - dlist_unshift(drv->devices, dev); - } - } - } - } -} - -/** - * sysfs_open_bus: opens specific bus and all its devices on system - * returns sysfs_bus structure with success or NULL with error. - */ -struct sysfs_bus *sysfs_open_bus(const unsigned char *name) -{ - struct sysfs_bus *bus = NULL; - struct sysfs_directory *busdir = NULL; - - if (name == NULL) { - errno = EINVAL; - return NULL; - } - bus = alloc_bus(); if (bus == NULL) { dprintf("calloc failed\n"); return NULL; } strcpy(bus->name, name); - busdir = open_bus_dir(name); - if (busdir == NULL) { - dprintf("Invalid bus, %s not supported on this system\n", - name); - sysfs_close_bus(bus); - return NULL; - } - strcpy(bus->path, busdir->path); - bus->directory = busdir; - if ((get_all_bus_devices(bus)) != 0) { - dprintf("Error reading %s bus devices\n", name); - sysfs_close_bus(bus); - return NULL; - } - if ((get_all_bus_drivers(bus)) != 0) { - dprintf("Error reading %s bus drivers\n", name); - sysfs_close_bus(bus); - return NULL; - } - link_bus_devices_to_drivers(bus); + strcpy(bus->path, buspath); return bus; } @@ -334,6 +235,12 @@ struct sysfs_device *sysfs_get_bus_device(struct sysfs_bus *bus, return NULL; } + if (bus->devices == NULL) { + bus->devices = sysfs_get_bus_devices(bus); + if (bus->devices == NULL) + return NULL; + } + return (struct sysfs_device *)dlist_find_custom(bus->devices, id, bus_device_id_equal); } @@ -352,6 +259,12 @@ struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus, return NULL; } + if (bus->drivers == NULL) { + bus->drivers = sysfs_get_bus_drivers(bus); + if (bus->drivers == NULL) + return NULL; + } + return (struct sysfs_driver *)dlist_find_custom(bus->drivers, drvname, bus_driver_name_equal); } @@ -363,8 +276,28 @@ struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus, */ struct dlist *sysfs_get_bus_attributes(struct sysfs_bus *bus) { - if (bus == NULL || bus->directory == NULL) + if (bus == NULL) return NULL; + + if (bus->directory == NULL) { + bus->directory = sysfs_open_directory(bus->path); + if (bus->directory == NULL) + return NULL; + } + if (bus->directory->attributes == NULL) { + if ((sysfs_read_dir_attributes(bus->directory)) != 0) + return NULL; + } else { + if ((sysfs_path_is_dir(bus->path)) != 0) { + dprintf("Bus at %s no longer exists\n", bus->path); + return NULL; + } + if ((sysfs_refresh_attributes + (bus->directory->attributes)) != 0) { + dprintf("Error refreshing bus attributes\n"); + return NULL; + } + } return bus->directory->attributes; } @@ -378,10 +311,16 @@ struct dlist *sysfs_get_bus_attributes(struct sysfs_bus *bus) struct sysfs_attribute *sysfs_get_bus_attribute(struct sysfs_bus *bus, unsigned char *attrname) { - if (bus == NULL || bus->directory == NULL || attrname == NULL) { + struct dlist *attrlist = NULL; + + if (bus == NULL) { errno = EINVAL; return NULL; } + attrlist = sysfs_get_bus_attributes(bus); + if (attrlist == NULL) + return NULL; + return sysfs_get_directory_attribute(bus->directory, attrname); } @@ -408,9 +347,10 @@ struct sysfs_device *sysfs_open_bus_device(unsigned char *busname, dprintf("Error getting sysfs mount point\n"); return NULL; } - + if (sysfs_trailing_slash(path) == 0) strcat(path, "/"); + strcat(path, SYSFS_BUS_NAME); strcat(path, "/"); strcat(path, busname); @@ -448,6 +388,7 @@ int sysfs_find_driver_bus(const unsigned char *driver, unsigned char *busname, } memset(subsys, 0, SYSFS_PATH_MAX); + strcat(subsys, "/"); strcpy(subsys, SYSFS_BUS_NAME); buslist = sysfs_open_subsystem_list(subsys); if (buslist != NULL) { @@ -476,4 +417,3 @@ int sysfs_find_driver_bus(const unsigned char *driver, unsigned char *busname, } return -1; } - diff --git a/libsysfs/sysfs_class.c b/libsysfs/sysfs_class.c index 54f22eee24..169600d5fd 100644 --- a/libsysfs/sysfs_class.c +++ b/libsysfs/sysfs_class.c @@ -58,6 +58,8 @@ void sysfs_close_class_device(struct sysfs_class_device *dev) sysfs_close_device(dev->sysdevice); if (dev->driver != NULL) sysfs_close_driver(dev->driver); + if (dev->parent != NULL) + sysfs_close_class_device(dev->parent); free(dev); } } @@ -96,54 +98,6 @@ static struct sysfs_class *alloc_class(void) return (struct sysfs_class *)calloc(1, sizeof(struct sysfs_class)); } -/** - * open_class_dir: opens up sysfs class directory - * returns sysfs_directory struct with success and NULL with error - */ -static struct sysfs_directory *open_class_dir(const unsigned char *name) -{ - struct sysfs_directory *classdir = NULL; - unsigned char classpath[SYSFS_PATH_MAX]; - - if (name == NULL) { - errno = EINVAL; - return NULL; - } - - memset(classpath, 0, SYSFS_PATH_MAX); - if ((sysfs_get_mnt_path(classpath, SYSFS_PATH_MAX)) != 0) { - dprintf("Sysfs not supported on this system\n"); - 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, SYSFS_BLOCK_NAME); - } else { - strcat(classpath, SYSFS_CLASS_NAME); - strcat(classpath, "/"); - strcat(classpath, name); - } - classdir = sysfs_open_directory(classpath); - if (classdir == NULL) { - errno = EINVAL; - dprintf("Class %s not supported on this system\n", name); - return NULL; - } - if ((sysfs_read_directory(classdir)) != 0) { - dprintf("Error reading %s class dir %s\n", name, classpath); - sysfs_close_directory(classdir); - return NULL; - } - - return classdir; -} - /** * set_classdev_classname: Grabs classname from path * @cdev: class device to set @@ -154,20 +108,18 @@ static void set_classdev_classname(struct sysfs_class_device *cdev) unsigned char *c = NULL, *e = NULL; int count = 0; - c = strstr(cdev->path, SYSFS_CLASS_DIR); - if (c == NULL) - c = strstr(cdev->path, SYSFS_BLOCK_DIR); - else { - c++; - while (c != NULL && *c != '/') - c++; + c = strstr(cdev->path, SYSFS_CLASS_NAME); + if (c == NULL) { + c = strstr(cdev->path, SYSFS_BLOCK_NAME); + } else { + c = strstr(c, "/"); } - if (c == NULL) + if (c == NULL) strcpy(cdev->classname, SYSFS_UNKNOWN); - else { - c++; + if (*c == '/') + c++; e = c; while (e != NULL && *e != '/' && *e != '\0') { e++; @@ -185,15 +137,15 @@ static void set_classdev_classname(struct sysfs_class_device *cdev) struct sysfs_class_device *sysfs_open_class_device(const unsigned char *path) { struct sysfs_class_device *cdev = NULL; - struct sysfs_directory *dir = NULL; - struct sysfs_link *curl = NULL; - struct sysfs_device *sdev = NULL; - struct sysfs_driver *drv = NULL; if (path == NULL) { errno = EINVAL; return NULL; } + if ((sysfs_path_is_dir(path)) != 0) { + dprintf("%s is not a valid path to a class device\n", path); + return NULL; + } cdev = alloc_class_device(); if (cdev == NULL) { dprintf("calloc failed\n"); @@ -201,81 +153,41 @@ struct sysfs_class_device *sysfs_open_class_device(const unsigned char *path) } if ((sysfs_get_name_from_path(path, cdev->name, SYSFS_NAME_LEN)) != 0) { errno = EINVAL; - dprintf("Invalid class device path %s\n", path); + dprintf("Error getting class device name\n"); sysfs_close_class_device(cdev); return NULL; } - dir = sysfs_open_directory(path); - if (dir == NULL) { - dprintf("Error opening class device at %s\n", path); - sysfs_close_class_device(cdev); - return NULL; - } - if ((sysfs_read_directory(dir)) != 0) { - dprintf("Error reading class device at %s\n", path); - sysfs_close_directory(dir); - sysfs_close_class_device(cdev); - return NULL; - } - sysfs_read_all_subdirs(dir); - cdev->directory = dir; - strcpy(cdev->path, dir->path); + strcpy(cdev->path, path); set_classdev_classname(cdev); - /* get driver and device, if implemented */ - if (cdev->directory->links != NULL) { - dlist_for_each_data(cdev->directory->links, curl, - struct sysfs_link) { - if (strncmp(curl->name, SYSFS_DEVICES_NAME, 6) == 0) { - sdev = sysfs_open_device(curl->target); - if (sdev != NULL) { - cdev->sysdevice = sdev; - if (cdev->driver != NULL) - strncpy(sdev->driver_name, - cdev->driver->name, - SYSFS_NAME_LEN); - } - } else if (strncmp(curl->name, - SYSFS_DRIVERS_NAME, 6) == 0) { - drv = sysfs_open_driver(curl->target); - if (drv != NULL) { - cdev->driver = drv; - if (cdev->sysdevice != NULL) { - strncpy(cdev->sysdevice->name, - drv->name, - SYSFS_NAME_LEN); - if (drv->devices == NULL) - drv->devices = - dlist_new - (sizeof(struct - sysfs_device)); - dlist_unshift(drv->devices, - cdev->sysdevice); - } - } - } - } - } return cdev; } /** - * get_all_class_devices: gets all devices for class + * sysfs_get_class_devices: gets all devices for class * @class: class to get devices for - * returns 0 with success and -1 with failure + * returns dlist of class_devices with success and NULL with error */ -static int get_all_class_devices(struct sysfs_class *cls) +struct dlist *sysfs_get_class_devices(struct sysfs_class *cls) { struct sysfs_class_device *dev = NULL; struct sysfs_directory *cur = NULL; - if (cls == NULL || cls->directory == NULL) { + if (cls == NULL) { errno = EINVAL; - return -1; + return NULL; } - if (cls->directory->subdirs == NULL) - return 0; + 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) + return NULL; + dlist_for_each_data(cls->directory->subdirs, cur, struct sysfs_directory) { dev = sysfs_open_class_device(cur->path); @@ -289,7 +201,7 @@ static int get_all_class_devices(struct sysfs_class *cls) sysfs_close_cls_dev); dlist_unshift(cls->devices, dev); } - return 0; + return cls->devices; } /** @@ -299,34 +211,46 @@ static int get_all_class_devices(struct sysfs_class *cls) struct sysfs_class *sysfs_open_class(const unsigned char *name) { struct sysfs_class *cls = NULL; - struct sysfs_directory *classdir = NULL; + unsigned char classpath[SYSFS_PATH_MAX]; if (name == NULL) { errno = EINVAL; return NULL; } + memset(classpath, 0, SYSFS_PATH_MAX); + if ((sysfs_get_mnt_path(classpath, SYSFS_PATH_MAX)) != 0) { + dprintf("Sysfs not supported on this system\n"); + 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, SYSFS_BLOCK_NAME); + } else { + strcat(classpath, SYSFS_CLASS_NAME); + strcat(classpath, "/"); + strcat(classpath, name); + } + if ((sysfs_path_is_dir(classpath)) != 0) { + dprintf("Class %s not found on the system\n", name); + return NULL; + } + cls = alloc_class(); if (cls == NULL) { dprintf("calloc failed\n"); return NULL; } strcpy(cls->name, name); - classdir = open_class_dir(name); - if (classdir == NULL) { - dprintf("Invalid class, %s not supported on this system\n", - name); - sysfs_close_class(cls); - return NULL; - } - cls->directory = classdir; - strcpy(cls->path, classdir->path); - if ((get_all_class_devices(cls)) != 0) { - dprintf("Error reading %s class devices\n", name); - sysfs_close_class(cls); - return NULL; - } - + strcpy(cls->path, classpath); + return cls; } @@ -338,15 +262,189 @@ 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; } + if (class->devices == NULL) { + class->devices = sysfs_get_class_devices(class); + if (devlist == NULL) + return NULL; + } return (struct sysfs_class_device *)dlist_find_custom(class->devices, name, class_name_equal); } +/** + * sysfs_get_classdev_device: returns the sysfs_device corresponding to + * sysfs_class_device, if present + * @clsdev: class device whose sysfs_device is required + * Returns sysfs_device on success, NULL on error or if device is not + * implemented + */ +struct sysfs_device *sysfs_get_classdev_device + (struct sysfs_class_device *clsdev) +{ + struct sysfs_link *devlink = NULL; + + if (clsdev == NULL) { + errno = EINVAL; + return NULL; + } + + if (clsdev->sysdevice != NULL) + return (clsdev->sysdevice); + + if (clsdev->directory == NULL) { + clsdev->directory = sysfs_open_directory(clsdev->path); + if (clsdev->directory == NULL) + return NULL; + } + devlink = sysfs_get_directory_link(clsdev->directory, "device"); + if (devlink == NULL) + return NULL; + + clsdev->sysdevice = sysfs_open_device(devlink->target); + if (clsdev->sysdevice == NULL) + return NULL; + if (clsdev->driver != NULL) + strcpy(clsdev->sysdevice->driver_name, clsdev->driver->name); + + return (clsdev->sysdevice); +} + +/** + * sysfs_get_classdev_driver: returns the sysfs_driver corresponding to + * sysfs_class_device, if present + * @clsdev: class device whose sysfs_device is required + * Returns sysfs_driver on success, NULL on error or if driver is not + * implemented + */ +struct sysfs_driver *sysfs_get_classdev_driver + (struct sysfs_class_device *clsdev) +{ + struct sysfs_link *drvlink = NULL; + + if (clsdev == NULL) { + errno = EINVAL; + return NULL; + } + + if (clsdev->driver != NULL) + return (clsdev->driver); + + 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; + + } + 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 + * Returns 0 on success and 1 on error + */ +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); + + c = strstr(parent_path, SYSFS_BLOCK_NAME); + if (c == NULL) { + dprintf("Class device %s does not belong to BLOCK subsystem", + 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, '/'); + if (c == NULL) + goto errout; + + *c = '\0'; + clsdev->parent = sysfs_open_class_device(parent_path); + if (clsdev->parent == NULL) { + dprintf("Error opening the parent class device at %s\n", + parent_path); + return 1; + } + return 0; + +errout: + dprintf("Invalid path %s\n", clsdev->path); + return 1; +} + +/** + * sysfs_get_classdev_parent: Retrieves the parent of a class device. + * eg., when working with hda1, this function can be used to retrieve the + * sysfs_class_device for hda + * + * @clsdev: class device whose parent details are required. + * Returns sysfs_class_device of the parent on success, NULL on failure + */ +struct sysfs_class_device *sysfs_get_classdev_parent + (struct sysfs_class_device *clsdev) +{ + if (clsdev == NULL) { + errno = EINVAL; + return NULL; + } + if (clsdev->parent != NULL) + return (clsdev->parent); + + /* + * As of now, only block devices have a parent child heirarchy in sysfs + * We do not know, if, in the future, more classes will have a similar + * structure. Hence, we now call a specialized function for block and + * later we can add support functions for other subsystems as required. + */ + if (!(strcmp(clsdev->classname, SYSFS_BLOCK_NAME))) { + if ((get_blockdev_parent(clsdev)) == 0) + return (clsdev->parent); + } + return NULL; +} + /** * get_classdev_path: given the class and a device in the class, return the * absolute path to the device @@ -367,6 +465,7 @@ 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, "/"); @@ -427,9 +526,32 @@ struct sysfs_class_device *sysfs_open_class_device_by_name */ struct dlist *sysfs_get_classdev_attributes(struct sysfs_class_device *cdev) { - if (cdev == NULL || cdev->directory == NULL) + if (cdev == NULL) return NULL; + if (cdev->directory == NULL) { + cdev->directory = sysfs_open_directory(cdev->path); + if (cdev->directory == NULL) + 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"); + return NULL; + } + } return (cdev->directory->attributes); } @@ -443,18 +565,38 @@ struct sysfs_attribute *sysfs_get_classdev_attr (struct sysfs_class_device *clsdev, const unsigned char *name) { struct sysfs_attribute *cur = NULL; - - if (clsdev == NULL || clsdev->directory == NULL || - clsdev->directory->attributes == NULL || name == NULL) { + struct sysfs_directory *sdir = NULL; + struct dlist *attrlist = NULL; + + if (clsdev == NULL || name == NULL) { 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, (unsigned char *)name); 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; + } + return NULL; } diff --git a/libsysfs/sysfs_device.c b/libsysfs/sysfs_device.c index 323a43dac1..82b54719ff 100644 --- a/libsysfs/sysfs_device.c +++ b/libsysfs/sysfs_device.c @@ -23,38 +23,6 @@ #include "libsysfs.h" #include "sysfs.h" -static int confirm_device_bus(struct sysfs_device *dev, - unsigned char *busname, unsigned char *bus_id) -{ - struct sysfs_link *devlink = NULL; - unsigned char devpath[SYSFS_PATH_MAX]; - int result = 0; - - if (busname == NULL || bus_id == NULL) - return -1; - - if (sysfs_get_mnt_path(devpath, SYSFS_PATH_MAX) != 0) - return -1; - - if (sysfs_trailing_slash(devpath) == 0) - strcat(devpath, "/"); - strcat(devpath, SYSFS_BUS_NAME); - strcat(devpath, "/"); - strcat(devpath, busname); - strcat(devpath, SYSFS_DEVICES_DIR); - strcat(devpath, "/"); - strcat(devpath, bus_id); - - devlink = sysfs_open_link(devpath); - if (devlink == NULL) - return -1; - - if (strcmp(devlink->target, dev->path) == 0) - result++; - sysfs_close_link(devlink); - return result; -} - /** * get_device_bus: retrieves the bus name the device is on, checks path to * bus' link to make sure it has correct device. @@ -63,33 +31,53 @@ static int confirm_device_bus(struct sysfs_device *dev, */ static int get_device_bus(struct sysfs_device *dev) { - unsigned char subsys[SYSFS_NAME_LEN], *bus = NULL, *curdev = NULL; - struct dlist *buslist = NULL, *device_list = NULL; + unsigned char subsys[SYSFS_NAME_LEN], path[SYSFS_PATH_MAX]; + unsigned char target[SYSFS_PATH_MAX], *bus = NULL, *c = NULL; + struct dlist *buslist = NULL; if (dev == NULL) { errno = EINVAL; return -1; } - strcpy(subsys, SYSFS_BUS_DIR); /* subsys = /bus */ + memset(subsys, 0, SYSFS_NAME_LEN); + strcat(subsys, "/"); + strcpy(subsys, SYSFS_BUS_NAME); /* subsys = /bus */ buslist = sysfs_open_subsystem_list(subsys); if (buslist != NULL) { dlist_for_each_data(buslist, bus, char) { - device_list = sysfs_open_bus_devices_list(bus); - if (device_list != NULL) { - dlist_for_each_data(device_list, - curdev, char) { - if (strcmp(dev->bus_id, curdev) == 0 - && confirm_device_bus(dev, bus, - curdev) > 0) { - strcpy(dev->bus, bus); - sysfs_close_list(device_list); - sysfs_close_list(buslist); - return 0; - } - } - sysfs_close_list(device_list); - } + memset(path, 0, SYSFS_PATH_MAX); + strcpy(path, dev->path); + c = strstr(path, "/devices"); + if (c == NULL) { + dprintf("Invalid path to device %s\n", path); + sysfs_close_list(buslist); + return -1; + } + *c = '\0'; + strcat(path, "/"); + strcat(path, SYSFS_BUS_NAME); + strcat(path, "/"); + strcat(path, bus); + strcat(path, "/"); + strcat(path, SYSFS_DEVICES_NAME); + strcat(path, "/"); + strcat(path, dev->bus_id); + if ((sysfs_path_is_link(path)) == 0) { + memset(target, 0, SYSFS_PATH_MAX); + if ((sysfs_get_link(path, target, + SYSFS_PATH_MAX)) != 0) { + dprintf("Error getting link target\n"); + sysfs_close_list(buslist); + return -1; + } + if (!(strncmp(target, dev->path, + SYSFS_PATH_MAX))) { + strcpy(dev->bus, bus); + sysfs_close_list(buslist); + return 0; + } + } } sysfs_close_list(buslist); } @@ -157,28 +145,32 @@ static struct sysfs_device *alloc_device(void) } /** - * sysfs_get_device_attr: searches dev's attributes by name - * @dev: device to look through - * @name: attribute name to get - * returns sysfs_attribute reference with success or NULL with error. + * open_device_dir: opens up sysfs_directory for specific root dev + * @name: name of root + * returns struct sysfs_directory with success and NULL with error */ -struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev, - const unsigned char *name) +static struct sysfs_directory *open_device_dir(const unsigned char *path) { - struct sysfs_attribute *cur = NULL; + struct sysfs_directory *rdir = NULL; - if (dev == NULL || dev->directory == NULL - || dev->directory->attributes == NULL || name == NULL) { + if (path == NULL) { errno = EINVAL; return NULL; } - - cur = sysfs_get_directory_attribute(dev->directory, - (unsigned char *)name); - if (cur != NULL) - return cur; - return NULL; + rdir = sysfs_open_directory(path); + if (rdir == NULL) { + errno = EINVAL; + dprintf ("Device %s not supported on this system\n", path); + return NULL; + } + if ((sysfs_read_directory(rdir)) != 0) { + dprintf ("Error reading device at dir %s\n", path); + sysfs_close_directory(rdir); + return NULL; + } + + return rdir; } /** @@ -189,40 +181,34 @@ struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev, struct sysfs_device *sysfs_open_device(const unsigned char *path) { struct sysfs_device *dev = NULL; - struct sysfs_directory *sdir = NULL; if (path == NULL) { errno = EINVAL; return NULL; } + if ((sysfs_path_is_dir(path)) != 0) { + dprintf("Incorrect path to device: %s\n", path); + return NULL; + } dev = alloc_device(); if (dev == NULL) { dprintf("Error allocating device at %s\n", path); return NULL; } - sdir = sysfs_open_directory(path); - if (sdir == NULL) { - dprintf("Invalid device at %s\n", path); + if ((sysfs_get_name_from_path(path, dev->bus_id, + SYSFS_NAME_LEN)) != 0) { errno = EINVAL; + dprintf("Error getting device bus_id\n"); sysfs_close_device(dev); return NULL; } - if ((sysfs_read_directory(sdir)) != 0) { - dprintf("Error reading device directory at %s\n", path); - sysfs_close_directory(sdir); - sysfs_close_device(dev); - return NULL; - } - dev->directory = sdir; - strcpy(dev->bus_id, sdir->name); - strcpy(dev->path, sdir->path); - + strcpy(dev->path, path); /* * The "name" attribute no longer exists... return the device's * sysfs representation instead, in the "dev->name" field, which * implies that the dev->name and dev->bus_id contain same data. */ - strncpy(dev->name, sdir->name, SYSFS_NAME_LEN); + strncpy(dev->name, dev->bus_id, SYSFS_NAME_LEN); if (get_device_bus(dev) != 0) strcpy(dev->bus, SYSFS_UNKNOWN); @@ -251,6 +237,11 @@ static struct sysfs_device *sysfs_open_device_tree(const unsigned char *path) dprintf("Error opening root device at %s\n", path); return NULL; } + if (rootdev->directory == NULL) { + rootdev->directory = open_device_dir(rootdev->path); + if (rootdev->directory == NULL) + return NULL; + } if (rootdev->directory->subdirs != NULL) { dlist_for_each_data(rootdev->directory->subdirs, cur, struct sysfs_directory) { @@ -288,63 +279,25 @@ void sysfs_close_root_device(struct sysfs_root_device *root) } /** - * open_root_device_dir: opens up sysfs_directory for specific root dev - * @name: name of root - * returns struct sysfs_directory with success and NULL with error - */ -static struct sysfs_directory *open_root_device_dir(const unsigned char *name) -{ - struct sysfs_directory *rdir = NULL; - unsigned char rootpath[SYSFS_PATH_MAX]; - - if (name == NULL) { - errno = EINVAL; - return NULL; - } - - memset(rootpath, 0, SYSFS_PATH_MAX); - if (sysfs_get_mnt_path(rootpath, SYSFS_PATH_MAX) != 0) { - dprintf ("Sysfs not supported on this system\n"); - return NULL; - } - - if (sysfs_trailing_slash(rootpath) == 0) - strcat(rootpath, "/"); - - strcat(rootpath, SYSFS_DEVICES_NAME); - strcat(rootpath, "/"); - strcat(rootpath, name); - rdir = sysfs_open_directory(rootpath); - if (rdir == NULL) { - errno = EINVAL; - dprintf ("Root device %s not supported on this system\n", - name); - return NULL; - } - if (sysfs_read_directory(rdir) != 0) { - dprintf ("Error reading %s root device at dir %s\n", name, - rootpath); - sysfs_close_directory(rdir); - return NULL; - } - - return rdir; -} - -/** - * get_all_root_devices: opens up all the devices under this root device + * sysfs_get_root_devices: opens up all the devices under this root device * @root: root device to open devices for - * returns 0 with success and -1 with error + * returns dlist of devices with success and NULL with error */ -static int get_all_root_devices(struct sysfs_root_device *root) +struct dlist *sysfs_get_root_devices(struct sysfs_root_device *root) { struct sysfs_device *dev = NULL; struct sysfs_directory *cur = NULL; - if (root == NULL || root->directory == NULL) { + if (root == NULL) { errno = EINVAL; - return -1; + return NULL; } + if (root->directory == NULL) { + root->directory = open_device_dir(root->path); + if (root->directory == NULL) + return NULL; + } + if (root->directory->subdirs == NULL) return 0; @@ -362,7 +315,7 @@ static int get_all_root_devices(struct sysfs_root_device *root) dlist_unshift(root->devices, dev); } - return 0; + return root->devices; } /** @@ -374,33 +327,37 @@ static int get_all_root_devices(struct sysfs_root_device *root) struct sysfs_root_device *sysfs_open_root_device(const unsigned char *name) { struct sysfs_root_device *root = NULL; - struct sysfs_directory *rootdir = NULL; + unsigned char rootpath[SYSFS_PATH_MAX]; if (name == NULL) { errno = EINVAL; return NULL; } + memset(rootpath, 0, SYSFS_PATH_MAX); + if (sysfs_get_mnt_path(rootpath, SYSFS_PATH_MAX) != 0) { + dprintf ("Sysfs not supported on this system\n"); + return NULL; + } + + if (sysfs_trailing_slash(rootpath) == 0) + strcat(rootpath, "/"); + + strcat(rootpath, SYSFS_DEVICES_NAME); + strcat(rootpath, "/"); + strcat(rootpath, name); + if ((sysfs_path_is_dir(rootpath)) != 0) { + errno = EINVAL; + dprintf("Invalid root device: %s\n", name); + return NULL; + } root = (struct sysfs_root_device *)calloc (1, sizeof(struct sysfs_root_device)); if (root == NULL) { dprintf("calloc failure\n"); return NULL; } - rootdir = open_root_device_dir(name); - if (rootdir == NULL) { - dprintf ("Invalid root device, %s not supported\n", name); - sysfs_close_root_device(root); - return NULL; - } - strcpy(root->path, rootdir->path); - root->directory = rootdir; - if (get_all_root_devices(root) != 0) { - dprintf ("Error retrieving devices for root %s\n", name); - sysfs_close_root_device(root); - return NULL; - } - + strcpy(root->path, rootpath); return root; } @@ -411,12 +368,60 @@ struct sysfs_root_device *sysfs_open_root_device(const unsigned char *name) */ struct dlist *sysfs_get_device_attributes(struct sysfs_device *device) { - if (device == NULL || device->directory == NULL) + if (device == NULL) return NULL; + if (device->directory == NULL) { + device->directory = sysfs_open_directory(device->path); + if (device->directory == NULL) + return NULL; + } + if (device->directory->attributes == NULL) { + if ((sysfs_read_dir_attributes(device->directory)) != 0) + return NULL; + } else { + if ((sysfs_path_is_dir(device->path)) != 0) { + dprintf("Device at %s no longer exists", device->path); + return NULL; + } + if ((sysfs_refresh_attributes + (device->directory->attributes)) != 0) { + dprintf("Error refreshing device attributes\n"); + return NULL; + } + } return (device->directory->attributes); } +/** + * sysfs_get_device_attr: searches dev's attributes by name + * @dev: device to look through + * @name: attribute name to get + * returns sysfs_attribute reference with success or NULL with error. + */ +struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev, + const unsigned char *name) +{ + struct sysfs_attribute *cur = NULL; + struct dlist *attrlist = NULL; + + if (dev == NULL || name == NULL) { + errno = EINVAL; + return NULL; + } + + attrlist = sysfs_get_device_attributes(dev); + if (attrlist == NULL) + return NULL; + + cur = sysfs_get_directory_attribute(dev->directory, + (unsigned char *)name); + if (cur != NULL) + return cur; + + return NULL; +} + /** * get_device_absolute_path: looks up the bus the device is on, gets * absolute path to the device @@ -428,24 +433,27 @@ struct dlist *sysfs_get_device_attributes(struct sysfs_device *device) static int get_device_absolute_path(const unsigned char *device, const unsigned char *bus, unsigned char *path, size_t psize) { - unsigned char bus_path[SYSFS_NAME_LEN]; + unsigned char bus_path[SYSFS_PATH_MAX]; if (device == NULL || path == NULL) { errno = EINVAL; return -1; } - memset(bus_path, 0, SYSFS_NAME_LEN); + memset(bus_path, 0, SYSFS_PATH_MAX); if (sysfs_get_mnt_path(bus_path, SYSFS_PATH_MAX) != 0) { dprintf ("Sysfs not supported on this system\n"); return -1; } + if (sysfs_trailing_slash(bus_path) == 0) strcat(bus_path, "/"); + strcat(bus_path, SYSFS_BUS_NAME); strcat(bus_path, "/"); strcat(bus_path, bus); - strcat(bus_path, SYSFS_DEVICES_DIR); + strcat(bus_path, "/"); + strcat(bus_path, SYSFS_DEVICES_NAME); strcat(bus_path, "/"); strcat(bus_path, device); /* @@ -464,7 +472,6 @@ static int get_device_absolute_path(const unsigned char *device, * @bus_id: bus_id of the device to open - has to be the "bus_id" in * /sys/bus/xxx/devices * @bus: bus the device belongs to - * @bsize: size of the bus buffer * returns struct sysfs_device if found, NULL otherwise * NOTE: * 1. Use sysfs_close_device to close the device @@ -472,7 +479,7 @@ static int get_device_absolute_path(const unsigned char *device, * Use sysfs_find_device_bus to get the bus name */ struct sysfs_device *sysfs_open_device_by_id(const unsigned char *bus_id, - const unsigned char *bus, size_t bsize) + const unsigned char *bus) { char sysfs_path[SYSFS_PATH_MAX]; struct sysfs_device *device = NULL; diff --git a/libsysfs/sysfs_dir.c b/libsysfs/sysfs_dir.c index e983d0eff4..ac2ecfcbe1 100644 --- a/libsysfs/sysfs_dir.c +++ b/libsysfs/sysfs_dir.c @@ -187,6 +187,13 @@ int sysfs_write_attribute(struct sysfs_attribute *sysattr, 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)) != 0) { + dprintf("Error reading attribute\n"); + return -1; + } if ((strncmp(sysattr->value, new_value, sysattr->len)) == 0) { dprintf("Attribute %s already has the requested value %s\n", sysattr->name, new_value); @@ -254,7 +261,7 @@ int sysfs_read_attribute(struct sysfs_attribute *sysattr) unsigned char *fbuf = NULL; unsigned char *vbuf = NULL; size_t length = 0; - int pgsize = 0; + long pgsize = 0; int fd; if (sysattr == NULL) { @@ -269,7 +276,7 @@ int sysfs_read_attribute(struct sysfs_attribute *sysattr) #ifdef __KLIBC__ pgsize = 0x4000; #else - pgsize = getpagesize(); + pgsize = sysconf(_SC_PAGESIZE); #endif fbuf = (unsigned char *)calloc(1, pgsize+1); if (fbuf == NULL) { @@ -288,6 +295,14 @@ int sysfs_read_attribute(struct sysfs_attribute *sysattr) free(fbuf); return -1; } + if (sysattr->len > 0) { + if ((sysattr->len == length) && + (!(strncmp(sysattr->value, fbuf, length)))) { + close(fd); + return 0; + } + free(sysattr->value); + } sysattr->len = length; close(fd); vbuf = (unsigned char *)realloc(fbuf, length+1); @@ -389,6 +404,7 @@ void sysfs_close_directory(struct sysfs_directory *sysdir) if (sysdir->attributes != NULL) dlist_destroy(sysdir->attributes); free(sysdir); + sysdir = NULL; } } @@ -424,10 +440,12 @@ int sysfs_read_all_subdirs(struct sysfs_directory *sysdir) errno = EINVAL; return -1; } - if (sysdir->subdirs == NULL) - return 0; + if (sysdir->subdirs == NULL) + if ((sysfs_read_dir_subdirs(sysdir) != 0) + || sysdir->subdirs == NULL) + return 0; dlist_for_each_data(sysdir->subdirs, cursub, struct sysfs_directory) { - if (sysfs_read_directory(cursub) != 0) + if ((sysfs_read_directory(cursub)) != 0) dprintf ("Error reading subdirectory %s\n", cursub->name); } @@ -494,18 +512,131 @@ struct sysfs_link *sysfs_open_link(const unsigned char *linkpath) } /** - * sysfs_read_directory: grabs attributes, links, and subdirectories + * sysfs_refresh_attributes: Refresh attributes list + * @attrlist: list of attributes to refresh + * Returns 0 on success, 1 on failure + */ +int sysfs_refresh_attributes(struct dlist *attrlist) +{ + struct sysfs_attribute *attr = NULL; + + if (attrlist == NULL) { + errno = EINVAL; + return 1; + } + dlist_for_each_data(attrlist, attr, struct sysfs_attribute) { + if (attr->method & SYSFS_METHOD_SHOW) { + if ((sysfs_read_attribute(attr)) != 0) { + dprintf("Error reading attribute %s\n", attr->path); + if ((sysfs_path_is_file(attr->path)) != 0) { + dprintf("Attr %s no longer exists\n", + attr->name); + } + } + } else { + if ((sysfs_path_is_file(attr->path)) != 0) { + dprintf("Attr %s no longer exists\n", + attr->name); + } + } + } + if (attrlist->count == 0) { + dprintf("No attributes in the list, destroying list now\n"); + dlist_destroy(attrlist); + attrlist = NULL; + return 1; + } + return 0; +} + +/** + * add_attribute: open and add attribute at path to given directory + * @sysdir: directory to add attribute to + * @path: path to attribute + * returns 0 with success and -1 with error. + */ +static int add_attribute(struct sysfs_directory *sysdir, + const unsigned char *path) +{ + struct sysfs_attribute *attr = NULL; + + attr = sysfs_open_attribute(path); + if (attr == NULL) { + dprintf("Error opening attribute %s\n", path); + return -1; + } + if (attr->method & SYSFS_METHOD_SHOW) { + if ((sysfs_read_attribute(attr)) != 0) { + dprintf("Error reading attribute %s\n", path); + sysfs_close_attribute(attr); + return 0; + } + } + + if (sysdir->attributes == NULL) { + sysdir->attributes = dlist_new_with_delete + (sizeof(struct sysfs_attribute), sysfs_del_attribute); + } + dlist_unshift(sysdir->attributes, attr); + + return 0; +} + +/** + * add_subdirectory: open and add subdirectory at path to given directory + * @sysdir: directory to add subdir to + * @path: path to subdirectory + * returns 0 with success and -1 with error. + */ +static int add_subdirectory(struct sysfs_directory *sysdir, + const unsigned char *path) +{ + struct sysfs_directory *subdir = NULL; + + subdir = sysfs_open_directory(path); + if (subdir == NULL) { + dprintf("Error opening directory %s\n", path); + return -1; + } + if (sysdir->subdirs == NULL) + sysdir->subdirs = dlist_new_with_delete + (sizeof(struct sysfs_directory), sysfs_del_directory); + dlist_unshift(sysdir->subdirs, subdir); + return 0; +} + +/** + * add_link: open and add link at path to given directory + * @sysdir: directory to add link to + * @path: path to link + * returns 0 with success and -1 with error. + */ +static int add_link(struct sysfs_directory *sysdir, const unsigned char *path) +{ + struct sysfs_link *ln = NULL; + + ln = sysfs_open_link(path); + if (ln == NULL) { + dprintf("Error opening link %s\n", path); + return -1; + } + if (sysdir->links == NULL) + sysdir->links = dlist_new_with_delete + (sizeof(struct sysfs_link), sysfs_del_link); + dlist_unshift(sysdir->links, ln); + return 0; +} + +/** + * sysfs_read_dir_attributes: grabs attributes for the given directory * @sysdir: sysfs directory to open * returns 0 with success and -1 with error. */ -int sysfs_read_directory(struct sysfs_directory *sysdir) +int sysfs_read_dir_attributes(struct sysfs_directory *sysdir) { DIR *dir = NULL; struct dirent *dirent = NULL; struct stat astats; - struct sysfs_attribute *attr = NULL; - struct sysfs_directory *subdir = NULL; - struct sysfs_link *ln = NULL; unsigned char file_path[SYSFS_PATH_MAX]; int retval = 0; @@ -531,54 +662,52 @@ int sysfs_read_directory(struct sysfs_directory *sysdir) dprintf("stat failed\n"); continue; } - if (S_ISREG(astats.st_mode)) { - attr = sysfs_open_attribute(file_path); - if (attr == NULL) { - dprintf("Error opening attribute %s\n", - file_path); - retval = -1; + if (S_ISREG(astats.st_mode)) + retval = add_attribute(sysdir, file_path); + } + closedir(dir); + return(retval); +} + +/** + * sysfs_read_dir_links: grabs links in a specific directory + * @sysdir: sysfs directory to read links + * returns 0 with success and -1 with error. + */ +int sysfs_read_dir_links(struct sysfs_directory *sysdir) +{ + DIR *dir = NULL; + struct dirent *dirent = NULL; + struct stat astats; + unsigned char file_path[SYSFS_PATH_MAX]; + int retval = 0; + + if (sysdir == NULL) { + errno = EINVAL; + return -1; + } + dir = opendir(sysdir->path); + if (dir == NULL) { + dprintf("Error opening directory %s\n", sysdir->path); + return -1; + } + while(((dirent = readdir(dir)) != NULL) && retval == 0) { + if (0 == strcmp(dirent->d_name, ".")) + continue; + if (0 == strcmp(dirent->d_name, "..")) + continue; + memset(file_path, 0, SYSFS_PATH_MAX); + strncpy(file_path, sysdir->path, sizeof(file_path)); + strncat(file_path, "/", sizeof(file_path)); + strncat(file_path, dirent->d_name, sizeof(file_path)); + if ((lstat(file_path, &astats)) != 0) { + dprintf("stat failed\n"); + continue; + } + if (S_ISLNK(astats.st_mode)) { + retval = add_link(sysdir, file_path); + if (retval != 0) break; - } - if (attr->method & SYSFS_METHOD_SHOW) { - if ((sysfs_read_attribute(attr)) != 0) { - dprintf("Error reading attribute %s\n", - file_path); - sysfs_close_attribute(attr); - continue; - } - } - - if (sysdir->attributes == NULL) { - sysdir->attributes = dlist_new_with_delete - (sizeof(struct sysfs_attribute), - sysfs_del_attribute); - } - dlist_unshift(sysdir->attributes, attr); - } else if (S_ISDIR(astats.st_mode)) { - subdir = sysfs_open_directory(file_path); - if (subdir == NULL) { - dprintf("Error opening directory %s\n", - file_path); - retval = -1; - break; - } - if (sysdir->subdirs == NULL) - sysdir->subdirs = dlist_new_with_delete - (sizeof(struct sysfs_directory), - sysfs_del_directory); - dlist_unshift(sysdir->subdirs, subdir); - } else if (S_ISLNK(astats.st_mode)) { - ln = sysfs_open_link(file_path); - if (ln == NULL) { - dprintf("Error opening link %s\n", file_path); - retval = -1; - break; - } - if (sysdir->links == NULL) - sysdir->links = dlist_new_with_delete - (sizeof(struct sysfs_link), - sysfs_del_link); - dlist_unshift(sysdir->links, ln); } } closedir(dir); @@ -586,7 +715,98 @@ int sysfs_read_directory(struct sysfs_directory *sysdir) } /** - * sysfs_get_directory_attribute: retrieves attribute attrname + * sysfs_read_dir_subdirs: grabs subdirs in a specific directory + * @sysdir: sysfs directory to read links + * returns 0 with success and -1 with error. + */ +int sysfs_read_dir_subdirs(struct sysfs_directory *sysdir) +{ + DIR *dir = NULL; + struct dirent *dirent = NULL; + struct stat astats; + unsigned char file_path[SYSFS_PATH_MAX]; + int retval = 0; + + if (sysdir == NULL) { + errno = EINVAL; + return -1; + } + dir = opendir(sysdir->path); + if (dir == NULL) { + dprintf("Error opening directory %s\n", sysdir->path); + return -1; + } + while(((dirent = readdir(dir)) != NULL) && retval == 0) { + if (0 == strcmp(dirent->d_name, ".")) + continue; + if (0 == strcmp(dirent->d_name, "..")) + continue; + memset(file_path, 0, SYSFS_PATH_MAX); + strncpy(file_path, sysdir->path, sizeof(file_path)); + strncat(file_path, "/", sizeof(file_path)); + strncat(file_path, dirent->d_name, sizeof(file_path)); + if ((lstat(file_path, &astats)) != 0) { + dprintf("stat failed\n"); + continue; + } + if (S_ISDIR(astats.st_mode)) + retval = add_subdirectory(sysdir, file_path); + } + closedir(dir); + return(retval); +} + +/** + * sysfs_read_directory: grabs attributes, links, and subdirectories + * @sysdir: sysfs directory to open + * returns 0 with success and -1 with error. + */ +int sysfs_read_directory(struct sysfs_directory *sysdir) +{ + DIR *dir = NULL; + struct dirent *dirent = NULL; + struct stat astats; + unsigned char file_path[SYSFS_PATH_MAX]; + int retval = 0; + + if (sysdir == NULL) { + errno = EINVAL; + return -1; + } + dir = opendir(sysdir->path); + if (dir == NULL) { + dprintf("Error opening directory %s\n", sysdir->path); + return -1; + } + while(((dirent = readdir(dir)) != NULL) && retval == 0) { + if (0 == strcmp(dirent->d_name, ".")) + continue; + if (0 == strcmp(dirent->d_name, "..")) + continue; + memset(file_path, 0, SYSFS_PATH_MAX); + strncpy(file_path, sysdir->path, sizeof(file_path)); + strncat(file_path, "/", sizeof(file_path)); + strncat(file_path, dirent->d_name, sizeof(file_path)); + if ((lstat(file_path, &astats)) != 0) { + dprintf("stat failed\n"); + continue; + } + if (S_ISDIR(astats.st_mode)) + retval = add_subdirectory(sysdir, file_path); + + else if (S_ISLNK(astats.st_mode)) + retval = add_link(sysdir, file_path); + + else if (S_ISREG(astats.st_mode)) + retval = add_attribute(sysdir, file_path); + } + closedir(dir); + return(retval); +} + +/** + * sysfs_get_directory_attribute: retrieves attribute attrname from current + * directory only * @dir: directory to retrieve attribute from * @attrname: name of attribute to look for * returns sysfs_attribute if found and NULL if not found @@ -594,27 +814,38 @@ int sysfs_read_directory(struct sysfs_directory *sysdir) struct sysfs_attribute *sysfs_get_directory_attribute (struct sysfs_directory *dir, unsigned char *attrname) { - struct sysfs_directory *sdir = NULL; struct sysfs_attribute *attr = NULL; + unsigned char new_path[SYSFS_PATH_MAX]; if (dir == NULL || attrname == NULL) { errno = EINVAL; return NULL; } - - attr = (struct sysfs_attribute *)dlist_find_custom(dir->attributes, - attrname, dir_attribute_name_equal); - if (attr != NULL) + + if (dir->attributes == NULL) + if ((sysfs_read_dir_attributes(dir) != 0) + || (dir->attributes == NULL)) + return NULL; + + attr = (struct sysfs_attribute *)dlist_find_custom + (dir->attributes, attrname, dir_attribute_name_equal); + if (attr != NULL) { + /* + * don't read here since we would have read the attribute in + * in the routine that called this routine + */ return attr; - - if (dir->subdirs != NULL) { - dlist_for_each_data(dir->subdirs, sdir, - struct sysfs_directory) { - if (sdir->attributes == NULL) - continue; - attr = sysfs_get_directory_attribute(sdir, attrname); - if (attr != NULL) - return attr; + } else { + memset(new_path, 0, SYSFS_PATH_MAX); + strcpy(new_path, dir->path); + strcat(new_path, "/"); + strcat(new_path, attrname); + if ((sysfs_path_is_file(new_path)) == 0) { + if ((add_attribute(dir, new_path)) == 0) { + attr = (struct sysfs_attribute *)dlist_find_custom + (dir->attributes, attrname, dir_attribute_name_equal); + } + return attr; } } return NULL; @@ -633,6 +864,10 @@ struct sysfs_link *sysfs_get_directory_link errno = EINVAL; return NULL; } + if (dir->links == NULL) + if ((sysfs_read_dir_links(dir) != 0) || (dir->links == NULL)) + return NULL; + return (struct sysfs_link *)dlist_find_custom(dir->links, linkname, dir_link_name_equal); } @@ -648,10 +883,15 @@ struct sysfs_directory *sysfs_get_subdirectory(struct sysfs_directory *dir, { struct sysfs_directory *sub = NULL, *cursub = NULL; - if (dir == NULL || dir->subdirs == NULL || subname == NULL) { + if (dir == NULL || subname == NULL) { errno = EINVAL; return NULL; } + + if (dir->subdirs == NULL) + if (sysfs_read_dir_subdirs(dir) != 0) + return NULL; + sub = (struct sysfs_directory *)dlist_find_custom(dir->subdirs, subname, dir_subdir_name_equal); if (sub != NULL) @@ -660,8 +900,12 @@ struct sysfs_directory *sysfs_get_subdirectory(struct sysfs_directory *dir, if (dir->subdirs != NULL) { dlist_for_each_data(dir->subdirs, cursub, struct sysfs_directory) { - if (cursub->subdirs == NULL) - continue; + if (cursub->subdirs == NULL) { + if (sysfs_read_dir_subdirs(cursub) != 0) + continue; + if (cursub->subdirs == NULL) + continue; + } sub = sysfs_get_subdirectory(cursub, subname); if (sub != NULL) return sub; @@ -682,7 +926,7 @@ struct sysfs_link *sysfs_get_subdirectory_link(struct sysfs_directory *dir, struct sysfs_directory *cursub = NULL; struct sysfs_link *ln = NULL; - if (dir == NULL || dir->links == NULL || linkname == NULL) { + if (dir == NULL || linkname == NULL) { errno = EINVAL; return NULL; } @@ -691,14 +935,13 @@ struct sysfs_link *sysfs_get_subdirectory_link(struct sysfs_directory *dir, if (ln != NULL) return ln; - if (dir->subdirs == NULL) - return NULL; + if (dir->subdirs == NULL) + if (sysfs_read_dir_subdirs(dir) != 0) + return NULL; if (dir->subdirs != NULL) { dlist_for_each_data(dir->subdirs, cursub, struct sysfs_directory) { - if (cursub->subdirs == NULL) - continue; ln = sysfs_get_subdirectory_link(cursub, linkname); if (ln != NULL) return ln; diff --git a/libsysfs/sysfs_driver.c b/libsysfs/sysfs_driver.c index 1877dbce0b..4372b19f10 100644 --- a/libsysfs/sysfs_driver.c +++ b/libsysfs/sysfs_driver.c @@ -23,36 +23,16 @@ #include "libsysfs.h" #include "sysfs.h" -static void sysfs_close_driver_by_name_dev(void *device) +static void sysfs_close_driver_device(void *device) { sysfs_close_device((struct sysfs_device *)device); } -/** - * sysfs_close_driver: closes and cleans up driver structure - * NOTE: This routine does not deallocate devices list - * @driver: driver to close - */ -void sysfs_close_driver(struct sysfs_driver *driver) -{ - if (driver != NULL) { - if (driver->devices != NULL) { - dlist_for_each(driver->devices) - dlist_shift(driver->devices); - free(driver->devices); - driver->devices = NULL; - } - if (driver->directory != NULL) - sysfs_close_directory(driver->directory); - free(driver); - } -} - /** - * sysfs_close_driver_by_name: closes driver and deletes device lists too + * sysfs_close_driver: closes driver and deletes device lists too * @driver: driver to close */ -void sysfs_close_driver_by_name(struct sysfs_driver *driver) +void sysfs_close_driver(struct sysfs_driver *driver) { if (driver != NULL) { if (driver->devices != NULL) @@ -63,6 +43,51 @@ void sysfs_close_driver_by_name(struct sysfs_driver *driver) } } +/** + * open_driver_dir: Open the sysfs_directory for this driver + * @driver: Driver whose directory to be opened + * Returns 0 on success and 1 on failure + */ +static int open_driver_dir(struct sysfs_driver *driver) +{ + if (driver == NULL) { + errno = EINVAL; + return 1; + } + if (driver->directory == NULL) { + driver->directory = sysfs_open_directory(driver->path); + if (driver->directory == NULL) { + dprintf("Error opening driver directory at %s\n", + driver->path); + return 1; + } + } + return 0; +} + +/** + * read_driver_dir: Read driver directory's subdirs and links + * @driver: Driver to read + * Returns 0 on success and 1 on failure + */ +static int read_driver_dir(struct sysfs_driver *driver) +{ + if (driver == NULL) { + errno = EINVAL; + return 1; + } + if (driver->directory == NULL) { + if ((open_driver_dir(driver)) == 1) + return 1; + } + if ((sysfs_read_directory(driver->directory)) != 0) { + dprintf("Error reading driver directory at %s\n", + driver->path); + return 1; + } + return 0; +} + /** * alloc_driver: allocates and initializes driver * returns struct sysfs_driver with success and NULL with error. @@ -80,31 +105,27 @@ static struct sysfs_driver *alloc_driver(void) struct sysfs_driver *sysfs_open_driver(const unsigned char *path) { struct sysfs_driver *driver = NULL; - struct sysfs_directory *sdir = NULL; if (path == NULL) { errno = EINVAL; return NULL; } - sdir = sysfs_open_directory(path); - if (sdir == NULL) { - dprintf("Error opening directory %s\n", path); - return NULL; - } - if ((sysfs_read_directory(sdir)) != 0) { - dprintf("Error reading directory %s\n", path); - sysfs_close_directory(sdir); + if ((sysfs_path_is_dir(path)) != 0) { + dprintf("Invalid path to driver: %s\n", path); return NULL; } driver = alloc_driver(); if (driver == NULL) { dprintf("Error allocating driver at %s\n", path); - sysfs_close_directory(sdir); return NULL; } - strcpy(driver->name, sdir->name); - driver->directory = sdir; - strcpy(driver->path, sdir->path); + if ((sysfs_get_name_from_path(path, driver->name, + SYSFS_NAME_LEN)) != 0) { + dprintf("Error getting driver name from path\n"); + free(driver); + return NULL; + } + strcpy(driver->path, path); return driver; } @@ -117,9 +138,32 @@ struct sysfs_driver *sysfs_open_driver(const unsigned char *path) */ struct dlist *sysfs_get_driver_attributes(struct sysfs_driver *driver) { - if (driver == NULL || driver->directory == NULL) + if (driver == NULL) { + errno = EINVAL; return NULL; + } + if (driver->directory == NULL) { + if ((open_driver_dir(driver)) == 1) + return NULL; + } + if (driver->directory->attributes == NULL) { + if ((sysfs_read_dir_attributes(driver->directory)) != 0) { + dprintf("Error reading driver attributes\n"); + return NULL; + } + } else { + if ((sysfs_path_is_dir(driver->path)) != 0) { + dprintf("Driver at %s no longer exists\n", + driver->path); + return NULL; + } + if ((sysfs_refresh_attributes + (driver->directory->attributes)) != 0) { + dprintf("Error refreshing driver attributes\n"); + return NULL; + } + } return(driver->directory->attributes); } @@ -133,18 +177,20 @@ struct sysfs_attribute *sysfs_get_driver_attr(struct sysfs_driver *drv, const unsigned char *name) { struct sysfs_attribute *cur = NULL; + struct dlist *attrlist = NULL; - if (drv == NULL || drv->directory == NULL - || drv->directory->attributes == NULL || name == NULL) { + if (drv == NULL) { errno = EINVAL; return NULL; } - - cur = sysfs_get_directory_attribute(drv->directory, - (unsigned char *)name); - if (cur != NULL) - return cur; - + + attrlist = sysfs_get_driver_attributes(drv); + if (attrlist != NULL) { + cur = sysfs_get_directory_attribute(drv->directory, + (unsigned char *)name); + if (cur != NULL) + return cur; + } return NULL; } @@ -156,12 +202,95 @@ struct sysfs_attribute *sysfs_get_driver_attr(struct sysfs_driver *drv, */ struct dlist *sysfs_get_driver_links(struct sysfs_driver *driver) { - if (driver == NULL || driver->directory == NULL) + if (driver == NULL) { + errno = EINVAL; return NULL; - + } + if (driver->directory == NULL) { + if ((open_driver_dir(driver)) == 1) + return NULL; + if ((read_driver_dir(driver)) != 0) + return NULL; + } return(driver->directory->links); } +/** + * sysfs_get_driver_devices: open up the list of devices this driver supports + * @driver: sysfs_driver for which devices are needed + * Returns dlist of devices on SUCCESS or NULL with ERROR + */ +struct dlist *sysfs_get_driver_devices(struct sysfs_driver *driver) +{ + struct sysfs_link *curlink = NULL; + struct sysfs_device *device = NULL; + + if (driver == NULL) { + errno = EINVAL; + return NULL; + } + + if (driver->devices != NULL) + return (driver->devices); + + if (driver->directory == NULL) { + if ((open_driver_dir(driver)) == 1) + return NULL; + if ((read_driver_dir(driver)) != 0) + return NULL; + } + if (driver->directory->links != NULL) { + dlist_for_each_data(driver->directory->links, curlink, + struct sysfs_link) { + device = sysfs_open_device(curlink->target); + if (device == NULL) { + dprintf("Error opening device at %s\n", + curlink->target); + return NULL; + } + strcpy(device->driver_name, driver->name); + if (driver->devices == NULL) + driver->devices = dlist_new_with_delete + (sizeof(struct sysfs_device), + sysfs_close_driver_device); + dlist_unshift(driver->devices, device); + } + } + return (driver->devices); +} + +/** + * sysfs_get_driver_device: looks up a device from a list of driver's devices + * and returns its sysfs_device corresponding to it + * @driver: sysfs_driver on which to search + * @name: name of the device to search + * Returns a sysfs_device if found, NULL otherwise + */ +struct sysfs_device *sysfs_get_driver_device(struct sysfs_driver *driver, + const unsigned char *name) +{ + struct sysfs_device *device = NULL; + struct dlist *devlist = NULL; + + if (driver == NULL || name == NULL) { + errno = EINVAL; + return NULL; + } + + if (driver->devices == NULL) { + devlist = sysfs_get_driver_devices(driver); + if (devlist == NULL) { + dprintf("Error getting driver devices\n"); + return NULL; + } + } + dlist_for_each_data(driver->devices, device, struct sysfs_device) { + if (!(strncmp(device->name, name, SYSFS_NAME_LEN))) + return device; + } + return NULL; +} + /** * get_driver_path: looks up the bus the driver is on and builds path to * the driver. @@ -171,8 +300,8 @@ struct dlist *sysfs_get_driver_links(struct sysfs_driver *driver) * @psize: size of "path" * Returns 0 on success and -1 on error */ -static int get_driver_path(const unsigned char *bus, const unsigned char *drv, - unsigned char *path, size_t psize) +static int get_driver_path(const unsigned char *bus, + const unsigned char *drv, unsigned char *path, size_t psize) { if (bus == NULL || drv == NULL || path == NULL) { errno = EINVAL; @@ -187,69 +316,13 @@ static int get_driver_path(const unsigned char *bus, const unsigned char *drv, strcat(path, SYSFS_BUS_NAME); strcat(path, "/"); strcat(path, bus); - strcat(path, SYSFS_DRIVERS_DIR); + strcat(path, "/"); + strcat(path, SYSFS_DRIVERS_NAME); strcat(path, "/"); strcat(path, drv); return 0; } -/** - * sysfs_open_driver_by_name: open a driver by name and return the bus - * the driver is on. - * @drv_name: driver to open - * @bus: the driver bus - * @bsize: size of bus buffer - * returns struct sysfs_driver if found, NULL otherwise - * NOTE: - * 1. Need to call sysfs_close_driver_by_name to free up memory - * 2. Bus the driver is registered with must be supplied. - * Use sysfs_find_driver_bus() to obtain the bus name - */ -struct sysfs_driver *sysfs_open_driver_by_name(const unsigned char *drv_name, - const unsigned char *bus, size_t bsize) -{ - struct sysfs_driver *driver = NULL; - struct sysfs_device *device = NULL; - struct sysfs_link *curlink = NULL; - unsigned char path[SYSFS_PATH_MAX]; - - if (drv_name == NULL || bus == NULL) { - errno = EINVAL; - return NULL; - } - - memset(path, 0, SYSFS_PATH_MAX); - if (get_driver_path(bus, drv_name, path, SYSFS_PATH_MAX) != 0) { - dprintf("Error getting to driver %s\n", drv_name); - return NULL; - } - driver = sysfs_open_driver(path); - if (driver == NULL) { - dprintf("Could not open driver %s\n", drv_name); - return NULL; - } - if (driver->directory->links != NULL) { - dlist_for_each_data(driver->directory->links, curlink, - struct sysfs_link) { - device = sysfs_open_device(curlink->target); - if (device == NULL) { - dprintf("Error opening device at %s\n", - curlink->target); - sysfs_close_driver_by_name(driver); - return NULL; - } - strcpy(device->driver_name, drv_name); - if (driver->devices == NULL) - driver->devices = dlist_new_with_delete - (sizeof(struct sysfs_device), - sysfs_close_driver_by_name_dev); - dlist_unshift(driver->devices, device); - } - } - return driver; -} - - /** * sysfs_open_driver_attr: read the user supplied driver attribute * @bus: bus on which to look diff --git a/libsysfs/sysfs_utils.c b/libsysfs/sysfs_utils.c index 627e618dce..c2ce13433d 100644 --- a/libsysfs/sysfs_utils.c +++ b/libsysfs/sysfs_utils.c @@ -280,7 +280,7 @@ struct dlist *sysfs_open_subsystem_list(unsigned char *name) * name requested here is "class", verify if "block" is supported on * this system and return the same. */ - if (strcmp(name, SYSFS_CLASS_DIR) == 0) { + if (strcmp(name, SYSFS_CLASS_NAME) == 0) { c = strstr(sysfs_path, SYSFS_CLASS_NAME); if (c == NULL) goto out; @@ -325,7 +325,8 @@ struct dlist *sysfs_open_bus_devices_list(unsigned char *name) strcat(sysfs_path, SYSFS_BUS_NAME); strcat(sysfs_path, "/"); strcat(sysfs_path, name); - strcat(sysfs_path, SYSFS_DEVICES_DIR); + strcat(sysfs_path, "/"); + strcat(sysfs_path, SYSFS_DEVICES_NAME); dir = sysfs_open_directory(sysfs_path); if (dir == NULL) { dprintf("Error opening sysfs_directory at %s\n", sysfs_path); @@ -358,3 +359,71 @@ struct dlist *sysfs_open_bus_devices_list(unsigned char *name) return list; } +/** + * sysfs_path_is_dir: Check if the path supplied points to a directory + * @path: path to validate + * Returns 0 if path points to dir, 1 otherwise + */ +int sysfs_path_is_dir(const unsigned char *path) +{ + struct stat astats; + + if (path == NULL) { + errno = EINVAL; + return 1; + } + if ((lstat(path, &astats)) != 0) { + dprintf("stat() failed\n"); + return 1; + } + if (S_ISDIR(astats.st_mode)) + return 0; + + return 1; +} + +/** + * sysfs_path_is_link: Check if the path supplied points to a link + * @path: path to validate + * Returns 0 if path points to link, 1 otherwise + */ +int sysfs_path_is_link(const unsigned char *path) +{ + struct stat astats; + + if (path == NULL) { + errno = EINVAL; + return 1; + } + if ((lstat(path, &astats)) != 0) { + dprintf("stat() failed\n"); + return 1; + } + if (S_ISLNK(astats.st_mode)) + return 0; + + return 1; +} + +/** + * sysfs_path_is_file: Check if the path supplied points to a file + * @path: path to validate + * Returns 0 if path points to file, 1 otherwise + */ +int sysfs_path_is_file(const unsigned char *path) +{ + struct stat astats; + + if (path == NULL) { + errno = EINVAL; + return 1; + } + if ((lstat(path, &astats)) != 0) { + dprintf("stat() failed\n"); + return 1; + } + if (S_ISREG(astats.st_mode)) + return 0; + + return 1; +} diff --git a/namedev.c b/namedev.c index 75e4d22370..633a7bf0da 100644 --- a/namedev.c +++ b/namedev.c @@ -552,37 +552,29 @@ int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *ud struct sysfs_device *sysfs_device = NULL; struct sysfs_class_device *class_dev_parent = NULL; int retval = 0; - char *temp = NULL; struct perm_device *perm; udev->mode = 0; /* find the sysfs_device for this class device */ /* Wouldn't it really be nice if libsysfs could do this for us? */ - if (class_dev->sysdevice) { - sysfs_device = class_dev->sysdevice; - } else { + sysfs_device = sysfs_get_classdev_device(class_dev); + if (sysfs_device == NULL) { /* bah, let's go backwards up a level to see if the device is there, * as block partitions don't point to the physical device. Need to fix that * up in the kernel... */ - if (strstr(class_dev->path, "block")) { + if (strcmp(class_dev->classname, SYSFS_BLOCK_NAME) == 0) { dbg("looking at block device"); if (isdigit(class_dev->path[strlen(class_dev->path)-1])) { - char path[SYSFS_PATH_MAX]; - dbg("really is a partition"); - strfieldcpy(path, class_dev->path); - temp = strrchr(path, '/'); - *temp = 0x00; - dbg("looking for a class device at '%s'", path); - class_dev_parent = sysfs_open_class_device(path); + class_dev_parent = sysfs_get_classdev_parent + (class_dev); if (class_dev_parent == NULL) { - dbg("sysfs_open_class_device at '%s' failed", path); + dbg("sysfs_get_classdev_parent for class device '%s' failed", class_dev->name); } else { dbg("class_dev_parent->name='%s'", class_dev_parent->name); - if (class_dev_parent->sysdevice) - sysfs_device = class_dev_parent->sysdevice; + sysfs_device = sysfs_get_classdev_device(class_dev_parent); } } } @@ -643,9 +635,6 @@ done: dbg("name, '%s' is going to have owner='%s', group='%s', mode = %#o", udev->name, udev->owner, udev->group, udev->mode); - if (class_dev_parent) - sysfs_close_class_device(class_dev_parent); - return 0; } diff --git a/udev-add.c b/udev-add.c index e64a845c7e..33ee633fd8 100644 --- a/udev-add.c +++ b/udev-add.c @@ -54,14 +54,14 @@ static int get_major_minor(struct sysfs_class_device *class_dev, struct udevice *udev) { int retval = -ENODEV; - char *dev; + struct sysfs_attribute *attr = NULL; - dev = sysfs_get_value_from_attributes(class_dev->directory->attributes, "dev"); - if (dev == NULL) + attr = sysfs_get_classdev_attr(class_dev, "dev"); + if (attr == NULL) goto exit; - dbg("dev='%s'", dev); + dbg("dev='%s'", attr->value); - if (sscanf(dev, "%u:%u", &udev->major, &udev->minor) != 2) + if (sscanf(attr->value, "%u:%u", &udev->major, &udev->minor) != 2) goto exit; dbg("found major=%d, minor=%d", udev->major, udev->minor);