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