• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2006-2022 Douglas Gilbert.
3  * All rights reserved.
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the BSD_LICENSE file.
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  */
9 
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <getopt.h>
19 #define __STDC_FORMAT_MACROS 1
20 #include <inttypes.h>
21 #include <errno.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include "sg_lib.h"
30 #include "sg_cmds_basic.h"
31 #include "sg_unaligned.h"
32 #include "sg_pr2serr.h"
33 
34 #include "sg_vpd_common.h"      /* shared with sg_inq */
35 
36 /* This utility program was originally written for the Linux OS SCSI subsystem.
37 
38    This program fetches Vital Product Data (VPD) pages from the given
39    device and outputs it as directed. VPD pages are obtained via a
40    SCSI INQUIRY command. Most of the data in this program is obtained
41    from the SCSI SPC-4 document at https://www.t10.org .
42 
43 */
44 
45 static const char * version_str = "1.83 20220915";  /* spc6r06 + sbc5r03 */
46 
47 #define MY_NAME "sg_vpd"
48 
49 /* Device identification VPD page associations */
50 #define VPD_ASSOC_LU 0
51 #define VPD_ASSOC_TPORT 1
52 #define VPD_ASSOC_TDEVICE 2
53 
54 /* values for selection one or more associations (2**vpd_assoc),
55    except _AS_IS */
56 #define VPD_DI_SEL_LU 1
57 #define VPD_DI_SEL_TPORT 2
58 #define VPD_DI_SEL_TARGET 4
59 #define VPD_DI_SEL_AS_IS 32
60 
61 #define DEF_ALLOC_LEN 252
62 #define MIN_MAXLEN 16
63 #define MX_ALLOC_LEN (0xc000 + 0x80)
64 #define VPD_ATA_INFO_LEN  572
65 
66 #define SENSE_BUFF_LEN  64       /* Arbitrary, could be larger */
67 #define INQUIRY_CMD     0x12
68 #define INQUIRY_CMDLEN  6
69 #define DEF_PT_TIMEOUT  60       /* 60 seconds */
70 
71 
72 uint8_t * rsp_buff;
73 
74 static int svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop,
75                            int subvalue, int off, const char * prefix);
76 static int svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue,
77                                  int off);
78 
79 static int filter_dev_ids(const char * print_if_found, int num_leading,
80                           uint8_t * buff, int len, int m_assoc,
81                           struct opts_t * op, sgj_opaque_p jop);
82 
83 static const int rsp_buff_sz = MX_ALLOC_LEN + 2;
84 
85 static uint8_t * free_rsp_buff;
86 
87 static struct option long_options[] = {
88         {"all", no_argument, 0, 'a'},
89         {"enumerate", no_argument, 0, 'e'},
90         {"examine", no_argument, 0, 'E'},
91         {"force", no_argument, 0, 'f'},
92         {"help", no_argument, 0, 'h'},
93         {"hex", no_argument, 0, 'H'},
94         {"ident", no_argument, 0, 'i'},
95         {"inhex", required_argument, 0, 'I'},
96         {"json", optional_argument, 0, 'j'},
97         {"long", no_argument, 0, 'l'},
98         {"maxlen", required_argument, 0, 'm'},
99         {"page", required_argument, 0, 'p'},
100         {"quiet", no_argument, 0, 'q'},
101         {"raw", no_argument, 0, 'r'},
102         {"sinq_inraw", required_argument, 0, 'Q'},
103         {"sinq-inraw", required_argument, 0, 'Q'},
104         {"vendor", required_argument, 0, 'M'},
105         {"verbose", no_argument, 0, 'v'},
106         {"version", no_argument, 0, 'V'},
107         {0, 0, 0, 0},
108 };
109 
110 
111 /* arranged in alphabetical order by acronym */
112 static struct svpd_values_name_t standard_vpd_pg[] = {
113     {VPD_AUTOMATION_DEV_SN, 0, 1, "adsn", "Automation device serial "
114      "number (SSC)"},
115     {VPD_ATA_INFO, 0, -1, "ai", "ATA information (SAT)"},
116     {VPD_ASCII_OP_DEF, 0, -1, "aod",
117      "ASCII implemented operating definition (obsolete)"},
118     {VPD_BLOCK_DEV_CHARS, 0, 0, "bdc", "Block device characteristics "
119      "(SBC)"},
120     {VPD_BLOCK_DEV_C_EXTENS, 0, 0, "bdce", "Block device characteristics "
121      "extension (SBC)"},
122     {VPD_BLOCK_LIMITS, 0, 0, "bl", "Block limits (SBC)"},
123     {VPD_BLOCK_LIMITS_EXT, 0, 0, "ble", "Block limits extension (SBC)"},
124     {VPD_CFA_PROFILE_INFO, 0, 0, "cfa", "CFA profile information"},
125     {VPD_CON_POS_RANGE, 0, 0, "cpr", "Concurrent positioning ranges"},
126     {VPD_DEVICE_CONSTITUENTS, 0, -1, "dc", "Device constituents"},
127     {VPD_DEVICE_ID, 0, -1, "di", "Device identification"},
128     {VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, "di_asis", "Like 'di' "
129      "but designators ordered as found"},
130     {VPD_DEVICE_ID, VPD_DI_SEL_LU, -1, "di_lu", "Device identification, "
131      "lu only"},
132     {VPD_DEVICE_ID, VPD_DI_SEL_TPORT, -1, "di_port", "Device "
133      "identification, target port only"},
134     {VPD_DEVICE_ID, VPD_DI_SEL_TARGET, -1, "di_target", "Device "
135      "identification, target device only"},
136     {VPD_DTDE_ADDRESS, 0, 1, "dtde",
137      "Data transfer device element address (SSC)"},
138     {VPD_EXT_INQ, 0, -1, "ei", "Extended inquiry data"},
139     {VPD_FORMAT_PRESETS, 0, 0, "fp", "Format presets"},
140     {VPD_IMP_OP_DEF, 0, -1, "iod",
141      "Implemented operating definition (obsolete)"},
142     {VPD_LB_PROTECTION, 0, 0, "lbpro", "Logical block protection (SSC)"},
143     {VPD_LB_PROVISIONING, 0, 0, "lbpv", "Logical block provisioning (SBC)"},
144     {VPD_MAN_ASS_SN, 0, 1, "mas", "Manufacturer assigned serial number (SSC)"},
145     {VPD_MAN_ASS_SN, 0, 0x12, "masa",
146      "Manufacturer assigned serial number (ADC)"},
147     {VPD_MAN_NET_ADDR, 0, -1, "mna", "Management network addresses"},
148     {VPD_MODE_PG_POLICY, 0, -1, "mpp", "Mode page policy"},
149     {VPD_OSD_INFO, 0, 0x11, "oi", "OSD information"},
150     {VPD_POWER_CONDITION, 0, -1, "pc", "Power condition"},/* "po" in sg_inq */
151     {VPD_POWER_CONSUMPTION, 0, -1, "psm", "Power consumption"},
152     {VPD_PROTO_LU, 0, -1, "pslu", "Protocol-specific logical unit "
153      "information"},
154     {VPD_PROTO_PORT, 0, -1, "pspo", "Protocol-specific port information"},
155     {VPD_REFERRALS, 0, 0, "ref", "Referrals (SBC)"},
156     {VPD_SA_DEV_CAP, 0, 1, "sad",
157      "Sequential access device capabilities (SSC)"},
158     {VPD_SUP_BLOCK_LENS, 0, 0, "sbl", "Supported block lengths and "
159      "protection types (SBC)"},
160     {VPD_SCSI_FEATURE_SETS, 0, -1, "sfs", "SCSI feature sets"},
161     {VPD_SOFTW_INF_ID, 0, -1, "sii", "Software interface identification"},
162     {VPD_NOPE_WANT_STD_INQ, 0, -1, "sinq", "Standard inquiry data format"},
163     {VPD_UNIT_SERIAL_NUM, 0, -1, "sn", "Unit serial number"},
164     {VPD_SCSI_PORTS, 0, -1, "sp", "SCSI ports"},
165     {VPD_SECURITY_TOKEN, 0, 0x11, "st", "Security token (OSD)"},
166     {VPD_SUPPORTED_VPDS, 0, -1, "sv", "Supported VPD pages"},
167     {VPD_TA_SUPPORTED, 0, 1, "tas", "TapeAlert supported flags (SSC)"},
168     {VPD_3PARTY_COPY, 0, -1, "tpc", "Third party copy"},
169     {VPD_ZBC_DEV_CHARS, 0, -1, "zbdch", "Zoned block device characteristics"},
170         /* Use pdt of -1 since this page both for pdt=0 and pdt=0x14 */
171     {0, 0, 0, NULL, NULL},
172 };
173 
174 
175 static void
usage()176 usage()
177 {
178     pr2serr("Usage: sg_vpd  [--all] [--enumerate] [--examine] [--force] "
179             "[--help] [--hex]\n"
180             "               [--ident] [--inhex=FN] [--long] [--maxlen=LEN] "
181             "[--page=PG]\n"
182             "               [--quiet] [--raw] [--sinq_inraw=RFN] "
183             "[--vendor=VP] [--verbose]\n"
184             "               [--version] DEVICE\n");
185     pr2serr("  where:\n"
186             "    --all|-a        output all pages listed in the supported "
187             "pages VPD\n"
188             "                    page\n"
189             "    --enumerate|-e    enumerate known VPD pages names (ignore "
190             "DEVICE),\n"
191             "                      can be used with --page=num to search\n"
192             "    --examine|-E    starting at 0x80 scan pages code to 0xff\n"
193             "    --force|-f      skip VPD page 0 (supported VPD pages) "
194             "checking\n"
195             "    --help|-h       output this usage message then exit\n"
196             "    --hex|-H        output page in ASCII hexadecimal\n"
197             "    --ident|-i      output device identification VPD page, "
198             "twice for\n"
199             "                    short logical unit designator (equiv: "
200             "'-qp di_lu')\n"
201             "    --inhex=FN|-I FN    read ASCII hex from file FN instead of "
202             "DEVICE;\n"
203             "                        if used with --raw then read binary "
204             "from FN\n"
205             "    --json[=JO]|-j[JO]    output in JSON instead of human "
206             "readable text.\n"
207             "                          Use --json=? for JSON help\n"
208             "    --long|-l       perform extra decoding\n"
209             "    --maxlen=LEN|-m LEN    max response length (allocation "
210             "length in cdb)\n"
211             "                           (def: 0 -> 252 bytes)\n"
212             "    --page=PG|-p PG    fetch VPD page where PG is an "
213             "acronym, or a decimal\n"
214             "                       number unless hex indicator "
215             "is given (e.g. '0x83');\n"
216             "                       can also take PG,VP as an "
217             "operand\n"
218             "    --quiet|-q      suppress some output when decoding\n"
219             "    --raw|-r        output page in binary; if --inhex=FN is "
220             "also\n"
221             "                    given, FN is in binary (else FN is in "
222             "hex)\n"
223             "    --sinq_inraw=RFN|-Q RFN    read raw (binary) standard "
224             "INQUIRY\n"
225             "                               response from the RFN filename\n"
226             "    --vendor=VP|-M VP    vendor/product abbreviation [or "
227             "number]\n"
228             "    --verbose|-v    increase verbosity\n"
229             "    --version|-V    print version string and exit\n\n"
230             "Fetch Vital Product Data (VPD) page using SCSI INQUIRY or "
231             "decodes VPD\npage response held in file FN. To list available "
232             "pages use '-e'. Also\n'-p -1' or '-p sinq' yields the standard "
233             "INQUIRY response.\n");
234 }
235 
236 static const struct svpd_values_name_t *
sdp_get_vpd_detail(int page_num,int subvalue,int pdt)237 sdp_get_vpd_detail(int page_num, int subvalue, int pdt)
238 {
239     const struct svpd_values_name_t * vnp;
240     int sv, ty;
241 
242     sv = (subvalue < 0) ? 1 : 0;
243     ty = (pdt < 0) ? 1 : 0;
244     for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
245         if ((page_num == vnp->value) &&
246             (sv || (subvalue == vnp->subvalue)) &&
247             (ty || (pdt == vnp->pdt)))
248             return vnp;
249     }
250     if (! ty)
251         return sdp_get_vpd_detail(page_num, subvalue, -1);
252     if (! sv)
253         return sdp_get_vpd_detail(page_num, -1, -1);
254     return NULL;
255 }
256 
257 static const struct svpd_values_name_t *
sdp_find_vpd_by_acron(const char * ap)258 sdp_find_vpd_by_acron(const char * ap)
259 {
260     const struct svpd_values_name_t * vnp;
261 
262     for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
263         if (0 == strcmp(vnp->acron, ap))
264             return vnp;
265     }
266     return NULL;
267 }
268 
269 static void
enumerate_vpds(int standard,int vendor)270 enumerate_vpds(int standard, int vendor)
271 {
272     const struct svpd_values_name_t * vnp;
273 
274     if (standard) {
275         for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
276             if (vnp->name) {
277                 if (vnp->value < 0)
278                     printf("  %-10s -1        %s\n", vnp->acron, vnp->name);
279                 else
280                     printf("  %-10s 0x%02x      %s\n", vnp->acron, vnp->value,
281                        vnp->name);
282             }
283         }
284     }
285     if (vendor)
286         svpd_enumerate_vendor(-2);
287 }
288 
289 static int
count_standard_vpds(int vpd_pn)290 count_standard_vpds(int vpd_pn)
291 {
292     const struct svpd_values_name_t * vnp;
293     int matches = 0;
294 
295     for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
296         if ((vpd_pn == vnp->value) && vnp->name) {
297             if (0 == matches)
298                 printf("Matching standard VPD pages:\n");
299             ++matches;
300             if (vnp->value < 0)
301                 printf("  %-10s -1        %s\n", vnp->acron, vnp->name);
302             else
303                 printf("  %-10s 0x%02x      %s\n", vnp->acron, vnp->value,
304                    vnp->name);
305         }
306     }
307     return matches;
308 }
309 
310 static void
dStrRaw(const uint8_t * str,int len)311 dStrRaw(const uint8_t * str, int len)
312 {
313     int k;
314 
315     for (k = 0; k < len; ++k)
316         printf("%c", str[k]);
317 }
318 
319 /* Assume index is less than 16 */
320 static const char * sg_ansi_version_arr[16] =
321 {
322     "no conformance claimed",
323     "SCSI-1",           /* obsolete, ANSI X3.131-1986 */
324     "SCSI-2",           /* obsolete, ANSI X3.131-1994 */
325     "SPC",              /* withdrawn, ANSI INCITS 301-1997 */
326     "SPC-2",            /* ANSI INCITS 351-2001, ISO/IEC 14776-452 */
327     "SPC-3",            /* ANSI INCITS 408-2005, ISO/IEC 14776-453 */
328     "SPC-4",            /* ANSI INCITS 513-2015 */
329     "SPC-5",            /* ANSI INCITS 502-2020 */
330     "ecma=1, [8h]",
331     "ecma=1, [9h]",
332     "ecma=1, [Ah]",
333     "ecma=1, [Bh]",
334     "reserved [Ch]",
335     "reserved [Dh]",
336     "reserved [Eh]",
337     "reserved [Fh]",
338 };
339 
340 static void
std_inq_decode(uint8_t * b,int len,struct opts_t * op,sgj_opaque_p jop)341 std_inq_decode(uint8_t * b, int len, struct opts_t * op, sgj_opaque_p jop)
342 {
343     uint8_t ver;
344     int pqual, pdt, hp, j, n;
345     sgj_state * jsp = &op->json_st;
346     const char * cp;
347     char c[256];
348     static const int clen = sizeof(c);
349     static const char * np = "Standard INQUIRY data format:";
350 
351     if (len < 4) {
352         pr2serr("%s: len [%d] too short\n", __func__, len);
353         return;
354     }
355     pqual = (b[0] & 0xe0) >> 5;
356     pdt = b[0] & PDT_MASK;
357     hp = (b[1] >> 4) & 0x3;
358     ver = b[2];
359     sgj_pr_hr(jsp, "%s", np);
360     if (0 == pqual)
361         sgj_pr_hr(jsp, "\n");
362     else {
363         cp = pqual_str(pqual);
364 
365         if (pqual < 3)
366             sgj_pr_hr(jsp, " [PQ indicates %s]\n", cp);
367         else
368             sgj_pr_hr(jsp, " [PQ indicates %s [0x%x] ]\n", cp, pqual);
369     }
370     sgj_pr_hr(jsp, "  PQual=%d  PDT=%d  RMB=%d  LU_CONG=%d  hot_pluggable="
371               "%d  version=0x%02x  [%s]\n", pqual, pdt, !!(b[1] & 0x80),
372               !!(b[1] & 0x40), hp, ver, sg_ansi_version_arr[ver & 0xf]);
373     sgj_pr_hr(jsp, "  [AERC=%d]  [TrmTsk=%d]  NormACA=%d  HiSUP=%d "
374            " Resp_data_format=%d\n",
375            !!(b[3] & 0x80), !!(b[3] & 0x40), !!(b[3] & 0x20),
376            !!(b[3] & 0x10), b[3] & 0x0f);
377     if (len < 5)
378         goto skip1;
379     j = b[4] + 5;
380     if (op->verbose > 2)
381         pr2serr(">> requested %d bytes, %d bytes available\n", len, j);
382     sgj_pr_hr(jsp, "  SCCS=%d  ACC=%d  TPGS=%d  3PC=%d  Protect=%d  "
383               "[BQue=%d]\n", !!(b[5] & 0x80), !!(b[5] & 0x40),
384               ((b[5] & 0x30) >> 4), !!(b[5] & 0x08), !!(b[5] & 0x01),
385               !!(b[6] & 0x80));
386     n = 0;
387     n += sg_scnpr(c + n, clen - n, "EncServ=%d  ", !!(b[6] & 0x40));
388     if (b[6] & 0x10)
389         n += sg_scnpr(c + n, clen - n, "MultiP=1 (VS=%d)  ", !!(b[6] & 0x20));
390     else
391         n += sg_scnpr(c + n, clen - n, "MultiP=0  ");
392     n += sg_scnpr(c + n, clen - n, "[MChngr=%d]  [ACKREQQ=%d]  Addr16=%d",
393                   !!(b[6] & 0x08), !!(b[6] & 0x04), !!(b[6] & 0x01));
394     sgj_pr_hr(jsp, "  %s\n", c);
395     sgj_pr_hr(jsp, "  [RelAdr=%d]  WBus16=%d  Sync=%d  [Linked=%d]  "
396               "[TranDis=%d]  CmdQue=%d\n", !!(b[7] & 0x80), !!(b[7] & 0x20),
397               !!(b[7] & 0x10), !!(b[7] & 0x08), !!(b[7] & 0x04),
398               !!(b[7] & 0x02));
399     if (len < 36)
400         goto skip1;
401     sgj_pr_hr(jsp, "  %s: %.8s\n", t10_vendor_id_hr, b + 8);
402     sgj_pr_hr(jsp, "  %s: %.16s\n", product_id_hr, b + 16);
403     sgj_pr_hr(jsp, "  %s: %.4s\n", product_rev_lev_hr, b + 32);
404 skip1:
405     if (! jsp->pr_as_json || (len < 8))
406         return;
407     std_inq_decode_js(b, len, op, jop);
408 }
409 
410 /* VPD_DEVICE_ID 0x83 ["di, di_asis, di_lu, di_port, di_target"] */
411 static void
device_id_vpd_variants(uint8_t * buff,int len,int subvalue,struct opts_t * op,sgj_opaque_p jap)412 device_id_vpd_variants(uint8_t * buff, int len, int subvalue,
413                        struct opts_t * op, sgj_opaque_p jap)
414 {
415     int m_a, blen;
416     uint8_t * b;
417 
418     if (len < 4) {
419         pr2serr("Device identification VPD page length too short=%d\n", len);
420         return;
421     }
422     blen = len - 4;
423     b = buff + 4;
424     m_a = -1;
425     if (0 == subvalue) {
426         filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
427                        VPD_ASSOC_LU, op, jap);
428         filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b, blen,
429                        VPD_ASSOC_TPORT, op, jap);
430         filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0, b, blen,
431                        VPD_ASSOC_TDEVICE, op, jap);
432     } else if (VPD_DI_SEL_AS_IS == subvalue)
433         filter_dev_ids(NULL, 0, b, blen, m_a, op, jap);
434     else {
435         if (VPD_DI_SEL_LU & subvalue)
436             filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
437                            VPD_ASSOC_LU, op, jap);
438         if (VPD_DI_SEL_TPORT & subvalue)
439             filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b,
440                            blen, VPD_ASSOC_TPORT, op, jap);
441         if (VPD_DI_SEL_TARGET & subvalue)
442             filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0,
443                            b, blen, VPD_ASSOC_TDEVICE, op, jap);
444     }
445 }
446 
447 static void             /* VPD_SUPPORTED_VPDS  ["sv"] */
decode_supported_vpd_4vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)448 decode_supported_vpd_4vpd(uint8_t * buff, int len, struct opts_t * op,
449                           sgj_opaque_p jap)
450 {
451     uint8_t pn;
452     int k, rlen, pdt;
453     sgj_state * jsp = &op->json_st;
454     sgj_opaque_p jo2p;
455     const struct svpd_values_name_t * vnp;
456     uint8_t * bp;
457     char b[144];
458     static const int blen = sizeof(b);
459     static const char * svps = "Supported VPD pages";
460 
461     if ((1 == op->do_hex) || (op->do_hex > 2)) {
462         hex2stdout(buff, len, no_ascii_4hex(op));
463         return;
464     }
465     pdt = PDT_MASK & buff[0];
466     rlen = buff[3] + 4;
467     if (rlen > len)
468         pr2serr("%s VPD page truncated, indicates %d, got %d\n", svps, rlen,
469                 len);
470     else
471         len = rlen;
472     if (len < 4) {
473         pr2serr("%s VPD page length too short=%d\n", svps, len);
474         return;
475     }
476     len -= 4;
477     bp = buff + 4;
478 
479     for (k = 0; k < len; ++k) {
480         pn = bp[k];
481         snprintf(b, blen, "0x%02x", pn);
482         vnp = sdp_get_vpd_detail(pn, -1, pdt);
483         if (vnp) {
484             if (op->do_long)
485                 sgj_pr_hr(jsp, "  %s  %s [%s]\n", b, vnp->name, vnp->acron);
486             else
487                 sgj_pr_hr(jsp, "  %s [%s]\n", vnp->name, vnp->acron);
488         } else if (op->vend_prod_num >= 0) {
489             vnp = svpd_find_vendor_by_num(pn, op->vend_prod_num);
490             if (vnp) {
491                 if (op->do_long)
492                     sgj_pr_hr(jsp, "  %s  %s [%s]\n", b, vnp->name,
493                               vnp->acron);
494                 else
495                     sgj_pr_hr(jsp, "  %s [%s]\n", vnp->name, vnp->acron);
496             } else
497                 sgj_pr_hr(jsp, "  %s\n", b);
498         } else
499             sgj_pr_hr(jsp, "  %s\n", b);
500         if (jsp->pr_as_json) {
501             jo2p = sgj_new_unattached_object_r(jsp);
502             sgj_js_nv_i(jsp, jo2p, "i", pn);
503             sgj_js_nv_s(jsp, jo2p, "hex", b + 2);
504             if (vnp) {
505                 sgj_js_nv_s(jsp, jo2p, "name", vnp->name);
506                 sgj_js_nv_s(jsp, jo2p, "acronym", vnp->acron);
507             } else {
508                 sgj_js_nv_s(jsp, jo2p, "name", "unknown");
509                 sgj_js_nv_s(jsp, jo2p, "acronym", "unknown");
510             }
511             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
512         }
513     }
514 }
515 
516 /* VPD_SCSI_PORTS     0x88  ["sp"] */
517 static void
decode_scsi_ports_vpd_4vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)518 decode_scsi_ports_vpd_4vpd(uint8_t * buff, int len, struct opts_t * op,
519                            sgj_opaque_p jap)
520 {
521     int k, bump, rel_port, ip_tid_len, tpd_len;
522     sgj_state * jsp = &op->json_st;
523     sgj_opaque_p jo2p = NULL;
524     sgj_opaque_p ja2p = NULL;
525     uint8_t * bp;
526 
527     if ((1 == op->do_hex) || (op->do_hex > 2)) {
528         hex2stdout(buff, len, no_ascii_4hex(op));
529         return;
530     }
531     if (len < 4) {
532         pr2serr("SCSI Ports VPD page length too short=%d\n", len);
533         return;
534     }
535     len -= 4;
536     bp = buff + 4;
537     for (k = 0; k < len; k += bump, bp += bump) {
538         rel_port = sg_get_unaligned_be16(bp + 2);
539         sgj_pr_hr(jsp, "  Relative port=%d\n", rel_port);
540         jo2p = sgj_new_unattached_object_r(jsp);
541         sgj_js_nv_i(jsp, jo2p, "relative_port", rel_port);
542         ip_tid_len = sg_get_unaligned_be16(bp + 6);
543         bump = 8 + ip_tid_len;
544         if ((k + bump) > len) {
545             pr2serr("SCSI Ports VPD page, short descriptor "
546                     "length=%d, left=%d\n", bump, (len - k));
547             return;
548         }
549         if (ip_tid_len > 0) {
550             if (op->do_hex > 1) {
551                 sgj_pr_hr(jsp, "    Initiator port transport id:\n");
552                 hex2stdout((bp + 8), ip_tid_len, 1);
553             } else {
554                 char b[1024];
555 
556                 sg_decode_transportid_str("    ", bp + 8, ip_tid_len,
557                                           true, sizeof(b), b);
558                 if (jsp->pr_as_json)
559                     sgj_js_nv_s(jsp, jo2p, "initiator_port_transport_id", b);
560                 sgj_pr_hr(jsp, "%s",
561                           sg_decode_transportid_str("    ", bp + 8,
562                                             ip_tid_len, true, sizeof(b), b));
563             }
564         }
565         tpd_len = sg_get_unaligned_be16(bp + bump + 2);
566         if ((k + bump + tpd_len + 4) > len) {
567             pr2serr("SCSI Ports VPD page, short descriptor(tgt) "
568                     "length=%d, left=%d\n", bump, (len - k));
569             return;
570         }
571         if (tpd_len > 0) {
572             if (op->do_hex > 1) {
573                 sgj_pr_hr(jsp, "    Target port descriptor(s):\n");
574                 hex2stdout(bp + bump + 4, tpd_len, 1);
575             } else {
576                 if ((0 == op->do_quiet) || (ip_tid_len > 0))
577                     sgj_pr_hr(jsp, "    Target port descriptor(s):\n");
578                 if (jsp->pr_as_json) {
579                     sgj_opaque_p jo3p = sgj_named_subobject_r(jsp, jo2p,
580                                                               "target_port");
581 
582                     ja2p = sgj_named_subarray_r(jsp, jo3p,
583                                         "designation_descriptor_list");
584                 }
585                 filter_dev_ids("", 2 /* leading spaces */, bp + bump + 4,
586                                tpd_len, VPD_ASSOC_TPORT, op, ja2p);
587             }
588         }
589         bump += tpd_len + 4;
590         sgj_js_nv_o(jsp, jap, NULL, jo2p);
591     }
592 }
593 
594 /* Prints outs an abridged set of device identification designators
595    selected by association, designator type and/or code set. Not used
596    for JSON output. */
597 static int
filter_dev_ids_quiet(uint8_t * buff,int len,int m_assoc)598 filter_dev_ids_quiet(uint8_t * buff, int len, int m_assoc)
599 {
600     int k, m, p_id, c_set, piv, desig_type, i_len, naa, off, u;
601     int assoc, is_sas, rtp;
602     const uint8_t * bp;
603     const uint8_t * ip;
604     uint8_t sas_tport_addr[8];
605 
606     rtp = 0;
607     memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
608     for (k = 0, off = -1; true; ++k) {
609         if ((0 == k) && (0 != buff[2])) {
610             /* first already in buff */
611             if (m_assoc != VPD_ASSOC_LU)
612                 return 0;
613             ip = buff;
614             c_set = 1;
615             assoc = VPD_ASSOC_LU;
616             is_sas = 0;
617             desig_type = 3;
618             i_len = 16;
619         } else {
620             u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, -1, -1);
621             if (0 != u)
622                 break;
623             bp = buff + off;
624             i_len = bp[3];
625             if ((off + i_len + 4) > len) {
626                 pr2serr("    VPD page error: designator length longer than\n"
627                         "     remaining response length=%d\n", (len - off));
628                 return SG_LIB_CAT_MALFORMED;
629             }
630             ip = bp + 4;
631             p_id = ((bp[0] >> 4) & 0xf);
632             c_set = (bp[0] & 0xf);
633             piv = ((bp[1] & 0x80) ? 1 : 0);
634             is_sas = (piv && (6 == p_id)) ? 1 : 0;
635             assoc = ((bp[1] >> 4) & 0x3);
636             desig_type = (bp[1] & 0xf);
637         }
638         switch (desig_type) {
639         case 0: /* vendor specific */
640             break;
641         case 1: /* T10 vendor identification */
642             break;
643         case 2: /* EUI-64 based */
644             if ((8 != i_len) && (12 != i_len) && (16 != i_len))
645                 pr2serr("      << expect 8, 12 and 16 byte "
646                         "EUI, got %d>>\n", i_len);
647             printf("  0x");
648             for (m = 0; m < i_len; ++m)
649                 printf("%02x", (unsigned int)ip[m]);
650             printf("\n");
651             break;
652         case 3: /* NAA */
653             naa = (ip[0] >> 4) & 0xff;
654             if (1 != c_set) {
655                 pr2serr("      << expected binary code_set (1), got %d for "
656                         "NAA=%d>>\n", c_set, naa);
657                 hex2stderr(ip, i_len, 0);
658                 break;
659             }
660             switch (naa) {
661             case 2:             /* NAA IEEE extended */
662                 if (8 != i_len) {
663                     pr2serr("      << unexpected NAA 2 identifier "
664                             "length: 0x%x>>\n", i_len);
665                     hex2stderr(ip, i_len, 0);
666                     break;
667                 }
668                 printf("  0x");
669                 for (m = 0; m < 8; ++m)
670                     printf("%02x", (unsigned int)ip[m]);
671                 printf("\n");
672                 break;
673             case 3:             /* Locally assigned */
674             case 5:             /* IEEE Registered */
675                 if (8 != i_len) {
676                     pr2serr("      << unexpected NAA 3 or 5 "
677                             "identifier length: 0x%x>>\n", i_len);
678                     hex2stderr(ip, i_len, 0);
679                     break;
680                 }
681                 if ((0 == is_sas) || (1 != assoc)) {
682                     printf("  0x");
683                     for (m = 0; m < 8; ++m)
684                         printf("%02x", (unsigned int)ip[m]);
685                     printf("\n");
686                 } else if (rtp) {
687                     printf("  0x");
688                     for (m = 0; m < 8; ++m)
689                         printf("%02x", (unsigned int)ip[m]);
690                     printf(",0x%x\n", rtp);
691                     rtp = 0;
692                 } else {
693                     if (sas_tport_addr[0]) {
694                         printf("  0x");
695                         for (m = 0; m < 8; ++m)
696                             printf("%02x", (unsigned int)sas_tport_addr[m]);
697                         printf("\n");
698                     }
699                     memcpy(sas_tport_addr, ip, sizeof(sas_tport_addr));
700                 }
701                 break;
702             case 6:             /* NAA IEEE registered extended */
703                 if (16 != i_len) {
704                     pr2serr("      << unexpected NAA 6 identifier length: "
705                             "0x%x>>\n", i_len);
706                     hex2stderr(ip, i_len, 0);
707                     break;
708                 }
709                 printf("  0x");
710                 for (m = 0; m < 16; ++m)
711                     printf("%02x", (unsigned int)ip[m]);
712                 printf("\n");
713                 break;
714             default:
715                 pr2serr("      << bad NAA nibble, expected 2, 3, 5 or 6, got "
716                         "%d>>\n", naa);
717                 hex2stderr(ip, i_len, 0);
718                 break;
719             }
720             break;
721         case 4: /* Relative target port */
722             if ((0 == is_sas) || (1 != c_set) || (1 != assoc) || (4 != i_len))
723                 break;
724             rtp = sg_get_unaligned_be16(ip + 2);
725             if (sas_tport_addr[0]) {
726                 printf("  0x");
727                 for (m = 0; m < 8; ++m)
728                     printf("%02x", (unsigned int)sas_tport_addr[m]);
729                 printf(",0x%x\n", rtp);
730                 memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
731                 rtp = 0;
732             }
733             break;
734         case 5: /* (primary) Target port group */
735             break;
736         case 6: /* Logical unit group */
737             break;
738         case 7: /* MD5 logical unit identifier */
739             break;
740         case 8: /* SCSI name string */
741             if (c_set < 2) {    /* quietly accept ASCII for UTF-8 */
742                 pr2serr("      << expected UTF-8 code_set>>\n");
743                 hex2stderr(ip, i_len, 0);
744                 break;
745             }
746             if (! (strncmp((const char *)ip, "eui.", 4) ||
747                    strncmp((const char *)ip, "EUI.", 4) ||
748                    strncmp((const char *)ip, "naa.", 4) ||
749                    strncmp((const char *)ip, "NAA.", 4) ||
750                    strncmp((const char *)ip, "iqn.", 4))) {
751                 pr2serr("      << expected name string prefix>>\n");
752                 hex2stderr(ip, i_len, -1);
753                 break;
754             }
755             /* does %s print out UTF-8 ok??
756              * Seems to depend on the locale. Looks ok here with my
757              * locale setting: en_AU.UTF-8
758              */
759             printf("  %.*s\n", i_len, (const char *)ip);
760             break;
761         case 9: /* Protocol specific port identifier */
762             break;
763         case 0xa: /* UUID identifier [spc5r08] RFC 4122 */
764             if ((1 != c_set) || (18 != i_len) || (1 != ((ip[0] >> 4) & 0xf)))
765                 break;
766             for (m = 0; m < 16; ++m) {
767                 if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
768                     printf("-");
769                 printf("%02x", (unsigned int)ip[2 + m]);
770             }
771             printf("\n");
772             break;
773         default: /* reserved */
774             break;
775         }
776     }
777     if (sas_tport_addr[0]) {
778         printf("  0x");
779         for (m = 0; m < 8; ++m)
780             printf("%02x", (unsigned int)sas_tport_addr[m]);
781         printf("\n");
782     }
783     if (-2 == u) {
784         pr2serr("VPD page error: short designator around offset %d\n", off);
785         return SG_LIB_CAT_MALFORMED;
786     }
787     return 0;
788 }
789 
790 /* Prints outs designation descriptors (dd_s) selected by association,
791    designator type and/or code set. VPD_DEVICE_ID and VPD_SCSI_PORTS */
792 static int
filter_dev_ids(const char * print_if_found,int num_leading,uint8_t * buff,int len,int m_assoc,struct opts_t * op,sgj_opaque_p jap)793 filter_dev_ids(const char * print_if_found, int num_leading, uint8_t * buff,
794                int len, int m_assoc, struct opts_t * op, sgj_opaque_p jap)
795 {
796     bool printed, sgj_out_hr;
797     int assoc, off, u, i_len;
798     const uint8_t * bp;
799     sgj_state * jsp = &op->json_st;
800     char b[1024];
801     char sp[82];
802     static const int blen = sizeof(b);
803 
804     if (op->do_quiet && (! jsp->pr_as_json))
805         return filter_dev_ids_quiet(buff, len, m_assoc);
806     sgj_out_hr = false;
807     if (jsp->pr_as_json) {
808         int ret = filter_json_dev_ids(buff, len, m_assoc, op, jap);
809 
810         if (ret || (! jsp->pr_out_hr))
811             return ret;
812         sgj_out_hr = true;
813     }
814     if (num_leading > (int)(sizeof(sp) - 2))
815         num_leading = sizeof(sp) - 2;
816     if (num_leading > 0)
817         snprintf(sp, sizeof(sp), "%*c", num_leading, ' ');
818     else
819         sp[0] = '\0';
820     if (buff[2] != 0) { /* all valid dd_s should have 0 in this byte */
821         if (op->verbose)
822             pr2serr("%s: designation descriptors byte 2 should be 0\n"
823                     "perhaps this is a standard inquiry response, ignore\n",
824                     __func__);
825         return 0;
826     }
827     off = -1;
828     printed = false;
829     while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, -1, -1)) == 0) {
830         bp = buff + off;
831         i_len = bp[3];
832         if ((off + i_len + 4) > len) {
833             pr2serr("    VPD page error: designator length longer than\n"
834                     "     remaining response length=%d\n", (len - off));
835             return SG_LIB_CAT_MALFORMED;
836         }
837         assoc = ((bp[1] >> 4) & 0x3);
838         if (print_if_found && (! printed)) {
839             printed = true;
840             if (strlen(print_if_found) > 0) {
841                 snprintf(b, blen, "  %s:", print_if_found);
842                 if (sgj_out_hr)
843                     sgj_js_str_out(jsp, b, strlen(b));
844                 else
845                     printf("%s\n", b);
846             }
847         }
848         if (NULL == print_if_found) {
849             snprintf(b, blen, "  %s%s:", sp, sg_get_desig_assoc_str(assoc));
850             if (sgj_out_hr)
851                 sgj_js_str_out(jsp, b, strlen(b));
852             else
853                 printf("%s\n", b);
854         }
855         sg_get_designation_descriptor_str(sp, bp, i_len + 4, false,
856                                           op->do_long, blen, b);
857         if (sgj_out_hr)
858             sgj_js_str_out(jsp, b, strlen(b));
859         else
860             printf("%s", b);
861     }
862     if (-2 == u) {
863         pr2serr("VPD page error: short designator around offset %d\n", off);
864         return SG_LIB_CAT_MALFORMED;
865     }
866     return 0;
867 }
868 
869 /* VPD_BLOCK_LIMITS sbc */
870 /* VPD_SA_DEV_CAP ssc */
871 /* VPD_OSD_INFO osd */
872 static void
decode_b0_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)873 decode_b0_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
874 {
875     int pdt = PDT_MASK & buff[0];
876     sgj_state * jsp = &op->json_st;
877 
878     if (op->do_hex) {
879         hex2stdout(buff, len, no_ascii_4hex(op));
880         return;
881     }
882     switch (pdt) {
883     case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
884          /* now done by decode_block_limits_vpd() in sg_vpd_common.c */
885         break;
886     case PDT_TAPE: case PDT_MCHANGER:
887         sgj_haj_vi_nex(jsp, jop, 2, "TSMC", SGJ_SEP_EQUAL_NO_SPACE,
888                        !!(buff[4] & 0x2), false, "Tape Stream Mirror "
889                        "Capable");
890         sgj_haj_vi_nex(jsp, jop, 2, "WORM", SGJ_SEP_EQUAL_NO_SPACE,
891                        !!(buff[4] & 0x1), false, "Write Once Read Multiple "
892                        "supported");
893         break;
894     case PDT_OSD:
895     default:
896         pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
897         hex2stderr(buff, len, 0);
898         break;
899     }
900 }
901 
902 /* VPD_BLOCK_DEV_CHARS sbc  0xb1 ["bdc"] */
903 /* VPD_MAN_ASS_SN ssc */
904 /* VPD_SECURITY_TOKEN osd */
905 /* VPD_ES_DEV_CHARS ses-4 */
906 static void
decode_b1_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)907 decode_b1_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
908 {
909     int pdt;
910     sgj_state * jsp = &op->json_st;
911 
912     pdt = buff[0] & PDT_MASK;
913     if (op->do_hex) {
914         hex2stdout(buff, len, no_ascii_4hex(op));
915         return;
916     }
917     switch (pdt) {
918     case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
919         /* now done by decode_block_dev_ch_vpd() in sg_vpd_common.c */
920     case PDT_TAPE: case PDT_MCHANGER: case PDT_ADC:
921         sgj_pr_hr(jsp, "  Manufacturer-assigned serial number: %.*s\n",
922                   len - 4, buff + 4);
923         sgj_js_nv_s_len(jsp, jop, "manufacturer_assigned_serial_number",
924                         (const char *)buff + 4, len - 4);
925         break;
926     default:
927         pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
928         hex2stderr(buff, len, 0);
929         break;
930     }
931 }
932 
933 /* VPD_LB_PROVISIONING sbc */
934 /* VPD_TA_SUPPORTED ssc */
935 static void
decode_b2_vpd(uint8_t * buff,int len,int pdt,struct opts_t * op)936 decode_b2_vpd(uint8_t * buff, int len, int pdt, struct opts_t * op)
937 {
938     if (op->do_hex) {
939         hex2stdout(buff, len, no_ascii_4hex(op));
940         return;
941     }
942     switch (pdt) {
943     case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
944         /* decode_block_lb_prov_vpd() is now in sg_vpd_common.c */
945         break;
946     case PDT_TAPE: case PDT_MCHANGER:
947         /* decode_tapealert_supported_vpd() is now in sg_vpd_common.c */
948         break;
949     default:
950         pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
951         hex2stderr(buff, len, 0);
952         break;
953     }
954 }
955 
956 /* VPD_REFERRALS sbc          0xb3 ["ref"] */
957 /* VPD_AUTOMATION_DEV_SN ssc  0xb3 ["adsn"] */
958 static void
decode_b3_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)959 decode_b3_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
960 {
961     int pdt;
962     sgj_state * jsp = &op->json_st;
963 
964     if (op->do_hex) {
965         hex2stdout(buff, len, no_ascii_4hex(op));
966         return;
967     }
968     pdt = buff[0] & PDT_MASK;
969     switch (pdt) {
970     case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
971         /* now done in decode_referrals_vpd() in sg_vpd_common.c */
972         break;
973     case PDT_TAPE: case PDT_MCHANGER:
974         sgj_pr_hr(jsp, "  Automation device serial number: %.*s\n",
975                   len - 4, buff + 4);
976         sgj_js_nv_s_len(jsp, jop, "automation_device_serial_number",
977                         (const char *)buff + 4, len - 4);
978         break;
979     default:
980         pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
981         hex2stderr(buff, len, 0);
982         break;
983     }
984 }
985 
986 /* VPD_SUP_BLOCK_LENS   sbc ["sbl"] */
987 /* VPD_DTDE_ADDRESS ssc */
988 static void
decode_b4_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)989 decode_b4_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
990 {
991     int pdt = buff[0] & PDT_MASK;
992     sgj_state * jsp = &op->json_st;
993 
994     if (op->do_hex) {
995         hex2stdout(buff, len, no_ascii_4hex(op));
996         return;
997     }
998     switch (pdt) {
999     case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1000         /* now done by decode_sup_block_lens_vpd() in sg_vpd_common.c */
1001         break;
1002     case PDT_TAPE: case PDT_MCHANGER:
1003         sgj_pr_hr(jsp, "  Device transfer data element:\n");
1004         if (! jsp->pr_as_json)
1005             hex2stdout(buff + 4, len - 4, 1);
1006         sgj_js_nv_hex_bytes(jsp, jop, "device_transfer_data_element",
1007                             buff + 4, len - 4);
1008         break;
1009     default:
1010         pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
1011         hex2stderr(buff, len, 0);
1012         break;
1013     }
1014 }
1015 
1016 /* VPD_BLOCK_DEV_C_EXTENS sbc */
1017 /* VPD_LB_PROTECTION  0xb5 ["lbpro"] ssc */
1018 static void
decode_b5_vpd(uint8_t * b,int len,int do_hex,int pdt)1019 decode_b5_vpd(uint8_t * b, int len, int do_hex, int pdt)
1020 {
1021     if (do_hex) {
1022         hex2stdout(b, len, (1 == do_hex) ? 0 : -1);
1023         return;
1024     }
1025     switch (pdt) {
1026     case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1027         /* now done by decode_block_dev_char_ext_vpd() in sg_vpd_common.c */
1028         break;
1029     case PDT_TAPE: case PDT_MCHANGER:
1030         /* now done by decode_lb_protection_vpd() in sg_vpd_common.c */
1031         break;
1032     default:
1033         pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
1034         hex2stderr(b, len, 0);
1035         break;
1036     }
1037 }
1038 
1039 /* Returns 0 if successful */
1040 static int
svpd_unable_to_decode(int sg_fd,struct opts_t * op,int subvalue,int off)1041 svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue, int off)
1042 {
1043     bool as_json, json_o_hr, hex0;
1044     int res, len, n;
1045     sgj_state * jsp = &op->json_st;
1046     uint8_t * rp;
1047 
1048     as_json = jsp->pr_as_json;
1049     json_o_hr = as_json && jsp->pr_out_hr;
1050     hex0 = (0 == op->do_hex);
1051     rp = rsp_buff + off;
1052     if (hex0 && (! op->do_raw) && (! op->examine_given))
1053         sgj_pr_hr(jsp, "Only hex output supported\n");
1054     if ((!op->do_raw) && (op->do_hex < 2) && (! op->examine_given)) {
1055         if (subvalue) {
1056             if (hex0)
1057                 sgj_pr_hr(jsp, "VPD page code=0x%.2x, subvalue=0x%.2x:\n",
1058                           op->vpd_pn, subvalue);
1059             else
1060                 printf("VPD page code=0x%.2x, subvalue=0x%.2x:\n", op->vpd_pn,
1061                        subvalue);
1062         } else if (op->vpd_pn >= 0) {
1063             if (hex0)
1064                 sgj_pr_hr(jsp, "VPD page code=0x%.2x:\n", op->vpd_pn);
1065             else
1066                 printf("VPD page code=0x%.2x:\n", op->vpd_pn);
1067         } else {
1068             if (hex0)
1069                 sgj_pr_hr(jsp, "VPD page code=%d:\n", op->vpd_pn);
1070             else
1071                 printf("VPD page code=%d:\n", op->vpd_pn);
1072         }
1073     }
1074 
1075     res = vpd_fetch_page(sg_fd, rp, op->vpd_pn, op->maxlen, op->do_quiet,
1076                          op->verbose, &len);
1077     if (0 == res) {
1078         if (op->do_raw)
1079             dStrRaw(rp, len);
1080         else {
1081            if (json_o_hr && hex0 && (len > 0) && (len < UINT16_MAX)) {
1082                 char * p;
1083 
1084                 n = len * 4;
1085                 p = malloc(n);
1086                 if (p) {
1087                     n = hex2str(rp, len, NULL, 1, n - 1, p);
1088                     sgj_js_str_out(jsp, p, n);
1089                 }
1090             } else
1091                 hex2stdout(rp, len, no_ascii_4hex(op));
1092         }
1093     } else if ((! op->do_quiet) && (! op->examine_given)) {
1094         if (op->vpd_pn >= 0)
1095             pr2serr("fetching VPD page code=0x%.2x: failed\n", op->vpd_pn);
1096         else
1097             pr2serr("fetching VPD page code=%d: failed\n", op->vpd_pn);
1098     }
1099     return res;
1100 }
1101 
1102 static int
recurse_vpd_decode(struct opts_t * op,sgj_opaque_p jop,int off)1103 recurse_vpd_decode(struct opts_t * op, sgj_opaque_p jop, int off)
1104 {
1105     int res = svpd_decode_t10(-1, op, jop, 0, off, NULL);
1106 
1107     if (SG_LIB_CAT_OTHER == res) {
1108         res = svpd_decode_vendor(-1, op, jop, off);
1109         if (SG_LIB_CAT_OTHER == res)
1110             svpd_unable_to_decode(-1, op, 0, off);
1111     }
1112     return res;
1113 }
1114 
1115 /* Returns 0 if successful. If don't know how to decode, returns
1116  * SG_LIB_CAT_OTHER else see sg_ll_inquiry(). */
1117 static int
svpd_decode_t10(int sg_fd,struct opts_t * op,sgj_opaque_p jop,int subvalue,int off,const char * prefix)1118 svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop,
1119                 int subvalue, int off, const char * prefix)
1120 {
1121     bool allow_name, allow_if_found, long_notquiet, qt;
1122     bool vpd_supported = false;
1123     bool inhex_active = (-1 == sg_fd);
1124     bool exam_not_given = ! op->examine_given;
1125     int len, pdt, pqual, num, k, resid, alloc_len, pn, vb;
1126     int res = 0;
1127     sgj_state * jsp = &op->json_st;
1128     uint8_t * rp;
1129     sgj_opaque_p jap = NULL;
1130     sgj_opaque_p jo2p = NULL;
1131     const char * np;
1132     const char * ep;
1133     const char * pre = (prefix ? prefix : "");
1134     const char * pdt_str;
1135     bool as_json = jsp->pr_as_json;
1136     bool not_json = ! as_json;
1137     char obuff[DEF_ALLOC_LEN];
1138     char d[48];
1139 
1140     vb = op->verbose;
1141     qt = op->do_quiet;
1142     long_notquiet = op->do_long && (! op->do_quiet);
1143     if (op->do_raw || (op->do_quiet && (! op->do_long) && (! op->do_all)) ||
1144         (op->do_hex >= 3) || op->examine_given)
1145         allow_name = false;
1146     else
1147         allow_name = true;
1148     allow_if_found = op->examine_given && (! op->do_quiet);
1149     rp = rsp_buff + off;
1150     pn = op->vpd_pn;
1151     if ((off > 0) && (VPD_NOPE_WANT_STD_INQ != op->vpd_pn))
1152         pn = rp[1];
1153     else
1154         pn = op->vpd_pn;
1155     if (!inhex_active && !op->do_force && exam_not_given &&
1156         pn != VPD_NOPE_WANT_STD_INQ &&
1157         pn != VPD_SUPPORTED_VPDS) {
1158         res = vpd_fetch_page(sg_fd, rp, VPD_SUPPORTED_VPDS, op->maxlen, qt,
1159                              vb, &len);
1160         if (res)
1161             return res;
1162 
1163         num = rp[3];
1164         if (num > (len - 4))
1165             num = (len - 4);
1166         if (vb > 1) {
1167             pr2serr("Supported VPD pages, hex list: ");
1168             hex2stderr(rp + 4, num, -1);
1169         }
1170         for (k = 0; k < num; ++k) {
1171             if (pn == rp[4 + k]) {
1172                 vpd_supported = true;
1173                 break;
1174             }
1175         }
1176         if (! vpd_supported) { /* get creative, was SG_LIB_CAT_ILLEGAL_REQ */
1177             if (vb)
1178                 pr2serr("Given VPD page not in supported list, use --force "
1179                         "to override this check\n");
1180             return sg_convert_errno(EDOM);
1181         }
1182     }
1183     pdt = rp[0] & PDT_MASK;
1184     pdt_str = sg_get_pdt_str(pdt, sizeof(d), d);
1185     pqual = (rp[0] & 0xe0) >> 5;
1186 
1187     switch(pn) {
1188     case VPD_NOPE_WANT_STD_INQ:    /* -2 (want standard inquiry response) */
1189         if (!inhex_active) {
1190             if (op->maxlen > 0)
1191                 alloc_len = op->maxlen;
1192             else if (op->do_long)
1193                 alloc_len = DEF_ALLOC_LEN;
1194             else
1195                 alloc_len = 36;
1196             res = sg_ll_inquiry_v2(sg_fd, false, 0, rp, alloc_len,
1197                                    DEF_PT_TIMEOUT, &resid, ! op->do_quiet, vb);
1198         } else {
1199             alloc_len = op->maxlen;
1200             resid = 0;
1201             res = 0;
1202         }
1203         if (0 == res) {
1204             alloc_len -= resid;
1205             if (op->do_raw)
1206                 dStrRaw(rp, alloc_len);
1207             else if (op->do_hex) {
1208                 if (! op->do_quiet && (op->do_hex < 3))
1209                     sgj_pr_hr(jsp, "Standard Inquiry data format:\n");
1210                 hex2stdout(rp, alloc_len, (1 == op->do_hex) ? 0 : -1);
1211             } else
1212                 std_inq_decode(rp, alloc_len, op, jop);
1213             return 0;
1214         }
1215         break;
1216     case VPD_SUPPORTED_VPDS:    /* 0x0 ["sv"] */
1217         np = "Supported VPD pages VPD page";
1218         if (allow_name)
1219             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1220         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1221         if (0 == res) {
1222             if (! allow_name && allow_if_found)
1223                 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1224             if (op->do_raw)
1225                 dStrRaw(rp, len);
1226             else if (op->do_hex)
1227                 hex2stdout(rp, len, no_ascii_4hex(op));
1228             else {
1229                 if (vb || long_notquiet)
1230                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1231                               "%s]\n", pqual, pdt_str);
1232                 num = rp[3];
1233                 if (num > (len - 4))
1234                     num = (len - 4);
1235                 if (as_json) {
1236                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1237                     jap = sgj_named_subarray_r(jsp, jo2p,
1238                                                "supported_vpd_page_list");
1239                 }
1240                 decode_supported_vpd_4vpd(rp, len, op, jap);
1241             }
1242             return 0;
1243         }
1244         break;
1245     case VPD_UNIT_SERIAL_NUM:   /* 0x80 ["sn"] */
1246         np = "Unit serial number VPD page";
1247         if (allow_name && not_json)
1248             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1249         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1250         if (0 == res) {
1251             if (! allow_name && allow_if_found)
1252                 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1253             if (op->do_raw)
1254                 dStrRaw(rp, len);
1255             else if (op->do_hex)
1256                 hex2stdout(rp, len, no_ascii_4hex(op));
1257             else {
1258                 if (vb || long_notquiet)
1259                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1260                               "%s]\n", pqual, pdt_str);
1261                 memset(obuff, 0, sizeof(obuff));
1262                 len -= 4;
1263                 if (len >= (int)sizeof(obuff))
1264                     len = sizeof(obuff) - 1;
1265                 memcpy(obuff, rp + 4, len);
1266                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1267                 sgj_haj_vs(jsp, jo2p, 2, np, SGJ_SEP_COLON_1_SPACE, obuff);
1268             }
1269             return 0;
1270         }
1271         break;
1272     case VPD_DEVICE_ID: /* 0x83 ["di, di_asis, di_lu, di_port, di_target"] */
1273         np = "Device Identification VPD page";
1274         if (allow_name)
1275             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1276         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1277         if (0 == res) {
1278             if (! allow_name && allow_if_found)
1279                 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1280             if (op->do_raw)
1281                 dStrRaw(rp, len);
1282             else if (op->do_hex)
1283                 hex2stdout(rp, len, no_ascii_4hex(op));
1284             else {
1285                 if (vb || long_notquiet)
1286                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1287                               "%s]\n", pqual, pdt_str);
1288                 if (as_json) {
1289                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1290                     jap = sgj_named_subarray_r(jsp, jo2p,
1291                                                "designation_descriptor_list");
1292                 }
1293                 device_id_vpd_variants(rp, len, subvalue, op, jap);
1294             }
1295             return 0;
1296         }
1297         break;
1298     case VPD_SOFTW_INF_ID:      /* 0x84 ["sii"] */
1299         np = "Software interface identification VPD page";
1300         if (allow_name)
1301             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1302         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1303         if (0 == res) {
1304             if (! allow_name && allow_if_found)
1305                 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1306             if (op->do_raw)
1307                 dStrRaw(rp, len);
1308             else {
1309                 if (vb || long_notquiet)
1310                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1311                               "%s]\n", pqual, pdt_str);
1312                 if (as_json) {
1313                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1314                     jap = sgj_named_subarray_r(jsp, jo2p,
1315                                       "software_interface_identifier_list");
1316                 }
1317                 decode_softw_inf_id(rp, len, op, jap);
1318             }
1319             return 0;
1320         }
1321         break;
1322     case VPD_MAN_NET_ADDR:      /* 0x85 ["mna"] */
1323         np= "Management network addresses VPD page";
1324         if (allow_name)
1325             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1326         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1327         if (0 == res) {
1328             if (! allow_name && allow_if_found)
1329                 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1330             if (op->do_raw)
1331                 dStrRaw(rp, len);
1332             else {
1333                 if (as_json) {
1334                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1335                     jap = sgj_named_subarray_r(jsp, jo2p,
1336                                       "network_services_descriptor_list");
1337                 }
1338                 decode_net_man_vpd(rp, len, op, jap);
1339             }
1340             return 0;
1341         }
1342         break;
1343     case VPD_EXT_INQ:           /* 0x86 ["ei"] */
1344         np = "extended INQUIRY data VPD page";
1345         if (allow_name)
1346             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1347         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1348         if (0 == res) {
1349             if (! allow_name && allow_if_found)
1350                 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1351             if (op->do_raw)
1352                 dStrRaw(rp, len);
1353             else {
1354                 bool protect = false;
1355 
1356                 op->protect_not_sure = false;
1357                 if (op->std_inq_a_valid)
1358                      protect = !! (0x1 & op->std_inq_a[5]);
1359                 else if ((sg_fd >= 0) && (! op->do_force)) {
1360                     struct sg_simple_inquiry_resp sir;
1361 
1362                     res = sg_simple_inquiry(sg_fd, &sir, false, vb);
1363                     if (res) {
1364                         if (op->verbose)
1365                             pr2serr("%s: sg_simple_inquiry() failed, "
1366                                     "res=%d\n", __func__, res);
1367                         op->protect_not_sure = true;
1368                     } else
1369                         protect = !!(sir.byte_5 & 0x1); /* SPC-3 and later */
1370                 } else
1371                     op->protect_not_sure = true;
1372                 if (vb || long_notquiet)
1373                     sgj_pr_hr(jsp,"   [PQual=%d  Peripheral device type: "
1374                               "%s]\n", pqual, pdt_str);
1375                 if (as_json)
1376                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1377                 decode_x_inq_vpd(rp, len, protect, op, jo2p);
1378             }
1379             return 0;
1380         }
1381         break;
1382     case VPD_MODE_PG_POLICY:    /* 0x87 */
1383         np = "Mode page policy VPD page";
1384         if (allow_name)
1385             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1386         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1387         if (0 == res) {
1388             if (! allow_name && allow_if_found)
1389                 sgj_pr_hr(jsp, "%s%s:\n", (prefix ? prefix : ""), np);
1390             if (op->do_raw)
1391                 dStrRaw(rp, len);
1392             else {
1393                 if (vb || long_notquiet)
1394                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1395                               "%s]\n", pqual, pdt_str);
1396                 if (as_json) {
1397                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1398                     jap = sgj_named_subarray_r(jsp, jo2p,
1399                                       "mode_page_policy_descriptor_list");
1400                 }
1401                 decode_mode_policy_vpd(rp, len, op, jap);
1402             }
1403             return 0;
1404         }
1405         break;
1406     case VPD_SCSI_PORTS:        /* 0x88  ["sp"] */
1407         np = "SCSI Ports VPD page";
1408         if (allow_name)
1409             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1410         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1411         if (0 == res) {
1412             if (! allow_name && allow_if_found)
1413                 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1414             if (op->do_raw)
1415                 dStrRaw(rp, len);
1416             else {
1417                 if (vb || long_notquiet)
1418                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1419                               "%s]\n", pqual, pdt_str);
1420                 if (as_json) {
1421                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1422                     jap = sgj_named_subarray_r(jsp, jo2p,
1423                                                "scsi_ports_descriptor_list");
1424                 }
1425                 decode_scsi_ports_vpd_4vpd(rp, len, op, jap);
1426             }
1427             return 0;
1428         }
1429         break;
1430     case VPD_ATA_INFO:          /* 0x89 ['ai"] */
1431         np = "ATA information VPD page";
1432         if (allow_name)
1433             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1434         alloc_len = op->maxlen ? op->maxlen : VPD_ATA_INFO_LEN;
1435         res = vpd_fetch_page(sg_fd, rp, pn, alloc_len, qt, vb, &len);
1436         if (0 == res) {
1437             if (! allow_name && allow_if_found)
1438                 sgj_pr_hr(jsp, "%s%s:\n", (prefix ? prefix : ""), np);
1439             if ((2 == op->do_raw) || (3 == op->do_hex)) {  /* for hdparm */
1440                 if (len < (60 + 512))
1441                     pr2serr("ATA_INFO VPD page len (%d) less than expected "
1442                             "572\n", len);
1443                 else
1444                     dWordHex((const unsigned short *)(rp + 60), 256, -2,
1445                              sg_is_big_endian());
1446             }
1447             else if (op->do_raw)
1448                 dStrRaw(rp, len);
1449             else {
1450                 if (vb || long_notquiet)
1451                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1452                               "%s]\n", pqual, pdt_str);
1453                 if (as_json)
1454                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1455                 decode_ata_info_vpd(rp, len, op, jo2p);
1456             }
1457             return 0;
1458         }
1459         break;
1460     case VPD_POWER_CONDITION:          /* 0x8a ["pc"] */
1461         np = "Power condition VPD page:";
1462         if (allow_name)
1463             sgj_pr_hr(jsp, "%s%s\n", pre, np);
1464         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1465         if (0 == res) {
1466             if (! allow_name && allow_if_found)
1467                 sgj_pr_hr(jsp,  "%s%s\n", pre, np);
1468             if (op->do_raw)
1469                 dStrRaw(rp, len);
1470             else {
1471                 if (vb || long_notquiet)
1472                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1473                               "%s]\n", pqual, pdt_str);
1474                 if (as_json)
1475                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1476                 decode_power_condition(rp, len, op, jo2p);
1477             }
1478             return 0;
1479         }
1480         break;
1481     case VPD_DEVICE_CONSTITUENTS:      /* 0x8b  ["dc"] */
1482         np = "Device constituents VPD page";
1483         if (allow_name)
1484             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1485         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1486         if (0 == res) {
1487             if (! allow_name && allow_if_found)
1488                 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1489             if (op->do_raw)
1490                 dStrRaw(rp, len);
1491             else {
1492                 if (vb || long_notquiet)
1493                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1494                               "%s]\n", pqual, pdt_str);
1495                 if (as_json) {
1496                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1497                     jap = sgj_named_subarray_r(jsp, jo2p,
1498                                                "constituent_descriptor_list");
1499                 }
1500                 decode_dev_constit_vpd(rp, len, op, jap, recurse_vpd_decode);
1501             }
1502             return 0;
1503         }
1504         break;
1505     case VPD_CFA_PROFILE_INFO:    /* 0x8c ["cfa"] */
1506         np = "CFA profile information VPD page";
1507         if (allow_name)
1508             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1509         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1510         if (0 == res) {
1511             if (! allow_name && allow_if_found)
1512                 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1513             if (op->do_raw)
1514                 dStrRaw(rp, len);
1515             else {
1516                 if (vb || long_notquiet)
1517                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1518                               "%s]\n", pqual, pdt_str);
1519                 if (as_json) {
1520                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1521                     jap = sgj_named_subarray_r(jsp, jo2p,
1522                                       "cfa_profile_descriptor_list");
1523                 }
1524                 decode_cga_profile_vpd(rp, len, op, jap);
1525             }
1526             return 0;
1527         }
1528         break;
1529     case VPD_POWER_CONSUMPTION:    /* 0x8d ["psm"] */
1530         np = "Power consumption VPD page";
1531         if (allow_name)
1532             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1533         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1534         if (0 == res) {
1535             if (! allow_name && allow_if_found)
1536                 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1537             if (op->do_raw)
1538                 dStrRaw(rp, len);
1539             else {
1540                 if (vb || long_notquiet)
1541                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1542                               "%s]\n", pqual, pdt_str);
1543                 if (as_json) {
1544                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1545                     jap = sgj_named_subarray_r(jsp, jo2p,
1546                                       "power_consumption_descriptor_list");
1547                 }
1548                 decode_power_consumption(rp, len, op, jap);
1549             }
1550             return 0;
1551         }
1552         break;
1553     case VPD_3PARTY_COPY:   /* 0x8f */
1554         np = "Third party copy VPD page";       /* ["tpc"] */
1555         if (allow_name)
1556             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1557         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1558         if (0 == res) {
1559             if (! allow_name && allow_if_found)
1560                 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1561             if (op->do_raw)
1562                 dStrRaw(rp, len);
1563             else {
1564                 if (vb || long_notquiet)
1565                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1566                               "%s]\n", pqual, pdt_str);
1567                 if (as_json) {
1568                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1569                     jap = sgj_named_subarray_r(jsp, jo2p,
1570                                       "third_party_copy_descriptors");
1571                 }
1572                 decode_3party_copy_vpd(rp, len, op, jap);
1573             }
1574             return 0;
1575         }
1576         break;
1577     case VPD_PROTO_LU:          /* 0x90 ["pslu"] */
1578         np = "Protocol-specific logical unit information VPD page";
1579         if (allow_name)
1580             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1581         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1582         if (0 == res) {
1583             if (! allow_name && allow_if_found)
1584                 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1585             if (op->do_raw)
1586                 dStrRaw(rp, len);
1587             else {
1588                 if (vb || long_notquiet)
1589                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1590                               "%s]\n", pqual, pdt_str);
1591                 if (as_json) {
1592                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1593                     jap = sgj_named_subarray_r(jsp, jo2p,
1594                               "logical_unit_information_descriptor_list");
1595                 }
1596                 decode_proto_lu_vpd(rp, len, op, jap);
1597             }
1598             return 0;
1599         }
1600         break;
1601     case VPD_PROTO_PORT:        /* 0x91  ["pspo"] */
1602         np = "Protocol-specific port VPD page";
1603         if (allow_name)
1604             sgj_pr_hr(jsp, "%s%s\n", pre, np);
1605         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1606         if (0 == res) {
1607             if (! allow_name && allow_if_found)
1608                 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1609             if (op->do_raw)
1610                 dStrRaw(rp, len);
1611             else {
1612                 if (vb || long_notquiet)
1613                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1614                               "%s]\n", pqual, pdt_str);
1615                 if (as_json) {
1616                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1617                     jap = sgj_named_subarray_r(jsp, jo2p,
1618                               "port_information_descriptor_list");
1619                 }
1620                 decode_proto_port_vpd(rp, len, op, jap);
1621             }
1622             return 0;
1623         }
1624         break;
1625     case VPD_SCSI_FEATURE_SETS:         /* 0x92  ["sfs"] */
1626         np = "SCSI Feature sets VPD page";
1627         if (allow_name)
1628             sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1629         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1630         if (0 == res) {
1631             if (! allow_name && allow_if_found)
1632                 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1633             if (op->do_raw)
1634                 dStrRaw(rp, len);
1635             else {
1636                 if (vb || long_notquiet)
1637                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1638                               "%s]\n", pqual, pdt_str);
1639                 if (as_json) {
1640                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1641                     jap = sgj_named_subarray_r(jsp, jo2p,
1642                                       "feature_set_code_list");
1643                 }
1644                 decode_feature_sets_vpd(rp, len, op, jap);
1645             }
1646             return 0;
1647         }
1648         break;
1649     case 0xb0:  /* depends on pdt */
1650         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1651         if (0 == res) {
1652             bool bl = false;
1653             bool sad = false;
1654             bool oi = false;
1655 
1656             switch (pdt) {
1657             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1658                 np = "Block limits VPD page";
1659                 ep = "(SBC)";
1660                 bl = true;
1661                 break;
1662             case PDT_TAPE: case PDT_MCHANGER:
1663                 np = "Sequential-access device capabilities VPD page";
1664                 ep = "(SSC)";
1665                 sad = true;
1666                 break;
1667             case PDT_OSD:
1668                 np = "OSD information VPD page";
1669                 ep = "(OSD)";
1670                 oi = true;
1671                 break;
1672             default:
1673                 np = NULL;
1674                 break;
1675             }
1676             if (NULL == np)
1677                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1678             else if (allow_name || allow_if_found)
1679                 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1680             if (op->do_raw)
1681                 dStrRaw(rp, len);
1682             else {
1683                 if (vb || long_notquiet)
1684                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1685                               "%s]\n", pqual, pdt_str);
1686                 if (as_json)
1687                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1688                 if (bl)
1689                     decode_block_limits_vpd(rp, len, op, jo2p);
1690                 else if (sad) {
1691                     decode_b0_vpd(rp, len, op, jop);
1692                 } else if (oi) {
1693                     decode_b0_vpd(rp, len, op, jop);
1694                 } else {
1695 
1696                 }
1697             }
1698             return 0;
1699         } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1700                    exam_not_given)
1701             sgj_pr_hr(jsp, "%sVPD page=0xb0\n", pre);
1702         break;
1703     case 0xb1:  /* depends on pdt */
1704         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1705         if (0 == res) {
1706             bool bdc = false;
1707             static const char * masn =
1708                         "Manufactured-assigned serial number VPD page";
1709 
1710             switch (pdt) {
1711             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1712                 np = "Block device characteristics VPD page";
1713                 ep = "(SBC)";
1714                 bdc = true;
1715                 break;
1716             case PDT_TAPE: case PDT_MCHANGER:
1717                 np = masn;
1718                 ep = "(SSC)";
1719                 break;
1720             case PDT_OSD:
1721                 np = "Security token VPD page";
1722                 ep = "(OSD)";
1723                 break;
1724             case PDT_ADC:
1725                 np = masn;
1726                 ep = "(ADC)";
1727                 break;
1728             default:
1729                 np = NULL;
1730                 break;
1731             }
1732             if (NULL == np)
1733                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1734             else if (allow_name || allow_if_found)
1735                 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1736             if (op->do_raw)
1737                 dStrRaw(rp, len);
1738             else {
1739                 if (vb || long_notquiet)
1740                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1741                               "%s]\n", pqual, pdt_str);
1742                 if (as_json)
1743                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1744                 if (bdc)
1745                     decode_block_dev_ch_vpd(rp, len, op, jo2p);
1746                 else
1747                     decode_b1_vpd(rp, len, op, jo2p);
1748             }
1749             return 0;
1750         } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1751                    exam_not_given)
1752             sgj_pr_hr(jsp, "%sVPD page=0xb1\n", pre);
1753         break;
1754     case 0xb2:          /* VPD page depends on pdt */
1755         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1756         if (0 == res) {
1757             bool lbpv = false;
1758             bool tas = false;
1759 
1760             switch (pdt) {
1761             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1762                 np = "Logical block provisioning VPD page";
1763                 ep = "(SBC)";
1764                 lbpv = true;
1765                 break;
1766             case PDT_TAPE: case PDT_MCHANGER:
1767                 np = "TapeAlert supported flags VPD page";
1768                 ep = "(SSC)";
1769                 tas = true;
1770                 break;
1771             default:
1772                 np = NULL;
1773                 break;
1774             }
1775             if (NULL == np)
1776                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1777             else if (allow_name || allow_if_found)
1778                 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1779             if (op->do_raw)
1780                 dStrRaw(rp, len);
1781             else {
1782                 if (vb || long_notquiet)
1783                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1784                               "%s]\n", pqual, pdt_str);
1785                 if (as_json)
1786                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1787                 if (lbpv)
1788                     decode_block_lb_prov_vpd(rp, len, op, jo2p);
1789                 else if (tas)
1790                     decode_tapealert_supported_vpd(rp, len, op, jo2p);
1791                 else
1792                     decode_b2_vpd(rp, len, pdt, op);
1793             }
1794             return 0;
1795         } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1796                    exam_not_given)
1797             sgj_pr_hr(jsp, "%sVPD page=0xb2\n", pre);
1798         break;
1799     case 0xb3:          /* VPD page depends on pdt */
1800         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1801         if (0 == res) {
1802             bool ref = false;
1803 
1804             pdt = rp[0] & PDT_MASK;
1805             switch (pdt) {
1806             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1807                 np = "Referrals VPD page";
1808                 ep = "(SBC)";
1809                 ref = true;
1810                 break;
1811             case PDT_TAPE: case PDT_MCHANGER:
1812                 np = "Automation device serial number VPD page";
1813                 ep = "(SSC)";
1814                 break;
1815             default:
1816                 np = NULL;
1817                 break;
1818             }
1819             if (NULL == np)
1820                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1821             else if (allow_name || allow_if_found)
1822                 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1823             if (op->do_raw)
1824                 dStrRaw(rp, len);
1825             else {
1826                 if (vb || long_notquiet)
1827                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1828                               "%s]\n", pqual, pdt_str);
1829                 if (as_json)
1830                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1831                 if (ref)
1832                     decode_referrals_vpd(rp, len, op, jo2p);
1833                 else
1834                     decode_b3_vpd(rp, len, op, jo2p);
1835             }
1836             return 0;
1837         } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1838                    exam_not_given)
1839             sgj_pr_hr(jsp, "%sVPD page=0xb3\n", pre);
1840         break;
1841     case 0xb4:          /* VPD page depends on pdt */
1842         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1843         if (0 == res) {
1844             bool sbl = false;
1845 
1846             pdt = rp[0] & PDT_MASK;
1847             switch (pdt) {
1848             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1849                 np = "Supported block lengths and protection types VPD page";
1850                 ep = "(SBC)";
1851                 sbl = true;
1852                 break;
1853             case PDT_TAPE: case PDT_MCHANGER:
1854                 np = "Data transfer device element address";
1855                 ep = "(SSC)";
1856                 break;
1857             default:
1858                 np = NULL;
1859                 break;
1860             }
1861             if (NULL == np)
1862                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1863             else if (allow_name || allow_if_found)
1864                 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1865             if (op->do_raw)
1866                 dStrRaw(rp, len);
1867             else {
1868                 if (vb || long_notquiet)
1869                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1870                               "%s]\n", pqual, pdt_str);
1871                 if (as_json)
1872                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1873                 if (sbl) {
1874                     if (as_json)
1875                         jap = sgj_named_subarray_r(jsp, jo2p, "logical_block_"
1876                                 "length_and_protection_types_descriptor_list");
1877                     decode_sup_block_lens_vpd(rp, len, op, jap);
1878                 } else
1879                     decode_b4_vpd(rp, len, op, jo2p);
1880             }
1881             return 0;
1882         } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1883                    exam_not_given)
1884             sgj_pr_hr(jsp, "%sVPD page=0xb4\n", pre);
1885         break;
1886     case 0xb5:          /* VPD page depends on pdt */
1887         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1888         if (0 == res) {
1889             bool bdce = false;
1890             bool lbp = false;
1891 
1892             pdt = rp[0] & PDT_MASK;
1893             switch (pdt) {
1894             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1895                 np = "Block device characteristics extension VPD page";
1896                 ep = "(SBC)";
1897                 bdce = true;
1898                 break;
1899             case PDT_TAPE: case PDT_MCHANGER:
1900                 np = "Logical block protection VPD page";
1901                 ep = "(SSC)";
1902                 lbp = true;
1903                 break;
1904             default:
1905                 np = NULL;
1906                 break;
1907             }
1908             if (NULL == np)
1909                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1910             else if (allow_name || allow_if_found)
1911                 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1912             if (op->do_raw)
1913                 dStrRaw(rp, len);
1914             else {
1915                 if (vb || long_notquiet)
1916                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1917                               "%s]\n", pqual, pdt_str);
1918                 if (as_json)
1919                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1920                 if (bdce)
1921                     decode_block_dev_char_ext_vpd(rp, len, op, jo2p);
1922                 else if (lbp) {
1923                     if (as_json)
1924                         jap = sgj_named_subarray_r(jsp, jo2p,
1925                          "logical_block_protection_method_descriptor_list");
1926                      decode_lb_protection_vpd(rp, len, op, jap);
1927                 } else
1928                     decode_b5_vpd(rp, len, op->do_hex, pdt);
1929             }
1930             return 0;
1931         } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1932                    exam_not_given)
1933             sgj_pr_hr(jsp, "%sVPD page=0xb5\n", pre);
1934         break;
1935     case VPD_ZBC_DEV_CHARS:       /* 0xb6 for both pdt=0 and pdt=0x14 */
1936         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1937         if (0 == res) {
1938             bool zbdch = false;
1939 
1940             pdt = rp[0] & PDT_MASK;
1941             switch (pdt) {
1942             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1943                 np = "Zoned block device characteristics VPD page";
1944                 ep = "(SBC, ZBC)";
1945                 zbdch = true;
1946                 break;
1947             default:
1948                 np = NULL;
1949                 break;
1950             }
1951             if (NULL == np)
1952                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1953             else if (allow_name || allow_if_found)
1954                 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1955             if (op->do_raw)
1956                 dStrRaw(rp, len);
1957             else {
1958                 if (vb || long_notquiet)
1959                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1960                               "%s]\n", pqual, pdt_str);
1961                 if (as_json)
1962                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1963                 if (zbdch)
1964                     decode_zbdch_vpd(rp, len, op, jo2p);
1965                 else
1966                     return SG_LIB_CAT_OTHER;
1967             }
1968             return 0;
1969         } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1970                    exam_not_given)
1971             sgj_pr_hr(jsp, "%sVPD page=0xb6\n", pre);
1972         break;
1973     case 0xb7:
1974         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1975         if (0 == res) {
1976             bool ble = false;
1977 
1978             pdt = rp[0] & PDT_MASK;
1979             switch (pdt) {
1980             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1981                 np = "Block limits extension VPD page";
1982                 ep = "(SBC)";
1983                 ble = true;
1984                 break;
1985             default:
1986                 np = NULL;
1987                 break;
1988             }
1989             if (NULL == np)
1990                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1991             else if (allow_name || allow_if_found)
1992                 sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : "");
1993             if (op->do_raw)
1994                 dStrRaw(rp, len);
1995             else {
1996                 if (vb || long_notquiet)
1997                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
1998                               "%s]\n", pqual, pdt_str);
1999                 if (as_json)
2000                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2001                 if (ble)
2002                     decode_block_limits_ext_vpd(rp, len, op, jo2p);
2003                 else
2004                     return SG_LIB_CAT_OTHER;
2005             }
2006             return 0;
2007         } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
2008                    exam_not_given)
2009             sgj_pr_hr(jsp, "%sVPD page=0xb7\n", pre);
2010         break;
2011     case 0xb8:          /* VPD_FORMAT_PRESETS */
2012         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
2013         if (0 == res) {
2014             bool fp = false;
2015 
2016             pdt = rp[0] & PDT_MASK;
2017             switch (pdt) {
2018             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
2019                 np = "Format presets VPD page";
2020                 ep = "(SBC)";
2021                 fp = true;
2022                 break;
2023             default:
2024                 np = NULL;
2025                 break;
2026             }
2027             if (NULL == np)
2028                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
2029             else if (allow_name || allow_if_found)
2030                 sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : "");
2031             if (op->do_raw)
2032                 dStrRaw(rp, len);
2033             else {
2034                 if (vb || long_notquiet)
2035                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
2036                               "%s]\n", pqual, pdt_str);
2037                 if (as_json) {
2038                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2039                     jap = sgj_named_subarray_r(jsp, jo2p, "format_preset_"
2040                                                "descriptor_list");
2041                 }
2042                 if (fp)
2043                     decode_format_presets_vpd(rp, len, op, jap);
2044                 else
2045                     return SG_LIB_CAT_OTHER;
2046             }
2047             return 0;
2048         } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
2049                    exam_not_given)
2050             sgj_pr_hr(jsp, "%sVPD page=0xb8\n", pre);
2051         break;
2052     case 0xb9:          /* VPD_CON_POS_RANGE */
2053         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
2054         if (0 == res) {
2055             bool cpr = false;           /* ["cpr"] */
2056 
2057             pdt = rp[0] & PDT_MASK;
2058             switch (pdt) {
2059             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
2060                 np = "Concurrent positioning ranges VPD page";
2061                 ep = "(SBC)";
2062                 cpr = true;
2063                 break;
2064             default:
2065                 np = NULL;
2066                 break;
2067             }
2068             if (NULL == np)
2069                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
2070             else if (allow_name || allow_if_found)
2071                 sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : "");
2072             if (op->do_raw)
2073                 dStrRaw(rp, len);
2074             else {
2075                 if (vb || long_notquiet)
2076                     sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device type: "
2077                               "%s]\n", pqual, pdt_str);
2078                 if (as_json) {
2079                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2080                     jap = sgj_named_subarray_r(jsp, jo2p, "lba_range_"
2081                                                "descriptor_list");
2082                 }
2083                 if (cpr)
2084                     decode_con_pos_range_vpd(rp, len, op, jap);
2085                 else
2086                     return SG_LIB_CAT_OTHER;
2087             }
2088             return 0;
2089         } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
2090                    exam_not_given)
2091             sgj_pr_hr(jsp, "%sVPD page=0xb8\n", pre);
2092         break;
2093     default:
2094         return SG_LIB_CAT_OTHER;
2095     }
2096     return res;
2097 }
2098 
2099 static int
svpd_decode_all(int sg_fd,struct opts_t * op,sgj_opaque_p jop)2100 svpd_decode_all(int sg_fd, struct opts_t * op, sgj_opaque_p jop)
2101 {
2102     int k, res, rlen, n, pn;
2103     int max_pn = 255;
2104     int any_err = 0;
2105     sgj_state * jsp = &op->json_st;
2106     uint8_t vpd0_buff[512];
2107     uint8_t * rp = vpd0_buff;
2108 
2109     if (op->vpd_pn > 0)
2110         max_pn = op->vpd_pn;
2111     if (sg_fd >= 0) {   /* have valid open file descriptor (handle) */
2112         res = vpd_fetch_page(sg_fd, rp, VPD_SUPPORTED_VPDS, op->maxlen,
2113                              op->do_quiet, op->verbose, &rlen);
2114         if (res) {
2115             if (! op->do_quiet) {
2116                 if (SG_LIB_CAT_ABORTED_COMMAND == res)
2117                     pr2serr("%s: VPD page 0, aborted command\n", __func__);
2118                 else if (res) {
2119                     char b[80];
2120 
2121                     sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
2122                     pr2serr("%s: fetching VPD page 0 failed: %s\n", __func__,
2123                             b);
2124                 }
2125             }
2126             return res;
2127         }
2128         n = sg_get_unaligned_be16(rp + 2);
2129         if (n > (rlen - 4)) {
2130             if (op->verbose)
2131                 pr2serr("%s: rlen=%d > page0 size=%d\n", __func__, rlen,
2132                         n + 4);
2133             n = (rlen - 4);
2134         }
2135         for (k = 0; k < n; ++k) {
2136             pn = rp[4 + k];
2137             if (pn > max_pn)
2138                 continue;
2139             op->vpd_pn = pn;
2140             if (k > 0)
2141                 sgj_pr_hr(jsp, "\n");
2142             if (op->do_long) {
2143                 if (jsp->pr_as_json)
2144                     sgj_pr_hr(jsp, "[0x%x]:\n", pn);
2145                 else
2146                     printf("[0x%x] ", pn);
2147             }
2148 
2149             res = svpd_decode_t10(sg_fd, op, jop, 0, 0, NULL);
2150             if (SG_LIB_CAT_OTHER == res) {
2151                 res = svpd_decode_vendor(sg_fd, op, jop, 0);
2152                 if (SG_LIB_CAT_OTHER == res)
2153                     res = svpd_unable_to_decode(sg_fd, op, 0, 0);
2154             }
2155             if (! op->do_quiet) {
2156                 if (SG_LIB_CAT_ABORTED_COMMAND == res)
2157                     pr2serr("fetching VPD page failed, aborted command\n");
2158                 else if (res) {
2159                     char b[80];
2160 
2161                     sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
2162                     pr2serr("fetching VPD page failed: %s\n", b);
2163                 }
2164             }
2165             if (res)
2166                 any_err = res;
2167         }
2168         res = any_err;
2169     } else {    /* input is coming from --inhex=FN */
2170         int bump, off;
2171         int in_len = op->maxlen;
2172         int prev_pn = -1;
2173 
2174         res = 0;
2175         if (op->page_given && (VPD_NOPE_WANT_STD_INQ == op->vpd_pn))
2176             return svpd_decode_t10(-1, op, jop, 0, 0, NULL);
2177 
2178         for (k = 0, off = 0; off < in_len; ++k, off += bump) {
2179             rp = rsp_buff + off;
2180             pn = rp[1];
2181             bump = sg_get_unaligned_be16(rp + 2) + 4;
2182             if ((off + bump) > in_len) {
2183                 pr2serr("%s: page 0x%x size (%d) exceeds buffer\n", __func__,
2184                         pn, bump);
2185                 bump = in_len - off;
2186             }
2187             if (op->page_given && (pn != op->vpd_pn))
2188                 continue;
2189             if (pn <= prev_pn) {
2190                 pr2serr("%s: prev_pn=0x%x, this pn=0x%x, not ascending so "
2191                         "exit\n", __func__, prev_pn, pn);
2192                 break;
2193             }
2194             prev_pn = pn;
2195             op->vpd_pn = pn;
2196             if (pn > max_pn) {
2197                 if (op->verbose > 2)
2198                     pr2serr("%s: skipping as this pn=0x%x exceeds "
2199                             "max_pn=0x%x\n", __func__, pn, max_pn);
2200                 continue;
2201             }
2202             if (op->do_long) {
2203                 if (jsp->pr_as_json)
2204                     sgj_pr_hr(jsp, "[0x%x]:\n", pn);
2205                 else
2206                     printf("[0x%x] ", pn);
2207             }
2208 
2209             res = svpd_decode_t10(-1, op, jop, 0, off, NULL);
2210             if (SG_LIB_CAT_OTHER == res) {
2211                 res = svpd_decode_vendor(-1, op, jop, off);
2212                 if (SG_LIB_CAT_OTHER == res)
2213                     res = svpd_unable_to_decode(-1, op, 0, off);
2214             }
2215         }
2216     }
2217     return res;
2218 }
2219 
2220 static int
svpd_examine_all(int sg_fd,struct opts_t * op,sgj_opaque_p jop)2221 svpd_examine_all(int sg_fd, struct opts_t * op, sgj_opaque_p jop)
2222 {
2223     bool first = true;
2224     bool got_one = false;
2225     int k, res, start;
2226     int max_pn;
2227     int any_err = 0;
2228     sgj_state * jsp = &op->json_st;
2229     char b[80];
2230 
2231     max_pn = (op->page_given ? op->vpd_pn : 0xff);
2232     switch (op->examine) {
2233     case 1:
2234         start = 0x80;
2235         break;
2236     case 2:
2237         start = 0x0;
2238         break;
2239     default:
2240         start = 0xc0;
2241         break;
2242     }
2243     if (start > max_pn) {       /* swap them around */
2244         k = start;
2245         start = max_pn;
2246         max_pn = k;
2247     }
2248     for (k = start; k <= max_pn; ++k) {
2249         op->vpd_pn = k;
2250         if (first)
2251             first = false;
2252         else if (got_one) {
2253             sgj_pr_hr(jsp, "\n");
2254             got_one = false;
2255         }
2256         if (op->do_long)
2257             snprintf(b, sizeof(b), "[0x%x] ", k);
2258         else
2259             b[0] = '\0';
2260         res = svpd_decode_t10(sg_fd, op, jop, 0, 0, b);
2261         if (SG_LIB_CAT_OTHER == res) {
2262             res = svpd_decode_vendor(sg_fd, op, jop, 0);
2263             if (SG_LIB_CAT_OTHER == res)
2264                 res = svpd_unable_to_decode(sg_fd, op, 0, 0);
2265         }
2266         if (! op->do_quiet) {
2267             if (SG_LIB_CAT_ABORTED_COMMAND == res)
2268                 pr2serr("fetching VPD page failed, aborted command\n");
2269             else if (res && (SG_LIB_CAT_ILLEGAL_REQ != res)) {
2270                 /* SG_LIB_CAT_ILLEGAL_REQ expected as well examine all */
2271                 sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
2272                 pr2serr("fetching VPD page failed: %s\n", b);
2273             }
2274         }
2275         if (res && (SG_LIB_CAT_ILLEGAL_REQ != res))
2276             any_err = res;
2277         if (0 == res)
2278             got_one = true;
2279     }
2280     return any_err;
2281 }
2282 
2283 
2284 int
main(int argc,char * argv[])2285 main(int argc, char * argv[])
2286 {
2287     bool as_json;
2288     int c, res, matches;
2289     int sg_fd = -1;
2290     int inhex_len = 0;
2291     int inraw_len = 0;
2292     int ret = 0;
2293     int subvalue = 0;
2294     const char * cp;
2295     sgj_state * jsp;
2296     sgj_opaque_p jop = NULL;
2297     const struct svpd_values_name_t * vnp;
2298     struct opts_t opts SG_C_CPP_ZERO_INIT;
2299     struct opts_t * op = &opts;
2300 
2301     op->invoker = SG_VPD_INV_SG_VPD;
2302     dup_sanity_chk((int)sizeof(opts), (int)sizeof(*vnp));
2303     op->vend_prod_num = -1;
2304     while (1) {
2305         int option_index = 0;
2306 
2307         c = getopt_long(argc, argv, "aeEfhHiI:j::lm:M:p:qQ:rvV", long_options,
2308                         &option_index);
2309         if (c == -1)
2310             break;
2311 
2312         switch (c) {
2313         case 'a':
2314             op->do_all = true;
2315             break;
2316         case 'e':
2317             op->do_enum = true;
2318             break;
2319         case 'E':
2320             ++op->examine;
2321             op->examine_given = true;
2322             break;
2323         case 'f':
2324             op->do_force = true;
2325             break;
2326         case 'h':
2327         case '?':
2328             usage();
2329             return 0;
2330         case 'H':
2331             ++op->do_hex;
2332             break;
2333         case 'i':
2334             ++op->do_ident;
2335             break;
2336         case 'I':
2337             if (op->inhex_fn) {
2338                 pr2serr("only one '--inhex=' option permitted\n");
2339                 usage();
2340                 return SG_LIB_SYNTAX_ERROR;
2341             } else
2342                 op->inhex_fn = optarg;
2343             break;
2344         case 'j':
2345             if (! sgj_init_state(&op->json_st, optarg)) {
2346                 int bad_char = op->json_st.first_bad_char;
2347                 char e[1500];
2348 
2349                 if (bad_char) {
2350                     pr2serr("bad argument to --json= option, unrecognized "
2351                             "character '%c'\n\n", bad_char);
2352                 }
2353                 sg_json_usage(0, e, sizeof(e));
2354                 pr2serr("%s", e);
2355                 return SG_LIB_SYNTAX_ERROR;
2356             }
2357             break;
2358         case 'l':
2359             op->do_long = true;
2360             break;
2361         case 'm':
2362             op->maxlen = sg_get_num(optarg);
2363             if ((op->maxlen < 0) || (op->maxlen > MX_ALLOC_LEN)) {
2364                 pr2serr("argument to '--maxlen' should be %d or less\n",
2365                         MX_ALLOC_LEN);
2366                 return SG_LIB_SYNTAX_ERROR;
2367             }
2368             if ((op->maxlen > 0) && (op->maxlen < MIN_MAXLEN)) {
2369                 pr2serr("Warning: overriding '--maxlen' < %d, using "
2370                         "default\n", MIN_MAXLEN);
2371                 op->maxlen = 0;
2372             }
2373             break;
2374         case 'M':
2375             if (op->vend_prod) {
2376                 pr2serr("only one '--vendor=' option permitted\n");
2377                 usage();
2378                 return SG_LIB_SYNTAX_ERROR;
2379             } else
2380                 op->vend_prod = optarg;
2381             break;
2382         case 'p':
2383             if (op->page_str) {
2384                 pr2serr("only one '--page=' option permitted\n");
2385                 usage();
2386                 return SG_LIB_SYNTAX_ERROR;
2387             } else
2388                 op->page_str = optarg;
2389             op->page_given = true;
2390             break;
2391         case 'q':
2392             op->do_quiet = true;
2393             break;
2394         case 'Q':
2395             op->sinq_inraw_fn = optarg;
2396             break;
2397         case 'r':
2398             ++op->do_raw;
2399             break;
2400         case 'v':
2401             op->verbose_given = true;
2402             ++op->verbose;
2403             break;
2404         case 'V':
2405             op->version_given = true;
2406             break;
2407         default:
2408             pr2serr("unrecognised option code 0x%x ??\n", c);
2409             usage();
2410             return SG_LIB_SYNTAX_ERROR;
2411         }
2412     }
2413     if (optind < argc) {
2414         if (NULL == op->device_name) {
2415             op->device_name = argv[optind];
2416             ++optind;
2417         }
2418         if (optind < argc) {
2419             for (; optind < argc; ++optind)
2420                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
2421             usage();
2422             return SG_LIB_SYNTAX_ERROR;
2423         }
2424     }
2425 
2426 #ifdef DEBUG
2427     pr2serr("In DEBUG mode, ");
2428     if (op->verbose_given && op->version_given) {
2429         pr2serr("but override: '-vV' given, zero verbose and continue\n");
2430         op->verbose_given = false;
2431         op->version_given = false;
2432         op->verbose = 0;
2433     } else if (! op->verbose_given) {
2434         pr2serr("set '-vv'\n");
2435         op->verbose = 2;
2436     } else
2437         pr2serr("keep verbose=%d\n", op->verbose);
2438 #else
2439     if (op->verbose_given && op->version_given)
2440         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
2441 #endif
2442     if (op->version_given) {
2443         pr2serr("version: %s\n", version_str);
2444         return 0;
2445     }
2446 
2447     jsp = &op->json_st;
2448     if (op->do_enum) {
2449         if (op->device_name)
2450             pr2serr("Device name %s ignored when --enumerate given\n",
2451                     op->device_name);
2452         if (op->vend_prod) {
2453             if (isdigit((uint8_t)op->vend_prod[0])) {
2454                 op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
2455                 if ((op->vend_prod_num < 0) || (op->vend_prod_num > 10)) {
2456                     pr2serr("Bad vendor/product number after '--vendor=' "
2457                             "option\n");
2458                     return SG_LIB_SYNTAX_ERROR;
2459                 }
2460             } else {
2461                 op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod);
2462                 if (op->vend_prod_num < 0) {
2463                     pr2serr("Bad vendor/product acronym after '--vendor=' "
2464                             "option\n");
2465                     return SG_LIB_SYNTAX_ERROR;
2466                 }
2467             }
2468             svpd_enumerate_vendor(op->vend_prod_num);
2469             return 0;
2470         }
2471         if (op->page_str) {
2472             if ((0 == strcmp("-1", op->page_str)) ||
2473                 (0 == strcmp("-2", op->page_str)))
2474                 op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
2475             else if (isdigit((uint8_t)op->page_str[0])) {
2476                 op->vpd_pn = sg_get_num_nomult(op->page_str);
2477                 if ((op->vpd_pn < 0) || (op->vpd_pn > 255)) {
2478                     pr2serr("Bad page code value after '-p' option\n");
2479                     return SG_LIB_SYNTAX_ERROR;
2480                 }
2481             } else {
2482                 pr2serr("with --enumerate only search using VPD page "
2483                         "numbers\n");
2484                 return SG_LIB_SYNTAX_ERROR;
2485             }
2486             matches = count_standard_vpds(op->vpd_pn);
2487             if (0 == matches)
2488                 matches = svpd_count_vendor_vpds(op->vpd_pn,
2489                                                  op->vend_prod_num);
2490             if (0 == matches)
2491                 sgj_pr_hr(jsp, "No matches found for VPD page number 0x%x\n",
2492                           op->vpd_pn);
2493         } else {        /* enumerate standard then vendor VPD pages */
2494             sgj_pr_hr(jsp, "Standard VPD pages:\n");
2495             enumerate_vpds(1, 1);
2496         }
2497         return 0;
2498     }
2499 
2500     as_json = jsp->pr_as_json;
2501     if (as_json)
2502         jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp);
2503 
2504     if (op->page_str) {
2505         if ('-' == op->page_str[0])
2506             op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
2507         else if (isalpha((uint8_t)op->page_str[0])) {
2508             vnp = sdp_find_vpd_by_acron(op->page_str);
2509             if (NULL == vnp) {
2510                 vnp = svpd_find_vendor_by_acron(op->page_str);
2511                 if (NULL == vnp) {
2512                     if (0 == strcmp("stdinq", op->page_str)) {
2513                         vnp = sdp_find_vpd_by_acron("sinq");
2514                     } else {
2515                         pr2serr("abbreviation doesn't match a VPD page\n");
2516                         sgj_pr_hr(jsp, "Available standard VPD pages:\n");
2517                         enumerate_vpds(1, 1);
2518                         ret = SG_LIB_SYNTAX_ERROR;
2519                         goto fini;
2520                     }
2521                 }
2522             }
2523             op->vpd_pn = vnp->value;
2524             subvalue = vnp->subvalue;
2525             op->vend_prod_num = subvalue;
2526         } else {
2527             cp = strchr(op->page_str, ',');
2528             if (cp && op->vend_prod) {
2529                 pr2serr("the --page=pg,vp and the --vendor=vp forms overlap, "
2530                         "choose one or the other\n");
2531                 ret = SG_LIB_SYNTAX_ERROR;
2532                 goto fini;
2533             }
2534             op->vpd_pn = sg_get_num_nomult(op->page_str);
2535             if ((op->vpd_pn < 0) || (op->vpd_pn > 255)) {
2536                 pr2serr("Bad page code value after '-p' option\n");
2537                 sgj_pr_hr(jsp, "Available standard VPD pages:\n");
2538                 enumerate_vpds(1, 1);
2539                 ret = SG_LIB_SYNTAX_ERROR;
2540                 goto fini;
2541             }
2542             if (cp) {
2543                 if (isdigit((uint8_t)*(cp + 1)))
2544                     op->vend_prod_num = sg_get_num_nomult(cp + 1);
2545                 else
2546                     op->vend_prod_num = svpd_find_vp_num_by_acron(cp + 1);
2547                 if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
2548                     pr2serr("Bad vendor/product acronym after comma in '-p' "
2549                             "option\n");
2550                     if (op->vend_prod_num < 0)
2551                         svpd_enumerate_vendor(-1);
2552                     ret = SG_LIB_SYNTAX_ERROR;
2553                     goto fini;
2554                 }
2555                 subvalue = op->vend_prod_num;
2556             } else if (op->vend_prod) {
2557                 if (isdigit((uint8_t)op->vend_prod[0]))
2558                     op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
2559                 else
2560                     op->vend_prod_num =
2561                         svpd_find_vp_num_by_acron(op->vend_prod);
2562                 if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
2563                     pr2serr("Bad vendor/product acronym after '--vendor=' "
2564                             "option\n");
2565                     svpd_enumerate_vendor(-1);
2566                     ret = SG_LIB_SYNTAX_ERROR;
2567                     goto fini;
2568                 }
2569                 subvalue = op->vend_prod_num;
2570             }
2571         }
2572         if (op->verbose > 3)
2573                pr2serr("'--page=' matched pn=%d [0x%x], subvalue=%d\n",
2574                        op->vpd_pn, op->vpd_pn, subvalue);
2575     } else if (op->vend_prod) {
2576         if (isdigit((uint8_t)op->vend_prod[0]))
2577             op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
2578         else
2579             op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod);
2580         if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
2581             pr2serr("Bad vendor/product acronym after '--vendor=' "
2582                     "option\n");
2583             svpd_enumerate_vendor(-1);
2584             ret = SG_LIB_SYNTAX_ERROR;
2585             goto fini;
2586         }
2587         subvalue = op->vend_prod_num;
2588     }
2589 
2590     rsp_buff = sg_memalign(rsp_buff_sz, 0 /* page align */, &free_rsp_buff,
2591                            false);
2592     if (NULL == rsp_buff) {
2593         pr2serr("Unable to allocate %d bytes on heap\n", rsp_buff_sz);
2594         ret = sg_convert_errno(ENOMEM);
2595         goto fini;
2596     }
2597     if (op->sinq_inraw_fn) {
2598         if ((ret = sg_f2hex_arr(op->sinq_inraw_fn, true, false, rsp_buff,
2599                                 &inraw_len, rsp_buff_sz))) {
2600             goto err_out;
2601         }
2602         if (inraw_len < 36) {
2603             pr2serr("Unable to read 36 or more bytes from %s\n",
2604                     op->sinq_inraw_fn);
2605             ret = SG_LIB_FILE_ERROR;
2606             goto err_out;
2607         }
2608         memcpy(op->std_inq_a,  rsp_buff, 36);
2609         op->std_inq_a_valid = true;
2610     }
2611     if (op->inhex_fn) {
2612         if (op->device_name) {
2613             pr2serr("Cannot have both a DEVICE and --inhex= option\n");
2614             ret = SG_LIB_SYNTAX_ERROR;
2615             goto err_out;
2616         }
2617         if ((ret = sg_f2hex_arr(op->inhex_fn, !!op->do_raw, false, rsp_buff,
2618                                 &inhex_len, rsp_buff_sz))) {
2619             goto err_out;
2620         }
2621         if (op->verbose > 2)
2622             pr2serr("Read %d [0x%x] bytes of user supplied data\n", inhex_len,
2623                     inhex_len);
2624         if (op->verbose > 3)
2625             hex2stderr(rsp_buff, inhex_len, 0);
2626         op->do_raw = 0;         /* don't want raw on output with --inhex= */
2627         if ((NULL == op->page_str) && (! op->do_all)) {
2628             /* may be able to deduce VPD page */
2629             if ((0x2 == (0xf & rsp_buff[3])) && (rsp_buff[2] > 2)) {
2630                 if (op->verbose)
2631                     pr2serr("Guessing from --inhex= this is a standard "
2632                             "INQUIRY\n");
2633             } else if (rsp_buff[2] <= 2) {
2634                 if (op->verbose)
2635                     pr2serr("Guessing from --inhex this is VPD page 0x%x\n",
2636                             rsp_buff[1]);
2637                 op->vpd_pn = rsp_buff[1];
2638             } else {
2639                 if (op->vpd_pn > 0x80) {
2640                     op->vpd_pn = rsp_buff[1];
2641                     if (op->verbose)
2642                         pr2serr("Guessing from --inhex this is VPD page "
2643                                 "0x%x\n", rsp_buff[1]);
2644                 } else {
2645                     op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
2646                     if (op->verbose)
2647                         pr2serr("page number unclear from --inhex, hope "
2648                                 "it's a standard INQUIRY response\n");
2649                 }
2650             }
2651         }
2652     } else if ((NULL == op->device_name) && (! op->std_inq_a_valid)) {
2653         pr2serr("No DEVICE argument given\n\n");
2654         usage();
2655         ret = SG_LIB_SYNTAX_ERROR;
2656         goto err_out;
2657     }
2658 
2659     if (op->do_raw && op->do_hex) {
2660         pr2serr("Can't do hex and raw at the same time\n");
2661         usage();
2662         ret = SG_LIB_SYNTAX_ERROR;
2663         goto err_out;
2664     }
2665     if (op->do_ident) {
2666         op->vpd_pn = VPD_DEVICE_ID;
2667         if (op->do_ident > 1) {
2668             if (! op->do_long)
2669                 op->do_quiet = true;
2670             subvalue = VPD_DI_SEL_LU;
2671         }
2672     }
2673     if (op->do_raw) {
2674         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
2675             perror("sg_set_binary_mode");
2676             ret = SG_LIB_FILE_ERROR;
2677             goto err_out;
2678         }
2679     }
2680 
2681     if (op->inhex_fn) {
2682         if ((0 == op->maxlen) || (inhex_len < op->maxlen))
2683             op->maxlen = inhex_len;
2684         if (op->do_all || op->page_given)
2685             res = svpd_decode_all(-1, op, jop);
2686         else {
2687             res = svpd_decode_t10(-1, op, jop, subvalue, 0, NULL);
2688             if (SG_LIB_CAT_OTHER == res) {
2689                 res = svpd_decode_vendor(-1, op, jop, 0);
2690                 if (SG_LIB_CAT_OTHER == res)
2691                     res = svpd_unable_to_decode(-1, op, subvalue, 0);
2692             }
2693         }
2694         ret = res;
2695         goto err_out;
2696     } else if (op->std_inq_a_valid && (NULL == op->device_name)) {
2697         /* nothing else to do ... */
2698         /* --sinq_inraw=RFN contents still in rsp_buff */
2699         if (op->do_raw)
2700             dStrRaw(rsp_buff, inraw_len);
2701         else if (op->do_hex) {
2702             if (! op->do_quiet && (op->do_hex < 3))
2703                 sgj_pr_hr(jsp, "Standard Inquiry data format:\n");
2704             hex2stdout(rsp_buff, inraw_len, (1 == op->do_hex) ? 0 : -1);
2705         } else
2706             std_inq_decode(rsp_buff, inraw_len, op, jop);
2707         ret = 0;
2708         goto fini;
2709     }
2710 
2711     if ((sg_fd = sg_cmds_open_device(op->device_name, true /* ro */,
2712                                      op->verbose)) < 0) {
2713         if (op->verbose > 0)
2714             pr2serr("error opening file: %s: %s\n", op->device_name,
2715                     safe_strerror(-sg_fd));
2716         ret = sg_convert_errno(-sg_fd);
2717         if (ret < 0)
2718             ret = SG_LIB_FILE_ERROR;
2719         goto err_out;
2720     }
2721 
2722     if (op->examine_given) {
2723         ret = svpd_examine_all(sg_fd, op, jop);
2724     } else if (op->do_all)
2725         ret = svpd_decode_all(sg_fd, op, jop);
2726     else {
2727         memset(rsp_buff, 0, rsp_buff_sz);
2728 
2729         res = svpd_decode_t10(sg_fd, op, jop, subvalue, 0, NULL);
2730         if (SG_LIB_CAT_OTHER == res) {
2731             res = svpd_decode_vendor(sg_fd, op, jop, 0);
2732             if (SG_LIB_CAT_OTHER == res)
2733                 res = svpd_unable_to_decode(sg_fd, op, subvalue, 0);
2734         }
2735         if (! op->do_quiet) {
2736             if (SG_LIB_CAT_ABORTED_COMMAND == res)
2737                 pr2serr("fetching VPD page failed, aborted command\n");
2738             else if (res) {
2739                 char b[80];
2740 
2741                 sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
2742                 pr2serr("fetching VPD page failed: %s\n", b);
2743             }
2744         }
2745         ret = res;
2746     }
2747 err_out:
2748     if (free_rsp_buff)
2749         free(free_rsp_buff);
2750     if ((0 == op->verbose) && (! op->do_quiet)) {
2751         if (! sg_if_can2stderr("sg_vpd failed: ", ret))
2752             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
2753                     "more information\n");
2754     }
2755 fini:
2756     res = (sg_fd >= 0) ? sg_cmds_close_device(sg_fd) : 0;
2757 
2758     if (res < 0) {
2759         pr2serr("close error: %s\n", safe_strerror(-res));
2760         if (0 == ret)
2761             ret = sg_convert_errno(-res);
2762     }
2763     ret = (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
2764     if (as_json) {
2765         if (0 == op->do_hex)
2766             sgj_js2file(jsp, NULL, ret, stdout);
2767         sgj_finish(jsp);
2768     }
2769     return ret;
2770 }
2771