2003-11-13 17:34:36 +03:00
/*
* scsi_id . c
*
* Main section of the scsi_id program
*
* Copyright ( C ) IBM Corp . 2003
2006-01-28 18:42:49 +03:00
* Copyright ( C ) SUSE Linux Products GmbH , 2006
2003-11-13 17:34:36 +03:00
*
2006-01-28 18:42:49 +03:00
* Author :
* Patrick Mansfield < patmans @ us . ibm . com >
2003-11-13 17:34:36 +03:00
*
2006-01-28 18:42:49 +03: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 version 2 of the License .
2003-11-13 17:34:36 +03:00
*/
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <signal.h>
# include <fcntl.h>
# include <errno.h>
# include <string.h>
# include <syslog.h>
# include <stdarg.h>
# include <ctype.h>
2007-05-25 19:37:47 +04:00
# include <getopt.h>
2003-11-13 17:34:36 +03:00
# include <sys/stat.h>
2006-01-09 23:18:00 +03:00
# include "../../udev.h"
# include "scsi_id.h"
# include "scsi_id_version.h"
2003-11-13 17:34:36 +03:00
2006-01-09 23:18:00 +03:00
/* temporary names for mknod */
# define TMP_DIR " / dev"
# define TMP_PREFIX "tmp-scsi"
2003-11-13 17:34:36 +03:00
2007-05-25 19:37:47 +04:00
static const struct option options [ ] = {
{ " device " , 1 , NULL , ' d ' } ,
{ " config " , 1 , NULL , ' f ' } ,
{ " page " , 1 , NULL , ' p ' } ,
{ " devpath " , 1 , NULL , ' s ' } ,
{ " fallback-to-sysfs " , 0 , NULL , ' a ' } ,
{ " blacklisted " , 0 , NULL , ' b ' } ,
{ " whitelisted " , 0 , NULL , ' g ' } ,
{ " prefix-bus-id " , 0 , NULL , ' i ' } ,
{ " replace-whitespace " , 0 , NULL , ' u ' } ,
{ " ignore-sysfs " , 0 , NULL , ' n ' } ,
{ " verbose " , 0 , NULL , ' v ' } ,
{ " version " , 0 , NULL , ' V ' } ,
{ " export " , 0 , NULL , ' x ' } ,
{ " help " , 0 , NULL , ' h ' } ,
{ }
} ;
static const char short_options [ ] = " abd:f:ghinp:s:uvVx " ;
2004-02-28 12:00:36 +03:00
static const char dev_short_options [ ] = " bgp: " ;
2003-11-13 17:34:36 +03:00
static int all_good ;
2005-07-08 05:29:19 +04:00
static int always_info ;
2003-11-13 17:34:36 +03:00
static int dev_specified ;
static int sys_specified ;
2006-01-18 06:24:48 +03:00
static char config_file [ MAX_PATH_LEN ] = SCSI_ID_CONFIG_FILE ;
2003-11-13 17:34:36 +03:00
static int display_bus_id ;
2005-09-14 22:23:48 +04:00
static enum page_code default_page_code ;
2003-11-13 17:34:36 +03:00
static int use_stderr ;
static int debug ;
static int hotplug_mode ;
2004-10-06 08:30:54 +04:00
static int reformat_serial ;
2007-05-25 16:48:08 +04:00
static int ignore_sysfs ;
2005-06-27 04:51:49 +04:00
static int export ;
static char vendor_str [ 64 ] ;
static char model_str [ 64 ] ;
2005-06-27 19:04:56 +04:00
static char revision_str [ 16 ] ;
static char type_str [ 16 ] ;
2003-11-13 17:34:36 +03:00
2006-01-09 23:18:00 +03:00
# ifdef USE_LOG
void log_message ( int priority , const char * format , . . . )
2003-11-13 17:34:36 +03:00
{
2006-01-09 23:18:00 +03:00
va_list args ;
static int udev_log = - 1 ;
2003-11-13 17:34:36 +03:00
2006-01-09 23:18:00 +03:00
if ( udev_log = = - 1 ) {
const char * value ;
2003-11-13 17:34:36 +03:00
2006-01-09 23:18:00 +03:00
value = getenv ( " UDEV_LOG " ) ;
if ( value )
udev_log = log_priority ( value ) ;
else
udev_log = LOG_ERR ;
2003-11-13 17:34:36 +03:00
}
2006-01-09 23:18:00 +03:00
if ( priority > udev_log )
return ;
va_start ( args , format ) ;
vsyslog ( priority , format , args ) ;
va_end ( args ) ;
2003-11-13 17:34:36 +03:00
}
2006-01-09 23:18:00 +03:00
# endif
2003-11-13 17:34:36 +03:00
2005-08-01 03:33:36 +04:00
static void set_str ( char * to , const char * from , size_t count )
2005-06-27 04:51:49 +04:00
{
2005-08-01 03:33:36 +04:00
size_t i , j , len ;
2005-06-27 04:51:49 +04:00
2005-06-27 19:04:56 +04:00
/* strip trailing whitespace */
2005-06-27 04:51:49 +04:00
len = strnlen ( from , count ) ;
2005-08-22 13:37:12 +04:00
while ( len & & isspace ( from [ len - 1 ] ) )
2005-06-27 04:51:49 +04:00
len - - ;
2005-06-27 19:04:56 +04:00
/* strip leading whitespace */
2005-06-27 04:51:49 +04:00
i = 0 ;
while ( isspace ( from [ i ] ) & & ( i < len ) )
i + + ;
j = 0 ;
while ( i < len ) {
2005-06-27 19:04:56 +04:00
/* substitute multiple whitespace */
if ( isspace ( from [ i ] ) ) {
while ( isspace ( from [ i ] ) )
i + + ;
2005-06-27 04:51:49 +04:00
to [ j + + ] = ' _ ' ;
2005-06-27 19:04:56 +04:00
}
/* skip chars */
if ( from [ i ] = = ' / ' ) {
i + + ;
continue ;
}
to [ j + + ] = from [ i + + ] ;
}
to [ j ] = ' \0 ' ;
}
2005-08-13 02:18:44 +04:00
static void set_type ( char * to , const char * from , size_t len )
2005-06-27 19:04:56 +04:00
{
int type_num ;
char * eptr ;
2005-08-13 02:18:44 +04:00
char * type = " generic " ;
2005-06-27 19:04:56 +04:00
type_num = strtoul ( from , & eptr , 0 ) ;
if ( eptr ! = from ) {
switch ( type_num ) {
case 0 :
2005-08-13 02:18:44 +04:00
type = " disk " ;
2005-06-27 19:04:56 +04:00
break ;
case 1 :
2005-08-13 02:18:44 +04:00
type = " tape " ;
2005-06-27 19:04:56 +04:00
break ;
case 4 :
2005-08-13 02:18:44 +04:00
type = " optical " ;
2005-06-27 19:04:56 +04:00
break ;
case 5 :
2005-08-13 02:18:44 +04:00
type = " cd " ;
2005-06-27 19:04:56 +04:00
break ;
case 7 :
2005-08-13 02:18:44 +04:00
type = " optical " ;
2005-06-27 19:04:56 +04:00
break ;
case 0xe :
2005-08-13 02:18:44 +04:00
type = " disk " ;
2005-06-27 19:04:56 +04:00
break ;
case 0xf :
2005-08-13 02:18:44 +04:00
type = " optical " ;
2005-06-27 04:51:49 +04:00
break ;
default :
2005-06-27 19:04:56 +04:00
break ;
2005-06-27 04:51:49 +04:00
}
}
2005-08-13 02:18:44 +04:00
strncpy ( to , type , len ) ;
to [ len - 1 ] = ' \0 ' ;
2005-06-27 04:51:49 +04:00
}
2006-01-09 23:18:00 +03:00
static int create_tmp_dev ( const char * devpath , char * tmpdev , int dev_type )
2004-01-17 09:03:50 +03:00
{
2006-01-09 23:18:00 +03:00
unsigned int maj , min ;
const char * attr ;
2004-01-17 09:03:50 +03:00
2008-04-20 23:07:06 +04:00
dbg ( " %s \n " , devpath ) ;
2006-01-09 23:18:00 +03:00
attr = sysfs_attr_get_value ( devpath , " dev " ) ;
if ( attr = = NULL ) {
2008-04-20 23:07:06 +04:00
dbg ( " %s: could not get dev attribute: %s \n " , devpath , strerror ( errno ) ) ;
2003-11-13 17:34:36 +03:00
return - 1 ;
}
2008-04-20 23:07:06 +04:00
dbg ( " dev value %s \n " , attr ) ;
2006-01-09 23:18:00 +03:00
if ( sscanf ( attr , " %u:%u " , & maj , & min ) ! = 2 ) {
2008-04-20 23:07:06 +04:00
err ( " %s: invalid dev major/minor \n " , devpath ) ;
2003-11-13 17:34:36 +03:00
return - 1 ;
}
2006-01-18 06:24:48 +03:00
snprintf ( tmpdev , MAX_PATH_LEN , " %s/%s-maj%d-min%d-%u " ,
2004-06-26 12:18:17 +04:00
TMP_DIR , TMP_PREFIX , maj , min , getpid ( ) ) ;
2003-11-13 17:34:36 +03:00
2008-04-20 23:07:06 +04:00
dbg ( " tmpdev '%s' \n " , tmpdev ) ;
2004-06-26 12:18:17 +04:00
if ( mknod ( tmpdev , 0600 | dev_type , makedev ( maj , min ) ) ) {
2008-04-20 23:07:06 +04:00
err ( " mknod failed: %s \n " , strerror ( errno ) ) ;
2003-11-13 17:34:36 +03:00
return - 1 ;
}
return 0 ;
}
/*
* get_value :
*
* buf points to an ' = ' followed by a quoted string ( " foo " ) or a string ending
* with a space or ' , ' .
*
* Return a pointer to the NUL terminated string , returns NULL if no
* matches .
*/
static char * get_value ( char * * buffer )
{
static char * quote_string = " \" \n " ;
static char * comma_string = " , \n " ;
char * val ;
char * end ;
if ( * * buffer = = ' " ' ) {
/*
* skip leading quote , terminate when quote seen
*/
( * buffer ) + + ;
end = quote_string ;
} else {
end = comma_string ;
}
val = strsep ( buffer , end ) ;
if ( val & & end = = quote_string )
/*
* skip trailing quote
*/
( * buffer ) + + ;
while ( isspace ( * * buffer ) )
( * buffer ) + + ;
return val ;
}
static int argc_count ( char * opts )
{
int i = 0 ;
while ( * opts ! = ' \0 ' )
if ( * opts + + = = ' ' )
i + + ;
return i ;
}
/*
* get_file_options :
*
* If vendor = = NULL , find a line in the config file with only " OPTIONS= " ;
* if vendor and model are set find the first OPTIONS line in the config
* file that matches . Set argc and argv to match the OPTIONS string .
*
* vendor and model can end in ' \n ' .
*/
2006-01-09 23:18:00 +03:00
static int get_file_options ( const char * vendor , const char * model ,
int * argc , char * * * newargv )
2003-11-13 17:34:36 +03:00
{
2003-12-07 19:55:40 +03:00
char * buffer ;
2003-11-13 17:34:36 +03:00
FILE * fd ;
char * buf ;
char * str1 ;
char * vendor_in , * model_in , * options_in ; /* read in from file */
int lineno ;
int c ;
int retval = 0 ;
2006-01-09 23:18:00 +03:00
dbg ( " vendor='%s'; model='%s' \n " , vendor , model ) ;
2003-11-13 17:34:36 +03:00
fd = fopen ( config_file , " r " ) ;
if ( fd = = NULL ) {
2006-01-09 23:18:00 +03:00
dbg ( " can't open %s \n " , config_file ) ;
2003-11-13 17:34:36 +03:00
if ( errno = = ENOENT ) {
return 1 ;
} else {
2008-04-20 23:07:06 +04:00
err ( " can't open %s: %s \n " , config_file , strerror ( errno ) ) ;
2003-11-13 17:34:36 +03:00
return - 1 ;
}
}
2003-12-07 19:55:40 +03:00
/*
* Allocate a buffer rather than put it on the stack so we can
* keep it around to parse any options ( any allocated newargv
* points into this buffer for its strings ) .
*/
buffer = malloc ( MAX_BUFFER_LEN ) ;
if ( ! buffer ) {
2008-04-20 23:07:06 +04:00
err ( " can't allocate memory \n " ) ;
2003-12-07 19:55:40 +03:00
return - 1 ;
}
2003-11-13 17:34:36 +03:00
* newargv = NULL ;
lineno = 0 ;
while ( 1 ) {
vendor_in = model_in = options_in = NULL ;
2003-12-07 19:55:40 +03:00
buf = fgets ( buffer , MAX_BUFFER_LEN , fd ) ;
2003-11-13 17:34:36 +03:00
if ( buf = = NULL )
break ;
lineno + + ;
2003-12-07 19:55:40 +03:00
if ( buf [ strlen ( buffer ) - 1 ] ! = ' \n ' ) {
2008-04-20 23:07:06 +04:00
info ( " Config file line %d too long \n " , lineno ) ;
2003-12-07 19:55:40 +03:00
break ;
}
2003-11-13 17:34:36 +03:00
while ( isspace ( * buf ) )
buf + + ;
2006-01-09 23:18:00 +03:00
/* blank or all whitespace line */
2003-11-13 17:34:36 +03:00
if ( * buf = = ' \0 ' )
continue ;
2006-01-09 23:18:00 +03:00
/* comment line */
2003-11-13 17:34:36 +03:00
if ( * buf = = ' # ' )
continue ;
2006-01-09 23:18:00 +03:00
dbg ( " lineno %d: '%s' \n " , lineno , buf ) ;
2003-11-13 17:34:36 +03:00
str1 = strsep ( & buf , " = " ) ;
if ( str1 & & strcasecmp ( str1 , " VENDOR " ) = = 0 ) {
str1 = get_value ( & buf ) ;
if ( ! str1 ) {
retval = - 1 ;
break ;
}
vendor_in = str1 ;
str1 = strsep ( & buf , " = " ) ;
if ( str1 & & strcasecmp ( str1 , " MODEL " ) = = 0 ) {
str1 = get_value ( & buf ) ;
if ( ! str1 ) {
retval = - 1 ;
break ;
}
model_in = str1 ;
str1 = strsep ( & buf , " = " ) ;
}
}
if ( str1 & & strcasecmp ( str1 , " OPTIONS " ) = = 0 ) {
str1 = get_value ( & buf ) ;
if ( ! str1 ) {
retval = - 1 ;
break ;
}
options_in = str1 ;
}
2008-04-20 23:07:06 +04:00
dbg ( " config file line %d: \n "
2003-11-13 17:34:36 +03:00
" vendor '%s'; model '%s'; options '%s' \n " ,
lineno , vendor_in , model_in , options_in ) ;
/*
* Only allow : [ vendor = foo [ , model = bar ] ] options = stuff
*/
if ( ! options_in | | ( ! vendor_in & & model_in ) ) {
2008-04-20 23:07:06 +04:00
info ( " Error parsing config file line %d '%s' \n " , lineno , buffer ) ;
2003-11-13 17:34:36 +03:00
retval = - 1 ;
break ;
}
if ( vendor = = NULL ) {
if ( vendor_in = = NULL ) {
2006-01-09 23:18:00 +03:00
dbg ( " matched global option \n " ) ;
2003-11-13 17:34:36 +03:00
break ;
}
} else if ( ( vendor_in & & strncmp ( vendor , vendor_in ,
strlen ( vendor_in ) ) = = 0 ) & &
( ! model_in | | ( strncmp ( model , model_in ,
strlen ( model_in ) ) = = 0 ) ) ) {
/*
* Matched vendor and optionally model .
*
* Note : a short vendor_in or model_in can
* give a partial match ( that is FOO
* matches FOOBAR ) .
*/
2006-01-09 23:18:00 +03:00
dbg ( " matched vendor/model \n " ) ;
2003-11-13 17:34:36 +03:00
break ;
} else {
2006-01-09 23:18:00 +03:00
dbg ( " no match \n " ) ;
2003-11-13 17:34:36 +03:00
}
}
if ( retval = = 0 ) {
if ( vendor_in ! = NULL | | model_in ! = NULL | |
options_in ! = NULL ) {
/*
* Something matched . Allocate newargv , and store
* values found in options_in .
*/
2003-12-07 19:55:40 +03:00
strcpy ( buffer , options_in ) ;
c = argc_count ( buffer ) + 2 ;
2003-11-13 17:34:36 +03:00
* newargv = calloc ( c , sizeof ( * * newargv ) ) ;
if ( ! * newargv ) {
2008-04-20 23:07:06 +04:00
err ( " can't allocate memory \n " ) ;
2003-11-13 17:34:36 +03:00
retval = - 1 ;
} else {
* argc = c ;
c = 0 ;
2003-12-07 19:55:40 +03:00
/*
* argv [ 0 ] at 0 is skipped by getopt , but
* store the buffer address there for
2007-02-26 00:14:14 +03:00
* later freeing
2003-12-07 19:55:40 +03:00
*/
( * newargv ) [ c ] = buffer ;
2003-11-13 17:34:36 +03:00
for ( c = 1 ; c < * argc ; c + + )
2007-02-26 00:14:14 +03:00
( * newargv ) [ c ] = strsep ( & buffer , " \t " ) ;
2003-11-13 17:34:36 +03:00
}
} else {
2006-01-09 23:18:00 +03:00
/* No matches */
2003-11-13 17:34:36 +03:00
retval = 1 ;
}
}
2003-12-07 19:55:40 +03:00
if ( retval ! = 0 )
free ( buffer ) ;
2003-11-13 17:34:36 +03:00
fclose ( fd ) ;
return retval ;
}
static int set_options ( int argc , char * * argv , const char * short_opts ,
2003-12-07 19:55:40 +03:00
char * target , char * maj_min_dev )
2003-11-13 17:34:36 +03:00
{
int option ;
/*
2003-12-07 19:55:40 +03:00
* optind is a global extern used by getopt . Since we can call
* set_options twice ( once for command line , and once for config
2006-08-05 02:35:05 +04:00
* file ) we have to reset this back to 1.
2003-11-13 17:34:36 +03:00
*/
2003-12-07 19:55:40 +03:00
optind = 1 ;
2003-11-13 17:34:36 +03:00
while ( 1 ) {
2007-05-25 19:37:47 +04:00
option = getopt_long ( argc , argv , short_opts , options , NULL ) ;
2003-11-13 17:34:36 +03:00
if ( option = = - 1 )
break ;
if ( optarg )
2006-01-09 23:18:00 +03:00
dbg ( " option '%c' arg '%s' \n " , option , optarg ) ;
2003-11-13 17:34:36 +03:00
else
2006-01-09 23:18:00 +03:00
dbg ( " option '%c' \n " , option ) ;
2003-11-13 17:34:36 +03:00
switch ( option ) {
2005-07-08 05:29:19 +04:00
case ' a ' :
always_info = 1 ;
break ;
2003-11-13 17:34:36 +03:00
case ' b ' :
all_good = 0 ;
break ;
case ' d ' :
dev_specified = 1 ;
2006-01-18 06:24:48 +03:00
strncpy ( maj_min_dev , optarg , MAX_PATH_LEN ) ;
maj_min_dev [ MAX_PATH_LEN - 1 ] = ' \0 ' ;
2003-11-13 17:34:36 +03:00
break ;
case ' e ' :
use_stderr = 1 ;
break ;
case ' f ' :
2006-01-18 06:24:48 +03:00
strncpy ( config_file , optarg , MAX_PATH_LEN ) ;
config_file [ MAX_PATH_LEN - 1 ] = ' \0 ' ;
2003-11-13 17:34:36 +03:00
break ;
case ' g ' :
all_good = 1 ;
break ;
2007-05-25 19:37:47 +04:00
case ' h ' :
printf ( " Usage: scsi_id OPTIONS <device> \n "
" --device device node for SG_IO commands \n "
" --devpath sysfs devpath \n "
" --config location of config file \n "
" --page SCSI page (0x80, 0x83, pre-spc3-83) \n "
" --fallback-to-sysfs print sysfs values if inquiry fails \n "
" --ignore-sysfs ignore sysfs entries \n "
" --blacklisted threat device as blacklisted \n "
" --whitelisted threat device as whitelisted \n "
" --prefix-bus-id prefix SCSI bus id \n "
" --replace-whitespace replace all whitespaces by underscores \n "
" --verbose verbose logging \n "
" --version print version \n "
2007-05-29 23:26:01 +04:00
" --export print values as environment keys \n "
2007-05-25 19:37:47 +04:00
" --help print this help text \n \n " ) ;
exit ( 0 ) ;
2003-11-13 17:34:36 +03:00
case ' i ' :
display_bus_id = 1 ;
break ;
case ' p ' :
if ( strcmp ( optarg , " 0x80 " ) = = 0 ) {
2005-09-14 22:23:48 +04:00
default_page_code = PAGE_80 ;
2003-11-13 17:34:36 +03:00
} else if ( strcmp ( optarg , " 0x83 " ) = = 0 ) {
2005-09-14 22:23:48 +04:00
default_page_code = PAGE_83 ;
} else if ( strcmp ( optarg , " pre-spc3-83 " ) = = 0 ) {
default_page_code = PAGE_83_PRE_SPC3 ;
2003-11-13 17:34:36 +03:00
} else {
2008-04-20 23:07:06 +04:00
info ( " Unknown page code '%s' \n " , optarg ) ;
2003-11-13 17:34:36 +03:00
return - 1 ;
}
break ;
2007-05-25 16:48:08 +04:00
case ' n ' :
ignore_sysfs = 1 ;
break ;
2003-11-13 17:34:36 +03:00
case ' s ' :
sys_specified = 1 ;
2006-01-18 06:24:48 +03:00
strncpy ( target , optarg , MAX_PATH_LEN ) ;
target [ MAX_PATH_LEN - 1 ] = ' \0 ' ;
2003-11-13 17:34:36 +03:00
break ;
2004-10-06 08:30:54 +04:00
case ' u ' :
reformat_serial = 1 ;
break ;
2005-06-27 04:51:49 +04:00
case ' x ' :
export = 1 ;
break ;
2003-11-13 17:34:36 +03:00
case ' v ' :
debug + + ;
break ;
case ' V ' :
2007-05-25 19:37:47 +04:00
printf ( " %s \n " , SCSI_ID_VERSION ) ;
2003-11-13 17:34:36 +03:00
exit ( 0 ) ;
break ;
default :
2007-05-25 19:37:47 +04:00
exit ( 1 ) ;
2003-11-13 17:34:36 +03:00
}
}
return 0 ;
}
2006-01-09 23:18:00 +03:00
static int per_dev_options ( struct sysfs_device * dev_scsi , int * good_bad , int * page_code )
2003-11-13 17:34:36 +03:00
{
int retval ;
int newargc ;
char * * newargv = NULL ;
int option ;
* good_bad = all_good ;
* page_code = default_page_code ;
2007-05-25 16:48:08 +04:00
retval = get_file_options ( vendor_str , model_str , & newargc , & newargv ) ;
2003-11-13 17:34:36 +03:00
2003-12-07 19:55:40 +03:00
optind = 1 ; /* reset this global extern */
2003-11-13 17:34:36 +03:00
while ( retval = = 0 ) {
2007-05-25 19:37:47 +04:00
option = getopt_long ( newargc , newargv , dev_short_options , options , NULL ) ;
2003-11-13 17:34:36 +03:00
if ( option = = - 1 )
break ;
if ( optarg )
2006-01-09 23:18:00 +03:00
dbg ( " option '%c' arg '%s' \n " , option , optarg ) ;
2003-11-13 17:34:36 +03:00
else
2006-01-09 23:18:00 +03:00
dbg ( " option '%c' \n " , option ) ;
2003-11-13 17:34:36 +03:00
switch ( option ) {
case ' b ' :
* good_bad = 0 ;
break ;
case ' g ' :
* good_bad = 1 ;
break ;
case ' p ' :
if ( strcmp ( optarg , " 0x80 " ) = = 0 ) {
2005-09-14 22:23:48 +04:00
* page_code = PAGE_80 ;
2003-11-13 17:34:36 +03:00
} else if ( strcmp ( optarg , " 0x83 " ) = = 0 ) {
2005-09-14 22:23:48 +04:00
* page_code = PAGE_83 ;
} else if ( strcmp ( optarg , " pre-spc3-83 " ) = = 0 ) {
* page_code = PAGE_83_PRE_SPC3 ;
2003-11-13 17:34:36 +03:00
} else {
2008-04-20 23:07:06 +04:00
info ( " Unknown page code '%s' \n " , optarg ) ;
2003-11-13 17:34:36 +03:00
retval = - 1 ;
}
break ;
default :
2008-04-20 23:07:06 +04:00
info ( " Unknown or bad option '%c' (0x%x) \n " , option , option ) ;
2003-11-13 17:34:36 +03:00
retval = - 1 ;
break ;
}
}
2003-12-07 19:55:40 +03:00
if ( newargv ) {
free ( newargv [ 0 ] ) ;
2003-11-13 17:34:36 +03:00
free ( newargv ) ;
2003-12-07 19:55:40 +03:00
}
2003-11-13 17:34:36 +03:00
return retval ;
}
2007-05-25 16:48:08 +04:00
static int set_sysfs_values ( struct sysfs_device * dev_scsi )
{
const char * vendor , * model , * type ;
vendor = sysfs_attr_get_value ( dev_scsi - > devpath , " vendor " ) ;
if ( ! vendor ) {
2008-04-20 23:07:06 +04:00
info ( " %s: cannot get vendor attribute \n " , dev_scsi - > devpath ) ;
2007-05-25 16:48:08 +04:00
return - 1 ;
}
set_str ( vendor_str , vendor , sizeof ( vendor_str ) - 1 ) ;
model = sysfs_attr_get_value ( dev_scsi - > devpath , " model " ) ;
if ( ! model ) {
info ( " %s: cannot get model attribute \n " , dev_scsi - > devpath ) ;
return - 1 ;
}
set_str ( model_str , model , sizeof ( model_str ) - 1 ) ;
type = sysfs_attr_get_value ( dev_scsi - > devpath , " type " ) ;
if ( ! type ) {
2008-04-20 23:07:06 +04:00
info ( " %s: cannot get type attribute \n " , dev_scsi - > devpath ) ;
2007-05-25 16:48:08 +04:00
return - 1 ;
}
set_type ( type_str , type , sizeof ( type_str ) ) ;
type = sysfs_attr_get_value ( dev_scsi - > devpath , " rev " ) ;
if ( ! type ) {
info ( " %s: cannot get type attribute \n " , dev_scsi - > devpath ) ;
return - 1 ;
}
set_str ( revision_str , type , sizeof ( revision_str ) - 1 ) ;
return 0 ;
}
static int set_inq_values ( struct sysfs_device * dev_scsi , const char * path )
{
int retval ;
char vendor [ 8 ] , model [ 16 ] , type [ 4 ] , rev [ 4 ] ;
retval = scsi_std_inquiry ( dev_scsi , path , vendor , model , rev , type ) ;
if ( retval )
return retval ;
set_str ( vendor_str , vendor , 8 ) ;
set_str ( model_str , model , 16 ) ;
set_type ( type_str , type , sizeof ( type_str ) - 1 ) ;
set_str ( revision_str , rev , sizeof ( revision_str ) - 1 ) ;
return 0 ;
}
2004-10-06 08:30:54 +04:00
/*
* format_serial : replace to whitespaces by underscores for calling
* programs that use the serial for device naming ( multipath , Suse
* naming , etc . . . )
*/
static void format_serial ( char * serial )
{
2005-12-15 14:22:42 +03:00
char * p = serial , * q ;
2004-10-06 08:30:54 +04:00
2005-12-15 14:22:42 +03:00
q = p ;
2004-10-06 08:30:54 +04:00
while ( * p ! = ' \0 ' ) {
2005-12-15 14:22:42 +03:00
if ( isspace ( * p ) ) {
if ( q > serial & & q [ - 1 ] ! = ' _ ' ) {
* q = ' _ ' ;
q + + ;
}
} else {
* q = * p ;
q + + ;
}
2004-10-06 08:30:54 +04:00
p + + ;
}
2005-12-15 14:22:42 +03:00
* q = ' \0 ' ;
2004-10-06 08:30:54 +04:00
}
2003-11-13 17:34:36 +03:00
/*
* scsi_id : try to get an id , if one is found , printf it to stdout .
* returns a value passed to exit ( ) - 0 if printed an id , else 1. This
* could be expanded , for example , if we want to report a failure like no
* memory etc . return 2 , and return 1 for expected cases ( like broken
* device found ) that do not print an id .
*/
2006-01-09 23:18:00 +03:00
static int scsi_id ( const char * devpath , char * maj_min_dev )
2003-11-13 17:34:36 +03:00
{
int retval ;
int dev_type = 0 ;
2006-01-09 23:18:00 +03:00
struct sysfs_device * dev ;
2007-05-25 16:48:08 +04:00
struct sysfs_device * dev_scsi = NULL ;
2003-11-13 17:34:36 +03:00
int good_dev ;
int page_code ;
2006-10-10 18:56:09 +04:00
char serial [ MAX_SERIAL_LEN ] ;
char serial_short [ MAX_SERIAL_LEN ] ;
2007-05-25 19:37:47 +04:00
const char * bus_str = NULL ;
2003-11-13 17:34:36 +03:00
2006-01-09 23:18:00 +03:00
dbg ( " devpath %s \n " , devpath ) ;
2003-11-13 17:34:36 +03:00
2006-01-09 23:18:00 +03:00
dev = sysfs_device_get ( devpath ) ;
if ( dev = = NULL ) {
2008-04-20 23:07:06 +04:00
err ( " unable to access '%s' \n " , devpath ) ;
2004-02-28 12:00:36 +03:00
return 1 ;
}
2006-01-09 23:18:00 +03:00
if ( strcmp ( dev - > subsystem , " block " ) = = 0 )
dev_type = S_IFBLK ;
else
dev_type = S_IFCHR ;
2004-02-28 12:00:36 +03:00
2006-01-09 23:18:00 +03:00
/* mknod a temp dev to communicate with the device */
if ( ! dev_specified & & create_tmp_dev ( dev - > devpath , maj_min_dev , dev_type ) ) {
dbg ( " create_tmp_dev failed \n " ) ;
2003-11-13 17:34:36 +03:00
return 1 ;
}
2007-05-25 16:48:08 +04:00
if ( ! ignore_sysfs ) {
2007-05-25 19:37:47 +04:00
/* get scsi parent device */
dev_scsi = sysfs_device_get_parent_with_subsystem ( dev , " scsi " ) ;
if ( dev_scsi = = NULL ) {
2008-04-20 23:07:06 +04:00
err ( " unable to access parent device of '%s' \n " , devpath ) ;
2007-05-25 19:37:47 +04:00
return 1 ;
}
2007-05-25 16:48:08 +04:00
set_sysfs_values ( dev_scsi ) ;
2007-05-25 19:37:47 +04:00
bus_str = " scsi " ;
2007-05-25 16:48:08 +04:00
} else {
dev_scsi = dev ;
set_inq_values ( dev_scsi , maj_min_dev ) ;
}
2006-01-09 23:18:00 +03:00
/* get per device (vendor + model) options from the config file */
retval = per_dev_options ( dev_scsi , & good_dev , & page_code ) ;
2008-04-20 23:07:06 +04:00
dbg ( " per dev options: good %d; page code 0x%x \n " , good_dev , page_code ) ;
2003-11-13 17:34:36 +03:00
if ( ! good_dev ) {
retval = 1 ;
2006-01-09 23:18:00 +03:00
} else if ( scsi_get_serial ( dev_scsi , maj_min_dev , page_code ,
2006-10-10 18:56:09 +04:00
serial , serial_short , MAX_SERIAL_LEN ) ) {
2005-07-08 05:29:19 +04:00
retval = always_info ? 0 : 1 ;
2003-11-13 17:34:36 +03:00
} else {
retval = 0 ;
}
if ( ! retval ) {
2005-06-27 04:51:49 +04:00
if ( export ) {
2006-10-10 18:56:09 +04:00
char serial_str [ MAX_SERIAL_LEN ] ;
2005-06-27 04:51:49 +04:00
printf ( " ID_VENDOR=%s \n " , vendor_str ) ;
printf ( " ID_MODEL=%s \n " , model_str ) ;
2005-06-27 19:04:56 +04:00
printf ( " ID_REVISION=%s \n " , revision_str ) ;
2005-06-27 04:51:49 +04:00
set_str ( serial_str , serial , sizeof ( serial_str ) ) ;
printf ( " ID_SERIAL=%s \n " , serial_str ) ;
2006-10-10 18:56:09 +04:00
set_str ( serial_str , serial_short , sizeof ( serial_str ) ) ;
printf ( " ID_SERIAL_SHORT=%s \n " , serial_str ) ;
2005-06-27 19:04:56 +04:00
printf ( " ID_TYPE=%s \n " , type_str ) ;
2007-05-25 19:37:47 +04:00
if ( bus_str ! = NULL )
printf ( " ID_BUS=%s \n " , bus_str ) ;
2005-06-27 04:51:49 +04:00
} else {
if ( reformat_serial )
format_serial ( serial ) ;
if ( display_bus_id )
2006-08-19 18:06:25 +04:00
printf ( " %s: " , dev_scsi - > kernel ) ;
2005-06-27 04:51:49 +04:00
printf ( " %s \n " , serial ) ;
}
2006-01-09 23:18:00 +03:00
dbg ( " %s \n " , serial ) ;
2003-11-13 17:34:36 +03:00
retval = 0 ;
}
if ( ! dev_specified )
unlink ( maj_min_dev ) ;
return retval ;
}
int main ( int argc , char * * argv )
{
2006-01-09 23:18:00 +03:00
int retval = 0 ;
2006-01-18 06:24:48 +03:00
char devpath [ MAX_PATH_LEN ] ;
char maj_min_dev [ MAX_PATH_LEN ] ;
2003-11-13 17:34:36 +03:00
int newargc ;
2006-01-09 23:18:00 +03:00
const char * env ;
2003-11-13 17:34:36 +03:00
char * * newargv ;
2006-01-09 23:18:00 +03:00
logging_init ( " scsi_id " ) ;
sysfs_init ( ) ;
dbg ( " argc is %d \n " , argc ) ;
2003-11-13 17:34:36 +03:00
2006-01-09 23:18:00 +03:00
/* sysfs path can be overridden for testing */
env = getenv ( " SYSFS_PATH " ) ;
if ( env ) {
strncpy ( sysfs_path , env , sizeof ( sysfs_path ) ) ;
sysfs_path [ sizeof ( sysfs_path ) - 1 ] = ' \0 ' ;
} else
strcpy ( sysfs_path , " /sys " ) ;
2003-11-13 17:34:36 +03:00
2006-01-09 23:18:00 +03:00
env = getenv ( " DEVPATH " ) ;
if ( env ) {
2005-02-12 05:30:01 +03:00
hotplug_mode = 1 ;
2003-11-13 17:34:36 +03:00
sys_specified = 1 ;
2006-01-18 06:24:48 +03:00
strncpy ( devpath , env , MAX_PATH_LEN ) ;
2006-01-09 23:18:00 +03:00
devpath [ sizeof ( devpath ) - 1 ] = ' \0 ' ;
2003-11-13 17:34:36 +03:00
}
/*
2005-02-12 05:30:01 +03:00
* Get config file options .
2003-11-13 17:34:36 +03:00
*/
newargv = NULL ;
retval = get_file_options ( NULL , NULL , & newargc , & newargv ) ;
if ( retval < 0 ) {
2006-01-09 23:18:00 +03:00
retval = 1 ;
goto exit ;
}
if ( newargv & & ( retval = = 0 ) ) {
if ( set_options ( newargc , newargv , short_options , devpath ,
maj_min_dev ) < 0 ) {
retval = 2 ;
goto exit ;
}
2003-11-13 17:34:36 +03:00
free ( newargv ) ;
}
2006-01-09 23:18:00 +03:00
2005-02-12 05:30:01 +03:00
/*
* Get command line options ( overriding any config file or DEVPATH
* settings ) .
*/
2006-01-09 23:18:00 +03:00
if ( set_options ( argc , argv , short_options , devpath , maj_min_dev ) < 0 )
2004-06-26 12:18:17 +04:00
exit ( 1 ) ;
2003-11-13 17:34:36 +03:00
if ( ! sys_specified ) {
2007-05-29 23:26:01 +04:00
info ( " --devpath=<path> must be specified \n " ) ;
2006-01-09 23:18:00 +03:00
retval = 1 ;
goto exit ;
2003-11-13 17:34:36 +03:00
}
2006-01-09 23:18:00 +03:00
retval = scsi_id ( devpath , maj_min_dev ) ;
exit :
sysfs_cleanup ( ) ;
logging_close ( ) ;
return retval ;
2003-11-13 17:34:36 +03:00
}