读取scsi磁盘serial number

来自: http://blog.chinaunix.net/uid-8462735-id-1641638.html
 
最近在用C实现一个服务器磁盘管理工具,其中磁盘使用磁盘的序列号(Serial Number)信息与磁盘上的文件系统UUID共同作为磁盘的主键,用于检索磁盘的位置信息;

要获取磁盘的序列号信息,首先需要确定获取序列号的可行方法:
1. 从sysfs获取:scsi设备驱动将scsi的很多信息通过sysfs导出给用户使用了,如vendor, rev, model, modalias等,但是可惜的是它刚好漏了serial的信息;

2. 从/dev/disk/by-id/获取:在系统启动后,udevd会为每个scsi存储设备在/dev/disk/by-id/目录下创建一个符号连接,格式为scsi-SATA_<model>_<serial_number>;可是在经过/dev/disk/by-uuid/下各种异常后,我还是选择了放弃;

3. 使用工具获取:目前我知道的工具有scsi_id(之前一直使用),hdparm(在debian下一直使用),smartctl(项目组有同事移植了一个库版本的,但是接口没有导出),sg3-utils等;目前scsi_id潜在的问题是在suse10和suse11上scsi_id的位置居然不一样,参数貌似也变了;hdparm用的少,不确定是不是每个服务器上都有;sg3-utils没有用过;

4. 查看工具源代码,直接截取目标代码:scsi_id的代码还是算了吧,花了2个小时我连scsi_id获取序列号的相关数据结构都没有折腾清楚;
  hdparm的代码没仔细看,上网找了一个简单的例子: http://stackoverflow.com/questions/4193514/get-hard-disk-serial-number-using-python-on-linux
   static struct hd_driveid hd;
#include <linux/hdreg.h> int fd ;

if (( fd = open ( "/dev/hda" , O_RDONLY | O_NONBLOCK )) < 0 ) {
    printf
( "ERROR opening /dev/hdan" );
   
exit ( 1 );
}

if (! ioctl ( fd , HDIO_GET_IDENTITY , & hd )) {
    printf
( "%.20sn" , hd . serial_no );
} else if ( errno == - ENOMSG ) {
    printf
( "No serial number availablen" );
} else {
    perror
( "ERROR: HDIO_GET_IDENTITY" );
   
exit ( 1 );
}在debian上一切正常,在suse10上报错(参数错误);后来查了一下,是太老的内核不支持(2.6.20以后的应该都好了吧,以后再用);最后看到sg_inq的调用比较简单,代码如下:

<CODE/>
#define BUFSIZE (0xc000 + 0x80) 
char buff[BUFSIZE]; 
int main(int argc, char *argv[]) 
   int sg_fd; 
   int ret; 
   int len; 
   if (argc != 2) { 
     fprintf(stderr, "Usage: %s <device>n", argv[0]); 
     exit(1); 
   } 
   sg_fd = sg_cmds_open_device(argv[1], O_RDONLY, 1); 
   if (sg_fd < 0) { 
      fprintf(stderr, "sg_inq: error opening file: %s : %sn", argv[1], strerror(errno)); 
       exit(1); 
    } 
    memset(buff, 0, sizeof(buff)); 
    ret = sg_ll_inquiry(sg_fd, 0, 1, 0x80, buff, 252, 1, 1); 
    if (ret == 0) { 
       char obuff[BUFSIZE]; 
       len = (buff[2] << 8) + buff[3] + 4; 
       memset(obuff, 0, sizeof(obuff)); 
       len -= 4; 
       memcpy(obuff, buff+4, len); 
       printf("Unit serial number: %sn", obuff); 
    } 
    sg_cmds_close_device(sg_fd); 
     return 0; 
}
</CODE>

整个程序的核心是sg_ll_inquiry,但是麻烦的是要依赖/usr/lib64/libsgutils2.so文件
最后smartctl的代码和scsi_id的代码一样麻烦,但是smartctl的库已经移植过来了,希望能够直接导出一个 和sg_ll_inquiry一样简单的接口吧

你可能感兴趣的:(读取scsi磁盘serial number)