2003-11-13 17:34:36 +03:00
/*
* scsi_id . c
*
* Main section of the scsi_id program
*
* Copyright ( C ) IBM Corp . 2003
*
* 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 .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
* USA
*/
# 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>
# include <sys/stat.h>
2003-12-07 19:55:40 +03:00
# include <sysfs/libsysfs.h>
2004-01-17 09:03:50 +03:00
# include "scsi_id_version.h"
2003-11-13 17:34:36 +03:00
# include "scsi_id.h"
2003-12-07 19:55:40 +03:00
# ifndef SCSI_ID_VERSION
2003-11-13 17:34:36 +03:00
# warning No version
2003-12-07 19:55:40 +03:00
# define SCSI_ID_VERSION "unknown"
2003-11-13 17:34:36 +03:00
# endif
/*
* temporary names for mknod .
*/
2005-09-18 06:49:23 +04:00
# define TMP_DIR " / dev"
# define TMP_PREFIX "tmp-scsi"
2003-11-13 17:34:36 +03:00
2004-02-28 12:00:36 +03:00
/*
* XXX Note the ' e ' ( send output to stderr in all cases ) , and ' c ' ( callout )
* options are not supported , but other code is still left in place for
* now .
*/
2005-07-08 05:29:19 +04:00
static const char short_options [ ] = " abd:f:gip:s:uvVx " ;
2003-11-13 17:34:36 +03:00
/*
* Just duplicate per dev options .
*/
2004-02-28 12:00:36 +03:00
static const char dev_short_options [ ] = " bgp: " ;
2003-11-13 17:34:36 +03:00
char sysfs_mnt_path [ SYSFS_PATH_MAX ] ;
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 char * default_callout ;
static int dev_specified ;
static int sys_specified ;
2004-01-17 09:03:50 +03:00
static char config_file [ MAX_NAME_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 ;
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
void log_message ( int level , const char * format , . . . )
{
va_list args ;
if ( ! debug & & level = = LOG_DEBUG )
return ;
va_start ( args , format ) ;
if ( ! hotplug_mode | | use_stderr ) {
vfprintf ( stderr , format , args ) ;
} else {
static int logging_init = 0 ;
if ( ! logging_init ) {
2005-06-27 04:51:49 +04:00
openlog ( " scsi_id " , LOG_PID , LOG_DAEMON ) ;
2003-11-13 17:34:36 +03:00
logging_init = 1 ;
}
vsyslog ( level , format , args ) ;
}
va_end ( args ) ;
return ;
}
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
}
2005-02-26 10:51:17 +03:00
static int get_major_minor ( struct sysfs_class_device * class_dev , int * maj ,
int * min )
2004-01-17 09:03:50 +03:00
{
2005-02-26 10:51:17 +03:00
struct sysfs_attribute * dev_attr ;
2004-01-17 09:03:50 +03:00
2005-02-26 10:51:17 +03:00
dev_attr = sysfs_get_classdev_attr ( class_dev , " dev " ) ;
if ( ! dev_attr ) {
2003-11-13 17:34:36 +03:00
/*
* XXX This happens a lot , since sg has no dev attr .
2004-01-17 09:03:50 +03:00
* And now sysfsutils does not set a meaningful errno
* value . Someday change this back to a LOG_WARNING .
* And if sysfsutils changes , check for ENOENT and handle
* it separately .
2003-11-13 17:34:36 +03:00
*/
2004-02-28 12:00:36 +03:00
log_message ( LOG_DEBUG , " %s: could not get dev attribute: %s \n " ,
2005-02-26 10:51:17 +03:00
class_dev - > name , strerror ( errno ) ) ;
2003-11-13 17:34:36 +03:00
return - 1 ;
}
2005-02-26 10:51:17 +03:00
dprintf ( " dev value %s " , dev_attr - > value ) ; /* value has a trailing \n */
if ( sscanf ( dev_attr - > value , " %u:%u " , maj , min ) ! = 2 ) {
2003-11-13 17:34:36 +03:00
log_message ( LOG_WARNING , " %s: invalid dev major/minor \n " ,
2005-02-26 10:51:17 +03:00
class_dev - > name ) ;
2003-11-13 17:34:36 +03:00
return - 1 ;
}
return 0 ;
}
2005-02-26 10:51:17 +03:00
static int create_tmp_dev ( struct sysfs_class_device * class_dev , char * tmpdev ,
int dev_type )
2003-11-13 17:34:36 +03:00
{
2004-06-26 12:18:17 +04:00
int maj , min ;
2003-11-13 17:34:36 +03:00
2005-02-26 10:51:17 +03:00
dprintf ( " (%s) \n " , class_dev - > name ) ;
2003-11-13 17:34:36 +03:00
2005-02-26 10:51:17 +03:00
if ( get_major_minor ( class_dev , & maj , & min ) )
2003-11-13 17:34:36 +03:00
return - 1 ;
snprintf ( tmpdev , MAX_NAME_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
dprintf ( " tmpdev '%s' \n " , tmpdev ) ;
2004-06-26 12:18:17 +04:00
if ( mknod ( tmpdev , 0600 | dev_type , makedev ( maj , min ) ) ) {
2003-11-13 17:34:36 +03:00
log_message ( LOG_WARNING , " mknod failed: %s \n " , strerror ( errno ) ) ;
return - 1 ;
}
return 0 ;
}
static int has_sysfs_prefix ( const char * path , const char * prefix )
{
char match [ MAX_NAME_LEN ] ;
strncpy ( match , sysfs_mnt_path , MAX_NAME_LEN ) ;
strncat ( match , prefix , MAX_NAME_LEN ) ;
if ( strncmp ( path , match , strlen ( match ) ) = = 0 )
return 1 ;
else
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 ' .
*/
static int get_file_options ( char * vendor , char * model , int * argc ,
char * * * newargv )
{
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 ;
dprintf ( " vendor='%s'; model='%s' \n " , vendor , model ) ;
fd = fopen ( config_file , " r " ) ;
if ( fd = = NULL ) {
dprintf ( " can't open %s \n " , config_file ) ;
if ( errno = = ENOENT ) {
return 1 ;
} else {
log_message ( LOG_WARNING , " can't open %s: %s \n " ,
config_file , strerror ( errno ) ) ;
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 ) {
log_message ( LOG_WARNING , " Can't allocate memory. \n " ) ;
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 ' ) {
log_message ( LOG_WARNING ,
" Config file line %d too long. \n " , lineno ) ;
break ;
}
2003-11-13 17:34:36 +03:00
while ( isspace ( * buf ) )
buf + + ;
if ( * buf = = ' \0 ' )
/*
* blank or all whitespace line
*/
continue ;
if ( * buf = = ' # ' )
/*
* comment line
*/
continue ;
# ifdef LOTS
dprintf ( " lineno %d: '%s' \n " , lineno , buf ) ;
# endif
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 ;
}
dprintf ( " config file line %d: "
" 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 ) ) {
log_message ( LOG_WARNING ,
" Error parsing config file line %d '%s' \n " ,
lineno , buffer ) ;
retval = - 1 ;
break ;
}
if ( vendor = = NULL ) {
if ( vendor_in = = NULL ) {
dprintf ( " matched global option \n " ) ;
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 ) .
*/
dprintf ( " matched vendor/model \n " ) ;
break ;
} else {
dprintf ( " no match \n " ) ;
}
}
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 ) {
log_message ( LOG_WARNING ,
2003-12-07 19:55:40 +03:00
" 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
* alter freeing .
*/
( * newargv ) [ c ] = buffer ;
2003-11-13 17:34:36 +03:00
for ( c = 1 ; c < * argc ; c + + )
2003-12-07 19:55:40 +03:00
( * newargv ) [ c ] = strsep ( & buffer , " " ) ;
2003-11-13 17:34:36 +03:00
}
} else {
/*
* No matches .
*/
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
* file ) we have to reset this back to 1. [ Note glibc handles
* setting this to 0 , but klibc does not . ]
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 ) {
2005-08-13 02:18:44 +04:00
option = getopt ( argc , argv , short_opts ) ;
2003-11-13 17:34:36 +03:00
if ( option = = - 1 )
break ;
if ( optarg )
dprintf ( " option '%c' arg '%s' \n " , option , optarg ) ;
else
dprintf ( " option '%c' \n " , option ) ;
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 ' c ' :
default_callout = optarg ;
break ;
case ' d ' :
dev_specified = 1 ;
strncpy ( maj_min_dev , optarg , MAX_NAME_LEN ) ;
break ;
case ' e ' :
use_stderr = 1 ;
break ;
case ' f ' :
strncpy ( config_file , optarg , MAX_NAME_LEN ) ;
break ;
case ' g ' :
all_good = 1 ;
break ;
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 {
log_message ( LOG_WARNING ,
" Unknown page code '%s' \n " , optarg ) ;
return - 1 ;
}
break ;
case ' s ' :
sys_specified = 1 ;
strncpy ( target , sysfs_mnt_path , MAX_NAME_LEN ) ;
strncat ( target , optarg , MAX_NAME_LEN ) ;
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 ' :
log_message ( LOG_WARNING , " scsi_id version: %s \n " ,
2003-12-07 19:55:40 +03:00
SCSI_ID_VERSION ) ;
2003-11-13 17:34:36 +03:00
exit ( 0 ) ;
break ;
default :
log_message ( LOG_WARNING ,
" Unknown or bad option '%c' (0x%x) \n " ,
option , option ) ;
return - 1 ;
}
}
return 0 ;
}
2004-02-28 12:00:36 +03:00
static int per_dev_options ( struct sysfs_device * scsi_dev , int * good_bad ,
2003-11-13 17:34:36 +03:00
int * page_code , char * callout )
{
int retval ;
int newargc ;
char * * newargv = NULL ;
2005-06-27 19:04:56 +04:00
struct sysfs_attribute * vendor , * model , * type ;
2003-11-13 17:34:36 +03:00
int option ;
* good_bad = all_good ;
* page_code = default_page_code ;
if ( default_callout & & ( callout ! = default_callout ) )
strncpy ( callout , default_callout , MAX_NAME_LEN ) ;
else
callout [ 0 ] = ' \0 ' ;
2005-02-26 10:51:17 +03:00
vendor = sysfs_get_device_attr ( scsi_dev , " vendor " ) ;
if ( ! vendor ) {
2004-01-17 09:03:50 +03:00
log_message ( LOG_WARNING , " %s: cannot get vendor attribute \n " ,
2003-11-13 17:34:36 +03:00
scsi_dev - > name ) ;
return - 1 ;
}
2005-06-27 04:51:49 +04:00
set_str ( vendor_str , vendor - > value , sizeof ( vendor_str ) - 1 ) ;
2003-11-13 17:34:36 +03:00
2005-02-26 10:51:17 +03:00
model = sysfs_get_device_attr ( scsi_dev , " model " ) ;
if ( ! model ) {
2004-01-17 09:03:50 +03:00
log_message ( LOG_WARNING , " %s: cannot get model attribute \n " ,
2003-11-13 17:34:36 +03:00
scsi_dev - > name ) ;
return - 1 ;
}
2005-06-27 04:51:49 +04:00
set_str ( model_str , model - > value , sizeof ( model_str ) - 1 ) ;
2003-11-13 17:34:36 +03:00
2005-06-27 19:04:56 +04:00
type = sysfs_get_device_attr ( scsi_dev , " type " ) ;
if ( ! type ) {
log_message ( LOG_WARNING , " %s: cannot get type attribute \n " ,
scsi_dev - > name ) ;
return - 1 ;
}
2005-08-13 02:18:44 +04:00
set_type ( type_str , type - > value , sizeof ( type_str ) ) ;
2005-06-27 19:04:56 +04:00
type = sysfs_get_device_attr ( scsi_dev , " rev " ) ;
if ( ! type ) {
log_message ( LOG_WARNING , " %s: cannot get type attribute \n " ,
scsi_dev - > name ) ;
return - 1 ;
}
set_str ( revision_str , type - > value , sizeof ( revision_str ) - 1 ) ;
2005-02-26 10:51:17 +03:00
retval = get_file_options ( vendor - > value , model - > value , & 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 ) {
2003-12-07 19:55:40 +03:00
option = getopt ( newargc , newargv , dev_short_options ) ;
2003-11-13 17:34:36 +03:00
if ( option = = - 1 )
break ;
if ( optarg )
dprintf ( " option '%c' arg '%s' \n " , option , optarg ) ;
else
dprintf ( " option '%c' \n " , option ) ;
switch ( option ) {
case ' b ' :
* good_bad = 0 ;
break ;
case ' c ' :
strncpy ( callout , default_callout , MAX_NAME_LEN ) ;
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 {
log_message ( LOG_WARNING ,
" Unknown page code '%s' \n " , optarg ) ;
retval = - 1 ;
}
break ;
default :
log_message ( LOG_WARNING ,
" Unknown or bad option '%c' (0x%x) \n " ,
option , option ) ;
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 ;
}
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 )
{
char * p = serial ;
while ( * p ! = ' \0 ' ) {
if ( isspace ( * p ) )
* p = ' _ ' ;
p + + ;
}
}
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 .
*/
static int scsi_id ( const char * target_path , char * maj_min_dev )
{
int retval ;
int dev_type = 0 ;
2004-10-06 08:30:54 +04:00
char * serial , * unaligned_buf ;
2004-02-28 12:00:36 +03:00
struct sysfs_class_device * class_dev ; /* of target_path */
struct sysfs_class_device * class_dev_parent ; /* for partitions */
struct sysfs_device * scsi_dev ; /* the scsi_device */
2003-11-13 17:34:36 +03:00
int good_dev ;
int page_code ;
char callout [ MAX_NAME_LEN ] ;
dprintf ( " target_path %s \n " , target_path ) ;
/*
* Ugly : depend on the sysfs path to tell us whether this is a
* block or char device . This should probably be encoded in the
* " dev " along with the major / minor .
*/
if ( has_sysfs_prefix ( target_path , " /block " ) ) {
dev_type = S_IFBLK ;
} else if ( has_sysfs_prefix ( target_path , " /class " ) ) {
dev_type = S_IFCHR ;
} else {
if ( ! hotplug_mode ) {
log_message ( LOG_WARNING ,
" Non block or class device '%s' \n " ,
target_path ) ;
return 1 ;
} else {
/*
* Expected in some cases .
*/
dprintf ( " Non block or class device \n " ) ;
return 0 ;
}
}
2004-02-28 12:00:36 +03:00
class_dev = sysfs_open_class_device_path ( target_path ) ;
if ( ! class_dev ) {
log_message ( LOG_WARNING , " open class %s failed: %s \n " ,
target_path , strerror ( errno ) ) ;
return 1 ;
}
class_dev_parent = sysfs_get_classdev_parent ( class_dev ) ;
dprintf ( " class_dev 0x%p; class_dev_parent 0x%p \n " , class_dev ,
class_dev_parent ) ;
if ( class_dev_parent ) {
scsi_dev = sysfs_get_classdev_device ( class_dev_parent ) ;
} else {
scsi_dev = sysfs_get_classdev_device ( class_dev ) ;
}
/*
* The close of scsi_dev will close class_dev or class_dev_parent .
*/
/*
* We assume we are called after the device is completely ready ,
* so we don ' t have to loop here like udev . ( And we are usually
* called via udev . )
*/
if ( ! scsi_dev ) {
/*
* errno is not set if we can ' t find the device link , so
* don ' t print it out here .
*/
log_message ( LOG_WARNING , " Cannot find sysfs device associated with %s \n " ,
target_path ) ;
2003-11-13 17:34:36 +03:00
return 1 ;
2004-02-28 12:00:36 +03:00
}
2003-11-13 17:34:36 +03:00
/*
2004-02-28 12:00:36 +03:00
* Allow only scsi devices .
2003-11-13 17:34:36 +03:00
*
* Other block devices can support SG IO , but only ide - cd does , so
* for now , don ' t bother with anything else .
*/
2004-02-28 12:00:36 +03:00
if ( strcmp ( scsi_dev - > bus , " scsi " ) ! = 0 ) {
2003-11-13 17:34:36 +03:00
if ( hotplug_mode )
/*
* Expected in some cases .
*/
dprintf ( " %s is not a scsi device \n " , target_path ) ;
else
log_message ( LOG_WARNING , " %s is not a scsi device \n " ,
target_path ) ;
return 1 ;
}
/*
* mknod a temp dev to communicate with the device .
*/
2005-02-26 10:51:17 +03:00
if ( ! dev_specified & & create_tmp_dev ( class_dev , maj_min_dev ,
2003-11-13 17:34:36 +03:00
dev_type ) ) {
dprintf ( " create_tmp_dev failed \n " ) ;
return 1 ;
}
/*
* Get any per device ( vendor + model ) options from the config
* file .
*/
retval = per_dev_options ( scsi_dev , & good_dev , & page_code , callout ) ;
dprintf ( " per dev options: good %d; page code 0x%x; callout '%s' \n " ,
good_dev , page_code , callout ) ;
2004-10-06 08:30:54 +04:00
# define ALIGN 512
unaligned_buf = malloc ( MAX_SERIAL_LEN + ALIGN ) ;
2004-10-16 10:04:44 +04:00
serial = ( char * ) ( ( ( unsigned long ) unaligned_buf + ( ALIGN - 1 ) )
& ~ ( ALIGN - 1 ) ) ;
2004-10-06 08:30:54 +04:00
dprintf ( " buffer unaligned 0x%p; aligned 0x%p \n " , unaligned_buf , serial ) ;
# undef ALIGN
2003-11-13 17:34:36 +03:00
if ( ! good_dev ) {
retval = 1 ;
} else if ( callout [ 0 ] ! = ' \0 ' ) {
/*
2004-02-28 12:00:36 +03:00
* XXX Disabled for now ( ' c ' is not in any options [ ] ) .
2003-11-13 17:34:36 +03:00
*/
retval = 1 ;
} else if ( scsi_get_serial ( scsi_dev , maj_min_dev , page_code ,
serial , 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 ) {
static char serial_str [ 64 ] ;
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 ) ;
2005-06-27 19:04:56 +04:00
printf ( " ID_TYPE=%s \n " , type_str ) ;
2005-07-19 19:18:19 +04:00
printf ( " ID_BUS=scsi \n " ) ;
2005-06-27 04:51:49 +04:00
} else {
if ( reformat_serial )
format_serial ( serial ) ;
if ( display_bus_id )
printf ( " %s: " , scsi_dev - > name ) ;
printf ( " %s \n " , serial ) ;
}
2003-11-13 17:34:36 +03:00
dprintf ( " %s \n " , serial ) ;
retval = 0 ;
}
2004-02-28 12:00:36 +03:00
sysfs_close_device ( scsi_dev ) ;
2003-11-13 17:34:36 +03:00
if ( ! dev_specified )
unlink ( maj_min_dev ) ;
return retval ;
}
int main ( int argc , char * * argv )
{
int retval ;
char * devpath ;
char target_path [ MAX_NAME_LEN ] ;
char maj_min_dev [ MAX_NAME_LEN ] ;
int newargc ;
char * * newargv ;
if ( getenv ( " DEBUG " ) )
debug + + ;
dprintf ( " argc is %d \n " , argc ) ;
if ( sysfs_get_mnt_path ( sysfs_mnt_path , MAX_NAME_LEN ) ) {
log_message ( LOG_WARNING , " sysfs_get_mnt_path failed: %s \n " ,
strerror ( errno ) ) ;
exit ( 1 ) ;
}
2005-02-12 05:30:01 +03:00
devpath = getenv ( " DEVPATH " ) ;
if ( devpath ) {
2003-11-13 17:34:36 +03:00
/*
2005-02-12 05:30:01 +03:00
* This implies that we were invoked via udev or hotplug .
2003-11-13 17:34:36 +03:00
*/
2005-02-12 05:30:01 +03:00
hotplug_mode = 1 ;
2003-11-13 17:34:36 +03:00
sys_specified = 1 ;
strncpy ( target_path , sysfs_mnt_path , MAX_NAME_LEN ) ;
strncat ( target_path , devpath , MAX_NAME_LEN ) ;
}
/*
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 ) {
exit ( 1 ) ;
} else if ( newargv & & ( retval = = 0 ) ) {
2003-12-07 19:55:40 +03:00
if ( set_options ( newargc , newargv , short_options , target_path ,
maj_min_dev ) < 0 )
2003-11-13 17:34:36 +03:00
exit ( 1 ) ;
free ( newargv ) ;
}
2005-02-12 05:30:01 +03:00
/*
* Get command line options ( overriding any config file or DEVPATH
* settings ) .
*/
if ( set_options ( argc , argv , short_options , target_path ,
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 ) {
log_message ( LOG_WARNING , " -s must be specified \n " ) ;
exit ( 1 ) ;
}
retval = scsi_id ( target_path , maj_min_dev ) ;
exit ( retval ) ;
}