1 /*
2 * Copyright (c) 2004-2018 Christophe Varoqui and 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 <getopt.h>
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "sg_lib.h"
24 #include "sg_cmds_basic.h"
25 #include "sg_cmds_extra.h"
26 #include "sg_unaligned.h"
27 #include "sg_pr2serr.h"
28
29 /* A utility program for the Linux OS SCSI subsystem.
30 *
31 *
32 * This program issues the SCSI command REPORT TARGET PORT GROUPS
33 * to the given SCSI device.
34 */
35
36 static const char * version_str = "1.27 20180628";
37
38 #define REPORT_TGT_GRP_BUFF_LEN 1024
39
40 #define TPGS_STATE_OPTIMIZED 0x0
41 #define TPGS_STATE_NONOPTIMIZED 0x1
42 #define TPGS_STATE_STANDBY 0x2
43 #define TPGS_STATE_UNAVAILABLE 0x3
44 #define TPGS_STATE_LB_DEPENDENT 0x4
45 #define TPGS_STATE_OFFLINE 0xe /* SPC-4 rev 9 */
46 #define TPGS_STATE_TRANSITIONING 0xf
47
48 #define STATUS_CODE_NOSTATUS 0x0
49 #define STATUS_CODE_CHANGED_BY_SET 0x1
50 #define STATUS_CODE_CHANGED_BY_IMPLICIT 0x2
51
52 static struct option long_options[] = {
53 {"decode", no_argument, 0, 'd'},
54 {"extended", no_argument, 0, 'e'},
55 {"help", no_argument, 0, 'h'},
56 {"hex", no_argument, 0, 'H'},
57 {"raw", no_argument, 0, 'r'},
58 {"readonly", no_argument, 0, 'R'},
59 {"verbose", no_argument, 0, 'v'},
60 {"version", no_argument, 0, 'V'},
61 {0, 0, 0, 0},
62 };
63
64
65 static void
usage()66 usage()
67 {
68 pr2serr("Usage: sg_rtpg [--decode] [--extended] [--help] [--hex] "
69 "[--raw] [--readonly]\n"
70 " [--verbose] [--version] DEVICE\n"
71 " where:\n"
72 " --decode|-d decode status and asym. access state\n"
73 " --extended|-e use extended header parameter data "
74 "format\n"
75 " --help|-h print out usage message\n"
76 " --hex|-H print out response in hex\n"
77 " --raw|-r output response in binary to stdout\n"
78 " --readonly|-R open DEVICE read-only (def: read-write)\n"
79 " --verbose|-v increase verbosity\n"
80 " --version|-V print version string and exit\n\n"
81 "Performs a SCSI REPORT TARGET PORT GROUPS command\n");
82
83 }
84
85 static void
dStrRaw(const uint8_t * str,int len)86 dStrRaw(const uint8_t * str, int len)
87 {
88 int k;
89
90 for (k = 0; k < len; ++k)
91 printf("%c", str[k]);
92 }
93
94 static void
decode_status(const int st)95 decode_status(const int st)
96 {
97 switch (st) {
98 case STATUS_CODE_NOSTATUS:
99 printf(" (no status available)");
100 break;
101 case STATUS_CODE_CHANGED_BY_SET:
102 printf(" (target port asym. state changed by SET TARGET PORT "
103 "GROUPS command)");
104 break;
105 case STATUS_CODE_CHANGED_BY_IMPLICIT:
106 printf(" (target port asym. state changed by implicit lu "
107 "behaviour)");
108 break;
109 default:
110 printf(" (unknown status code)");
111 break;
112 }
113 }
114
115 static void
decode_tpgs_state(const int st)116 decode_tpgs_state(const int st)
117 {
118 switch (st) {
119 case TPGS_STATE_OPTIMIZED:
120 printf(" (active/optimized)");
121 break;
122 case TPGS_STATE_NONOPTIMIZED:
123 printf(" (active/non optimized)");
124 break;
125 case TPGS_STATE_STANDBY:
126 printf(" (standby)");
127 break;
128 case TPGS_STATE_UNAVAILABLE:
129 printf(" (unavailable)");
130 break;
131 case TPGS_STATE_LB_DEPENDENT:
132 printf(" (logical block dependent)");
133 break;
134 case TPGS_STATE_OFFLINE:
135 printf(" (offline)");
136 break;
137 case TPGS_STATE_TRANSITIONING:
138 printf(" (transitioning between states)");
139 break;
140 default:
141 printf(" (unknown)");
142 break;
143 }
144 }
145
146 int
main(int argc,char * argv[])147 main(int argc, char * argv[])
148 {
149 bool decode = false;
150 bool hex = false;
151 bool raw = false;
152 bool o_readonly = false;
153 bool extended = false;
154 bool verbose_given = false;
155 bool version_given = false;
156 int k, j, off, res, c, report_len, buff_len, tgt_port_count;
157 int sg_fd = -1;
158 int ret = 0;
159 int verbose = 0;
160 uint8_t * reportTgtGrpBuff = NULL;
161 uint8_t * bp;
162 const char * device_name = NULL;
163
164 while (1) {
165 int option_index = 0;
166
167 c = getopt_long(argc, argv, "dehHrRvV", long_options,
168 &option_index);
169 if (c == -1)
170 break;
171
172 switch (c) {
173 case 'd':
174 decode = true;
175 break;
176 case 'e':
177 extended = true;
178 break;
179 case 'h':
180 case '?':
181 usage();
182 return 0;
183 case 'H':
184 hex = true;
185 break;
186 case 'r':
187 raw = true;
188 break;
189 case 'R':
190 o_readonly = true;
191 break;
192 case 'v':
193 verbose_given = true;
194 ++verbose;
195 break;
196 case 'V':
197 version_given = true;
198 break;
199 default:
200 pr2serr("unrecognised option code 0x%x ??\n", c);
201 usage();
202 return SG_LIB_SYNTAX_ERROR;
203 }
204 }
205 if (optind < argc) {
206 if (NULL == device_name) {
207 device_name = argv[optind];
208 ++optind;
209 }
210 if (optind < argc) {
211 for (; optind < argc; ++optind)
212 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
213 usage();
214 return SG_LIB_SYNTAX_ERROR;
215 }
216 }
217 #ifdef DEBUG
218 pr2serr("In DEBUG mode, ");
219 if (verbose_given && version_given) {
220 pr2serr("but override: '-vV' given, zero verbose and continue\n");
221 verbose_given = false;
222 version_given = false;
223 verbose = 0;
224 } else if (! verbose_given) {
225 pr2serr("set '-vv'\n");
226 verbose = 2;
227 } else
228 pr2serr("keep verbose=%d\n", verbose);
229 #else
230 if (verbose_given && version_given)
231 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
232 #endif
233 if (version_given) {
234 pr2serr("Version: %s\n", version_str);
235 return 0;
236 }
237 if (NULL == device_name) {
238 pr2serr("Missing device name!\n\n");
239 usage();
240 return SG_LIB_SYNTAX_ERROR;
241 }
242 if (raw) {
243 if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
244 perror("sg_set_binary_mode");
245 return SG_LIB_FILE_ERROR;
246 }
247 }
248
249 sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
250 if (sg_fd < 0) {
251 if (verbose)
252 pr2serr("open error: %s: %s\n", device_name,
253 safe_strerror(-sg_fd));
254 ret = sg_convert_errno(-sg_fd);
255 goto err_out;
256 }
257
258 buff_len = REPORT_TGT_GRP_BUFF_LEN;
259
260 retry:
261 reportTgtGrpBuff = (uint8_t *)malloc(buff_len);
262 if (NULL == reportTgtGrpBuff) {
263 pr2serr(" Out of memory (ram)\n");
264 goto err_out;
265 }
266 memset(reportTgtGrpBuff, 0x0, buff_len);
267
268 res = sg_ll_report_tgt_prt_grp2(sg_fd, reportTgtGrpBuff,
269 buff_len,
270 extended, true, verbose);
271 ret = res;
272 if (0 == res) {
273 report_len = sg_get_unaligned_be32(reportTgtGrpBuff + 0) + 4;
274 if (report_len > buff_len) {
275 free(reportTgtGrpBuff);
276 buff_len = report_len;
277 goto retry;
278 }
279 if (raw) {
280 dStrRaw(reportTgtGrpBuff, report_len);
281 goto err_out;
282 }
283 if (verbose)
284 printf("Report list length = %d\n", report_len);
285 if (hex) {
286 if (verbose)
287 printf("\nOutput response in hex:\n");
288 hex2stdout(reportTgtGrpBuff, report_len, 1);
289 goto err_out;
290 }
291 printf("Report target port groups:\n");
292 bp = reportTgtGrpBuff + 4;
293 if (extended) {
294 if (0x10 != (bp[0] & 0x70)) {
295 pr2serr(" <<invalid extended header format\n");
296 goto err_out;
297 }
298 printf(" Implicit transition time: %d\n", bp[1]);
299 bp += 4;
300 }
301 for (k = bp - reportTgtGrpBuff; k < report_len;
302 k += off, bp += off) {
303
304 printf(" target port group id : 0x%x , Pref=%d, Rtpg_fmt=%d\n",
305 sg_get_unaligned_be16(bp + 2), !!(bp[0] & 0x80),
306 (bp[0] >> 4) & 0x07);
307 printf(" target port group asymmetric access state : ");
308 printf("0x%02x", bp[0] & 0x0f);
309 if (decode)
310 decode_tpgs_state(bp[0] & 0x0f);
311 printf("\n");
312
313 printf(" T_SUP : %d, ", !!(bp[1] & 0x80));
314 printf("O_SUP : %d, ", !!(bp[1] & 0x40));
315 printf("LBD_SUP : %d, ", !!(bp[1] & 0x10));
316 printf("U_SUP : %d, ", !!(bp[1] & 0x08));
317 printf("S_SUP : %d, ", !!(bp[1] & 0x04));
318 printf("AN_SUP : %d, ", !!(bp[1] & 0x02));
319 printf("AO_SUP : %d\n", !!(bp[1] & 0x01));
320
321 printf(" status code : ");
322 printf("0x%02x", bp[5]);
323 if (decode)
324 decode_status(bp[5]);
325 printf("\n");
326
327 printf(" vendor unique status : ");
328 printf("0x%02x\n", bp[6]);
329
330 printf(" target port count : ");
331 tgt_port_count = bp[7];
332 printf("%02x\n", tgt_port_count);
333
334 for (j = 0; j < tgt_port_count * 4; j += 4) {
335 if (0 == j)
336 printf(" Relative target port ids:\n");
337 printf(" 0x%02x\n",
338 sg_get_unaligned_be16(bp + 8 + j + 2));
339 }
340 off = 8 + j;
341 }
342 } else if (SG_LIB_CAT_INVALID_OP == res)
343 pr2serr("Report Target Port Groups command not supported\n");
344 else if (SG_LIB_CAT_ILLEGAL_REQ == res)
345 pr2serr("bad field in Report Target Port Groups cdb including "
346 "unsupported service action\n");
347 else {
348 char b[80];
349
350 sg_get_category_sense_str(res, sizeof(b), b, verbose);
351 pr2serr("Report Target Port Groups: %s\n", b);
352 }
353
354 err_out:
355 if (sg_fd >= 0) {
356 res = sg_cmds_close_device(sg_fd);
357 if (res < 0) {
358 pr2serr("close error: %s\n", safe_strerror(-res));
359 if (0 == ret)
360 ret = sg_convert_errno(-res);
361 }
362 }
363 if (reportTgtGrpBuff)
364 free(reportTgtGrpBuff);
365 if (0 == verbose) {
366 if (! sg_if_can2stderr("sg_rtpg failed: ", ret))
367 pr2serr("Some error occurred, try again with '-v' "
368 "or '-vv' for more information\n");
369 }
370 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
371 }
372