chiark / gitweb /
add "Persistent Device Naming" rules file for disks
[elogind.git] / extras / scsi_id / scsi_serial.c
index ae83330c304466d7b293e52760540015dcb68b03..d1b1d94f53982e91136462d11a02907b1bad4789 100644 (file)
@@ -30,6 +30,7 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <syslog.h>
+#include <linux/compiler.h> /* need __user when built via klibc */
 #include <scsi/sg.h>
 #include <sysfs/libsysfs.h>
 #include "scsi_id.h"
@@ -88,6 +89,7 @@ static const char hex_str[]="0123456789abcdef";
 #define SG_ERR_CAT_RESET       2      /* interpreted from sense buffer */
 #define SG_ERR_CAT_TIMEOUT     3
 #define SG_ERR_CAT_RECOVERED   4  /* Successful command after recovered err */
+#define SG_ERR_CAT_NOTSUPPORTED 5 /* Illegal / unsupported command */
 #define SG_ERR_CAT_SENSE       98     /* Something else in the sense buffer */
 #define SG_ERR_CAT_OTHER       99     /* Some other error/warning */
 
@@ -129,6 +131,8 @@ static int sg_err_category_new(int scsi_status, int msg_status, int
                                        return SG_ERR_CAT_MEDIA_CHANGED;
                                if (0x29 == asc)
                                        return SG_ERR_CAT_RESET;
+                       } else if (sense_key == ILLEGAL_REQUEST) {
+                               return SG_ERR_CAT_NOTSUPPORTED;
                        }
                }
                return SG_ERR_CAT_SENSE;
@@ -290,35 +294,32 @@ static int scsi_dump(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
                return -1;
 }
 
-static int scsi_inquiry(struct sysfs_device *scsi_dev, int fd, unsigned
-                       char evpd, unsigned char page, unsigned char *buf,
-                       unsigned int buflen)
+static int scsi_inquiry(struct sysfs_device *scsi_dev, int fd,
+                       unsigned char evpd, unsigned char page,
+                       unsigned char *buf, unsigned int buflen)
 {
        unsigned char inq_cmd[INQUIRY_CMDLEN] =
                { INQUIRY_CMD, evpd, page, 0, buflen, 0 };
        unsigned char sense[SENSE_BUFF_LEN];
        struct sg_io_hdr io_hdr;
        int retval;
-       unsigned char *inq;
-       unsigned char *buffer;
        int retry = 3; /* rather random */
 
-       if (buflen > 255) {
+       if (buflen > SCSI_INQ_BUFF_LEN) {
                log_message(LOG_WARNING, "buflen %d too long\n", buflen);
                return -1;
        }
-       inq = malloc(OFFSET + sizeof (inq_cmd) + 512);
-       memset(inq, 0, OFFSET + sizeof (inq_cmd) + 512);
-       buffer = inq + OFFSET;
 
 resend:
+       dprintf("%s evpd %d, page 0x%x\n", scsi_dev->name, evpd, page);
+
        memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
        io_hdr.interface_id = 'S';
        io_hdr.cmd_len = sizeof(inq_cmd);
        io_hdr.mx_sb_len = sizeof(sense);
        io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
        io_hdr.dxfer_len = buflen;
-       io_hdr.dxferp = buffer;
+       io_hdr.dxferp = buf;
        io_hdr.cmdp = inq_cmd;
        io_hdr.sbp = sense;
        io_hdr.timeout = DEF_TIMEOUT;
@@ -326,12 +327,16 @@ resend:
        if (ioctl(fd, SG_IO, &io_hdr) < 0) {
                log_message(LOG_WARNING, "%s: ioctl failed: %s\n",
                            scsi_dev->name, strerror(errno));
-               return -1;
+               retval = -1;
+               goto error;
        }
 
        retval = sg_err_category3(&io_hdr);
 
        switch (retval) {
+               case SG_ERR_CAT_NOTSUPPORTED:
+                       buf[1] = 0;
+                       /* Fallthrough */
                case SG_ERR_CAT_CLEAN:
                case SG_ERR_CAT_RECOVERED:
                        retval = 0;
@@ -343,7 +348,6 @@ resend:
 
        if (!retval) {
                retval = buflen;
-               memcpy(buf, buffer, retval);
        } else if (retval > 0) {
                if (--retry > 0) {
                        dprintf("%s: Retrying ...\n", scsi_dev->name);
@@ -352,15 +356,21 @@ resend:
                retval = -1;
        }
 
-       free(inq);
+error:
+       if (retval < 0)
+               log_message(LOG_WARNING,
+                           "%s: Unable to get INQUIRY vpd %d page 0x%x.\n",
+                           scsi_dev->name, evpd, page);
+
        return retval;
 }
 
+/* Get list of supported EVPD pages */
 static int do_scsi_page0_inquiry(struct sysfs_device *scsi_dev, int fd,
-                                char *buffer, int len)
+                                unsigned char *buffer, unsigned int len)
 {
        int retval;
-       char vendor[MAX_ATTR_LEN];
+       struct sysfs_attribute *vendor;
 
        memset(buffer, 0, len);
        retval = scsi_inquiry(scsi_dev, fd, 1, 0x0, buffer, len);
@@ -390,14 +400,15 @@ static int do_scsi_page0_inquiry(struct sysfs_device *scsi_dev, int fd,
                 * If the vendor id appears in the page assume the page is
                 * invalid.
                 */
-               if (sysfs_get_attr(scsi_dev->path, "vendor", vendor,
-                                  MAX_ATTR_LEN)) {
+               vendor = sysfs_get_device_attr(scsi_dev, "vendor");
+               if (!vendor) {
                        log_message(LOG_WARNING,
                                    "%s: cannot get model attribute\n",
                                    scsi_dev->name);
                        return 1;
                }
-               if (!strncmp(&buffer[VENDOR_LENGTH], vendor, VENDOR_LENGTH)) {
+               if (!strncmp((char *)&buffer[VENDOR_LENGTH], vendor->value,
+                            VENDOR_LENGTH)) {
                        log_message(LOG_WARNING, "%s: invalid page0 data\n",
                                    scsi_dev->name);
                        return 1;
@@ -412,15 +423,16 @@ static int do_scsi_page0_inquiry(struct sysfs_device *scsi_dev, int fd,
  */
 static int prepend_vendor_model(struct sysfs_device *scsi_dev, char *serial)
 {
-       char attr[MAX_ATTR_LEN];
+       struct sysfs_attribute *attr;
        int ind;
 
-       if (sysfs_get_attr(scsi_dev->path, "vendor", attr, MAX_ATTR_LEN)) {
+       attr = sysfs_get_device_attr(scsi_dev, "vendor");
+       if (!attr) {
                log_message(LOG_WARNING, "%s: cannot get vendor attribute\n",
                            scsi_dev->name);
                return 1;
        }
-       strncpy(serial, attr, VENDOR_LENGTH);
+       strncpy(serial, attr->value, VENDOR_LENGTH);
        ind = strlen(serial) - 1;
        /*
         * Remove sysfs added newlines.
@@ -428,12 +440,13 @@ static int prepend_vendor_model(struct sysfs_device *scsi_dev, char *serial)
        if (serial[ind] == '\n')
                serial[ind] = '\0';
 
-       if (sysfs_get_attr(scsi_dev->path, "model", attr, MAX_ATTR_LEN)) {
+       attr = sysfs_get_device_attr(scsi_dev, "model");
+       if (!attr) {
                log_message(LOG_WARNING, "%s: cannot get model attribute\n",
                            scsi_dev->name);
                return 1;
        }
-       strncat(serial, attr, MODEL_LENGTH);
+       strncat(serial, attr->value, MODEL_LENGTH);
        ind = strlen(serial) - 1;
        if (serial[ind] == '\n')
                serial[ind] = '\0';
@@ -457,8 +470,9 @@ static int prepend_vendor_model(struct sysfs_device *scsi_dev, char *serial)
  * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill
  * serial number.
  **/
-static int check_fill_0x83_id(struct sysfs_device *scsi_dev, char
-                             *page_83, const struct scsi_id_search_values
+static int check_fill_0x83_id(struct sysfs_device *scsi_dev,
+                             unsigned char *page_83,
+                             const struct scsi_id_search_values
                              *id_search, char *serial, int max_len)
 {
        int i, j, len;
@@ -544,19 +558,21 @@ static int check_fill_0x83_id(struct sysfs_device *scsi_dev, char
        return 0;
 }
 
+/* Get device identification VPD page */
 static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
                                  char *serial, int len)
 {
        int retval;
-       int id_ind, j;
-       unsigned char page_83[256];
+       unsigned int id_ind, j;
+       unsigned char page_83[SCSI_INQ_BUFF_LEN];
 
-       memset(page_83, 0, 256);
-       retval = scsi_inquiry(scsi_dev, fd, 1, 0x83, page_83, 255);
+       memset(page_83, 0, SCSI_INQ_BUFF_LEN);
+       retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_83, page_83,
+                             SCSI_INQ_BUFF_LEN);
        if (retval < 0)
                return 1;
 
-       if (page_83[1] != 0x83) {
+       if (page_83[1] != PAGE_83) {
                log_message(LOG_WARNING, "%s: Invalid page 0x83\n",
                            scsi_dev->name);
                return 1;
@@ -578,8 +594,7 @@ static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
                 * Examine each descriptor returned. There is normally only
                 * one or a small number of descriptors.
                 */
-               for (j = 4; j <= page_83[3] + 3;
-                       j += page_83[j + 3] + 4) {
+               for (j = 4; j <= (unsigned int)page_83[3] + 3; j += page_83[j + 3] + 4) {
                        retval = check_fill_0x83_id(scsi_dev, &page_83[j],
                                                    &id_search_list[id_ind],
                                                    serial, len);
@@ -601,6 +616,75 @@ static int do_scsi_page83_inquiry(struct sysfs_device *scsi_dev, int fd,
        return 1;
 }
 
+/*
+ * Get device identification VPD page for older SCSI-2 device which is not
+ * compliant with either SPC-2 or SPC-3 format.
+ *
+ * Return the hard coded error code value 2 if the page 83 reply is not
+ * conformant to the SCSI-2 format.
+ */
+static int do_scsi_page83_prespc3_inquiry(struct sysfs_device *scsi_dev, int fd,
+                                         char *serial, int len)
+{
+       int retval;
+       int i, j;
+       unsigned char page_83[SCSI_INQ_BUFF_LEN];
+
+       memset(page_83, 0, SCSI_INQ_BUFF_LEN);
+       retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_83, page_83, SCSI_INQ_BUFF_LEN);
+       if (retval < 0)
+               return 1;
+
+       if (page_83[1] != PAGE_83) {
+               log_message(LOG_WARNING, "%s: Invalid page 0x83\n", scsi_dev->name);
+               return 1;
+       }
+       /*
+        * Model 4, 5, and (some) model 6 EMC Symmetrix devices return
+        * a page 83 reply according to SCSI-2 format instead of SPC-2/3.
+        *
+        * The SCSI-2 page 83 format returns an IEEE WWN in binary
+        * encoded hexi-decimal in the 16 bytes following the initial
+        * 4-byte page 83 reply header.
+        *
+        * Both the SPC-2 and SPC-3 formats return an IEEE WWN as part
+        * of an Identification descriptor.  The 3rd byte of the first
+        * Identification descriptor is a reserved (BSZ) byte field.
+        *
+        * Reference the 7th byte of the page 83 reply to determine
+        * whether the reply is compliant with SCSI-2 or SPC-2/3
+        * specifications.  A zero value in the 7th byte indicates
+        * an SPC-2/3 conformant reply, (i.e., the reserved field of the
+        * first Identification descriptor).  This byte will be non-zero
+        * for a SCSI-2 conformant page 83 reply from these EMC
+        * Symmetrix models since the 7th byte of the reply corresponds
+        * to the 4th and 5th nibbles of the 6-byte OUI for EMC, that is,
+        * 0x006048.
+        */
+       if (page_83[6] == 0)
+               return 2;
+
+       serial[0] = hex_str[id_search_list[0].id_type];
+       /*
+        * The first four bytes contain data, not a descriptor.
+        */
+       i = 4;
+       j = strlen(serial);
+       /*
+        * Binary descriptor, convert to ASCII,
+        * using two bytes of ASCII for each byte
+        * in the page_83.
+        */
+       while (i < (page_83[3]+4)) {
+               serial[j++] = hex_str[(page_83[i] & 0xf0) >> 4];
+               serial[j++] = hex_str[page_83[i] & 0x0f];
+               i++;
+       }
+       dprintf("using pre-spc3-83 for %s.\n", scsi_dev->name);
+       return 0;
+}
+
+/* Get unit serial number VPD page */
 static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
                                  char *serial, int max_len)
 {
@@ -608,14 +692,14 @@ static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
        int ser_ind;
        int i;
        int len;
-       unsigned char buf[256];
+       unsigned char buf[SCSI_INQ_BUFF_LEN];
 
-       memset(buf, 0, 256);
-       retval = scsi_inquiry(scsi_dev, fd, 1, 0x80, buf, 255);
+       memset(buf, 0, SCSI_INQ_BUFF_LEN);
+       retval = scsi_inquiry(scsi_dev, fd, 1, PAGE_80, buf, SCSI_INQ_BUFF_LEN);
        if (retval < 0)
                return retval;
 
-       if (buf[1] != 0x80) {
+       if (buf[1] != PAGE_80) {
                log_message(LOG_WARNING, "%s: Invalid page 0x80\n",
                            scsi_dev->name);
                return 1;
@@ -644,13 +728,11 @@ static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
 int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
                     int page_code, char *serial, int len)
 {
-       unsigned char page0[256];
+       unsigned char page0[SCSI_INQ_BUFF_LEN];
        int fd;
        int ind;
        int retval;
 
-       if (len > 255) {
-       }
        memset(serial, 0, len);
        dprintf("opening %s\n", devname);
        fd = open(devname, O_RDONLY | O_NONBLOCK);
@@ -660,7 +742,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
                return 1;
        }
 
-       if (page_code == 0x80) {
+       if (page_code == PAGE_80) {
                if (do_scsi_page80_inquiry(scsi_dev, fd, serial, len)) {
                        retval = 1;
                        goto completed;
@@ -668,7 +750,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
                        retval = 0;
                        goto completed;
                }
-       } else if (page_code == 0x83) {
+       } else if (page_code == PAGE_83) {
                if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
                        retval = 1;
                        goto completed;
@@ -676,6 +758,31 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
                        retval = 0;
                        goto completed;
                }
+       } else if (page_code == PAGE_83_PRE_SPC3) {
+               retval = do_scsi_page83_prespc3_inquiry(scsi_dev, fd, serial, len);
+               if (retval) {
+                       /*
+                        * Fallback to servicing a SPC-2/3 compliant page 83
+                        * inquiry if the page 83 reply format does not
+                        * conform to pre-SPC3 expectations.
+                        */
+                       if (retval == 2) {
+                               if (do_scsi_page83_inquiry(scsi_dev, fd, serial, len)) {
+                                       retval = 1;
+                                       goto completed;
+                               } else  {
+                                       retval = 0;
+                                       goto completed;
+                               }
+                       }
+                       else {
+                               retval = 1;
+                               goto completed;
+                       }
+               } else  {
+                       retval = 0;
+                       goto completed;
+               }
        } else if (page_code != 0x00) {
                log_message(LOG_WARNING, "%s: unsupported page code 0x%d\n",
                            scsi_dev->name, page_code);
@@ -686,7 +793,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
         * Get page 0, the page of the pages. By default, try from best to
         * worst of supported pages: 0x83 then 0x80.
         */
-       if (do_scsi_page0_inquiry(scsi_dev, fd, page0, 255)) {
+       if (do_scsi_page0_inquiry(scsi_dev, fd, page0, SCSI_INQ_BUFF_LEN)) {
                /*
                 * Don't try anything else. Black list if a specific page
                 * should be used for this vendor+model, or maybe have an
@@ -699,7 +806,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
        dprintf("%s: Checking page0\n", scsi_dev->name);
 
        for (ind = 4; ind <= page0[3] + 3; ind++)
-               if (page0[ind] == 0x83)
+               if (page0[ind] == PAGE_83)
                        if (!do_scsi_page83_inquiry(scsi_dev, fd, serial,
                                                    len)) {
                                /*
@@ -710,7 +817,7 @@ int scsi_get_serial (struct sysfs_device *scsi_dev, const char *devname,
                        }
 
        for (ind = 4; ind <= page0[3] + 3; ind++)
-               if (page0[ind] == 0x80)
+               if (page0[ind] == PAGE_80)
                        if (!do_scsi_page80_inquiry(scsi_dev, fd, serial,
                                                    len)) {
                                /*