2005-09-06 14:18:04 +04:00
/*
* edd_id - naming of BIOS disk devices via EDD
*
* Copyright ( C ) 2005 John Hull < John_Hull @ Dell . com >
* Copyright ( C ) 2005 Kay Sievers < kay . sievers @ vrfy . org >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation version 2 of the License .
*/
# ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
# endif
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
# include <fcntl.h>
# include <ctype.h>
2006-01-09 23:18:00 +03:00
# include <string.h>
2005-09-06 14:18:04 +04:00
# include <errno.h>
# include <dirent.h>
# include <stdint.h>
2008-07-30 03:45:23 +04:00
# include "../../udev/udev.h"
2005-09-06 14:18:04 +04:00
2008-09-06 17:45:31 +04:00
static void log_fn ( struct udev * udev , int priority ,
const char * file , int line , const char * fn ,
const char * format , va_list args )
2005-09-06 14:18:04 +04:00
{
vsyslog ( priority , format , args ) ;
}
int main ( int argc , char * argv [ ] )
{
2008-09-06 17:45:31 +04:00
struct udev * udev ;
2005-09-06 14:18:04 +04:00
const char * node = NULL ;
int i ;
int export = 0 ;
uint32_t disk_id ;
uint16_t mbr_valid ;
struct dirent * dent ;
int disk_fd ;
int sysfs_fd ;
DIR * dir = NULL ;
int rc = 1 ;
2006-05-10 19:11:00 +04:00
char match [ NAME_MAX ] = " " ;
2005-09-06 14:18:04 +04:00
2008-09-06 17:45:31 +04:00
udev = udev_new ( ) ;
if ( udev = = NULL )
goto exit ;
2005-09-06 14:18:04 +04:00
logging_init ( " edd_id " ) ;
2008-09-06 17:45:31 +04:00
udev_set_log_fn ( udev , log_fn ) ;
2005-09-06 14:18:04 +04:00
for ( i = 1 ; i < argc ; i + + ) {
char * arg = argv [ i ] ;
if ( strcmp ( arg , " --export " ) = = 0 ) {
export = 1 ;
} else
node = arg ;
}
2006-05-10 19:11:00 +04:00
if ( node = = NULL ) {
2008-09-06 17:45:31 +04:00
err ( udev , " no node specified \n " ) ;
2005-09-06 14:18:04 +04:00
fprintf ( stderr , " no node specified \n " ) ;
goto exit ;
}
/* check for kernel support */
dir = opendir ( " /sys/firmware/edd " ) ;
2006-05-10 19:11:00 +04:00
if ( dir = = NULL ) {
2008-09-06 17:45:31 +04:00
info ( udev , " no kernel EDD support \n " ) ;
2005-09-06 14:18:04 +04:00
fprintf ( stderr , " no kernel EDD support \n " ) ;
rc = 2 ;
goto exit ;
}
disk_fd = open ( node , O_RDONLY ) ;
if ( disk_fd < 0 ) {
2008-09-06 17:45:31 +04:00
info ( udev , " unable to open '%s' \n " , node ) ;
2005-09-06 14:18:04 +04:00
fprintf ( stderr , " unable to open '%s' \n " , node ) ;
rc = 3 ;
goto closedir ;
}
/* check for valid MBR signature */
if ( lseek ( disk_fd , 510 , SEEK_SET ) < 0 ) {
2008-09-06 17:45:31 +04:00
info ( udev , " seek to MBR validity failed '%s' \n " , node ) ;
2005-09-06 14:18:04 +04:00
rc = 4 ;
goto close ;
}
if ( read ( disk_fd , & mbr_valid , sizeof ( mbr_valid ) ) ! = sizeof ( mbr_valid ) ) {
2008-09-06 17:45:31 +04:00
info ( udev , " read MBR validity failed '%s' \n " , node ) ;
2005-09-06 14:18:04 +04:00
rc = 5 ;
goto close ;
}
if ( mbr_valid ! = 0xAA55 ) {
fprintf ( stderr , " no valid MBR signature '%s' \n " , node ) ;
2008-09-06 17:45:31 +04:00
info ( udev , " no valid MBR signature '%s' \n " , node ) ;
2005-09-06 14:18:04 +04:00
rc = 6 ;
goto close ;
}
/* read EDD signature */
if ( lseek ( disk_fd , 440 , SEEK_SET ) < 0 ) {
2008-09-06 17:45:31 +04:00
info ( udev , " seek to signature failed '%s' \n " , node ) ;
2005-09-06 14:18:04 +04:00
rc = 7 ;
goto close ;
}
if ( read ( disk_fd , & disk_id , sizeof ( disk_id ) ) ! = sizeof ( disk_id ) ) {
2008-09-06 17:45:31 +04:00
info ( udev , " read signature failed '%s' \n " , node ) ;
2005-09-06 14:18:04 +04:00
rc = 8 ;
goto close ;
}
/* all zero is invalid */
2008-09-06 17:45:31 +04:00
info ( udev , " read id 0x%08x from '%s' \n " , disk_id , node ) ;
2005-09-06 14:18:04 +04:00
if ( disk_id = = 0 ) {
fprintf ( stderr , " no EDD signature '%s' \n " , node ) ;
2008-09-06 17:45:31 +04:00
info ( udev , " '%s' signature is zero \n " , node ) ;
2005-09-06 14:18:04 +04:00
rc = 9 ;
goto close ;
}
/* lookup signature in sysfs to determine the name */
for ( dent = readdir ( dir ) ; dent ! = NULL ; dent = readdir ( dir ) ) {
2008-09-10 23:50:21 +04:00
char file [ UTIL_PATH_SIZE ] ;
2005-09-06 14:18:04 +04:00
char sysfs_id_buf [ 256 ] ;
uint32_t sysfs_id ;
ssize_t size ;
if ( dent - > d_name [ 0 ] = = ' . ' )
continue ;
snprintf ( file , sizeof ( file ) , " /sys/firmware/edd/%s/mbr_signature " , dent - > d_name ) ;
file [ sizeof ( file ) - 1 ] = ' \0 ' ;
sysfs_fd = open ( file , O_RDONLY ) ;
if ( sysfs_fd < 0 ) {
2008-09-06 17:45:31 +04:00
info ( udev , " unable to open sysfs '%s' \n " , file ) ;
2005-09-06 14:18:04 +04:00
continue ;
}
size = read ( sysfs_fd , sysfs_id_buf , sizeof ( sysfs_id_buf ) - 1 ) ;
close ( sysfs_fd ) ;
2006-05-10 19:11:00 +04:00
if ( size < = 0 ) {
2008-09-06 17:45:31 +04:00
info ( udev , " read sysfs '%s' failed \n " , file ) ;
2005-09-06 14:18:04 +04:00
continue ;
}
sysfs_id_buf [ size ] = ' \0 ' ;
2008-09-06 17:45:31 +04:00
info ( udev , " read '%s' from '%s' \n " , sysfs_id_buf , file ) ;
2005-09-06 14:18:04 +04:00
sysfs_id = strtoul ( sysfs_id_buf , NULL , 16 ) ;
2006-05-10 19:11:00 +04:00
/* look for matching value, that appears only once */
2005-09-06 14:18:04 +04:00
if ( disk_id = = sysfs_id ) {
2006-05-10 19:11:00 +04:00
if ( match [ 0 ] = = ' \0 ' ) {
/* store id */
2008-09-10 20:24:39 +04:00
util_strlcpy ( match , dent - > d_name , sizeof ( match ) ) ;
2005-09-16 23:10:12 +04:00
} else {
2006-05-10 19:11:00 +04:00
/* error, same signature for another device */
2008-09-06 17:45:31 +04:00
info ( udev , " '%s' does not have a unique signature \n " , node ) ;
2005-09-16 23:10:12 +04:00
fprintf ( stderr , " '%s' does not have a unique signature \n " , node ) ;
2006-05-10 19:11:00 +04:00
rc = 10 ;
2005-09-16 23:10:12 +04:00
goto exit ;
}
}
}
2006-05-10 19:11:00 +04:00
if ( match [ 0 ] ! = ' \0 ' ) {
if ( export )
printf ( " ID_EDD=%s \n " , match ) ;
else
printf ( " %s \n " , match ) ;
rc = 0 ;
}
2005-09-06 14:18:04 +04:00
close :
close ( disk_fd ) ;
closedir :
closedir ( dir ) ;
exit :
2008-09-06 17:45:31 +04:00
udev_unref ( udev ) ;
2005-09-06 14:18:04 +04:00
logging_close ( ) ;
return rc ;
}