Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I'll try to summarize my experience regarding storage drive serial number retrieval on linux.<br> I assume you want the serial number of the <strong>storage device</strong> identity (as per SCSI specification) not the serial number of the <strong>USB device</strong> (as per USB specification under <a href="http://www.beyondlogic.org/usbnutshell/usb5.htm#DeviceDescriptors" rel="noreferrer">Device Descriptor</a> ), these two are different entities.</p> <blockquote> <p>NOTICE!<br> Most devices tend to implement a serial number in the USB-Controller and leave the serial number of the inner SCSI-disk unimplemented.<br> So if you want to uniquely identify an USB-device the best way is to create a string from the <a href="http://www.beyondlogic.org/usbnutshell/usb5.htm#DeviceDescriptors" rel="noreferrer">Device Descriptor</a> (USB specification) like <em>VendorId-ProductId-HardwareRevision-SerialNumber</em><br> In the following I shall describe how to retrieve the SN of the storage drive, as asked.</p> </blockquote> <p>Drives fall in 2 categories (actually more, but let's simplify): ATA-like (hda, hdb ...) and SCSI-like (sda sdb ...). USB drives fall in the second category, they are called <em>SCSI attached disks</em>. In both situation <a href="http://en.wikipedia.org/wiki/Ioctl" rel="noreferrer">ioctl</a> calls can be used to retrieve the required information (in our case the serial number).</p> <p>For <em>SCSI devices (and these include USB drives)</em> the Linux generic driver and it's API is documented at <a href="http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/index.html" rel="noreferrer">tldp</a>.<br> The serial number on SCSI devices is available inside the <a href="http://en.wikipedia.org/wiki/Vital_Product_Data" rel="noreferrer">Vital Product Data</a> (short: VPD) and is retrievable by using the <a href="http://en.wikipedia.org/wiki/SCSI_Inquiry_Command" rel="noreferrer">SCSI Inquiry Command</a>. A commad line utility in linux that can fetch this VPD is <a href="http://sg.danny.cz/sg/sdparm.html" rel="noreferrer">sdparm</a>:</p> <pre><code>&gt; yum install sdparm &gt; sdparm --quiet --page=sn /dev/sda Unit serial number VPD page: 3BT1ZQGR000081240XP7 </code></pre> <p>Note that not all devices have this serial number, the market is flooded with cheep knockoffs, and some usb flash disks return strange serials (for example my sandisk cruzer returns just the letter "u"). To overcome this some people choose to create a unique identifier by mixing different strings from VPD like Product ID, Vendor ID and Serial Number. </p> <p>Code in c:</p> <pre><code>#include &lt;stdlib.h&gt; #include &lt;stdio.h&gt; #include &lt;string.h&gt; #include &lt;unistd.h&gt; #include &lt;fcntl.h&gt; #include &lt;errno.h&gt; #include &lt;scsi/scsi.h&gt; #include &lt;scsi/sg.h&gt; #include &lt;sys/ioctl.h&gt; 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(&amp;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, &amp;io_hdr); if (result &lt; 0) return result; if ((io_hdr.info &amp; 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 &lt; 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 &lt; 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 *) &amp; scsi_serial[4]); } close(fd); return (EXIT_SUCCESS); } </code></pre> <p>For the sake of completness i'll also provide the code to retrieve the serial number for <strong>ATA devices</strong> (hda, hdb ...). This will NOT work for USB devices.</p> <pre><code>#include &lt;stdlib.h&gt; #include &lt;stdio.h&gt; #include &lt;sys/ioctl.h&gt; #include &lt;linux/hdreg.h&gt; #include &lt;fcntl.h&gt; #include &lt;errno.h&gt; #include &lt;string.h&gt; #include &lt;cctype&gt; #include &lt;unistd.h&gt; int main(){ struct hd_driveid *id; char *dev = "/dev/hda"; int fd; fd = open(dev, O_RDONLY|O_NONBLOCK); if(fd &lt; 0) { perror("cannot open"); } if (ioctl(fd, HDIO_GET_IDENTITY, id) &lt; 0) { close(fd); perror("ioctl error"); } else { // if we want to retrieve only for removable drives use this branching if ((id-&gt;config &amp; (1 &lt;&lt; 7)) || (id-&gt;command_set_1 &amp; 4)) { close(fd); printf("Serial Number: %s\n", id-&gt;serial_no); } else { perror("support not removable"); } close(fd); } } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload