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