diff options
author | David Zeuthen <david@fubar.dk> | 2003-11-22 19:51:15 +0000 |
---|---|---|
committer | David Zeuthen <david@fubar.dk> | 2003-11-22 19:51:15 +0000 |
commit | c6160293f92deecd3896c1439c29fbd0961a5fa6 (patch) | |
tree | 44d2fa01f4724e304db30bd6cfab2caab5b99e41 /agents | |
parent | cbac423b98e34e48b1be5185cf26feea886ef5e8 (diff) |
Updated to mention some source files may be LGPL also
Updated to use doxygen 1.3.4
Updated to use doxygen 1.3.4
Don't build agents/linux/usb/Makefile; we've dropped 2.4 support. Build
agents/linux26/sysfs/libsysfs/Makefile
Don't build in agents/linux
Build in libsysfs
Now using sysfsutils 0.3 (bus_support_append_device): removed
(bus_support_find_bus): removed (visit_device_usb_interface): Don't
require device UDI to be given; find it self (visit_device_usb): Don't
manually visit USB interfaces (visit_device): Change to recursively
visit children in device hierachy (visit_device_tree): removed
(visit_root_device): removed (bus_support_collect): removed; not
neccesary with sysfsutils 0.3 since we can get the bus from there
New directory for holding local copy of sysfsutils 0.3.
new file
new file
new file
new file
new file
new file
new file
new file
new file
new file
new file
new file
Diffstat (limited to 'agents')
-rw-r--r-- | agents/Makefile.am | 3 | ||||
-rw-r--r-- | agents/linux26/sysfs/Makefile.am | 7 | ||||
-rw-r--r-- | agents/linux26/sysfs/hal-sysfs-agent.c | 412 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/Makefile.am | 8 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/dlist.c | 343 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/dlist.h | 195 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/libsysfs.h | 243 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/sysfs.h | 49 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/sysfs_bus.c | 471 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/sysfs_class.c | 498 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/sysfs_device.c | 537 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/sysfs_dir.c | 704 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/sysfs_driver.c | 294 | ||||
-rw-r--r-- | agents/linux26/sysfs/libsysfs/sysfs_utils.c | 331 |
14 files changed, 3877 insertions, 218 deletions
diff --git a/agents/Makefile.am b/agents/Makefile.am index 136a6f1f..4d2bad36 100644 --- a/agents/Makefile.am +++ b/agents/Makefile.am @@ -1,6 +1,7 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = linux linux26 +#SUBDIRS = linux linux26 +SUBDIRS = linux26 clean-local : rm -f *~ diff --git a/agents/linux26/sysfs/Makefile.am b/agents/linux26/sysfs/Makefile.am index f5f12ee7..257e5fa0 100644 --- a/agents/linux26/sysfs/Makefile.am +++ b/agents/linux26/sysfs/Makefile.am @@ -1,9 +1,12 @@ ## Process this file with automake to produce Makefile.in +SUBDIRS = libsysfs + INCLUDES = \ -DPACKAGE_DATA_DIR=\""$(datadir)"\" \ -DPACKAGE_BIN_DIR=\""$(bindir)"\" \ -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ + -Ilibsysfs \ @PACKAGE_CFLAGS@ bin_PROGRAMS = hal-sysfs-agent.hotplug @@ -11,7 +14,9 @@ bin_PROGRAMS = hal-sysfs-agent.hotplug hal_sysfs_agent_hotplug_SOURCES = \ hal-sysfs-agent.c -hal_sysfs_agent_hotplug_LDADD = @PACKAGE_LIBS@ $(top_builddir)/libhal/libhal.la -lsysfs +#hal_sysfs_agent_hotplug_LDADD = @PACKAGE_LIBS@ + +hal_sysfs_agent_hotplug_LDADD = libsysfs/libsysfs.la $(top_builddir)/libhal/libhal.la -ldbus-1 -ldbus-glib-1 clean-local : rm -f *~ diff --git a/agents/linux26/sysfs/hal-sysfs-agent.c b/agents/linux26/sysfs/hal-sysfs-agent.c index 4d897766..db2224b1 100644 --- a/agents/linux26/sysfs/hal-sysfs-agent.c +++ b/agents/linux26/sysfs/hal-sysfs-agent.c @@ -34,6 +34,7 @@ #include <getopt.h> #include <assert.h> #include <unistd.h> +//#include <syslog.h> #include <dbus/dbus.h> #include <dbus/dbus-glib.h> @@ -50,6 +51,10 @@ */ +/** Macro to abort the program. + * + * @param expr Format line and arguments + */ #define DIE(expr) do {printf("*** [DIE] %s:%s():%d : ", __FILE__, __FUNCTION__, __LINE__); printf expr; printf("\n"); exit(1); } while(0) @@ -76,58 +81,6 @@ static void* xmalloc(unsigned int how_much) */ static dbus_bool_t is_probing = FALSE; -/** List of busses we know how to handle */ -static const char* bus_support[] = {"usb", "pci", "ide", "scsi"}; - -/** Size of list of busses to handle */ -static const int bus_support_num = sizeof(bus_support)/sizeof(char*); - -/** Maximum number of devices */ -#define HAL_MAX_DEVICES 16384 - -/** List of pair of (bustype, devicepath_in_sysfs) of bus type that we know - * how to handle - */ -static const char* bus_support_devices[HAL_MAX_DEVICES*2]; - -/** Number of elements in list of devices we know how to handle */ -static int bus_support_devices_num = 0; - -/** Append a device to list of devices of type we know how to handle. - * - * @param bus Bus-type, only the pointer is copied - * @param path Path in sysfs; the contents of the string - * is copied - */ -static void bus_support_append_device(const char* bus, const char* path) -{ - if( bus_support_devices_num>=HAL_MAX_DEVICES ) - return; - - bus_support_devices[bus_support_devices_num*2+0] = bus; - bus_support_devices[bus_support_devices_num*2+1] = strdup(path); - bus_support_devices_num++; -} - -/** Given a sysfs-path to a device, determine the bus type. Only works - * for bus types we know how to handle, cf. #bus_support. - * - * @param path Sysfs-path of device, e.g. - * /sys/devices/pci0000:00/0000:00:07.2 - * @return Bus-type or #NULL if unknown type - */ -static const char* bus_support_find_bus(const char* path) -{ - int i; - for(i=0; i<bus_support_devices_num; i++) - { - if( strcmp(bus_support_devices[i*2+1], path)==0 ) - return bus_support_devices[i*2]; - } - - return NULL; -} - /** Parse a double represented as a decimal number (base 10) in a string. * * @param str String to parse @@ -758,7 +711,17 @@ static dbus_bool_t usb_ids_free() -// fails if number not found +/** Find an integer appearing right after a substring in a string. + * + * The result is undefined if the number isn't properly formatted or + * the substring didn't exist in the given string. + * + * @param pre Substring preceding the value to parse + * @param s String to analyze + * @param base Base, e.g. decimal or hexadecimal, that + * number appears in + * @return Number + */ static long int find_num(char* pre, char* s, int base) { char* where; @@ -770,19 +733,30 @@ static long int find_num(char* pre, char* s, int base) where += strlen(pre); result = strtol(where, NULL, base); + /** @todo Handle errors gracefully */ if( result==LONG_MIN || result==LONG_MAX ) DIE(("Error parsing value for '%s' in '%s'", pre, s)); return result; } -// fails if double not found +/** Find a floating point number appearing right after a substring in a string + * and return it as a double precision IEEE754 floating point number. + * + * The result is undefined if the number isn't properly formatted or + * the substring didn't exist in the given string. + * + * @param pre Substring preceding the value to parse + * @param s String to analyze + * @return Number + */ static double find_double(char* pre, char* s) { char* where; double result; where = strstr(s, pre); + /** @todo Handle errors gracefully */ if( where==NULL ) DIE(("Didn't find '%s' in '%s'", pre, s)); where += strlen(pre); @@ -792,7 +766,16 @@ static double find_double(char* pre, char* s) return result; } -// decode a number into a BCD number with two digits of precision +/** Find a floating point number appearing right after a substring in a string + * and return it as a BCD encoded number with 2 digits of precision. + * + * The result is undefined if the number isn't properly formatted or + * the substring didn't exist in the given string. + * + * @param pre Substring preceding the value to parse + * @param s String to analyze + * @return Number + */ static int find_bcd2(char* pre, char* s) { int i; @@ -850,7 +833,16 @@ static int find_bcd2(char* pre, char* s) return result; } -// return val only valid until next invocation. Return NULL if string not found +/** Find a string appearing right after a substring in a string + * and return it. The string return is statically allocated and is + * only valid until the next invocation of this function. + * + * The result is undefined if the substring didn't exist in the given string. + * + * @param pre Substring preceding the value to parse + * @param s String to analyze + * @return Number + */ static char* find_string(char* pre, char* s) { char* where; @@ -889,7 +881,9 @@ static char* find_string(char* pre, char* s) return buf; } -/** Key information from /proc not available in sysfs */ +/** Key information about USB devices from /proc that is not available + * in sysfs + */ typedef struct usb_proc_info_s { int t_bus; /**< Bus number */ @@ -976,7 +970,12 @@ static usb_proc_info* usb_proc_find_on_hub(int bus_number, int port_number, } -// T: Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 +/** Parse the topology field + * + * @param info Structure to put information into + * @param s Line from /proc/bus/usb/devices starting + * with "T:" + */ static void usb_proc_handle_topology(usb_proc_info* info, char* s) { info->t_bus = find_num("Bus=", s, 10); @@ -989,12 +988,22 @@ static void usb_proc_handle_topology(usb_proc_info* info, char* s) info->t_max_children = find_num("MxCh=",s, 10); } +/** Parse the device descriptor field + * + * @param info Structure to put information into + * @param s Line from /proc/bus/usb/devices starting + * with "D:" + */ static void usb_proc_handle_device_info(usb_proc_info* info, char* s) { info->d_version_bcd = find_bcd2("Ver=",s); } +/** Called when an entry from /proc/bus/usb/devices have been parsed. + * + * @param info Structure representing the entry + */ static void usb_proc_device_done(usb_proc_info* info) { info->next = usb_proc_head; @@ -1072,6 +1081,7 @@ static void usb_proc_parse() } usb_proc_device_done(usb_proc_cur_info); +/* { usb_proc_info* i; for(i=usb_proc_head; i!=NULL; i=i->next) @@ -1091,6 +1101,7 @@ static void usb_proc_parse() printf("\n"); } } +*/ } @@ -1350,26 +1361,79 @@ static char* find_parent_udi_from_sysfs_path(const char* path) return NULL; } + + +/** Given a sysfs-path for a device, this functions finds the HAL device + * representing the given device. + * + * @param path Sysfs-path of device to find UDI for + * @return UDI (unique device id) of device, or #NULL + * if no device was found. + */ +static char* find_udi_from_sysfs_path(const char* path) +{ + int i; + int len; + char** udis; + int num_udi; + + + udis = hal_manager_find_device_string_match("Linux.sysfs_path", + path, + &num_udi); + + if( num_udi==1 && udis!=NULL ) + { + return udis[0]; + } + + /** @todo Log the error */ + + return NULL; +} + + /** Visitor function for interfaces on a USB device. * * @param path Sysfs-path for USB interface * @param device libsysfs object for USB interface - * @param d UDI of HAL device to amend interface data to */ static void visit_device_usb_interface(const char* path, - struct sysfs_device *device, - const char* d) + struct sysfs_device *device) { int i; int len; int in_num; int conf_num; struct sysfs_attribute* cur; + char* d; char buf[256]; char attr_name[SYSFS_NAME_LEN]; + char parent_path[SYSFS_PATH_MAX+1]; + + //printf("usb_interface: path=%s\n", path); + + /* First find parent device; that's easy; just remove chars from end + * until and including first '/' + */ + strncpy(parent_path, path, SYSFS_PATH_MAX); + len = strlen(parent_path); + for(i=len-1; i>0; --i) + { + if( parent_path[i]=='/' ) + break; + parent_path[i]='\0'; + } + parent_path[i]='\0'; + //printf("parent_path = '%s'\n", parent_path); + /* Lookup HAL device from parent_path */ + d = find_udi_from_sysfs_path(parent_path); + //printf("parent udi = '%s'\n", d); + + + if( d==NULL ) + return; - /*printf("usb_interface: path=%s, d=%s\n", path, d);*/ - if( device->directory==NULL || device->directory->attributes==NULL ) return; @@ -1380,7 +1444,8 @@ static void visit_device_usb_interface(const char* path, /* first, find interface number */ in_num = -1; - for(cur=device->directory->attributes; cur!=NULL; cur=cur->next) + dlist_for_each_data(sysfs_get_device_attributes(device), cur, + struct sysfs_attribute) { if( sysfs_get_name_from_path(cur->path, attr_name, SYSFS_NAME_LEN) != 0 ) @@ -1396,20 +1461,21 @@ static void visit_device_usb_interface(const char* path, /*printf("conf_num=%d, in_num=%d\n", conf_num, in_num);*/ - for(cur=device->directory->attributes; cur!=NULL; cur=cur->next) + dlist_for_each_data(sysfs_get_device_attributes(device), cur, + struct sysfs_attribute) { if( sysfs_get_name_from_path(cur->path, attr_name, SYSFS_NAME_LEN) != 0 ) continue; - + /* strip whitespace */ len = strlen(cur->value); - for(i=len-1; isspace(cur->value[i]); --i) + for(i=len-1; i>0 && isspace(cur->value[i]); --i) cur->value[i] = '\0'; /*printf("attr_name=%s -> '%s'\n", attr_name, cur->value);*/ - + if( strcmp(attr_name, "bInterfaceClass")==0 ) { snprintf(buf, 256, "usb.%d.%d.bInterfaceClass", conf_num, in_num); @@ -1441,7 +1507,6 @@ static void visit_device_usb_interface(const char* path, } } } - } /** Visitor function for USB device. @@ -1472,17 +1537,19 @@ static void visit_device_usb(const char* path, struct sysfs_device *device) int bus_number; usb_proc_info* proc_info; - printf("usb: %s, bus_id=%s\n", path, device->bus_id); + /*printf("usb: %s, bus_id=%s\n", path, device->bus_id);*/ if( device->directory==NULL || device->directory->attributes==NULL ) return; /* Check if this is an USB interface */ is_interface = FALSE; - for(cur=device->directory->attributes; - cur!=NULL && !is_interface; - cur=cur->next) + dlist_for_each_data(sysfs_get_device_attributes(device), cur, + struct sysfs_attribute) { + if( is_interface ) + break; + if( sysfs_get_name_from_path(cur->path, attr_name, SYSFS_NAME_LEN) != 0 ) continue; @@ -1491,9 +1558,12 @@ static void visit_device_usb(const char* path, struct sysfs_device *device) is_interface = TRUE; } - /* We handle USB interfaces when visiting parent device; see below */ + /* USB interfaces are handled by a separate function */ if( is_interface ) + { + visit_device_usb_interface(path, device); return; + } /* Must be a new USB device */ d = hal_agent_new_device(); @@ -1507,7 +1577,8 @@ static void visit_device_usb(const char* path, struct sysfs_device *device) hal_device_set_property_string(d, "usb.linux.sysfs_path", path); /*printf("*** created udi=%s for path=%s\n", d, path);*/ - for(cur=device->directory->attributes; cur!=NULL; cur=cur->next) + dlist_for_each_data(sysfs_get_device_attributes(device), cur, + struct sysfs_attribute) { if( sysfs_get_name_from_path(cur->path, @@ -1531,6 +1602,18 @@ static void visit_device_usb(const char* path, struct sysfs_device *device) else if( strcmp(attr_name, "bMaxPower")==0 ) hal_device_set_property_int(d, "usb.bMaxPower", parse_dec(cur->value)); + else if( strcmp(attr_name, "serial")==0 && strlen(cur->value)>0 ) + hal_device_set_property_string(d, "usb.serial", cur->value); + else if( strcmp(attr_name, "bmAttributes")==0 ) + { + int bmAttributes = parse_hex(cur->value); + + /* USB_CONFIG_ATT_SELFPOWER */ + hal_device_set_property_bool(d, "usb.selfPowered", + (bmAttributes&0x40)!=0 ); + hal_device_set_property_bool(d, "usb.canWakeUp", + (bmAttributes&0x20)!=0 ); + } /* else if( strcmp(attr_name, "speed")==0 ) hal_device_set_property_double(d, "usb.speed", @@ -1600,19 +1683,6 @@ static void visit_device_usb(const char* path, struct sysfs_device *device) hal_device_set_property_string(d, "Product", product_name_kernel); } - - /* Now visit interfaces of this USB device */ - for(in=device->children; in!=NULL; in=in->next) - { - /* Interfaces have a ":" in their name, children-devices doesn't */ - if( strstr(in->bus_id, ":")!=NULL ) - { - /*printf("Visiting interface %s\n", in->bus_id);*/ - snprintf(in_path, SYSFS_PATH_MAX, "%s/%s", path, in->bus_id); - visit_device_usb_interface(in_path, in, d); - } - } - /* Compute parent */ parent_udi = find_parent_udi_from_sysfs_path(path); if( parent_udi!=NULL ) @@ -1712,7 +1782,7 @@ static void visit_device_usb(const char* path, struct sysfs_device *device) parent_device_number = hal_device_get_property_int(parent_udi, "usb.linux.device_number"); - printf("parent_device_number = %d\n", parent_device_number); + //printf("parent_device_number = %d\n", parent_device_number); proc_info = usb_proc_find_on_hub(bus_number, port_number, parent_device_number); } @@ -1822,7 +1892,8 @@ static void visit_device_pci(const char* path, struct sysfs_device *device) hal_device_set_property_string(d, "pci.linux.sysfs_path", path); /*printf("*** created udi=%s for path=%s\n", d, path);*/ - for(cur=device->directory->attributes; cur!=NULL; cur=cur->next) + dlist_for_each_data(sysfs_get_device_attributes(device), cur, + struct sysfs_attribute) { if( sysfs_get_name_from_path(cur->path, @@ -1938,132 +2009,48 @@ static void visit_device_scsi(const char* path, struct sysfs_device *device) /** Visitor function for any device. * - * This function determines the bus-type of the device using the - * #bus_support_devices list and call the appropriate visit_device_<bustype> - * if matched. + * This function determines the bus-type of the device and call the + * appropriate visit_device_<bustype> function if matched. * * @param path Sysfs-path for device * @param device libsysfs object for device */ -static void visit_device(const char* path, struct sysfs_device *device) +static void visit_device(const char* path) { - const char* bus; + struct sysfs_device* device; + struct sysfs_directory* subdir; - if( device!=NULL && device->directory!=NULL) - { - bus = bus_support_find_bus(path); - if( bus!=NULL ) - { - if( strcmp(bus, "usb")==0 ) - visit_device_usb(path, device); - else if( strcmp(bus, "pci")==0 ) - visit_device_pci(path, device); - else if( strcmp(bus, "ide")==0 ) - visit_device_ide(path, device); - else if( strcmp(bus, "scsi")==0 ) - visit_device_scsi(path, device); - } - } -} + device = sysfs_open_device(path); + if( device==NULL ) + DIE(("Coulnd't get sysfs device object for path %s", path)); -/** Recurse through a device tree and visit all the children of the device. - * Calls #visit_device on every node. - * - * @param path Sysfs-path for device - * @param device libsysfs object for device - */ -static void visit_device_tree(const char* path, struct sysfs_device* device) -{ - char newpath[SYSFS_PATH_MAX]; + //printf("bus=%s driver=%s device=%s\n", device->bus, device->driver_name, path); - if( device!=NULL ) + if( device->bus!=NULL ) { - struct sysfs_device *cur = NULL; - - visit_device(path, device); - - /* Visit all devices */ - for(cur=device->children; cur!=NULL; cur=cur->next) - { - snprintf(newpath, SYSFS_PATH_MAX, "%s/%s", path, cur->bus_id); - visit_device_tree(newpath, cur); - } + if( strcmp(device->bus, "usb")==0 ) + visit_device_usb(path, device); + else if( strcmp(device->bus, "pci")==0 ) + visit_device_pci(path, device); + else if( strcmp(device->bus, "ide")==0 ) + visit_device_ide(path, device); + else if( strcmp(device->bus, "scsi")==0 ) + visit_device_scsi(path, device); } -} -/** Visit device tree for a root device. Root devices are stored in - * /sys/devices. - * - * @param path Path of root device, e.g. - * /sys/devices/pci0000:00 - */ -static void visit_root_device(const char* path) -{ - struct sysfs_device* root; - - root = sysfs_open_device_tree((char*)path); - if( root==NULL ) - DIE(("Error opening root device %s\n", path)); - - visit_device_tree(path, root); - sysfs_close_device_tree(root); -} - -/** Make a list of all devices on the busses we know. Essentially traverses - * /sys/bus/_busname_/devices and builds the list #bus_support_devices. - * - * This is being done so we can determine the bus-type of a device - * when traversing the root devices on the system. - */ -static void bus_support_collect() -{ - int rc; - int i; - char sysfs_path[SYSFS_PATH_MAX]; - char path[SYSFS_PATH_MAX]; - char linkpath[SYSFS_PATH_MAX]; - char targetpath[SYSFS_PATH_MAX]; - struct sysfs_directory* dir; - struct sysfs_dlink* current_link; - - /* get mount path */ - rc = sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX); - if( rc!=0 ) - DIE(("Couldn't get mount path for sysfs")); - - /* first get a list of all devices at the busses we support by - * collecting the links in /sys/bus/<busname>/devices - */ - for(i=0; i<bus_support_num; i++) + /* Visit children */ + if( device->directory->subdirs!=NULL ) { - const char* bus; - - bus = bus_support[i]; - snprintf(path, SYSFS_PATH_MAX, - "%s%s/%s%s",sysfs_path,SYSFS_BUS_DIR, bus, SYSFS_DEVICES_DIR); - dir = sysfs_open_directory(path); - if( dir==NULL ) - { - DIE(("Error opening sysfs directory at %s\n", path)); - } - if( sysfs_read_directory(dir)!=0 ) - { - DIE(("Error reading sysfs directory at %s\n", path)); - } - current_link = dir->links; - while( current_link!=NULL ) + dlist_for_each_data(device->directory->subdirs, subdir, + struct sysfs_directory) { - snprintf(linkpath, SYSFS_PATH_MAX, "%s/%s", - path, current_link->name); - rc = sysfs_get_link(linkpath, targetpath, SYSFS_PATH_MAX); - - /* Append this device to list of devices */ - bus_support_append_device(bus, targetpath); - - current_link = current_link->next; + char newpath[SYSFS_PATH_MAX]; + snprintf(newpath, SYSFS_PATH_MAX, "%s/%s", path, subdir->name); + visit_device(newpath); } - sysfs_close_directory(dir); } + + sysfs_close_device(device); } /** This function is called when the program is invoked to probe sysfs. @@ -2078,9 +2065,6 @@ static void hal_sysfs_probe() is_probing = TRUE; - /* Collect devices from supported busses */ - bus_support_collect(); - /* get mount path */ rc = sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX); if( rc!=0 ) @@ -2097,11 +2081,14 @@ static void hal_sysfs_probe() { DIE(("Error reading sysfs directory at %s\n", path)); } - current = dir->subdirs; - while( current!=NULL ) + + + if( dir->subdirs!=NULL ) { - visit_root_device(current->path); - current = current->next; + dlist_for_each_data(dir->subdirs, current, struct sysfs_directory) + { + visit_device(current->path); + } } sysfs_close_directory(dir); } @@ -2116,9 +2103,6 @@ static void device_hotplug_add() char sysfs_path[SYSFS_PATH_MAX]; struct sysfs_device* device; - /* Collect devices from supported busses */ - bus_support_collect(); - /* Discard USB interface hotplug events */ interface = getenv("INTERFACE"); if( interface!=NULL ) @@ -2136,11 +2120,7 @@ static void device_hotplug_add() snprintf(path, SYSFS_PATH_MAX, "%s%s", sysfs_path, devpath); - device = sysfs_open_device_tree(path); - if( device==NULL ) - DIE(("Coulnd't get sysfs device object for path %s", path)); - visit_device(path, device); - sysfs_close_device_tree(device); + visit_device(path); } diff --git a/agents/linux26/sysfs/libsysfs/Makefile.am b/agents/linux26/sysfs/libsysfs/Makefile.am new file mode 100644 index 00000000..1529eb99 --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/Makefile.am @@ -0,0 +1,8 @@ + +lib_LTLIBRARIES = libsysfs.la +libsysfs_la_SOURCES = sysfs_utils.c sysfs_dir.c sysfs_bus.c sysfs_class.c \ + sysfs_device.c sysfs_driver.c sysfs.h dlist.c + +clean-local : + rm -f *~ + diff --git a/agents/linux26/sysfs/libsysfs/dlist.c b/agents/linux26/sysfs/libsysfs/dlist.c new file mode 100644 index 00000000..6dfcf726 --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/dlist.c @@ -0,0 +1,343 @@ +/* + * dlist.c + * + * Copyright (C) 2003 Eric J Bohm + * + * 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 021110307 USA + * + */ + + +/* Double linked list implementation. + + * You allocate the data and give dlist the pointer. + * If your data is complex set the dlist->del_func to a an appropriate + * delete function. Otherwise dlist will just use free. + +*/ +#include "dlist.h" + +/* + * Return pointer to node at marker. + * else null if no nodes. + */ + +inline void *dlist_mark(Dlist *list) +{ + if(list->marker!=NULL) + return(list->marker->data); + else + return(NULL); +} + +/* + * Set marker to start. + */ + +inline void dlist_start(Dlist *list) +{ + list->marker=list->head; +} + +/* + * Set marker to end. + */ + +inline void dlist_end(Dlist *list) +{ + list->marker=list->head; +} + +/* internal use function + * quickie inline to consolidate the marker movement logic + * in one place + * + * when direction true it moves marker after + * when direction false it moves marker before. + * return pointer to data at new marker + * if nowhere to move the marker in desired direction return null + */ +inline void *_dlist_mark_move(Dlist *list,int direction) +{ + if(direction) + { + if( list->marker->next!=NULL) + list->marker=list->marker->next; + else + return(NULL); + } + else + { + if( list->marker->prev!=NULL) + list->marker=list->marker->prev; + else + return(NULL); + } + if(list->marker!=list->head) + return(list->marker->data); + else + return(NULL); +} + +/* + * Create new linked list to store nodes of datasize. + * return null if list cannot be created. + */ +Dlist *dlist_new(size_t datasize) +{ + Dlist *list=NULL; + if((list=malloc(sizeof(Dlist)))) + { + list->marker=NULL; + list->count=0L; + list->data_size=datasize; + list->del_func=free; + list->head=&(list->headnode); + list->head->prev=NULL; + list->head->next=NULL; + list->head->data=NULL; + } + return(list); +} + +/* + * Create new linked list to store nodes of datasize set list + * data node delete function to the passed in del_func + * return null if list cannot be created. + */ +Dlist *dlist_new_with_delete(size_t datasize,void (*del_func)(void*)) +{ + Dlist *list=NULL; + list=dlist_new(datasize); + if(list!=NULL) + list->del_func=del_func; + return(list); +} + + +/* + * remove marker node from list + * call data_delete function on data if registered. + * otherwise call free. + * when direction true it moves marker after + * when direction false it moves marker before. + * free marker node + * return nothing. + */ +void dlist_delete(Dlist *list,int direction) +{ + if((list->marker != list->head)&&(list->marker!=NULL)) + { + DL_node *corpse; + corpse=list->marker; + _dlist_mark_move(list,direction); + if(list->head->next==corpse) + list->head->next=corpse->next; + if(list->head->prev==corpse) + list->head->prev=corpse->prev; + if(corpse->prev!=NULL) //should be impossible + corpse->prev->next=corpse->next; + if(corpse->next!=NULL) //should be impossible + corpse->next->prev=corpse->prev; + list->del_func(corpse->data); + list->count--; + free(corpse); + } +} + +/* + * Insert node containing data at marker. + * If direction true it inserts after. + * If direction false it inserts before. + * move marker to inserted node + * return pointer to inserted node + */ +void *dlist_insert(Dlist *list,void *data,int direction) +{ + DL_node *new_node=NULL; + if(list==NULL || data==NULL) + return(NULL); + if(list->marker==NULL) //in case the marker ends up unset + list->marker=list->head; + if((new_node=malloc(sizeof(DL_node)))) + { + new_node->data=data; + new_node->prev=NULL; + new_node->next=NULL; + list->count++; + if(list->head->next==NULL) //no l + { + list->head->next=list->head->prev=new_node; + new_node->prev=list->head; + new_node->next=list->head; + } + else if(direction) + { + new_node->next=list->marker->next; + new_node->prev=list->marker; + list->marker->next->prev=new_node; + list->marker->next=new_node; + } + else + { + new_node->prev=list->marker->prev; + new_node->next=list->marker; + list->marker->prev->next=new_node; + list->marker->prev=new_node; + } + list->marker=new_node; + } + else + { + return(NULL); + } + return(list->marker->data); +} + +/* + * Remove DL_node from list without deallocating data. + * if marker == killme . + * when direction true it moves marker after + * when direction false it moves marker before. + * to previous if there is no next. + */ +void *_dlist_remove(Dlist *list,DL_node *killme,int direction) +{ + if(killme!=NULL) + { + void *killer_data=killme->data; + // take care of head and marker pointers. + if(list->marker==killme) + _dlist_mark_move(list,direction); + if(killme ==list->head->next) + list->head->next=killme->next; + if(killme==list->head->prev) + list->head->prev=killme->prev; + // remove from list + if(killme->prev !=NULL) + killme->prev->next=killme->next; + if(killme->next !=NULL) + killme->next->prev=killme->prev; + list->count--; + free(killme); + return(killer_data); + } + else + return (NULL); +} + + +/* + * Insert node containing data after end. + */ +void dlist_push(Dlist *list,void *data) +{ + list->marker=list->head->prev; + dlist_insert(list,data,1); +} + +/* + * Insert node containing data at start. + */ + +void dlist_unshift(Dlist *list,void *data) + +{ + list->marker=list->head->next; + dlist_insert(list,data,0); +} + + +/* + * Remove end node from list. + * Return pointer to data in removed node. + * Null if no nodes. + */ + +void *dlist_pop(Dlist *list) +{ + return(_dlist_remove(list,list->head->prev,0)); +} + +/* + * Remove start node from list. + * Return pointer to data in removed node. + * Null if no nodes. + */ + +void *dlist_shift(Dlist *list) +{ + return(_dlist_remove(list,list->head->next,1)); +} + + +/* + * destroy the list freeing all memory + */ + + +void dlist_destroy(Dlist *list) +{ + if(list !=NULL) + { + dlist_start(list); + dlist_next(list); + while (dlist_mark(list)) { + dlist_delete(list,1); + } + free(list); + } +} + +/** + * Return void pointer to list_data element matching comp function criteria + * else null + * Does not move the marker. + */ + +void *dlist_find_custom(struct dlist *list, void *target, int (*comp)(void *, void *)) +{ + /* test the comp function on each node */ + struct dl_node *nodepointer; + dlist_for_each_nomark(list,nodepointer) + if(comp(target,nodepointer->data)) + return(nodepointer->data); + return(NULL); +} + +/** + * Apply the node_operation function to each data node in the list + */ +void dlist_transform(struct dlist *list, void (*node_operation)(void *)) +{ + struct dl_node *nodepointer; + dlist_for_each_nomark(list,nodepointer) + node_operation(nodepointer->data); +} + +/** + * insert new into list in sorted order + * sorter function in form int sorter(new,ith) + * must return 1 for when new should go before ith + * else 0 + * return pointer to inserted node + * NOTE: assumes list is already sorted + */ +void *dlist_insert_sorted(struct dlist *list, void *new, int (*sorter)(void *, void *)) +{ + for(dlist_start(list),dlist_next(list); \ + list->marker!=list->head && !sorter(new,list->marker->data);dlist_next(list)); + return(dlist_insert_before(list,new)); +} diff --git a/agents/linux26/sysfs/libsysfs/dlist.h b/agents/linux26/sysfs/libsysfs/dlist.h new file mode 100644 index 00000000..5da79f9b --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/dlist.h @@ -0,0 +1,195 @@ +/* + * dlist.h + * + * Copyright (C) 2003 Eric J Bohm + * + * 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 + * + */ +#ifndef _DLIST_H_ +#define _DLIST_H_ + +/* Double linked list header. + +* navigate your list with DLIST_PREV and DLIST_NEXT. These are macros +* so function call overhead is minimized. + +* Supports perl style push, pop, shift, unshift list semantics. + +* You allocate the data and give dlist the pointer. If your data is +* complex set the dlist->del_func to a an appropriate delete using +* dlist_new_with_delete. Your delete function must match +(void * )(del(void *) +*Otherwise dlist will just use free. + +* NOTE: The small amount of pain involved in doing that allows us to +* avoid copy in copy out semantics. + +* Dlist uses an internal mark pointer to keep track of where you are +* in the list. + +* insert and delete take a directional parameter. Where direction +* corresponds to the direction in which you want the list to go. +* true direction corresponded to progressing forward in the last +* false to regressing in the list. +* so a dlist_insert(yourlist,item,1) will insert it after the mark +* so a dlist_insert(yourlist,item,0) will insert it before the mark +* any insert will move the mark to the new node regardless of the direction. + +* Just use the dlist_(insert|delete)_(before|after) macros if you do not want +* to think about it. + +*/ +#include <malloc.h> +typedef struct dl_node { + struct dl_node *prev; + struct dl_node *next; + void *data; +} DL_node; + +typedef struct dlist { + DL_node *marker; + unsigned long count; + size_t data_size; + void (*del_func)(void *); + DL_node headnode; + DL_node *head; +} Dlist; + +Dlist *dlist_new(size_t datasize); +Dlist *dlist_new_with_delete(size_t datasize,void (*del_func)(void*)); +void *_dlist_mark_move(Dlist *list,int direction); +void *dlist_mark(Dlist *); +void dlist_start(Dlist *); +void dlist_end(Dlist *); + +void *dlist_insert(Dlist *,void *,int) ; + +void *dlist_insert_sorted(struct dlist *list, void *new, int (*sorter)(void *, void *)); + +void dlist_delete(Dlist *,int); + +void dlist_push(Dlist *,void *); + +void dlist_unshift(Dlist *,void *); + +void *dlist_pop(Dlist *); + +void *dlist_shift(Dlist *); + +void dlist_destroy(Dlist *); + +void *dlist_find_custom(struct dlist *list, void *target, int (*comp)(void *, void *)); +void dlist_transform(struct dlist *list, void (*node_operation)(void *)); + + +/* + * _dlist_remove is for internal use only + * _dlist_mark_move is for internal use only + */ +void *_dlist_remove(struct dlist *,struct dl_node *,int ); + +#define dlist_prev(A) _dlist_mark_move((A),0) +#define dlist_next(A) _dlist_mark_move((A),1) + +#define dlist_insert_before(A,B) dlist_insert((A),(B),0) +#define dlist_insert_after(A,B) dlist_insert((A),(B),1) + +#define dlist_delete_before(A) dlist_delete((A),0) +#define dlist_delete_after(A) dlist_delete((A),1) + +/** + * provide for loop header which iterates the mark from start to end + * list: the dlist pointer, use dlist_mark(list) to get iterator + */ +#define dlist_for_each(list) \ + for(dlist_start(list),dlist_next(list); \ + (list)->marker!=(list)->head;dlist_next(list)) + +/** + * provide for loop header which iterates the mark from end to start + * list: the dlist pointer, use dlist_mark(list) to get iterator + */ +#define dlist_for_each_rev(list) \ + for(dlist_end(list),dlist_prev(list); \ + (list)->marker!=(list)->head;dlist_prev(list)) + +/** + * provide for loop header which iterates through the list without moving mark + * list: the dlist_pointer + * iterator: dl_node pointer to iterate + */ +#define dlist_for_each_nomark(list,iterator) \ + for((iterator)=(list)->head->next; (iterator)!=(list)->head; \ + (iterator)=(iterator)->next) + +/** + * provide for loop header which iterates through the list without moving mark + * in reverse + * list: the dlist_pointer + * iterator: dl_node pointer to iterate + */ +#define dlist_for_each_nomark_rev(list,iterator) \ + for((iterator)=(list)->head->prev; (iterator)!=(list)->head; \ + (iterator)=(iterator)->prev) +/** + * provide for loop header which iterates through the list providing a + * data iterator + * list: the dlist pointer + * data_iterator: the pointer of type datatype to iterate + * datatype: actual type of the contents in the dl_node->data + */ + +#define dlist_for_each_data(list,data_iterator,datatype) \ + for(dlist_start(list), (data_iterator)=(datatype *) dlist_next(list); \ + (list)->marker!=(list)->head;(data_iterator)=(datatype *) dlist_next(list)) + +/** + * provide for loop header which iterates through the list providing a + * data iterator in reverse + * list: the dlist pointer + * data_iterator: the pointer of type datatype to iterate + * datatype: actual type of the contents in the dl_node->data + */ +#define dlist_for_each_data_rev(list,data_iterator,datatype) \ + for(dlist_end(list), (data_iterator)=(datatype *) dlist_prev(list); \ + (list)->marker!=(list)->head;(data_iterator)=(datatype *) dlist_prev(list)) + +/** + * provide for loop header which iterates through the list providing a + * data iterator without moving the mark + * list: the dlist pointer + * iterator: the dl_node pointer to iterate + * data_iterator: the pointer of type datatype to iterate + * datatype: actual type of the contents in the dl_node->data + */ + +#define dlist_for_each_data_nomark(list,iterator,data_iterator,datatype) \ + for((iterator)=(list)->head->next, (data_iterator)=(datatype *) (iterator)->data; \ + (iterator)!=(list)->head;(iterator)=(iterator)->next,(data_iterator)=(datatype *) (iterator)) + +/** + * provide for loop header which iterates through the list providing a + * data iterator in reverse without moving the mark + * list: the dlist pointer + * iterator: the dl_node pointer to iterate + * data_iterator: the pointer of type datatype to iterate + * datatype: actual type of the contents in the dl_node->data + */ +#define dlist_for_each_data_nomark_rev(list,iterator, data_iterator,datatype) \ + for((iterator)=(list)->head->prev, (data_iterator)=(datatype *) (iterator)->data; \ + (iterator)!=(list)->head;(iterator)=(iterator)->prev,(data_iterator)=(datatype *) (iterator)) + +#endif /* _DLIST_H_ */ diff --git a/agents/linux26/sysfs/libsysfs/libsysfs.h b/agents/linux26/sysfs/libsysfs/libsysfs.h new file mode 100644 index 00000000..f7e989e2 --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/libsysfs.h @@ -0,0 +1,243 @@ +/* + * libsysfs.h + * + * Header Definitions for libsysfs + * + * Copyright (C) IBM Corp. 2003 + * + * 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 + * + */ +#ifndef _LIBSYSFS_H_ +#define _LIBSYSFS_H_ + +#include <sys/types.h> +#include "dlist.h" + +/* + * Generic #defines go here.. + */ +#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" +#define SYSFS_PATH_ENV "SYSFS_PATH" + +#define SYSFS_PATH_MAX 255 +#define SYSFS_NAME_LEN 50 +#define SYSFS_BUS_ID_SIZE 20 + +#define SYSFS_METHOD_SHOW 0x01 /* attr can be read by user */ +#define SYSFS_METHOD_STORE 0x02 /* attr can be changed by user */ + +struct sysfs_attribute { + unsigned char *value; + unsigned short len; /* value length */ + unsigned short method; /* show and store */ + unsigned char name[SYSFS_NAME_LEN]; + unsigned char path[SYSFS_PATH_MAX]; +}; + +struct sysfs_link { + unsigned char name[SYSFS_NAME_LEN]; + unsigned char path[SYSFS_PATH_MAX]; + unsigned char target[SYSFS_PATH_MAX]; +}; + +struct sysfs_directory { + struct dlist *subdirs; + struct dlist *links; + struct dlist *attributes; + unsigned char name[SYSFS_NAME_LEN]; + unsigned char path[SYSFS_PATH_MAX]; +}; + +struct sysfs_driver { + struct dlist *devices; + unsigned char name[SYSFS_NAME_LEN]; + unsigned char path[SYSFS_PATH_MAX]; + + /* for internal use only */ + 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]; + unsigned char driver_name[SYSFS_NAME_LEN]; + unsigned char path[SYSFS_PATH_MAX]; + + /* for internal use only */ + 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 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 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_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 sysfs_directory *directory; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Function Prototypes + */ +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_get_link(const unsigned char *path, unsigned char *target, + size_t len); +extern struct dlist *sysfs_open_subsystem_list(unsigned char *name); +extern struct dlist *sysfs_open_bus_devices_list(unsigned char *name); +extern void sysfs_close_list(struct dlist *list); + +/* sysfs directory and file access */ +extern void sysfs_close_attribute(struct sysfs_attribute *sysattr); +extern struct sysfs_attribute *sysfs_open_attribute(const unsigned char *path); +extern int sysfs_read_attribute(struct sysfs_attribute *sysattr); +extern int sysfs_read_attribute_value(const unsigned char *attrpath, + unsigned char *value, size_t vsize); +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 void sysfs_close_directory(struct sysfs_directory *sysdir); +extern struct sysfs_directory *sysfs_open_directory(const unsigned char *path); +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 + (struct sysfs_directory *dir, unsigned char *subname); +extern void sysfs_close_link(struct sysfs_link *ln); +extern struct sysfs_link *sysfs_open_link(const unsigned char *lnpath); +extern struct sysfs_link *sysfs_get_directory_link(struct sysfs_directory *dir, + unsigned char *linkname); +extern struct sysfs_link *sysfs_get_subdirectory_link + (struct sysfs_directory *dir, unsigned char *linkname); +extern struct sysfs_attribute *sysfs_get_directory_attribute + (struct sysfs_directory *dir, unsigned char *attrname); + +/* sysfs driver access */ +extern void sysfs_close_driver(struct sysfs_driver *driver); +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_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_attribute *sysfs_open_driver_attr(const unsigned char *bus, + const unsigned char *drv, const unsigned char *attrib); + +/* generic sysfs device access */ +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 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); +extern struct sysfs_attribute *sysfs_open_device_attr(const unsigned char *bus, + const unsigned char *bus_id, const unsigned char *attrib); + +/* generic sysfs bus access */ +extern void sysfs_close_bus(struct sysfs_bus *bus); +extern struct sysfs_bus *sysfs_open_bus(const unsigned char *name); +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_attributes(struct sysfs_bus *bus); +extern struct sysfs_attribute *sysfs_get_bus_attribute(struct sysfs_bus *bus, + unsigned char *attrname); +extern struct sysfs_device *sysfs_open_bus_device(unsigned char *busname, + unsigned char *dev_id); +extern int sysfs_find_driver_bus(const unsigned char *driver, + unsigned char *busname, size_t bsize); + +/* generic sysfs class access */ +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 void sysfs_close_class(struct sysfs_class *cls); +extern struct sysfs_class *sysfs_open_class(const unsigned char *name); +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 + (const unsigned char *class, const unsigned char *name); +extern struct dlist *sysfs_get_classdev_attributes + (struct sysfs_class_device *cdev); +extern struct sysfs_attribute *sysfs_get_classdev_attr + (struct sysfs_class_device *clsdev, const unsigned char *name); +extern struct sysfs_attribute *sysfs_open_classdev_attr + (const unsigned char *classname, const unsigned char *dev, + const unsigned char *attrib); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBSYSFS_H_ */ diff --git a/agents/linux26/sysfs/libsysfs/sysfs.h b/agents/linux26/sysfs/libsysfs/sysfs.h new file mode 100644 index 00000000..00599954 --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/sysfs.h @@ -0,0 +1,49 @@ +/* + * sysfs.h + * + * Internal Header Definitions for libsysfs + * + * Copyright (C) IBM Corp. 2003 + * + * 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 + * + */ +#ifndef _SYSFS_H_ +#define _SYSFS_H_ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <mntent.h> +#include <dirent.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +/* external library functions */ +extern int lstat(const char *file_name, struct stat *buf); +extern int readlink(const char *path, char *buf, size_t bufsize); +extern int getpagesize(void); +extern int isascii(int c); + +/* Debugging */ +#ifdef DEBUG +#define dprintf(format, arg...) fprintf(stderr, format, ## arg) +#else +#define dprintf(format, arg...) do { } while (0) +#endif + +#endif /* _SYSFS_H_ */ diff --git a/agents/linux26/sysfs/libsysfs/sysfs_bus.c b/agents/linux26/sysfs/libsysfs/sysfs_bus.c new file mode 100644 index 00000000..3111154c --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/sysfs_bus.c @@ -0,0 +1,471 @@ +/* + * sysfs_bus.c + * + * Generic bus utility functions for libsysfs + * + * Copyright (C) IBM Corp. 2003 + * + * 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 void sysfs_close_dev(void *dev) +{ + sysfs_close_device((struct sysfs_device *)dev); +} + +static void sysfs_close_drv(void *drv) +{ + sysfs_close_driver((struct sysfs_driver *)drv); +} + +/* + * compares devices' bus ids. + * @a: device id looking for + * @b: sysfs_device comparing being compared + * returns 1 if a==b->bus_id or 0 not equal + */ +static int bus_device_id_equal(void *a, void *b) +{ + if (a == NULL || b == NULL) + return 0; + + if (strcmp(((unsigned char *)a), ((struct sysfs_device *)b)->bus_id) + == 0) + return 1; + return 0; +} + +/* + * compares drivers' names. + * @a: driver name looking for + * @b: sysfs_driver comparing being compared + * returns 1 if a==b->name or 0 not equal + */ +static int bus_driver_name_equal(void *a, void *b) +{ + if (a == NULL || b == NULL) + return 0; + + if (strcmp(((unsigned char *)a), ((struct sysfs_driver *)b)->name) == 0) + return 1; + return 0; +} + +/** + * sysfs_close_bus: close single bus + * @bus: bus structure + */ +void sysfs_close_bus(struct sysfs_bus *bus) +{ + if (bus != NULL) { + if (bus->directory != NULL) + sysfs_close_directory(bus->directory); + if (bus->devices) + dlist_destroy(bus->devices); + if (bus->drivers) + dlist_destroy(bus->drivers); + free(bus); + } +} + +/** + * alloc_bus: mallocs new bus structure + * returns sysfs_bus_bus struct or NULL + */ +static struct sysfs_bus *alloc_bus(void) +{ + return (struct sysfs_bus *)calloc(1, sizeof(struct sysfs_bus)); +} + +/** + * open_bus_dir: opens up sysfs bus directory + * returns sysfs_directory struct with success and NULL with error + */ +static struct sysfs_directory *open_bus_dir(const unsigned char *name) +{ + struct sysfs_directory *busdir = NULL; + unsigned char buspath[SYSFS_PATH_MAX]; + + if (name == NULL) { + errno = EINVAL; + return NULL; + } + + memset(buspath, 0, SYSFS_PATH_MAX); + if ((sysfs_get_mnt_path(buspath, SYSFS_PATH_MAX)) != 0) { + dprintf("Sysfs not supported on this system\n"); + return NULL; + } + + strcat(buspath, SYSFS_BUS_DIR); + 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); + 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); + + return bus; +} + +/** + * sysfs_get_bus_device: Get specific device on bus using device's id + * @bus: bus to find device on + * @id: bus_id for device + * returns struct sysfs_device reference or NULL if not found. + */ +struct sysfs_device *sysfs_get_bus_device(struct sysfs_bus *bus, + unsigned char *id) +{ + if (bus == NULL || id == NULL) { + errno = EINVAL; + return NULL; + } + + return (struct sysfs_device *)dlist_find_custom(bus->devices, id, + bus_device_id_equal); +} + +/** + * sysfs_get_bus_driver: Get specific driver on bus using driver name + * @bus: bus to find driver on + * @drvname: name of driver + * returns struct sysfs_driver reference or NULL if not found. + */ +struct sysfs_driver *sysfs_get_bus_driver(struct sysfs_bus *bus, + unsigned char *drvname) +{ + if (bus == NULL || drvname == NULL) { + errno = EINVAL; + return NULL; + } + + return (struct sysfs_driver *)dlist_find_custom(bus->drivers, drvname, + bus_driver_name_equal); +} + +/** + * sysfs_get_bus_attributes: returns bus' dlist of attributes + * @bus: bus to get attributes for. + * returns dlist of attributes or NULL if there aren't any. + */ +struct dlist *sysfs_get_bus_attributes(struct sysfs_bus *bus) +{ + if (bus == NULL || bus->directory == NULL) + return NULL; + return bus->directory->attributes; +} + +/** + * sysfs_get_bus_attribute: gets a specific bus attribute, if buses had + * attributes. + * @bus: bus to retrieve attribute from + * @attrname: attribute name to retrieve + * returns reference to sysfs_attribute if found or NULL if not found + */ +struct sysfs_attribute *sysfs_get_bus_attribute(struct sysfs_bus *bus, + unsigned char *attrname) +{ + if (bus == NULL || bus->directory == NULL || attrname == NULL) { + errno = EINVAL; + return NULL; + } + return sysfs_get_directory_attribute(bus->directory, attrname); +} + +/** + * sysfs_open_bus_device: locates a device on a bus and returns it. Device + * must be closed using sysfs_close_device. + * @busname: Name of bus to search + * @dev_id: Id of device on bus. + * returns sysfs_device if found or NULL if not. + */ +struct sysfs_device *sysfs_open_bus_device(unsigned char *busname, + unsigned char *dev_id) +{ + struct sysfs_device *rdev = NULL; + char path[SYSFS_PATH_MAX]; + + if (busname == NULL || dev_id == NULL) { + errno = EINVAL; + return NULL; + } + + memset(path, 0, SYSFS_PATH_MAX); + if (sysfs_get_mnt_path(path, SYSFS_PATH_MAX) != 0) { + dprintf("Error getting sysfs mount point\n"); + return NULL; + } + + strcat(path, SYSFS_BUS_DIR); + strcat(path, "/"); + strcat(path, busname); + strcat(path, SYSFS_DEVICES_DIR); + strcat(path, "/"); + strcat(path, dev_id); + + rdev = sysfs_open_device(path); + if (rdev == NULL) { + dprintf("Error getting device %s on bus %s\n", + dev_id, busname); + return NULL; + } + + return rdev; +} + +/** + * sysfs_find_driver_bus: locates the bus the driver is on. + * @driver: name of the driver to locate + * @busname: buffer to copy name to + * @bsize: buffer size + * returns 0 with success, -1 with error + */ +int sysfs_find_driver_bus(const unsigned char *driver, unsigned char *busname, + size_t bsize) +{ + unsigned char subsys[SYSFS_PATH_MAX], *bus = NULL, *curdrv = NULL; + struct dlist *buslist = NULL, *drivers = NULL; + + if (driver == NULL || busname == NULL) { + errno = EINVAL; + return -1; + } + + memset(subsys, 0, SYSFS_PATH_MAX); + strcpy(subsys, SYSFS_BUS_DIR); + buslist = sysfs_open_subsystem_list(subsys); + if (buslist != NULL) { + dlist_for_each_data(buslist, bus, char) { + memset(subsys, 0, SYSFS_PATH_MAX); + strcpy(subsys, SYSFS_BUS_DIR); + strcat(subsys, "/"); + strcat(subsys, bus); + strcat(subsys, SYSFS_DRIVERS_DIR); + drivers = sysfs_open_subsystem_list(subsys); + if (drivers != NULL) { + dlist_for_each_data(drivers, curdrv, char) { + if (strcmp(driver, curdrv) == 0) { + strncpy(busname, bus, bsize); + sysfs_close_list(drivers); + sysfs_close_list(buslist); + return 0; + } + } + sysfs_close_list(drivers); + } + } + sysfs_close_list(buslist); + } + return -1; +} + diff --git a/agents/linux26/sysfs/libsysfs/sysfs_class.c b/agents/linux26/sysfs/libsysfs/sysfs_class.c new file mode 100644 index 00000000..d7521df7 --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/sysfs_class.c @@ -0,0 +1,498 @@ +/* + * sysfs_class.c + * + * Generic class utility functions for libsysfs + * + * Copyright (C) IBM Corp. 2003 + * + * 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" + +void sysfs_close_cls_dev(void *dev) +{ + sysfs_close_class_device((struct sysfs_class_device *)dev); +} + +/** + * class_name_equal: compares class_devices' name + * @a: class_name looking for + * @b: sysfs_class_device being compared + */ +static int class_name_equal(void *a, void *b) +{ + if (a == NULL || b == NULL) + return 0; + + if (strcmp(((unsigned char *)a), ((struct sysfs_class_device *)b)->name) + == 0) + return 1; + + return 0; +} + +/** + * sysfs_close_class_device: closes a single class device. + * @dev: class device to close. + */ +void sysfs_close_class_device(struct sysfs_class_device *dev) +{ + if (dev != NULL) { + if (dev->directory != NULL) + sysfs_close_directory(dev->directory); + if (dev->sysdevice != NULL) + sysfs_close_device(dev->sysdevice); + if (dev->driver != NULL) + sysfs_close_driver(dev->driver); + free(dev); + } +} + +/** + * sysfs_close_class: close single class + * @class: class structure + */ +void sysfs_close_class(struct sysfs_class *cls) +{ + if (cls != NULL) { + if (cls->directory != NULL) + sysfs_close_directory(cls->directory); + if (cls->devices != NULL) + dlist_destroy(cls->devices); + free(cls); + } +} + +/** + * alloc_class_device: mallocs and initializes new class device struct. + * returns sysfs_class_device or NULL. + */ +static struct sysfs_class_device *alloc_class_device(void) +{ + return (struct sysfs_class_device *) + calloc(1, sizeof(struct sysfs_class_device)); +} + +/** + * alloc_class: mallocs new class structure + * returns sysfs_class struct or NULL + */ +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; + } + + /* + * 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_DIR); + } else { + strcat(classpath, SYSFS_CLASS_DIR); + 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 + * Returns nothing + */ +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++; + } + + if (c == NULL) + strcpy(cdev->classname, SYSFS_UNKNOWN); + + else { + c++; + e = c; + while (e != NULL && *e != '/') { + e++; + count++; + } + strncpy(cdev->classname, c, count); + } +} + +/** + * sysfs_open_class_device: 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 *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; + } + cdev = alloc_class_device(); + if (cdev == NULL) { + dprintf("calloc failed\n"); + return NULL; + } + if ((sysfs_get_name_from_path(path, cdev->name, SYSFS_NAME_LEN)) != 0) { + errno = EINVAL; + dprintf("Invalid class device path %s\n", path); + 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); + 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 + * @class: class to get devices for + * returns 0 with success and -1 with failure + */ +static int get_all_class_devices(struct sysfs_class *cls) +{ + struct sysfs_class_device *dev = NULL; + struct sysfs_directory *cur = NULL; + + if (cls == NULL || cls->directory == NULL) { + errno = EINVAL; + return -1; + } + if (cls->directory->subdirs == NULL) + return 0; + 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 + (sizeof(struct sysfs_class_device), + sysfs_close_cls_dev); + dlist_unshift(cls->devices, dev); + } + return 0; +} + +/** + * sysfs_open_class: opens specific class and all its devices on system + * returns sysfs_class structure with success or NULL with error. + */ +struct sysfs_class *sysfs_open_class(const unsigned char *name) +{ + struct sysfs_class *cls = NULL; + struct sysfs_directory *classdir = NULL; + + if (name == NULL) { + errno = EINVAL; + 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; + } + + return cls; +} + +/** + * sysfs_get_class_device: Get specific class device using the device's id + * @class: class to find device on + * @name: class name of the device + */ +struct sysfs_class_device *sysfs_get_class_device(struct sysfs_class *class, + unsigned char *name) +{ + if (class == NULL || name == NULL) { + errno = EINVAL; + return NULL; + } + + return (struct sysfs_class_device *)dlist_find_custom(class->devices, + name, class_name_equal); +} + +/** + * get_classdev_path: given the class and a device in the class, return the + * absolute path to the device + * @classname: name of the class + * @clsdev: the class device + * @path: buffer to return path + * @psize: size of "path" + * Returns 0 on SUCCESS or -1 on error + */ +static int get_classdev_path(const unsigned char *classname, + const unsigned char *clsdev, unsigned char *path, size_t len) +{ + if (classname == NULL || clsdev == NULL || path == NULL) { + errno = EINVAL; + return -1; + } + if (sysfs_get_mnt_path(path, len) != 0) { + dprintf("Error getting sysfs mount path\n"); + return -1; + } + if (strcmp(classname, SYSFS_BLOCK_NAME) == 0) { + strcat(path, SYSFS_BLOCK_DIR); + } else { + strcat(path, SYSFS_CLASS_DIR); + strcat(path, "/"); + strcat(path, classname); + } + strcat(path, "/"); + strcat(path, clsdev); + return 0; +} + +/** + * sysfs_open_class_device_by_name: 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 + * + * NOTE: + * Call sysfs_close_class_device() to close the class device + */ +struct sysfs_class_device *sysfs_open_class_device_by_name + (const unsigned char *classname, const unsigned char *name) +{ + unsigned char devpath[SYSFS_PATH_MAX]; + struct sysfs_class_device *cdev = NULL; + + if (classname == NULL || name == NULL) { + errno = EINVAL; + return NULL; + } + + memset(devpath, 0, SYSFS_PATH_MAX); + if ((get_classdev_path(classname, name, devpath, + SYSFS_PATH_MAX)) != 0) { + dprintf("Error getting to device %s on class %s\n", + name, classname); + return NULL; + } + + cdev = sysfs_open_class_device(devpath); + if (cdev == NULL) { + dprintf("Error getting class device %s from class %s\n", + name, classname); + return NULL; + } + return cdev; +} + +/** + * sysfs_get_classdev_attributes: returns a dlist of attributes for + * the requested class_device + * @cdev: sysfs_class_dev for which attributes are needed + * returns a dlist of attributes if exists, NULL otherwise + */ +struct dlist *sysfs_get_classdev_attributes(struct sysfs_class_device *cdev) +{ + if (cdev == NULL || cdev->directory == NULL) + return NULL; + + return (cdev->directory->attributes); +} + +/** + * sysfs_get_classdev_attr: searches class device's attributes by name + * @clsdev: class device to look through + * @name: attribute name to get + * returns sysfs_attribute reference with success or NULL with error + */ +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) { + errno = EINVAL; + return NULL; + } + + cur = sysfs_get_directory_attribute(clsdev->directory, + (unsigned char *)name); + if (cur != NULL) + return cur; + + return NULL; +} + +/** + * sysfs_open_classdev_attr: read an attribute for a given class device + * @classname: name of the class on which to look + * @dev: class device name for which the attribute has to be read + * @attrib: attribute to read + * Returns sysfs_attribute * on SUCCESS and NULL on error + * + * NOTE: + * A call to sysfs_close_attribute() is required to close the + * attribute returned and to free memory + */ +struct sysfs_attribute *sysfs_open_classdev_attr(const unsigned char *classname, + const unsigned char *dev, const unsigned char *attrib) +{ + struct sysfs_attribute *attribute = NULL; + unsigned char path[SYSFS_PATH_MAX]; + + if (classname == NULL || dev == NULL || attrib == NULL) { + errno = EINVAL; + return NULL; + } + memset(path, 0, SYSFS_PATH_MAX); + if ((get_classdev_path(classname, dev, path, SYSFS_PATH_MAX)) != 0) { + dprintf("Error getting to device %s on class %s\n", + dev, classname); + return NULL; + } + strcat(path, "/"); + strcat(path, attrib); + attribute = sysfs_open_attribute(path); + if (attribute == NULL) { + dprintf("Error opening attribute %s on class device %s\n", + attrib, dev); + return NULL; + } + if ((sysfs_read_attribute(attribute)) != 0) { + dprintf("Error reading attribute %s for class device %s\n", + attrib, dev); + sysfs_close_attribute(attribute); + return NULL; + } + return attribute; +} diff --git a/agents/linux26/sysfs/libsysfs/sysfs_device.c b/agents/linux26/sysfs/libsysfs/sysfs_device.c new file mode 100644 index 00000000..fbd046f2 --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/sysfs_device.c @@ -0,0 +1,537 @@ +/* + * sysfs_device.c + * + * Generic device utility functions for libsysfs + * + * Copyright (C) IBM Corp. 2003 + * + * 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 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; + + strcat(devpath, SYSFS_BUS_DIR); + 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. + * @dev: device to get busname. + * returns 0 with success and -1 with error. + */ +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; + + if (dev == NULL) { + errno = EINVAL; + return -1; + } + + strcpy(subsys, SYSFS_BUS_DIR); /* 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); + } + } + sysfs_close_list(buslist); + } + return -1; +} + +/** + * sysfs_close_device_tree: closes every device in the supplied tree, + * closing children only. + * @devroot: device root of tree. + */ +static void sysfs_close_device_tree(struct sysfs_device *devroot) +{ + if (devroot != NULL) { + if (devroot->children != NULL) { + struct sysfs_device *child = NULL; + + dlist_for_each_data(devroot->children, child, + struct sysfs_device) { + sysfs_close_device_tree(child); + } + } + sysfs_close_device(devroot); + } +} + +/** + * sysfs_del_device: routine for dlist integration + */ +static void sysfs_del_device(void *dev) +{ + sysfs_close_device((struct sysfs_device *)dev); +} + +/** + * sysfs_close_dev_tree: routine for dlist integration + */ +static void sysfs_close_dev_tree(void *dev) +{ + sysfs_close_device_tree((struct sysfs_device *)dev); +} + +/** + * sysfs_close_device: closes and cleans up a device + * @dev = device to clean up + */ +void sysfs_close_device(struct sysfs_device *dev) +{ + if (dev != NULL) { + if (dev->directory != NULL) + sysfs_close_directory(dev->directory); + if (dev->children != NULL && dev->children->count == 0) + dlist_destroy(dev->children); + free(dev); + } +} + +/** + * alloc_device: allocates and initializes device structure + * returns struct sysfs_device + */ +static struct sysfs_device *alloc_device(void) +{ + return (struct sysfs_device *)calloc(1, sizeof(struct sysfs_device)); +} + +/** + * 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; + + if (dev == NULL || dev->directory == NULL + || dev->directory->attributes == NULL || name == NULL) { + errno = EINVAL; + return NULL; + } + + cur = sysfs_get_directory_attribute(dev->directory, + (unsigned char *)name); + if (cur != NULL) + return cur; + + return NULL; +} + +/** + * sysfs_open_device: opens and populates device structure + * @path: path to device, this is the /sys/devices/ path + * returns sysfs_device structure with success or NULL with error + */ +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; + } + 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); + errno = EINVAL; + 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); + + /* + * 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); + + if (get_device_bus(dev) != 0) + strcpy(dev->bus, SYSFS_UNKNOWN); + + return dev; +} + +/** + * sysfs_open_device_tree: opens root device and all of its children, + * creating a tree of devices. Only opens children. + * @path: sysfs path to devices + * returns struct sysfs_device and its children with success or NULL with + * error. + */ +static struct sysfs_device *sysfs_open_device_tree(const unsigned char *path) +{ + struct sysfs_device *rootdev = NULL, *new = NULL; + struct sysfs_directory *cur = NULL; + + if (path == NULL) { + errno = EINVAL; + return NULL; + } + rootdev = sysfs_open_device(path); + if (rootdev == NULL) { + dprintf("Error opening root device at %s\n", path); + return NULL; + } + if (rootdev->directory->subdirs != NULL) { + dlist_for_each_data(rootdev->directory->subdirs, cur, + struct sysfs_directory) { + new = sysfs_open_device_tree(cur->path); + if (new == NULL) { + dprintf("Error opening device tree at %s\n", + cur->path); + sysfs_close_device_tree(rootdev); + return NULL; + } + if (rootdev->children == NULL) + rootdev->children = dlist_new_with_delete + (sizeof(struct sysfs_device), + sysfs_del_device); + dlist_unshift(rootdev->children, new); + } + } + + return rootdev; +} + +/** + * sysfs_close_root_device: closes root and all devices + * @root: root device to close + */ +void sysfs_close_root_device(struct sysfs_root_device *root) +{ + if (root != NULL) { + if (root->devices != NULL) + dlist_destroy(root->devices); + if (root->directory != NULL) + sysfs_close_directory(root->directory); + free(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; + } + + strcat(rootpath, SYSFS_DEVICES_DIR); + 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 + * @root: root device to open devices for + * returns 0 with success and -1 with error + */ +static int get_all_root_devices(struct sysfs_root_device *root) +{ + struct sysfs_device *dev = NULL; + struct sysfs_directory *cur = NULL; + + if (root == NULL || root->directory == NULL) { + errno = EINVAL; + return -1; + } + if (root->directory->subdirs == NULL) + return 0; + + dlist_for_each_data(root->directory->subdirs, cur, + struct sysfs_directory) { + dev = sysfs_open_device_tree(cur->path); + if (dev == NULL) { + dprintf ("Error opening device at %s\n", cur->path); + continue; + } + if (root->devices == NULL) + root->devices = dlist_new_with_delete + (sizeof(struct sysfs_device), + sysfs_close_dev_tree); + dlist_unshift(root->devices, dev); + } + + return 0; +} + +/** + * sysfs_open_root_device: opens sysfs devices root and all of its + * devices. + * @name: name of /sys/devices/root to open + * returns struct sysfs_root_device if success and NULL with error + */ +struct sysfs_root_device *sysfs_open_root_device(const unsigned char *name) +{ + struct sysfs_root_device *root = NULL; + struct sysfs_directory *rootdir = NULL; + + if (name == NULL) { + errno = EINVAL; + 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; + } + + return root; +} + +/** + * sysfs_get_device_attributes: returns a dlist of attributes corresponding to + * the specific device + * @device: struct sysfs_device * for which attributes are to be returned + */ +struct dlist *sysfs_get_device_attributes(struct sysfs_device *device) +{ + if (device == NULL || device->directory == NULL) + return NULL; + + return (device->directory->attributes); +} + +/** + * get_device_absolute_path: looks up the bus the device is on, gets + * absolute path to the device + * @device: device for which path is needed + * @path: buffer to store absolute path + * @psize: size of "path" + * Returns 0 on success -1 on failure + */ +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]; + + if (device == NULL || path == NULL) { + errno = EINVAL; + return -1; + } + + memset(bus_path, 0, SYSFS_NAME_LEN); + if (sysfs_get_mnt_path(bus_path, SYSFS_PATH_MAX) != 0) { + dprintf ("Sysfs not supported on this system\n"); + return -1; + } + strcat(bus_path, SYSFS_BUS_DIR); + strcat(bus_path, "/"); + strcat(bus_path, bus); + strcat(bus_path, SYSFS_DEVICES_DIR); + strcat(bus_path, "/"); + strcat(bus_path, device); + /* + * We now are at /sys/bus/"bus_name"/devices/"device" which is a link. + * Now read this link to reach to the device. + */ + if ((sysfs_get_link(bus_path, path, SYSFS_PATH_MAX)) != 0) { + dprintf("Error getting to device %s\n", device); + return -1; + } + return 0; +} + +/** + * sysfs_open_device_by_id: open a device by id (use the "bus" subsystem) + * @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 + * 2. Bus the device is on must be supplied + * 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) +{ + char sysfs_path[SYSFS_PATH_MAX]; + struct sysfs_device *device = NULL; + + if (bus_id == NULL || bus == NULL) { + errno = EINVAL; + return NULL; + } + memset(sysfs_path, 0, SYSFS_PATH_MAX); + if ((get_device_absolute_path(bus_id, bus, sysfs_path, + SYSFS_PATH_MAX)) != 0) { + dprintf("Error getting to device %s\n", bus_id); + return NULL; + } + + device = sysfs_open_device(sysfs_path); + if (device == NULL) { + dprintf("Error opening device %s\n", bus_id); + return NULL; + } + + return device; +} + +/* + * sysfs_open_device_attr: open the given device's attribute + * @bus: Bus on which to look + * @dev_id: device for which attribute is required + * @attrname: name of the attribute to look for + * Returns struct sysfs_attribute on success and NULL on failure + * + * NOTE: + * A call to sysfs_close_attribute() is required to close + * the attribute returned and free memory. + */ +struct sysfs_attribute *sysfs_open_device_attr(const unsigned char *bus, + const unsigned char *bus_id, const unsigned char *attrib) +{ + struct sysfs_attribute *attribute = NULL; + unsigned char devpath[SYSFS_PATH_MAX]; + + if (bus == NULL || bus_id == NULL || attrib == NULL) { + errno = EINVAL; + return NULL; + } + + memset(devpath, 0, SYSFS_PATH_MAX); + if ((get_device_absolute_path(bus_id, bus, devpath, + SYSFS_PATH_MAX)) != 0) { + dprintf("Error getting to device %s\n", bus_id); + return NULL; + } + strcat(devpath, "/"); + strcat(devpath, attrib); + attribute = sysfs_open_attribute(devpath); + if (attribute == NULL) { + dprintf("Error opening attribute %s for device %s\n", + attrib, bus_id); + return NULL; + } + if ((sysfs_read_attribute(attribute)) != 0) { + dprintf("Error reading attribute %s for device %s\n", + attrib, bus_id); + sysfs_close_attribute(attribute); + return NULL; + } + return attribute; +} + diff --git a/agents/linux26/sysfs/libsysfs/sysfs_dir.c b/agents/linux26/sysfs/libsysfs/sysfs_dir.c new file mode 100644 index 00000000..ff2edf46 --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/sysfs_dir.c @@ -0,0 +1,704 @@ +/* + * syfs_dir.c + * + * Directory utility functions for libsysfs + * + * Copyright (C) IBM Corp. 2003 + * + * 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" + +/** + * sysfs_del_attribute: routine for dlist integration + */ +static void sysfs_del_attribute(void *attr) +{ + sysfs_close_attribute((struct sysfs_attribute *)attr); +} + +/** + * sysfs_del_link: routine for dlist integration + */ +static void sysfs_del_link(void *ln) +{ + sysfs_close_link((struct sysfs_link *)ln); +} + +/** + * sysfs_del_dir: routine for dlist integration + */ +static void sysfs_del_directory(void *dir) +{ + sysfs_close_directory((struct sysfs_directory *)dir); +} + +/** + * dir_attribute_name_equal: compares dir 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 dir_attribute_name_equal(void *a, void *b) +{ + if (a == NULL || b == NULL) + return 0; + + if (strcmp(((unsigned char *)a), ((struct sysfs_attribute *)b)->name) + == 0) + return 1; + return 0; +} + +/** + * dir_link_name_equal: compares dir links by name + * @a: link name for comparison + * @b: sysfs_link to be compared. + * returns 1 if a==b->name or 0 if not equal + */ +static int dir_link_name_equal(void *a, void *b) +{ + if (a == NULL || b == NULL) + return 0; + + if (strcmp(((unsigned char *)a), ((struct sysfs_link *)b)->name) + == 0) + return 1; + return 0; +} + +/** + * dir_subdir_name_equal: compares subdirs by name + * @a: name of subdirectory to compare + * @b: sysfs_directory subdirectory to be compared + * returns 1 if a==b->name or 0 if not equal + */ +static int dir_subdir_name_equal(void *a, void *b) +{ + if (a == NULL || b == NULL) + return 0; + + if (strcmp(((unsigned char *)a), ((struct sysfs_directory *)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 != NULL) { + if (sysattr->value != NULL) + 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 unsigned char *path) +{ + struct sysfs_attribute *sysattr = NULL; + struct stat fileinfo; + + if (path == NULL) { + errno = EINVAL; + return NULL; + } + sysattr = alloc_attribute(); + if (sysattr == NULL) { + 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 attribute name from path: %s\n", + path); + sysfs_close_attribute(sysattr); + return NULL; + } + strncpy(sysattr->path, path, sizeof(sysattr->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_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 unsigned char *new_value, size_t len) +{ + int fd; + int length; + + if (sysattr == NULL || new_value == NULL || len == 0) { + errno = EINVAL; + return -1; + } + + if (!(sysattr->method & SYSFS_METHOD_STORE)) { + dprintf ("Store method not supported for attribute %s\n", + sysattr->path); + return -1; + } + if (sysattr->method & SYSFS_METHOD_SHOW) { + if ((strncmp(sysattr->value, new_value, sysattr->len)) == 0) { + dprintf("Attribute %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 (length != len) { + dprintf("Could not write %d 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; + strncpy(sysattr->value, new_value, length); + } else { + /*"length" of the new value is same as old one */ + strncpy(sysattr->value, new_value, length); + } + } + + close(fd); + return 0; +} + + +/** + * 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) +{ + unsigned char *fbuf = NULL; + unsigned char *vbuf = NULL; + size_t length = 0; + int pgsize = 0; + int fd; + + if (sysattr == NULL) { + errno = EINVAL; + return -1; + } + if (!(sysattr->method & SYSFS_METHOD_SHOW)) { + dprintf("Show method not supported for attribute %s\n", + sysattr->path); + return -1; + } + pgsize = getpagesize(); + fbuf = (unsigned char *)calloc(1, pgsize+1); + if (fbuf == NULL) { + 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; + } + sysattr->len = length; + close(fd); + vbuf = (unsigned char *)realloc(fbuf, length+1); + if (vbuf == NULL) { + dprintf("realloc failed\n"); + free(fbuf); + return -1; + } + sysattr->value = vbuf; + + return 0; +} + +/** + * sysfs_read_attribute_value: given path to attribute, return its value. + * values can be up to a pagesize, if buffer is smaller the value will + * be truncated. + * @attrpath: sysfs path to attribute + * @value: buffer to put value + * @vsize: size of value buffer + * returns 0 with success and -1 with error. + */ +int sysfs_read_attribute_value(const unsigned char *attrpath, + unsigned char *value, size_t vsize) +{ + struct sysfs_attribute *attr = NULL; + size_t length = 0; + + if (attrpath == NULL || value == NULL) { + errno = EINVAL; + return -1; + } + + attr = sysfs_open_attribute(attrpath); + if (attr == NULL) { + dprintf("Invalid attribute path %s\n", attrpath); + errno = EINVAL; + return -1; + } + if((sysfs_read_attribute(attr)) != 0 || attr->value == NULL) { + dprintf("Error reading from attribute %s\n", attrpath); + sysfs_close_attribute(attr); + return -1; + } + length = strlen(attr->value); + if (length > vsize) + dprintf("Value length %d is larger than supplied buffer %d\n", + length, vsize); + strncpy(value, attr->value, vsize); + sysfs_close_attribute(attr); + + return 0; +} + +/** + * sysfs_get_value_from_attrbutes: given a linked list of attributes and an + * attribute name, return its value + * @attr: attribute to search + * @name: name to look for + * returns unsigned char * value - could be NULL + */ +unsigned char *sysfs_get_value_from_attributes(struct dlist *attr, + const unsigned char *name) +{ + struct sysfs_attribute *cur = NULL; + + if (attr == NULL || name == NULL) { + errno = EINVAL; + return NULL; + } + dlist_for_each_data(attr, cur, struct sysfs_attribute) { + if (strcmp(cur->name, name) == 0) + return cur->value; + } + return NULL; +} + +/** + * sysfs_close_link: closes and cleans up link. + * @ln: link to close. + */ +void sysfs_close_link(struct sysfs_link *ln) +{ + if (ln != NULL) + free(ln); +} + +/** + * sysfs_close_directory: closes directory, cleans up attributes and links + * @sysdir: sysfs_directory to close + */ +void sysfs_close_directory(struct sysfs_directory *sysdir) +{ + if (sysdir != NULL) { + if (sysdir->subdirs != NULL) + dlist_destroy(sysdir->subdirs); + if (sysdir->links != NULL) + dlist_destroy(sysdir->links); + if (sysdir->attributes != NULL) + dlist_destroy(sysdir->attributes); + free(sysdir); + } +} + +/** + * alloc_directory: allocates and initializes directory structure + * returns struct sysfs_directory with success or NULL with error. + */ +static struct sysfs_directory *alloc_directory(void) +{ + return (struct sysfs_directory *) + calloc(1, sizeof(struct sysfs_directory)); +} + +/** + * alloc_link: allocates and initializes link structure + * returns struct sysfs_link with success or NULL with error. + */ +static struct sysfs_link *alloc_link(void) +{ + return (struct sysfs_link *)calloc(1, sizeof(struct sysfs_link)); +} + +/** + * sysfs_read_all_subdirs: calls sysfs_read_directory for all subdirs + * @sysdir: directory whose subdirs need reading. + * returns 0 with success and -1 with error. + */ +int sysfs_read_all_subdirs(struct sysfs_directory *sysdir) +{ + struct sysfs_directory *cursub = NULL; + + if (sysdir == NULL) { + errno = EINVAL; + return -1; + } + if (sysdir->subdirs == NULL) + return 0; + dlist_for_each_data(sysdir->subdirs, cursub, struct sysfs_directory) { + if (sysfs_read_directory(cursub) != 0) + dprintf ("Error reading subdirectory %s\n", + cursub->name); + } + return 0; +} + +/** + * sysfs_open_directory: opens a sysfs directory, creates dir struct, and + * returns. + * @path: path of directory to open. + * returns: struct sysfs_directory * with success and NULL on error. + */ +struct sysfs_directory *sysfs_open_directory(const unsigned char *path) +{ + struct sysfs_directory *sdir = NULL; + + if (path == NULL) { + errno = EINVAL; + return NULL; + } + sdir = alloc_directory(); + if (sdir == NULL) { + dprintf("Error allocating directory %s\n", path); + return NULL; + } + if (sysfs_get_name_from_path(path, sdir->name, SYSFS_NAME_LEN) != 0) { + dprintf("Error getting directory name from path: %s\n", path); + sysfs_close_directory(sdir); + return NULL; + } + strncpy(sdir->path, path, sizeof(sdir->path)); + + return sdir; +} + +/** + * sysfs_open_link: opens a sysfs link, creates struct, and returns + * @path: path of link to open. + * returns: struct sysfs_link * with success and NULL on error. + */ +struct sysfs_link *sysfs_open_link(const unsigned char *linkpath) +{ + struct sysfs_link *ln = NULL; + + if (linkpath == NULL || strlen(linkpath) > SYSFS_PATH_MAX) { + errno = EINVAL; + return NULL; + } + + ln = alloc_link(); + if (ln == NULL) { + dprintf("Error allocating link %s\n", linkpath); + return NULL; + } + strcpy(ln->path, linkpath); + if ((sysfs_get_name_from_path(linkpath, ln->name, SYSFS_NAME_LEN)) != 0 + || (sysfs_get_link(linkpath, ln->target, SYSFS_PATH_MAX)) != 0) { + errno = EINVAL; + dprintf("Invalid link path %s\n", linkpath); + return NULL; + } + + return ln; +} + +/** + * 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; + 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; + + 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_ISREG(astats.st_mode)) { + attr = sysfs_open_attribute(file_path); + if (attr == NULL) { + dprintf("Error opening attribute %s\n", + file_path); + retval = -1; + 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); + return(retval); +} + +/** + * sysfs_get_directory_attribute: retrieves attribute attrname + * @dir: directory to retrieve attribute from + * @attrname: name of attribute to look for + * returns sysfs_attribute if found and NULL if not found + */ +struct sysfs_attribute *sysfs_get_directory_attribute + (struct sysfs_directory *dir, unsigned char *attrname) +{ + struct sysfs_directory *sdir = NULL; + struct sysfs_attribute *attr = NULL; + + 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) + 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; + } + } + return NULL; +} + +/** + * sysfs_get_directory_link: retrieves link from one directory list + * @dir: directory to retrieve link from + * @linkname: name of link to look for + * returns reference to sysfs_link if found and NULL if not found + */ +struct sysfs_link *sysfs_get_directory_link + (struct sysfs_directory *dir, unsigned char *linkname) +{ + if (dir == NULL || linkname == NULL) { + errno = EINVAL; + return NULL; + } + return (struct sysfs_link *)dlist_find_custom(dir->links, + linkname, dir_link_name_equal); +} + +/** + * sysfs_get_subdirectory: retrieves subdirectory by name. + * @dir: directory to search for subdirectory. + * @subname: subdirectory name to get. + * returns reference to subdirectory or NULL if not found + */ +struct sysfs_directory *sysfs_get_subdirectory(struct sysfs_directory *dir, + unsigned char *subname) +{ + struct sysfs_directory *sub = NULL, *cursub = NULL; + + if (dir == NULL || dir->subdirs == NULL || subname == NULL) { + errno = EINVAL; + return NULL; + } + sub = (struct sysfs_directory *)dlist_find_custom(dir->subdirs, + subname, dir_subdir_name_equal); + if (sub != NULL) + return sub; + + if (dir->subdirs != NULL) { + dlist_for_each_data(dir->subdirs, cursub, + struct sysfs_directory) { + if (cursub->subdirs == NULL) + continue; + sub = sysfs_get_subdirectory(cursub, subname); + if (sub != NULL) + return sub; + } + } + return NULL; +} + +/** + * sysfs_get_subdirectory_link: looks through all subdirs for specific link. + * @dir: directory and subdirectories to search for link. + * @linkname: link name to get. + * returns reference to link or NULL if not found + */ +struct sysfs_link *sysfs_get_subdirectory_link(struct sysfs_directory *dir, + unsigned char *linkname) +{ + struct sysfs_directory *cursub = NULL; + struct sysfs_link *ln = NULL; + + if (dir == NULL || dir->links == NULL || linkname == NULL) { + errno = EINVAL; + return NULL; + } + + ln = sysfs_get_directory_link(dir, linkname); + if (ln != NULL) + return ln; + + if (dir->subdirs == NULL) + 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; + } + } + return NULL; +} diff --git a/agents/linux26/sysfs/libsysfs/sysfs_driver.c b/agents/linux26/sysfs/libsysfs/sysfs_driver.c new file mode 100644 index 00000000..0011177e --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/sysfs_driver.c @@ -0,0 +1,294 @@ +/* + * sysfs_driver.c + * + * Driver utility functions for libsysfs + * + * Copyright (C) IBM Corp. 2003 + * + * 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 void sysfs_close_driver_by_name_dev(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 + * @driver: driver to close + */ +void sysfs_close_driver_by_name(struct sysfs_driver *driver) +{ + if (driver != NULL) { + if (driver->devices != NULL) + dlist_destroy(driver->devices); + if (driver->directory != NULL) + sysfs_close_directory(driver->directory); + free(driver); + } +} + +/** + * alloc_driver: allocates and initializes driver + * returns struct sysfs_driver with success and NULL with error. + */ +static struct sysfs_driver *alloc_driver(void) +{ + return (struct sysfs_driver *)calloc(1, sizeof(struct sysfs_driver)); +} + +/** + * sysfs_open_driver: opens and initializes driver structure + * @path: path to driver directory + * returns struct sysfs_driver with success and NULL with error + */ +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); + 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); + + return driver; +} + +/** + * sysfs_get_driver_attributes: gets list of attributes for the given driver + * @driver: sysfs_driver for which attributes are required + * returns a dlist of attributes corresponding to the driver if present + * NULL otherwise + */ +struct dlist *sysfs_get_driver_attributes(struct sysfs_driver *driver) +{ + if (driver == NULL || driver->directory == NULL) + return NULL; + + return(driver->directory->attributes); +} + +/** + * sysfs_get_driver_attr: searches driver's attributes by name + * @drv: driver to look through + * @name: attribute name to get + * returns sysfs_attribute reference on success or NULL with error + */ +struct sysfs_attribute *sysfs_get_driver_attr(struct sysfs_driver *drv, + const unsigned char *name) +{ + struct sysfs_attribute *cur = NULL; + + if (drv == NULL || drv->directory == NULL + || drv->directory->attributes == NULL || name == NULL) { + errno = EINVAL; + return NULL; + } + + cur = sysfs_get_directory_attribute(drv->directory, + (unsigned char *)name); + if (cur != NULL) + return cur; + + return NULL; +} + +/** + * sysfs_get_driver_links: gets list of links from the given driver + * @driver: sysfs_driver for which links list is required + * returns a dlist of links corresponding to the driver if present + * NULL otherwise + */ +struct dlist *sysfs_get_driver_links(struct sysfs_driver *driver) +{ + if (driver == NULL || driver->directory == NULL) + return NULL; + + return(driver->directory->links); +} + +/** + * get_driver_path: looks up the bus the driver is on and builds path to + * the driver. + * @bus: bus on which to search + * @drv: driver to look for + * @path: buffer to return path to 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) +{ + if (bus == NULL || drv == NULL || path == NULL) { + errno = EINVAL; + return -1; + } + if (sysfs_get_mnt_path(path, psize) != 0) { + dprintf("Error getting sysfs mount path\n"); + return -1; + } + strcat(path, SYSFS_BUS_DIR); + strcat(path, "/"); + strcat(path, bus); + strcat(path, SYSFS_DRIVERS_DIR); + 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 + * @drv: driver whose attribute has to be read + * @attrib: Attribute to be read + * Returns struct sysfs_attribute on success and NULL on failure + * + * NOTE: + * A call to sysfs_close_attribute() is required to close the + * attribute returned and to free memory + */ +struct sysfs_attribute *sysfs_open_driver_attr(const unsigned char *bus, + const unsigned char *drv, const unsigned char *attrib) +{ + struct sysfs_attribute *attribute = NULL; + unsigned char path[SYSFS_PATH_MAX]; + + if (bus == NULL || drv == NULL || attrib == NULL) { + errno = EINVAL; + return NULL; + } + + memset(path, 0, SYSFS_NAME_LEN); + if ((get_driver_path(bus, drv, path, SYSFS_PATH_MAX)) != 0) { + dprintf("Error getting to driver %s\n", drv); + return NULL; + } + strcat(path, "/"); + strcat(path, attrib); + attribute = sysfs_open_attribute(path); + if (attribute == NULL) { + dprintf("Error opening attribute %s for driver %s\n", + attrib, drv); + return NULL; + } + if ((sysfs_read_attribute(attribute)) != 0) { + dprintf("Error reading attribute %s for driver %s\n", + attrib, drv); + sysfs_close_attribute(attribute); + return NULL; + } + return attribute; +} + diff --git a/agents/linux26/sysfs/libsysfs/sysfs_utils.c b/agents/linux26/sysfs/libsysfs/sysfs_utils.c new file mode 100644 index 00000000..7563dff7 --- /dev/null +++ b/agents/linux26/sysfs/libsysfs/sysfs_utils.c @@ -0,0 +1,331 @@ +/* + * syfs_utils.c + * + * System utility functions for libsysfs + * + * Copyright (C) IBM Corp. 2003 + * + * 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" + +/** + * sysfs_get_mnt_path: Gets the mount point for specified filesystem. + * @fs_type: filesystem type to retrieve mount point + * @mnt_path: place to put the retrieved mount path + * @len: size of mnt_path + * returns 0 with success and -1 with error. + */ +static int sysfs_get_fs_mnt_path(const unsigned char *fs_type, + unsigned char *mnt_path, size_t len) +{ + FILE *mnt; + struct mntent *mntent; + int ret = 0; + size_t dirlen = 0; + + /* check arg */ + if (fs_type == NULL || mnt_path == NULL) { + errno = EINVAL; + return -1; + } + + if ((mnt = setmntent(SYSFS_PROC_MNTS, "r")) == NULL) { + dprintf("Error getting mount information\n"); + return -1; + } + while (ret == 0 && dirlen == 0 && (mntent = getmntent(mnt)) != NULL) { + if (strcmp(mntent->mnt_type, fs_type) == 0) { + dirlen = strlen(mntent->mnt_dir); + if (dirlen <= (len - 1)) { + strcpy(mnt_path, mntent->mnt_dir); + } else { + dprintf("Error - mount path too long\n"); + ret = -1; + } + } + } + endmntent(mnt); + if (dirlen == 0 && ret == 0) { + dprintf("Filesystem %s not found!\n", fs_type); + errno = EINVAL; + ret = -1; + } + return ret; +} + +/* + * sysfs_get_mnt_path: Gets the sysfs mount point. + * @mnt_path: place to put "sysfs" mount point + * @len: size of mnt_path + * returns 0 with success and -1 with error. + */ +int sysfs_get_mnt_path(unsigned char *mnt_path, size_t len) +{ + char *sysfs_path = NULL; + int ret = 0; + + if (mnt_path == NULL) { + errno = EINVAL; + return -1; + } + sysfs_path = getenv(SYSFS_PATH_ENV); + if (sysfs_path != NULL) + strncpy(mnt_path, sysfs_path, len); + else + ret = sysfs_get_fs_mnt_path(SYSFS_FSTYPE_NAME, mnt_path, len); + + return ret; +} + +/** + * sysfs_get_name_from_path: returns last name from a "/" delimited path + * @path: path to get name from + * @name: where to put name + * @len: size of name + */ +int sysfs_get_name_from_path(const unsigned char *path, unsigned char *name, + size_t len) +{ + unsigned char tmp[SYSFS_PATH_MAX]; + unsigned char *n = NULL; + + if (path == NULL || name == NULL) { + errno = EINVAL; + return -1; + } + memset(tmp, 0, SYSFS_PATH_MAX); + strcpy(tmp, path); + n = &tmp[strlen(tmp)-1]; + if (strncmp(n, "/", 1) == 0) + *n = '\0'; + n = strrchr(tmp, '/'); + if (n == NULL) { + errno = EINVAL; + return -1; + } + n++; + strncpy(name, n, len); + + return 0; +} + +/** + * sysfs_get_link: returns link source + * @path: symbolic link's path + * @target: where to put name + * @len: size of name + */ +int sysfs_get_link(const unsigned char *path, unsigned char *target, size_t len) +{ + unsigned char devdir[SYSFS_PATH_MAX]; + unsigned char linkpath[SYSFS_PATH_MAX]; + unsigned char *d = NULL, *s = NULL; + int slashes = 0, count = 0; + + if (path == NULL || target == NULL) { + errno = EINVAL; + return -1; + } + + memset(devdir, 0, SYSFS_PATH_MAX); + memset(linkpath, 0, SYSFS_PATH_MAX); + strncpy(devdir, path, SYSFS_PATH_MAX); + + if ((readlink(path, linkpath, SYSFS_PATH_MAX)) < 0) { + return -1; + } + + d = linkpath; + + /* getting rid of leading "../.." */ + while (*d == '/' || *d == '.') { + if (*d == '/') + slashes++; + d++; + } + + d--; + + s = &devdir[strlen(devdir)-1]; + while (s != NULL && count != (slashes+1)) { + s--; + if (*s == '/') + count++; + } + + strncpy(s, d, (SYSFS_PATH_MAX-strlen(devdir))); + strncpy(target, devdir, len); + + return 0; +} + + +/** + * sysfs_del_name: free function for sysfs_open_subsystem_list + * @name: memory area to be freed + */ +void sysfs_del_name(void *name) +{ + free(name); +} + + +/** + * sysfs_close_list: generic list free routine + * @list: dlist to free + * Returns nothing + */ +void sysfs_close_list(struct dlist *list) +{ + if (list != NULL) + dlist_destroy(list); +} + +/** + * sysfs_open_subsystem_list: gets a list of all supported "name" subsystem + * details from the system + * @name: name of the subsystem, eg., "bus", "class", "devices" + * Returns a dlist of supported names or NULL if subsystem not supported + */ +struct dlist *sysfs_open_subsystem_list(unsigned char *name) +{ + unsigned char sysfs_path[SYSFS_PATH_MAX], *subsys_name = NULL; + unsigned char *c = NULL; + struct sysfs_directory *dir = NULL, *cur = NULL; + struct dlist *list = NULL; + struct stat astats; + + if (name == NULL) + return NULL; + + if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) { + dprintf("Error getting sysfs mount point\n"); + return NULL; + } + + strcat(sysfs_path, name); + dir = sysfs_open_directory(sysfs_path); + if (dir == NULL) { + dprintf("Error opening sysfs_directory at %s\n", sysfs_path); + return NULL; + } + + if (sysfs_read_directory(dir) != 0) { + dprintf("Error reading sysfs_directory at %s\n", sysfs_path); + sysfs_close_directory(dir); + return NULL; + } + + if (dir->subdirs != NULL) { + list = dlist_new_with_delete(SYSFS_NAME_LEN, + sysfs_del_name); + if (list == NULL) { + dprintf("Error creating list\n"); + sysfs_close_directory(dir); + return NULL; + } + + dlist_for_each_data(dir->subdirs, cur, + struct sysfs_directory) { + subsys_name = (char *)calloc(1, SYSFS_NAME_LEN); + strcpy(subsys_name, cur->name); + dlist_unshift(list, subsys_name); + } + } + sysfs_close_directory(dir); + /* + * We are now considering "block" as a "class". Hence, if the subsys + * name requested here is "class", verify if "block" is supported on + * this system and return the same. + */ + if (strcmp(name, SYSFS_CLASS_DIR) == 0) { + c = strstr(sysfs_path, SYSFS_CLASS_NAME); + if (c == NULL) + goto out; + strcpy(c, SYSFS_BLOCK_NAME); + if ((lstat(sysfs_path, &astats)) != 0) { + dprintf("stat() failed\n"); + goto out; + } + if (S_ISDIR(astats.st_mode)) { + subsys_name = (char *)calloc(1, SYSFS_NAME_LEN); + strcpy(subsys_name, SYSFS_BLOCK_NAME); + dlist_unshift(list, subsys_name); + } + } +out: + return list; +} + + +/** + * sysfs_open_bus_devices_list: gets a list of all devices on "name" bus + * @name: name of the subsystem, eg., "pci", "scsi", "usb" + * Returns a dlist of supported names or NULL if subsystem not supported + */ +struct dlist *sysfs_open_bus_devices_list(unsigned char *name) +{ + unsigned char sysfs_path[SYSFS_PATH_MAX], *device_name = NULL; + struct sysfs_directory *dir = NULL; + struct sysfs_link *cur = NULL; + struct dlist *list = NULL; + + if (name == NULL) + return NULL; + + if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) { + dprintf("Error getting sysfs mount point\n"); + return NULL; + } + + strcat(sysfs_path, SYSFS_BUS_DIR); + strcat(sysfs_path, "/"); + strcat(sysfs_path, name); + strcat(sysfs_path, SYSFS_DEVICES_DIR); + dir = sysfs_open_directory(sysfs_path); + if (dir == NULL) { + dprintf("Error opening sysfs_directory at %s\n", sysfs_path); + return NULL; + } + + if (sysfs_read_directory(dir) != 0) { + dprintf("Error reading sysfs_directory at %s\n", sysfs_path); + sysfs_close_directory(dir); + return NULL; + } + + if (dir->links != NULL) { + list = dlist_new_with_delete(SYSFS_NAME_LEN, + sysfs_del_name); + if (list == NULL) { + dprintf("Error creating list\n"); + sysfs_close_directory(dir); + return NULL; + } + + dlist_for_each_data(dir->links, cur, + struct sysfs_link) { + device_name = (char *)calloc(1, SYSFS_NAME_LEN); + strcpy(device_name, cur->name); + dlist_unshift(list, device_name); + } + } + sysfs_close_directory(dir); + return list; +} + |