有没有办法用C ++确定linux中usb-drive的s / n?
如果不是C ++还有其他方式与hwinfo -disk
和hdparm -i
不同吗?
答案 0 :(得分:22)
我将总结一下我在linux上存储驱动器序列号检索的经验 我假设您需要存储设备标识的序列号(根据SCSI规范)而不是 USB设备的序列号(根据{{3}下的USB规范}),这两个是不同的实体。
注意!
大多数设备倾向于在USB控制器中实现序列号,并保留内部SCSI磁盘的序列号未实现 因此,如果您想唯一地识别USB设备,最好的方法是从Device Descriptor(USB规范)创建一个字符串,如 VendorId-ProductId-HardwareRevision-SerialNumber
在下文中,我将描述如何检索存储驱动器的SN,如所要求的那样。
驱动器分为两类(实际上更多,但让我们简化):类似ATA(hda,hdb ......)和类似SCSI(sda sdb ...)。 USB驱动器属于第二类,它们被称为 SCSI连接磁盘。 在这两种情况下,Device Descriptor次调用都可用于检索所需信息(在我们的例子中是序列号)。
对于 SCSI设备(包括USB驱动器) Linux通用驱动程序,它的API记录在ioctl。
SCSI设备上的序列号在tldp(短:VPD)内可用,可以使用Vital Product Data检索。
linux中可以获取此VPD的commad行实用程序是SCSI Inquiry Command:
> yum install sdparm
> sdparm --quiet --page=sn /dev/sda
Unit serial number VPD page:
3BT1ZQGR000081240XP7
请注意,并非所有设备都有这个序列号,市场充斥着吱吱声的仿冒品,而且一些usb闪存盘返回奇怪的连续剧(例如我的sandisk cruzer只返回字母“u”)。为了克服这个问题,一些人选择通过混合VPD中的不同字符串来创建唯一标识符,如产品ID,供应商ID和序列号。
c中的代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <sys/ioctl.h>
int scsi_get_serial(int fd, void *buf, size_t buf_len) {
// we shall retrieve page 0x80 as per http://en.wikipedia.org/wiki/SCSI_Inquiry_Command
unsigned char inq_cmd[] = {INQUIRY, 1, 0x80, 0, buf_len, 0};
unsigned char sense[32];
struct sg_io_hdr io_hdr;
int result;
memset(&io_hdr, 0, sizeof (io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmdp = inq_cmd;
io_hdr.cmd_len = sizeof (inq_cmd);
io_hdr.dxferp = buf;
io_hdr.dxfer_len = buf_len;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.sbp = sense;
io_hdr.mx_sb_len = sizeof (sense);
io_hdr.timeout = 5000;
result = ioctl(fd, SG_IO, &io_hdr);
if (result < 0)
return result;
if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK)
return 1;
return 0;
}
int main(int argc, char** argv) {
char *dev = "/dev/sda";
char scsi_serial[255];
int rc;
int fd;
fd = open(dev, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
perror(dev);
}
memset(scsi_serial, 0, sizeof (scsi_serial));
rc = scsi_get_serial(fd, scsi_serial, 255);
// scsi_serial[3] is the length of the serial number
// scsi_serial[4] is serial number (raw, NOT null terminated)
if (rc < 0) {
printf("FAIL, rc=%d, errno=%d\n", rc, errno);
} else
if (rc == 1) {
printf("FAIL, rc=%d, drive doesn't report serial number\n", rc);
} else {
if (!scsi_serial[3]) {
printf("Failed to retrieve serial for %s\n", dev);
return -1;
}
printf("Serial Number: %.*s\n", (size_t) scsi_serial[3], (char *) & scsi_serial[4]);
}
close(fd);
return (EXIT_SUCCESS);
}
为了完整性,我还将提供代码来检索 ATA设备(hda,hdb ...)的序列号。这不适用于USB设备。
#include <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <cctype>
#include <unistd.h>
int main(){
struct hd_driveid *id;
char *dev = "/dev/hda";
int fd;
fd = open(dev, O_RDONLY|O_NONBLOCK);
if(fd < 0) {
perror("cannot open");
}
if (ioctl(fd, HDIO_GET_IDENTITY, id) < 0) {
close(fd);
perror("ioctl error");
} else {
// if we want to retrieve only for removable drives use this branching
if ((id->config & (1 << 7)) || (id->command_set_1 & 4)) {
close(fd);
printf("Serial Number: %s\n", id->serial_no);
} else {
perror("support not removable");
}
close(fd);
}
}
答案 1 :(得分:2)
这段代码将获得USB序列号......它不像clyfe那样在技术上令人印象深刻,但它似乎每次都有这个技巧。
#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(int arg, char **argv) {
ssize_t len;
char buf[256], *p;
char buf2[256];
int i;
len = readlink("/sys/block/sdb", buf, 256);
buf[len] = 0;
// printf("%s\n", buf);
sprintf(buf2, "%s/%s", "/sys/block/", buf);
for (i=0; i<6; i++) {
p = strrchr(buf2, '/');
*p = 0;
}
// printf("%s\n", buf2);
strcat(buf2, "/serial");
// printf("opening %s\n", buf2);
int f = open(buf2, 0);
len = read(f, buf, 256);
if (len <= 0) {
perror("read()");
}
buf[len] = 0;
printf("serial: %s\n", buf);
}
答案 2 :(得分:1)
我找到了一些对你来说也很有趣的东西:
答案 3 :(得分:0)
最好的方法可能是执行命令行工具(可能再次)做的事情:检查/proc
或/sys
中的相关文件,但是来自C ++代码。