2010-03-12 29 views
9

Có cách nào để xác định s/n của ổ đĩa USB trong linux bằng C++ không?Số sê-ri USB theo linux C++

Nếu không C++ có cách nào khác khác với hwinfo -diskhdparm -i?

Trả lời

22

Tôi sẽ cố gắng tóm tắt trải nghiệm của mình về truy xuất số sê-ri lưu trữ trên linux.
Tôi giả sử bạn muốn số serial của thiết bị lưu trữ sắc (theo đặc điểm kỹ thuật SCSI) không phải là số sê-ri của thiết bị USB (theo đặc điểm kỹ thuật USB dưới Device Descriptor), hai là thực thể khác nhau.

THÔNG BÁO!
Hầu hết các thiết bị có xu hướng triển khai số sê-ri trong Bộ điều khiển USB và để lại số sê-ri của đĩa SCSI bên trong chưa được thực hiện.
Vì vậy, nếu bạn muốn xác định duy nhất một USB-thiết bị cách tốt nhất là để tạo ra một chuỗi từ (đặc tả USB) Device Descriptor như VendorID-ProductID-HardwareRevision-serialNumber
Trong sau tôi sẽ mô tả làm thế nào để lấy lại SN của ổ lưu trữ, như được yêu cầu.

Ổ đĩa nằm trong 2 danh mục (thực tế hơn, nhưng đơn giản hóa): ATA giống (hda, hdb ...) và kiểu SCSI (sda sdb ...). Ổ đĩa USB thuộc loại thứ hai, chúng được gọi là Đĩa đính kèm SCSI. Trong cả hai trường hợp, ioctl cuộc gọi có thể được sử dụng để truy xuất thông tin bắt buộc (trong trường hợp của chúng tôi là số sê - ri).

Đối với Thiết bị SCSI (và các thiết bị này bao gồm ổ USB) trình điều khiển chung chung của Linux và API của nó được ghi lại tại tldp.
Số sê-ri trên thiết bị SCSI có sẵn bên trong Vital Product Data (viết tắt là VPD) và có thể truy xuất được bằng cách sử dụng SCSI Inquiry Command. Một tiện ích dòng commad trong linux có thể lấy VPD đây là sdparm:

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

Lưu ý rằng không phải tất cả các thiết bị có số sê-ri này, thị trường đang tràn ngập hàng nhái chim kêu, và một số đĩa usb flash trở serial lạ (đối với ví dụ: sandisk cruzer của tôi chỉ trả về chữ "u"). Để khắc phục điều này, một số người chọn tạo một định danh duy nhất bằng cách trộn các chuỗi khác nhau từ VPD như ID sản phẩm, ID nhà cung cấp và Số sê-ri.

Mã trong 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); 
} 

Vì lợi ích của completness tôi cũng sẽ cung cấp mã để lấy số serial cho thiết bị ATA (hda, hdb ...). Điều này sẽ KHÔNG làm việc cho các thiết bị 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); 
    } 
} 
+0

Mô tả tuyệt vời và tôi thích sự tách biệt giữa SCSI và ATA. –

+0

công việc tuyệt vời, điều duy nhất còn thiếu là làm thế nào để xác định xem một ổ đĩa là scsi hay ide? – chacham15

+0

điều này là tuyệt vời, nhưng có một lỗi, kiểm tra cho thành công ioctl là không đủ để xác minh bạn có một nối tiếp hợp lệ ... bạn cũng cần phải xác minh 'if ((io_hdr.info & SG_INFO_OK_MASK)! = SG_INFO_OK) return -1 ' – Geoffrey

0

Cách tốt nhất có thể là làm những gì các công cụ dòng lệnh (một lần nữa, có thể) thực hiện: kiểm tra các tệp có liên quan theo số /proc hoặc /sys, nhưng từ mã C++.

2

Và đoạn mã này sẽ nhận được số serial USB ... nó không phải là về mặt kỹ thuật ấn tượng như của clyfe , nhưng có vẻ như nó đã làm mọi thứ.

#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

Tôi đã thử điều này và nó tạo ra kết quả sai. – chacham15

Các vấn đề liên quan