2006-01-09 23:18:00 +03:00
/*
2008-09-10 04:40:42 +04:00
* Copyright ( C ) 2005 - 2008 Kay Sievers < kay . sievers @ vrfy . org >
2006-01-09 23:18:00 +03:00
*
2008-09-10 04:40:42 +04:00
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
2006-01-09 23:18:00 +03:00
*
2008-09-10 04:40:42 +04:00
* This program 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 General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2006-01-09 23:18:00 +03:00
*/
# include <stdlib.h>
# include <stdio.h>
# include <stddef.h>
# include <unistd.h>
2008-07-30 03:45:23 +04:00
# include <string.h>
2006-01-09 23:18:00 +03:00
# include <fcntl.h>
# include <ctype.h>
# include <errno.h>
2006-01-21 01:18:23 +03:00
# include <sys/stat.h>
2006-01-09 23:18:00 +03:00
# include "udev.h"
/* device cache */
static LIST_HEAD ( dev_list ) ;
/* attribute value cache */
static LIST_HEAD ( attr_list ) ;
2008-09-06 17:45:31 +04:00
2006-01-09 23:18:00 +03:00
struct sysfs_attr {
struct list_head node ;
2008-09-10 23:50:21 +04:00
char path [ UTIL_PATH_SIZE ] ;
2006-01-29 04:09:35 +03:00
char * value ; /* points to value_local if value is cached */
2008-09-10 23:50:21 +04:00
char value_local [ UTIL_NAME_SIZE ] ;
2006-01-09 23:18:00 +03:00
} ;
2008-09-16 20:53:36 +04:00
static int resolve_sys_link ( struct udev * udev , char * path , size_t size )
{
char link_path [ UTIL_PATH_SIZE ] ;
char link_target [ UTIL_PATH_SIZE ] ;
int len ;
int i ;
int back ;
util_strlcpy ( link_path , udev_get_sys_path ( udev ) , sizeof ( link_path ) ) ;
util_strlcat ( link_path , path , sizeof ( link_path ) ) ;
len = readlink ( link_path , link_target , sizeof ( link_target ) ) ;
if ( len < = 0 )
return - 1 ;
link_target [ len ] = ' \0 ' ;
dbg ( udev , " path link '%s' points to '%s' \n " , path , link_target ) ;
for ( back = 0 ; strncmp ( & link_target [ back * 3 ] , " ../ " , 3 ) = = 0 ; back + + )
;
dbg ( udev , " base '%s', tail '%s', back %i \n " , path , & link_target [ back * 3 ] , back ) ;
for ( i = 0 ; i < = back ; i + + ) {
char * pos = strrchr ( path , ' / ' ) ;
if ( pos = = NULL )
return - 1 ;
pos [ 0 ] = ' \0 ' ;
}
dbg ( udev , " after moving back '%s' \n " , path ) ;
util_strlcat ( path , " / " , size ) ;
util_strlcat ( path , & link_target [ back * 3 ] , size ) ;
return 0 ;
}
2006-01-09 23:18:00 +03:00
int sysfs_init ( void )
{
INIT_LIST_HEAD ( & dev_list ) ;
INIT_LIST_HEAD ( & attr_list ) ;
return 0 ;
}
void sysfs_cleanup ( void )
{
struct sysfs_attr * attr_loop ;
struct sysfs_attr * attr_temp ;
struct sysfs_device * dev_loop ;
struct sysfs_device * dev_temp ;
list_for_each_entry_safe ( attr_loop , attr_temp , & attr_list , node ) {
list_del ( & attr_loop - > node ) ;
free ( attr_loop ) ;
}
list_for_each_entry_safe ( dev_loop , dev_temp , & dev_list , node ) {
list_del ( & dev_loop - > node ) ;
free ( dev_loop ) ;
}
}
2008-09-06 17:45:31 +04:00
void sysfs_device_set_values ( struct udev * udev ,
struct sysfs_device * dev , const char * devpath ,
2006-08-13 07:32:09 +04:00
const char * subsystem , const char * driver )
2006-01-09 23:18:00 +03:00
{
char * pos ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > devpath , devpath , sizeof ( dev - > devpath ) ) ;
2006-01-09 23:18:00 +03:00
if ( subsystem ! = NULL )
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > subsystem , subsystem , sizeof ( dev - > subsystem ) ) ;
2006-08-13 07:32:09 +04:00
if ( driver ! = NULL )
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > driver , driver , sizeof ( dev - > driver ) ) ;
2006-01-09 23:18:00 +03:00
/* set kernel name */
pos = strrchr ( dev - > devpath , ' / ' ) ;
if ( pos = = NULL )
return ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > kernel , & pos [ 1 ] , sizeof ( dev - > kernel ) ) ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " kernel='%s' \n " , dev - > kernel ) ;
2006-01-09 23:18:00 +03:00
/* some devices have '!' in their name, change that to '/' */
2006-08-19 18:06:25 +04:00
pos = dev - > kernel ;
2006-01-09 23:18:00 +03:00
while ( pos [ 0 ] ! = ' \0 ' ) {
if ( pos [ 0 ] = = ' ! ' )
pos [ 0 ] = ' / ' ;
pos + + ;
}
/* get kernel number */
2006-08-19 18:06:25 +04:00
pos = & dev - > kernel [ strlen ( dev - > kernel ) ] ;
2006-01-09 23:18:00 +03:00
while ( isdigit ( pos [ - 1 ] ) )
pos - - ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > kernel_number , pos , sizeof ( dev - > kernel_number ) ) ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " kernel_number='%s' \n " , dev - > kernel_number ) ;
2006-01-09 23:18:00 +03:00
}
2008-09-06 17:45:31 +04:00
struct sysfs_device * sysfs_device_get ( struct udev * udev , const char * devpath )
2006-01-09 23:18:00 +03:00
{
2008-09-10 23:50:21 +04:00
char path [ UTIL_PATH_SIZE ] ;
char devpath_real [ UTIL_PATH_SIZE ] ;
2006-01-09 23:18:00 +03:00
struct sysfs_device * dev ;
struct sysfs_device * dev_loop ;
struct stat statbuf ;
2008-09-10 23:50:21 +04:00
char link_path [ UTIL_PATH_SIZE ] ;
char link_target [ UTIL_PATH_SIZE ] ;
2006-01-09 23:18:00 +03:00
int len ;
char * pos ;
2007-06-04 13:20:18 +04:00
/* we handle only these devpathes */
if ( devpath ! = NULL & &
strncmp ( devpath , " /devices/ " , 9 ) ! = 0 & &
strncmp ( devpath , " /subsystem/ " , 11 ) ! = 0 & &
strncmp ( devpath , " /module/ " , 8 ) ! = 0 & &
strncmp ( devpath , " /bus/ " , 5 ) ! = 0 & &
strncmp ( devpath , " /class/ " , 7 ) ! = 0 & &
strncmp ( devpath , " /block/ " , 7 ) ! = 0 )
return NULL ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " open '%s' \n " , devpath ) ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( devpath_real , devpath , sizeof ( devpath_real ) ) ;
2008-09-10 20:39:23 +04:00
util_remove_trailing_chars ( devpath_real , ' / ' ) ;
2007-06-04 13:20:18 +04:00
if ( devpath [ 0 ] = = ' \0 ' )
return NULL ;
2006-01-09 23:18:00 +03:00
2006-01-29 04:09:35 +03:00
/* look for device already in cache (we never put an untranslated path in the cache) */
list_for_each_entry ( dev_loop , & dev_list , node ) {
if ( strcmp ( dev_loop - > devpath , devpath_real ) = = 0 ) {
2008-09-06 17:45:31 +04:00
dbg ( udev , " found in cache '%s' \n " , dev_loop - > devpath ) ;
2006-01-29 04:09:35 +03:00
return dev_loop ;
}
}
/* if we got a link, resolve it to the real device */
2008-09-10 20:59:42 +04:00
util_strlcpy ( path , udev_get_sys_path ( udev ) , sizeof ( path ) ) ;
util_strlcat ( path , devpath_real , sizeof ( path ) ) ;
2006-01-09 23:18:00 +03:00
if ( lstat ( path , & statbuf ) ! = 0 ) {
2008-09-29 19:01:32 +04:00
dbg ( udev , " stat '%s' failed: %m \n " , path ) ;
2006-01-09 23:18:00 +03:00
return NULL ;
}
if ( S_ISLNK ( statbuf . st_mode ) ) {
2008-09-16 20:53:36 +04:00
if ( resolve_sys_link ( udev , devpath_real , sizeof ( devpath_real ) ) ! = 0 )
2006-01-09 23:18:00 +03:00
return NULL ;
2006-01-29 04:09:35 +03:00
/* now look for device in cache after path translation */
list_for_each_entry ( dev_loop , & dev_list , node ) {
if ( strcmp ( dev_loop - > devpath , devpath_real ) = = 0 ) {
2008-09-06 17:45:31 +04:00
dbg ( udev , " found in cache '%s' \n " , dev_loop - > devpath ) ;
2006-01-29 04:09:35 +03:00
return dev_loop ;
}
2006-01-09 23:18:00 +03:00
}
}
2006-01-29 04:09:35 +03:00
/* it is a new device */
2008-09-06 17:45:31 +04:00
dbg ( udev , " new uncached device '%s' \n " , devpath_real ) ;
2006-01-09 23:18:00 +03:00
dev = malloc ( sizeof ( struct sysfs_device ) ) ;
if ( dev = = NULL )
return NULL ;
memset ( dev , 0x00 , sizeof ( struct sysfs_device ) ) ;
2008-09-06 17:45:31 +04:00
sysfs_device_set_values ( udev , dev , devpath_real , NULL , NULL ) ;
2006-01-09 23:18:00 +03:00
2006-10-08 18:04:54 +04:00
/* get subsystem name */
2008-09-10 20:59:42 +04:00
util_strlcpy ( link_path , udev_get_sys_path ( udev ) , sizeof ( link_path ) ) ;
util_strlcat ( link_path , dev - > devpath , sizeof ( link_path ) ) ;
util_strlcat ( link_path , " /subsystem " , sizeof ( link_path ) ) ;
2006-10-08 18:04:54 +04:00
len = readlink ( link_path , link_target , sizeof ( link_target ) ) ;
if ( len > 0 ) {
/* get subsystem from "subsystem" link */
link_target [ len ] = ' \0 ' ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " subsystem link '%s' points to '%s' \n " , link_path , link_target ) ;
2006-10-08 18:04:54 +04:00
pos = strrchr ( link_target , ' / ' ) ;
if ( pos ! = NULL )
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > subsystem , & pos [ 1 ] , sizeof ( dev - > subsystem ) ) ;
2006-10-08 18:04:54 +04:00
} else if ( strstr ( dev - > devpath , " /drivers/ " ) ! = NULL ) {
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > subsystem , " drivers " , sizeof ( dev - > subsystem ) ) ;
2006-01-16 08:14:26 +03:00
} else if ( strncmp ( dev - > devpath , " /module/ " , 8 ) = = 0 ) {
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > subsystem , " module " , sizeof ( dev - > subsystem ) ) ;
2007-06-04 12:50:05 +04:00
} else if ( strncmp ( dev - > devpath , " /subsystem/ " , 11 ) = = 0 ) {
pos = strrchr ( dev - > devpath , ' / ' ) ;
if ( pos = = & dev - > devpath [ 10 ] )
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > subsystem , " subsystem " , sizeof ( dev - > subsystem ) ) ;
2008-03-15 01:32:45 +03:00
} else if ( strncmp ( dev - > devpath , " /class/ " , 7 ) = = 0 ) {
pos = strrchr ( dev - > devpath , ' / ' ) ;
if ( pos = = & dev - > devpath [ 6 ] )
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > subsystem , " subsystem " , sizeof ( dev - > subsystem ) ) ;
2007-06-04 12:50:05 +04:00
} else if ( strncmp ( dev - > devpath , " /bus/ " , 5 ) = = 0 ) {
pos = strrchr ( dev - > devpath , ' / ' ) ;
if ( pos = = & dev - > devpath [ 4 ] )
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > subsystem , " subsystem " , sizeof ( dev - > subsystem ) ) ;
2006-01-09 23:18:00 +03:00
}
2006-10-08 18:04:54 +04:00
/* get driver name */
2008-09-10 20:59:42 +04:00
util_strlcpy ( link_path , udev_get_sys_path ( udev ) , sizeof ( link_path ) ) ;
util_strlcat ( link_path , dev - > devpath , sizeof ( link_path ) ) ;
util_strlcat ( link_path , " /driver " , sizeof ( link_path ) ) ;
2006-10-08 18:04:54 +04:00
len = readlink ( link_path , link_target , sizeof ( link_target ) ) ;
if ( len > 0 ) {
link_target [ len ] = ' \0 ' ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " driver link '%s' points to '%s' \n " , link_path , link_target ) ;
2006-10-08 18:04:54 +04:00
pos = strrchr ( link_target , ' / ' ) ;
if ( pos ! = NULL )
2008-09-10 20:59:42 +04:00
util_strlcpy ( dev - > driver , & pos [ 1 ] , sizeof ( dev - > driver ) ) ;
2006-10-08 18:04:54 +04:00
}
2008-09-06 17:45:31 +04:00
dbg ( udev , " add to cache 'devpath=%s', subsystem='%s', driver='%s' \n " , dev - > devpath , dev - > subsystem , dev - > driver ) ;
2006-01-09 23:18:00 +03:00
list_add ( & dev - > node , & dev_list ) ;
return dev ;
}
2008-09-06 17:45:31 +04:00
struct sysfs_device * sysfs_device_get_parent ( struct udev * udev , struct sysfs_device * dev )
2006-01-09 23:18:00 +03:00
{
2008-09-10 23:50:21 +04:00
char parent_devpath [ UTIL_PATH_SIZE ] ;
2006-01-09 23:18:00 +03:00
char * pos ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " open '%s' \n " , dev - > devpath ) ;
2006-01-30 10:39:55 +03:00
/* look if we already know the parent */
2006-01-29 04:09:35 +03:00
if ( dev - > parent ! = NULL )
return dev - > parent ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( parent_devpath , dev - > devpath , sizeof ( parent_devpath ) ) ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " '%s' \n " , parent_devpath ) ;
2006-01-09 23:18:00 +03:00
/* strip last element */
pos = strrchr ( parent_devpath , ' / ' ) ;
if ( pos = = NULL | | pos = = parent_devpath )
return NULL ;
pos [ 0 ] = ' \0 ' ;
if ( strncmp ( parent_devpath , " /class " , 6 ) = = 0 ) {
pos = strrchr ( parent_devpath , ' / ' ) ;
if ( pos = = & parent_devpath [ 6 ] | | pos = = parent_devpath ) {
2008-09-06 17:45:31 +04:00
dbg ( udev , " /class top level, look for device link \n " ) ;
2006-01-09 23:18:00 +03:00
goto device_link ;
}
}
2006-10-08 18:04:54 +04:00
if ( strcmp ( parent_devpath , " /block " ) = = 0 ) {
2008-09-06 17:45:31 +04:00
dbg ( udev , " /block top level, look for device link \n " ) ;
2006-10-08 18:04:54 +04:00
goto device_link ;
}
2007-06-04 12:50:05 +04:00
/* are we at the top level? */
pos = strrchr ( parent_devpath , ' / ' ) ;
if ( pos = = NULL | | pos = = parent_devpath )
return NULL ;
2006-01-30 10:39:55 +03:00
/* get parent and remember it */
2008-09-06 17:45:31 +04:00
dev - > parent = sysfs_device_get ( udev , parent_devpath ) ;
2006-01-29 04:09:35 +03:00
return dev - > parent ;
2006-01-09 23:18:00 +03:00
device_link :
2008-09-10 20:59:42 +04:00
util_strlcpy ( parent_devpath , dev - > devpath , sizeof ( parent_devpath ) ) ;
util_strlcat ( parent_devpath , " /device " , sizeof ( parent_devpath ) ) ;
2008-09-16 20:53:36 +04:00
if ( resolve_sys_link ( udev , parent_devpath , sizeof ( parent_devpath ) ) ! = 0 )
2006-09-19 19:46:04 +04:00
return NULL ;
2006-01-29 04:09:35 +03:00
2006-01-30 10:39:55 +03:00
/* get parent and remember it */
2008-09-06 17:45:31 +04:00
dev - > parent = sysfs_device_get ( udev , parent_devpath ) ;
2006-01-29 04:09:35 +03:00
return dev - > parent ;
2006-01-09 23:18:00 +03:00
}
2008-09-06 17:45:31 +04:00
struct sysfs_device * sysfs_device_get_parent_with_subsystem ( struct udev * udev , struct sysfs_device * dev , const char * subsystem )
2006-01-18 06:24:48 +03:00
{
struct sysfs_device * dev_parent ;
2008-09-06 17:45:31 +04:00
dev_parent = sysfs_device_get_parent ( udev , dev ) ;
2006-01-18 06:24:48 +03:00
while ( dev_parent ! = NULL ) {
if ( strcmp ( dev_parent - > subsystem , subsystem ) = = 0 )
return dev_parent ;
2008-09-06 17:45:31 +04:00
dev_parent = sysfs_device_get_parent ( udev , dev_parent ) ;
2006-01-18 06:24:48 +03:00
}
return NULL ;
}
2008-09-06 17:45:31 +04:00
char * sysfs_attr_get_value ( struct udev * udev , const char * devpath , const char * attr_name )
2006-01-09 23:18:00 +03:00
{
2008-09-10 23:50:21 +04:00
char path_full [ UTIL_PATH_SIZE ] ;
2006-01-09 23:18:00 +03:00
const char * path ;
2008-09-10 23:50:21 +04:00
char value [ UTIL_NAME_SIZE ] ;
2006-01-09 23:18:00 +03:00
struct sysfs_attr * attr_loop ;
struct sysfs_attr * attr ;
2006-09-05 04:18:06 +04:00
struct stat statbuf ;
2006-01-09 23:18:00 +03:00
int fd ;
ssize_t size ;
size_t sysfs_len ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " open '%s'/'%s' \n " , devpath , attr_name ) ;
2008-09-10 20:59:42 +04:00
sysfs_len = util_strlcpy ( path_full , udev_get_sys_path ( udev ) , sizeof ( path_full ) ) ;
2007-08-24 10:14:21 +04:00
if ( sysfs_len > = sizeof ( path_full ) )
sysfs_len = sizeof ( path_full ) - 1 ;
2006-01-09 23:18:00 +03:00
path = & path_full [ sysfs_len ] ;
2008-09-10 20:59:42 +04:00
util_strlcat ( path_full , devpath , sizeof ( path_full ) ) ;
util_strlcat ( path_full , " / " , sizeof ( path_full ) ) ;
util_strlcat ( path_full , attr_name , sizeof ( path_full ) ) ;
2006-01-09 23:18:00 +03:00
/* look for attribute in cache */
list_for_each_entry ( attr_loop , & attr_list , node ) {
if ( strcmp ( attr_loop - > path , path ) = = 0 ) {
2008-09-06 17:45:31 +04:00
dbg ( udev , " found in cache '%s' \n " , attr_loop - > path ) ;
2006-01-09 23:18:00 +03:00
return attr_loop - > value ;
}
}
2006-01-29 04:09:35 +03:00
/* store attribute in cache (also negatives are kept in cache) */
2008-09-06 17:45:31 +04:00
dbg ( udev , " new uncached attribute '%s' \n " , path_full ) ;
2006-01-29 04:09:35 +03:00
attr = malloc ( sizeof ( struct sysfs_attr ) ) ;
if ( attr = = NULL )
return NULL ;
memset ( attr , 0x00 , sizeof ( struct sysfs_attr ) ) ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( attr - > path , path , sizeof ( attr - > path ) ) ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " add to cache '%s' \n " , path_full ) ;
2006-01-29 04:09:35 +03:00
list_add ( & attr - > node , & attr_list ) ;
2006-09-05 04:18:06 +04:00
if ( lstat ( path_full , & statbuf ) ! = 0 ) {
2008-09-29 19:01:32 +04:00
dbg ( udev , " stat '%s' failed: %m \n " , path_full ) ;
2006-01-29 04:09:35 +03:00
goto out ;
2006-01-30 10:39:55 +03:00
}
2006-01-29 04:09:35 +03:00
2006-09-05 04:18:06 +04:00
if ( S_ISLNK ( statbuf . st_mode ) ) {
/* links return the last element of the target path */
2008-09-10 23:50:21 +04:00
char link_target [ UTIL_PATH_SIZE ] ;
2006-09-05 04:18:06 +04:00
int len ;
const char * pos ;
len = readlink ( path_full , link_target , sizeof ( link_target ) ) ;
if ( len > 0 ) {
link_target [ len ] = ' \0 ' ;
pos = strrchr ( link_target , ' / ' ) ;
if ( pos ! = NULL ) {
2008-09-06 17:45:31 +04:00
dbg ( udev , " cache '%s' with link value '%s' \n " , path_full , value ) ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( attr - > value_local , & pos [ 1 ] , sizeof ( attr - > value_local ) ) ;
2006-09-05 04:18:06 +04:00
attr - > value = attr - > value_local ;
}
}
2006-09-05 15:54:08 +04:00
goto out ;
}
/* skip directories */
if ( S_ISDIR ( statbuf . st_mode ) )
goto out ;
/* skip non-readable files */
if ( ( statbuf . st_mode & S_IRUSR ) = = 0 )
goto out ;
/* read attribute value */
fd = open ( path_full , O_RDONLY ) ;
if ( fd < 0 ) {
2008-09-06 17:45:31 +04:00
dbg ( udev , " attribute '%s' can not be opened \n " , path_full ) ;
2006-09-05 15:54:08 +04:00
goto out ;
2006-09-05 04:18:06 +04:00
}
2006-09-05 15:54:08 +04:00
size = read ( fd , value , sizeof ( value ) ) ;
close ( fd ) ;
if ( size < 0 )
goto out ;
if ( size = = sizeof ( value ) )
goto out ;
/* got a valid value, store and return it */
value [ size ] = ' \0 ' ;
2008-09-10 20:39:23 +04:00
util_remove_trailing_chars ( value , ' \n ' ) ;
2008-09-06 17:45:31 +04:00
dbg ( udev , " cache '%s' with attribute value '%s' \n " , path_full , value ) ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( attr - > value_local , value , sizeof ( attr - > value_local ) ) ;
2006-09-05 15:54:08 +04:00
attr - > value = attr - > value_local ;
2006-01-09 23:18:00 +03:00
2006-01-29 04:09:35 +03:00
out :
2006-01-09 23:18:00 +03:00
return attr - > value ;
}
2007-06-02 11:43:57 +04:00
2008-09-06 17:45:31 +04:00
int sysfs_lookup_devpath_by_subsys_id ( struct udev * udev , char * devpath_full , size_t len , const char * subsystem , const char * id )
2007-06-02 11:43:57 +04:00
{
size_t sysfs_len ;
2008-09-10 23:50:21 +04:00
char path_full [ UTIL_PATH_SIZE ] ;
2007-06-02 11:43:57 +04:00
char * path ;
struct stat statbuf ;
2008-09-10 20:59:42 +04:00
sysfs_len = util_strlcpy ( path_full , udev_get_sys_path ( udev ) , sizeof ( path_full ) ) ;
2007-06-02 11:43:57 +04:00
path = & path_full [ sysfs_len ] ;
if ( strcmp ( subsystem , " subsystem " ) = = 0 ) {
2008-09-10 20:59:42 +04:00
util_strlcpy ( path , " /subsystem/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , id , sizeof ( path_full ) - sysfs_len ) ;
2007-06-02 11:43:57 +04:00
if ( stat ( path_full , & statbuf ) = = 0 )
goto found ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( path , " /bus/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , id , sizeof ( path_full ) - sysfs_len ) ;
2007-06-02 11:43:57 +04:00
if ( stat ( path_full , & statbuf ) = = 0 )
goto found ;
2007-06-03 00:00:10 +04:00
goto out ;
2007-06-02 11:43:57 +04:00
2008-09-10 20:59:42 +04:00
util_strlcpy ( path , " /class/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , id , sizeof ( path_full ) - sysfs_len ) ;
2007-06-02 11:43:57 +04:00
if ( stat ( path_full , & statbuf ) = = 0 )
goto found ;
}
if ( strcmp ( subsystem , " module " ) = = 0 ) {
2008-09-10 20:59:42 +04:00
util_strlcpy ( path , " /module/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , id , sizeof ( path_full ) - sysfs_len ) ;
2007-06-02 11:43:57 +04:00
if ( stat ( path_full , & statbuf ) = = 0 )
goto found ;
goto out ;
}
if ( strcmp ( subsystem , " drivers " ) = = 0 ) {
2008-09-10 23:50:21 +04:00
char subsys [ UTIL_NAME_SIZE ] ;
2007-06-02 11:43:57 +04:00
char * driver ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( subsys , id , sizeof ( subsys ) ) ;
2007-06-02 11:43:57 +04:00
driver = strchr ( subsys , ' : ' ) ;
if ( driver ! = NULL ) {
driver [ 0 ] = ' \0 ' ;
driver = & driver [ 1 ] ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( path , " /subsystem/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , subsys , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , " /drivers/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , driver , sizeof ( path_full ) - sysfs_len ) ;
2007-06-02 11:43:57 +04:00
if ( stat ( path_full , & statbuf ) = = 0 )
goto found ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( path , " /bus/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , subsys , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , " /drivers/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , driver , sizeof ( path_full ) - sysfs_len ) ;
2007-06-02 11:43:57 +04:00
if ( stat ( path_full , & statbuf ) = = 0 )
goto found ;
}
goto out ;
}
2008-09-10 20:59:42 +04:00
util_strlcpy ( path , " /subsystem/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , subsystem , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , " /devices/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , id , sizeof ( path_full ) - sysfs_len ) ;
2007-06-02 11:43:57 +04:00
if ( stat ( path_full , & statbuf ) = = 0 )
goto found ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( path , " /bus/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , subsystem , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , " /devices/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , id , sizeof ( path_full ) - sysfs_len ) ;
2007-06-02 11:43:57 +04:00
if ( stat ( path_full , & statbuf ) = = 0 )
goto found ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( path , " /class/ " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , subsystem , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , " / " , sizeof ( path_full ) - sysfs_len ) ;
util_strlcat ( path , id , sizeof ( path_full ) - sysfs_len ) ;
2007-06-02 11:43:57 +04:00
if ( stat ( path_full , & statbuf ) = = 0 )
goto found ;
out :
return 0 ;
found :
if ( S_ISLNK ( statbuf . st_mode ) )
2008-09-16 20:53:36 +04:00
resolve_sys_link ( udev , path , sizeof ( path_full ) - sysfs_len ) ;
2008-09-10 20:59:42 +04:00
util_strlcpy ( devpath_full , path , len ) ;
2007-06-02 11:43:57 +04:00
return 1 ;
}