2004-01-20 06:46:26 +03:00
/*
2012-11-12 22:36:23 +04:00
* Copyright ( C ) 2004 - 2009 Kay Sievers < kay @ vrfy . org >
2004-01-20 06:46:26 +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 .
2004-01-20 06:46:26 +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/>.
2004-01-20 06:46:26 +03:00
*/
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
2006-01-09 23:18:00 +03:00
# include <stddef.h>
2004-01-23 14:01:02 +03:00
# include <ctype.h>
2004-01-27 05:20:12 +03:00
# include <stdarg.h>
# include <unistd.h>
2006-01-09 23:18:00 +03:00
# include <dirent.h>
2004-01-27 05:20:12 +03:00
# include <errno.h>
2006-09-03 06:05:20 +04:00
# include <getopt.h>
2009-05-20 20:58:52 +04:00
# include <fcntl.h>
2006-09-05 15:54:08 +04:00
# include <sys/stat.h>
# include <sys/types.h>
2004-01-20 06:46:26 +03:00
2004-01-27 05:55:37 +03:00
# include "udev.h"
2013-12-18 06:49:07 +04:00
# include "udev-util.h"
2004-01-20 06:46:26 +03:00
2014-07-29 17:47:41 +04:00
static bool skip_attribute ( const char * name ) {
2013-03-23 06:55:05 +04:00
static const char * const skip [ ] = {
2012-01-10 04:34:15 +04:00
" uevent " ,
" dev " ,
" modalias " ,
" resource " ,
" driver " ,
" subsystem " ,
" module " ,
} ;
unsigned int i ;
2012-04-16 05:13:22 +04:00
for ( i = 0 ; i < ELEMENTSOF ( skip ) ; i + + )
2013-02-13 21:13:22 +04:00
if ( streq ( name , skip [ i ] ) )
2012-01-10 04:34:15 +04:00
return true ;
return false ;
2011-03-05 01:00:52 +03:00
}
2014-07-29 17:47:41 +04:00
static void print_all_attributes ( struct udev_device * device , const char * key ) {
2012-01-10 04:34:15 +04:00
struct udev_list_entry * sysattr ;
udev_list_entry_foreach ( sysattr , udev_device_get_sysattr_list_entry ( device ) ) {
const char * name ;
const char * value ;
size_t len ;
name = udev_list_entry_get_name ( sysattr ) ;
if ( skip_attribute ( name ) )
continue ;
value = udev_device_get_sysattr_value ( device , name ) ;
if ( value = = NULL )
continue ;
/* skip any values that look like a path */
if ( value [ 0 ] = = ' / ' )
continue ;
/* skip nonprintable attributes */
len = strlen ( value ) ;
while ( len > 0 & & isprint ( value [ len - 1 ] ) )
len - - ;
2012-04-08 18:06:20 +04:00
if ( len > 0 )
2012-01-10 04:34:15 +04:00
continue ;
printf ( " %s{%s}== \" %s \" \n " , key , name , value ) ;
}
printf ( " \n " ) ;
2004-01-20 06:46:26 +03:00
}
2014-07-29 17:47:41 +04:00
static int print_device_chain ( struct udev_device * device ) {
2012-01-10 04:34:15 +04:00
struct udev_device * device_parent ;
const char * str ;
printf ( " \n "
" Udevadm info starts with the device specified by the devpath and then \n "
" walks up the chain of parent devices. It prints for every device \n "
" found, all possible attributes in the udev rules key format. \n "
" A rule to match, can be composed by the attributes of the device \n "
" and the attributes from one single parent device. \n "
" \n " ) ;
printf ( " looking at device '%s': \n " , udev_device_get_devpath ( device ) ) ;
printf ( " KERNEL== \" %s \" \n " , udev_device_get_sysname ( device ) ) ;
str = udev_device_get_subsystem ( device ) ;
if ( str = = NULL )
str = " " ;
printf ( " SUBSYSTEM== \" %s \" \n " , str ) ;
str = udev_device_get_driver ( device ) ;
if ( str = = NULL )
str = " " ;
printf ( " DRIVER== \" %s \" \n " , str ) ;
print_all_attributes ( device , " ATTR " ) ;
device_parent = device ;
do {
device_parent = udev_device_get_parent ( device_parent ) ;
if ( device_parent = = NULL )
break ;
printf ( " looking at parent device '%s': \n " , udev_device_get_devpath ( device_parent ) ) ;
printf ( " KERNELS== \" %s \" \n " , udev_device_get_sysname ( device_parent ) ) ;
str = udev_device_get_subsystem ( device_parent ) ;
if ( str = = NULL )
str = " " ;
printf ( " SUBSYSTEMS== \" %s \" \n " , str ) ;
str = udev_device_get_driver ( device_parent ) ;
if ( str = = NULL )
str = " " ;
printf ( " DRIVERS== \" %s \" \n " , str ) ;
print_all_attributes ( device_parent , " ATTRS " ) ;
} while ( device_parent ! = NULL ) ;
return 0 ;
2006-01-09 23:18:00 +03:00
}
2014-07-29 17:47:41 +04:00
static void print_record ( struct udev_device * device ) {
2012-01-10 04:34:15 +04:00
const char * str ;
int i ;
struct udev_list_entry * list_entry ;
printf ( " P: %s \n " , udev_device_get_devpath ( device ) ) ;
str = udev_device_get_devnode ( device ) ;
if ( str ! = NULL )
2012-04-16 19:21:22 +04:00
printf ( " N: %s \n " , str + strlen ( " /dev/ " ) ) ;
2012-01-10 04:34:15 +04:00
i = udev_device_get_devlink_priority ( device ) ;
if ( i ! = 0 )
printf ( " L: %i \n " , i ) ;
2012-04-16 19:21:22 +04:00
udev_list_entry_foreach ( list_entry , udev_device_get_devlinks_list_entry ( device ) )
printf ( " S: %s \n " , udev_list_entry_get_name ( list_entry ) + strlen ( " /dev/ " ) ) ;
2012-01-10 04:34:15 +04:00
udev_list_entry_foreach ( list_entry , udev_device_get_properties_list_entry ( device ) )
printf ( " E: %s=%s \n " ,
udev_list_entry_get_name ( list_entry ) ,
udev_list_entry_get_value ( list_entry ) ) ;
printf ( " \n " ) ;
2007-03-17 12:08:25 +03:00
}
2014-07-29 17:47:41 +04:00
static int stat_device ( const char * name , bool export , const char * prefix ) {
2012-01-10 04:34:15 +04:00
struct stat statbuf ;
if ( stat ( name , & statbuf ) ! = 0 )
return - 1 ;
if ( export ) {
if ( prefix = = NULL )
prefix = " INFO_ " ;
printf ( " %sMAJOR=%d \n "
" %sMINOR=%d \n " ,
prefix , major ( statbuf . st_dev ) ,
prefix , minor ( statbuf . st_dev ) ) ;
} else
printf ( " %d:%d \n " , major ( statbuf . st_dev ) , minor ( statbuf . st_dev ) ) ;
return 0 ;
2007-11-09 12:22:30 +03:00
}
2014-07-29 17:47:41 +04:00
static int export_devices ( struct udev * udev ) {
2012-01-10 04:34:15 +04:00
struct udev_enumerate * udev_enumerate ;
struct udev_list_entry * list_entry ;
udev_enumerate = udev_enumerate_new ( udev ) ;
if ( udev_enumerate = = NULL )
return - 1 ;
udev_enumerate_scan_devices ( udev_enumerate ) ;
udev_list_entry_foreach ( list_entry , udev_enumerate_get_list_entry ( udev_enumerate ) ) {
struct udev_device * device ;
device = udev_device_new_from_syspath ( udev , udev_list_entry_get_name ( list_entry ) ) ;
if ( device ! = NULL ) {
print_record ( device ) ;
udev_device_unref ( device ) ;
}
}
udev_enumerate_unref ( udev_enumerate ) ;
return 0 ;
2008-09-25 15:20:27 +04:00
}
2014-07-29 17:47:41 +04:00
static void cleanup_dir ( DIR * dir , mode_t mask , int depth ) {
2012-01-10 04:34:15 +04:00
struct dirent * dent ;
if ( depth < = 0 )
return ;
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
struct stat stats ;
if ( dent - > d_name [ 0 ] = = ' . ' )
continue ;
if ( fstatat ( dirfd ( dir ) , dent - > d_name , & stats , AT_SYMLINK_NOFOLLOW ) ! = 0 )
continue ;
if ( ( stats . st_mode & mask ) ! = 0 )
continue ;
if ( S_ISDIR ( stats . st_mode ) ) {
DIR * dir2 ;
dir2 = fdopendir ( openat ( dirfd ( dir ) , dent - > d_name , O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC ) ) ;
if ( dir2 ! = NULL ) {
cleanup_dir ( dir2 , mask , depth - 1 ) ;
closedir ( dir2 ) ;
}
unlinkat ( dirfd ( dir ) , dent - > d_name , AT_REMOVEDIR ) ;
} else {
unlinkat ( dirfd ( dir ) , dent - > d_name , 0 ) ;
}
}
2011-04-14 00:33:01 +04:00
}
2014-07-29 17:47:41 +04:00
static void cleanup_db ( struct udev * udev ) {
2012-01-10 04:34:15 +04:00
DIR * dir ;
2012-04-16 19:21:22 +04:00
unlink ( " /run/udev/queue.bin " ) ;
2012-01-10 04:34:15 +04:00
2012-04-16 19:21:22 +04:00
dir = opendir ( " /run/udev/data " ) ;
2012-01-10 04:34:15 +04:00
if ( dir ! = NULL ) {
cleanup_dir ( dir , S_ISVTX , 1 ) ;
closedir ( dir ) ;
}
2012-04-16 19:21:22 +04:00
dir = opendir ( " /run/udev/links " ) ;
2012-01-10 04:34:15 +04:00
if ( dir ! = NULL ) {
cleanup_dir ( dir , 0 , 2 ) ;
closedir ( dir ) ;
}
2012-04-16 19:21:22 +04:00
dir = opendir ( " /run/udev/tags " ) ;
2012-01-10 04:34:15 +04:00
if ( dir ! = NULL ) {
cleanup_dir ( dir , 0 , 2 ) ;
closedir ( dir ) ;
}
2013-07-07 20:32:34 +04:00
dir = opendir ( " /run/udev/static_node-tags " ) ;
if ( dir ! = NULL ) {
cleanup_dir ( dir , 0 , 2 ) ;
closedir ( dir ) ;
}
2012-04-16 19:21:22 +04:00
dir = opendir ( " /run/udev/watch " ) ;
2012-01-10 04:34:15 +04:00
if ( dir ! = NULL ) {
cleanup_dir ( dir , 0 , 1 ) ;
closedir ( dir ) ;
}
2011-04-14 00:33:01 +04:00
}
2014-07-29 17:47:41 +04:00
static struct udev_device * find_device ( struct udev * udev , const char * id , const char * prefix ) {
2012-06-11 00:53:07 +04:00
char name [ UTIL_PATH_SIZE ] ;
if ( prefix & & ! startswith ( id , prefix ) ) {
2013-01-09 22:06:46 +04:00
strscpyl ( name , sizeof ( name ) , prefix , id , NULL ) ;
2012-06-11 00:53:07 +04:00
id = name ;
}
if ( startswith ( id , " /dev/ " ) ) {
struct stat statbuf ;
char type ;
if ( stat ( id , & statbuf ) < 0 )
return NULL ;
if ( S_ISBLK ( statbuf . st_mode ) )
type = ' b ' ;
else if ( S_ISCHR ( statbuf . st_mode ) )
type = ' c ' ;
else
return NULL ;
return udev_device_new_from_devnum ( udev , type , statbuf . st_rdev ) ;
} else if ( startswith ( id , " /sys/ " ) )
return udev_device_new_from_syspath ( udev , id ) ;
else
return NULL ;
}
2014-07-29 17:47:41 +04:00
static int uinfo ( struct udev * udev , int argc , char * argv [ ] ) {
2013-12-18 06:49:07 +04:00
_cleanup_udev_device_unref_ struct udev_device * device = NULL ;
2012-01-10 04:34:15 +04:00
bool root = 0 ;
bool export = 0 ;
const char * export_prefix = NULL ;
char name [ UTIL_PATH_SIZE ] ;
struct udev_list_entry * list_entry ;
2013-12-18 06:49:07 +04:00
int c ;
2012-01-10 04:34:15 +04:00
static const struct option options [ ] = {
2013-12-18 06:48:14 +04:00
{ " name " , required_argument , NULL , ' n ' } ,
{ " path " , required_argument , NULL , ' p ' } ,
{ " query " , required_argument , NULL , ' q ' } ,
{ " attribute-walk " , no_argument , NULL , ' a ' } ,
{ " cleanup-db " , no_argument , NULL , ' c ' } ,
{ " export-db " , no_argument , NULL , ' e ' } ,
{ " root " , no_argument , NULL , ' r ' } ,
2012-01-10 04:34:15 +04:00
{ " device-id-of-file " , required_argument , NULL , ' d ' } ,
2013-12-18 06:48:14 +04:00
{ " export " , no_argument , NULL , ' x ' } ,
{ " export-prefix " , required_argument , NULL , ' P ' } ,
{ " version " , no_argument , NULL , ' V ' } ,
{ " help " , no_argument , NULL , ' h ' } ,
2012-01-10 04:34:15 +04:00
{ }
} ;
2012-06-11 00:53:07 +04:00
static const char * usage =
2013-12-18 06:48:14 +04:00
" Usage: udevadm info [OPTIONS] [DEVPATH|FILE] \n "
" -q,--query=TYPE query device information: \n "
2012-06-11 00:53:07 +04:00
" name name of device node \n "
" symlink pointing to node \n "
" path sys device path \n "
" property the device properties \n "
" all all values \n "
2013-12-18 06:48:14 +04:00
" -p,--path=SYSPATH sys device path used for query or attribute walk \n "
" -n,--name=NAME node or symlink name used for query or attribute walk \n "
" -r,--root prepend dev directory to path names \n "
" -a,--attribute-walk print all key matches walking along the chain \n "
2012-06-11 00:53:07 +04:00
" of parent devices \n "
2013-12-18 06:48:14 +04:00
" -d,--device-id-of-file=FILE print major:minor of device containing this file \n "
" -x,--export export key/value pairs \n "
" -P,--export-prefix export the key name with a prefix \n "
" -e,--export-db export the content of the udev database \n "
" -c,--cleanup-db cleanup the udev database \n "
" --version print version of the program \n "
" -h,--help print this message \n " ;
2012-06-11 00:53:07 +04:00
2012-01-10 04:34:15 +04:00
enum action_type {
ACTION_QUERY ,
ACTION_ATTRIBUTE_WALK ,
ACTION_DEVICE_ID_FILE ,
2012-06-11 00:53:07 +04:00
} action = ACTION_QUERY ;
2012-01-10 04:34:15 +04:00
enum query_type {
QUERY_NAME ,
QUERY_PATH ,
QUERY_SYMLINK ,
QUERY_PROPERTY ,
QUERY_ALL ,
2012-06-11 00:53:07 +04:00
} query = QUERY_ALL ;
2012-01-10 04:34:15 +04:00
2013-12-18 06:48:14 +04:00
while ( ( c = getopt_long ( argc , argv , " aced:n:p:q:rxP:RVh " , options , NULL ) ) > = 0 )
switch ( c ) {
2012-06-11 00:53:07 +04:00
case ' n ' : {
2012-01-10 04:34:15 +04:00
if ( device ! = NULL ) {
fprintf ( stderr , " device already specified \n " ) ;
2013-12-18 06:49:07 +04:00
return 2 ;
2012-01-10 04:34:15 +04:00
}
2012-06-11 00:53:07 +04:00
device = find_device ( udev , optarg , " /dev/ " ) ;
if ( device = = NULL ) {
2012-01-10 04:34:15 +04:00
fprintf ( stderr , " device node not found \n " ) ;
2013-12-18 06:49:07 +04:00
return 2 ;
2012-01-10 04:34:15 +04:00
}
break ;
2012-06-11 00:53:07 +04:00
}
2012-01-10 04:34:15 +04:00
case ' p ' :
if ( device ! = NULL ) {
fprintf ( stderr , " device already specified \n " ) ;
2013-12-18 06:49:07 +04:00
return 2 ;
2012-01-10 04:34:15 +04:00
}
2012-06-11 00:53:07 +04:00
device = find_device ( udev , optarg , " /sys " ) ;
2012-01-10 04:34:15 +04:00
if ( device = = NULL ) {
2012-06-11 00:53:07 +04:00
fprintf ( stderr , " syspath not found \n " ) ;
2013-12-18 06:49:07 +04:00
return 2 ;
2012-01-10 04:34:15 +04:00
}
break ;
case ' q ' :
action = ACTION_QUERY ;
2013-12-18 06:49:07 +04:00
if ( streq ( optarg , " property " ) | | streq ( optarg , " env " ) )
2012-01-10 04:34:15 +04:00
query = QUERY_PROPERTY ;
2013-12-18 06:49:07 +04:00
else if ( streq ( optarg , " name " ) )
2012-01-10 04:34:15 +04:00
query = QUERY_NAME ;
2013-12-18 06:49:07 +04:00
else if ( streq ( optarg , " symlink " ) )
2012-01-10 04:34:15 +04:00
query = QUERY_SYMLINK ;
2013-12-18 06:49:07 +04:00
else if ( streq ( optarg , " path " ) )
2012-01-10 04:34:15 +04:00
query = QUERY_PATH ;
2013-12-18 06:49:07 +04:00
else if ( streq ( optarg , " all " ) )
2012-01-10 04:34:15 +04:00
query = QUERY_ALL ;
2013-12-18 06:49:07 +04:00
else {
2012-01-10 04:34:15 +04:00
fprintf ( stderr , " unknown query type \n " ) ;
2013-12-18 06:49:07 +04:00
return 3 ;
2012-01-10 04:34:15 +04:00
}
break ;
case ' r ' :
root = true ;
break ;
case ' d ' :
action = ACTION_DEVICE_ID_FILE ;
2013-01-09 22:06:46 +04:00
strscpy ( name , sizeof ( name ) , optarg ) ;
2012-01-10 04:34:15 +04:00
break ;
case ' a ' :
action = ACTION_ATTRIBUTE_WALK ;
break ;
case ' e ' :
export_devices ( udev ) ;
2013-12-18 06:49:07 +04:00
return 0 ;
2012-01-10 04:34:15 +04:00
case ' c ' :
cleanup_db ( udev ) ;
2013-12-18 06:49:07 +04:00
return 0 ;
2012-01-10 04:34:15 +04:00
case ' x ' :
export = true ;
break ;
case ' P ' :
export_prefix = optarg ;
break ;
case ' V ' :
printf ( " %s \n " , VERSION ) ;
2013-12-18 06:49:07 +04:00
return 0 ;
2012-01-10 04:34:15 +04:00
case ' h ' :
2012-06-11 00:53:07 +04:00
printf ( " %s \n " , usage ) ;
2013-12-18 06:49:07 +04:00
return 0 ;
2012-01-10 04:34:15 +04:00
default :
2013-12-18 06:49:07 +04:00
return 1 ;
2012-01-10 04:34:15 +04:00
}
switch ( action ) {
case ACTION_QUERY :
2012-06-11 00:53:07 +04:00
if ( ! device ) {
if ( ! argv [ optind ] ) {
fprintf ( stderr , " %s \n " , usage ) ;
2013-12-18 06:49:07 +04:00
return 2 ;
2012-06-11 00:53:07 +04:00
}
device = find_device ( udev , argv [ optind ] , NULL ) ;
if ( ! device ) {
fprintf ( stderr , " Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected. \n " ) ;
2013-12-18 06:49:07 +04:00
return 4 ;
2012-06-11 00:53:07 +04:00
}
2012-01-10 04:34:15 +04:00
}
switch ( query ) {
case QUERY_NAME : {
const char * node = udev_device_get_devnode ( device ) ;
if ( node = = NULL ) {
fprintf ( stderr , " no device node found \n " ) ;
2013-12-18 06:49:07 +04:00
return 5 ;
2012-01-10 04:34:15 +04:00
}
2012-04-16 19:21:22 +04:00
if ( root )
2012-01-10 04:34:15 +04:00
printf ( " %s \n " , udev_device_get_devnode ( device ) ) ;
2012-04-16 19:21:22 +04:00
else
printf ( " %s \n " , udev_device_get_devnode ( device ) + strlen ( " /dev/ " ) ) ;
2012-01-10 04:34:15 +04:00
break ;
}
case QUERY_SYMLINK :
list_entry = udev_device_get_devlinks_list_entry ( device ) ;
while ( list_entry ! = NULL ) {
2012-04-16 19:21:22 +04:00
if ( root )
2012-01-10 04:34:15 +04:00
printf ( " %s " , udev_list_entry_get_name ( list_entry ) ) ;
2012-04-16 19:21:22 +04:00
else
printf ( " %s " , udev_list_entry_get_name ( list_entry ) + strlen ( " /dev/ " ) ) ;
2012-01-10 04:34:15 +04:00
list_entry = udev_list_entry_get_next ( list_entry ) ;
if ( list_entry ! = NULL )
printf ( " " ) ;
}
printf ( " \n " ) ;
break ;
case QUERY_PATH :
printf ( " %s \n " , udev_device_get_devpath ( device ) ) ;
2013-12-18 06:49:07 +04:00
return 0 ;
2012-01-10 04:34:15 +04:00
case QUERY_PROPERTY :
list_entry = udev_device_get_properties_list_entry ( device ) ;
while ( list_entry ! = NULL ) {
if ( export ) {
const char * prefix = export_prefix ;
if ( prefix = = NULL )
prefix = " " ;
printf ( " %s%s='%s' \n " , prefix ,
udev_list_entry_get_name ( list_entry ) ,
udev_list_entry_get_value ( list_entry ) ) ;
} else {
printf ( " %s=%s \n " , udev_list_entry_get_name ( list_entry ) , udev_list_entry_get_value ( list_entry ) ) ;
}
list_entry = udev_list_entry_get_next ( list_entry ) ;
}
break ;
case QUERY_ALL :
print_record ( device ) ;
break ;
default :
2014-02-23 04:26:27 +04:00
assert_not_reached ( " unknown query type " ) ;
2012-01-10 04:34:15 +04:00
}
break ;
case ACTION_ATTRIBUTE_WALK :
2012-06-11 00:53:07 +04:00
if ( ! device & & argv [ optind ] ) {
device = find_device ( udev , argv [ optind ] , NULL ) ;
if ( ! device ) {
fprintf ( stderr , " Unknown device, absolute path in /dev/ or /sys expected. \n " ) ;
2013-12-18 06:49:07 +04:00
return 4 ;
2012-06-11 00:53:07 +04:00
}
}
if ( ! device ) {
fprintf ( stderr , " Unknown device, --name=, --path=, or absolute path in /dev/ or /sys expected. \n " ) ;
2013-12-18 06:49:07 +04:00
return 4 ;
2012-01-10 04:34:15 +04:00
}
print_device_chain ( device ) ;
break ;
case ACTION_DEVICE_ID_FILE :
if ( stat_device ( name , export , export_prefix ) ! = 0 )
2013-12-18 06:49:07 +04:00
return 1 ;
2012-01-10 04:34:15 +04:00
break ;
}
2004-01-27 05:20:12 +03:00
2013-12-18 06:49:07 +04:00
return 0 ;
2004-01-27 05:20:12 +03:00
}
2011-07-14 03:53:23 +04:00
const struct udevadm_cmd udevadm_info = {
2012-01-10 04:34:15 +04:00
. name = " info " ,
. cmd = uinfo ,
. help = " query sysfs or the udev database " ,
2011-07-14 03:53:23 +04:00
} ;