9

C++ を使用して Linux で USB ドライブの s/n を特定する方法はありますか?

hwinfo -diskC++ でない場合、 and とは異なる方法はありhdparm -iますか?

4

4 に答える 4

22

Linuxでのストレージドライブのシリアル番号の取得に関する私の経験を要約してみます。USBデバイスのシリアル番号( Device DescriptorのUSB仕様による)ではなく、ストレージデバイスIDのシリアル番号(SCSI仕様による)
が必要だと思います。これら2つは異なるエンティティです。

知らせ!
ほとんどのデバイスは、USBコントローラーにシリアル番号を実装し、内部SCSIディスクのシリアル番号を実装しないままにする傾向があります。
したがって、USBデバイスを一意に識別したい場合は、VendorId-ProductId-HardwareRevision-SerialNumberのようなデバイス記述子(USB仕様)から文字列を作成するのが最善の方法です 。以下では、ストレージドライブのSNを取得する方法について説明します。 、尋ねられたように。

ドライブは2つのカテゴリに分類されます(実際にはもっと多くなりますが、単純化しましょう):ATAのようなもの(hda、hdb ...)とSCSIのようなもの(sda sdb ...)。USBドライブは2番目のカテゴリに分類され、SCSI接続ディスクと呼ばれます。どちらの状況でも、 ioctl呼び出しを使用して、必要な情報(この場合はシリアル番号)を取得できます。

SCSIデバイス(およびこれらにはUSBドライブが含まれます)の場合、Linux汎用ドライバーとそのAPIはtldpに記載されています。
SCSIデバイスのシリアル番号は、Vital Product Data(略称:VPD)内で入手でき、SCSIInquiryCommandを使用して取得できます。このVPDをフェッチできるLinuxのコンマラインユーティリティはsdparmです:

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

すべてのデバイスがこのシリアル番号を持っているわけではなく、市場はチープノックオフで溢れ、一部のUSBフラッシュディスクは奇妙なシリアルを返します(たとえば、私のsandisk cruzerは文字「u」だけを返します)。これを克服するために、製品ID、ベンダーID、シリアル番号など、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);
    }
}
于 2010-04-19T10:11:02.853 に答える
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);


}
于 2011-08-23T10:23:14.767 に答える
1

あなたにとっても興味深いものを見つけました:

/dev/* が USB デバイスかどうかを検出する方法は?

于 2011-02-19T09:12:51.477 に答える
0

おそらく最良の方法は、コマンドライン ツールと同じように (これもおそらく) 実行することです。関連するファイルを または のいずれ/procかで検査します/sysが、C++ コードから検査します。

于 2010-03-12T12:55:02.293 に答える