最近在用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一样简单的接口吧