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>
# include <sys/stat.h>
# include "libudev.h"
# include "libudev-private.h"
2008-09-25 15:20:27 +04:00
struct udev_enumerate {
struct udev * udev ;
int refcount ;
2008-09-26 21:44:53 +04:00
struct list_node devices_list ;
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 ;
list_cleanup ( udev_enumerate - > udev , & udev_enumerate - > devices_list ) ;
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 ;
return list_get_entry ( & udev_enumerate - > devices_list ) ;
}
2008-08-28 00:02:41 +04:00
static int devices_scan_subsystem ( struct udev * udev ,
const char * basedir , const char * subsystem , const char * subdir ,
2008-09-28 19:39:31 +04:00
struct list_node * devices_list )
2008-08-28 00:02:41 +04:00
{
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-10 20:00:31 +04:00
util_strlcat ( path , basedir , sizeof ( path ) ) ;
2008-09-29 00:18:40 +04:00
if ( subsystem ! = NULL ) {
util_strlcat ( path , " / " , sizeof ( path ) ) ;
util_strlcat ( path , subsystem , sizeof ( path ) ) ;
}
2008-08-28 00:02:41 +04:00
if ( subdir ! = NULL )
2008-09-10 20:00:31 +04:00
util_strlcat ( path , subdir , sizeof ( path ) ) ;
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 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-09-29 00:18:40 +04:00
if ( stat ( syspath , & statbuf ) ! = 0 )
continue ;
if ( ! S_ISDIR ( statbuf . st_mode ) )
continue ;
2008-09-16 04:12:47 +04:00
util_resolve_sys_link ( udev , syspath , sizeof ( syspath ) ) ;
2008-09-28 19:39:31 +04:00
list_entry_add ( udev , devices_list , syspath , NULL , 1 , 1 ) ;
2008-08-28 00:02:41 +04:00
}
closedir ( dir ) ;
return 0 ;
}
static int devices_scan_subsystems ( struct udev * udev ,
2008-09-28 19:39:31 +04:00
const char * basedir , const char * subdir ,
struct udev_list_entry * subsystem_include_list ,
struct udev_list_entry * subsystem_exclude_list ,
struct list_node * devices_list )
2008-08-28 00:02:41 +04:00
{
2008-09-28 19:39:31 +04:00
if ( subsystem_include_list ! = NULL ) {
struct udev_list_entry * list_entry ;
2008-08-28 00:02:41 +04:00
2008-09-28 19:39:31 +04:00
/* if list of subsystems to scan is given, just use this list */
udev_list_entry_foreach ( list_entry , subsystem_include_list )
devices_scan_subsystem ( udev , basedir , udev_list_entry_get_name ( list_entry ) , subdir , devices_list ) ;
} else {
char path [ UTIL_PATH_SIZE ] ;
DIR * dir ;
struct dirent * dent ;
/* if no list of subsystems to scan is given, scan all, and possible exclude some subsystems */
util_strlcpy ( path , udev_get_sys_path ( udev ) , 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 ;
if ( udev_list_entry_get_by_name ( subsystem_exclude_list , dent - > d_name ) ! = NULL )
continue ;
devices_scan_subsystem ( udev , basedir , dent - > d_name , subdir , devices_list ) ;
}
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-09-29 00:18:40 +04:00
static struct udev_enumerate * 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 ;
list_init ( & udev_enumerate - > devices_list ) ;
return udev_enumerate ;
}
2008-08-28 00:02:41 +04:00
/**
2008-09-28 19:39:31 +04:00
* udev_enumerate_new_from_devices :
2008-09-18 10:32:43 +04:00
* @ udev : udev library context
2008-09-28 19:39:31 +04:00
* @ subsystem : the list of names of subsystems to look for devices
2008-08-28 00:02:41 +04:00
*
2008-09-25 15:20:27 +04:00
* Returns : an enumeration context
2008-08-28 00:02:41 +04:00
* */
2008-09-28 19:39:31 +04:00
struct udev_enumerate * udev_enumerate_new_from_devices ( struct udev * udev , const char * subsystem , . . . )
2008-08-28 00:02:41 +04:00
{
2008-09-25 15:20:27 +04:00
struct udev_enumerate * udev_enumerate ;
2008-09-28 19:39:31 +04:00
va_list vargs ;
const char * arg ;
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 19:39:31 +04:00
struct list_node subsystem_include_list ;
struct list_node subsystem_exclude_list ;
2008-09-28 03:34:55 +04:00
struct udev_list_entry * list_entry ;
2008-09-25 15:20:27 +04:00
if ( udev = = NULL )
return NULL ;
2008-08-28 00:02:41 +04:00
2008-09-29 00:18:40 +04:00
udev_enumerate = enumerate_new ( udev ) ;
2008-09-25 15:20:27 +04:00
if ( udev_enumerate = = NULL )
return NULL ;
2008-08-28 00:02:41 +04:00
2008-09-28 19:39:31 +04:00
va_start ( vargs , subsystem ) ;
list_init ( & subsystem_include_list ) ;
list_init ( & subsystem_exclude_list ) ;
for ( arg = subsystem ; arg ! = NULL ; arg = va_arg ( vargs , const char * ) ) {
if ( arg [ 0 ] ! = ' ! ' )
list_entry_add ( udev , & subsystem_include_list , arg , NULL , 1 , 0 ) ;
else
list_entry_add ( udev , & subsystem_exclude_list , & arg [ 1 ] , NULL , 1 , 0 ) ;
}
va_end ( vargs ) ;
2008-08-28 00:02:41 +04:00
/* if we have /sys/subsystem/, forget all the old stuff */
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 00:18:40 +04:00
info ( udev , " searching '/subsystem/*/devices/*' dir \n " ) ;
2008-09-28 19:39:31 +04:00
devices_scan_subsystems ( udev , " /subsystem " , " /devices " ,
list_get_entry ( & subsystem_include_list ) ,
list_get_entry ( & subsystem_exclude_list ) ,
& udev_enumerate - > devices_list ) ;
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-28 19:39:31 +04:00
devices_scan_subsystems ( udev , " /bus " , " /devices " ,
list_get_entry ( & subsystem_include_list ) ,
list_get_entry ( & subsystem_exclude_list ) ,
& udev_enumerate - > devices_list ) ;
2008-09-29 00:18:40 +04:00
info ( udev , " searching '/class/*' dir \n " ) ;
2008-09-28 19:39:31 +04:00
devices_scan_subsystems ( udev , " /class " , NULL ,
list_get_entry ( & subsystem_include_list ) ,
list_get_entry ( & subsystem_exclude_list ) ,
& udev_enumerate - > devices_list ) ;
2008-08-28 00:02:41 +04:00
}
2008-09-28 19:39:31 +04:00
list_cleanup ( udev , & subsystem_include_list ) ;
list_cleanup ( udev , & subsystem_exclude_list ) ;
2008-09-25 15:20:27 +04:00
/* sort delayed devices to the end of the list */
2008-09-28 03:34:55 +04:00
udev_list_entry_foreach ( list_entry , list_get_entry ( & udev_enumerate - > devices_list ) ) {
if ( devices_delay ( udev , udev_list_entry_get_name ( list_entry ) ) )
list_entry_move_to_end ( list_entry ) ;
2008-08-28 00:02:41 +04:00
}
2008-09-25 15:20:27 +04:00
return udev_enumerate ;
2008-08-28 00:02:41 +04:00
}
2008-09-28 19:39:31 +04:00
struct udev_enumerate * udev_enumerate_new_from_subsystems ( struct udev * udev )
{
2008-09-29 00:18:40 +04:00
struct udev_enumerate * udev_enumerate ;
char base [ UTIL_PATH_SIZE ] ;
struct stat statbuf ;
const char * subsysdir ;
if ( udev = = NULL )
return NULL ;
udev_enumerate = enumerate_new ( udev ) ;
if ( udev_enumerate = = NULL )
return NULL ;
util_strlcpy ( base , udev_get_sys_path ( udev ) , sizeof ( base ) ) ;
util_strlcat ( base , " /subsystem " , sizeof ( base ) ) ;
if ( stat ( base , & statbuf ) = = 0 )
subsysdir = " /subsystem " ;
else
subsysdir = " /bus " ;
info ( udev , " searching '%s/*' dir \n " , subsysdir ) ;
devices_scan_subsystem ( udev , subsysdir , NULL , NULL , & udev_enumerate - > devices_list ) ;
info ( udev , " searching '%s/*/drivers/*' dir \n " , subsysdir ) ;
devices_scan_subsystems ( udev , subsysdir , " /drivers " ,
NULL , NULL ,
& udev_enumerate - > devices_list ) ;
return udev_enumerate ;
2008-09-28 19:39:31 +04:00
}