Серийный номер USB-накопителя под Linux C ++ - PullRequest
9 голосов
/ 12 марта 2010

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

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

Ответы [ 4 ]

22 голосов
/ 19 апреля 2010

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

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

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

Для устройств SCSI (включая USB-накопители) универсальный драйвер Linux и его API задокументированы в tldp .
Серийный номер на устройствах SCSI доступен в Vital Product Data (сокращение: VPD) и может быть получен с помощью SCSI Inquiry Command . Утилита запятой линии в Linux, которая может получить этот VPD: sdparm :

> 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);
    }
}
2 голосов
/ 23 августа 2011

И этот фрагмент кода получит серийный номер 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);


}
1 голос
/ 19 февраля 2011

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

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

0 голосов
/ 12 марта 2010

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

...