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