1 /*
2 * Copyright (c) 2004-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 <stdbool.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <ctype.h>
18 #include <getopt.h>
19 #define __STDC_FORMAT_MACROS 1
20 #include <inttypes.h>
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "sg_lib.h"
26 #include "sg_cmds_basic.h"
27 #include "sg_unaligned.h"
28 #include "sg_pr2serr.h"
29
30 /* A utility program originally written for the Linux OS SCSI subsystem.
31 *
32 *
33 * This program issues the SCSI REPORT LUNS command to the given SCSI device
34 * and decodes the response.
35 */
36
37 static const char * version_str = "1.48 20210804"; /* spc6r05 */
38
39 #define MAX_RLUNS_BUFF_LEN (1024 * 1024)
40 #define DEF_RLUNS_BUFF_LEN (1024 * 8)
41
42
43 static struct option long_options[] = {
44 {"decode", no_argument, 0, 'd'},
45 {"help", no_argument, 0, 'h'},
46 {"hex", no_argument, 0, 'H'},
47 #ifdef SG_LIB_LINUX
48 {"linux", no_argument, 0, 'l'},
49 #endif
50 {"lu_cong", no_argument, 0, 'L'},
51 {"lu-cong", no_argument, 0, 'L'},
52 {"maxlen", required_argument, 0, 'm'},
53 {"quiet", no_argument, 0, 'q'},
54 {"raw", no_argument, 0, 'r'},
55 {"readonly", no_argument, 0, 'R'},
56 {"select", required_argument, 0, 's'},
57 {"test", required_argument, 0, 't'},
58 {"verbose", no_argument, 0, 'v'},
59 {"version", no_argument, 0, 'V'},
60 {0, 0, 0, 0},
61 };
62
63
64 static void
usage()65 usage()
66 {
67 #ifdef SG_LIB_LINUX
68 pr2serr("Usage: sg_luns [--decode] [--help] [--hex] [--linux] "
69 "[--lu_cong]\n"
70 " [--maxlen=LEN] [--quiet] [--raw] "
71 "[--readonly]\n"
72 " [--select=SR] [--verbose] [--version] "
73 "DEVICE\n");
74 #else
75 pr2serr("Usage: sg_luns [--decode] [--help] [--hex] [--lu_cong] "
76 "[--maxlen=LEN]\n"
77 " [--quiet] [--raw] [--readonly] "
78 "[--select=SR]\n"
79 " [--verbose] [--version] DEVICE\n");
80 #endif
81 pr2serr(" or\n"
82 " sg_luns --test=ALUN [--decode] [--hex] [--lu_cong] "
83 "[--verbose]\n"
84 " where:\n"
85 " --decode|-d decode all luns into component parts\n"
86 " --help|-h print out usage message\n"
87 " --hex|-H output response in hexadecimal; used "
88 "twice\n"
89 " shows decoded values in hex\n");
90 #ifdef SG_LIB_LINUX
91 pr2serr(" --linux|-l show Linux integer lun after T10 "
92 "representation\n");
93 #endif
94 pr2serr(" --lu_cong|-L decode as if LU_CONG is set; used "
95 "twice:\n"
96 " decode as if LU_CONG is clear\n"
97 " --maxlen=LEN|-m LEN max response length (allocation "
98 "length in cdb)\n"
99 " (def: 0 -> %d bytes)\n"
100 " --quiet|-q output only ASCII hex lun values\n"
101 " --raw|-r output response in binary\n"
102 " --readonly|-R open DEVICE read-only (def: read-write)\n"
103 " --select=SR|-s SR select report SR (def: 0)\n"
104 " 0 -> luns apart from 'well "
105 "known' lus\n"
106 " 1 -> only 'well known' "
107 "logical unit numbers\n"
108 " 2 -> all luns\n"
109 " 0x10 -> administrative luns\n"
110 " 0x11 -> admin luns + "
111 "non-conglomerate luns\n"
112 " 0x12 -> admin lun + its "
113 "subsidiary luns\n"
114 " --test=ALUN|-t ALUN decode ALUN and ignore most other "
115 "options\n"
116 " and DEVICE (apart from '-H')\n"
117 " --verbose|-v increase verbosity\n"
118 " --version|-V print version string and exit\n\n"
119 "Performs a SCSI REPORT LUNS command or decodes the given ALUN. "
120 "When SR is\n0x10 or 0x11 DEVICE must be LUN 0 or REPORT LUNS "
121 "well known logical unit;\nwhen SR is 0x12 DEVICE must be an "
122 "administrative logical unit. When the\n--test=ALUN option is "
123 "given, decodes ALUN rather than sending a REPORT\nLUNS "
124 "command.\n", DEF_RLUNS_BUFF_LEN );
125 }
126
127 /* Decoded according to SAM-5 rev 10. Note that one draft: BCC rev 0,
128 * defines its own "bridge addressing method" in place of the SAM-3
129 * "logical addressing method". */
130 static void
decode_lun(const char * leadin,const uint8_t * lunp,bool lu_cong,int do_hex,int verbose)131 decode_lun(const char * leadin, const uint8_t * lunp, bool lu_cong,
132 int do_hex, int verbose)
133 {
134 bool next_level, admin_lu_cong;
135 int k, x, a_method, bus_id, target, lun, len_fld, e_a_method;
136 uint64_t ull;
137 char l_leadin[128];
138 char b[256];
139
140 if (0xff == lunp[0]) {
141 printf("%sLogical unit _not_ specified\n", leadin);
142 return;
143 }
144 admin_lu_cong = lu_cong;
145 memset(l_leadin, 0, sizeof(l_leadin));
146 for (k = 0; k < 4; ++k, lunp += 2) {
147 next_level = false;
148 strncpy(l_leadin, leadin, sizeof(l_leadin) - 3);
149 if (k > 0) {
150 if (lu_cong) {
151 admin_lu_cong = false;
152 if ((0 == lunp[0]) && (0 == lunp[1])) {
153 printf("%s>>>> Administrative LU\n", l_leadin);
154 if (do_hex || verbose)
155 printf(" since Subsidiary element is "
156 "0x0000\n");
157 break;
158 } else
159 printf("%s>>Subsidiary element:\n", l_leadin);
160 } else
161 printf("%s>>%s level addressing:\n", l_leadin, ((1 == k) ?
162 "Second" : ((2 == k) ? "Third" : "Fourth")));
163 strcat(l_leadin, " ");
164 } else if (lu_cong) {
165 printf("%s>>Administrative element:\n", l_leadin);
166 strcat(l_leadin, " ");
167 }
168 a_method = (lunp[0] >> 6) & 0x3;
169 switch (a_method) {
170 case 0: /* peripheral device addressing method */
171 if (lu_cong) {
172 snprintf(b, sizeof(b), "%sSimple lu addressing: ",
173 l_leadin);
174 x = 0x3fff & sg_get_unaligned_be16(lunp + 0);
175 if (do_hex)
176 printf("%s0x%04x\n", b, x);
177 else
178 printf("%s%d\n", b, x);
179 if (admin_lu_cong)
180 next_level = true;
181 } else {
182 bus_id = lunp[0] & 0x3f;
183 snprintf(b, sizeof(b), "%sPeripheral device addressing: ",
184 l_leadin);
185 if ((0 == bus_id) && (0 == verbose)) {
186 if (do_hex)
187 printf("%slun=0x%02x\n", b, lunp[1]);
188 else
189 printf("%slun=%d\n", b, lunp[1]);
190 } else {
191 if (do_hex)
192 printf("%sbus_id=0x%02x, %s=0x%02x\n", b, bus_id,
193 (bus_id ? "target" : "lun"), lunp[1]);
194 else
195 printf("%sbus_id=%d, %s=%d\n", b, bus_id,
196 (bus_id ? "target" : "lun"), lunp[1]);
197 }
198 if (bus_id)
199 next_level = true;
200 }
201 break;
202 case 1: /* flat space addressing method */
203 lun = 0x3fff & sg_get_unaligned_be16(lunp + 0);
204 if (lu_cong) {
205 printf("%sSince LU_CONG=1, unexpected Flat space "
206 "addressing: lun=0x%04x\n", l_leadin, lun);
207 break;
208 }
209 if (do_hex)
210 printf("%sFlat space addressing: lun=0x%04x\n", l_leadin,
211 lun);
212 else
213 printf("%sFlat space addressing: lun=%d\n", l_leadin, lun);
214 break;
215 case 2: /* logical unit addressing method */
216 target = (lunp[0] & 0x3f);
217 bus_id = (lunp[1] >> 5) & 0x7;
218 lun = lunp[1] & 0x1f;
219 if (lu_cong) {
220 printf("%sSince LU_CONG=1, unexpected lu addressing: "
221 "bus_id=0x%x, target=0x%02x, lun=0x%02x\n", l_leadin,
222 bus_id, target, lun);
223 break;
224 }
225 if (do_hex)
226 printf("%sLogical unit addressing: bus_id=0x%x, "
227 "target=0x%02x, lun=0x%02x\n", l_leadin, bus_id,
228 target, lun);
229 else
230 printf("%sLogical unit addressing: bus_id=%d, target=%d, "
231 "lun=%d\n", l_leadin, bus_id, target, lun);
232 break;
233 case 3: /* extended logical unit + flat space addressing */
234 len_fld = (lunp[0] & 0x30) >> 4;
235 e_a_method = lunp[0] & 0xf;
236 x = lunp[1];
237 if ((0 == len_fld) && (1 == e_a_method)) {
238 snprintf(b, sizeof(b), "well known logical unit");
239 switch (x) {
240 case 1:
241 printf("%sREPORT LUNS %s\n", l_leadin, b);
242 break;
243 case 2: /* obsolete in spc5r01 */
244 printf("%sACCESS CONTROLS %s\n", l_leadin, b);
245 break;
246 case 3:
247 printf("%sTARGET LOG PAGES %s\n", l_leadin, b);
248 break;
249 case 4:
250 printf("%sSECURITY PROTOCOL %s\n", l_leadin, b);
251 break;
252 case 5:
253 printf("%sMANAGEMENT PROTOCOL %s\n", l_leadin, b);
254 break;
255 case 6:
256 printf("%sTARGET COMMANDS %s\n", l_leadin, b);
257 break;
258 default:
259 if (do_hex)
260 printf("%s%s 0x%02x\n", l_leadin, b, x);
261 else
262 printf("%s%s %d\n", l_leadin, b, x);
263 break;
264 }
265 } else if ((1 == len_fld) && (2 == e_a_method)) {
266 x = sg_get_unaligned_be24(lunp + 1);
267 if (do_hex)
268 printf("%sExtended flat space addressing: lun=0x%06x\n",
269 l_leadin, x);
270 else
271 printf("%sExtended flat space addressing: lun=%d\n",
272 l_leadin, x);
273 } else if ((2 == len_fld) && (2 == e_a_method)) {
274 ull = sg_get_unaligned_be(5, lunp + 1);
275 if (do_hex)
276 printf("%sLong extended flat space addressing: "
277 "lun=0x%010" PRIx64 "\n", l_leadin, ull);
278 else
279 printf("%sLong extended flat space addressing: "
280 "lun=%" PRIu64 "\n", l_leadin, ull);
281 } else if ((3 == len_fld) && (0xf == e_a_method))
282 printf("%sLogical unit _not_ specified addressing\n",
283 l_leadin);
284 else {
285 if (len_fld < 2) {
286 if (1 == len_fld)
287 x = sg_get_unaligned_be24(lunp + 1);
288 if (do_hex)
289 printf("%sExtended logical unit addressing: "
290 "length=%d, e.a. method=%d, value=0x%06x\n",
291 l_leadin, len_fld, e_a_method, x);
292 else
293 printf("%sExtended logical unit addressing: "
294 "length=%d, e.a. method=%d, value=%d\n",
295 l_leadin, len_fld, e_a_method, x);
296 } else {
297 ull = sg_get_unaligned_be(((2 == len_fld) ? 5 : 7),
298 lunp + 1);
299 if (do_hex) {
300 printf("%sExtended logical unit addressing: "
301 "length=%d, e. a. method=%d, ", l_leadin,
302 len_fld, e_a_method);
303 if (5 == len_fld)
304 printf("value=0x%010" PRIx64 "\n", ull);
305 else
306 printf("value=0x%014" PRIx64 "\n", ull);
307 } else
308 printf("%sExtended logical unit addressing: "
309 "length=%d, e. a. method=%d, value=%" PRIu64
310 "\n", l_leadin, len_fld, e_a_method, ull);
311 }
312 }
313 break;
314 }
315 if (next_level)
316 continue;
317 if ((2 == a_method) && (k < 3) && (lunp[2] || lunp[3]))
318 printf("%s<<unexpected data at next level, continue>>\n",
319 l_leadin);
320 break;
321 }
322 }
323
324 #ifdef SG_LIB_LINUX
325 static void
linux2t10_lun(uint64_t linux_lun,uint8_t t10_lun[])326 linux2t10_lun(uint64_t linux_lun, uint8_t t10_lun[])
327 {
328 int k;
329
330 for (k = 0; k < 8; k += 2, linux_lun >>= 16)
331 sg_put_unaligned_be16((uint16_t)linux_lun, t10_lun + k);
332 }
333
334 static uint64_t
t10_2linux_lun(const uint8_t t10_lun[])335 t10_2linux_lun(const uint8_t t10_lun[])
336 {
337 int k;
338 const uint8_t * cp;
339 uint64_t res;
340
341 res = sg_get_unaligned_be16(t10_lun + 6);
342 for (cp = t10_lun + 4, k = 0; k < 3; ++k, cp -= 2)
343 res = (res << 16) + sg_get_unaligned_be16(cp);
344 return res;
345 }
346 #endif /* SG_LIB_LINUX */
347
348
349 static void
dStrRaw(const char * str,int len)350 dStrRaw(const char * str, int len)
351 {
352 int k;
353
354 for (k = 0; k < len; ++k)
355 printf("%c", str[k]);
356 }
357
358 int
main(int argc,char * argv[])359 main(int argc, char * argv[])
360 {
361 #ifdef SG_LIB_LINUX
362 bool do_linux = false;
363 #endif
364 bool do_quiet = false;
365 bool do_raw = false;
366 bool lu_cong_arg_given = false;
367 bool o_readonly = false;
368 #ifdef SG_LIB_LINUX
369 bool test_linux_in = false;
370 bool test_linux_out = false;
371 #endif
372 bool trunc;
373 bool verbose_given = false;
374 bool version_given = false;
375 int sg_fd, k, m, off, res, c, list_len, len_cap, luns;
376 int decode_arg = 0;
377 int do_hex = 0;
378 int lu_cong_arg = 0;
379 int maxlen = 0;
380 int ret = 0;
381 int select_rep = 0;
382 int verbose = 0;
383 unsigned int h;
384 const char * test_arg = NULL;
385 const char * device_name = NULL;
386 const char * cp;
387 uint8_t * reportLunsBuff = NULL;
388 uint8_t * free_reportLunsBuff = NULL;
389 uint8_t lun_arr[8];
390 struct sg_simple_inquiry_resp sir;
391
392 while (1) {
393 int option_index = 0;
394
395 #ifdef SG_LIB_LINUX
396 c = getopt_long(argc, argv, "dhHlLm:qrRs:t:vV", long_options,
397 &option_index);
398 #else
399 c = getopt_long(argc, argv, "dhHLm:qrRs:t:vV", long_options,
400 &option_index);
401 #endif
402 if (c == -1)
403 break;
404
405 switch (c) {
406 case 'd':
407 ++decode_arg;
408 break;
409 case 'h':
410 case '?':
411 usage();
412 return 0;
413 case 'H':
414 ++do_hex;
415 break;
416 #ifdef SG_LIB_LINUX
417 case 'l':
418 do_linux = false;
419 break;
420 #endif
421 case 'L':
422 ++lu_cong_arg;
423 lu_cong_arg_given = true;
424 break;
425 case 'm':
426 maxlen = sg_get_num(optarg);
427 if ((maxlen < 0) || (maxlen > MAX_RLUNS_BUFF_LEN)) {
428 pr2serr("argument to '--maxlen' should be %d or less\n",
429 MAX_RLUNS_BUFF_LEN);
430 return SG_LIB_SYNTAX_ERROR;
431 } else if (maxlen < 4) {
432 pr2serr("Warning: setting '--maxlen' to 4\n");
433 maxlen = 4;
434 }
435 break;
436 case 'q':
437 do_quiet = true;
438 break;
439 case 'r':
440 do_raw = true;
441 break;
442 case 'R':
443 o_readonly = true;
444 break;
445 case 's':
446 select_rep = sg_get_num(optarg);
447 if ((select_rep < 0) || (select_rep > 255)) {
448 pr2serr("bad argument to '--select', expect 0 to 255\n");
449 return SG_LIB_SYNTAX_ERROR;
450 }
451 break;
452 case 't':
453 test_arg = optarg;
454 break;
455 case 'v':
456 verbose_given = true;
457 ++verbose;
458 break;
459 case 'V':
460 version_given = true;
461 break;
462 default:
463 pr2serr("unrecognised option code 0x%x ??\n", c);
464 usage();
465 return SG_LIB_SYNTAX_ERROR;
466 }
467 }
468 if (optind < argc) {
469 if (NULL == device_name) {
470 device_name = argv[optind];
471 ++optind;
472 }
473 if (optind < argc) {
474 for (; optind < argc; ++optind)
475 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
476 usage();
477 return SG_LIB_SYNTAX_ERROR;
478 }
479 }
480 #ifdef DEBUG
481 pr2serr("In DEBUG mode, ");
482 if (verbose_given && version_given) {
483 pr2serr("but override: '-vV' given, zero verbose and continue\n");
484 verbose_given = false;
485 version_given = false;
486 verbose = 0;
487 } else if (! verbose_given) {
488 pr2serr("set '-vv'\n");
489 verbose = 2;
490 } else
491 pr2serr("keep verbose=%d\n", verbose);
492 #else
493 if (verbose_given && version_given)
494 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
495 #endif
496 if (version_given) {
497 pr2serr("version: %s\n", version_str);
498 return 0;
499 }
500
501 if (test_arg) {
502 memset(lun_arr, 0, sizeof(lun_arr));
503 cp = test_arg;
504 /* check for leading 'L' */
505 #ifdef SG_LIB_LINUX
506 if ('L' == toupper(cp[0])) {
507 uint64_t ull;
508
509 if (('0' == cp[1]) && ('X' == toupper((uint8_t)cp[2])))
510 k = sscanf(cp + 3, " %" SCNx64, &ull);
511 else
512 k = sscanf(cp + 1, " %" SCNu64, &ull);
513 if (1 != k) {
514 pr2serr("Unable to read Linux style LUN integer given to "
515 "--test=\n");
516 return SG_LIB_SYNTAX_ERROR;
517 }
518 linux2t10_lun(ull, lun_arr);
519 test_linux_in = true;
520 } else
521 #endif
522 {
523 /* Check if trailing 'L' */
524 #ifdef SG_LIB_LINUX
525 m = strlen(cp); /* must be at least 1 char in test_arg */
526 if ('L' == toupper(cp[m - 1]))
527 test_linux_out = true;
528 #endif
529 if (('0' == cp[0]) && ('X' == toupper(cp[1])))
530 cp += 2;
531 if (strchr(cp, ' ') || strchr(cp, '\t') || strchr(cp, '-')) {
532 for (k = 0; k < 8; ++k, cp += 2) {
533 c = *cp;
534 if ('\0' == c)
535 break;
536 else if (! isxdigit(c))
537 ++cp;
538 if (1 != sscanf(cp, "%2x", &h))
539 break;
540 lun_arr[k] = h & 0xff;
541 }
542 } else {
543 for (k = 0; k < 8; ++k, cp += 2) {
544 if (1 != sscanf(cp, "%2x", &h))
545 break;
546 lun_arr[k] = h & 0xff;
547 }
548 }
549 if (0 == k) {
550 pr2serr("expected a hex number, optionally prefixed by "
551 "'0x'\n");
552 return SG_LIB_SYNTAX_ERROR;
553 }
554 }
555 #ifdef SG_LIB_LINUX
556 if (verbose || test_linux_in || decode_arg)
557 #else
558 if (verbose || decode_arg)
559 #endif
560 {
561 if (decode_arg > 1) {
562 printf("64 bit LUN in T10 (hex, dashed) format: ");
563 for (k = 0; k < 8; k += 2)
564 printf("%c%02x%02x", (k ? '-' : ' '), lun_arr[k],
565 lun_arr[k + 1]);
566 } else {
567 printf("64 bit LUN in T10 preferred (hex) format: ");
568 for (k = 0; k < 8; ++k)
569 printf(" %02x", lun_arr[k]);
570 }
571 printf("\n");
572 }
573 #ifdef SG_LIB_LINUX
574 if (test_linux_out) {
575 if (do_hex > 1)
576 printf("Linux 'word flipped' integer LUN representation: "
577 "0x%016" PRIx64 "\n", t10_2linux_lun(lun_arr));
578 else if (do_hex)
579 printf("Linux 'word flipped' integer LUN representation: 0x%"
580 PRIx64 "\n", t10_2linux_lun(lun_arr));
581 else
582 printf("Linux 'word flipped' integer LUN representation: %"
583 PRIu64 "\n", t10_2linux_lun(lun_arr));
584 }
585 #endif
586 printf("Decoded LUN:\n");
587 decode_lun(" ", lun_arr, (lu_cong_arg % 2), do_hex, verbose);
588 return 0;
589 }
590 if (NULL == device_name) {
591 pr2serr("missing device name!\n");
592 usage();
593 return SG_LIB_SYNTAX_ERROR;
594 }
595
596 if (do_raw) {
597 if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
598 perror("sg_set_binary_mode");
599 return SG_LIB_FILE_ERROR;
600 }
601 }
602
603 sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
604 if (sg_fd < 0) {
605 int err = -sg_fd;
606
607 pr2serr("open error: %s: %s\n", device_name, safe_strerror(err));
608 if ((! o_readonly) && ((err == EACCES) || (err == EROFS)))
609 pr2serr("Perhaps try again with --readonly option or with root "
610 "permissions\n");
611 return sg_convert_errno(-sg_fd);
612 }
613 if (decode_arg && (! lu_cong_arg_given)) {
614 if (verbose > 1)
615 pr2serr("in order to decode LUN and since --lu_cong not given, "
616 "do standard\nINQUIRY to find LU_CONG bit\n");
617 /* check if LU_CONG set in standard INQUIRY response */
618 res = sg_simple_inquiry(sg_fd, &sir, false, verbose);
619 ret = res;
620 if (res) {
621 pr2serr("fetching standard INQUIRY response failed\n");
622 goto the_end;
623 }
624 lu_cong_arg = !!(0x40 & sir.byte_1);
625 if (verbose && lu_cong_arg)
626 pr2serr("LU_CONG bit set in standard INQUIRY response\n");
627 }
628
629 if (0 == maxlen)
630 maxlen = DEF_RLUNS_BUFF_LEN;
631 reportLunsBuff = (uint8_t *)sg_memalign(maxlen, 0, &free_reportLunsBuff,
632 verbose > 3);
633 if (NULL == reportLunsBuff) {
634 pr2serr("unable to sg_memalign %d bytes\n", maxlen);
635 return sg_convert_errno(ENOMEM);
636 }
637 trunc = false;
638
639 res = sg_ll_report_luns(sg_fd, select_rep, reportLunsBuff, maxlen, true,
640 verbose);
641 ret = res;
642 if (0 == res) {
643 list_len = sg_get_unaligned_be32(reportLunsBuff + 0);
644 len_cap = list_len + 8;
645 if (len_cap > maxlen)
646 len_cap = maxlen;
647 if (do_raw) {
648 dStrRaw((const char *)reportLunsBuff, len_cap);
649 goto the_end;
650 }
651 if (1 == do_hex) {
652 hex2stdout(reportLunsBuff, len_cap, 1);
653 goto the_end;
654 }
655 luns = (list_len / 8);
656 if (! do_quiet)
657 printf("Lun list length = %d which imples %d lun entr%s\n",
658 list_len, luns, ((1 == luns) ? "y" : "ies"));
659 if ((list_len + 8) > maxlen) {
660 luns = ((maxlen - 8) / 8);
661 trunc = true;
662 pr2serr(" <<too many luns for internal buffer, will show %d "
663 "lun%s>>\n", luns, ((1 == luns) ? "" : "s"));
664 }
665 if (verbose > 1) {
666 pr2serr("\nOutput response in hex\n");
667 hex2stderr(reportLunsBuff, (trunc ? maxlen : list_len + 8), 1);
668 }
669 for (k = 0, off = 8; k < luns; ++k, off += 8) {
670 if (! do_quiet) {
671 if (0 == k)
672 printf("Report luns [select_report=0x%x]:\n", select_rep);
673 printf(" ");
674 }
675 for (m = 0; m < 8; ++m)
676 printf("%02x", reportLunsBuff[off + m]);
677 #ifdef SG_LIB_LINUX
678 if (do_linux) {
679 uint64_t lin_lun;
680
681 lin_lun = t10_2linux_lun(reportLunsBuff + off);
682 if (do_hex > 1)
683 printf(" [0x%" PRIx64 "]", lin_lun);
684 else
685 printf(" [%" PRIu64 "]", lin_lun);
686 }
687 #endif
688 printf("\n");
689 if (decode_arg)
690 decode_lun(" ", reportLunsBuff + off,
691 (bool)(lu_cong_arg % 2), do_hex, verbose);
692 }
693 } else if (SG_LIB_CAT_INVALID_OP == res)
694 pr2serr("Report Luns command not supported (support mandatory in "
695 "SPC-3)\n");
696 else if (SG_LIB_CAT_ABORTED_COMMAND == res)
697 pr2serr("Report Luns, aborted command\n");
698 else if (SG_LIB_CAT_ILLEGAL_REQ == res)
699 pr2serr("Report Luns command has bad field in cdb\n");
700 else {
701 char b[80];
702
703 sg_get_category_sense_str(res, sizeof(b), b, verbose);
704 pr2serr("Report Luns command: %s\n", b);
705 }
706
707 the_end:
708 if (free_reportLunsBuff)
709 free(free_reportLunsBuff);
710 res = sg_cmds_close_device(sg_fd);
711 if (res < 0) {
712 pr2serr("close error: %s\n", safe_strerror(-res));
713 if (0 == ret)
714 return sg_convert_errno(-res);
715 }
716 if (0 == verbose) {
717 if (! sg_if_can2stderr("sg_luns failed: ", ret))
718 pr2serr("Some error occurred, try again with '-v' or '-vv' for "
719 "more information\n");
720 }
721 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
722 }
723