xref: /aosp_15_r20/external/sg3_utils/src/sg_rtpg.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
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