2008-08-28 00:02:41 +04:00
/*
* libudev - interface to udev device information
*
2010-04-22 20:12:36 +04:00
* Copyright ( C ) 2008 - 2010 Kay Sievers < kay . sievers @ vrfy . org >
2008-08-28 00:02:41 +04:00
*
2009-03-26 21:29:36 +03: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 .
2008-08-28 00:02:41 +04:00
*/
# 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>
2009-07-08 04:04:49 +04:00
# include <stdbool.h>
2008-08-28 00:02:41 +04:00
# include <sys/stat.h>
2009-07-08 04:04:49 +04:00
# include <sys/param.h>
2008-08-28 00:02:41 +04:00
# include "libudev.h"
# include "libudev-private.h"
2009-06-15 19:09:43 +04:00
/**
* SECTION : libudev - enumerate
* @ short_description : lookup and sort sys devices
*
* Lookup devices in the sys filesystem , filter devices by properties ,
2009-06-15 22:04:46 +04:00
* and return a sorted list of devices .
2009-06-15 19:09:43 +04:00
*/
2009-07-08 04:04:49 +04:00
struct syspath {
char * syspath ;
size_t len ;
} ;
2009-06-15 19:09:43 +04:00
/**
* udev_enumerate :
*
* Opaque object representing one device lookup / sort context .
*/
2008-09-25 15:20:27 +04:00
struct udev_enumerate {
struct udev * udev ;
int refcount ;
2008-10-20 20:12:36 +04:00
struct udev_list_node sysattr_match_list ;
struct udev_list_node sysattr_nomatch_list ;
2008-10-10 00:24:43 +04:00
struct udev_list_node subsystem_match_list ;
struct udev_list_node subsystem_nomatch_list ;
2009-07-23 02:02:28 +04:00
struct udev_list_node sysname_match_list ;
2008-12-29 09:42:19 +03:00
struct udev_list_node properties_match_list ;
2010-04-22 20:12:36 +04:00
struct udev_list_node tags_match_list ;
2008-10-10 00:24:43 +04:00
struct udev_list_node devices_list ;
2009-07-08 04:04:49 +04:00
struct syspath * devices ;
unsigned int devices_cur ;
unsigned int devices_max ;
bool devices_uptodate : 1 ;
2010-12-14 16:18:32 +03:00
bool match_is_initialized ;
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 ;
2008-10-21 14:10:32 +04:00
udev_enumerate = calloc ( 1 , sizeof ( struct udev_enumerate ) ) ;
2008-09-30 15:42:26 +04:00
if ( udev_enumerate = = NULL )
return NULL ;
udev_enumerate - > refcount = 1 ;
udev_enumerate - > udev = udev ;
2008-10-20 20:12:36 +04:00
udev_list_init ( & udev_enumerate - > sysattr_match_list ) ;
udev_list_init ( & udev_enumerate - > sysattr_nomatch_list ) ;
2008-10-10 00:24:43 +04:00
udev_list_init ( & udev_enumerate - > subsystem_match_list ) ;
udev_list_init ( & udev_enumerate - > subsystem_nomatch_list ) ;
2009-07-23 02:02:28 +04:00
udev_list_init ( & udev_enumerate - > sysname_match_list ) ;
2008-12-29 09:42:19 +03:00
udev_list_init ( & udev_enumerate - > properties_match_list ) ;
2010-04-22 20:12:36 +04:00
udev_list_init ( & udev_enumerate - > tags_match_list ) ;
2009-07-08 04:04:49 +04:00
udev_list_init ( & udev_enumerate - > devices_list ) ;
2008-09-30 15:42:26 +04:00
return udev_enumerate ;
}
2009-06-15 19:09:43 +04:00
/**
* udev_enumerate_ref :
* @ udev_enumerate : context
*
* Take a reference of a enumeration context .
*
* Returns : the passed enumeration context
* */
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 ;
}
2009-06-15 19:09:43 +04:00
/**
* udev_enumerate_unref :
* @ udev_enumerate : context
*
* Drop a reference of an enumeration context . If the refcount reaches zero ,
* all resources of the enumeration context will be released .
* */
2008-09-25 15:20:27 +04:00
void udev_enumerate_unref ( struct udev_enumerate * udev_enumerate )
{
2009-07-08 04:04:49 +04:00
unsigned int i ;
2008-09-25 15:20:27 +04:00
if ( udev_enumerate = = NULL )
return ;
udev_enumerate - > refcount - - ;
if ( udev_enumerate - > refcount > 0 )
return ;
2008-10-20 20:12:36 +04:00
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > sysattr_match_list ) ;
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > sysattr_nomatch_list ) ;
2008-10-18 21:27:38 +04:00
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > subsystem_match_list ) ;
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > subsystem_nomatch_list ) ;
2009-07-23 02:02:28 +04:00
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > sysname_match_list ) ;
2008-12-29 09:42:19 +03:00
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > properties_match_list ) ;
2010-04-22 20:12:36 +04:00
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > tags_match_list ) ;
2009-07-08 04:04:49 +04:00
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > devices_list ) ;
for ( i = 0 ; i < udev_enumerate - > devices_cur ; i + + )
free ( udev_enumerate - > devices [ i ] . syspath ) ;
free ( udev_enumerate - > devices ) ;
2008-09-25 15:20:27 +04:00
free ( udev_enumerate ) ;
}
2009-06-15 19:09:43 +04:00
/**
* udev_enumerate_get_udev :
* @ udev_enumerate : context
*
* Returns : the udev library context .
*/
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 ;
}
2009-07-08 04:04:49 +04:00
static int syspath_add ( struct udev_enumerate * udev_enumerate , const char * syspath )
{
char * path ;
struct syspath * entry ;
/* double array size if needed */
if ( udev_enumerate - > devices_cur > = udev_enumerate - > devices_max ) {
struct syspath * buf ;
unsigned int add ;
add = udev_enumerate - > devices_max ;
if ( add < 1024 )
add = 1024 ;
buf = realloc ( udev_enumerate - > devices , ( udev_enumerate - > devices_max + add ) * sizeof ( struct syspath ) ) ;
if ( buf = = NULL )
return - ENOMEM ;
udev_enumerate - > devices = buf ;
udev_enumerate - > devices_max + = add ;
}
path = strdup ( syspath ) ;
if ( path = = NULL )
return - ENOMEM ;
entry = & udev_enumerate - > devices [ udev_enumerate - > devices_cur ] ;
entry - > syspath = path ;
entry - > len = strlen ( path ) ;
udev_enumerate - > devices_cur + + ;
udev_enumerate - > devices_uptodate = false ;
return 0 ;
}
static int syspath_cmp ( const void * p1 , const void * p2 )
{
const struct syspath * path1 = p1 ;
const struct syspath * path2 = p2 ;
size_t len ;
int ret ;
len = MIN ( path1 - > len , path2 - > len ) ;
ret = memcmp ( path1 - > syspath , path2 - > syspath , len ) ;
if ( ret = = 0 ) {
if ( path1 - > len < path2 - > len )
ret = - 1 ;
else if ( path1 - > len > path2 - > len )
ret = 1 ;
}
return ret ;
}
2009-07-28 01:24:27 +04:00
/* For devices that should be moved to the absolute end of the list */
2010-04-22 20:12:36 +04:00
static bool devices_delay_end ( struct udev * udev , const char * syspath )
2009-07-08 04:04:49 +04:00
{
static const char * delay_device_list [ ] = {
" /block/md " ,
" /block/dm- " ,
NULL
} ;
size_t len ;
int i ;
len = strlen ( udev_get_sys_path ( udev ) ) ;
for ( i = 0 ; delay_device_list [ i ] ! = NULL ; i + + ) {
if ( strstr ( & syspath [ len ] , delay_device_list [ i ] ) ! = NULL ) {
dbg ( udev , " delaying: %s \n " , syspath ) ;
2010-04-22 20:12:36 +04:00
return true ;
2009-07-08 04:04:49 +04:00
}
}
2010-04-22 20:12:36 +04:00
return false ;
2009-07-08 04:04:49 +04:00
}
2009-07-28 01:24:27 +04:00
/* For devices that should just be moved a little bit later, just
* before the point where some common path prefix changes . Returns the
* number of characters that make up that common prefix */
static size_t devices_delay_later ( struct udev * udev , const char * syspath )
{
const char * c ;
/* For sound cards the control device must be enumerated last
* to make sure it ' s the final device node that gets ACLs
* applied . Applications rely on this fact and use ACL changes
* on the control node as an indicator that the ACL change of
* the entire sound card completed . The kernel makes this
* guarantee when creating those devices , and hence we should
* too when enumerating them . */
if ( ( c = strstr ( syspath , " /sound/card " ) ) ) {
c + = 11 ;
c + = strcspn ( c , " / " ) ;
if ( strncmp ( c , " /controlC " , 9 ) = = 0 )
return c - syspath + 1 ;
}
return 0 ;
}
2009-06-15 22:04:46 +04:00
/**
* udev_enumerate_get_list_entry :
* @ udev_enumerate : context
*
* Returns : the first entry of the sorted list of device paths .
*/
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 ;
2009-07-08 04:04:49 +04:00
if ( ! udev_enumerate - > devices_uptodate ) {
unsigned int i ;
unsigned int max ;
2009-07-28 01:24:27 +04:00
struct syspath * prev = NULL , * move_later = NULL ;
2009-12-30 18:21:59 +03:00
size_t move_later_prefix = 0 ;
2009-07-08 04:04:49 +04:00
udev_list_cleanup_entries ( udev_enumerate - > udev , & udev_enumerate - > devices_list ) ;
qsort ( udev_enumerate - > devices , udev_enumerate - > devices_cur , sizeof ( struct syspath ) , syspath_cmp ) ;
max = udev_enumerate - > devices_cur ;
for ( i = 0 ; i < max ; i + + ) {
struct syspath * entry = & udev_enumerate - > devices [ i ] ;
/* skip duplicated entries */
if ( prev ! = NULL & &
entry - > len = = prev - > len & &
memcmp ( entry - > syspath , prev - > syspath , entry - > len ) = = 0 )
continue ;
prev = entry ;
/* skip to be delayed devices, and add them to the end of the list */
2009-07-28 01:24:27 +04:00
if ( devices_delay_end ( udev_enumerate - > udev , entry - > syspath ) ) {
2009-07-08 04:04:49 +04:00
syspath_add ( udev_enumerate , entry - > syspath ) ;
2011-02-16 00:27:33 +03:00
/* need to update prev here for the case realloc() gives a different address */
2011-02-15 12:59:24 +03:00
prev = & udev_enumerate - > devices [ i ] ;
2009-07-08 04:04:49 +04:00
continue ;
}
2009-07-28 01:24:27 +04:00
/* skip to be delayed devices, and move the to
* the point where the prefix changes . We can
* only move one item at a time . */
if ( ! move_later ) {
move_later_prefix = devices_delay_later ( udev_enumerate - > udev , entry - > syspath ) ;
if ( move_later_prefix > 0 ) {
move_later = entry ;
continue ;
}
}
if ( move_later & &
strncmp ( entry - > syspath , move_later - > syspath , move_later_prefix ) ! = 0 ) {
udev_list_entry_add ( udev_enumerate - > udev , & udev_enumerate - > devices_list ,
move_later - > syspath , NULL , 0 , 0 ) ;
move_later = NULL ;
}
2009-07-08 04:04:49 +04:00
udev_list_entry_add ( udev_enumerate - > udev , & udev_enumerate - > devices_list ,
entry - > syspath , NULL , 0 , 0 ) ;
}
2009-07-28 01:24:27 +04:00
if ( move_later )
udev_list_entry_add ( udev_enumerate - > udev , & udev_enumerate - > devices_list ,
move_later - > syspath , NULL , 0 , 0 ) ;
2009-07-08 04:04:49 +04:00
/* add and cleanup delayed devices from end of list */
for ( i = max ; i < udev_enumerate - > devices_cur ; i + + ) {
struct syspath * entry = & udev_enumerate - > devices [ i ] ;
udev_list_entry_add ( udev_enumerate - > udev , & udev_enumerate - > devices_list ,
entry - > syspath , NULL , 0 , 0 ) ;
free ( entry - > syspath ) ;
}
udev_enumerate - > devices_cur = max ;
udev_enumerate - > devices_uptodate = true ;
}
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
}
2009-06-15 22:04:46 +04:00
/**
* udev_enumerate_add_match_subsystem :
* @ udev_enumerate : context
* @ subsystem : filter for a subsystem of the device to include in the list
*
* Returns : 0 on success , otherwise a negative error value .
*/
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 ;
}
2009-06-15 22:04:46 +04:00
/**
* udev_enumerate_add_nomatch_subsystem :
* @ udev_enumerate : context
* @ subsystem : filter for a subsystem of the device to exclude from the list
*
* Returns : 0 on success , otherwise a negative error value .
*/
2008-09-30 15:42:26 +04:00
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 ;
}
2009-06-15 22:04:46 +04:00
/**
* udev_enumerate_add_match_sysattr :
* @ udev_enumerate : context
* @ sysattr : filter for a sys attribute at the device to include in the list
* @ value : optional value of the sys attribute
*
* Returns : 0 on success , otherwise a negative error value .
*/
2008-10-20 20:12:36 +04:00
int udev_enumerate_add_match_sysattr ( struct udev_enumerate * udev_enumerate , const char * sysattr , const char * value )
2008-09-30 15:42:26 +04:00
{
if ( udev_enumerate = = NULL )
return - EINVAL ;
2008-10-20 20:12:36 +04:00
if ( sysattr = = NULL )
2008-09-30 15:42:26 +04:00
return 0 ;
2008-10-10 00:24:43 +04:00
if ( udev_list_entry_add ( udev_enumerate_get_udev ( udev_enumerate ) ,
2009-06-07 03:51:38 +04:00
& udev_enumerate - > sysattr_match_list , sysattr , value , 0 , 0 ) = = NULL )
2008-09-30 15:42:26 +04:00
return - ENOMEM ;
return 0 ;
}
2009-06-15 22:04:46 +04:00
/**
* udev_enumerate_add_nomatch_sysattr :
* @ udev_enumerate : context
* @ sysattr : filter for a sys attribute at the device to exclude from the list
* @ value : optional value of the sys attribute
*
* Returns : 0 on success , otherwise a negative error value .
*/
2008-10-20 20:12:36 +04:00
int udev_enumerate_add_nomatch_sysattr ( struct udev_enumerate * udev_enumerate , const char * sysattr , const char * value )
2008-09-30 15:42:26 +04:00
{
if ( udev_enumerate = = NULL )
return - EINVAL ;
2008-10-20 20:12:36 +04:00
if ( sysattr = = NULL )
2008-09-30 15:42:26 +04:00
return 0 ;
2008-10-10 00:24:43 +04:00
if ( udev_list_entry_add ( udev_enumerate_get_udev ( udev_enumerate ) ,
2009-06-07 03:51:38 +04:00
& udev_enumerate - > sysattr_nomatch_list , sysattr , value , 0 , 0 ) = = NULL )
2008-09-30 15:42:26 +04:00
return - ENOMEM ;
return 0 ;
}
2010-04-22 20:12:36 +04:00
static int match_sysattr_value ( struct udev_device * dev , const char * sysattr , const char * match_val )
2008-09-30 15:42:26 +04:00
{
const char * val = NULL ;
2009-07-08 04:04:49 +04:00
bool match = false ;
2008-09-30 15:42:26 +04:00
2010-04-22 20:12:36 +04:00
val = udev_device_get_sysattr_value ( dev , sysattr ) ;
2008-09-30 15:42:26 +04:00
if ( val = = NULL )
goto exit ;
if ( match_val = = NULL ) {
2009-07-08 04:04:49 +04:00
match = true ;
2008-09-30 15:42:26 +04:00
goto exit ;
}
if ( fnmatch ( match_val , val , 0 ) = = 0 ) {
2009-07-08 04:04:49 +04:00
match = true ;
2008-09-30 15:42:26 +04:00
goto exit ;
}
exit :
return match ;
}
2009-06-15 22:04:46 +04:00
/**
* udev_enumerate_add_match_property :
* @ udev_enumerate : context
* @ property : filter for a property of the device to include in the list
* @ value : value of the property
*
* Returns : 0 on success , otherwise a negative error value .
*/
2008-12-29 09:42:19 +03:00
int udev_enumerate_add_match_property ( struct udev_enumerate * udev_enumerate , const char * property , const char * value )
{
if ( udev_enumerate = = NULL )
return - EINVAL ;
if ( property = = NULL )
return 0 ;
if ( udev_list_entry_add ( udev_enumerate_get_udev ( udev_enumerate ) ,
2009-06-07 03:51:38 +04:00
& udev_enumerate - > properties_match_list , property , value , 0 , 0 ) = = NULL )
2008-12-29 09:42:19 +03:00
return - ENOMEM ;
return 0 ;
}
2010-04-22 20:12:36 +04:00
/**
* udev_enumerate_add_match_tag :
* @ udev_enumerate : context
* @ tag : filter for a tag of the device to include in the list
*
* Returns : 0 on success , otherwise a negative error value .
*/
int udev_enumerate_add_match_tag ( struct udev_enumerate * udev_enumerate , const char * tag )
{
if ( udev_enumerate = = NULL )
return - EINVAL ;
if ( tag = = NULL )
return 0 ;
if ( udev_list_entry_add ( udev_enumerate_get_udev ( udev_enumerate ) ,
& udev_enumerate - > tags_match_list , tag , NULL , 1 , 0 ) = = NULL )
return - ENOMEM ;
return 0 ;
}
2010-12-14 16:18:32 +03:00
/**
* udev_enumerate_add_match_is_initialized :
* @ udev_enumerate : context
*
* Match only devices which udev has set up already . This makes
* sure , that the device node permissions and context are properly set
* and that network devices are fully renamed .
*
* Usually , devices which are found in the kernel but not already
* handled by udev , have still pending events . Services should subscribe
* to monitor events and wait for these devices to become ready , instead
* of using uninitialized devices .
*
* For now , this will not affect devices which do not have a device node
* and are not network interfaces .
*
* Returns : 0 on success , otherwise a negative error value .
*/
int udev_enumerate_add_match_is_initialized ( struct udev_enumerate * udev_enumerate )
{
if ( udev_enumerate = = NULL )
return - EINVAL ;
udev_enumerate - > match_is_initialized = true ;
return 0 ;
}
2009-07-23 02:02:28 +04:00
/**
* udev_enumerate_add_match_sysname :
* @ udev_enumerate : context
* @ sysname : filter for the name of the device to include in the list
*
* Returns : 0 on success , otherwise a negative error value .
*/
int udev_enumerate_add_match_sysname ( struct udev_enumerate * udev_enumerate , const char * sysname )
{
if ( udev_enumerate = = NULL )
return - EINVAL ;
if ( sysname = = NULL )
return 0 ;
if ( udev_list_entry_add ( udev_enumerate_get_udev ( udev_enumerate ) ,
& udev_enumerate - > sysname_match_list , sysname , NULL , 1 , 0 ) = = NULL )
return - ENOMEM ;
return 0 ;
}
2010-04-22 20:12:36 +04:00
static bool match_sysattr ( struct udev_enumerate * udev_enumerate , struct udev_device * dev )
2008-09-30 15:42:26 +04:00
{
struct udev_list_entry * list_entry ;
/* skip list */
2008-10-20 20:12:36 +04:00
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > sysattr_nomatch_list ) ) {
2010-04-22 20:12:36 +04:00
if ( match_sysattr_value ( dev , udev_list_entry_get_name ( list_entry ) ,
udev_list_entry_get_value ( list_entry ) ) )
return false ;
2008-09-30 15:42:26 +04:00
}
/* include list */
2008-10-20 20:12:36 +04:00
if ( udev_list_get_entry ( & udev_enumerate - > sysattr_match_list ) ! = NULL ) {
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > sysattr_match_list ) ) {
2008-09-30 15:42:26 +04:00
/* anything that does not match, will make it FALSE */
2010-04-22 20:12:36 +04:00
if ( ! match_sysattr_value ( dev , udev_list_entry_get_name ( list_entry ) ,
udev_list_entry_get_value ( list_entry ) ) )
return false ;
2008-09-30 15:42:26 +04:00
}
2010-04-22 20:12:36 +04:00
return true ;
2008-09-30 15:42:26 +04:00
}
2010-04-22 20:12:36 +04:00
return true ;
2008-09-30 15:42:26 +04:00
}
2010-04-22 20:12:36 +04:00
static bool match_property ( struct udev_enumerate * udev_enumerate , struct udev_device * dev )
2008-12-29 09:42:19 +03:00
{
struct udev_list_entry * list_entry ;
2010-04-22 20:12:36 +04:00
bool match = false ;
2008-12-29 09:42:19 +03:00
/* no match always matches */
if ( udev_list_get_entry ( & udev_enumerate - > properties_match_list ) = = NULL )
2010-04-22 20:12:36 +04:00
return true ;
2008-12-29 09:42:19 +03:00
/* loop over matches */
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > properties_match_list ) ) {
2009-06-07 03:51:38 +04:00
const char * match_key = udev_list_entry_get_name ( list_entry ) ;
const char * match_value = udev_list_entry_get_value ( list_entry ) ;
2008-12-29 09:42:19 +03:00
struct udev_list_entry * property_entry ;
/* loop over device properties */
udev_list_entry_foreach ( property_entry , udev_device_get_properties_list_entry ( dev ) ) {
2009-06-07 03:51:38 +04:00
const char * dev_key = udev_list_entry_get_name ( property_entry ) ;
const char * dev_value = udev_list_entry_get_value ( property_entry ) ;
2008-12-29 09:42:19 +03:00
2009-06-07 03:51:38 +04:00
if ( fnmatch ( match_key , dev_key , 0 ) ! = 0 )
continue ;
if ( match_value = = NULL & & dev_value = = NULL ) {
2009-07-08 04:04:49 +04:00
match = true ;
2009-06-07 03:51:38 +04:00
goto out ;
}
if ( match_value = = NULL | | dev_value = = NULL )
continue ;
if ( fnmatch ( match_value , dev_value , 0 ) = = 0 ) {
2009-07-08 04:04:49 +04:00
match = true ;
2009-06-07 03:51:38 +04:00
goto out ;
2008-12-29 09:42:19 +03:00
}
}
}
out :
return match ;
}
2010-04-22 20:12:36 +04:00
static bool match_tag ( struct udev_enumerate * udev_enumerate , struct udev_device * dev )
{
struct udev_list_entry * list_entry ;
/* no match always matches */
if ( udev_list_get_entry ( & udev_enumerate - > tags_match_list ) = = NULL )
return true ;
/* loop over matches */
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > tags_match_list ) )
if ( ! udev_device_has_tag ( dev , udev_list_entry_get_name ( list_entry ) ) )
return false ;
return true ;
}
static bool match_sysname ( struct udev_enumerate * udev_enumerate , const char * sysname )
2009-07-23 02:02:28 +04:00
{
struct udev_list_entry * list_entry ;
if ( udev_list_get_entry ( & udev_enumerate - > sysname_match_list ) = = NULL )
2010-04-22 20:12:36 +04:00
return true ;
2009-07-23 02:02:28 +04:00
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > sysname_match_list ) ) {
if ( fnmatch ( udev_list_entry_get_name ( list_entry ) , sysname , 0 ) ! = 0 )
continue ;
2010-04-22 20:12:36 +04:00
return true ;
2009-07-23 02:02:28 +04:00
}
2010-04-22 20:12:36 +04:00
return false ;
2009-07-23 02:02:28 +04:00
}
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 ] ;
2009-05-20 19:57:52 +04:00
size_t l ;
char * s ;
2008-08-28 00:02:41 +04:00
DIR * dir ;
struct dirent * dent ;
2009-05-20 19:57:52 +04:00
s = path ;
l = util_strpcpyl ( & s , sizeof ( path ) , udev_get_sys_path ( udev ) , " / " , basedir , NULL ) ;
if ( subdir1 ! = NULL )
l = util_strpcpyl ( & s , l , " / " , subdir1 , NULL ) ;
if ( subdir2 ! = NULL )
2009-09-09 00:11:04 +04:00
util_strpcpyl ( & s , l , " / " , subdir2 , NULL ) ;
2008-08-28 00:02:41 +04:00
dir = opendir ( path ) ;
if ( dir = = NULL )
2010-04-22 20:12:36 +04:00
return - ENOENT ;
2008-08-28 00:02:41 +04:00
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
2008-09-16 04:12:47 +04:00
char syspath [ UTIL_PATH_SIZE ] ;
2010-04-22 20:12:36 +04:00
struct udev_device * dev ;
2008-08-28 00:02:41 +04:00
if ( dent - > d_name [ 0 ] = = ' . ' )
continue ;
2010-04-22 20:12:36 +04:00
2009-07-23 02:02:28 +04:00
if ( ! match_sysname ( udev_enumerate , dent - > d_name ) )
continue ;
2009-05-20 19:57:52 +04:00
util_strscpyl ( syspath , sizeof ( syspath ) , path , " / " , dent - > d_name , NULL ) ;
2010-04-22 20:12:36 +04:00
dev = udev_device_new_from_syspath ( udev_enumerate - > udev , syspath ) ;
if ( dev = = NULL )
2008-10-01 15:57:39 +04:00
continue ;
2009-05-20 19:57:52 +04:00
2010-12-14 16:18:32 +03:00
if ( udev_enumerate - > match_is_initialized ) {
/*
* All devices with a device node or network interfaces
* possibly need udev to adjust the device node permission
* or context , or rename the interface before it can be
* reliably used from other processes .
*
* For now , we can only check these types of devices , we
* might not store a database , and have no way to find out
* for all other types of devices .
*/
if ( ! udev_device_get_is_initialized ( dev ) & &
( major ( udev_device_get_devnum ( dev ) ) > 0 | | udev_device_get_ifindex ( dev ) > 0 ) )
goto nomatch ;
}
2010-04-22 20:12:36 +04:00
if ( ! match_tag ( udev_enumerate , dev ) )
goto nomatch ;
if ( ! match_property ( udev_enumerate , dev ) )
goto nomatch ;
if ( ! match_sysattr ( udev_enumerate , dev ) )
goto nomatch ;
syspath_add ( udev_enumerate , udev_device_get_syspath ( dev ) ) ;
nomatch :
udev_device_unref ( dev ) ;
2008-08-28 00:02:41 +04:00
}
closedir ( dir ) ;
return 0 ;
}
2010-04-22 20:12:36 +04:00
static bool 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 )
2010-04-22 20:12:36 +04:00
return false ;
2008-09-30 15:42:26 +04:00
}
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 )
2010-04-22 20:12:36 +04:00
return true ;
2008-09-28 19:39:31 +04:00
}
2010-04-22 20:12:36 +04:00
return false ;
2008-08-28 00:02:41 +04:00
}
2010-04-22 20:12:36 +04:00
return true ;
2008-09-30 15:42:26 +04:00
}
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 ;
2009-05-20 19:57:52 +04:00
util_strscpyl ( path , sizeof ( path ) , udev_get_sys_path ( udev ) , " / " , basedir , NULL ) ;
2008-09-30 15:42:26 +04:00
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 ;
}
2009-06-15 22:04:46 +04:00
/**
* udev_enumerate_add_syspath :
* @ udev_enumerate : context
* @ syspath : path of a device
*
* Add a device to the list of devices , to retrieve it back sorted in dependency order .
*
* Returns : 0 on success , otherwise a negative error value .
*/
2008-10-02 13:54:33 +04:00
int udev_enumerate_add_syspath ( struct udev_enumerate * udev_enumerate , const char * syspath )
{
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 ;
2009-07-08 04:04:49 +04:00
syspath_add ( udev_enumerate , udev_device_get_syspath ( udev_device ) ) ;
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
*
2009-06-15 22:04:46 +04:00
* Returns : 0 on success , otherwise a negative error value .
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-25 15:20:27 +04:00
if ( udev_enumerate = = NULL )
2008-09-29 04:00:17 +04:00
return - EINVAL ;
2010-04-22 20:12:36 +04:00
if ( udev_list_get_entry ( & udev_enumerate - > tags_match_list ) ! = NULL ) {
struct udev_list_entry * list_entry ;
/* scan only tagged devices, use tags reverse-index, instead of searching all devices in /sys */
udev_list_entry_foreach ( list_entry , udev_list_get_entry ( & udev_enumerate - > tags_match_list ) ) {
DIR * dir ;
struct dirent * dent ;
char path [ UTIL_PATH_SIZE ] ;
2011-03-23 04:31:09 +03:00
util_strscpyl ( path , sizeof ( path ) , udev_get_run_path ( udev ) , " /tags/ " ,
2010-04-22 20:12:36 +04:00
udev_list_entry_get_name ( list_entry ) , NULL ) ;
dir = opendir ( path ) ;
if ( dir = = NULL )
continue ;
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
struct udev_device * dev ;
if ( dent - > d_name [ 0 ] = = ' . ' )
continue ;
2010-12-12 22:07:15 +03:00
dev = udev_device_new_from_id_filename ( udev_enumerate - > udev , dent - > d_name ) ;
2010-04-22 20:12:36 +04:00
if ( dev = = NULL )
continue ;
syspath_add ( udev_enumerate , udev_device_get_syspath ( dev ) ) ;
udev_device_unref ( dev ) ;
}
2010-06-05 12:30:36 +04:00
closedir ( dir ) ;
2010-04-22 20:12:36 +04:00
}
2008-08-28 00:02:41 +04:00
} else {
2010-04-22 20:12:36 +04:00
util_strscpyl ( base , sizeof ( base ) , udev_get_sys_path ( udev ) , " /subsystem " , NULL ) ;
if ( stat ( base , & statbuf ) = = 0 ) {
/* we have /subsystem/, forget all the old stuff */
dbg ( udev , " searching '/subsystem/*/devices/*' dir \n " ) ;
scan_dir ( udev_enumerate , " subsystem " , " devices " , NULL ) ;
} else {
2008-11-01 22:16:24 +03:00
dbg ( udev , " searching '/bus/*/devices/*' dir \n " ) ;
2010-04-22 20:12:36 +04:00
scan_dir ( udev_enumerate , " bus " , " devices " , NULL ) ;
dbg ( udev , " searching '/class/*' dir \n " ) ;
scan_dir ( udev_enumerate , " class " , NULL , NULL ) ;
}
2008-08-28 00:02:41 +04:00
}
2010-04-22 20:12:36 +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
*
2009-06-15 22:04:46 +04:00
* Returns : 0 on success , otherwise a negative error value .
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 ;
2010-04-22 20:12:36 +04:00
2009-05-20 19:57:52 +04:00
util_strscpyl ( base , sizeof ( base ) , udev_get_sys_path ( udev ) , " /subsystem " , NULL ) ;
2008-09-29 00:18:40 +04:00
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 " ) ) {
2008-11-01 22:16:24 +03:00
dbg ( udev , " searching '%s/*' dir \n " , subsysdir ) ;
2008-09-30 15:42:26 +04:00
scan_dir_and_add_devices ( udev_enumerate , subsysdir , NULL , NULL ) ;
}
2008-10-01 15:57:39 +04:00
if ( match_subsystem ( udev_enumerate , " drivers " ) ) {
2008-11-01 22:16:24 +03:00
dbg ( udev , " searching '%s/*/drivers/*' dir \n " , subsysdir ) ;
2008-10-01 15:57:39 +04:00
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
}