2008-08-28 00:02:41 +04:00
/*
* libudev - interface to udev device information
*
* Copyright ( C ) 2008 Kay Sievers < kay . sievers @ vrfy . org >
*
* 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 .
*
* 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/>.
*/
# include <stdio.h>
# include <stdlib.h>
# include <stddef.h>
# include <unistd.h>
# include <errno.h>
# include <string.h>
# include <dirent.h>
2008-09-30 15:42:26 +04:00
# include <fnmatch.h>
2008-08-28 00:02:41 +04:00
# include <sys/stat.h>
# include "libudev.h"
# include "libudev-private.h"
2008-10-02 13:54:33 +04:00
static int devices_sort ( struct udev_enumerate * udev_enumerate ) ;
2008-09-25 15:20:27 +04:00
struct udev_enumerate {
struct udev * udev ;
int refcount ;
2008-10-10 00:24:43 +04:00
struct udev_list_node attr_match_list ;
struct udev_list_node attr_nomatch_list ;
struct udev_list_node subsystem_match_list ;
struct udev_list_node subsystem_nomatch_list ;
struct udev_list_node devices_list ;
2008-10-02 13:54:33 +04:00
int devices_sorted ;
2008-09-25 15:20:27 +04:00
} ;
2008-09-30 15:42:26 +04:00
/**
* udev_enumerate_new :
* @ udev : udev library context
*
* Returns : an enumeration context
* */
struct udev_enumerate * udev_enumerate_new ( struct udev * udev )
{
struct udev_enumerate * udev_enumerate ;
udev_enumerate = malloc ( sizeof ( struct udev_enumerate ) ) ;
if ( udev_enumerate = = NULL )
return NULL ;
memset ( udev_enumerate , 0x00 , ( sizeof ( struct udev_enumerate ) ) ) ;
udev_enumerate - > refcount = 1 ;
udev_enumerate - > udev = udev ;
2008-10-10 00:24:43 +04:00
udev_list_init ( & udev_enumerate - > devices_list ) ;
udev_list_init ( & udev_enumerate - > attr_match_list ) ;
udev_list_init ( & udev_enumerate - > attr_nomatch_list ) ;
udev_list_init ( & udev_enumerate - > subsystem_match_list ) ;
udev_list_init ( & udev_enumerate - > subsystem_nomatch_list ) ;
2008-09-30 15:42:26 +04:00
return udev_enumerate ;
}
2008-09-25 15:20:27 +04:00
struct udev_enumerate * udev_enumerate_ref ( struct udev_enumerate * udev_enumerate )
{
if ( udev_enumerate = = NULL )
return NULL ;
udev_enumerate - > refcount + + ;
return udev_enumerate ;
}
void udev_enumerate_unref ( struct udev_enumerate * udev_enumerate )
{
if ( udev_enumerate = = NULL )
return ;
udev_enumerate - > refcount - - ;
if ( udev_enumerate - > refcount > 0 )
return ;
2008-10-18 21:27:38 +04:00
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > devices_list ) ;
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > attr_match_list ) ;
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > attr_nomatch_list ) ;
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > subsystem_match_list ) ;
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > subsystem_nomatch_list ) ;
2008-09-25 15:20:27 +04:00
free ( udev_enumerate ) ;
}
2008-09-28 19:39:31 +04:00
struct udev * udev_enumerate_get_udev ( struct udev_enumerate * udev_enumerate )
{
if ( udev_enumerate = = NULL )
return NULL ;
return udev_enumerate - > udev ;
}
2008-09-28 03:34:55 +04:00
struct udev_list_entry * udev_enumerate_get_list_entry ( struct udev_enumerate * udev_enumerate )
2008-09-25 15:20:27 +04:00
{
if ( udev_enumerate = = NULL )
return NULL ;
2008-10-02 13:54:33 +04:00
if ( ! udev_enumerate - > devices_sorted )
devices_sort ( udev_enumerate ) ;
2008-10-10 00:24:43 +04:00
return udev_list_get_entry ( & udev_enumerate - > devices_list ) ;
2008-09-25 15:20:27 +04:00
}
2008-09-30 15:42:26 +04:00
int udev_enumerate_add_match_subsystem ( struct udev_enumerate * udev_enumerate , const char * subsystem )
2008-08-28 00:02:41 +04:00
{
2008-09-30 15:42:26 +04:00
if ( udev_enumerate = = NULL )
return - EINVAL ;
if ( subsystem = = NULL )
return 0 ;
2008-10-10 00:24:43 +04:00
if ( udev_list_entry_add ( udev_enumerate_get_udev ( udev_enumerate ) ,
& udev_enumerate - > subsystem_match_list , subsystem , NULL , 1 , 0 ) = = NULL )
2008-09-30 15:42:26 +04:00
return - ENOMEM ;
return 0 ;
}
int udev_enumerate_add_nomatch_subsystem ( struct udev_enumerate * udev_enumerate , const char * subsystem )
{
if ( udev_enumerate = = NULL )
return - EINVAL ;
if ( subsystem = = NULL )
return 0 ;
2008-10-10 00:24:43 +04:00
if ( udev_list_entry_add ( udev_enumerate_get_udev ( udev_enumerate ) ,
& udev_enumerate - > subsystem_nomatch_list , subsystem , NULL , 1 , 0 ) = = NULL )
2008-09-30 15:42:26 +04:00
return - ENOMEM ;
return 0 ;
}
int udev_enumerate_add_match_attr ( struct udev_enumerate * udev_enumerate , const char * attr , const char * value )
{
if ( udev_enumerate = = NULL )
return - EINVAL ;
if ( attr = = NULL )
return 0 ;
2008-10-10 00:24:43 +04:00
if ( udev_list_entry_add ( udev_enumerate_get_udev ( udev_enumerate ) ,
2008-09-30 15:42:26 +04:00
& udev_enumerate - > attr_match_list , attr , value , 1 , 0 ) = = NULL )
return - ENOMEM ;
return 0 ;
}
int udev_enumerate_add_nomatch_attr ( struct udev_enumerate * udev_enumerate , const char * attr , const char * value )
{
if ( udev_enumerate = = NULL )
return - EINVAL ;
if ( attr = = NULL )
return 0 ;
2008-10-10 00:24:43 +04:00
if ( udev_list_entry_add ( udev_enumerate_get_udev ( udev_enumerate ) ,
2008-09-30 15:42:26 +04:00
& udev_enumerate - > attr_nomatch_list , attr , value , 1 , 0 ) = = NULL )
return - ENOMEM ;
return 0 ;
}
static int match_attr_value ( struct udev * udev , const char * syspath , const char * attr , const char * match_val )
{
struct udev_device * device ;
const char * val = NULL ;
int match = 0 ;
device = udev_device_new_from_syspath ( udev , syspath ) ;
if ( device = = NULL )
return - EINVAL ;
val = udev_device_get_attr_value ( device , attr ) ;
if ( val = = NULL )
goto exit ;
if ( match_val = = NULL ) {
match = 1 ;
goto exit ;
}
if ( fnmatch ( match_val , val , 0 ) = = 0 ) {
match = 1 ;
goto exit ;
}
exit :
udev_device_unref ( device ) ;
return match ;
}
static int match_attr ( struct udev_enumerate * udev_enumerate , const char * syspath )
{
struct udev * udev = udev_enumerate_get_udev ( udev_enumerate ) ;
struct udev_list_entry * list_entry ;
/* skip list */
2008-10-10 00:24:43 +04:00
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > attr_nomatch_list ) ) {
2008-09-30 15:42:26 +04:00
if ( match_attr_value ( udev , syspath ,
udev_list_entry_get_name ( list_entry ) ,
udev_list_entry_get_value ( list_entry ) ) )
return 0 ;
}
/* include list */
2008-10-10 00:24:43 +04:00
if ( udev_list_get_entry ( & udev_enumerate - > attr_match_list ) ! = NULL ) {
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > attr_match_list ) ) {
2008-09-30 15:42:26 +04:00
/* anything that does not match, will make it FALSE */
if ( ! match_attr_value ( udev , syspath ,
udev_list_entry_get_name ( list_entry ) ,
udev_list_entry_get_value ( list_entry ) ) )
return 0 ;
}
return 1 ;
}
return 1 ;
}
2008-10-01 15:57:39 +04:00
static int scan_dir_and_add_devices ( struct udev_enumerate * udev_enumerate ,
const char * basedir , const char * subdir1 , const char * subdir2 )
2008-09-30 15:42:26 +04:00
{
struct udev * udev = udev_enumerate_get_udev ( udev_enumerate ) ;
2008-09-10 20:00:31 +04:00
char path [ UTIL_PATH_SIZE ] ;
2008-08-28 00:02:41 +04:00
DIR * dir ;
struct dirent * dent ;
2008-09-16 04:12:47 +04:00
util_strlcpy ( path , udev_get_sys_path ( udev ) , sizeof ( path ) ) ;
2008-09-30 15:42:26 +04:00
util_strlcat ( path , " / " , sizeof ( path ) ) ;
2008-09-10 20:00:31 +04:00
util_strlcat ( path , basedir , sizeof ( path ) ) ;
2008-09-30 15:42:26 +04:00
if ( subdir1 ! = NULL ) {
2008-09-29 00:18:40 +04:00
util_strlcat ( path , " / " , sizeof ( path ) ) ;
2008-09-30 15:42:26 +04:00
util_strlcat ( path , subdir1 , sizeof ( path ) ) ;
}
if ( subdir2 ! = NULL ) {
util_strlcat ( path , " / " , sizeof ( path ) ) ;
util_strlcat ( path , subdir2 , sizeof ( path ) ) ;
2008-09-29 00:18:40 +04:00
}
2008-08-28 00:02:41 +04:00
dir = opendir ( path ) ;
if ( dir = = NULL )
return - 1 ;
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
2008-09-16 04:12:47 +04:00
char syspath [ UTIL_PATH_SIZE ] ;
2008-09-29 01:17:29 +04:00
char filename [ UTIL_PATH_SIZE ] ;
2008-09-29 00:18:40 +04:00
struct stat statbuf ;
2008-08-28 00:02:41 +04:00
if ( dent - > d_name [ 0 ] = = ' . ' )
continue ;
2008-09-16 04:12:47 +04:00
util_strlcpy ( syspath , path , sizeof ( syspath ) ) ;
util_strlcat ( syspath , " / " , sizeof ( syspath ) ) ;
util_strlcat ( syspath , dent - > d_name , sizeof ( syspath ) ) ;
2008-10-01 15:57:39 +04:00
if ( lstat ( syspath , & statbuf ) ! = 0 )
continue ;
if ( S_ISREG ( statbuf . st_mode ) )
continue ;
if ( S_ISLNK ( statbuf . st_mode ) )
util_resolve_sys_link ( udev , syspath , sizeof ( syspath ) ) ;
2008-09-29 01:17:29 +04:00
util_strlcpy ( filename , syspath , sizeof ( filename ) ) ;
util_strlcat ( filename , " /uevent " , sizeof ( filename ) ) ;
if ( stat ( filename , & statbuf ) ! = 0 )
2008-09-29 00:18:40 +04:00
continue ;
2008-09-30 15:42:26 +04:00
if ( ! match_attr ( udev_enumerate , syspath ) )
continue ;
2008-10-10 00:24:43 +04:00
udev_list_entry_add ( udev , & udev_enumerate - > devices_list , syspath , NULL , 1 , 1 ) ;
2008-08-28 00:02:41 +04:00
}
closedir ( dir ) ;
return 0 ;
}
2008-09-30 15:42:26 +04:00
static int match_subsystem ( struct udev_enumerate * udev_enumerate , const char * subsystem )
2008-08-28 00:02:41 +04:00
{
2008-09-30 15:42:26 +04:00
struct udev_list_entry * list_entry ;
2008-10-10 00:24:43 +04:00
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > subsystem_nomatch_list ) ) {
2008-09-30 15:42:26 +04:00
if ( fnmatch ( udev_list_entry_get_name ( list_entry ) , subsystem , 0 ) = = 0 )
return 0 ;
}
2008-10-10 00:24:43 +04:00
if ( udev_list_get_entry ( & udev_enumerate - > subsystem_match_list ) ! = NULL ) {
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > subsystem_match_list ) ) {
2008-09-30 15:42:26 +04:00
if ( fnmatch ( udev_list_entry_get_name ( list_entry ) , subsystem , 0 ) = = 0 )
return 1 ;
2008-09-28 19:39:31 +04:00
}
2008-09-30 15:42:26 +04:00
return 0 ;
2008-08-28 00:02:41 +04:00
}
2008-09-30 15:42:26 +04:00
return 1 ;
}
2008-10-01 15:57:39 +04:00
static int scan_dir ( struct udev_enumerate * udev_enumerate , const char * basedir , const char * subdir , const char * subsystem )
2008-09-30 15:42:26 +04:00
{
struct udev * udev = udev_enumerate_get_udev ( udev_enumerate ) ;
char path [ UTIL_PATH_SIZE ] ;
DIR * dir ;
struct dirent * dent ;
util_strlcpy ( path , udev_get_sys_path ( udev ) , sizeof ( path ) ) ;
util_strlcat ( path , " / " , sizeof ( path ) ) ;
util_strlcat ( path , basedir , sizeof ( path ) ) ;
dir = opendir ( path ) ;
if ( dir = = NULL )
return - 1 ;
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
if ( dent - > d_name [ 0 ] = = ' . ' )
continue ;
2008-10-01 15:57:39 +04:00
if ( ! match_subsystem ( udev_enumerate , subsystem ! = NULL ? subsystem : dent - > d_name ) )
2008-09-30 15:42:26 +04:00
continue ;
scan_dir_and_add_devices ( udev_enumerate , basedir , dent - > d_name , subdir ) ;
}
closedir ( dir ) ;
2008-08-28 00:02:41 +04:00
return 0 ;
}
2008-09-16 04:12:47 +04:00
static int devices_delay ( struct udev * udev , const char * syspath )
2008-08-28 00:02:41 +04:00
{
static const char * delay_device_list [ ] = {
" /block/md " ,
" /block/dm- " ,
NULL
} ;
2008-09-16 04:12:47 +04:00
size_t len ;
2008-08-28 00:02:41 +04:00
int i ;
2008-09-16 04:12:47 +04:00
len = strlen ( udev_get_sys_path ( udev ) ) ;
2008-08-28 00:02:41 +04:00
for ( i = 0 ; delay_device_list [ i ] ! = NULL ; i + + ) {
2008-09-16 04:12:47 +04:00
if ( strstr ( & syspath [ len ] , delay_device_list [ i ] ) ! = NULL ) {
info ( udev , " delaying: %s \n " , syspath ) ;
2008-08-28 00:02:41 +04:00
return 1 ;
}
}
return 0 ;
}
2008-10-02 13:54:33 +04:00
/* sort delayed devices to the end of the list */
static int devices_sort ( struct udev_enumerate * udev_enumerate )
2008-09-29 00:18:40 +04:00
{
2008-09-30 15:42:26 +04:00
struct udev_list_entry * list_entry ;
2008-09-29 00:18:40 +04:00
2008-10-10 00:24:43 +04:00
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > devices_list ) ) {
2008-10-02 13:54:33 +04:00
if ( devices_delay ( udev_enumerate - > udev , udev_list_entry_get_name ( list_entry ) ) )
2008-10-10 00:24:43 +04:00
udev_list_entry_move_to_end ( list_entry ) ;
2008-10-02 13:54:33 +04:00
}
udev_enumerate - > devices_sorted = 1 ;
return 0 ;
}
int udev_enumerate_add_syspath ( struct udev_enumerate * udev_enumerate , const char * syspath )
{
struct udev * udev = udev_enumerate_get_udev ( udev_enumerate ) ;
struct udev_device * udev_device ;
2008-09-29 00:18:40 +04:00
if ( udev_enumerate = = NULL )
2008-09-30 15:42:26 +04:00
return - EINVAL ;
2008-10-02 13:54:33 +04:00
if ( syspath = = NULL )
2008-09-30 15:42:26 +04:00
return 0 ;
2008-10-02 13:54:33 +04:00
/* resolve to real syspath */
udev_device = udev_device_new_from_syspath ( udev_enumerate - > udev , syspath ) ;
if ( udev_device = = NULL )
return - EINVAL ;
2008-10-10 00:24:43 +04:00
udev_list_entry_add ( udev , & udev_enumerate - > devices_list ,
udev_device_get_syspath ( udev_device ) , NULL , 1 , 1 ) ;
2008-10-02 13:54:33 +04:00
udev_device_unref ( udev_device ) ;
2008-09-30 15:42:26 +04:00
return 0 ;
2008-09-29 00:18:40 +04:00
}
2008-08-28 00:02:41 +04:00
/**
2008-09-29 04:00:17 +04:00
* udev_enumerate_scan_devices :
* @ udev_enumerate : udev enumeration context
2008-08-28 00:02:41 +04:00
*
2008-09-30 15:42:26 +04:00
* Returns : a negative value on error .
2008-08-28 00:02:41 +04:00
* */
2008-09-30 15:42:26 +04:00
int udev_enumerate_scan_devices ( struct udev_enumerate * udev_enumerate )
2008-08-28 00:02:41 +04:00
{
2008-09-29 04:00:17 +04:00
struct udev * udev = udev_enumerate_get_udev ( udev_enumerate ) ;
2008-09-10 20:00:31 +04:00
char base [ UTIL_PATH_SIZE ] ;
2008-08-28 00:02:41 +04:00
struct stat statbuf ;
2008-09-28 03:34:55 +04:00
struct udev_list_entry * list_entry ;
2008-09-25 15:20:27 +04:00
if ( udev_enumerate = = NULL )
2008-09-29 04:00:17 +04:00
return - EINVAL ;
2008-09-10 20:00:31 +04:00
util_strlcpy ( base , udev_get_sys_path ( udev ) , sizeof ( base ) ) ;
util_strlcat ( base , " /subsystem " , sizeof ( base ) ) ;
2008-08-28 00:02:41 +04:00
if ( stat ( base , & statbuf ) = = 0 ) {
2008-09-29 01:17:29 +04:00
/* we have /subsystem/, forget all the old stuff */
2008-09-29 00:18:40 +04:00
info ( udev , " searching '/subsystem/*/devices/*' dir \n " ) ;
2008-09-30 15:42:26 +04:00
scan_dir ( udev_enumerate , " subsystem " , " devices " , NULL ) ;
2008-08-28 00:02:41 +04:00
} else {
2008-09-29 00:18:40 +04:00
info ( udev , " searching '/bus/*/devices/*' dir \n " ) ;
2008-09-30 15:42:26 +04:00
scan_dir ( udev_enumerate , " bus " , " devices " , NULL ) ;
2008-09-29 00:18:40 +04:00
info ( udev , " searching '/class/*' dir \n " ) ;
2008-09-30 15:42:26 +04:00
scan_dir ( udev_enumerate , " class " , NULL , NULL ) ;
2008-09-29 01:17:29 +04:00
/* if block isn't a class, scan /block/ */
util_strlcpy ( base , udev_get_sys_path ( udev ) , sizeof ( base ) ) ;
util_strlcat ( base , " /class/block " , sizeof ( base ) ) ;
if ( stat ( base , & statbuf ) ! = 0 ) {
2008-09-30 15:42:26 +04:00
if ( match_subsystem ( udev_enumerate , " block " ) ) {
2008-09-29 04:00:17 +04:00
info ( udev , " searching '/block/*' dir \n " ) ;
2008-09-29 01:17:29 +04:00
/* scan disks */
2008-09-30 15:42:26 +04:00
scan_dir_and_add_devices ( udev_enumerate , " block " , NULL , NULL ) ;
2008-09-29 01:17:29 +04:00
/* scan partitions */
2008-09-29 04:00:17 +04:00
info ( udev , " searching '/block/*/*' dir \n " ) ;
2008-09-30 15:42:26 +04:00
scan_dir ( udev_enumerate , " block " , NULL , " block " ) ;
2008-09-29 01:17:29 +04:00
}
}
2008-08-28 00:02:41 +04:00
}
2008-09-25 15:20:27 +04:00
/* sort delayed devices to the end of the list */
2008-10-10 00:24:43 +04:00
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > devices_list ) ) {
2008-09-28 03:34:55 +04:00
if ( devices_delay ( udev , udev_list_entry_get_name ( list_entry ) ) )
2008-10-10 00:24:43 +04:00
udev_list_entry_move_to_end ( list_entry ) ;
2008-08-28 00:02:41 +04:00
}
2008-09-29 04:00:17 +04:00
return 0 ;
2008-08-28 00:02:41 +04:00
}
2008-09-28 19:39:31 +04:00
2008-09-29 04:00:17 +04:00
/**
* udev_enumerate_scan_subsystems :
* @ udev_enumerate : udev enumeration context
*
2008-09-30 15:42:26 +04:00
* Returns : a negative value on error .
2008-09-29 04:00:17 +04:00
* */
int udev_enumerate_scan_subsystems ( struct udev_enumerate * udev_enumerate )
2008-09-28 19:39:31 +04:00
{
2008-09-29 04:00:17 +04:00
struct udev * udev = udev_enumerate_get_udev ( udev_enumerate ) ;
2008-09-29 00:18:40 +04:00
char base [ UTIL_PATH_SIZE ] ;
struct stat statbuf ;
const char * subsysdir ;
if ( udev_enumerate = = NULL )
2008-09-29 04:00:17 +04:00
return - EINVAL ;
2008-09-29 00:18:40 +04:00
util_strlcpy ( base , udev_get_sys_path ( udev ) , sizeof ( base ) ) ;
util_strlcat ( base , " /subsystem " , sizeof ( base ) ) ;
if ( stat ( base , & statbuf ) = = 0 )
2008-09-30 15:42:26 +04:00
subsysdir = " subsystem " ;
2008-09-29 00:18:40 +04:00
else
2008-09-30 15:42:26 +04:00
subsysdir = " bus " ;
if ( match_subsystem ( udev_enumerate , " subsystem " ) ) {
info ( udev , " searching '%s/*' dir \n " , subsysdir ) ;
scan_dir_and_add_devices ( udev_enumerate , subsysdir , NULL , NULL ) ;
}
2008-10-01 15:57:39 +04:00
if ( match_subsystem ( udev_enumerate , " drivers " ) ) {
info ( udev , " searching '%s/*/drivers/*' dir \n " , subsysdir ) ;
scan_dir ( udev_enumerate , subsysdir , " drivers " , " drivers " ) ;
}
2008-09-29 04:00:17 +04:00
return 0 ;
2008-09-28 19:39:31 +04:00
}