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