xref: /aosp_15_r20/external/sg3_utils/src/sg_opcodes.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1 /* A utility program originally written for the Linux OS SCSI subsystem.
2  *  Copyright (C) 2004-2022 D. Gilbert
3  *  This program is free software; you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation; either version 2, or (at your option)
6  *  any later version.
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  *
10  *  This program outputs information provided by a SCSI REPORT SUPPORTED
11  *  OPERATION CODES [0xa3/0xc] (RSOC) and REPORT SUPPORTED TASK MANAGEMENT
12  *  FUNCTIONS [0xa3/0xd] (RSTMF) commands.
13  */
14 
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdbool.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <errno.h>
23 #include <getopt.h>
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include "sg_lib.h"
29 #include "sg_cmds_basic.h"
30 #include "sg_cmds_extra.h"
31 #include "sg_unaligned.h"
32 #include "sg_pr2serr.h"
33 
34 #include "sg_pt.h"
35 
36 static const char * version_str = "0.86 20221005";    /* spc6r06 */
37 
38 #define MY_NAME "sg_opcodes"
39 
40 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
41 #define DEF_TIMEOUT_SECS 60
42 
43 #define SG_MAINTENANCE_IN 0xa3
44 #define RSOC_SA     0xc
45 #define RSTMF_SA    0xd
46 #define RSOC_CMD_LEN 12
47 #define RSTMF_CMD_LEN 12
48 #define MX_ALLOC_LEN 8192
49 
50 #define NAME_BUFF_SZ 128
51 
52 #define SEAGATE_READ_UDS_DATA_CMD 0xf7  /* may start reporting vendor cmds */
53 
54 static int peri_dtype = -1; /* ugly but not easy to pass to alpha compare */
55 static bool no_final_msg = false;
56 
57 static struct option long_options[] = {
58         {"alpha", no_argument, 0, 'a'},
59         {"compact", no_argument, 0, 'c'},
60         {"enumerate", no_argument, 0, 'e'},
61         {"help", no_argument, 0, 'h'},
62         {"hex", no_argument, 0, 'H'},
63         {"inhex", required_argument, 0, 'i'},
64         {"in", required_argument, 0, 'i'},
65         {"json", optional_argument, 0, 'j'},
66         {"mask", no_argument, 0, 'm'},
67         {"mlu", no_argument, 0, 'M'},           /* added in spc5r20 */
68         {"no-inquiry", no_argument, 0, 'n'},
69         {"no_inquiry", no_argument, 0, 'n'},
70         {"new", no_argument, 0, 'N'},
71         {"opcode", required_argument, 0, 'o'},
72         {"old", no_argument, 0, 'O'},
73         {"pdt", required_argument, 0, 'p'},
74         {"raw", no_argument, 0, 'r'},
75         {"rctd", no_argument, 0, 'R'},
76         {"repd", no_argument, 0, 'q'},
77         {"sa", required_argument, 0, 's'},
78         {"tmf", no_argument, 0, 't'},
79         {"unsorted", no_argument, 0, 'u'},
80         {"verbose", no_argument, 0, 'v'},
81         {"version", no_argument, 0, 'V'},
82         {0, 0, 0, 0},
83 };
84 
85 struct opts_t {
86     bool do_alpha;
87     bool do_compact;
88     bool do_enumerate;
89     bool no_inquiry;
90     bool do_mask;
91     bool do_mlu;
92     bool do_raw;
93     bool do_rctd;       /* Return command timeout descriptor */
94     bool do_repd;
95     bool do_unsorted;
96     bool do_taskman;
97     bool opt_new;
98     bool verbose_given;
99     bool version_given;
100     int do_help;
101     int do_hex;
102     int opcode;
103     int servact;
104     int verbose;
105     const char * device_name;
106     const char * inhex_fn;
107     sgj_state json_st;
108 };
109 
110 
111 static void
usage()112 usage()
113 {
114     pr2serr("Usage:  sg_opcodes [--alpha] [--compact] [--enumerate] "
115             "[--help] [--hex]\n"
116             "                   [--inhex=FN] [--json[=JO]] [--mask] [--mlu] "
117             "[--no-inquiry]\n"
118             "                   [--opcode=OP[,SA]] [--pdt=DT] [--raw] "
119             "[--rctd]\n"
120             "                   [--repd] [--sa=SA] [--tmf] [--unsorted] "
121             "[--verbose]\n"
122             "                   [--version] DEVICE\n"
123             "  where:\n"
124             "    --alpha|-a      output list of operation codes sorted "
125             "alphabetically\n"
126             "    --compact|-c    more compact output\n"
127             "    --enumerate|-e    use '--opcode=' and '--pdt=' to look up "
128             "name,\n"
129             "                      ignore DEVICE\n"
130             "    --help|-h       print usage message then exit\n"
131             "    --hex|-H        output response in hex, use -HHH for "
132             "hex\n"
133             "                    suitable for later use of --inhex= "
134             "option\n"
135             "    --inhex=FN|-i FN    contents of file FN treated as hex "
136             "and used\n"
137             "                        instead of DEVICE which is ignored\n"
138             "    --json[=JO]|-jJO    output in JSON instead of human "
139             "readable\n"
140             "                        test. Use --json=? for JSON help\n"
141             "    --mask|-m       show cdb usage data (a mask) when "
142             "all listed\n"
143             "    --mlu|-M        show MLU bit when all listed\n"
144             "    --no-inquiry|-n    don't output INQUIRY information\n"
145             "    --opcode=OP[,SA]|-o OP[,SA]    opcode (OP) and service "
146             "action (SA)\n"
147             "    --pdt=DT|-p DT    give peripheral device type for "
148             "'--no-inquiry'\n"
149             "                      '--enumerate'\n"
150             "    --raw|-r        output response in binary to stdout unless "
151             "--inhex=FN\n"
152             "                    is given then FN is parsed as binary "
153             "instead\n"
154             "    --rctd|-R       set RCTD (return command timeout "
155             "descriptor) bit\n"
156             "    --repd|-q       set Report Extended Parameter Data bit, "
157             "with --tmf\n"
158             "    --sa=SA|-s SA    service action in addition to opcode\n"
159             "    --tmf|-t        output list of supported task management "
160             "functions\n"
161             "    --unsorted|-u    output list of operation codes as is\n"
162             "                     (def: sort by opcode (then service "
163             "action))\n"
164             "    --verbose|-v    increase verbosity\n"
165             "    --old|-O        use old interface (use as first option)\n"
166             "    --version|-V    print version string then exit\n\n"
167             "Performs a SCSI REPORT SUPPORTED OPERATION CODES or a REPORT "
168             "SUPPORTED\nTASK MANAGEMENT FUNCTIONS command. All values are "
169             "in decimal by default,\nprefix with '0x' or add a trailing 'h' "
170             "for hex numbers.\n");
171 }
172 
173 static void
usage_old()174 usage_old()
175 {
176     pr2serr("Usage:  sg_opcodes [-a] [-c] [-e] [-H] [-j] [-m] [-M] [-n] "
177             "[-o=OP]\n"
178             "                   [-p=DT] [-q] [-r] [-R] [-s=SA] [-t] [-u] "
179             "[-v] [-V]\n"
180             "                   DEVICE\n"
181             "  where:\n"
182             "    -a    output list of operation codes sorted "
183             "alphabetically\n"
184             "    -c    more compact output\n"
185             "    -e    use '--opcode=' and '--pdt=' to look up name, "
186             "ignore DEVICE\n"
187             "    -H    print response in hex\n"
188             "    -j    print response in JSON\n"
189             "    -m    show cdb usage data (a mask) when all listed\n"
190             "    -M    show MLU bit when all listed\n"
191             "    -n    don't output INQUIRY information\n"
192             "    -o=OP    first byte of command to query (in hex)\n"
193             "    -p=DT    alternate source of pdt (normally obtained from "
194             "inquiry)\n"
195             "    -q    set REPD bit for tmf_s\n"
196             "    -r    output response in binary to stdout\n"
197             "    -R    set RCTD (return command timeout "
198             "descriptor) bit\n"
199             "    -s=SA    in addition to opcode (in hex)\n"
200             "    -t    output list of supported task management functions\n"
201             "    -u    output list of operation codes as is (unsorted)\n"
202             "    -v    verbose\n"
203             "    -V    output version string\n"
204             "    -N|--new   use new interface\n"
205             "    -?    output this usage message\n\n"
206             "Performs a SCSI REPORT SUPPORTED OPERATION CODES (or a REPORT "
207             "TASK MANAGEMENT\nFUNCTIONS) command\n");
208 }
209 
210 static const char * const rsoc_s = "Report supported operation codes";
211 
212 static int
do_rsoc(struct sg_pt_base * ptvp,bool rctd,int rep_opts,int rq_opcode,int rq_servact,void * resp,int mx_resp_len,int * act_resp_lenp,bool noisy,int verbose)213 do_rsoc(struct sg_pt_base * ptvp, bool rctd, int rep_opts, int rq_opcode,
214         int rq_servact, void * resp, int mx_resp_len, int * act_resp_lenp,
215         bool noisy, int verbose)
216 {
217     int ret, res, sense_cat;
218     uint8_t rsoc_cdb[RSOC_CMD_LEN] = {SG_MAINTENANCE_IN, RSOC_SA, 0,
219                                               0, 0, 0, 0, 0, 0, 0, 0, 0};
220     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
221 
222     if (rctd)
223         rsoc_cdb[2] |= 0x80;
224     if (rep_opts)
225         rsoc_cdb[2] |= (rep_opts & 0x7);
226     if (rq_opcode > 0)
227         rsoc_cdb[3] = (rq_opcode & 0xff);
228     if (rq_servact > 0)
229         sg_put_unaligned_be16((uint16_t)rq_servact, rsoc_cdb + 4);
230     if (act_resp_lenp)
231         *act_resp_lenp = 0;
232     sg_put_unaligned_be32((uint32_t)mx_resp_len, rsoc_cdb + 6);
233 
234     if (verbose) {
235         char b[128];
236 
237         pr2serr("    %s cdb: %s\n", rsoc_s,
238                 sg_get_command_str(rsoc_cdb, RSOC_CMD_LEN, false,
239                                    sizeof(b), b));
240     }
241     clear_scsi_pt_obj(ptvp);
242     set_scsi_pt_cdb(ptvp, rsoc_cdb, sizeof(rsoc_cdb));
243     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
244     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
245     res = do_scsi_pt(ptvp, -1, DEF_TIMEOUT_SECS, verbose);
246     ret = sg_cmds_process_resp(ptvp, rsoc_s, res, noisy, verbose, &sense_cat);
247     if (-1 == ret) {
248         if (get_scsi_pt_transport_err(ptvp))
249             ret = SG_LIB_TRANSPORT_ERROR;
250         else
251             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
252     } else if (-2 == ret) {
253         switch (sense_cat) {
254         case SG_LIB_CAT_RECOVERED:
255         case SG_LIB_CAT_NO_SENSE:
256             ret = 0;
257             break;
258         default:
259             ret = sense_cat;
260             break;
261         }
262     } else {
263         if (act_resp_lenp)
264             *act_resp_lenp = ret;
265         if ((verbose > 2) && (ret > 0)) {
266             pr2serr("%s response:\n", rsoc_s);
267             hex2stderr((const uint8_t *)resp, ret, 1);
268         }
269         ret = 0;
270     }
271     return ret;
272 }
273 
274 static const char * const rstmf_s = "Report supported task management "
275                                     "functions";
276 
277 static int
do_rstmf(struct sg_pt_base * ptvp,bool repd,void * resp,int mx_resp_len,int * act_resp_lenp,bool noisy,int verbose)278 do_rstmf(struct sg_pt_base * ptvp, bool repd, void * resp, int mx_resp_len,
279          int * act_resp_lenp, bool noisy, int verbose)
280 {
281     int ret, res, sense_cat;
282     uint8_t rstmf_cdb[RSTMF_CMD_LEN] = {SG_MAINTENANCE_IN, RSTMF_SA,
283                                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
284     uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
285 
286     if (repd)
287         rstmf_cdb[2] = 0x80;
288     if (act_resp_lenp)
289         *act_resp_lenp = 0;
290     sg_put_unaligned_be32((uint32_t)mx_resp_len, rstmf_cdb + 6);
291 
292     if (verbose) {
293         char b[128];
294 
295         pr2serr("    %s cdb: %s\n", rstmf_s,
296                 sg_get_command_str(rstmf_cdb, RSTMF_CMD_LEN, false,
297                                    sizeof(b), b));
298     }
299     clear_scsi_pt_obj(ptvp);
300     set_scsi_pt_cdb(ptvp, rstmf_cdb, sizeof(rstmf_cdb));
301     set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
302     set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len);
303     res = do_scsi_pt(ptvp, -1, DEF_TIMEOUT_SECS, verbose);
304     ret = sg_cmds_process_resp(ptvp, rstmf_s, res, noisy, verbose,
305                                &sense_cat);
306     if (-1 == ret) {
307         if (get_scsi_pt_transport_err(ptvp))
308             ret = SG_LIB_TRANSPORT_ERROR;
309         else
310             ret = sg_convert_errno(get_scsi_pt_os_err(ptvp));
311     } else if (-2 == ret) {
312         switch (sense_cat) {
313         case SG_LIB_CAT_RECOVERED:
314         case SG_LIB_CAT_NO_SENSE:
315             ret = 0;
316             break;
317         default:
318             ret = sense_cat;
319             break;
320         }
321     } else {
322         if (act_resp_lenp)
323             *act_resp_lenp = ret;
324         if ((verbose > 2) && (ret > 0)) {
325             pr2serr("%s response:\n", rstmf_s);
326             hex2stderr((const uint8_t *)resp, ret, 1);
327         }
328         ret = 0;
329     }
330     return ret;
331 }
332 
333 static int
new_parse_cmd_line(struct opts_t * op,int argc,char * argv[])334 new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
335 {
336     int c, n;
337     char * cp;
338     char b[32];
339 
340     while (1) {
341         int option_index = 0;
342 
343         c = getopt_long(argc, argv, "acehHi:j::mMnNo:Op:qrRs:tuvV",
344                         long_options, &option_index);
345         if (c == -1)
346             break;
347 
348         switch (c) {
349         case 'a':
350             op->do_alpha = true;
351             break;
352         case 'c':
353             op->do_compact = true;
354             break;
355         case 'e':
356             op->do_enumerate = true;
357             break;
358         case 'h':
359         case '?':
360             ++op->do_help;
361             break;
362         case 'H':
363             ++op->do_hex;
364             break;
365         case 'i':
366             op->inhex_fn = optarg;
367             break;
368         case 'j':
369             if (! sgj_init_state(&op->json_st, optarg)) {
370                 int bad_char = op->json_st.first_bad_char;
371                 char e[1500];
372 
373                 if (bad_char) {
374                     pr2serr("bad argument to --json= option, unrecognized "
375                             "character '%c'\n\n", bad_char);
376                 }
377                 sg_json_usage(0, e, sizeof(e));
378                 pr2serr("%s", e);
379                 return SG_LIB_SYNTAX_ERROR;
380             }
381             break;
382         case 'm':
383             op->do_mask = true;
384             break;
385         case 'M':
386             op->do_mlu = true;
387             break;
388         case 'n':
389             op->no_inquiry = true;
390             break;
391         case 'N':
392             break;      /* ignore */
393         case 'o':
394             if (strlen(optarg) >= (sizeof(b) - 1)) {
395                 pr2serr("argument to '--opcode' too long\n");
396                 return SG_LIB_SYNTAX_ERROR;
397             }
398             cp = strchr(optarg, ',');
399             if (cp) {
400                 memset(b, 0, sizeof(b));
401                 strncpy(b, optarg, cp - optarg);
402                 n = sg_get_num(b);
403                 if ((n < 0) || (n > 255)) {
404                     pr2serr("bad OP argument to '--opcode'\n");
405                     return SG_LIB_SYNTAX_ERROR;
406                 }
407                 op->opcode = n;
408                 n = sg_get_num(cp + 1);
409                 if ((n < 0) || (n > 0xffff)) {
410                     pr2serr("bad SA argument to '--opcode'\n");
411                     usage();
412                     return SG_LIB_SYNTAX_ERROR;
413                 }
414                 op->servact = n;
415             } else {
416                 n = sg_get_num(optarg);
417                 if ((n < 0) || (n > 255)) {
418                     pr2serr("bad argument to '--opcode'\n");
419                     usage();
420                     return SG_LIB_SYNTAX_ERROR;
421                 }
422                 op->opcode = n;
423             }
424             break;
425         case 'O':
426             op->opt_new = false;
427             return 0;
428         case 'p':
429             n = -2;
430             if (isdigit((uint8_t)optarg[0]))
431                 n = sg_get_num(optarg);
432             else if ((2 == strlen(optarg)) && (0 == strcmp("-1", optarg)))
433                 n = -1;
434             if ((n < -1) || (n > PDT_MAX)) {
435                 pr2serr("bad argument to '--pdt=DT', expect -1 to 31\n");
436                 return SG_LIB_SYNTAX_ERROR;
437             }
438             peri_dtype = n;
439             break;
440         case 'q':
441             op->do_repd = true;
442             break;
443         case 'r':
444             op->do_raw = true;
445             break;
446         case 'R':
447             op->do_rctd = true;
448             break;
449         case 's':
450             n = sg_get_num(optarg);
451             if ((n < 0) || (n > 0xffff)) {
452                 pr2serr("bad argument to '--sa'\n");
453                 usage();
454                 return SG_LIB_SYNTAX_ERROR;
455             }
456             op->servact = n;
457             break;
458         case 't':
459             op->do_taskman = true;
460             break;
461         case 'u':
462             op->do_unsorted = true;
463             break;
464         case 'v':
465             op->verbose_given = true;
466             ++op->verbose;
467             break;
468         case 'V':
469             op->version_given = true;
470             break;
471         default:
472             pr2serr("unrecognised option code %c [0x%x]\n", c, c);
473             if (op->do_help)
474                 break;
475             usage();
476             return SG_LIB_SYNTAX_ERROR;
477         }
478     }
479     if (optind < argc) {
480         if (NULL == op->device_name) {
481             op->device_name = argv[optind];
482             ++optind;
483         }
484         if (optind < argc) {
485             for (; optind < argc; ++optind)
486                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
487             usage();
488             return SG_LIB_SYNTAX_ERROR;
489         }
490     }
491     return 0;
492 }
493 
494 static int
old_parse_cmd_line(struct opts_t * op,int argc,char * argv[])495 old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
496 {
497     bool jmp_out;
498     int k, plen, n, num;
499     const char * cp;
500 
501     for (k = 1; k < argc; ++k) {
502         cp = argv[k];
503         plen = strlen(cp);
504         if (plen <= 0)
505             continue;
506         if ('-' == *cp) {
507             for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
508                 switch (*cp) {
509                 case 'a':
510                     op->do_alpha = true;
511                     break;
512                 case 'c':
513                     op->do_compact = true;
514                     break;
515                 case 'e':
516                     op->do_enumerate = true;
517                     break;
518                 case 'H':
519                     ++op->do_hex;
520                     break;
521                 case 'j':    /* don't accept argument with this old syntax */
522                     sgj_init_state(&op->json_st, NULL);
523                     break;
524                 case 'm':
525                     op->do_mask = true;
526                     break;
527                 case 'M':
528                     op->do_mlu = true;
529                     break;
530                 case 'n':
531                     op->no_inquiry = true;
532                     break;
533                 case 'N':
534                     op->opt_new = true;
535                     return 0;
536                 case 'O':
537                     break;
538                 case 'q':
539                     op->do_repd = true;
540                     break;
541                 case 'r':
542                     op->do_raw = true;
543                     break;
544                 case 'R':
545                     op->do_rctd = true;
546                     break;
547                 case 't':
548                     op->do_taskman = true;
549                     break;
550                 case 'u':
551                     op->do_unsorted = true;
552                     break;
553                 case 'v':
554                     op->verbose_given = true;
555                     ++op->verbose;
556                     break;
557                 case 'V':
558                     op->version_given = true;
559                     break;
560                 case 'h':
561                 case '?':
562                     ++op->do_help;
563                     break;
564                 default:
565                     jmp_out = true;
566                     break;
567                 }
568                 if (jmp_out)
569                     break;
570             }
571             if (plen <= 0)
572                 continue;
573             if (0 == strncmp("i=", cp, 2))
574                 op->inhex_fn = cp + 2;
575             else if (0 == strncmp("o=", cp, 2)) {
576                 num = sscanf(cp + 2, "%x", (unsigned int *)&n);
577                 if ((1 != num) || (n > 255)) {
578                     pr2serr("Bad number after 'o=' option\n");
579                     usage_old();
580                     return SG_LIB_SYNTAX_ERROR;
581                 }
582                 op->opcode = n;
583             } else if (0 == strncmp("p=", cp, 2)) {
584                 num = sscanf(cp + 2, "%d", &n);
585                 if ((1 != num) || (n > PDT_MAX) || (n < -1)) {
586                     pr2serr("Bad number after 'p=' option, expect -1 to "
587                             "31\n");
588                     return SG_LIB_SYNTAX_ERROR;
589                 }
590                 peri_dtype = n;
591             } else if (0 == strncmp("s=", cp, 2)) {
592                 num = sscanf(cp + 2, "%x", (unsigned int *)&n);
593                 if (1 != num) {
594                     pr2serr("Bad number after 's=' option\n");
595                     usage_old();
596                     return SG_LIB_SYNTAX_ERROR;
597                 }
598                 op->servact = n;
599             } else if (0 == strncmp("-old", cp, 4))
600                 ;
601             else if (jmp_out) {
602                 pr2serr("Unrecognized option: %s\n", cp);
603                 usage_old();
604                 return SG_LIB_SYNTAX_ERROR;
605             }
606         } else if (NULL == op->device_name)
607             op->device_name = cp;
608         else {
609             pr2serr("too many arguments, got: %s, not expecting: %s\n",
610                     op->device_name, cp);
611             usage_old();
612             return SG_LIB_SYNTAX_ERROR;
613         }
614     }
615     return 0;
616 }
617 
618 static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])619 parse_cmd_line(struct opts_t * op, int argc, char * argv[])
620 {
621     int res;
622     char * cp;
623 
624     cp = getenv("SG3_UTILS_OLD_OPTS");
625     if (cp) {
626         op->opt_new = false;
627         res = old_parse_cmd_line(op, argc, argv);
628         if ((0 == res) && op->opt_new)
629             res = new_parse_cmd_line(op, argc, argv);
630     } else {
631         op->opt_new = true;
632         res = new_parse_cmd_line(op, argc, argv);
633         if ((0 == res) && (! op->opt_new))
634             res = old_parse_cmd_line(op, argc, argv);
635     }
636     return res;
637 }
638 
639 static void
dStrRaw(const char * str,int len)640 dStrRaw(const char * str, int len)
641 {
642     int k;
643 
644     for (k = 0; k < len; ++k)
645         printf("%c", str[k]);
646 }
647 
648 /* returns -1 when left < right, 0 when left == right, else returns 1 */
649 static int
opcode_num_compare(const void * left,const void * right)650 opcode_num_compare(const void * left, const void * right)
651 {
652     int l_serv_act = 0;
653     int r_serv_act = 0;
654     int l_opc, r_opc;
655     const uint8_t * ll = *(uint8_t **)left;
656     const uint8_t * rr = *(uint8_t **)right;
657 
658     if (NULL == ll)
659         return -1;
660     if (NULL == rr)
661         return -1;
662     l_opc = ll[0];
663     if (ll[5] & 1)
664         l_serv_act = sg_get_unaligned_be16(ll + 2);
665     r_opc = rr[0];
666     if (rr[5] & 1)
667         r_serv_act = sg_get_unaligned_be16(rr + 2);
668     if (l_opc < r_opc)
669         return -1;
670     if (l_opc > r_opc)
671         return 1;
672     if (l_serv_act < r_serv_act)
673         return -1;
674     if (l_serv_act > r_serv_act)
675         return 1;
676     return 0;
677 }
678 
679 /* returns -1 when left < right, 0 when left == right, else returns 1 */
680 static int
opcode_alpha_compare(const void * left,const void * right)681 opcode_alpha_compare(const void * left, const void * right)
682 {
683     const uint8_t * ll = *(uint8_t **)left;
684     const uint8_t * rr = *(uint8_t **)right;
685     int l_serv_act = 0;
686     int r_serv_act = 0;
687     char l_name_buff[NAME_BUFF_SZ];
688     char r_name_buff[NAME_BUFF_SZ];
689     int l_opc, r_opc;
690 
691     if (NULL == ll)
692         return -1;
693     if (NULL == rr)
694         return -1;
695     l_opc = ll[0];
696     if (ll[5] & 1)
697         l_serv_act = sg_get_unaligned_be16(ll + 2);
698     l_name_buff[0] = '\0';
699     sg_get_opcode_sa_name(l_opc, l_serv_act, peri_dtype,
700                           NAME_BUFF_SZ, l_name_buff);
701     r_opc = rr[0];
702     if (rr[5] & 1)
703         r_serv_act = sg_get_unaligned_be16(rr + 2);
704     r_name_buff[0] = '\0';
705     sg_get_opcode_sa_name(r_opc, r_serv_act, peri_dtype,
706                           NAME_BUFF_SZ, r_name_buff);
707     return strncmp(l_name_buff, r_name_buff, NAME_BUFF_SZ);
708 }
709 
710 /* For decoding a RSOC command's "All_commands" parameter data */
711 static int
list_all_codes(uint8_t * rsoc_buff,int rsoc_len,struct opts_t * op,struct sg_pt_base * ptvp)712 list_all_codes(uint8_t * rsoc_buff, int rsoc_len, struct opts_t * op,
713                struct sg_pt_base * ptvp)
714 {
715     bool sa_v;
716     int k, j, m, n, cd_len, serv_act, len, act_len, opcode, res;
717     uint8_t byt5;
718     unsigned int timeout;
719     uint8_t * bp;
720     uint8_t ** sort_arr = NULL;
721     sgj_state * jsp = &op->json_st;
722     sgj_opaque_p jap = NULL;
723     sgj_opaque_p jop = NULL;
724     char name_buff[NAME_BUFF_SZ];
725     char sa_buff[8];
726     char b[192];
727     const int blen = sizeof(b);
728 
729     cd_len = sg_get_unaligned_be32(rsoc_buff + 0);
730     if (cd_len > (rsoc_len - 4)) {
731         sgj_pr_hr(jsp, "sg_opcodes: command data length=%d, allocation=%d; "
732                    "truncate\n", cd_len, rsoc_len - 4);
733         cd_len = ((rsoc_len - 4) / 8) * 8;
734     }
735     if (0 == cd_len) {
736         sgj_pr_hr(jsp, "sg_opcodes: no commands to display\n");
737         return 0;
738     }
739     if (op->do_rctd) {  /* Return command timeout descriptor */
740         if (op->do_compact) {
741             sgj_pr_hr(jsp, "\nOpcode,sa  Nominal  Recommended  Name\n");
742             sgj_pr_hr(jsp,   "  (hex)    timeout  timeout(sec)     \n");
743             sgj_pr_hr(jsp, "-----------------------------------------------"
744                        "---------\n");
745         } else {
746             sgj_pr_hr(jsp, "\nOpcode  Service    CDB   Nominal  Recommended  "
747                       "Name\n");
748             sgj_pr_hr(jsp,   "(hex)   action(h)  size  timeout  timeout(sec) "
749                       "    \n");
750             sgj_pr_hr(jsp, "-------------------------------------------------"
751                       "---------------\n");
752         }
753     } else {            /* RCTD clear in cdb */
754         if (op->do_compact) {
755             sgj_pr_hr(jsp, "\nOpcode,sa  Name\n");
756             sgj_pr_hr(jsp,   "  (hex)        \n");
757             sgj_pr_hr(jsp, "---------------------------------------\n");
758         } else if (op->do_mlu) {
759             sgj_pr_hr(jsp, "\nOpcode  Service    CDB    MLU    Name\n");
760             sgj_pr_hr(jsp,   "(hex)   action(h)  size              \n");
761             sgj_pr_hr(jsp, "-------------------------------------------"
762                       "----\n");
763         } else {
764             sgj_pr_hr(jsp, "\nOpcode  Service    CDB  RWCDLP,  Name\n");
765             sgj_pr_hr(jsp,   "(hex)   action(h)  size   CDLP       \n");
766             sgj_pr_hr(jsp, "-------------------------------------------"
767                       "----\n");
768         }
769     }
770     /* SPC-4 does _not_ require any ordering of opcodes in the response */
771     if (! op->do_unsorted) {
772         sort_arr = (uint8_t **)calloc(cd_len, sizeof(uint8_t *));
773         if (NULL == sort_arr) {
774             pr2serr("sg_opcodes: no memory to sort operation codes, "
775                     "try '-u'\n");
776             return sg_convert_errno(ENOMEM);
777         }
778         memset(sort_arr, 0, cd_len * sizeof(uint8_t *));
779         bp = rsoc_buff + 4;
780         for (k = 0, j = 0; k < cd_len; ++j, k += len, bp += len) {
781             sort_arr[j] = bp;
782             len = (bp[5] & 0x2) ? 20 : 8;
783         }
784         qsort(sort_arr, j, sizeof(uint8_t *),
785               (op->do_alpha ? opcode_alpha_compare : opcode_num_compare));
786     }
787 
788     jap = sgj_named_subarray_r(jsp, jsp->basep, "all_command_descriptor");
789     for (k = 0, j = 0; k < cd_len; ++j, k += len) {
790         jop = sgj_new_unattached_object_r(jsp);
791 
792         bp = op->do_unsorted ? (rsoc_buff + 4 + k) : sort_arr[j];
793         byt5 = bp[5];
794         len = (byt5 & 0x2) ? 20 : 8;
795         opcode = bp[0];
796         sa_v = !!(byt5 & 1);    /* service action valid */
797         serv_act = 0;
798         name_buff[0] = '\0';
799         if (sa_v) {
800             serv_act = sg_get_unaligned_be16(bp + 2);
801             sg_get_opcode_sa_name(opcode, serv_act, peri_dtype, NAME_BUFF_SZ,
802                                   name_buff);
803             if (op->do_compact)
804                 snprintf(sa_buff, sizeof(sa_buff), "%-4x", serv_act);
805             else
806                 snprintf(sa_buff, sizeof(sa_buff), "%4x", serv_act);
807         } else {
808             sg_get_opcode_name(opcode, peri_dtype, NAME_BUFF_SZ, name_buff);
809             memset(sa_buff, ' ', sizeof(sa_buff));
810         }
811         if (op->do_rctd) {
812             n = 0;
813             if (byt5 & 0x2) {          /* CTDP set */
814                 /* don't show CDLP because it makes line too long */
815                 if (op->do_compact)
816                     n += sg_scnpr(b + n, blen - n, " %.2x%c%.4s", opcode,
817                                   (sa_v ? ',' : ' '), sa_buff);
818                 else
819                     n += sg_scnpr(b + n, blen - n, " %.2x     %.4s       %3d",
820                                   opcode,
821                                   sa_buff, sg_get_unaligned_be16(bp + 6));
822                 timeout = sg_get_unaligned_be32(bp + 12);
823                 if (0 == timeout)
824                     n += sg_scnpr(b + n, blen - n, "         -");
825                 else
826                     n += sg_scnpr(b + n, blen - n, "  %8u", timeout);
827                 timeout = sg_get_unaligned_be32(bp + 16);
828                 if (0 == timeout)
829                     n += sg_scnpr(b + n, blen - n, "          -");
830                 else
831                     n += sg_scnpr(b + n, blen - n, "   %8u", timeout);
832                 sgj_pr_hr(jsp, "%s    %s\n", b, name_buff);
833             } else                      /* CTDP clear */
834                 if (op->do_compact)
835                     sgj_pr_hr(jsp, " %.2x%c%.4s                        %s\n",
836                               opcode, (sa_v ? ',' : ' '), sa_buff, name_buff);
837                 else
838                     sgj_pr_hr(jsp, " %.2x     %.4s       %3d                 "
839                               "        %s\n", opcode, sa_buff,
840                               sg_get_unaligned_be16(bp + 6), name_buff);
841         } else {            /* RCTD clear in cdb */
842             /* before version 0.69 treated RWCDLP (1 bit) and CDLP (2 bits),
843              * as a 3 bit field, now break them out separately */
844             int rwcdlp = (byt5 >> 2) & 0x3;
845             int cdlp = !!(0x40 & byt5);
846 
847             if (op->do_compact)
848                 sgj_pr_hr(jsp, " %.2x%c%.4s   %s\n", bp[0],
849                           (sa_v ? ',' : ' '), sa_buff, name_buff);
850             else if (op->do_mlu)
851                 sgj_pr_hr(jsp, " %.2x     %.4s       %3d   %3d     %s\n",
852                           bp[0], sa_buff, sg_get_unaligned_be16(bp + 6),
853                           ((byt5 >> 4) & 0x3), name_buff);
854             else
855                 sgj_pr_hr(jsp, " %.2x     %.4s       %3d    %d,%d    %s\n",
856                           bp[0], sa_buff, sg_get_unaligned_be16(bp + 6),
857                           rwcdlp, cdlp, name_buff);
858         }
859         if (jsp->pr_as_json) {
860             snprintf(b, blen, "0x%x", opcode);
861             sgj_js_nv_s(jsp, jop, "operation_code", b);
862             if (sa_v) {
863                 snprintf(b, blen, "0x%x", serv_act);
864                 sgj_js_nv_s(jsp, jop, "service_action", b);
865             }
866             if (name_buff[0])
867                 sgj_js_nv_s(jsp, jop, "name", name_buff);
868             sgj_js_nv_i(jsp, jop, "rwcdlp", (byt5 >> 6) & 0x1);
869             sgj_js_nv_i(jsp, jop, "mlu", (byt5 >> 4) & 0x3);
870             sgj_js_nv_i(jsp, jop, "cdlp", (byt5 >> 2) & 0x3);
871             sgj_js_nv_i(jsp, jop, "ctdp", (byt5 >> 1) & 0x1);
872             sgj_js_nv_i(jsp, jop, "servactv", byt5 & 0x1);
873             sgj_js_nv_i(jsp, jop, "cdb_length",
874                         sg_get_unaligned_be16(bp + 6));
875 
876             sgj_js_nv_o(jsp, jap, NULL /* implies an array add */, jop);
877         }
878 
879         if (op->do_mask && ptvp) {
880             int cdb_sz;
881             uint8_t d[64];
882 
883             n = 0;
884             memset(d, 0, sizeof(d));
885             res = do_rsoc(ptvp, false, (sa_v ? 2 : 1), opcode, serv_act,
886                           d, sizeof(d), &act_len, true, op->verbose);
887             if (0 == res) {
888                 int nn;
889 
890                 cdb_sz = sg_get_unaligned_be16(d + 2);
891                 cdb_sz = (cdb_sz < act_len) ? cdb_sz : act_len;
892                 if ((cdb_sz > 0) && (cdb_sz <= 80)) {
893                     if (op->do_compact)
894                         n += sg_scnpr(b + n, blen - n,
895                                       "             usage: ");
896                     else
897                         n += sg_scnpr(b + n, blen - n, "        cdb usage: ");
898                     nn = n;
899                     for (m = 0; (m < cdb_sz) && ((4 + m) < (int)sizeof(d));
900                          ++m)
901                         n += sg_scnpr(b + n, blen - n, "%.2x ", d[4 + m]);
902                     sgj_pr_hr(jsp, "%s\n", b);
903                     if (jsp->pr_as_json) {
904                         int l;
905                         char *b2p = b + nn;
906                         sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop,
907                                          "one_command_descriptor");
908 
909                         l = strlen(b2p);
910                         if ((l > 0) && (' ' == b2p[l - 1]))
911                             b2p[l - 1] = '\0';
912                         sgj_js_nv_i(jsp, jo2p, "cdb_size", cdb_sz);
913                         sgj_js_nv_s(jsp, jo2p, "cdb_usage_data", b2p);
914                     }
915                 }
916             } else
917                 goto err_out;
918         }
919     }
920     res = 0;
921 err_out:
922     if (sort_arr)
923         free(sort_arr);
924     return res;
925 }
926 
927 static void
decode_cmd_timeout_desc(uint8_t * dp,int max_b_len,char * b,struct opts_t * op)928 decode_cmd_timeout_desc(uint8_t * dp, int max_b_len, char * b,
929                         struct opts_t * op)
930 {
931     int len;
932     unsigned int timeout;
933     sgj_state * jsp = &op->json_st;
934 
935     if ((max_b_len < 2) || (NULL == dp))
936         return;
937     b[max_b_len - 1] = '\0';
938     --max_b_len;
939     len = sg_get_unaligned_be16(dp + 0);
940     if (10 != len) {
941         snprintf(b, max_b_len, "command timeout descriptor length %d "
942                  "(expect 10)", len);
943         return;
944     }
945     timeout = sg_get_unaligned_be32(dp + 4);
946     if (0 == timeout)
947         snprintf(b, max_b_len, "no nominal timeout, ");
948     else
949         snprintf(b, max_b_len, "nominal timeout: %u secs, ", timeout);
950     if (jsp->pr_as_json) {
951         sgj_js_nv_i(jsp, jsp->userp, "command_specific", dp[3]);
952         sgj_js_nv_i(jsp, jsp->userp, "nominal_command_processing_timeout",
953                     timeout);
954     }
955     len = strlen(b);
956     max_b_len -= len;
957     b += len;
958     timeout = sg_get_unaligned_be32(dp + 8);
959     if (0 == timeout)
960         snprintf(b, max_b_len, "no recommended timeout");
961     else
962         snprintf(b, max_b_len, "recommended timeout: %u secs", timeout);
963     if (jsp->pr_as_json)
964         sgj_js_nv_i(jsp, jsp->userp, "recommended_command_timeout", timeout);
965     return;
966 }
967 
968 /* For decoding a RSOC command's "One_command" parameter data which includes
969  * cdb usage data. */
970 static void
list_one(uint8_t * rsoc_buff,int cd_len,int rep_opts,struct opts_t * op)971 list_one(uint8_t * rsoc_buff, int cd_len, int rep_opts,
972          struct opts_t * op)
973 {
974     bool valid = false;
975     int k, mlu, cdlp, rwcdlp, support, ctdp;
976     int n = 0;
977     uint8_t * bp;
978     const char * cp;
979     const char * dlp;
980     const char * mlu_p;
981     sgj_state * jsp = &op->json_st;
982     sgj_opaque_p jop = NULL;
983     char name_buff[NAME_BUFF_SZ];
984     char d[64];
985     char b[192];
986     const int blen = sizeof(b);
987 
988 
989     jop = sgj_named_subobject_r(jsp, jsp->basep, "one_command_descriptor");
990     n += sg_scnpr(b + n, blen - n, "\n  Opcode=0x%.2x", op->opcode);
991     if (rep_opts > 1)
992         n += sg_scnpr(b + n, blen - n, "  Service_action=0x%.4x", op->servact);
993     sgj_pr_hr(jsp, "%s\n", b);
994     sg_get_opcode_sa_name(((op->opcode > 0) ? op->opcode : 0),
995                           ((op->servact > 0) ? op->servact : 0),
996                           peri_dtype, NAME_BUFF_SZ, name_buff);
997     sgj_pr_hr(jsp, "  Command_name: %s\n", name_buff);
998     ctdp = !!(0x80 & rsoc_buff[1]);
999     support = rsoc_buff[1] & 7;
1000     switch(support) {
1001     case 0:
1002         cp = "not currently available";
1003         break;
1004     case 1:
1005         cp = "NOT supported";
1006         break;
1007     case 3:
1008         cp = "supported [conforming to SCSI standard]";
1009         valid = true;
1010         break;
1011     case 5:
1012         cp = "supported [in a vendor specific manner]";
1013         valid = true;
1014         break;
1015     default:
1016         snprintf(name_buff, NAME_BUFF_SZ, "support reserved [0x%x]",
1017                  rsoc_buff[1] & 7);
1018         cp = name_buff;
1019         break;
1020     }
1021     cdlp = 0x3 & (rsoc_buff[1] >> 3);
1022     rwcdlp = rsoc_buff[0] & 1;
1023     switch (cdlp) {
1024     case 0:
1025         if (rwcdlp)
1026             dlp = "Reserved [RWCDLP=1, CDLP=0]";
1027         else
1028             dlp = "No command duration limit mode page";
1029         break;
1030     case 1:
1031         if (rwcdlp)
1032             dlp = "Command duration limit T2A mode page";
1033         else
1034             dlp = "Command duration limit A mode page";
1035         break;
1036     case 2:
1037         if (rwcdlp)
1038             dlp = "Command duration limit T2B mode page";
1039         else
1040             dlp = "Command duration limit B mode page";
1041         break;
1042     default:
1043         dlp = "reserved [CDLP=3]";
1044         break;
1045     }
1046     sgj_pr_hr(jsp, "  Command is %s\n", cp);
1047     sgj_pr_hr(jsp, "  %s\n", dlp);
1048     mlu = 0x3 & (rsoc_buff[1] >> 5);
1049     switch (mlu) {
1050     case 0:
1051         mlu_p = "not reported";
1052         break;
1053     case 1:
1054         mlu_p = "affects only this logical unit";
1055         break;
1056     case 2:
1057         mlu_p = "affects more than 1, but not all LUs in this target";
1058         break;
1059     case 3:
1060         mlu_p = "affects all LUs in this target";
1061         break;
1062     default:
1063         snprintf(d, sizeof(d), "reserved [MLU=%d]", mlu);
1064         mlu_p = d;
1065         break;
1066     }
1067     sgj_pr_hr(jsp, "  Multiple Logical Units (MLU): %s\n", mlu_p);
1068     if (valid) {
1069         n = 0;
1070         n += sg_scnpr(b + n, blen - n, "  Usage data: ");
1071         bp = rsoc_buff + 4;
1072         for (k = 0; k < cd_len; ++k)
1073             n += sg_scnpr(b + n, blen - n, "%.2x ", bp[k]);
1074         sgj_pr_hr(jsp, "%s\n", b);
1075     }
1076     if (jsp->pr_as_json) {
1077         int l;
1078 
1079         snprintf(b, blen, "0x%x", op->opcode);
1080         sgj_js_nv_s(jsp, jop, "operation_code", b);
1081         if (rep_opts > 1) {
1082             snprintf(b, blen, "0x%x", op->servact);
1083             sgj_js_nv_s(jsp, jop, "service_action", b);
1084         }
1085         sgj_js_nv_i(jsp, jop, "rwcdlp", rwcdlp);
1086         sgj_js_nv_i(jsp, jop, "ctdp", ctdp);
1087         sgj_js_nv_i(jsp, jop, "mlu", mlu);
1088         sgj_js_nv_i(jsp, jop, "cdlp", cdlp);
1089         sgj_js_nv_i(jsp, jop, "support", support);
1090         sgj_js_nv_s(jsp, jop, "support_str", cp);
1091         sgj_js_nv_i(jsp, jop, "cdb_size", cd_len);
1092         n = 0;
1093         for (k = 0; k < cd_len; ++k)
1094             n += sg_scnpr(b + n, blen - n, "%.2x ", rsoc_buff[k + 4]);
1095         l = strlen(b);
1096         if ((l > 0) && (' ' == b[l - 1]))
1097             b[l - 1] = '\0';
1098         sgj_js_nv_s(jsp, jop, "cdb_usage_data", b);
1099     }
1100     if (ctdp) {
1101         jsp->userp = sgj_named_subobject_r(jsp, jsp->basep,
1102                                            "command_timeouts_descriptor");
1103         bp = rsoc_buff + 4 + cd_len;
1104         decode_cmd_timeout_desc(bp, NAME_BUFF_SZ, name_buff, op);
1105         sgj_pr_hr(jsp, "  %s\n", name_buff);
1106     }
1107 }
1108 
1109 
1110 int
main(int argc,char * argv[])1111 main(int argc, char * argv[])
1112 {
1113     bool as_json;
1114     int cd_len, res, len, act_len, rq_len, in_len, vb;
1115     int rep_opts = 0;
1116     int sg_fd = -1;
1117     const char * cp;
1118     struct opts_t * op;
1119     const char * op_name;
1120     uint8_t * rsoc_buff = NULL;
1121     uint8_t * free_rsoc_buff = NULL;
1122     struct sg_pt_base * ptvp = NULL;
1123     sgj_state * jsp;
1124     sgj_opaque_p jop = NULL;
1125     char buff[48];
1126     char b[80];
1127     struct sg_simple_inquiry_resp inq_resp;
1128     struct opts_t opts;
1129 
1130     op = &opts;
1131     memset(op, 0, sizeof(opts));
1132     op->opcode = -1;
1133     op->servact = -1;
1134     res = parse_cmd_line(op, argc, argv);
1135     if (res)
1136         return SG_LIB_SYNTAX_ERROR;
1137     if (op->do_help) {
1138         if (op->opt_new)
1139             usage();
1140         else
1141             usage_old();
1142         return 0;
1143     }
1144     jsp = &op->json_st;
1145     as_json = jsp->pr_as_json;
1146     if (as_json) {
1147         jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp);
1148     }
1149 #ifdef DEBUG
1150     pr2serr("In DEBUG mode, ");
1151     if (op->verbose_given && op->version_given) {
1152         pr2serr("but override: '-vV' given, zero verbose and continue\n");
1153         op->verbose_given = false;
1154         op->version_given = false;
1155         op->verbose = 0;
1156     } else if (! op->verbose_given) {
1157         pr2serr("set '-vv'\n");
1158         op->verbose = 2;
1159     } else
1160         pr2serr("keep verbose=%d\n", op->verbose);
1161 #else
1162     if (op->verbose_given && op->version_given)
1163         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
1164 #endif
1165     if (op->version_given) {
1166         pr2serr("Version string: %s\n", version_str);
1167         goto fini;
1168     }
1169     vb = op->verbose;
1170     if (op->do_enumerate) {
1171         char name_buff[NAME_BUFF_SZ];
1172 
1173         if (op->do_taskman)
1174             printf("enumerate not supported with task management "
1175                    "functions\n");
1176         else {  /* SCSI command */
1177             if (op->opcode < 0)
1178                 op->opcode = 0;
1179             if (op->servact < 0)
1180                 op->servact = 0;
1181             if (peri_dtype < 0)
1182                 peri_dtype = 0;
1183             printf("SCSI command:");
1184             if (vb)
1185                 printf(" [opcode=0x%x, sa=0x%x, pdt=0x%x]\n", op->opcode,
1186                        op->servact, peri_dtype);
1187             else
1188                 printf("\n");
1189             sg_get_opcode_sa_name(op->opcode, op->servact, peri_dtype,
1190                                   NAME_BUFF_SZ, name_buff);
1191             printf("  %s\n", name_buff);
1192         }
1193         goto fini;
1194     } else if (op->inhex_fn) {
1195         if (op->device_name) {
1196             if (! as_json)
1197                 pr2serr("ignoring DEVICE, best to give DEVICE or "
1198                         "--inhex=FN, but not both\n");
1199             op->device_name = NULL;
1200         }
1201     } else if (NULL == op->device_name) {
1202         pr2serr("No DEVICE argument given\n\n");
1203         if (op->opt_new)
1204             usage();
1205         else
1206             usage_old();
1207         res = SG_LIB_SYNTAX_ERROR;
1208         goto err_out;
1209     }
1210     if ((-1 != op->servact) && (-1 == op->opcode)) {
1211         pr2serr("When '-s' is chosen, so must '-o' be chosen\n");
1212         if (op->opt_new)
1213             usage();
1214         else
1215             usage_old();
1216         res = SG_LIB_CONTRADICT;
1217         goto err_out;
1218     }
1219     if (op->do_unsorted && op->do_alpha)
1220         pr2serr("warning: unsorted ('-u') and alpha ('-a') options chosen, "
1221                 "ignoring alpha\n");
1222     if (op->do_taskman && ((-1 != op->opcode) || op->do_alpha ||
1223         op->do_unsorted)) {
1224         pr2serr("warning: task management functions ('-t') chosen so alpha "
1225                 "('-a'),\n          unsorted ('-u') and opcode ('-o') "
1226                 "options ignored\n");
1227     }
1228     op_name = op->do_taskman ? "Report supported task management functions" :
1229               "Report supported operation codes";
1230 
1231     rsoc_buff = (uint8_t *)sg_memalign(MX_ALLOC_LEN, 0, &free_rsoc_buff,
1232                                        false);
1233     if (NULL == rsoc_buff) {
1234         pr2serr("Unable to allocate memory\n");
1235         res = sg_convert_errno(ENOMEM);
1236         no_final_msg = true;
1237         goto err_out;
1238     }
1239 
1240     if (op->inhex_fn) {
1241         if ((res = sg_f2hex_arr(op->inhex_fn, op->do_raw, false, rsoc_buff,
1242                                 &in_len, MX_ALLOC_LEN))) {
1243             if (SG_LIB_LBA_OUT_OF_RANGE == res)
1244                 pr2serr("decode buffer [%d] not large enough??\n",
1245                         MX_ALLOC_LEN);
1246             goto err_out;
1247         }
1248         if (op->verbose > 2)
1249             pr2serr("Read %d [0x%x] bytes of user supplied data\n",
1250                     in_len, in_len);
1251         if (op->do_raw)
1252             op->do_raw = false;    /* can interfere on decode */
1253         if (in_len < 4) {
1254             pr2serr("--inhex=%s only decoded %d bytes (needs 4 at "
1255                     "least)\n", op->inhex_fn, in_len);
1256             res = SG_LIB_SYNTAX_ERROR;
1257             goto err_out;
1258         }
1259         res = 0;
1260         act_len = in_len;
1261         goto start_response;
1262     }
1263     if (op->opcode < 0) {
1264         /* Try to open read-only */
1265         if ((sg_fd = scsi_pt_open_device(op->device_name, true, vb)) < 0) {
1266             int err = -sg_fd;
1267 
1268             if (op->verbose)
1269                 pr2serr("sg_opcodes: error opening file (ro): %s: %s\n",
1270                         op->device_name, safe_strerror(err));
1271 #ifndef SG_LIB_WIN32
1272             if (ENOENT == err) {
1273                 /* file or directory in the file's path doesn't exist, no
1274                  * point in retrying with read-write flag */
1275                 res = sg_convert_errno(err);
1276                 goto err_out;
1277             }
1278 #endif
1279             goto open_rw;
1280         }
1281         ptvp = construct_scsi_pt_obj_with_fd(sg_fd, op->verbose);
1282         if (NULL == ptvp) {
1283             pr2serr("Out of memory (ro)\n");
1284             res = sg_convert_errno(ENOMEM);
1285             no_final_msg = true;
1286             goto err_out;
1287         }
1288         if (op->no_inquiry && (peri_dtype < 0))
1289             pr2serr("--no-inquiry ignored because --pdt= not given\n");
1290         if (op->no_inquiry && (peri_dtype >= 0))
1291             ;
1292         else if (0 == sg_simple_inquiry_pt(ptvp, &inq_resp, true, vb)) {
1293             peri_dtype = inq_resp.peripheral_type;
1294             if (! (as_json || op->do_raw || op->no_inquiry ||
1295                    (op->do_hex > 2))) {
1296                 printf("  %.8s  %.16s  %.4s\n", inq_resp.vendor,
1297                        inq_resp.product, inq_resp.revision);
1298                 cp = sg_get_pdt_str(peri_dtype, sizeof(buff), buff);
1299                 if (strlen(cp) > 0)
1300                     printf("  Peripheral device type: %s\n", cp);
1301                 else
1302                     printf("  Peripheral device type: 0x%x\n", peri_dtype);
1303             }
1304         } else {
1305             pr2serr("sg_opcodes: %s doesn't respond to a SCSI INQUIRY\n",
1306                     op->device_name);
1307             res = SG_LIB_CAT_OTHER;
1308             no_final_msg = true;
1309             goto err_out;
1310         }
1311     }
1312 
1313 open_rw:                /* if not already open */
1314     if (sg_fd < 0) {
1315         sg_fd = scsi_pt_open_device(op->device_name, false /* RW */, vb);
1316         if (sg_fd < 0) {
1317             pr2serr("sg_opcodes: error opening file (rw): %s: %s\n",
1318                     op->device_name, safe_strerror(-sg_fd));
1319             res = sg_convert_errno(-sg_fd);
1320             no_final_msg = true;
1321             goto err_out;
1322         }
1323         ptvp = construct_scsi_pt_obj_with_fd(sg_fd, op->verbose);
1324         if (NULL == ptvp) {
1325             pr2serr("Out of memory (rw)\n");
1326             res = sg_convert_errno(ENOMEM);
1327             no_final_msg = true;
1328             goto err_out;
1329         }
1330     }
1331     if (op->opcode >= 0)
1332         rep_opts = ((op->servact >= 0) ? 2 : 1);
1333     if (op->do_taskman) {
1334         rq_len = (op->do_repd ? 16 : 4);
1335         res = do_rstmf(ptvp, op->do_repd, rsoc_buff, rq_len, &act_len, true,
1336                        vb);
1337     } else {
1338         rq_len = MX_ALLOC_LEN;
1339         res = do_rsoc(ptvp, op->do_rctd, rep_opts, op->opcode, op->servact,
1340                       rsoc_buff, rq_len, &act_len, true, vb);
1341     }
1342     if (res) {
1343         sg_get_category_sense_str(res, sizeof(b), b, vb);
1344         pr2serr("%s: %s\n", op_name, b);
1345         no_final_msg = true;
1346         if ((0 == op->servact) && (op->opcode >= 0))
1347             pr2serr("    >> perhaps try again without a service action "
1348                     "[SA] of 0\n");
1349         goto err_out;
1350     }
1351     act_len = (rq_len < act_len) ? rq_len : act_len;
1352 
1353 start_response:
1354     if (act_len < 4) {
1355         pr2serr("Actual length of response [%d] is too small\n", act_len);
1356         res = SG_LIB_CAT_OTHER;
1357         no_final_msg = true;
1358         goto err_out;
1359     }
1360     if (op->do_taskman) {
1361         if (op->do_raw) {
1362             dStrRaw((const char *)rsoc_buff, act_len);
1363             goto fini;
1364         }
1365         if (op->do_hex) {
1366             if (op->do_hex > 2)
1367                 hex2stdout(rsoc_buff, act_len, -1);
1368             else  {
1369                 printf("\nTask Management Functions supported by device:\n");
1370                 if (2 == op->do_hex)
1371                     hex2stdout(rsoc_buff, act_len, 0);
1372                 else
1373                     hex2stdout(rsoc_buff, act_len, 1);
1374             }
1375             goto fini;
1376         }
1377         if (jsp->pr_as_json) {
1378             sgj_js_nv_b(jsp, jop, "ats", rsoc_buff[0] & 0x80);
1379             sgj_js_nv_b(jsp, jop, "atss", rsoc_buff[0] & 0x40);
1380             sgj_js_nv_b(jsp, jop, "cacas", rsoc_buff[0] & 0x20);
1381             sgj_js_nv_b(jsp, jop, "ctss", rsoc_buff[0] & 0x10);
1382             sgj_js_nv_b(jsp, jop, "lurs", rsoc_buff[0] & 0x8);
1383             sgj_js_nv_b(jsp, jop, "qts", rsoc_buff[0] & 0x4);
1384             sgj_js_nv_b(jsp, jop, "trs", rsoc_buff[0] & 0x2);
1385             sgj_js_nv_b(jsp, jop, "ws", rsoc_buff[0] & 0x1);
1386             sgj_js_nv_b(jsp, jop, "qaes", rsoc_buff[1] & 0x4);
1387             sgj_js_nv_b(jsp, jop, "qtss", rsoc_buff[1] & 0x2);
1388             sgj_js_nv_b(jsp, jop, "itnrs", rsoc_buff[1] & 0x1);
1389             if (! jsp->pr_out_hr)
1390                 goto fini;
1391         }
1392         sgj_pr_hr(jsp, "\nTask Management Functions supported by device:\n");
1393         if (rsoc_buff[0] & 0x80)
1394             sgj_pr_hr(jsp, "    Abort task\n");
1395         if (rsoc_buff[0] & 0x40)
1396             sgj_pr_hr(jsp, "    Abort task set\n");
1397         if (rsoc_buff[0] & 0x20)
1398             sgj_pr_hr(jsp, "    Clear ACA\n");
1399         if (rsoc_buff[0] & 0x10)
1400             sgj_pr_hr(jsp, "    Clear task set\n");
1401         if (rsoc_buff[0] & 0x8)
1402             sgj_pr_hr(jsp, "    Logical unit reset\n");
1403         if (rsoc_buff[0] & 0x4)
1404             sgj_pr_hr(jsp, "    Query task\n");
1405         if (rsoc_buff[0] & 0x2)
1406             sgj_pr_hr(jsp, "    Target reset (obsolete)\n");
1407         if (rsoc_buff[0] & 0x1)
1408             sgj_pr_hr(jsp, "    Wakeup (obsolete)\n");
1409         if (rsoc_buff[1] & 0x4)
1410             sgj_pr_hr(jsp, "    Query asynchronous event\n");
1411         if (rsoc_buff[1] & 0x2)
1412             sgj_pr_hr(jsp, "    Query task set\n");
1413         if (rsoc_buff[1] & 0x1)
1414             sgj_pr_hr(jsp, "    I_T nexus reset\n");
1415         if (op->do_repd) {
1416             if (rsoc_buff[3] < 0xc) {
1417                 pr2serr("when REPD given, byte 3 of response should be >= "
1418                         "12\n");
1419                 res = SG_LIB_CAT_OTHER;
1420                 no_final_msg = true;
1421                 goto err_out;
1422             } else
1423                 sgj_pr_hr(jsp, "  Extended parameter data:\n");
1424             sgj_pr_hr(jsp, "    TMFTMOV=%d\n", !!(rsoc_buff[4] & 0x1));
1425             sgj_pr_hr(jsp, "    ATTS=%d\n", !!(rsoc_buff[6] & 0x80));
1426             sgj_pr_hr(jsp, "    ATSTS=%d\n", !!(rsoc_buff[6] & 0x40));
1427             sgj_pr_hr(jsp, "    CACATS=%d\n", !!(rsoc_buff[6] & 0x20));
1428             sgj_pr_hr(jsp, "    CTSTS=%d\n", !!(rsoc_buff[6] & 0x10));
1429             sgj_pr_hr(jsp, "    LURTS=%d\n", !!(rsoc_buff[6] & 0x8));
1430             sgj_pr_hr(jsp, "    QTTS=%d\n", !!(rsoc_buff[6] & 0x4));
1431             sgj_pr_hr(jsp, "    QAETS=%d\n", !!(rsoc_buff[7] & 0x4));
1432             sgj_pr_hr(jsp, "    QTSTS=%d\n", !!(rsoc_buff[7] & 0x2));
1433             sgj_pr_hr(jsp, "    ITNRTS=%d\n", !!(rsoc_buff[7] & 0x1));
1434             sgj_pr_hr(jsp, "    tmf long timeout: %u (100 ms units)\n",
1435                       sg_get_unaligned_be32(rsoc_buff + 8));
1436             sgj_pr_hr(jsp, "    tmf short timeout: %u (100 ms units)\n",
1437                       sg_get_unaligned_be32(rsoc_buff + 12));
1438         }
1439     } else if (0 == rep_opts) {  /* list all supported operation codes */
1440         len = sg_get_unaligned_be32(rsoc_buff + 0) + 4;
1441         len = (len < act_len) ? len : act_len;
1442         if (op->do_raw) {
1443             dStrRaw((const char *)rsoc_buff, len);
1444             goto fini;
1445         }
1446         if (op->do_hex) {
1447             if (op->do_hex > 2)
1448                 hex2stdout(rsoc_buff, len, -1);
1449             else if (2 == op->do_hex)
1450                 hex2stdout(rsoc_buff, len, 0);
1451             else
1452                 hex2stdout(rsoc_buff, len, 1);
1453             goto fini;
1454         }
1455         list_all_codes(rsoc_buff, len, op, ptvp);
1456     } else {    /* asked about specific command */
1457         cd_len = sg_get_unaligned_be16(rsoc_buff + 2);
1458         len = cd_len + 4;
1459         len = (len < act_len) ? len : act_len;
1460         cd_len = (cd_len < act_len) ? cd_len : act_len;
1461         if (op->do_raw) {
1462             dStrRaw((const char *)rsoc_buff, len);
1463             goto fini;
1464         }
1465         if (op->do_hex) {
1466             if (op->do_hex > 2)
1467                 hex2stdout(rsoc_buff, len, -1);
1468             else if (2 == op->do_hex)
1469                 hex2stdout(rsoc_buff, len, 0);
1470             else
1471                 hex2stdout(rsoc_buff, len, 1);
1472             goto fini;
1473         }
1474         list_one(rsoc_buff, cd_len, rep_opts, op);
1475     }
1476 fini:
1477     res = 0;
1478 
1479 err_out:
1480     if (free_rsoc_buff)
1481         free(free_rsoc_buff);
1482     if (! op->inhex_fn) {
1483         if (ptvp)
1484             destruct_scsi_pt_obj(ptvp);
1485         if (sg_fd >= 0)
1486             scsi_pt_close_device(sg_fd);
1487     }
1488     if ((0 == op->verbose) && (! no_final_msg)) {
1489         if (! sg_if_can2stderr("sg_opcodes failed: ", res))
1490             pr2serr("Some error occurred, try again with '-v' "
1491                     "or '-vv' for more information\n");
1492     }
1493     res = (res >= 0) ? res : SG_LIB_CAT_OTHER;
1494     if (as_json) {
1495         if (0 == op->do_hex)
1496             sgj_js2file(jsp, NULL, res, stdout);
1497         sgj_finish(jsp);
1498     }
1499     return res;
1500 }
1501