chiark / gitweb /
fix usb_id and let scsi_id ignore "illegal request"
[elogind.git] / extras / scsi_id / scsi_serial.c
index 302429c..7bfa9d1 100644 (file)
@@ -30,8 +30,9 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <syslog.h>
+#include <linux/compiler.h> /* need __user when built via klibc */
 #include <scsi/sg.h>
-#include <sys/libsysfs.h>
+#include <sysfs/libsysfs.h>
 #include "scsi_id.h"
 #include "scsi.h"
 
@@ -70,12 +71,6 @@ static const struct scsi_id_search_values id_search_list[] = {
 static const char hex_str[]="0123456789abcdef";
 
 /*
- * XXX maybe move all these to an sg_io.c file.
- *
- * From here ...
- */
-
-/*
  * Values returned in the result/status, only the ones used by the code
  * are used here.
  */
@@ -94,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 */
 
@@ -135,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;
@@ -159,8 +157,7 @@ static int sg_err_category3(struct sg_io_hdr *hp)
                                   hp->sbp, hp->sb_len_wr);
 }
 
-static int scsi_dump_sense(struct sysfs_class_device *scsi_dev,
-                          struct sg_io_hdr *io)
+static int scsi_dump_sense(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
 {
        unsigned char *sense_buffer;
        int s;
@@ -256,7 +253,7 @@ static int scsi_dump_sense(struct sysfs_class_device *scsi_dev,
                                    scsi_dev->name,  sense_buffer[0],
                                    sense_buffer[2]);
                log_message(LOG_WARNING,
-                           "%s: non-extended sense class %d code 0x%0x ",
+                           "%s: non-extended sense class %d code 0x%0x\n",
                            scsi_dev->name, sense_class, code);
 
        }
@@ -276,7 +273,7 @@ static int scsi_dump_sense(struct sysfs_class_device *scsi_dev,
        return -1;
 }
 
-static int scsi_dump(struct sysfs_class_device *scsi_dev, struct sg_io_hdr *io)
+static int scsi_dump(struct sysfs_device *scsi_dev, struct sg_io_hdr *io)
 {
        if (!io->status && !io->host_status && !io->msg_status &&
            !io->driver_status) {
@@ -297,48 +294,49 @@ static int scsi_dump(struct sysfs_class_device *scsi_dev, struct sg_io_hdr *io)
                return -1;
 }
 
-static int scsi_inquiry(struct sysfs_class_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;
 
        if (ioctl(fd, SG_IO, &io_hdr) < 0) {
-               log_message(LOG_WARNING, "%s ioctl failed: %s\n",
+               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;
@@ -350,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);
@@ -359,21 +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;
 }
 
-/*
- * XXX maybe move all these to an sg_io.c file.
- *
- * Ending here.
- */
-
-int do_scsi_page0_inquiry(struct sysfs_class_device *scsi_dev, int fd,
-                         char *buffer, int len)
+/* Get list of supported EVPD pages */
+static int do_scsi_page0_inquiry(struct sysfs_device *scsi_dev, int fd,
+                                char *buffer, int len)
 {
        int retval;
-       char *vendor;
+       struct sysfs_attribute *vendor;
 
        memset(buffer, 0, len);
        retval = scsi_inquiry(scsi_dev, fd, 1, 0x0, buffer, len);
@@ -386,7 +383,7 @@ int do_scsi_page0_inquiry(struct sysfs_class_device *scsi_dev, int fd,
                return 1;
        }
        if (buffer[3] > len) {
-               log_message(LOG_WARNING, "%s: page 0 buffer too long %d",
+               log_message(LOG_WARNING, "%s: page 0 buffer too long %d\n",
                           scsi_dev->name,  buffer[3]);
                return 1;
        }
@@ -403,14 +400,16 @@ int do_scsi_page0_inquiry(struct sysfs_class_device *scsi_dev, int fd,
                 * If the vendor id appears in the page assume the page is
                 * invalid.
                 */
-               vendor = sysfs_get_attr(scsi_dev, "vendor");
+               vendor = sysfs_get_device_attr(scsi_dev, "vendor");
                if (!vendor) {
-                       log_message(LOG_WARNING, "%s: no vendor attribute\n",
+                       log_message(LOG_WARNING,
+                                   "%s: cannot get model attribute\n",
                                    scsi_dev->name);
                        return 1;
                }
-               if (!strncmp(&buffer[VENDOR_LENGTH], vendor, VENDOR_LENGTH)) {
-                       log_message(LOG_WARNING, "%s invalid page0 data\n",
+               if (!strncmp(&buffer[VENDOR_LENGTH], vendor->value,
+                            VENDOR_LENGTH)) {
+                       log_message(LOG_WARNING, "%s: invalid page0 data\n",
                                    scsi_dev->name);
                        return 1;
                }
@@ -422,19 +421,18 @@ int do_scsi_page0_inquiry(struct sysfs_class_device *scsi_dev, int fd,
  * The caller checks that serial is long enough to include the vendor +
  * model.
  */
-static int prepend_vendor_model(struct sysfs_class_device *scsi_dev,
-                               char *serial)
+static int prepend_vendor_model(struct sysfs_device *scsi_dev, char *serial)
 {
-       char *attr;
+       struct sysfs_attribute *attr;
        int ind;
 
-       attr = sysfs_get_attr(scsi_dev, "vendor");
+       attr = sysfs_get_device_attr(scsi_dev, "vendor");
        if (!attr) {
-               log_message(LOG_WARNING, "%s: no vendor attribute\n",
+               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.
@@ -442,13 +440,13 @@ static int prepend_vendor_model(struct sysfs_class_device *scsi_dev,
        if (serial[ind] == '\n')
                serial[ind] = '\0';
 
-       attr = sysfs_get_attr(scsi_dev, "model");
+       attr = sysfs_get_device_attr(scsi_dev, "model");
        if (!attr) {
-               log_message(LOG_WARNING, "%s: no model attribute\n",
+               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';
@@ -472,10 +470,9 @@ static int prepend_vendor_model(struct sysfs_class_device *scsi_dev,
  * check_fill_0x83_id - check the page 0x83 id, if OK allocate and fill
  * serial number.
  **/
-static int check_fill_0x83_id(struct sysfs_class_device *scsi_dev,
-                             char *page_83,
-                             const struct scsi_id_search_values *id_search,
-                             char *serial, int max_len)
+static int check_fill_0x83_id(struct sysfs_device *scsi_dev, char
+                             *page_83, const struct scsi_id_search_values
+                             *id_search, char *serial, int max_len)
 {
        int i, j, len;
 
@@ -527,8 +524,10 @@ static int check_fill_0x83_id(struct sysfs_class_device *scsi_dev,
        serial[0] = hex_str[id_search->id_type];
 
        /*
-        * Prepend the vendor and model before the id since if it is not
-        * unique across all vendors and models.
+        * For SCSI_ID_VENDOR_SPECIFIC prepend the vendor and model before
+        * the id since it is not unique across all vendors and models,
+        * this differs from SCSI_ID_T10_VENDOR, where the vendor is
+        * included in the identifier.
         */
        if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC)
                if (prepend_vendor_model(scsi_dev, &serial[1]) < 0) {
@@ -558,15 +557,17 @@ static int check_fill_0x83_id(struct sysfs_class_device *scsi_dev,
        return 0;
 }
 
-static int do_scsi_page83_inquiry(struct sysfs_class_device *scsi_dev, int fd,
+/* 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, 0x83, page_83,
+                             SCSI_INQ_BUFF_LEN);
        if (retval < 0)
                return 1;
 
@@ -575,6 +576,12 @@ static int do_scsi_page83_inquiry(struct sysfs_class_device *scsi_dev, int fd,
                            scsi_dev->name);
                return 1;
        }
+       
+       /*
+        * XXX Some devices (IBM 3542) return all spaces for an identifier if
+        * the LUN is not actually configured. This leads to identifers of
+        * the form: "1            ".
+        */
 
        /*
         * Search for a match in the prioritized id_search_list.
@@ -586,8 +593,7 @@ static int do_scsi_page83_inquiry(struct sysfs_class_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);
@@ -609,17 +615,18 @@ static int do_scsi_page83_inquiry(struct sysfs_class_device *scsi_dev, int fd,
        return 1;
 }
 
-int do_scsi_page80_inquiry(struct sysfs_class_device *scsi_dev, int fd,
-                          char *serial, int max_len)
+/* Get unit serial number VPD page */
+static int do_scsi_page80_inquiry(struct sysfs_device *scsi_dev, int fd,
+                                 char *serial, int max_len)
 {
        int retval;
        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, 0x80, buf, SCSI_INQ_BUFF_LEN);
        if (retval < 0)
                return retval;
 
@@ -649,21 +656,19 @@ int do_scsi_page80_inquiry(struct sysfs_class_device *scsi_dev, int fd,
        return 0;
 }
 
-int scsi_get_serial (struct sysfs_class_device *scsi_dev, const char *devname,
+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);
+       fd = open(devname, O_RDONLY | O_NONBLOCK);
        if (fd < 0) {
-               log_message(LOG_WARNING, "%s cannot open %s: %s\n",
+               log_message(LOG_WARNING, "%s: cannot open %s: %s\n",
                            scsi_dev->name, devname, strerror(errno));
                return 1;
        }
@@ -685,7 +690,7 @@ int scsi_get_serial (struct sysfs_class_device *scsi_dev, const char *devname,
                        goto completed;
                }
        } else if (page_code != 0x00) {
-               log_message(LOG_WARNING, "%s unsupported page code 0x%d\n",
+               log_message(LOG_WARNING, "%s: unsupported page code 0x%d\n",
                            scsi_dev->name, page_code);
                return 1;
        }
@@ -694,7 +699,7 @@ int scsi_get_serial (struct sysfs_class_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
@@ -730,6 +735,7 @@ int scsi_get_serial (struct sysfs_class_device *scsi_dev, const char *devname,
        retval = 1;
 completed:
        if (close(fd) < 0)
-               log_message(LOG_WARNING, "close failed: %s", strerror(errno));
+               log_message(LOG_WARNING, "%s: close failed: %s\n", 
+                           scsi_dev->name, strerror(errno));
        return retval;
 }