2010-03-12 13 views

Antwort

22

Ich werde versuchen, meine Erfahrung in Bezug auf Speicher Laufwerk Seriennummer Abruf auf Linux zusammenzufassen.
Ich gehe davon aus Sie die Seriennummer der (per SCSI-Spezifikation) Speichergerät Identität wollen nicht die Seriennummer des USB-Geräts (per USB-Spezifikation unter Device Descriptor), diese zwei unterschiedliche Einheiten sind.

HINWEIS!
Die meisten Geräte neigen dazu, eine Seriennummer im USB-Controller zu implementieren und die Seriennummer der inneren SCSI-Platte nicht implementiert zu lassen.
Also, wenn Sie ein USB-Gerät der beste Weg, um eine Zeichenfolge aus der Device Descriptor (USB-Spezifikation) wie VendorId-ProductId-HardwareRevision-Serial
Im Folgenden werde ich zu erstellen, ist eindeutig identifizieren wollen beschreiben, wie die zum Abrufen SN des Speicherlaufwerks, wie gefragt.

Antriebe fallen in zwei Kategorien (eigentlich mehr, aber sie vereinfachen): ATA-like (hda, hdb ...) und SCSI-like (sda sdb ...). USB-Laufwerke fallen in die zweite Kategorie, sie heißen SCSI-Festplatten. In beiden Situationen ioctl Anrufe können verwendet werden, um die erforderlichen Informationen (in unserem Fall die Seriennummer) abrufen.

Für SCSI-Geräte (und dazu gehören USB-Laufwerke) der Linux-generische Treiber und seine API ist unter tldp dokumentiert.
Die Seriennummer auf SCSI-Geräten ist in der Vital Product Data (kurz: VPD) verfügbar und kann mit der SCSI Inquiry Command abgerufen werden. A commad Zeilenprogramm unter Linux, die diese VPD holen kann, ist sdparm:

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

Beachten Sie, dass nicht alle Geräte diese Seriennummer haben, ist der Markt mit cheep Imitationen überflutet und einige USB-Flash-Laufwerke Rückkehr seltsame Serien (für Beispiel mein Sandisk Cruzer gibt nur den Buchstaben "u" zurück. Um dies zu umgehen, wählen einige Leute einen eindeutigen Bezeichner, indem sie verschiedene Strings von VPD wie Product ID, Vendor ID und Serial Number mischen.

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

Aus Gründen der Vollständigkeit i auch den Code zur Verfügung stellen würde die Seriennummer für ATA-Geräte (hda, hdb ...) abgerufen werden. Dies funktioniert NICHT für USB-Geräte.

#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); 
    } 
} 
+0

Ausgezeichnete Beschreibung und ich mag die Trennung zwischen SCSI und ATA. –

+0

großartige Arbeit, die einzige fehlende Sache ist, wie zu bestimmen, ob ein Laufwerk ist SCSI oder IDE? – chacham15

+0

das ist großartig, aber es gibt einen Fehler, die Überprüfung auf ioctl Erfolg ist nicht genug, um zu überprüfen, dass Sie eine gültige Seriennummer ... Sie müssen auch überprüfen, ob if ((io_hdr.info & SG_INFO_OK_MASK)! = SG_INFO_OK) -1 zurückgeben ' – Geoffrey

0

Der beste Weg ist wahrscheinlich zu tun, was die Kommandozeilen-Tools (wieder, wahrscheinlich) tun: Überprüfen Sie die relevanten Dateien in /proc oder , aber aus C++ - Code.

2

Und dieses Stück Code erhalten Sie die USB-Seriennummer ... es ist nicht so technisch beeindruckend wie clyfe der , aber es scheint jedes Mal den Trick zu machen.

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


} 
+0

Ich habe das versucht und es hat zu falschen Ergebnissen geführt. – chacham15