xref: /aosp_15_r20/external/sg3_utils/src/sg_read_attr.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /*
2  * Copyright (c) 2016-2021 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 <errno.h>
18 #include <ctype.h>
19 #include <getopt.h>
20 #define __STDC_FORMAT_MACROS 1
21 #include <inttypes.h>
22 #include <errno.h>
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "sg_lib.h"
29 #include "sg_lib_data.h"
30 #include "sg_pt.h"
31 #include "sg_cmds_basic.h"
32 #include "sg_unaligned.h"
33 #include "sg_pr2serr.h"
34 
35 /* A utility program originally written for the Linux OS SCSI subsystem.
36  *
37  *
38  * This program issues the SCSI READ ATTRIBUTE command to the given SCSI device
39  * and decodes the response. Based on spc5r08.pdf
40  */
41 
42 static const char * version_str = "1.16 20211114";
43 
44 #define MAX_RATTR_BUFF_LEN (1024 * 1024)
45 #define DEF_RATTR_BUFF_LEN (1024 * 8)
46 
47 #define SG_READ_ATTRIBUTE_CMD 0x8c
48 #define SG_READ_ATTRIBUTE_CMDLEN 16
49 
50 #define RA_ATTR_VAL_SA 0x0
51 #define RA_ATTR_LIST_SA 0x1
52 #define RA_LV_LIST_SA 0x2
53 #define RA_PART_LIST_SA 0x3
54 #define RA_SMC2_SA 0x4
55 #define RA_SUP_ATTR_SA 0x5
56 #define RA_HIGHEST_SA 0x5
57 
58 #define RA_FMT_BINARY 0x0
59 #define RA_FMT_ASCII 0x1
60 #define RA_FMT_TEXT 0x2         /* takes into account locale */
61 #define RA_FMT_RES 0x3          /* reserved */
62 
63 
64 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
65 #define DEF_PT_TIMEOUT  60      /* 60 seconds */
66 
67 struct opts_t {
68     bool cache;
69     bool enumerate;
70     bool do_raw;
71     bool o_readonly;
72     bool verbose_given;
73     bool version_given;
74     int elem_addr;
75     int filter;
76     int fai;
77     int do_hex;
78     int lvn;
79     int maxlen;
80     int pn;
81     int quiet;
82     int sa;
83     int verbose;
84 };
85 
86 struct acron_nv_t {
87     const char * acron;
88     const char * name;
89     int val;
90 };
91 
92 struct attr_name_info_t {
93     int id;
94     const char * name;  /* tab ('\t') suggest line break */
95     int format;         /* RA_FMT_BINARY and friends, -1 --> unknown */
96     int len;            /* -1 --> not fixed (variable) */
97     int process;        /* 0 --> print decimal if binary, 1 --> print hex,
98                          * 2 --> further processing */
99 };
100 
101 static struct option long_options[] = {
102     {"cache", no_argument, 0, 'c'},
103     {"enumerate", no_argument, 0, 'e'},
104     {"element", required_argument, 0, 'E'},   /* SMC-3 element address */
105     {"filter", required_argument, 0, 'f'},
106     {"first", required_argument, 0, 'F'},
107     {"help", no_argument, 0, 'h'},
108     {"hex", no_argument, 0, 'H'},
109     {"in", required_argument, 0, 'i'},
110     {"lvn", required_argument, 0, 'l'},
111     {"maxlen", required_argument, 0, 'm'},
112     {"partition", required_argument, 0, 'p'},
113     {"quiet", required_argument, 0, 'q'},
114     {"raw", no_argument, 0, 'r'},
115     {"readonly", no_argument, 0, 'R'},
116     {"sa", required_argument, 0, 's'},
117     {"verbose", no_argument, 0, 'v'},
118     {"version", no_argument, 0, 'V'},
119     {0, 0, 0, 0},   /* sentinel */
120 };
121 
122 static struct acron_nv_t sa_acron_arr[] = {
123     {"av", "attribute values", 0},
124     {"al", "attribute list", 1},
125     {"lvl", "logical volume list", 2},
126     {"pl", "partition list", 3},
127     {"smc", "SMC-2 should define this", 4},
128     {"sa", "supported attributes", 5},
129     {NULL, NULL, -1},           /* sentinel */
130 };
131 
132 static struct attr_name_info_t attr_name_arr[] = {
133 /* Device type attributes */
134     {0x0, "Remaining capacity in partition [MiB]", RA_FMT_BINARY, 8, 0},
135     {0x1, "Maximum capacity in partition [MiB]", RA_FMT_BINARY, 8, 0},
136     {0x2, "TapeAlert flags", RA_FMT_BINARY, 8, 0},   /* SSC-4 */
137     {0x3, "Load count", RA_FMT_BINARY, 8, 0},
138     {0x4, "MAM space remaining [B]", RA_FMT_BINARY, 8, 0},
139     {0x5, "Assigning organization", RA_FMT_ASCII, 8, 0}, /* SSC-4 */
140     {0x6, "Format density code", RA_FMT_BINARY, 1, 1},    /* SSC-4 */
141     {0x7, "Initialization count", RA_FMT_BINARY, 2, 0},
142     {0x8, "Volume identifier", RA_FMT_ASCII, 32, 0},
143     {0x9, "Volume change reference", RA_FMT_BINARY, -1, 1}, /* SSC-4 */
144     {0x20a, "Density vendor/serial number at last load", RA_FMT_ASCII, 40, 0},
145     {0x20b, "Density vendor/serial number at load-1", RA_FMT_ASCII, 40, 0},
146     {0x20c, "Density vendor/serial number at load-2", RA_FMT_ASCII, 40, 0},
147     {0x20d, "Density vendor/serial number at load-3", RA_FMT_ASCII, 40, 0},
148     {0x220, "Total MiB written in medium life", RA_FMT_BINARY, 8, 0},
149     {0x221, "Total MiB read in medium life", RA_FMT_BINARY, 8, 0},
150     {0x222, "Total MiB written in current/last load", RA_FMT_BINARY, 8, 0},
151     {0x223, "Total MiB read in current/last load", RA_FMT_BINARY, 8, 0},
152     {0x224, "Logical position of first encrypted block", RA_FMT_BINARY, 8, 2},
153     {0x225, "Logical position of first unencrypted block\tafter first "
154      "encrypted block", RA_FMT_BINARY, 8, 2},
155     {0x340, "Medium usage history", RA_FMT_BINARY, 90, 2},
156     {0x341, "Partition usage history", RA_FMT_BINARY, 60, 2},
157 
158 /* Medium type attributes */
159     {0x400, "Medium manufacturer", RA_FMT_ASCII, 8, 0},
160     {0x401, "Medium serial number", RA_FMT_ASCII, 32, 0},
161     {0x402, "Medium length [m]", RA_FMT_BINARY, 4, 0},      /* SSC-4 */
162     {0x403, "Medium width [0.1 mm]", RA_FMT_BINARY, 4, 0},  /* SSC-4 */
163     {0x404, "Assigning organization", RA_FMT_ASCII, 8, 0},  /* SSC-4 */
164     {0x405, "Medium density code", RA_FMT_BINARY, 1, 1},    /* SSC-4 */
165     {0x406, "Medium manufacture date", RA_FMT_ASCII, 8, 0},
166     {0x407, "MAM capacity [B]", RA_FMT_BINARY, 8, 0},
167     {0x408, "Medium type", RA_FMT_BINARY, 1, 1},
168     {0x409, "Medium type information", RA_FMT_BINARY, 2, 1},
169     {0x40a, "Numeric medium serial number", -1, -1, 1},
170 
171 /* Host type attributes */
172     {0x800, "Application vendor", RA_FMT_ASCII, 8, 0},
173     {0x801, "Application name", RA_FMT_ASCII, 32, 0},
174     {0x802, "Application version", RA_FMT_ASCII, 8, 0},
175     {0x803, "User medium text label", RA_FMT_TEXT, 160, 0},
176     {0x804, "Date and time last written", RA_FMT_ASCII, 12, 0},
177     {0x805, "Text localization identifier", RA_FMT_BINARY, 1, 0},
178     {0x806, "Barcode", RA_FMT_ASCII, 32, 0},
179     {0x807, "Owning host textual name", RA_FMT_TEXT, 80, 0},
180     {0x808, "Media pool", RA_FMT_TEXT, 160, 0},
181     {0x809, "Partition user text label", RA_FMT_ASCII, 16, 0},
182     {0x80a, "Load/unload at partition", RA_FMT_BINARY, 1, 0},
183     {0x80a, "Application format version", RA_FMT_ASCII, 16, 0},
184     {0x80c, "Volume coherency information", RA_FMT_BINARY, -1, 1},
185      /* SSC-5 */
186     {0x820, "Medium globally unique identifier", RA_FMT_BINARY, 36, 1},
187     {0x821, "Media pool globally unique identifier", RA_FMT_BINARY, 36, 1},
188 
189     {-1, NULL, -1, -1, 0},
190 };
191 
192 
193 static void
usage()194 usage()
195 {
196     pr2serr("Usage: sg_read_attr [--cache] [--element=EA] [--enumerate] "
197             "[--filter=FL]\n"
198             "                    [--first=FAI] [--help] [--hex] [--in=FN] "
199             "[--lvn=LVN]\n"
200             "                    [--maxlen=LEN] [--partition=PN] [--quiet] "
201             "[--raw]\n"
202             "                    [--readonly] [--sa=SA] [--verbose] "
203             "[--version]\n"
204             "                    DEVICE\n");
205     pr2serr("  where:\n"
206             "    --cache|-c         set CACHE bit in cdn (def: clear)\n"
207             "    --enumerate|-e     enumerate known attributes and service "
208             "actions\n"
209             "    --element=EA|-E EA    EA is placed in 'element address' "
210             "field in\n"
211             "                          cdb [SMC-3] (def: 0)\n"
212             "    --filter=FL|-f FL    FL is parameter code to match (def: "
213             "-1 -> all)\n"
214             "    --first=FAI|-F FAI    FAI is placed in 'first attribute "
215             "identifier'\n"
216             "                          field in cdb (def: 0)\n"
217             "    --help|-h          print out usage message\n"
218             "    --hex|-H           output response in hexadecimal; used "
219             "twice\n"
220             "                       shows decoded values in hex\n"
221             "    --in=FN|-i FN      FN is a filename containing attribute "
222             "values in\n"
223             "                       ASCII hex or binary if --raw also "
224             "given\n"
225             "    --lvn=LVN|-l LVN    logical volume number (LVN) (def:0)\n"
226             "    --maxlen=LEN|-m LEN    max response length (allocation "
227             "length in cdb)\n"
228             "                           (def: 0 -> 8192 bytes)\n"
229             "    --partition=PN|-p PN    partition number (PN) (def:0)\n"
230             "    --quiet|-q         reduce the amount of output, can use "
231             "more than once\n"
232             "    --raw|-r           output response in binary\n"
233             "    --readonly|-R      open DEVICE read-only (def: read-write)\n"
234             "    --sa=SA|-s SA      SA is service action (def: 0)\n"
235             "    --verbose|-v       increase verbosity\n"
236             "    --version|-V       print version string and exit\n\n"
237             "Performs a SCSI READ ATTRIBUTE command. Even though it is "
238             "defined in\nSPC-3 and later it is typically used on tape "
239             "systems.\n");
240 }
241 
242 /* Invokes a SCSI READ ATTRIBUTE command (SPC+SMC).  Return of 0 -> success,
243  * various SG_LIB_CAT_* positive values or -1 -> other errors */
244 static int
sg_ll_read_attr(int sg_fd,void * resp,int * residp,bool noisy,const struct opts_t * op)245 sg_ll_read_attr(int sg_fd, void * resp, int * residp, bool noisy,
246                 const struct opts_t * op)
247 {
248     int ret, res, sense_cat;
249     uint8_t ra_cdb[SG_READ_ATTRIBUTE_CMDLEN] =
250           {SG_READ_ATTRIBUTE_CMD, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,
251            0, 0, 0, 0};
252     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
253     struct sg_pt_base * ptvp;
254 
255     ra_cdb[1] = 0x1f & op->sa;
256     if (op->elem_addr)
257         sg_put_unaligned_be16(op->elem_addr, ra_cdb + 2);
258     if (op->lvn)
259         ra_cdb[5] = 0xff & op->lvn;
260     if (op->pn)
261         ra_cdb[7] = 0xff & op->pn;
262     if (op->fai)
263         sg_put_unaligned_be16(op->fai, ra_cdb + 8);
264     sg_put_unaligned_be32((uint32_t)op->maxlen, ra_cdb + 10);
265     if (op->cache)
266         ra_cdb[14] |= 0x1;
267     if (op->verbose) {
268         char b[128];
269 
270         pr2serr("    Read attribute cdb: %s\n",
271                 sg_get_command_str(ra_cdb, SG_READ_ATTRIBUTE_CMDLEN, false,
272                                    sizeof(b), b));
273     }
274 
275     ptvp = construct_scsi_pt_obj();
276     if (NULL == ptvp) {
277         pr2serr("%s: out of memory\n", __func__);
278         return -1;
279     }
280     set_scsi_pt_cdb(ptvp, ra_cdb, sizeof(ra_cdb));
281     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
282     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, op->maxlen);
283     res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, op->verbose);
284     ret = sg_cmds_process_resp(ptvp, "read attribute", res, noisy,
285                                op->verbose, &sense_cat);
286     if (-1 == ret) {
287         if (get_scsi_pt_transport_err(ptvp))
288             ret = SG_LIB_TRANSPORT_ERROR;
289         else
290             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
291     } else if (-2 == ret) {
292         switch (sense_cat) {
293         case SG_LIB_CAT_RECOVERED:
294         case SG_LIB_CAT_NO_SENSE:
295             ret = 0;
296             break;
297         default:
298             ret = sense_cat;
299             break;
300         }
301     } else
302         ret = 0;
303     if (residp)
304         *residp = get_scsi_pt_resid(ptvp);
305     destruct_scsi_pt_obj(ptvp);
306     return ret;
307 }
308 
309 static void
dStrRaw(const char * str,int len)310 dStrRaw(const char * str, int len)
311 {
312     int k;
313 
314     for (k = 0; k < len; ++k)
315         printf("%c", str[k]);
316 }
317 
318 static int
find_sa_acron(const char * cp)319 find_sa_acron(const char * cp)
320 {
321     int k;
322     const struct acron_nv_t * anvp;
323     const char * mp;
324 
325     for (anvp = sa_acron_arr; anvp->acron ; ++anvp) {
326         for (mp = cp, k = 0; *mp; ++mp, ++k) {
327             if (0 == anvp->acron[k])
328                 return anvp->val;
329             if (tolower((uint8_t)*mp) != (uint8_t)anvp->acron[k])
330                 break;
331         }
332         if ((0 == *mp) && (0 == anvp->acron[k]))
333             return anvp->val;
334     }
335     return -1;  /* not found */
336 }
337 
338 const char * a_format[] = {
339     "binary",
340     "ascii",
341     "text",
342     "format[0x3]",
343 };
344 
345 static void
enum_attributes(void)346 enum_attributes(void)
347 {
348     const struct attr_name_info_t * anip;
349     const char * cp;
350     char b[32];
351 
352     printf("Attribute ID\tLength\tFormat\tName\n");
353     printf("------------------------------------------\n");
354     for (anip = attr_name_arr; anip->name ; ++anip) {
355         if (anip->format < 0)
356             snprintf(b, sizeof(b), "unknown");
357         else
358             snprintf(b, sizeof(b), "%s", a_format[0x3 & anip->format]);
359         printf("  0x%04x:\t%d\t%s\t", anip->id, anip->len, b);
360         cp = strchr(anip->name, '\t');
361         if (cp ) {
362             printf("%.*s\n", (int)(cp - anip->name), anip->name);
363             printf("\t\t\t\t%s\n", cp + 1);
364         } else
365             printf("%s\n", anip->name);
366     }
367 }
368 
369 static void
enum_sa_acrons(void)370 enum_sa_acrons(void)
371 {
372     const struct acron_nv_t * anvp;
373 
374     printf("SA_value\tAcronym\tDescription\n");
375     printf("------------------------------------------\n");
376     for (anvp = sa_acron_arr; anvp->acron ; ++anvp)
377         printf("  %d:\t\t%s\t%s\n", anvp->val, anvp->acron, anvp->name);
378 }
379 
380 /* Returns 1 if 'bp' all 0xff bytes, returns 2 is all 0xff bytes apart
381  * from last being 0xfe; otherwise returns 0. */
382 static int
all_ffs_or_last_fe(const uint8_t * bp,int len)383 all_ffs_or_last_fe(const uint8_t * bp, int len)
384 {
385     for ( ; len > 0; ++bp, --len) {
386         if (*bp < 0xfe)
387             return 0;
388         if (0xfe == *bp)
389             return (1 == len) ? 2 : 0;
390 
391     }
392     return 1;
393 }
394 
395 static char *
attr_id_lookup(unsigned int id,const struct attr_name_info_t ** anipp,int blen,char * b)396 attr_id_lookup(unsigned int id, const struct attr_name_info_t ** anipp,
397                int blen, char * b)
398 {
399     const struct attr_name_info_t * anip;
400 
401     for (anip = attr_name_arr; anip->name; ++anip) {
402         if (id == (unsigned int)anip->id)
403             break;
404     }
405     if (anip->name) {
406         snprintf(b, blen, "%s", anip->name);
407         if (anipp)
408             *anipp = anip;
409         return b;
410     }
411     if (anipp)
412         *anipp = NULL;
413     if (id < 0x400)
414         snprintf(b, blen, "Unknown device attribute 0x%x", id);
415     else if (id < 0x800)
416         snprintf(b, blen, "Unknown medium attribute 0x%x", id);
417     else if (id < 0xc00)
418         snprintf(b, blen, "Unknown host attribute 0x%x", id);
419     else if (id < 0x1000)
420         snprintf(b, blen, "Vendor specific device attribute 0x%x", id);
421     else if (id < 0x1400)
422         snprintf(b, blen, "Vendor specific medium attribute 0x%x", id);
423     else if (id < 0x1800)
424         snprintf(b, blen, "Vendor specific host attribute 0x%x", id);
425     else
426         snprintf(b, blen, "Reserved attribute 0x%x", id);
427     return b;
428 }
429 
430 static void
decode_attr_list(const uint8_t * alp,int len,bool supported,const struct opts_t * op)431 decode_attr_list(const uint8_t * alp, int len, bool supported,
432                  const struct opts_t * op)
433 {
434     int id;
435     char b[160];
436     char * cp;
437     char * c2p;
438     const char * leadin = supported ? "Supported a" : "A";
439 
440     if (op->verbose)
441         printf("%sttribute list: [len=%d]\n", leadin, len);
442     else if (0 == op->quiet)
443         printf("%sttribute list:\n", leadin);
444     if (op->do_hex) {
445         hex2stdout(alp, len, 0);
446         return;
447     }
448     for ( ; len > 0; alp += 2, len -= 2) {
449         id = sg_get_unaligned_be16(alp + 0);
450         if ((op->filter >= 0) && (op->filter != id))
451             continue;
452         if (op->verbose)
453             printf("  0x%.4x:\t", id);
454         cp = attr_id_lookup(id, NULL, sizeof(b), b);
455         c2p = strchr(cp, '\t');
456         if (c2p) {
457             printf("  %.*s -\n", (int)(c2p - cp), cp);
458             if (op->verbose)
459                 printf("\t\t      %s\n", c2p + 1);
460             else
461                 printf("      %s\n", c2p + 1);
462         } else
463             printf("  %s\n", cp);
464     }
465 }
466 
467 static void
helper_full_attr(const uint8_t * alp,int len,int id,const struct attr_name_info_t * anip,const struct opts_t * op)468 helper_full_attr(const uint8_t * alp, int len, int id,
469                  const struct attr_name_info_t * anip,
470                  const struct opts_t * op)
471 {
472     int k;
473     const uint8_t * bp;
474 
475     if (op->verbose)
476         printf("[r%c] ", (0x80 & alp[2]) ? 'o' : 'w');
477     if (op->verbose > 3)
478         pr2serr("%s: id=0x%x, len=%d, anip->format=%d, anip->len=%d\n",
479                 __func__, id, len, anip->format, anip->len);
480     switch (id) {
481     case 0x224:         /* logical position of first encrypted block */
482         k = all_ffs_or_last_fe(alp + 5, len - 5);
483         if (1 == k)
484             printf("<unknown> [ff]\n");
485         else if (2 == k)
486             printf("<unknown [fe]>\n");
487         else {
488             if ((len - 5) <= 8)
489                 printf("%" PRIx64, sg_get_unaligned_be(len - 5, alp + 5));
490             else {
491                 printf("\n");
492                 hex2stdout((alp + 5), len - 5, 0);
493             }
494         }
495         break;
496     case 0x225:         /* logical position of first unencrypted block
497                          * after first encrypted block */
498         k = all_ffs_or_last_fe(alp + 5, len - 5);
499         if (1 == k)
500             printf("<unknown> [ff]\n");
501         else if (2 == k)
502             printf("<unknown [fe]>\n");
503         else {
504             if ((len - 5) <= 8)
505                 printf("%" PRIx64, sg_get_unaligned_be(len - 5, alp + 5));
506             else {
507                 printf("\n");
508                 hex2stdout(alp + 5, len - 5, 0);
509             }
510         }
511         break;
512     case 0x340:         /* Medium Usage history */
513         bp = alp + 5;
514         printf("\n");
515         if ((len - 5) < 90) {
516             pr2serr("%s: expected 90 bytes, got %d\n", __func__, len - 5);
517             break;
518         }
519         printf("    Current amount of data written [MiB]: %" PRIu64 "\n",
520                sg_get_unaligned_be48(bp + 0));
521         printf("    Current write retry count: %" PRIu64 "\n",
522                sg_get_unaligned_be48(bp + 6));
523         printf("    Current amount of data read [MiB]: %" PRIu64 "\n",
524                sg_get_unaligned_be48(bp + 12));
525         printf("    Current read retry count: %" PRIu64 "\n",
526                sg_get_unaligned_be48(bp + 18));
527         printf("    Previous amount of data written [MiB]: %" PRIu64 "\n",
528                sg_get_unaligned_be48(bp + 24));
529         printf("    Previous write retry count: %" PRIu64 "\n",
530                sg_get_unaligned_be48(bp + 30));
531         printf("    Previous amount of data read [MiB]: %" PRIu64 "\n",
532                sg_get_unaligned_be48(bp + 36));
533         printf("    Previous read retry count: %" PRIu64 "\n",
534                sg_get_unaligned_be48(bp + 42));
535         printf("    Total amount of data written [MiB]: %" PRIu64 "\n",
536                sg_get_unaligned_be48(bp + 48));
537         printf("    Total write retry count: %" PRIu64 "\n",
538                sg_get_unaligned_be48(bp + 54));
539         printf("    Total amount of data read [MiB]: %" PRIu64 "\n",
540                sg_get_unaligned_be48(bp + 60));
541         printf("    Total read retry count: %" PRIu64 "\n",
542                sg_get_unaligned_be48(bp + 66));
543         printf("    Load count: %" PRIu64 "\n",
544                sg_get_unaligned_be48(bp + 72));
545         printf("    Total change partition count: %" PRIu64 "\n",
546                sg_get_unaligned_be48(bp + 78));
547         printf("    Total partition initialization count: %" PRIu64 "\n",
548                sg_get_unaligned_be48(bp + 84));
549         break;
550     case 0x341:         /* Partition Usage history */
551         bp = alp + 5;
552         printf("\n");
553         if ((len - 5) < 60) {
554             pr2serr("%s: expected 60 bytes, got %d\n", __func__, len - 5);
555             break;
556         }
557         printf("    Current amount of data written [MiB]: %" PRIu32 "\n",
558                sg_get_unaligned_be32(bp + 0));
559         printf("    Current write retry count: %" PRIu32 "\n",
560                sg_get_unaligned_be32(bp + 4));
561         printf("    Current amount of data read [MiB]: %" PRIu32 "\n",
562                sg_get_unaligned_be32(bp + 8));
563         printf("    Current read retry count: %" PRIu32 "\n",
564                sg_get_unaligned_be32(bp + 12));
565         printf("    Previous amount of data written [MiB]: %" PRIu32 "\n",
566                sg_get_unaligned_be32(bp + 16));
567         printf("    Previous write retry count: %" PRIu32 "\n",
568                sg_get_unaligned_be32(bp + 20));
569         printf("    Previous amount of data read [MiB]: %" PRIu32 "\n",
570                sg_get_unaligned_be32(bp + 24));
571         printf("    Previous read retry count: %" PRIu32 "\n",
572                sg_get_unaligned_be32(bp + 28));
573         printf("    Total amount of data written [MiB]: %" PRIu32 "\n",
574                sg_get_unaligned_be32(bp + 32));
575         printf("    Total write retry count: %" PRIu32 "\n",
576                sg_get_unaligned_be32(bp + 36));
577         printf("    Total amount of data read [MiB]: %" PRIu32 "\n",
578                sg_get_unaligned_be32(bp + 40));
579         printf("    Total read retry count: %" PRIu32 "\n",
580                sg_get_unaligned_be32(bp + 44));
581         printf("    Load count: %" PRIu32 "\n",
582                sg_get_unaligned_be32(bp + 48));
583         printf("    change partition count: %" PRIu32 "\n",
584                sg_get_unaligned_be32(bp + 52));
585         printf("    partition initialization count: %" PRIu32 "\n",
586                sg_get_unaligned_be32(bp + 56));
587         break;
588     default:
589         pr2serr("%s: unknown attribute id: 0x%x\n", __func__, id);
590         printf("  In hex:\n");
591         hex2stdout(alp, len, 0);
592         break;
593     }
594 }
595 
596 static void
decode_attr_vals(const uint8_t * alp,int len,const struct opts_t * op)597 decode_attr_vals(const uint8_t * alp, int len, const struct opts_t * op)
598 {
599     int bump, id, alen;
600     uint64_t ull;
601     char * cp;
602     char * c2p;
603     const struct attr_name_info_t * anip;
604     char b[160];
605 
606     if (op->verbose)
607         printf("Attribute values: [len=%d]\n", len);
608     else if (op->filter < 0) {
609         if (0 == op->quiet)
610             printf("Attribute values:\n");
611         if (op->do_hex) {       /* only expect -HH to get through here */
612             hex2stdout(alp, len, 0);
613             return;
614         }
615     }
616     for ( ; len > 4; alp += bump, len -= bump) {
617         id = sg_get_unaligned_be16(alp + 0);
618         bump = sg_get_unaligned_be16(alp + 3) + 5;
619         alen = bump - 5;
620         if ((op->filter >= 0) && (op->filter != id)) {
621             if (id < op->filter)
622                 continue;
623             else
624                 break;  /* Assume array is ascending id order */
625         }
626         anip = NULL;
627         cp = attr_id_lookup(id, &anip, sizeof(b), b);
628         if (op->quiet < 2) {
629             c2p = strchr(cp, '\t');
630             if (c2p) {
631                 printf("  %.*s -\n", (int)(c2p - cp), cp);
632                 printf("      %s: ", c2p + 1);
633             } else
634                 printf("  %s: ", cp);
635         }
636         if (op->verbose)
637             printf("[r%c] ", (0x80 & alp[2]) ? 'o' : 'w');
638         if (anip) {
639             if ((RA_FMT_BINARY == anip->format) && (bump <= 13)) {
640                 ull = sg_get_unaligned_be(alen, alp + 5);
641                 if (0 == anip->process)
642                     printf("%" PRIu64 "\n", ull);
643                 else if (1 == anip->process)
644                     printf("0x%" PRIx64 "\n", ull);
645                 else
646                     helper_full_attr(alp, bump, id, anip, op);
647                 if (op->verbose) {
648                     if ((anip->len > 0) && (alen > 0) && (alen != anip->len))
649                         printf(" <<< T10 length (%d) differs from length in "
650                                "response (%d) >>>\n", anip->len, alen);
651                 }
652             } else if (RA_FMT_BINARY == anip->format) {
653                 if (2 == anip->process)
654                     helper_full_attr(alp, bump, id, anip, op);
655                 else {
656                     printf("\n");
657                     hex2stdout(alp + 5, alen, 0);
658                 }
659            } else {
660                 if (2 == anip->process)
661                     helper_full_attr(alp, bump, id, anip, op);
662                 else {
663                     printf("%.*s\n", alen, alp + 5);
664                     if (op->verbose) {
665                         if ((anip->len > 0) && (alen > 0) &&
666                             (alen != anip->len))
667                             printf(" <<< T10 length (%d) differs from length "
668                                    "in response (%d) >>>\n", anip->len, alen);
669                     }
670                 }
671             }
672         } else {
673             if (op->verbose > 1)
674                 printf("Attribute id lookup failed, in hex:\n");
675             else
676                 printf("\n");
677             hex2stdout(alp + 5, alen, 0);
678         }
679     }
680     if (op->verbose && (len > 0) && (len <= 4))
681         pr2serr("warning: iterate of attributes should end a residual of "
682                 "%d\n", len);
683 }
684 
685 static void
decode_all_sa_s(const uint8_t * rabp,int len,const struct opts_t * op)686 decode_all_sa_s(const uint8_t * rabp, int len, const struct opts_t * op)
687 {
688     if (op->do_hex && (2 != op->do_hex)) {
689         hex2stdout(rabp, len, ((1 == op->do_hex) ? 1 : -1));
690         return;
691     }
692     switch (op->sa) {
693     case RA_ATTR_VAL_SA:
694         decode_attr_vals(rabp + 4, len - 4, op);
695         break;
696     case RA_ATTR_LIST_SA:
697         decode_attr_list(rabp + 4, len - 4, false, op);
698         break;
699     case RA_LV_LIST_SA:
700         if ((0 == op->quiet) || op->verbose)
701             printf("Logical volume list:\n");
702         if (len < 4) {
703             pr2serr(">>> response length unexpectedly short: %d bytes\n",
704                     len);
705             break;
706         }
707         printf("  First logical volume number: %u\n", rabp[2]);
708         printf("  Number of logical volumes available: %u\n", rabp[3]);
709         break;
710     case RA_PART_LIST_SA:
711         if ((0 == op->quiet) || op->verbose)
712             printf("Partition number list:\n");
713         if (len < 4) {
714             pr2serr(">>> response length unexpectedly short: %d bytes\n",
715                     len);
716             break;
717         }
718         printf("  First partition number: %u\n", rabp[2]);
719         printf("  Number of partitions available: %u\n", rabp[3]);
720         break;
721     case RA_SMC2_SA:
722         printf("Used by SMC-2, not information, output in hex:\n");
723         hex2stdout(rabp, len, 0);
724         break;
725     case RA_SUP_ATTR_SA:
726         decode_attr_list(rabp + 4, len - 4, true, op);
727         break;
728     default:
729         printf("Unrecognized service action [0x%x], response in hex:\n",
730                op->sa);
731         hex2stdout(rabp, len, 0);
732         break;
733     }
734 }
735 
736 int
main(int argc,char * argv[])737 main(int argc, char * argv[])
738 {
739     int sg_fd, res, c, len, resid, rlen;
740     unsigned int ra_len;
741     int in_len = 0;
742     int ret = 0;
743     const char * device_name = NULL;
744     const char * fname = NULL;
745     uint8_t * rabp = NULL;
746     uint8_t * free_rabp = NULL;
747     struct opts_t opts;
748     struct opts_t * op;
749     char b[80];
750 
751     op = &opts;
752     memset(op, 0, sizeof(opts));
753     op->filter = -1;
754     while (1) {
755         int option_index = 0;
756 
757         c = getopt_long(argc, argv, "ceE:f:F:hHi:l:m:p:qrRs:vV",
758                         long_options, &option_index);
759         if (c == -1)
760             break;
761 
762         switch (c) {
763         case 'c':
764             op->cache = true;
765             break;
766         case 'e':
767             op->enumerate = true;
768             break;
769         case 'E':
770            op->elem_addr = sg_get_num(optarg);
771            if ((op->elem_addr < 0) || (op->elem_addr > 65535)) {
772                 pr2serr("bad argument to '--element=EA', expect 0 to 65535\n");
773                 return SG_LIB_SYNTAX_ERROR;
774             }
775             break;
776         case 'f':
777            op->filter = sg_get_num(optarg);
778            if ((op->filter < -3) || (op->filter > 65535)) {
779                 pr2serr("bad argument to '--filter=FL', expect -3 to "
780                         "65535\n");
781                 return SG_LIB_SYNTAX_ERROR;
782             }
783             break;
784         case 'F':
785            op->fai = sg_get_num(optarg);
786            if ((op->fai < 0) || (op->fai > 65535)) {
787                 pr2serr("bad argument to '--first=FAI', expect 0 to 65535\n");
788                 return SG_LIB_SYNTAX_ERROR;
789             }
790             break;
791         case 'h':
792         case '?':
793             usage();
794             return 0;
795         case 'H':
796             ++op->do_hex;
797             break;
798         case 'i':
799             fname = optarg;
800             break;
801         case 'l':
802            op->lvn = sg_get_num(optarg);
803            if ((op->lvn < 0) || (op->lvn > 255)) {
804                 pr2serr("bad argument to '--lvn=LVN', expect 0 to 255\n");
805                 return SG_LIB_SYNTAX_ERROR;
806             }
807             break;
808         case 'm':
809             op->maxlen = sg_get_num(optarg);
810             if ((op->maxlen < 0) || (op->maxlen > MAX_RATTR_BUFF_LEN)) {
811                 pr2serr("argument to '--maxlen' should be %d or "
812                         "less\n", MAX_RATTR_BUFF_LEN);
813                 return SG_LIB_SYNTAX_ERROR;
814             }
815             break;
816         case 'p':
817            op->pn = sg_get_num(optarg);
818            if ((op->pn < 0) || (op->pn > 255)) {
819                 pr2serr("bad argument to '--pn=PN', expect 0 to 255\n");
820                 return SG_LIB_SYNTAX_ERROR;
821             }
822             break;
823         case 'q':
824             ++op->quiet;
825             break;
826         case 'r':
827             op->do_raw = true;
828             break;
829         case 'R':
830             op->o_readonly = true;
831             break;
832         case 's':
833            if (isdigit((uint8_t)*optarg)) {
834                op->sa = sg_get_num(optarg);
835                if ((op->sa < 0) || (op->sa > 63)) {
836                     pr2serr("bad argument to '--sa=SA', expect 0 to 63\n");
837                     return SG_LIB_SYNTAX_ERROR;
838                 }
839             } else {
840                 res = find_sa_acron(optarg);
841                 if (res < 0) {
842                     enum_sa_acrons();
843                     return SG_LIB_SYNTAX_ERROR;
844                 }
845                 op->sa = res;
846             }
847             break;
848         case 'v':
849             op->verbose_given = true;
850             ++op->verbose;
851             break;
852         case 'V':
853             op->version_given = true;
854             break;
855         default:
856             pr2serr("unrecognised option code 0x%x ??\n", c);
857             usage();
858             return SG_LIB_SYNTAX_ERROR;
859         }
860     }
861     if (optind < argc) {
862         if (NULL == device_name) {
863             device_name = argv[optind];
864             ++optind;
865         }
866         if (optind < argc) {
867             for (; optind < argc; ++optind)
868                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
869             usage();
870             return SG_LIB_SYNTAX_ERROR;
871         }
872     }
873 #ifdef DEBUG
874     pr2serr("In DEBUG mode, ");
875     if (op->verbose_given && op->version_given) {
876         pr2serr("but override: '-vV' given, zero verbose and continue\n");
877         op->verbose_given = false;
878         op->version_given = false;
879         op->verbose = 0;
880     } else if (! op->verbose_given) {
881         pr2serr("set '-vv'\n");
882         op->verbose = 2;
883     } else
884         pr2serr("keep verbose=%d\n", op->verbose);
885 #else
886     if (op->verbose_given && op->version_given)
887         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
888 #endif
889     if (op->version_given) {
890         pr2serr("version: %s\n", version_str);
891         return 0;
892     }
893 
894     if (op->enumerate) {
895         enum_attributes();
896         printf("\n");
897         enum_sa_acrons();
898         return 0;
899     }
900 
901     if (fname && device_name) {
902         pr2serr("since '--in=FN' given, ignoring DEVICE\n");
903         device_name = NULL;
904     }
905 
906     if (0 == op->maxlen)
907         op->maxlen = DEF_RATTR_BUFF_LEN;
908     rabp = (uint8_t *)sg_memalign(op->maxlen, 0, &free_rabp, op->verbose > 3);
909     if (NULL == rabp) {
910         pr2serr("unable to sg_memalign %d bytes\n", op->maxlen);
911         return sg_convert_errno(ENOMEM);
912     }
913 
914     if (NULL == device_name) {
915         if (fname) {
916             if ((ret = sg_f2hex_arr(fname, op->do_raw, false /* no space */,
917                                     rabp, &in_len, op->maxlen)))
918                 goto clean_up;
919             if (op->do_raw)
920                 op->do_raw = false;    /* can interfere on decode */
921             if (in_len < 4) {
922                 pr2serr("--in=%s only decoded %d bytes (needs 4 at least)\n",
923                         fname, in_len);
924                 ret = SG_LIB_SYNTAX_ERROR;
925                 goto clean_up;
926             }
927             decode_all_sa_s(rabp, in_len, op);
928             goto clean_up;
929         }
930         pr2serr("missing device name!\n");
931         usage();
932         ret = SG_LIB_SYNTAX_ERROR;
933         goto clean_up;
934     }
935 
936     if (op->do_raw) {
937         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
938             perror("sg_set_binary_mode");
939             ret = SG_LIB_FILE_ERROR;
940                 goto clean_up;
941         }
942     }
943 
944     sg_fd = sg_cmds_open_device(device_name, op->o_readonly, op->verbose);
945     if (sg_fd < 0) {
946         pr2serr("open error: %s: %s\n", device_name,
947                 safe_strerror(-sg_fd));
948         ret = sg_convert_errno(-sg_fd);
949         goto clean_up;
950     }
951 
952     res = sg_ll_read_attr(sg_fd, rabp, &resid, op->verbose > 0, op);
953     ret = res;
954     if (0 == res) {
955         rlen = op->maxlen - resid;
956         if (rlen < 4) {
957             pr2serr("Response length (%d) too short\n", rlen);
958             ret = SG_LIB_CAT_MALFORMED;
959             goto close_then_end;
960         }
961         if ((op->sa <= RA_HIGHEST_SA) && (op->sa != RA_SMC2_SA)) {
962             ra_len = ((RA_LV_LIST_SA == op->sa) ||
963                       (RA_PART_LIST_SA == op->sa)) ?
964                         (unsigned int)sg_get_unaligned_be16(rabp + 0) :
965                         sg_get_unaligned_be32(rabp + 0) + 2;
966             ra_len += 2;
967         } else
968             ra_len = rlen;
969         if ((int)ra_len > rlen) {
970             if (op->verbose)
971                 pr2serr("ra_len available is %d, response length is %d\n",
972                         ra_len, rlen);
973             len = rlen;
974         } else
975             len = (int)ra_len;
976         if (op->do_raw) {
977             dStrRaw((const char *)rabp, len);
978             goto close_then_end;
979         }
980         decode_all_sa_s(rabp, len, op);
981     } else if (SG_LIB_CAT_INVALID_OP == res)
982         pr2serr("Read attribute command not supported\n");
983     else {
984         sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
985         pr2serr("Read attribute command: %s\n", b);
986     }
987 
988 close_then_end:
989     res = sg_cmds_close_device(sg_fd);
990     if (res < 0) {
991         pr2serr("close error: %s\n", safe_strerror(-res));
992         if (0 == ret)
993             ret = sg_convert_errno(-res);
994     }
995 clean_up:
996     if (free_rabp)
997         free(free_rabp);
998     if (0 == op->verbose) {
999         if (! sg_if_can2stderr("sg_read_attr failed: ", ret))
1000             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
1001                     "more information\n");
1002     }
1003     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
1004 }
1005