Серийный номер USB-накопителя под linux c++

есть ли способ определить s / n usb-накопителя в linux с помощью C++ ?

Если не C++, есть ли другой способ, отличный от hwinfo -disk и hdparm -i ?

4 ответов


я попытаюсь обобщить свой опыт в отношении поиска серийного номера накопителя в linux.
Я предполагаю, что вам нужен серийный номер запоминающее устройство идентичность (согласно спецификации SCSI) не серийный номер USB устройства (согласно спецификации USB под Дескриптор Устройства ), эти две разные сущности.

обратите внимание!
Большинство устройств, как правило, реализуют серийный номер в USB-контроллер и оставьте серийный номер внутреннего SCSI-диска неисполненным.
Поэтому, если вы хотите однозначно идентифицировать USB-устройство, лучший способ-создать строку из Дескриптор Устройства (спецификация USB) как VendorId-ProductId-HardwareRevision-SerialNumber
В следующем я опишу, как получить SN накопителя, как было предложено.

диски попадают в 2 категории (на самом деле больше, но давайте упрощение): ATA-like (hda, hdb ...) и SCSI-like (sda sdb ...). USB накопители попадают во вторую категорию, они называются SCSI подключенные диски. В обоих случаях функции ioctl вызовы могут быть использованы для получения необходимой информации (в нашем случае серийный номер).

на устройства SCSI (и они включают USB-накопители) общий драйвер Linux и его API документированы в tldp.
Серийный номер на устройствах SCSI доступно внутри Жизненно Важные Данные О Продукте (short: VPD) и извлекается с помощью команда запроса SCSI. Утилита commad line в linux, которая может получить этот VPD, -sdparm:

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

обратите внимание, что не все устройства имеют этот серийный номер, рынок наводнен подделками cheep, а некоторые USB-флеш-диски возвращают странные сериалы (например, мой sandisk cruzer возвращает только букву "u"). Чтобы преодолеть это, некоторые люди выбирают создайте уникальный идентификатор, смешав различные строки из VPD, такие как идентификатор продукта, идентификатор поставщика и серийный номер.

код в 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);
    }
}

и этот кусок кода получит серийный номер USB... технически это не так впечатляюще, как у Клайфа, но, кажется, это делает трюк каждый раз.

#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);


}

Я нашел кое-что, что было бы также интересно для вас:

"как определить, является ли/ dev / * USB-устройством?"


лучший способ, вероятно, сделать то, что делают инструменты командной строки (опять же, возможно): проверьте соответствующие файлы в любом /proc или /sys, но из кода C++.