2003-07-19 07:08:00 +04:00
/*
* syfs_utils . c
*
* System utility functions for libsysfs
*
2003-10-21 12:19:14 +04:00
* Copyright ( C ) IBM Corp . 2003
2003-07-19 07:08:00 +04:00
*
* 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"
2003-10-23 10:48:55 +04:00
# ifndef __KLIBC__
# include <mntent.h>
# endif
2003-07-19 07:08:00 +04:00
/**
* 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 .
*/
2003-10-21 12:19:14 +04:00
static int sysfs_get_fs_mnt_path ( const unsigned char * fs_type ,
unsigned char * mnt_path , size_t len )
2003-07-19 07:08:00 +04:00
{
2003-10-23 10:48:55 +04:00
# ifdef __KLIBC__
strcpy ( mnt_path , " /sys " ) ;
return 0 ;
# else
2003-07-19 07:08:00 +04:00
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 ) {
2003-10-21 12:19:14 +04:00
dprintf ( " Error getting mount information \n " ) ;
2003-07-19 07:08:00 +04:00
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 {
2003-10-21 12:19:14 +04:00
dprintf ( " Error - mount path too long \n " ) ;
2003-07-19 07:08:00 +04:00
ret = - 1 ;
}
}
}
endmntent ( mnt ) ;
if ( dirlen = = 0 & & ret = = 0 ) {
2003-10-21 12:19:14 +04:00
dprintf ( " Filesystem %s not found! \n " , fs_type ) ;
2003-07-19 07:08:00 +04:00
errno = EINVAL ;
ret = - 1 ;
}
return ret ;
2003-10-23 10:48:55 +04:00
# endif
2003-07-19 07:08:00 +04:00
}
2003-12-02 11:48:01 +03:00
/*
* sysfs_trailing_slash : checks if there ' s a trailing slash to path
* @ path : path to check
* returns 1 if true and 0 if not
*/
int sysfs_trailing_slash ( unsigned char * path )
{
unsigned char * s = NULL ;
if ( path = = NULL )
return 0 ;
s = & path [ strlen ( path ) - 1 ] ;
if ( strncmp ( s , " / " , 1 ) = = 0 )
return 1 ;
return 0 ;
}
2003-07-19 07:08:00 +04:00
/*
* 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 .
*/
2003-10-21 12:19:14 +04:00
int sysfs_get_mnt_path ( unsigned char * mnt_path , size_t len )
2003-07-19 07:08:00 +04:00
{
2003-11-12 16:37:24 +03:00
char * sysfs_path = NULL ;
int ret = 0 ;
2003-07-19 07:08:00 +04:00
2003-11-12 16:37:24 +03:00
if ( mnt_path = = NULL ) {
2003-07-19 07:08:00 +04:00
errno = EINVAL ;
2003-11-12 16:37:24 +03:00
return - 1 ;
}
sysfs_path = getenv ( SYSFS_PATH_ENV ) ;
2003-11-25 10:47:43 +03:00
if ( sysfs_path ! = NULL )
2003-11-12 16:37:24 +03:00
strncpy ( mnt_path , sysfs_path , len ) ;
else
ret = sysfs_get_fs_mnt_path ( SYSFS_FSTYPE_NAME , mnt_path , len ) ;
2003-07-19 07:08:00 +04:00
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
*/
2003-10-21 12:19:14 +04:00
int sysfs_get_name_from_path ( const unsigned char * path , unsigned char * name ,
size_t len )
2003-07-19 07:08:00 +04:00
{
2003-11-25 10:47:43 +03:00
unsigned char tmp [ SYSFS_PATH_MAX ] ;
2003-10-21 12:19:14 +04:00
unsigned char * n = NULL ;
2003-07-19 07:08:00 +04:00
if ( path = = NULL | | name = = NULL ) {
errno = EINVAL ;
return - 1 ;
}
2003-11-25 10:47:43 +03:00
memset ( tmp , 0 , SYSFS_PATH_MAX ) ;
strcpy ( tmp , path ) ;
n = & tmp [ strlen ( tmp ) - 1 ] ;
if ( strncmp ( n , " / " , 1 ) = = 0 )
* n = ' \0 ' ;
n = strrchr ( tmp , ' / ' ) ;
2003-07-19 07:08:00 +04:00
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
*/
2003-10-21 12:19:14 +04:00
int sysfs_get_link ( const unsigned char * path , unsigned char * target , size_t len )
2003-07-19 07:08:00 +04:00
{
2003-10-21 12:19:14 +04:00
unsigned char devdir [ SYSFS_PATH_MAX ] ;
unsigned char linkpath [ SYSFS_PATH_MAX ] ;
2003-11-12 16:37:24 +03:00
unsigned char * d = NULL , * s = NULL ;
int slashes = 0 , count = 0 ;
2003-07-19 07:08:00 +04:00
if ( path = = NULL | | target = = NULL ) {
errno = EINVAL ;
return - 1 ;
}
memset ( devdir , 0 , SYSFS_PATH_MAX ) ;
memset ( linkpath , 0 , SYSFS_PATH_MAX ) ;
2003-11-12 16:37:24 +03:00
strncpy ( devdir , path , SYSFS_PATH_MAX ) ;
2003-07-19 07:08:00 +04:00
if ( ( readlink ( path , linkpath , SYSFS_PATH_MAX ) ) < 0 ) {
return - 1 ;
}
d = linkpath ;
/* getting rid of leading "../.." */
2003-11-12 16:37:24 +03:00
while ( * d = = ' / ' | | * d = = ' . ' ) {
if ( * d = = ' / ' )
slashes + + ;
2003-07-19 07:08:00 +04:00
d + + ;
2003-11-12 16:37:24 +03:00
}
2003-07-19 07:08:00 +04:00
d - - ;
2003-11-12 16:37:24 +03:00
s = & devdir [ strlen ( devdir ) - 1 ] ;
while ( s ! = NULL & & count ! = ( slashes + 1 ) ) {
s - - ;
if ( * s = = ' / ' )
count + + ;
}
2003-11-25 10:47:43 +03:00
2003-11-12 16:37:24 +03:00
strncpy ( s , d , ( SYSFS_PATH_MAX - strlen ( devdir ) ) ) ;
2003-07-19 07:08:00 +04:00
strncpy ( target , devdir , len ) ;
return 0 ;
}
2003-10-21 12:19:14 +04:00
/**
* sysfs_del_name : free function for sysfs_open_subsystem_list
* @ name : memory area to be freed
*/
2003-11-25 10:47:43 +03:00
static void sysfs_del_name ( void * name )
2003-10-21 12:19:14 +04:00
{
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 ;
2003-11-25 10:47:43 +03:00
unsigned char * c = NULL ;
2003-10-21 12:19:14 +04:00
struct sysfs_directory * dir = NULL , * cur = NULL ;
struct dlist * list = NULL ;
2003-11-25 10:47:43 +03:00
struct stat astats ;
2003-10-21 12:19:14 +04:00
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 ;
}
2003-12-02 11:48:01 +03:00
if ( sysfs_trailing_slash ( sysfs_path ) = = 0 )
strcat ( sysfs_path , " / " ) ;
2003-10-21 12:19:14 +04:00
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 ) ;
2003-11-25 10:47:43 +03:00
/*
* 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 .
*/
2003-12-16 08:53:28 +03:00
if ( strcmp ( name , SYSFS_CLASS_NAME ) = = 0 ) {
2003-11-25 10:47:43 +03:00
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 :
2003-10-21 12:19:14 +04:00
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 ;
}
2003-12-02 11:48:01 +03:00
if ( sysfs_trailing_slash ( sysfs_path ) = = 0 )
strcat ( sysfs_path , " / " ) ;
strcat ( sysfs_path , SYSFS_BUS_NAME ) ;
2003-10-21 12:19:14 +04:00
strcat ( sysfs_path , " / " ) ;
strcat ( sysfs_path , name ) ;
2003-12-16 08:53:28 +03:00
strcat ( sysfs_path , " / " ) ;
strcat ( sysfs_path , SYSFS_DEVICES_NAME ) ;
2003-10-21 12:19:14 +04:00
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 ;
}
2003-12-16 08:53:28 +03:00
/**
* sysfs_path_is_dir : Check if the path supplied points to a directory
* @ path : path to validate
* Returns 0 if path points to dir , 1 otherwise
*/
int sysfs_path_is_dir ( const unsigned char * path )
{
struct stat astats ;
if ( path = = NULL ) {
errno = EINVAL ;
return 1 ;
}
if ( ( lstat ( path , & astats ) ) ! = 0 ) {
dprintf ( " stat() failed \n " ) ;
return 1 ;
}
if ( S_ISDIR ( astats . st_mode ) )
return 0 ;
return 1 ;
}
/**
* sysfs_path_is_link : Check if the path supplied points to a link
* @ path : path to validate
* Returns 0 if path points to link , 1 otherwise
*/
int sysfs_path_is_link ( const unsigned char * path )
{
struct stat astats ;
if ( path = = NULL ) {
errno = EINVAL ;
return 1 ;
}
if ( ( lstat ( path , & astats ) ) ! = 0 ) {
dprintf ( " stat() failed \n " ) ;
return 1 ;
}
if ( S_ISLNK ( astats . st_mode ) )
return 0 ;
return 1 ;
}
/**
* sysfs_path_is_file : Check if the path supplied points to a file
* @ path : path to validate
* Returns 0 if path points to file , 1 otherwise
*/
int sysfs_path_is_file ( const unsigned char * path )
{
struct stat astats ;
if ( path = = NULL ) {
errno = EINVAL ;
return 1 ;
}
if ( ( lstat ( path , & astats ) ) ! = 0 ) {
dprintf ( " stat() failed \n " ) ;
return 1 ;
}
if ( S_ISREG ( astats . st_mode ) )
return 0 ;
return 1 ;
}