See sg3_utils-1.33 -> 34 /ChangeLog
+  - sg_inq: add --export so sg_inq can replace udev's scsi_id

This backport is just enough to provide the required info for the udev rule.

References: bnc#805059

---
 src/sg_inq.c |  327 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 308 insertions(+), 19 deletions(-)

Index: sg3_utils-1.31/src/sg_inq.c
===================================================================
--- sg3_utils-1.31.orig/src/sg_inq.c
+++ sg3_utils-1.31/src/sg_inq.c
@@ -171,6 +171,7 @@ static struct option long_options[] = {
 #endif
         {"cmddt", 0, 0, 'c'},
         {"descriptors", 0, 0, 'd'},
+        {"export", 0, 0, 'u'},
         {"extended", 0, 0, 'x'},
         {"help", 0, 0, 'h'},
         {"hex", 0, 0, 'H'},
@@ -191,6 +192,7 @@ struct opts_t {
     int do_ata;
     int do_cmddt;
     int do_descriptors;
+    int do_export;
     int do_help;
     int do_hex;
     int do_raw;
@@ -335,10 +337,10 @@ process_cl_new(struct opts_t * optsp, in
         int option_index = 0;
 
 #ifdef SG_LIB_LINUX
-        c = getopt_long(argc, argv, "acdeEhHil:m:NOp:rvVx", long_options,
+        c = getopt_long(argc, argv, "acdeEhHil:m:NOp:ruvVx", long_options,
                         &option_index);
 #else
-        c = getopt_long(argc, argv, "cdeEhHil:m:NOp:rvVx", long_options,
+        c = getopt_long(argc, argv, "cdeEhHil:m:NOp:ruvVx", long_options,
                         &option_index);
 #endif
         if (c == -1)
@@ -401,6 +403,9 @@ process_cl_new(struct opts_t * optsp, in
         case 'r':
             ++optsp->do_raw;
             break;
+        case 'u':
+            ++optsp->do_export;
+            break;
         case 'v':
             ++optsp->do_verbose;
             break;
@@ -520,6 +525,9 @@ process_cl_old(struct opts_t * optsp, in
                     ++optsp->do_vpd;
                     ++optsp->num_pages;
                     break;
+                case 'u':
+                    ++optsp->do_export;
+                    break;
                 case 'v':
                     ++optsp->do_verbose;
                     break;
@@ -636,6 +644,67 @@ dStrRaw(const char* str, int len)
         printf("%c", str[k]);
 }
 
+/* Strip initial and trailing whitespaces; convert one or repeated
+ * whitespaces to a single "_"; convert non-printable characters to "."
+ * and if there are no valid (i.e. printable) characters return 0.
+ * Process 'str' in place (i.e. it's input and output) and return the
+ * length of the output, excluding the trailing '\0'.
+ */
+static int
+encode_whitespaces(unsigned char *str, int inlen)
+{
+    int k, res;
+    int j = 0;
+    int valid = 0;
+    int outlen = inlen;
+
+    /* Skip initial whitespaces */
+    while (isblank(str[j]))
+        j++;
+    k = j;
+    /* Strip trailing whitespaces */
+    while ((outlen > k) &&
+           (isblank(str[outlen - 1]) || ('\0' == str[outlen - 1]))) {
+        str[outlen - 1] = '\0';
+        outlen--;
+    }
+    for (res = 0; k < outlen; ++k) {
+        if (isblank(str[k])) {
+            if ((res > 0) && ('_' != str[res - 1])) {
+                str[res++] = '_';
+                valid++;
+            }
+        } else if (! isprint(str[k]))
+            str[res++] = '.';
+        else {
+            str[res++] = str[k];
+            valid++;
+        }
+    }
+    if (! valid)
+        res = 0;
+    str[res] = '\0';
+    return res;
+}
+
+static int
+encode_string(char *out, const unsigned char *in, int inlen)
+{
+    int i, j = 0;
+
+    for (i = 0; (i < inlen); ++i) {
+        if (isblank(in[i]) || !isprint(in[i])) {
+            sprintf(&out[j], "\\x%02x", in[i]);
+            j += 4;
+        } else {
+            out[j] = in[i];
+            j++;
+        }
+    }
+    out[j] = '\0';
+    return j;
+}
+
 struct vpd_name {
     int number;
     int peri_type;
@@ -1161,6 +1230,183 @@ decode_dev_ids(const char * leadin, unsi
         fprintf(stderr, "%s VPD page error: around offset=%d\n", leadin, off);
 }
 
+static void
+export_dev_ids(unsigned char * buff, int len)
+{
+    int u, j, m, id_len, c_set, assoc, desig_type, i_len;
+    int off, d_id, naa, k;
+    unsigned char * ucp;
+    unsigned char * ip;
+    const char * assoc_str;
+
+    if (buff[2] != 0) {
+        /*
+         * Cf decode_dev_ids() for details
+         */
+        i_len = len;
+        ip = buff;
+        c_set = 1;
+        assoc = 0;
+        desig_type = 3;
+        j = 1;
+        off = 16;
+        goto decode;
+    }
+
+    for (j = 1, off = -1;
+         (u = sg_vpd_dev_id_iter(buff, len, &off, -1, -1, -1)) == 0;
+         ++j) {
+        ucp = buff + off;
+        i_len = ucp[3];
+        id_len = i_len + 4;
+        if ((off + id_len) > len) {
+            fprintf(stderr, "Device Identification VPD page error: designator "
+                    "length longer than\n     remaining response length=%d\n",
+                    (len - off));
+            return;
+        }
+        ip = ucp + 4;
+        c_set = (ucp[0] & 0xf);
+        assoc = ((ucp[1] >> 4) & 0x3);
+        desig_type = (ucp[1] & 0xf);
+  decode:
+        switch (assoc) {
+            case 0:
+                assoc_str = "LUN";
+                break;
+            case 1:
+                assoc_str = "PORT";
+                break;
+            case 2:
+                assoc_str = "TARGET";
+                break;
+            default:
+                fprintf(stderr, "    Invalid association %d\n", assoc);
+                return;
+        }
+        switch (desig_type) {
+        case 0: /* vendor specific */
+            k = 0;
+            if ((1 == c_set) || (2 == c_set)) { /* ASCII or UTF-8 */
+                k = encode_whitespaces(ip, i_len);
+                if (k >= i_len)
+                    k = 1;
+            }
+            if (k)
+                printf("SCSI_IDENT_%s_VENDOR=%.*s\n", assoc_str, k, ip);
+            break;
+        case 1: /* T10 vendor identification */
+            k = encode_whitespaces(ip, i_len);
+            printf("SCSI_IDENT_%s_T10=%.*s\n", assoc_str, k, ip);
+            break;
+        case 2: /* EUI-64 based */
+            if (1 != c_set) {
+                fprintf(stderr, "      << expected binary code_set (1)>>\n");
+                dStrHex((const char *)ip, i_len, 0);
+                break;
+            }
+            printf("SCSI_IDENT_%s_EUI64=", assoc_str);
+            for (m = 0; m < i_len; ++m)
+                printf("%02x", (unsigned int)ip[m]);
+            printf("\n");
+            break;
+        case 3: /* NAA */
+            if (1 != c_set) {
+                fprintf(stderr, "      << expected binary code_set (1)>>\n");
+                dStrHex((const char *)ip, i_len, 0);
+                break;
+            }
+            naa = (ip[0] >> 4) & 0xff;
+            if ((naa < 2) || (naa > 6) || (4 == naa)) {
+                fprintf(stderr, "      << unexpected naa [0x%x]>>\n", naa);
+                dStrHex((const char *)ip, i_len, 0);
+                break;
+            }
+            if (6 != naa) {
+                if (8 != i_len) {
+                    fprintf(stderr, "      << unexpected NAA 2 identifier "
+                            "length: 0x%x>>\n", i_len);
+                    dStrHex((const char *)ip, i_len, 0);
+                    break;
+                }
+                printf("SCSI_IDENT_%s_NAA=", assoc_str);
+                for (m = 0; m < 8; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+            } else {      /* NAA IEEE Registered extended */
+                if (16 != i_len) {
+                    fprintf(stderr, "      << unexpected NAA 6 identifier "
+                            "length: 0x%x>>\n", i_len);
+                    dStrHex((const char *)ip, i_len, 0);
+                    break;
+                }
+                printf("SCSI_IDENT_%s_NAA=", assoc_str);
+                for (m = 0; m < 16; ++m)
+                    printf("%02x", (unsigned int)ip[m]);
+                printf("\n");
+            }
+            break;
+        case 4: /* Relative target port */
+            if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+                fprintf(stderr, "      << expected binary code_set, target "
+                        "port association, length 4>>\n");
+                dStrHex((const char *)ip, i_len, 0);
+                break;
+            }
+            d_id = ((ip[2] << 8) | ip[3]);
+            printf("SCSI_IDENT_%s_RELATIVE=%d\n", assoc_str, d_id);
+            break;
+        case 5: /* (primary) Target port group */
+            if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
+                fprintf(stderr, "      << expected binary code_set, target "
+                        "port association, length 4>>\n");
+                dStrHex((const char *)ip, i_len, 0);
+                break;
+            }
+            d_id = ((ip[2] << 8) | ip[3]);
+            printf("SCSI_IDENT_%s_TARGET_PORT_GROUP=0x%x\n", assoc_str, d_id);
+            break;
+        case 6: /* Logical unit group */
+            if ((1 != c_set) || (0 != assoc) || (4 != i_len)) {
+                fprintf(stderr, "      << expected binary code_set, logical "
+                        "unit association, length 4>>\n");
+                dStrHex((const char *)ip, i_len, 0);
+                break;
+            }
+            d_id = ((ip[2] << 8) | ip[3]);
+            printf("SCSI_IDENT_%s_LOGICAL_UNIT_GROUP=0x%x\n", assoc_str, d_id);
+            break;
+        case 7: /* MD5 logical unit identifier */
+            if ((1 != c_set) || (0 != assoc)) {
+                fprintf(stderr, "      << expected binary code_set, logical "
+                        "unit association>>\n");
+                dStrHex((const char *)ip, i_len, 0);
+                break;
+            }
+            printf("SCSI_IDENT_%s_MD5=", assoc_str);
+            dStrHex((const char *)ip, i_len, -1);
+            break;
+        case 8: /* SCSI name string */
+            if (3 != c_set) {
+                fprintf(stderr, "      << expected UTF-8 code_set>>\n");
+                dStrHex((const char *)ip, i_len, -1);
+                break;
+            }
+            printf("SCSI_IDENT_%s_NAME=%.*s\n", assoc_str, i_len,
+                   (const char *)ip);
+            break;
+        case 9: /* PCIe Routing ID */
+            /* new in spc4r34, looks under-specified, drop through now */
+        default: /* reserved */
+            dStrHex((const char *)ip, i_len, -1);
+            break;
+        }
+    }
+    if (-2 == u)
+        fprintf(stderr, "Device identification VPD page error: "
+                "around offset=%d\n", off);
+}
+
 /* Transport IDs are initiator port identifiers, typically other than the
    initiator port issuing a SCSI command. Code borrowed from sg_persist.c */
 static void
@@ -1807,7 +2053,7 @@ process_std_inq(int sg_fd, const struct
     res = sg_ll_inquiry(sg_fd, 0, 0, 0, rsp_buff, rlen, 0, verb);
     if (0 == res) {
         pqual = (rsp_buff[0] & 0xe0) >> 5;
-        if (! optsp->do_raw) {
+        if (! optsp->do_raw && ! optsp->do_export) {
             if (0 == pqual)
                 printf("standard INQUIRY:\n");
             else if (1 == pqual)
@@ -1849,7 +2095,7 @@ process_std_inq(int sg_fd, const struct
             dStrRaw((const char *)rsp_buff, act_len);
         else if (optsp->do_hex)
             dStrHex((const char *)rsp_buff, act_len, 0);
-        else {
+        else if (!optsp->do_export) {
             printf("  PQual=%d  Device_type=%d  RMB=%d  version=0x%02x ",
                    pqual, peri_type, !!(rsp_buff[1] & 0x80),
                    (unsigned int)rsp_buff[2]);
@@ -1890,11 +2136,12 @@ process_std_inq(int sg_fd, const struct
             cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
             if (strlen(cp) > 0)
                 printf("   Peripheral device type: %s\n", cp);
-
-            if (act_len <= 8)
+	}
+        if (act_len <= 8) {
+            if (! optsp->do_export)
                 printf(" Inquiry response length=%d, no vendor, "
                        "product or revision data\n", act_len);
-            else {
+        } else {
 		int i;
 
                 if (act_len < SAFE_STD_INQ_RESP_LEN)
@@ -1905,30 +2152,47 @@ process_std_inq(int sg_fd, const struct
 		for (i = 0; i < 8; ++i)
 		    if (xtra_buff[i] == 0x09)
 			xtra_buff[i] = ' ';
+            if (optsp->do_export) {
+                len = encode_whitespaces((unsigned char *)xtra_buff, 8);
+                printf("SCSI_VENDOR=%s\n", xtra_buff);
+                encode_string(xtra_buff, &rsp_buff[8], 8);
+                printf("SCSI_VENDOR_ENC=%s\n", xtra_buff);
+            } else
                 printf(" Vendor identification: %s\n", xtra_buff);
-                if (act_len <= 16)
+            if (act_len <= 16) {
+                if (! optsp->do_export)
                     printf(" Product identification: <none>\n");
-                else {
+            } else {
                     memcpy(xtra_buff, &rsp_buff[16], 16);
                     xtra_buff[16] = '\0';
+                if (optsp->do_export) {
+                    len = encode_whitespaces((unsigned char *)xtra_buff, 16);
+                    printf("SCSI_MODEL=%s\n", xtra_buff);
+                    encode_string(xtra_buff, &rsp_buff[16], 16);
+                    printf("SCSI_MODEL_ENC=%s\n", xtra_buff);
+                } else
                     printf(" Product identification: %s\n", xtra_buff);
-                }
-                if (act_len <= 32)
+            }
+            if (act_len <= 32) {
+                if (!optsp->do_export)
                     printf(" Product revision level: <none>\n");
-                else {
+            } else {
                     memcpy(xtra_buff, &rsp_buff[32], 4);
                     xtra_buff[4] = '\0';
+                if (optsp->do_export) {
+                    len = encode_whitespaces((unsigned char *)xtra_buff, 4);
+                    printf("SCSI_REVISION=%s\n", xtra_buff);
+                } else
                     printf(" Product revision level: %s\n", xtra_buff);
-                }
+            }
                 if (optsp->do_descriptors) {
                     for (j = 0, k = 58; ((j < 8) && ((k + 1) < act_len));
                          k +=2, ++j)
                         vdesc_arr[j] = ((rsp_buff[k] << 8) +
                                          rsp_buff[k + 1]);
                 }
-            }
         }
-        if (! (optsp->do_raw || optsp->do_hex)) {
+        if (! (optsp->do_raw || optsp->do_hex || optsp->do_export)) {
             if (0 == optsp->resp_len) {
                 if (0 == fetch_unit_serial_num(sg_fd, xtra_buff,
                                    sizeof(xtra_buff), optsp->do_verbose))
@@ -2178,7 +2442,7 @@ decode_vpd(int sg_fd, const struct opts_
 
     switch (optsp->page_num) {
     case VPD_UNIT_SERIAL_NUM:
-        if (! optsp->do_raw)
+        if (! optsp->do_raw && ! optsp->do_export)
             printf("VPD INQUIRY: Unit serial number page\n");
         res = sg_ll_inquiry(sg_fd, 0, 1, VPD_UNIT_SERIAL_NUM, rsp_buff,
                             DEF_ALLOC_LEN, 1, optsp->do_verbose);
@@ -2201,12 +2465,17 @@ decode_vpd(int sg_fd, const struct opts_
                 if (len >= (int)sizeof(obuff))
                     len = sizeof(obuff) - 1;
                 memcpy(obuff, rsp_buff + 4, len);
-                printf("  Unit serial number: %s\n", obuff);
+                if (optsp->do_export) {
+                    len = encode_whitespaces((unsigned char *)obuff, len);
+                    printf("SCSI_IDENT_SERIAL=%s\n", obuff);
+                } else {
+                    printf("  Unit serial number: %s\n", obuff);
+                }
             }
         }
         break;
     case VPD_DEVICE_ID:
-        if (! optsp->do_raw)
+        if (! optsp->do_raw && ! optsp->do_export)
             printf("VPD INQUIRY: Device Identification page\n");
         res = sg_ll_inquiry(sg_fd, 0, 1, VPD_DEVICE_ID, rsp_buff,
                             DEF_ALLOC_LEN, 1, optsp->do_verbose);
@@ -2228,7 +2497,14 @@ decode_vpd(int sg_fd, const struct opts_
             }
             if (optsp->do_raw)
                 dStrRaw((const char *)rsp_buff, len);
-            else
+            else if (optsp->do_export) {
+                if (len < 4) {
+                    fprintf(stderr, "Device identification page length too "
+                            "short=%d\n", len);
+                } else {
+                    export_dev_ids(rsp_buff + 4, len - 4);
+                }
+            } else
                 decode_id_vpd(rsp_buff, len, optsp->do_hex);
         }
         break;
@@ -2731,6 +3007,19 @@ main(int argc, char * argv[])
             opts.page_num = n;
         }
     }
+    if (opts.do_export) {
+        if (opts.page_num != -1) {
+            if (opts.page_num != VPD_DEVICE_ID &&
+                opts.page_num != VPD_UNIT_SERIAL_NUM) {
+                fprintf(stderr, "Option '--export' only supported "
+                        "for VPD pages 0x80 and 0x83\n");
+                usage_for(&opts);
+                return SG_LIB_SYNTAX_ERROR;
+            }
+            ++opts.do_decode;
+            ++opts.do_vpd;
+        }
+    }
 
     if ((0 == opts.do_cmddt) && (opts.page_num >= 0) && opts.p_given)
         ++opts.do_vpd;
