1 /*
2 * A utility program originally written for the Linux OS SCSI subsystem.
3 *
4 * Copyright (C) 2000-2022 Ingo van Lil <[email protected]>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * SPDX-License-Identifier: GPL-2.0-or-later
12 *
13 * This program can be used to send raw SCSI commands (with an optional
14 * data phase) through a Generic SCSI interface.
15 */
16
17 #define _XOPEN_SOURCE 600 /* clear up posix_memalign() warning */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <stdbool.h>
23 #include <ctype.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <string.h>
27 #include <getopt.h>
28 #include <inttypes.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 #include "sg_lib.h"
37 #include "sg_pt.h"
38 #include "sg_pt_nvme.h"
39 #include "sg_pr2serr.h"
40 #include "sg_unaligned.h"
41
42 #define SG_RAW_VERSION "0.4.39 (2022-04-25)"
43
44 #define DEFAULT_TIMEOUT 20
45 #define MIN_SCSI_CDBSZ 6
46 #define MAX_SCSI_CDBSZ 260
47 #define MAX_SCSI_DXLEN (1024 * 1024)
48
49 #define NVME_ADDR_DATA_IN 0xfffffffffffffffe
50 #define NVME_ADDR_DATA_OUT 0xfffffffffffffffd
51 #define NVME_DATA_LEN_DATA_IN 0xfffffffe
52 #define NVME_DATA_LEN_DATA_OUT 0xfffffffd
53
54 static struct option long_options[] = {
55 { "binary", no_argument, NULL, 'b' },
56 { "cmdfile", required_argument, NULL, 'c' },
57 { "cmdset", required_argument, NULL, 'C' },
58 { "enumerate", no_argument, NULL, 'e' },
59 { "help", no_argument, NULL, 'h' },
60 { "infile", required_argument, NULL, 'i' },
61 { "skip", required_argument, NULL, 'k' },
62 { "nosense", no_argument, NULL, 'n' },
63 { "nvm", no_argument, NULL, 'N' },
64 { "outfile", required_argument, NULL, 'o' },
65 { "raw", no_argument, NULL, 'w' },
66 { "request", required_argument, NULL, 'r' },
67 { "readonly", no_argument, NULL, 'R' },
68 { "scan", required_argument, NULL, 'Q' },
69 { "send", required_argument, NULL, 's' },
70 { "timeout", required_argument, NULL, 't' },
71 { "verbose", no_argument, NULL, 'v' },
72 { "version", no_argument, NULL, 'V' },
73 { 0, 0, 0, 0 }
74 };
75
76 struct opts_t {
77 bool cmdfile_given;
78 bool do_datain;
79 bool datain_binary;
80 bool do_dataout;
81 bool do_enumerate;
82 bool no_sense;
83 bool do_nvm; /* the NVMe command set: NVM containing its READ+WRITE */
84 bool do_help;
85 bool verbose_given;
86 bool version_given;
87 int cdb_length;
88 int cmdset;
89 int datain_len;
90 int dataout_len;
91 int timeout;
92 int raw;
93 int readonly;
94 int scan_first;
95 int scan_last;
96 int verbose;
97 off_t dataout_offset;
98 uint8_t cdb[MAX_SCSI_CDBSZ]; /* might be NVMe command (64 byte) */
99 const char *cmd_file;
100 const char *datain_file;
101 const char *dataout_file;
102 char *device_name;
103 };
104
105
106 static void
pr_version()107 pr_version()
108 {
109 pr2serr("sg_raw " SG_RAW_VERSION "\n"
110 "Copyright (C) 2007-2021 Ingo van Lil <[email protected]>\n"
111 "This is free software. You may redistribute copies of it "
112 "under the terms of\n"
113 "the GNU General Public License "
114 "<https://www.gnu.org/licenses/gpl.html>.\n"
115 "There is NO WARRANTY, to the extent permitted by law.\n");
116 }
117
118 static void
usage()119 usage()
120 {
121 pr2serr("Usage: sg_raw [OPTION]* DEVICE [CDB0 CDB1 ...]\n"
122 "\n"
123 "Options:\n"
124 " --binary|-b Dump data in binary form, even when "
125 "writing to\n"
126 " stdout\n"
127 " --cmdfile=CF|-c CF CF is file containing command in hex "
128 "bytes\n"
129 " --cmdset=CS|-C CS CS is 0 (def) heuristic chooses "
130 "command set;\n"
131 " 1: force SCSI; 2: force NVMe\n"
132 " --enumerate|-e Decodes cdb name then exits; requires "
133 "DEVICE but\n"
134 " ignores it\n"
135 " --help|-h Show this message and exit\n"
136 " --infile=IFILE|-i IFILE Read binary data to send (i.e. "
137 "data-out)\n"
138 " from IFILE (default: stdin)\n"
139 " --nosense|-n Don't display sense information\n"
140 " --nvm|-N command is for NVM command set (e.g. "
141 "Read);\n"
142 " default, if NVMe fd, Admin command "
143 "set\n"
144 " --outfile=OFILE|-o OFILE Write binary data from device "
145 "(i.e. data-in)\n"
146 " to OFILE (def: hexdump to "
147 "stdout)\n"
148 " --raw|-w interpret CF (command file) as "
149 "binary (def:\n"
150 " interpret as ASCII hex)\n"
151 " --readonly|-R Open DEVICE read-only (default: "
152 "read-write)\n"
153 " --request=RLEN|-r RLEN Request up to RLEN bytes of data "
154 "(data-in)\n"
155 " --scan=FO,LO|-Q FO,LO scan command set from FO (first "
156 "opcode)\n"
157 " to LO (last opcode) inclusive. Uses "
158 "given\n"
159 " command bytes, varying the opcode\n"
160 " --send=SLEN|-s SLEN Send SLEN bytes of data (data-out)\n"
161 " --skip=KLEN|-k KLEN Skip the first KLEN bytes when "
162 "reading\n"
163 " data to send (default: 0)\n"
164 " --timeout=SECS|-t SECS Timeout in seconds (default: 20)\n"
165 " --verbose|-v Increase verbosity\n"
166 " --version|-V Show version information and exit\n"
167 "\n"
168 "Between 6 and 260 command bytes (two hex digits each) can be "
169 "specified\nand will be sent to DEVICE. Lengths RLEN, SLEN and "
170 "KLEN are decimal by\ndefault. Bidirectional commands "
171 "accepted.\n\nSimple example: Perform INQUIRY on /dev/sg0:\n"
172 " sg_raw -r 1k /dev/sg0 12 00 00 00 60 00\n");
173 }
174
175 static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])176 parse_cmd_line(struct opts_t * op, int argc, char *argv[])
177 {
178 while (1) {
179 int c, n;
180 const char * cp;
181
182 c = getopt_long(argc, argv, "bc:C:ehi:k:nNo:Q:r:Rs:t:vVw",
183 long_options, NULL);
184 if (c == -1)
185 break;
186
187 switch (c) {
188 case 'b':
189 op->datain_binary = true;
190 break;
191 case 'c':
192 op->cmd_file = optarg;
193 op->cmdfile_given = true;
194 break;
195 case 'C':
196 n = sg_get_num(optarg);
197 if ((n < 0) || (n > 2)) {
198 pr2serr("Invalid argument to --cmdset= expect 0, 1 or 2\n");
199 return SG_LIB_SYNTAX_ERROR;
200 }
201 op->cmdset = n;
202 break;
203 case 'e':
204 op->do_enumerate = true;
205 break;
206 case 'h':
207 case '?':
208 op->do_help = true;
209 return 0;
210 case 'i':
211 if (op->dataout_file) {
212 pr2serr("Too many '--infile=' options\n");
213 return SG_LIB_CONTRADICT;
214 }
215 op->dataout_file = optarg;
216 break;
217 case 'k':
218 n = sg_get_num(optarg);
219 if (n < 0) {
220 pr2serr("Invalid argument to '--skip'\n");
221 return SG_LIB_SYNTAX_ERROR;
222 }
223 op->dataout_offset = n;
224 break;
225 case 'n':
226 op->no_sense = true;
227 break;
228 case 'N':
229 op->do_nvm = true;
230 break;
231 case 'o':
232 if (op->datain_file) {
233 pr2serr("Too many '--outfile=' options\n");
234 return SG_LIB_CONTRADICT;
235 }
236 op->datain_file = optarg;
237 break;
238 case 'Q': /* --scan=FO,LO */
239 cp = strchr(optarg, ',');
240 if (NULL == cp) {
241 pr2serr("--scan= expects two numbers, comma separated\n");
242 return SG_LIB_SYNTAX_ERROR;
243 }
244 n = sg_get_num(optarg);
245 if ((n < 0) || (n > 255)) {
246 pr2serr("Invalid first number to --scan= expect 0 to 255\n");
247 return SG_LIB_SYNTAX_ERROR;
248 }
249 op->scan_first = n;
250 n = sg_get_num(cp + 1);
251 if ((n < 0) || (n > 255)) {
252 pr2serr("Invalid second number to --scan= expect 0 to 255\n");
253 return SG_LIB_SYNTAX_ERROR;
254 }
255 op->scan_last = n;
256 if (op->scan_first >= n)
257 pr2serr("Warning: scan range degenerate, ignore\n");
258 break;
259 case 'r':
260 op->do_datain = true;
261 n = sg_get_num(optarg);
262 if (n < 0 || n > MAX_SCSI_DXLEN) {
263 pr2serr("Invalid argument to '--request'\n");
264 return SG_LIB_SYNTAX_ERROR;
265 }
266 op->datain_len = n;
267 break;
268 case 'R':
269 ++op->readonly;
270 break;
271 case 's':
272 op->do_dataout = true;
273 n = sg_get_num(optarg);
274 if (n < 0 || n > MAX_SCSI_DXLEN) {
275 pr2serr("Invalid argument to '--send'\n");
276 return SG_LIB_SYNTAX_ERROR;
277 }
278 op->dataout_len = n;
279 break;
280 case 't':
281 n = sg_get_num(optarg);
282 if (n < 0) {
283 pr2serr("Invalid argument to '--timeout'\n");
284 return SG_LIB_SYNTAX_ERROR;
285 }
286 op->timeout = n;
287 break;
288 case 'v':
289 op->verbose_given = true;
290 ++op->verbose;
291 break;
292 case 'V':
293 op->version_given = true;
294 break;
295 case 'w': /* -r and -R already in use, this is --raw */
296 ++op->raw;
297 break;
298 default:
299 return SG_LIB_SYNTAX_ERROR;
300 }
301 }
302
303 if (op->version_given
304 #ifdef DEBUG
305 && ! op->verbose_given
306 #endif
307 )
308 return 0;
309
310 if (optind >= argc) {
311 pr2serr("No device specified\n");
312 return SG_LIB_SYNTAX_ERROR;
313 }
314 op->device_name = argv[optind];
315 ++optind;
316
317 while (optind < argc) {
318 char *opt = argv[optind++];
319 char *endptr;
320 int cmd = strtol(opt, &endptr, 16);
321
322 if (*opt == '\0' || *endptr != '\0' || cmd < 0x00 || cmd > 0xff) {
323 pr2serr("Invalid command byte '%s'\n", opt);
324 return SG_LIB_SYNTAX_ERROR;
325 }
326
327 if (op->cdb_length >= MAX_SCSI_CDBSZ) {
328 pr2serr("CDB too long (max. %d bytes)\n", MAX_SCSI_CDBSZ);
329 return SG_LIB_SYNTAX_ERROR;
330 }
331 op->cdb[op->cdb_length] = cmd;
332 ++op->cdb_length;
333 }
334
335 if (op->cmdfile_given) {
336 int err;
337
338 err = sg_f2hex_arr(op->cmd_file, (op->raw > 0) /* as_binary */,
339 false /* no_space */, op->cdb, &op->cdb_length,
340 MAX_SCSI_CDBSZ);
341 if (err) {
342 pr2serr("Unable to parse: %s as %s\n", op->cmd_file,
343 (op->raw > 0) ? "binary" : "hex");
344 return SG_LIB_SYNTAX_ERROR;
345 }
346 if (op->verbose > 2) {
347 pr2serr("Read %d from %s . They are in hex:\n", op->cdb_length,
348 op->cmd_file);
349 hex2stderr(op->cdb, op->cdb_length, -1);
350 }
351 }
352 if (op->cdb_length < MIN_SCSI_CDBSZ) {
353 pr2serr("CDB too short (min. %d bytes)\n", MIN_SCSI_CDBSZ);
354 return SG_LIB_SYNTAX_ERROR;
355 }
356 if (op->do_enumerate || (op->verbose > 1)) {
357 bool is_scsi_cdb = sg_is_scsi_cdb(op->cdb, op->cdb_length);
358 int sa;
359 char b[80];
360
361 if ((1 == op->cmdset) && !is_scsi_cdb) {
362 is_scsi_cdb = true;
363 if (op->verbose > 3)
364 printf(">>> overriding cmdset guess to SCSI\n");
365 }
366 if ((2 == op->cmdset) && is_scsi_cdb) {
367 is_scsi_cdb = false;
368 if (op->verbose > 3)
369 printf(">>> overriding cmdset guess to NVMe\n");
370 }
371 if (is_scsi_cdb) {
372 if (op->cdb_length > 16) {
373 sa = sg_get_unaligned_be16(op->cdb + 8);
374 if ((0x7f != op->cdb[0]) && (0x7e != op->cdb[0]))
375 printf(">>> Unlikely to be SCSI CDB since all over 16 "
376 "bytes long should\n>>> start with 0x7f or "
377 "0x7e\n");
378 } else
379 sa = op->cdb[1] & 0x1f;
380 sg_get_opcode_sa_name(op->cdb[0], sa, 0, sizeof(b), b);
381 printf("Attempt to decode cdb name: %s\n", b);
382 } else
383 printf(">>> Seems to be NVMe %s command\n",
384 sg_get_nvme_opcode_name(op->cdb[0], ! op->do_nvm,
385 sizeof(b), b));
386 }
387 return 0;
388 }
389
390 static int
skip(int fd,off_t offset)391 skip(int fd, off_t offset)
392 {
393 int err;
394 off_t remain;
395 char buffer[512];
396
397 if (lseek(fd, offset, SEEK_SET) >= 0)
398 return 0;
399
400 // lseek failed; fall back to reading and discarding data
401 remain = offset;
402 while (remain > 0) {
403 ssize_t amount, done;
404 amount = (remain < (off_t)sizeof(buffer)) ? remain
405 : (off_t)sizeof(buffer);
406 done = read(fd, buffer, amount);
407 if (done < 0) {
408 err = errno;
409 perror("Error reading input data to skip");
410 return sg_convert_errno(err);
411 } else if (done == 0) {
412 pr2serr("EOF on input file/stream\n");
413 return SG_LIB_FILE_ERROR;
414 } else
415 remain -= done;
416 }
417 return 0;
418 }
419
420 static uint8_t *
fetch_dataout(struct opts_t * op,uint8_t ** free_buf,int * errp)421 fetch_dataout(struct opts_t * op, uint8_t ** free_buf, int * errp)
422 {
423 bool ok = false;
424 int fd, len, tot_len, boff, err;
425 uint8_t *buf = NULL;
426
427 *free_buf = NULL;
428 if (errp)
429 *errp = 0;
430 if (op->dataout_file) {
431 fd = open(op->dataout_file, O_RDONLY);
432 if (fd < 0) {
433 err = errno;
434 if (errp)
435 *errp = sg_convert_errno(err);
436 perror(op->dataout_file);
437 goto bail;
438 }
439 } else
440 fd = STDIN_FILENO;
441 if (sg_set_binary_mode(fd) < 0) {
442 err = errno;
443 if (errp)
444 *errp = err;
445 perror("sg_set_binary_mode");
446 goto bail;
447 }
448
449 if (op->dataout_offset > 0) {
450 err = skip(fd, op->dataout_offset);
451 if (err != 0) {
452 if (errp)
453 *errp = err;
454 goto bail;
455 }
456 }
457
458 tot_len = op->dataout_len;
459 buf = sg_memalign(tot_len, 0 /* page_size */, free_buf, false);
460 if (buf == NULL) {
461 pr2serr("sg_memalign: failed to get %d bytes of memory\n", tot_len);
462 if (errp)
463 *errp = sg_convert_errno(ENOMEM);
464 goto bail;
465 }
466
467 for (boff = 0; boff < tot_len; boff += len) {
468 len = read(fd, buf + boff , tot_len - boff);
469 if (len < 0) {
470 err = errno;
471 if (errp)
472 *errp = sg_convert_errno(err);
473 perror("Failed to read input data");
474 goto bail;
475 } else if (0 == len) {
476 if (errp)
477 *errp = SG_LIB_FILE_ERROR;
478 pr2serr("EOF on input file/stream at buffer offset %d\n", boff);
479 goto bail;
480 }
481 }
482 ok = true;
483
484 bail:
485 if (fd >= 0 && fd != STDIN_FILENO)
486 close(fd);
487 if (! ok) {
488 if (*free_buf) {
489 free(*free_buf);
490 *free_buf = NULL;
491 }
492 return NULL;
493 }
494 return buf;
495 }
496
497 static int
write_dataout(const char * filename,uint8_t * buf,int len)498 write_dataout(const char *filename, uint8_t *buf, int len)
499 {
500 int ret = SG_LIB_FILE_ERROR;
501 int fd;
502
503 if ((filename == NULL) ||
504 ((1 == strlen(filename)) && ('-' == filename[0])))
505 fd = STDOUT_FILENO;
506 else {
507 fd = creat(filename, 0666);
508 if (fd < 0) {
509 ret = sg_convert_errno(errno);
510 perror(filename);
511 goto bail;
512 }
513 }
514 if (sg_set_binary_mode(fd) < 0) {
515 perror("sg_set_binary_mode");
516 goto bail;
517 }
518
519 if (write(fd, buf, len) != len) {
520 ret = sg_convert_errno(errno);
521 perror(filename ? filename : "stdout");
522 goto bail;
523 }
524
525 ret = 0;
526
527 bail:
528 if (fd >= 0 && fd != STDOUT_FILENO)
529 close(fd);
530 return ret;
531 }
532
533
534 int
main(int argc,char * argv[])535 main(int argc, char *argv[])
536 {
537 bool is_scsi_cdb = true;
538 bool do_scan = false;
539 int ret = 0;
540 int err = 0;
541 int res_cat, status, s_len, k, ret2;
542 int sg_fd = -1;
543 uint16_t sct_sc;
544 uint32_t result;
545 struct sg_pt_base *ptvp = NULL;
546 uint8_t sense_buffer[32] SG_C_CPP_ZERO_INIT;
547 uint8_t * dinp = NULL;
548 uint8_t * doutp = NULL;
549 uint8_t * free_buf_out = NULL;
550 uint8_t * wrkBuf = NULL;
551 struct opts_t opts;
552 struct opts_t * op;
553 char b[128];
554 const int b_len = sizeof(b);
555
556 op = &opts;
557 memset(op, 0, sizeof(opts));
558 op->timeout = DEFAULT_TIMEOUT;
559 ret = parse_cmd_line(op, argc, argv);
560 #ifdef DEBUG
561 pr2serr("In DEBUG mode, ");
562 if (op->verbose_given && op->version_given) {
563 pr2serr("but override: '-vV' given, zero verbose and continue\n");
564 op->verbose_given = false;
565 op->version_given = false;
566 op->verbose = 0;
567 } else if (! op->verbose_given) {
568 pr2serr("set '-vv'\n");
569 op->verbose = 2;
570 } else
571 pr2serr("keep verbose=%d\n", op->verbose);
572 #else
573 if (op->verbose_given && op->version_given)
574 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
575 #endif
576 if (op->version_given) {
577 pr_version();
578 goto done;
579 }
580
581 if (ret != 0) {
582 pr2serr("\n"); /* blank line before outputting usage */
583 usage();
584 goto done;
585 } else if (op->do_help) {
586 usage();
587 goto done;
588 } else if (op->do_enumerate)
589 goto done;
590
591 sg_fd = scsi_pt_open_device(op->device_name, op->readonly,
592 op->verbose);
593 if (sg_fd < 0) {
594 pr2serr("%s: %s\n", op->device_name, safe_strerror(-sg_fd));
595 ret = sg_convert_errno(-sg_fd);
596 goto done;
597 }
598
599 ptvp = construct_scsi_pt_obj_with_fd(sg_fd, op->verbose);
600 if (ptvp == NULL) {
601 pr2serr("construct_scsi_pt_obj_with_fd() failed\n");
602 ret = SG_LIB_CAT_OTHER;
603 goto done;
604 }
605
606 if (op->scan_first < op->scan_last)
607 do_scan = true;
608
609 and_again:
610 if (do_scan) {
611 op->cdb[0] = op->scan_first;
612 printf("Command bytes in hex:");
613 for (k = 0; k < op->cdb_length; ++k)
614 printf(" %02x", op->cdb[k]);
615 printf("\n");
616 }
617
618 is_scsi_cdb = sg_is_scsi_cdb(op->cdb, op->cdb_length);
619 if ((1 == op->cmdset) && !is_scsi_cdb)
620 is_scsi_cdb = true;
621 else if ((2 == op->cmdset) && is_scsi_cdb)
622 is_scsi_cdb = false;
623
624 if (op->do_dataout) {
625 uint32_t dout_len;
626
627 doutp = fetch_dataout(op, &free_buf_out, &err);
628 if (doutp == NULL) {
629 ret = err;
630 goto done;
631 }
632 dout_len = op->dataout_len;
633 if (op->verbose > 2)
634 pr2serr("dxfer_buffer_out=%p, length=%d\n",
635 (void *)doutp, dout_len);
636 set_scsi_pt_data_out(ptvp, doutp, dout_len);
637 if (op->cmdfile_given) {
638 if (NVME_ADDR_DATA_OUT ==
639 sg_get_unaligned_le64(op->cdb + SG_NVME_PT_ADDR))
640 sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)doutp,
641 op->cdb + SG_NVME_PT_ADDR);
642 if (NVME_DATA_LEN_DATA_OUT ==
643 sg_get_unaligned_le32(op->cdb + SG_NVME_PT_DATA_LEN))
644 sg_put_unaligned_le32(dout_len,
645 op->cdb + SG_NVME_PT_DATA_LEN);
646 }
647 }
648 if (op->do_datain) {
649 uint32_t din_len = op->datain_len;
650
651 dinp = sg_memalign(din_len, 0 /* page_size */, &wrkBuf, false);
652 if (dinp == NULL) {
653 pr2serr("sg_memalign: failed to get %d bytes of memory\n",
654 din_len);
655 ret = sg_convert_errno(ENOMEM);
656 goto done;
657 }
658 if (op->verbose > 2)
659 pr2serr("dxfer_buffer_in=%p, length=%d\n", (void *)dinp, din_len);
660 set_scsi_pt_data_in(ptvp, dinp, din_len);
661 if (op->cmdfile_given) {
662 if (NVME_ADDR_DATA_IN ==
663 sg_get_unaligned_le64(op->cdb + SG_NVME_PT_ADDR))
664 sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)dinp,
665 op->cdb + SG_NVME_PT_ADDR);
666 if (NVME_DATA_LEN_DATA_IN ==
667 sg_get_unaligned_le32(op->cdb + SG_NVME_PT_DATA_LEN))
668 sg_put_unaligned_le32(din_len,
669 op->cdb + SG_NVME_PT_DATA_LEN);
670 }
671 }
672 if (op->verbose) {
673 char d[128];
674
675 pr2serr(" %s to send: ", is_scsi_cdb ? "cdb" : "cmd");
676 if (is_scsi_cdb) {
677 pr2serr("%s\n", sg_get_command_str(op->cdb, op->cdb_length,
678 op->verbose > 1,
679 sizeof(d), d));
680 } else { /* If not SCSI cdb then treat as NVMe command */
681 pr2serr("\n");
682 hex2stderr(op->cdb, op->cdb_length, -1);
683 if (op->verbose > 1)
684 pr2serr(" Command name: %s\n",
685 sg_get_nvme_opcode_name(op->cdb[0], ! op->do_nvm,
686 b_len, b));
687 }
688 }
689 set_scsi_pt_cdb(ptvp, op->cdb, op->cdb_length);
690 if (op->verbose > 2)
691 pr2serr("sense_buffer=%p, length=%d\n", (void *)sense_buffer,
692 (int)sizeof(sense_buffer));
693 set_scsi_pt_sense(ptvp, sense_buffer, sizeof(sense_buffer));
694
695 if (op->do_nvm)
696 ret = do_nvm_pt(ptvp, 0, op->timeout, op->verbose);
697 else
698 ret = do_scsi_pt(ptvp, -1, op->timeout, op->verbose);
699 if (ret > 0) {
700 switch (ret) {
701 case SCSI_PT_DO_BAD_PARAMS:
702 pr2serr("do_scsi_pt: bad pass through setup\n");
703 ret = SG_LIB_CAT_OTHER;
704 break;
705 case SCSI_PT_DO_TIMEOUT:
706 pr2serr("do_scsi_pt: timeout\n");
707 ret = SG_LIB_CAT_TIMEOUT;
708 break;
709 case SCSI_PT_DO_NVME_STATUS:
710 sct_sc = (uint16_t)get_scsi_pt_status_response(ptvp);
711 pr2serr("NVMe Status: %s [0x%x]\n",
712 sg_get_nvme_cmd_status_str(sct_sc, b_len, b), sct_sc);
713 if (op->verbose) {
714 result = get_pt_result(ptvp);
715 pr2serr("NVMe Result=0x%x\n", result);
716 s_len = get_scsi_pt_sense_len(ptvp);
717 if ((op->verbose > 1) && (s_len > 0)) {
718 pr2serr("NVMe completion queue 4 DWords (as byte "
719 "string):\n");
720 hex2stderr(sense_buffer, s_len, -1);
721 }
722 }
723 break;
724 case SCSI_PT_DO_NOT_SUPPORTED:
725 pr2serr("do_scsi_pt: not supported\n");
726 ret = SG_LIB_CAT_TIMEOUT;
727 break;
728 default:
729 pr2serr("do_scsi_pt: unknown error: %d\n", ret);
730 ret = SG_LIB_CAT_OTHER;
731 break;
732 }
733 goto done;
734 } else if (ret < 0) {
735 k = -ret;
736 pr2serr("do_scsi_pt: %s\n", safe_strerror(k));
737 err = get_scsi_pt_os_err(ptvp);
738 if ((err != 0) && (err != k))
739 pr2serr(" ... or perhaps: %s\n", safe_strerror(err));
740 ret = sg_convert_errno(err);
741 goto done;
742 }
743
744 s_len = get_scsi_pt_sense_len(ptvp);
745 if (is_scsi_cdb) {
746 res_cat = get_scsi_pt_result_category(ptvp);
747 switch (res_cat) {
748 case SCSI_PT_RESULT_GOOD:
749 ret = 0;
750 break;
751 case SCSI_PT_RESULT_SENSE:
752 ret = sg_err_category_sense(sense_buffer, s_len);
753 break;
754 case SCSI_PT_RESULT_TRANSPORT_ERR:
755 get_scsi_pt_transport_err_str(ptvp, b_len, b);
756 pr2serr(">>> transport error: %s\n", b);
757 ret = SG_LIB_CAT_OTHER;
758 break;
759 case SCSI_PT_RESULT_OS_ERR:
760 get_scsi_pt_os_err_str(ptvp, b_len, b);
761 pr2serr(">>> os error: %s\n", b);
762 ret = SG_LIB_CAT_OTHER;
763 break;
764 default:
765 pr2serr(">>> unknown pass through result category (%d)\n",
766 res_cat);
767 ret = SG_LIB_CAT_OTHER;
768 break;
769 }
770
771 status = get_scsi_pt_status_response(ptvp);
772 pr2serr("SCSI Status: ");
773 sg_print_scsi_status(status);
774 pr2serr("\n\n");
775 if ((SAM_STAT_CHECK_CONDITION == status) && (! op->no_sense)) {
776 if (0 == s_len)
777 pr2serr(">>> Strange: status is CHECK CONDITION but no Sense "
778 "Information\n");
779 else {
780 pr2serr("Sense Information:\n");
781 sg_print_sense(NULL, sense_buffer, s_len, (op->verbose > 0));
782 pr2serr("\n");
783 }
784 }
785 if (SAM_STAT_RESERVATION_CONFLICT == status)
786 ret = SG_LIB_CAT_RES_CONFLICT;
787 } else { /* NVMe command */
788 result = get_pt_result(ptvp);
789 pr2serr("NVMe Result=0x%x\n", result);
790 if (op->verbose && (s_len > 0)) {
791 pr2serr("NVMe completion queue 4 DWords (as byte string):\n");
792 hex2stderr(sense_buffer, s_len, -1);
793 }
794 }
795
796 if (op->do_datain) {
797 int data_len = op->datain_len - get_scsi_pt_resid(ptvp);
798
799 if (ret && !(SG_LIB_CAT_RECOVERED == ret ||
800 SG_LIB_CAT_NO_SENSE == ret))
801 pr2serr("Error %d occurred, no data received\n", ret);
802 else if (data_len == 0) {
803 pr2serr("No data received\n");
804 } else {
805 if (op->datain_file == NULL && !op->datain_binary) {
806 pr2serr("Received %d bytes of data:\n", data_len);
807 hex2stderr(dinp, data_len, 0);
808 } else {
809 const char * cp = "stdout";
810
811 if (op->datain_file &&
812 ! ((1 == strlen(op->datain_file)) &&
813 ('-' == op->datain_file[0])))
814 cp = op->datain_file;
815 pr2serr("Writing %d bytes of data to %s\n", data_len, cp);
816 ret2 = write_dataout(op->datain_file, dinp,
817 data_len);
818 if (0 != ret2) {
819 if (0 == ret)
820 ret = ret2;
821 goto done;
822 }
823 }
824 }
825 }
826
827 done:
828 if (do_scan) {
829 ++op->scan_first;
830 if (op->scan_first <= op->scan_last) {
831 clear_scsi_pt_obj(ptvp);
832 goto and_again;
833 }
834 }
835
836 if (op->verbose && is_scsi_cdb) {
837 sg_get_category_sense_str(ret, b_len, b, op->verbose - 1);
838 pr2serr("%s\n", b);
839 }
840 if (wrkBuf)
841 free(wrkBuf);
842 if (free_buf_out)
843 free(free_buf_out);
844 if (ptvp)
845 destruct_scsi_pt_obj(ptvp);
846 if (sg_fd >= 0)
847 scsi_pt_close_device(sg_fd);
848 return ret >= 0 ? ret : SG_LIB_CAT_OTHER;
849 }
850