1*44704f69SBart Van Assche /*
2*44704f69SBart Van Assche * Copyright (c) 2010-2018 Hannes Reinecke.
3*44704f69SBart Van Assche * All rights reserved.
4*44704f69SBart Van Assche * Use of this source code is governed by a BSD-style
5*44704f69SBart Van Assche * license that can be found in the BSD_LICENSE file.
6*44704f69SBart Van Assche *
7*44704f69SBart Van Assche * SPDX-License-Identifier: BSD-2-Clause
8*44704f69SBart Van Assche */
9*44704f69SBart Van Assche
10*44704f69SBart Van Assche #include <unistd.h>
11*44704f69SBart Van Assche #include <fcntl.h>
12*44704f69SBart Van Assche #include <stdio.h>
13*44704f69SBart Van Assche #include <stdlib.h>
14*44704f69SBart Van Assche #include <stdarg.h>
15*44704f69SBart Van Assche #include <stdbool.h>
16*44704f69SBart Van Assche #include <string.h>
17*44704f69SBart Van Assche #include <errno.h>
18*44704f69SBart Van Assche #include <getopt.h>
19*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
20*44704f69SBart Van Assche #include <inttypes.h>
21*44704f69SBart Van Assche
22*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
23*44704f69SBart Van Assche #include "config.h"
24*44704f69SBart Van Assche #endif
25*44704f69SBart Van Assche
26*44704f69SBart Van Assche #include "sg_lib.h"
27*44704f69SBart Van Assche #include "sg_cmds_basic.h"
28*44704f69SBart Van Assche #include "sg_cmds_extra.h"
29*44704f69SBart Van Assche #include "sg_unaligned.h"
30*44704f69SBart Van Assche #include "sg_pr2serr.h"
31*44704f69SBart Van Assche
32*44704f69SBart Van Assche /*
33*44704f69SBart Van Assche * A utility program originally written for the Linux OS SCSI subsystem.
34*44704f69SBart Van Assche *
35*44704f69SBart Van Assche *
36*44704f69SBart Van Assche * This program issues the SCSI REPORT REFERRALS command to the given
37*44704f69SBart Van Assche * SCSI device.
38*44704f69SBart Van Assche */
39*44704f69SBart Van Assche
40*44704f69SBart Van Assche static const char * version_str = "1.13 20180628"; /* sbc4r10 */
41*44704f69SBart Van Assche
42*44704f69SBart Van Assche #define MAX_REFER_BUFF_LEN (1024 * 1024)
43*44704f69SBart Van Assche #define DEF_REFER_BUFF_LEN 256
44*44704f69SBart Van Assche
45*44704f69SBart Van Assche #define TPGS_STATE_OPTIMIZED 0x0
46*44704f69SBart Van Assche #define TPGS_STATE_NONOPTIMIZED 0x1
47*44704f69SBart Van Assche #define TPGS_STATE_STANDBY 0x2
48*44704f69SBart Van Assche #define TPGS_STATE_UNAVAILABLE 0x3
49*44704f69SBart Van Assche #define TPGS_STATE_LB_DEPENDENT 0x4
50*44704f69SBart Van Assche #define TPGS_STATE_OFFLINE 0xe /* SPC-4 rev 9 */
51*44704f69SBart Van Assche #define TPGS_STATE_TRANSITIONING 0xf
52*44704f69SBart Van Assche
53*44704f69SBart Van Assche static uint8_t referralBuff[DEF_REFER_BUFF_LEN];
54*44704f69SBart Van Assche
decode_tpgs_state(const int st)55*44704f69SBart Van Assche static const char *decode_tpgs_state(const int st)
56*44704f69SBart Van Assche {
57*44704f69SBart Van Assche switch (st) {
58*44704f69SBart Van Assche case TPGS_STATE_OPTIMIZED:
59*44704f69SBart Van Assche return "active/optimized";
60*44704f69SBart Van Assche break;
61*44704f69SBart Van Assche case TPGS_STATE_NONOPTIMIZED:
62*44704f69SBart Van Assche return "active/non optimized";
63*44704f69SBart Van Assche break;
64*44704f69SBart Van Assche case TPGS_STATE_STANDBY:
65*44704f69SBart Van Assche return "standby";
66*44704f69SBart Van Assche break;
67*44704f69SBart Van Assche case TPGS_STATE_UNAVAILABLE:
68*44704f69SBart Van Assche return "unavailable";
69*44704f69SBart Van Assche break;
70*44704f69SBart Van Assche case TPGS_STATE_LB_DEPENDENT:
71*44704f69SBart Van Assche return "logical block dependent";
72*44704f69SBart Van Assche break;
73*44704f69SBart Van Assche case TPGS_STATE_OFFLINE:
74*44704f69SBart Van Assche return "offline";
75*44704f69SBart Van Assche break;
76*44704f69SBart Van Assche case TPGS_STATE_TRANSITIONING:
77*44704f69SBart Van Assche return "transitioning between states";
78*44704f69SBart Van Assche break;
79*44704f69SBart Van Assche default:
80*44704f69SBart Van Assche return "unknown";
81*44704f69SBart Van Assche break;
82*44704f69SBart Van Assche }
83*44704f69SBart Van Assche }
84*44704f69SBart Van Assche
85*44704f69SBart Van Assche static struct option long_options[] = {
86*44704f69SBart Van Assche {"help", no_argument, 0, 'h'},
87*44704f69SBart Van Assche {"hex", no_argument, 0, 'H'},
88*44704f69SBart Van Assche {"lba", required_argument, 0, 'l'},
89*44704f69SBart Van Assche {"maxlen", required_argument, 0, 'm'},
90*44704f69SBart Van Assche {"one-segment", no_argument, 0, 's'},
91*44704f69SBart Van Assche {"one_segment", no_argument, 0, 's'},
92*44704f69SBart Van Assche {"raw", no_argument, 0, 'r'},
93*44704f69SBart Van Assche {"readonly", no_argument, 0, 'R'},
94*44704f69SBart Van Assche {"verbose", no_argument, 0, 'v'},
95*44704f69SBart Van Assche {"version", no_argument, 0, 'V'},
96*44704f69SBart Van Assche {0, 0, 0, 0},
97*44704f69SBart Van Assche };
98*44704f69SBart Van Assche
99*44704f69SBart Van Assche static void
usage()100*44704f69SBart Van Assche usage()
101*44704f69SBart Van Assche {
102*44704f69SBart Van Assche pr2serr("Usage: sg_referrals [--help] [--hex] [--lba=LBA] "
103*44704f69SBart Van Assche "[--maxlen=LEN]\n"
104*44704f69SBart Van Assche " [--one-segment] [--raw] [--readonly] "
105*44704f69SBart Van Assche "[--verbose]\n"
106*44704f69SBart Van Assche " [--version] DEVICE\n"
107*44704f69SBart Van Assche " where:\n"
108*44704f69SBart Van Assche " --help|-h print out usage message\n"
109*44704f69SBart Van Assche " --hex|-H output in hexadecimal\n"
110*44704f69SBart Van Assche " --lba=LBA|-l LBA starting LBA (logical block address) "
111*44704f69SBart Van Assche "(def: 0)\n"
112*44704f69SBart Van Assche " --maxlen=LEN|-m LEN max response length (allocation "
113*44704f69SBart Van Assche "length in cdb)\n"
114*44704f69SBart Van Assche " (def: 0 -> %d bytes)\n",
115*44704f69SBart Van Assche DEF_REFER_BUFF_LEN );
116*44704f69SBart Van Assche pr2serr(" --one-segment|-s return information about the specified "
117*44704f69SBart Van Assche "segment only\n"
118*44704f69SBart Van Assche " --raw|-r output in binary\n"
119*44704f69SBart Van Assche " --verbose|-v increase verbosity\n"
120*44704f69SBart Van Assche " --version|-V print version string and exit\n\n"
121*44704f69SBart Van Assche "Performs a SCSI REPORT REFERRALS command (SBC-3)\n"
122*44704f69SBart Van Assche );
123*44704f69SBart Van Assche }
124*44704f69SBart Van Assche
125*44704f69SBart Van Assche static void
dStrRaw(const uint8_t * str,int len)126*44704f69SBart Van Assche dStrRaw(const uint8_t * str, int len)
127*44704f69SBart Van Assche {
128*44704f69SBart Van Assche int k;
129*44704f69SBart Van Assche
130*44704f69SBart Van Assche for (k = 0; k < len; ++k)
131*44704f69SBart Van Assche printf("%c", str[k]);
132*44704f69SBart Van Assche }
133*44704f69SBart Van Assche
134*44704f69SBart Van Assche /* Decodes given user data referral segment descriptor
135*44704f69SBart Van Assche * the number of blocks and returns the number of bytes processed,
136*44704f69SBart Van Assche * -1 for error.
137*44704f69SBart Van Assche */
138*44704f69SBart Van Assche static int
decode_referral_desc(const uint8_t * bp,int bytes)139*44704f69SBart Van Assche decode_referral_desc(const uint8_t * bp, int bytes)
140*44704f69SBart Van Assche {
141*44704f69SBart Van Assche int j, n;
142*44704f69SBart Van Assche uint64_t first, last;
143*44704f69SBart Van Assche
144*44704f69SBart Van Assche if (NULL == bp)
145*44704f69SBart Van Assche return -1;
146*44704f69SBart Van Assche
147*44704f69SBart Van Assche if (bytes < 20)
148*44704f69SBart Van Assche return -1;
149*44704f69SBart Van Assche
150*44704f69SBart Van Assche first = sg_get_unaligned_be64(bp + 4);
151*44704f69SBart Van Assche last = sg_get_unaligned_be64(bp + 12);
152*44704f69SBart Van Assche
153*44704f69SBart Van Assche printf(" target port descriptors: %d\n", bp[3]);
154*44704f69SBart Van Assche printf(" user data segment: first lba %" PRIu64 ", last lba %"
155*44704f69SBart Van Assche PRIu64 "\n", first, last);
156*44704f69SBart Van Assche n = 20;
157*44704f69SBart Van Assche bytes -= n;
158*44704f69SBart Van Assche for (j = 0; j < bp[3]; j++) {
159*44704f69SBart Van Assche if (bytes < 4)
160*44704f69SBart Van Assche return -1;
161*44704f69SBart Van Assche printf(" target port descriptor %d:\n", j);
162*44704f69SBart Van Assche printf(" port group %x state (%s)\n",
163*44704f69SBart Van Assche sg_get_unaligned_be16(bp + n + 2),
164*44704f69SBart Van Assche decode_tpgs_state(bp[n] & 0xf));
165*44704f69SBart Van Assche n += 4;
166*44704f69SBart Van Assche bytes -= 4;
167*44704f69SBart Van Assche }
168*44704f69SBart Van Assche return n;
169*44704f69SBart Van Assche }
170*44704f69SBart Van Assche
171*44704f69SBart Van Assche
172*44704f69SBart Van Assche int
main(int argc,char * argv[])173*44704f69SBart Van Assche main(int argc, char * argv[])
174*44704f69SBart Van Assche {
175*44704f69SBart Van Assche bool do_one_segment = false;
176*44704f69SBart Van Assche bool o_readonly = false;
177*44704f69SBart Van Assche bool do_raw = false;
178*44704f69SBart Van Assche bool verbose_given = false;
179*44704f69SBart Van Assche bool version_given = false;
180*44704f69SBart Van Assche int k, res, c, rlen;
181*44704f69SBart Van Assche int sg_fd = -1;
182*44704f69SBart Van Assche int do_hex = 0;
183*44704f69SBart Van Assche int maxlen = DEF_REFER_BUFF_LEN;
184*44704f69SBart Van Assche int verbose = 0;
185*44704f69SBart Van Assche int desc = 0;
186*44704f69SBart Van Assche int ret = 0;
187*44704f69SBart Van Assche int64_t ll;
188*44704f69SBart Van Assche uint64_t lba = 0;
189*44704f69SBart Van Assche const char * device_name = NULL;
190*44704f69SBart Van Assche const uint8_t * bp;
191*44704f69SBart Van Assche uint8_t * referralBuffp = referralBuff;
192*44704f69SBart Van Assche uint8_t * free_referralBuffp = NULL;
193*44704f69SBart Van Assche
194*44704f69SBart Van Assche while (1) {
195*44704f69SBart Van Assche int option_index = 0;
196*44704f69SBart Van Assche
197*44704f69SBart Van Assche c = getopt_long(argc, argv, "hHl:m:rRsvV", long_options,
198*44704f69SBart Van Assche &option_index);
199*44704f69SBart Van Assche if (c == -1)
200*44704f69SBart Van Assche break;
201*44704f69SBart Van Assche
202*44704f69SBart Van Assche switch (c) {
203*44704f69SBart Van Assche case 'h':
204*44704f69SBart Van Assche case '?':
205*44704f69SBart Van Assche usage();
206*44704f69SBart Van Assche return 0;
207*44704f69SBart Van Assche case 'H':
208*44704f69SBart Van Assche ++do_hex;
209*44704f69SBart Van Assche break;
210*44704f69SBart Van Assche case 'l':
211*44704f69SBart Van Assche ll = sg_get_llnum(optarg);
212*44704f69SBart Van Assche if (-1 == ll) {
213*44704f69SBart Van Assche pr2serr("bad argument to '--lba'\n");
214*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
215*44704f69SBart Van Assche }
216*44704f69SBart Van Assche lba = (uint64_t)ll;
217*44704f69SBart Van Assche break;
218*44704f69SBart Van Assche case 'm':
219*44704f69SBart Van Assche maxlen = sg_get_num(optarg);
220*44704f69SBart Van Assche if ((maxlen < 0) || (maxlen > MAX_REFER_BUFF_LEN)) {
221*44704f69SBart Van Assche pr2serr("argument to '--maxlen' should be %d or less\n",
222*44704f69SBart Van Assche MAX_REFER_BUFF_LEN);
223*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
224*44704f69SBart Van Assche }
225*44704f69SBart Van Assche break;
226*44704f69SBart Van Assche case 's':
227*44704f69SBart Van Assche do_one_segment = true;
228*44704f69SBart Van Assche break;
229*44704f69SBart Van Assche case 'r':
230*44704f69SBart Van Assche do_raw = true;
231*44704f69SBart Van Assche break;
232*44704f69SBart Van Assche case 'R':
233*44704f69SBart Van Assche o_readonly = true;
234*44704f69SBart Van Assche break;
235*44704f69SBart Van Assche case 'v':
236*44704f69SBart Van Assche verbose_given = true;
237*44704f69SBart Van Assche ++verbose;
238*44704f69SBart Van Assche break;
239*44704f69SBart Van Assche case 'V':
240*44704f69SBart Van Assche version_given = true;
241*44704f69SBart Van Assche break;
242*44704f69SBart Van Assche default:
243*44704f69SBart Van Assche pr2serr("unrecognised option code 0x%x ??\n", c);
244*44704f69SBart Van Assche usage();
245*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
246*44704f69SBart Van Assche }
247*44704f69SBart Van Assche }
248*44704f69SBart Van Assche if (optind < argc) {
249*44704f69SBart Van Assche if (NULL == device_name) {
250*44704f69SBart Van Assche device_name = argv[optind];
251*44704f69SBart Van Assche ++optind;
252*44704f69SBart Van Assche }
253*44704f69SBart Van Assche if (optind < argc) {
254*44704f69SBart Van Assche for (; optind < argc; ++optind)
255*44704f69SBart Van Assche pr2serr("Unexpected extra argument: %s\n", argv[optind]);
256*44704f69SBart Van Assche usage();
257*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
258*44704f69SBart Van Assche }
259*44704f69SBart Van Assche }
260*44704f69SBart Van Assche #ifdef DEBUG
261*44704f69SBart Van Assche pr2serr("In DEBUG mode, ");
262*44704f69SBart Van Assche if (verbose_given && version_given) {
263*44704f69SBart Van Assche pr2serr("but override: '-vV' given, zero verbose and continue\n");
264*44704f69SBart Van Assche verbose_given = false;
265*44704f69SBart Van Assche version_given = false;
266*44704f69SBart Van Assche verbose = 0;
267*44704f69SBart Van Assche } else if (! verbose_given) {
268*44704f69SBart Van Assche pr2serr("set '-vv'\n");
269*44704f69SBart Van Assche verbose = 2;
270*44704f69SBart Van Assche } else
271*44704f69SBart Van Assche pr2serr("keep verbose=%d\n", verbose);
272*44704f69SBart Van Assche #else
273*44704f69SBart Van Assche if (verbose_given && version_given)
274*44704f69SBart Van Assche pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
275*44704f69SBart Van Assche #endif
276*44704f69SBart Van Assche if (version_given) {
277*44704f69SBart Van Assche pr2serr("version: %s\n", version_str);
278*44704f69SBart Van Assche return 0;
279*44704f69SBart Van Assche }
280*44704f69SBart Van Assche
281*44704f69SBart Van Assche if (NULL == device_name) {
282*44704f69SBart Van Assche pr2serr("No DEVICE argument given\n\n");
283*44704f69SBart Van Assche usage();
284*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
285*44704f69SBart Van Assche }
286*44704f69SBart Van Assche if (maxlen > DEF_REFER_BUFF_LEN) {
287*44704f69SBart Van Assche referralBuffp = (uint8_t *)sg_memalign(maxlen, 0,
288*44704f69SBart Van Assche &free_referralBuffp,
289*44704f69SBart Van Assche verbose > 3);
290*44704f69SBart Van Assche if (NULL == referralBuffp) {
291*44704f69SBart Van Assche pr2serr("unable to allocate %d bytes on heap\n", maxlen);
292*44704f69SBart Van Assche return sg_convert_errno(ENOMEM);
293*44704f69SBart Van Assche }
294*44704f69SBart Van Assche }
295*44704f69SBart Van Assche if (do_raw) {
296*44704f69SBart Van Assche if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
297*44704f69SBart Van Assche perror("sg_set_binary_mode");
298*44704f69SBart Van Assche ret = SG_LIB_FILE_ERROR;
299*44704f69SBart Van Assche goto free_buff;
300*44704f69SBart Van Assche }
301*44704f69SBart Van Assche }
302*44704f69SBart Van Assche
303*44704f69SBart Van Assche sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
304*44704f69SBart Van Assche if (sg_fd < 0) {
305*44704f69SBart Van Assche if (verbose)
306*44704f69SBart Van Assche pr2serr("open error: %s: %s\n", device_name,
307*44704f69SBart Van Assche safe_strerror(-sg_fd));
308*44704f69SBart Van Assche ret = sg_convert_errno(-sg_fd);
309*44704f69SBart Van Assche goto free_buff;
310*44704f69SBart Van Assche }
311*44704f69SBart Van Assche
312*44704f69SBart Van Assche res = sg_ll_report_referrals(sg_fd, lba, do_one_segment, referralBuffp,
313*44704f69SBart Van Assche maxlen, true, verbose);
314*44704f69SBart Van Assche ret = res;
315*44704f69SBart Van Assche if (0 == res) {
316*44704f69SBart Van Assche if (maxlen >= 4)
317*44704f69SBart Van Assche /*
318*44704f69SBart Van Assche * This is strictly speaking incorrect. However, the
319*44704f69SBart Van Assche * spec reserved bytes 0 and 1, so some implementations
320*44704f69SBart Van Assche * might want to use them to increase the number of
321*44704f69SBart Van Assche * possible user segments.
322*44704f69SBart Van Assche * And maybe someone takes a pity and updates the spec ...
323*44704f69SBart Van Assche */
324*44704f69SBart Van Assche rlen = sg_get_unaligned_be32(referralBuffp + 0) + 4;
325*44704f69SBart Van Assche else
326*44704f69SBart Van Assche rlen = maxlen;
327*44704f69SBart Van Assche k = (rlen > maxlen) ? maxlen : rlen;
328*44704f69SBart Van Assche if (do_raw) {
329*44704f69SBart Van Assche dStrRaw(referralBuffp, k);
330*44704f69SBart Van Assche goto the_end;
331*44704f69SBart Van Assche }
332*44704f69SBart Van Assche if (do_hex) {
333*44704f69SBart Van Assche hex2stdout(referralBuffp, k, 1);
334*44704f69SBart Van Assche goto the_end;
335*44704f69SBart Van Assche }
336*44704f69SBart Van Assche if (maxlen < 4) {
337*44704f69SBart Van Assche if (verbose)
338*44704f69SBart Van Assche pr2serr("Exiting because allocation length (maxlen) less "
339*44704f69SBart Van Assche "than 4\n");
340*44704f69SBart Van Assche goto the_end;
341*44704f69SBart Van Assche }
342*44704f69SBart Van Assche if ((verbose > 1) || (verbose && (rlen > maxlen))) {
343*44704f69SBart Van Assche pr2serr("response length %d bytes\n", rlen);
344*44704f69SBart Van Assche if (rlen > maxlen)
345*44704f69SBart Van Assche pr2serr(" ... which is greater than maxlen (allocation "
346*44704f69SBart Van Assche "length %d), truncation\n", maxlen);
347*44704f69SBart Van Assche }
348*44704f69SBart Van Assche if (rlen > maxlen)
349*44704f69SBart Van Assche rlen = maxlen;
350*44704f69SBart Van Assche
351*44704f69SBart Van Assche bp = referralBuffp + 4;
352*44704f69SBart Van Assche k = 0;
353*44704f69SBart Van Assche printf("Report referrals:\n");
354*44704f69SBart Van Assche while (k < rlen - 4) {
355*44704f69SBart Van Assche printf(" descriptor %d:\n", desc);
356*44704f69SBart Van Assche res = decode_referral_desc(bp + k, rlen - 4 - k);
357*44704f69SBart Van Assche if (res < 0) {
358*44704f69SBart Van Assche pr2serr("bad user data segment referral descriptor\n");
359*44704f69SBart Van Assche break;
360*44704f69SBart Van Assche }
361*44704f69SBart Van Assche k += res;
362*44704f69SBart Van Assche desc++;
363*44704f69SBart Van Assche }
364*44704f69SBart Van Assche } else {
365*44704f69SBart Van Assche char b[80];
366*44704f69SBart Van Assche
367*44704f69SBart Van Assche sg_get_category_sense_str(res, sizeof(b), b, verbose);
368*44704f69SBart Van Assche pr2serr("Report Referrals command failed: %s\n", b);
369*44704f69SBart Van Assche }
370*44704f69SBart Van Assche
371*44704f69SBart Van Assche the_end:
372*44704f69SBart Van Assche res = sg_cmds_close_device(sg_fd);
373*44704f69SBart Van Assche if (res < 0) {
374*44704f69SBart Van Assche pr2serr("close error: %s\n", safe_strerror(-res));
375*44704f69SBart Van Assche if (0 == ret)
376*44704f69SBart Van Assche ret = sg_convert_errno(-res);
377*44704f69SBart Van Assche }
378*44704f69SBart Van Assche free_buff:
379*44704f69SBart Van Assche if (free_referralBuffp)
380*44704f69SBart Van Assche free(free_referralBuffp);
381*44704f69SBart Van Assche if (0 == verbose) {
382*44704f69SBart Van Assche if (! sg_if_can2stderr("sg_referrals failed: ", ret))
383*44704f69SBart Van Assche pr2serr("Some error occurred, try again with '-v' "
384*44704f69SBart Van Assche "or '-vv' for more information\n");
385*44704f69SBart Van Assche }
386*44704f69SBart Van Assche return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
387*44704f69SBart Van Assche }
388