简体   繁体   English

Linux C ++下的USB驱动器序列号

[英]USB-drive serial number under linux C++

Is there any way to determine s/n of usb-drive in linux using C++ ? 有没有办法用C ++确定linux中usb-drive的s / n?

If not C++ is there any other way different from hwinfo -disk and hdparm -i ? 如果不是C ++还有其他方式不同于hwinfo -diskhdparm -i

I'll try to summarize my experience regarding storage drive serial number retrieval on linux. 我将尝试总结一下我在linux上存储驱动器序列号检索的经验。
I assume you want the serial number of the storage device identity (as per SCSI specification) not the serial number of the USB device (as per USB specification under Device Descriptor ), these two are different entities. 我假设你想要存储设备标识的序列号(根据SCSI规范)而不是USB设备的序列号(根据设备描述符下的USB规范),这两个是不同的实体。

NOTICE! 注意!
Most devices tend to implement a serial number in the USB-Controller and leave the serial number of the inner SCSI-disk unimplemented. 大多数设备倾向于在USB控制器中实现序列号,并保留内部SCSI磁盘的序列号未实现。
So if you want to uniquely identify an USB-device the best way is to create a string from the Device Descriptor (USB specification) like VendorId-ProductId-HardwareRevision-SerialNumber 因此,如果您想要唯一地识别USB设备,最好的方法是从Device Descriptor (USB规范)创建一个字符串,如VendorId-ProductId-HardwareRevision-SerialNumber
In the following I shall describe how to retrieve the SN of the storage drive, as asked. 在下文中,我将描述如何检索存储驱动器的SN,如所要求的那样。

Drives fall in 2 categories (actually more, but let's simplify): ATA-like (hda, hdb ...) and SCSI-like (sda sdb ...). 驱动器分为两类(实际上更多,但让我们简化):类似ATA(hda,hdb ......)和类似SCSI(sda sdb ......)。 USB drives fall in the second category, they are called SCSI attached disks . USB驱动器属于第二类,它们被称为SCSI连接磁盘 In both situation ioctl calls can be used to retrieve the required information (in our case the serial number). 在这两种情况下, ioctl调用都可用于检索所需的信息(在我们的例子中是序列号)。

For SCSI devices (and these include USB drives) the Linux generic driver and it's API is documented at tldp . 对于SCSI设备(包括USB驱动器) ,Linux通用驱动程序及其API在tldp中有记录。
The serial number on SCSI devices is available inside the Vital Product Data (short: VPD) and is retrievable by using the SCSI Inquiry Command . SCSI设备上的序列号可在重要产品数据 (简称:VPD)中找到,并可使用SCSI查询命令检索。 A commad line utility in linux that can fetch this VPD is sdparm : linux中可以获取此VPD的commad行实用程序是sdparm

> yum install sdparm
> sdparm --quiet --page=sn /dev/sda
    Unit serial number VPD page:
    3BT1ZQGR000081240XP7

Note that not all devices have this serial number, the market is flooded with cheep knockoffs, and some usb flash disks return strange serials (for example my sandisk cruzer returns just the letter "u"). 请注意,并非所有设备都具有此序列号,市场充斥着吱吱声仿冒品,并且一些USB闪存盘返回奇怪的连续剧(例如我的sandisk cruzer返回字母“u”)。 To overcome this some people choose to create a unique identifier by mixing different strings from VPD like Product ID, Vendor ID and Serial Number. 为了克服这个问题,一些人选择通过混合VPD中的不同字符串来创建唯一标识符,如产品ID,供应商ID和序列号。

Code in c: 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);
}

For the sake of completness i'll also provide the code to retrieve the serial number for ATA devices (hda, hdb ...). 为了完整性,我还将提供代码来检索ATA设备的序列号(hda,hdb ...)。 This will NOT work for USB devices. 这不适用于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);
    }
}

And this piece of code will get the USB serial number... it's not as technically impressive as clyfe's, but it seems to do the trick every time. 这段代码将获得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);


}

I found something that would be also interesting for you: 我发现了一些对你来说也很有趣的东西:

" How to detect if /dev/* is a USB device? " 如何检测/ dev / *是否是USB设备?

最好的方法可能是执行命令行工具(可能再次)做的事情:检查/proc/sys的相关文件,但是从C ++代码中检查。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM