1 /*
2 * A utility program originally written for the Linux OS SCSI subsystem
3 * Copyright (C) 2003-2022 D. Gilbert
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2, or (at your option)
7 * any later version.
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10
11 This program issues the SCSI SEND DIAGNOSTIC command and in one case
12 the SCSI RECEIVE DIAGNOSTIC command to list supported diagnostic pages.
13 */
14
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <getopt.h>
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "sg_lib.h"
28 #include "sg_cmds_basic.h"
29 #include "sg_cmds_extra.h"
30 #if SG_LIB_WIN32
31 #include "sg_pt.h" /* needed for scsi_pt_win32_direct() */
32 #endif
33 #include "sg_unaligned.h"
34 #include "sg_pr2serr.h"
35
36
37 static const char * version_str = "0.65 20220128";
38
39 #define ME "sg_senddiag: "
40
41 #define DEF_ALLOC_LEN (1024 * 4)
42
43 static struct option long_options[] = {
44 {"doff", no_argument, 0, 'd'},
45 {"extdur", no_argument, 0, 'e'},
46 {"help", no_argument, 0, 'h'},
47 {"hex", no_argument, 0, 'H'},
48 {"list", no_argument, 0, 'l'},
49 {"maxlen", required_argument, 0, 'm'},
50 {"new", no_argument, 0, 'N'},
51 {"old", no_argument, 0, 'O'},
52 {"page", required_argument, 0, 'P'},
53 {"pf", no_argument, 0, 'p'},
54 {"raw", required_argument, 0, 'r'},
55 {"selftest", required_argument, 0, 's'},
56 {"test", no_argument, 0, 't'},
57 {"timeout", required_argument, 0, 'T'},
58 {"uoff", no_argument, 0, 'u'},
59 {"verbose", no_argument, 0, 'v'},
60 {"version", no_argument, 0, 'V'},
61 {0, 0, 0, 0},
62 };
63
64 struct opts_t {
65 bool do_deftest;
66 bool do_doff;
67 bool do_extdur;
68 bool do_list;
69 bool do_pf;
70 bool do_raw;
71 bool do_uoff;
72 bool opt_new;
73 bool verbose_given;
74 bool version_given;
75 int do_help;
76 int do_hex;
77 int maxlen;
78 int page_code;
79 int do_selftest;
80 int timeout;
81 int verbose;
82 const char * device_name;
83 const char * raw_arg;
84 };
85
86
87 static void
usage()88 usage()
89 {
90 printf("Usage: sg_senddiag [--doff] [--extdur] [--help] [--hex] "
91 "[--list]\n"
92 " [--maxlen=LEN] [--page=PG] [--pf] "
93 "[--raw=H,H...]\n"
94 " [--selftest=ST] [--test] [--timeout=SECS] "
95 "[--uoff]\n"
96 " [--verbose] [--version] [DEVICE]\n"
97 " where:\n"
98 " --doff|-d device online (def: 0, only with '--test')\n"
99 " --extdur|-e duration of an extended self-test (from mode "
100 "page 0xa)\n"
101 " --help|-h print usage message then exit\n"
102 " --hex|-H output RDR in hex; twice: plus ASCII; thrice: "
103 "suitable\n"
104 " for '--raw=-' with later invocation\n"
105 " --list|-l list supported page codes (with or without "
106 "DEVICE)\n"
107 " --maxlen=LEN|-m LEN parameter list length or maximum "
108 "allocation\n"
109 " length (default: 4096 bytes)\n"
110 " --page=PG|-P PG do RECEIVE DIAGNOSTIC RESULTS only, set "
111 "PCV\n"
112 " --pf|-p set PF bit (def: 0)\n"
113 " --raw=H,H...|-r H,H... sequence of hex bytes to form "
114 "diag page to send\n"
115 " --raw=-|-r - read stdin for sequence of bytes to send\n"
116 " --selftest=ST|-s ST self-test code, default: 0 "
117 "(inactive)\n"
118 " 1->background short, 2->background "
119 "extended\n"
120 " 4->abort test\n"
121 " 5->foreground short, 6->foreground "
122 "extended\n"
123 " --test|-t default self-test\n"
124 " --timeout=SECS|-T SECS timeout for foreground self tests\n"
125 " unit: second (def: 7200 seconds)\n"
126 " --uoff|-u unit offline (def: 0, only with '--test')\n"
127 " --verbose|-v increase verbosity\n"
128 " --old|-O use old interface (use as first option)\n"
129 " --version|-V output version string then exit\n\n"
130 "Performs a SCSI SEND DIAGNOSTIC (and/or a RECEIVE DIAGNOSTIC "
131 "RESULTS) command\n"
132 );
133 }
134
135 static void
usage_old()136 usage_old()
137 {
138 printf("Usage: sg_senddiag [-doff] [-e] [-h] [-H] [-l] [-pf]"
139 " [-raw=H,H...]\n"
140 " [-s=SF] [-t] [-T=SECS] [-uoff] [-v] [-V] "
141 "[DEVICE]\n"
142 " where:\n"
143 " -doff device online (def: 0, only with '-t')\n"
144 " -e duration of an extended self-test (from mode page "
145 "0xa)\n"
146 " -h output in hex\n"
147 " -H output in hex (same as '-h')\n"
148 " -l list supported page codes\n"
149 " -pf set PF bit (def: 0)\n"
150 " -raw=H,H... sequence of bytes to form diag page to "
151 "send\n"
152 " -raw=- read stdin for sequence of bytes to send\n"
153 " -s=SF self-test code (def: 0)\n"
154 " 1->background short, 2->background extended,"
155 " 4->abort test\n"
156 " 5->foreground short, 6->foreground extended\n"
157 " -t default self-test\n"
158 " -T SECS timeout for foreground self tests\n"
159 " -uoff unit offline (def: 0, only with '-t')\n"
160 " -v increase verbosity (print issued SCSI cmds)\n"
161 " -V output version string\n"
162 " -N|--new use new interface\n"
163 " -? output this usage message\n\n"
164 "Performs a SCSI SEND DIAGNOSTIC (and/or a RECEIVE DIAGNOSTIC "
165 "RESULTS) command\n"
166 );
167 }
168
169 static int
new_parse_cmd_line(struct opts_t * op,int argc,char * argv[])170 new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
171 {
172 int c, n;
173
174 while (1) {
175 int option_index = 0;
176
177 c = getopt_long(argc, argv, "dehHlm:NOpP:r:s:tT:uvV", long_options,
178 &option_index);
179 if (c == -1)
180 break;
181
182 switch (c) {
183 case 'd':
184 op->do_doff = true;
185 break;
186 case 'e':
187 op->do_extdur = true;
188 break;
189 case 'h':
190 case '?':
191 ++op->do_help;
192 break;
193 case 'H':
194 ++op->do_hex;
195 break;
196 case 'l':
197 op->do_list = true;
198 break;
199 case 'm':
200 n = sg_get_num(optarg);
201 if ((n < 0) || (n > 0xffff)) {
202 pr2serr("bad argument to '--maxlen=' or greater than 65535 "
203 "[0xffff]\n");
204 return SG_LIB_SYNTAX_ERROR;
205 }
206 op->maxlen = n;
207 break;
208 case 'N':
209 break; /* ignore */
210 case 'O':
211 op->opt_new = false;
212 return 0;
213 case 'p':
214 op->do_pf = true;
215 break;
216 case 'P':
217 n = sg_get_num(optarg);
218 if ((n < 0) || (n > 0xff)) {
219 pr2serr("bad argument to '--page=' or greater than 255 "
220 "[0xff]\n");
221 return SG_LIB_SYNTAX_ERROR;
222 }
223 op->page_code = n;
224 break;
225 case 'r':
226 op->raw_arg = optarg;
227 op->do_raw = true;
228 break;
229 case 's':
230 n = sg_get_num(optarg);
231 if ((n < 0) || (n > 7)) {
232 pr2serr("bad argument to '--selftest='\n");
233 usage();
234 return SG_LIB_SYNTAX_ERROR;
235 }
236 op->do_selftest = n;
237 break;
238 case 't':
239 op->do_deftest = true;
240 break;
241 case 'T':
242 n = sg_get_num(optarg);
243 if (n < 0) {
244 pr2serr("bad argument to '--timeout=SECS'\n");
245 return SG_LIB_SYNTAX_ERROR;
246 }
247 op->timeout = n;
248 break;
249 case 'u':
250 op->do_uoff = true;
251 break;
252 case 'v':
253 op->verbose_given = true;
254 ++op->verbose;
255 break;
256 case 'V':
257 op->version_given = true;
258 break;
259 default:
260 pr2serr("unrecognised option code %c [0x%x]\n", c, c);
261 if (op->do_help)
262 break;
263 usage();
264 return SG_LIB_SYNTAX_ERROR;
265 }
266 }
267 if (optind < argc) {
268 if (NULL == op->device_name) {
269 op->device_name = argv[optind];
270 ++optind;
271 }
272 if (optind < argc) {
273 for (; optind < argc; ++optind)
274 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
275 usage();
276 return SG_LIB_SYNTAX_ERROR;
277 }
278 }
279 return 0;
280 }
281
282 static int
old_parse_cmd_line(struct opts_t * op,int argc,char * argv[])283 old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
284 {
285 bool jmp_out;
286 int k, plen, num, n;
287 unsigned int u;
288 const char * cp;
289
290 for (k = 1; k < argc; ++k) {
291 cp = argv[k];
292 plen = strlen(cp);
293 if (plen <= 0)
294 continue;
295 if ('-' == *cp) {
296 for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
297 switch (*cp) {
298 case 'd':
299 if (0 == strncmp("doff", cp, 4)) {
300 op->do_doff = true;
301 cp += 3;
302 plen -= 3;
303 } else
304 jmp_out = true;
305 break;
306 case 'e':
307 op->do_extdur = true;
308 break;
309 case 'h':
310 case 'H':
311 ++op->do_hex;
312 break;
313 case 'l':
314 op->do_list = true;
315 break;
316 case 'N':
317 op->opt_new = true;
318 return 0;
319 case 'O':
320 break;
321 case 'p':
322 if (0 == strncmp("pf", cp, 2)) {
323 op->do_pf = true;
324 ++cp;
325 --plen;
326 } else
327 jmp_out = true;
328 break;
329 case 't':
330 op->do_deftest = true;
331 break;
332 case 'u':
333 if (0 == strncmp("uoff", cp, 4)) {
334 op->do_uoff = true;
335 cp += 3;
336 plen -= 3;
337 } else
338 jmp_out = true;
339 break;
340 case 'v':
341 op->verbose_given = true;
342 ++op->verbose;
343 break;
344 case 'V':
345 op->version_given = true;
346 break;
347 case '?':
348 ++op->do_help;
349 break;
350 default:
351 jmp_out = true;
352 break;
353 }
354 if (jmp_out)
355 break;
356 }
357 if (plen <= 0)
358 continue;
359 if (0 == strncmp("raw=", cp, 4)) {
360 op->raw_arg = cp + 4;
361 op->do_raw = true;
362 } else if (0 == strncmp("s=", cp, 2)) {
363 num = sscanf(cp + 2, "%x", &u);
364 if ((1 != num) || (u > 7)) {
365 printf("Bad page code after '-s=' option\n");
366 usage_old();
367 return SG_LIB_SYNTAX_ERROR;
368 }
369 op->do_selftest = u;
370 } else if (0 == strncmp("T=", cp, 2)) {
371 num = sscanf(cp + 2, "%d", &n);
372 if ((1 != num) || (n < 0)) {
373 printf("Bad page code after '-T=SECS' option\n");
374 usage_old();
375 return SG_LIB_SYNTAX_ERROR;
376 }
377 op->timeout = n;
378 } else if (0 == strncmp("-old", cp, 5))
379 ;
380 else if (jmp_out) {
381 pr2serr("Unrecognized option: %s\n", cp);
382 usage_old();
383 return SG_LIB_SYNTAX_ERROR;
384 }
385 } else if (0 == op->device_name)
386 op->device_name = cp;
387 else {
388 pr2serr("too many arguments, got: %s, not expecting: %s\n",
389 op->device_name, cp);
390 usage_old();
391 return SG_LIB_SYNTAX_ERROR;
392 }
393 }
394 return 0;
395 }
396
397 static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])398 parse_cmd_line(struct opts_t * op, int argc, char * argv[])
399 {
400 int res;
401 char * cp;
402
403 cp = getenv("SG3_UTILS_OLD_OPTS");
404 if (cp) {
405 op->opt_new = false;
406 res = old_parse_cmd_line(op, argc, argv);
407 if ((0 == res) && op->opt_new)
408 res = new_parse_cmd_line(op, argc, argv);
409 } else {
410 op->opt_new = true;
411 res = new_parse_cmd_line(op, argc, argv);
412 if ((0 == res) && (! op->opt_new))
413 res = old_parse_cmd_line(op, argc, argv);
414 }
415 return res;
416 }
417
418 /* Return of 0 -> success, otherwise see sg_ll_send_diag() */
419 static int
do_senddiag(int sg_fd,int sf_code,bool pf_bit,bool sf_bit,bool devofl_bit,bool unitofl_bit,void * outgoing_pg,int outgoing_len,int tmout,bool noisy,int verbose)420 do_senddiag(int sg_fd, int sf_code, bool pf_bit, bool sf_bit,
421 bool devofl_bit, bool unitofl_bit, void * outgoing_pg,
422 int outgoing_len, int tmout, bool noisy, int verbose)
423 {
424 int long_duration = 0;
425
426 if ((0 == sf_bit) && ((5 == sf_code) || (6 == sf_code))) {
427 /* foreground self-tests */
428 if (tmout <= 0)
429 long_duration = 1;
430 else
431 long_duration = tmout;
432 }
433 return sg_ll_send_diag(sg_fd, sf_code, pf_bit, sf_bit, devofl_bit,
434 unitofl_bit, long_duration, outgoing_pg,
435 outgoing_len, noisy, verbose);
436 }
437
438 /* Get expected extended self-test time from mode page 0xa (for '-e') */
439 static int
do_modes_0a(int sg_fd,void * resp,int mx_resp_len,bool mode6,bool noisy,int verbose)440 do_modes_0a(int sg_fd, void * resp, int mx_resp_len, bool mode6, bool noisy,
441 int verbose)
442 {
443 int res;
444 int resid = 0;
445
446 if (mode6)
447 res = sg_ll_mode_sense6(sg_fd, true /* dbd */, false /* pc */,
448 0xa /* page */, false, resp, mx_resp_len,
449 noisy, verbose);
450 else
451 res = sg_ll_mode_sense10_v2(sg_fd, false /* llbaa */, true /* dbd */,
452 false, 0xa, false, resp, mx_resp_len,
453 0, &resid, noisy, verbose);
454 if (res) {
455 char b[80];
456
457 sg_get_category_sense_str(res, sizeof(b), b, verbose);
458 pr2serr("Mode sense (%s): %s\n", (mode6 ? "6" : "10"), b);
459 } else {
460 mx_resp_len -= resid;
461 if (mx_resp_len < 4) {
462 pr2serr("%s: response length (%d) too small (resid=%d)\n",
463 __func__, mx_resp_len, resid);
464 res = SG_LIB_WILD_RESID;
465 }
466 }
467 return res;
468 }
469
470 /* Read hex numbers from command line (comma separated list) or from */
471 /* stdin (one per line, comma separated list or space separated list). */
472 /* Returns 0 if ok, or 1 if error. */
473 static int
build_diag_page(const char * inp,uint8_t * mp_arr,int * mp_arr_len,int max_arr_len)474 build_diag_page(const char * inp, uint8_t * mp_arr, int * mp_arr_len,
475 int max_arr_len)
476 {
477 int in_len, k, j, m;
478 unsigned int h;
479 const char * lcp;
480 char * cp;
481 char * c2p;
482
483 if ((NULL == inp) || (NULL == mp_arr) ||
484 (NULL == mp_arr_len))
485 return 1;
486 lcp = inp;
487 in_len = strlen(inp);
488 if (0 == in_len)
489 *mp_arr_len = 0;
490 if ('-' == inp[0]) { /* read from stdin */
491 bool split_line;
492 int off = 0;
493 char line[512];
494 char carry_over[4];
495
496 carry_over[0] = 0;
497 for (j = 0; j < 512; ++j) {
498 if (NULL == fgets(line, sizeof(line), stdin))
499 break;
500 in_len = strlen(line);
501 if (in_len > 0) {
502 if ('\n' == line[in_len - 1]) {
503 --in_len;
504 line[in_len] = '\0';
505 split_line = false;
506 } else
507 split_line = true;
508 }
509 if (in_len < 1) {
510 carry_over[0] = 0;
511 continue;
512 }
513 if (carry_over[0]) {
514 if (isxdigit((uint8_t)line[0])) {
515 carry_over[1] = line[0];
516 carry_over[2] = '\0';
517 if (1 == sscanf(carry_over, "%x", &h))
518 mp_arr[off - 1] = h; /* back up and overwrite */
519 else {
520 pr2serr("build_diag_page: carry_over error ['%s'] "
521 "around line %d\n", carry_over, j + 1);
522 return 1;
523 }
524 lcp = line + 1;
525 --in_len;
526 } else
527 lcp = line;
528 carry_over[0] = 0;
529 } else
530 lcp = line;
531 m = strspn(lcp, " \t");
532 if (m == in_len)
533 continue;
534 lcp += m;
535 in_len -= m;
536 if ('#' == *lcp)
537 continue;
538 k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t");
539 if ((k < in_len) && ('#' != lcp[k])) {
540 pr2serr("build_diag_page: syntax error at line %d, pos %d\n",
541 j + 1, m + k + 1);
542 return 1;
543 }
544 for (k = 0; k < 1024; ++k) {
545 if (1 == sscanf(lcp, "%x", &h)) {
546 if (h > 0xff) {
547 pr2serr("build_diag_page: hex number larger than "
548 "0xff in line %d, pos %d\n", j + 1,
549 (int)(lcp - line + 1));
550 return 1;
551 }
552 if (split_line && (1 == strlen(lcp))) {
553 /* single trailing hex digit might be a split pair */
554 carry_over[0] = *lcp;
555 }
556 if ((off + k) >= max_arr_len) {
557 pr2serr("build_diag_page: array length exceeded\n");
558 return 1;
559 }
560 mp_arr[off + k] = h;
561 lcp = strpbrk(lcp, " ,\t");
562 if (NULL == lcp)
563 break;
564 lcp += strspn(lcp, " ,\t");
565 if ('\0' == *lcp)
566 break;
567 } else {
568 if ('#' == *lcp) {
569 --k;
570 break;
571 }
572 pr2serr("build_diag_page: error in line %d, at pos %d\n",
573 j + 1, (int)(lcp - line + 1));
574 return 1;
575 }
576 }
577 off += (k + 1);
578 }
579 *mp_arr_len = off;
580 } else { /* hex string on command line */
581 k = strspn(inp, "0123456789aAbBcCdDeEfF, ");
582 if (in_len != k) {
583 pr2serr("build_diag_page: error at pos %d\n", k + 1);
584 return 1;
585 }
586 for (k = 0; k < max_arr_len; ++k) {
587 if (1 == sscanf(lcp, "%x", &h)) {
588 if (h > 0xff) {
589 pr2serr("build_diag_page: hex number larger than 0xff at "
590 "pos %d\n", (int)(lcp - inp + 1));
591 return 1;
592 }
593 mp_arr[k] = h;
594 cp = (char *)strchr(lcp, ',');
595 c2p = (char *)strchr(lcp, ' ');
596 if (NULL == cp)
597 cp = c2p;
598 if (NULL == cp)
599 break;
600 if (c2p && (c2p < cp))
601 cp = c2p;
602 lcp = cp + 1;
603 } else {
604 pr2serr("build_diag_page: error at pos %d\n",
605 (int)(lcp - inp + 1));
606 return 1;
607 }
608 }
609 *mp_arr_len = k + 1;
610 if (k == max_arr_len) {
611 pr2serr("build_diag_page: array length exceeded\n");
612 return 1;
613 }
614 }
615 return 0;
616 }
617
618
619 struct page_code_desc {
620 int page_code;
621 const char * desc;
622 };
623 static struct page_code_desc pc_desc_arr[] = {
624 {0x0, "Supported diagnostic pages"},
625 {0x1, "Configuration (SES)"},
626 {0x2, "Enclosure status/control (SES)"},
627 {0x3, "Help text (SES)"},
628 {0x4, "String In/Out (SES)"},
629 {0x5, "Threshold In/Out (SES)"},
630 {0x6, "Array Status/Control (SES, obsolete)"},
631 {0x7, "Element descriptor (SES)"},
632 {0x8, "Short enclosure status (SES)"},
633 {0x9, "Enclosure busy (SES-2)"},
634 {0xa, "Additional (device) element status (SES-2)"},
635 {0xb, "Subenclosure help text (SES-2)"},
636 {0xc, "Subenclosure string In/Out (SES-2)"},
637 {0xd, "Supported SES diagnostic pages (SES-2)"},
638 {0xe, "Download microcode diagnostic pages (SES-2)"},
639 {0xf, "Subenclosure nickname diagnostic pages (SES-2)"},
640 {0x3f, "Protocol specific (SAS transport)"},
641 {0x40, "Translate address (direct access)"},
642 {0x41, "Device status (direct access)"},
643 {0x42, "Rebuild assist (direct access)"}, /* sbc3r31 */
644 };
645
646 static const char *
find_page_code_desc(int page_num)647 find_page_code_desc(int page_num)
648 {
649 int k;
650 int num = SG_ARRAY_SIZE(pc_desc_arr);
651 const struct page_code_desc * pcdp = &pc_desc_arr[0];
652
653 for (k = 0; k < num; ++k, ++pcdp) {
654 if (page_num == pcdp->page_code)
655 return pcdp->desc;
656 else if (page_num < pcdp->page_code)
657 return NULL;
658 }
659 return NULL;
660 }
661
662 static void
list_page_codes()663 list_page_codes()
664 {
665 int k;
666 int num = SG_ARRAY_SIZE(pc_desc_arr);
667 const struct page_code_desc * pcdp = &pc_desc_arr[0];
668
669 printf("Page_Code Description\n");
670 for (k = 0; k < num; ++k, ++pcdp)
671 printf(" 0x%02x %s\n", pcdp->page_code,
672 (pcdp->desc ? pcdp->desc : "<unknown>"));
673 }
674
675
676 int
main(int argc,char * argv[])677 main(int argc, char * argv[])
678 {
679 int k, num, rsp_len, res, rsp_buff_size, pg, bd_len, resid, vb;
680 int sg_fd = -1;
681 int read_in_len = 0;
682 int ret = 0;
683 struct opts_t opts;
684 struct opts_t * op;
685 uint8_t * rsp_buff = NULL;
686 uint8_t * free_rsp_buff = NULL;
687 const char * cp;
688 uint8_t * read_in = NULL;
689 uint8_t * free_read_in = NULL;
690
691 op = &opts;
692 memset(op, 0, sizeof(opts));
693 op->maxlen = DEF_ALLOC_LEN;
694 op->page_code = -1;
695 res = parse_cmd_line(op, argc, argv);
696 if (res)
697 return SG_LIB_SYNTAX_ERROR;
698 if (op->do_help) {
699 if (op->opt_new)
700 usage();
701 else
702 usage_old();
703 return 0;
704 }
705 #ifdef DEBUG
706 pr2serr("In DEBUG mode, ");
707 if (op->verbose_given && op->version_given) {
708 pr2serr("but override: '-vV' given, zero verbose and continue\n");
709 op->verbose_given = false;
710 op->version_given = false;
711 op->verbose = 0;
712 } else if (! op->verbose_given) {
713 pr2serr("set '-vv'\n");
714 op->verbose = 2;
715 } else
716 pr2serr("keep verbose=%d\n", op->verbose);
717 #else
718 if (op->verbose_given && op->version_given)
719 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
720 #endif
721 if (op->version_given) {
722 pr2serr("Version string: %s\n", version_str);
723 return 0;
724 }
725
726 rsp_buff_size = op->maxlen;
727
728 if (NULL == op->device_name) {
729 if (op->do_list) {
730 list_page_codes();
731 return 0;
732 }
733 pr2serr("No DEVICE argument given\n\n");
734 if (op->opt_new)
735 usage();
736 else
737 usage_old();
738 return SG_LIB_SYNTAX_ERROR;
739 }
740 vb = op->verbose;
741 if (op->do_raw) {
742 read_in = sg_memalign(op->maxlen, 0, &free_read_in, vb > 3);
743 if (NULL == read_in) {
744 pr2serr("unable to allocate %d bytes\n", op->maxlen);
745 return SG_LIB_CAT_OTHER;
746 }
747 if (build_diag_page(op->raw_arg, read_in, &read_in_len, op->maxlen)) {
748 if (op->opt_new) {
749 printf("Bad sequence after '--raw=' option\n");
750 usage();
751 } else {
752 printf("Bad sequence after '-raw=' option\n");
753 usage_old();
754 }
755 ret = SG_LIB_SYNTAX_ERROR;
756 goto fini;
757 }
758 }
759
760 if ((op->do_doff || op->do_uoff) && (! op->do_deftest)) {
761 if (op->opt_new) {
762 printf("setting --doff or --uoff only useful when -t is set\n");
763 usage();
764 } else {
765 printf("setting -doff or -uoff only useful when -t is set\n");
766 usage_old();
767 }
768 ret = SG_LIB_CONTRADICT;
769 goto fini;
770 }
771 if ((op->do_selftest > 0) && op->do_deftest) {
772 if (op->opt_new) {
773 printf("either set --selftest=SF or --test (not both)\n");
774 usage();
775 } else {
776 printf("either set -s=SF or -t (not both)\n");
777 usage_old();
778 }
779 ret = SG_LIB_CONTRADICT;
780 goto fini;
781 }
782 if (op->do_raw) {
783 if ((op->do_selftest > 0) || op->do_deftest || op->do_extdur ||
784 op->do_list) {
785 if (op->opt_new) {
786 printf("'--raw=' cannot be used with self-tests, '-e' or "
787 "'-l'\n");
788 usage();
789 } else {
790 printf("'-raw=' cannot be used with self-tests, '-e' or "
791 "'-l'\n");
792 usage_old();
793 }
794 ret = SG_LIB_CONTRADICT;
795 goto fini;
796 }
797 if (! op->do_pf) {
798 if (op->opt_new)
799 printf(">>> warning, '--pf' probably should be used with "
800 "'--raw='\n");
801 else
802 printf(">>> warning, '-pf' probably should be used with "
803 "'-raw='\n");
804 }
805 }
806 #ifdef SG_LIB_WIN32
807 #ifdef SG_LIB_WIN32_DIRECT
808 if (vb > 4)
809 pr2serr("Initial win32 SPT interface state: %s\n",
810 scsi_pt_win32_spt_state() ? "direct" : "indirect");
811 if (op->maxlen >= 16384)
812 scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
813 #endif
814 #endif
815
816 if ((sg_fd = sg_cmds_open_device(op->device_name, false /* rw */, vb)) <
817 0) {
818 if (vb)
819 pr2serr(ME "error opening file: %s: %s\n", op->device_name,
820 safe_strerror(-sg_fd));
821 ret = sg_convert_errno(-sg_fd);
822 goto fini;
823 }
824 rsp_buff = sg_memalign(op->maxlen, 0, &free_rsp_buff, vb > 3);
825 if (NULL == rsp_buff) {
826 pr2serr("unable to allocate %d bytes (2)\n", op->maxlen);
827 ret = SG_LIB_CAT_OTHER;
828 goto close_fini;
829 }
830 if (op->do_extdur) { /* fetch Extended self-test time from Control
831 * mode page with Mode Sense(10) command*/
832 res = do_modes_0a(sg_fd, rsp_buff, 32, false /* mode6 */,
833 true /* noisy */, vb);
834 if (0 == res) {
835 /* Mode sense(10) response, step over any block descriptors */
836 num = sg_msense_calc_length(rsp_buff, 32, false, &bd_len);
837 num -= (8 /* MS(10) header length */ + bd_len);
838 if (num >= 0xc) {
839 int secs = sg_get_unaligned_be16(rsp_buff + 8 + bd_len + 10);
840
841 if (0xffff == secs) {
842 if (op->verbose > 1)
843 printf("Expected extended self-test duration's value "
844 "[65535] indicates the\nsimilarly named field "
845 "in the Extended Inquiry VPD page should be "
846 "used\n");
847 } else {
848 #ifdef SG_LIB_MINGW
849 printf("Expected extended self-test duration=%d seconds "
850 "(%g minutes)\n", secs, secs / 60.0);
851 #else
852 printf("Expected extended self-test duration=%d seconds "
853 "(%.2f minutes)\n", secs, secs / 60.0);
854 #endif
855 }
856 } else
857 printf("Extended self-test duration not available\n");
858 } else {
859 ret = res;
860 printf("Extended self-test duration (mode page 0xa) failed\n");
861 goto err_out9;
862 }
863 } else if (op->do_list || (op->page_code >= 0x0)) {
864 pg = op->page_code;
865 if (pg < 0)
866 res = do_senddiag(sg_fd, 0, true /* pf */, false, false, false,
867 rsp_buff, 4, op->timeout, 1, vb);
868 else
869 res = 0;
870 if (0 == res) {
871 resid = 0;
872 if (0 == sg_ll_receive_diag_v2(sg_fd, (pg >= 0x0),
873 ((pg >= 0x0) ? pg : 0), rsp_buff,
874 rsp_buff_size, 0, &resid,
875 true, vb)) {
876 rsp_buff_size -= resid;
877 if (rsp_buff_size < 4) {
878 pr2serr("RD resid (%d) indicates response too small "
879 "(lem=%d)\n", resid, rsp_buff_size);
880 goto err_out;
881 }
882 rsp_len = sg_get_unaligned_be16(rsp_buff + 2) + 4;
883 rsp_len= (rsp_len < rsp_buff_size) ? rsp_len : rsp_buff_size;
884 if (op->do_hex > 1)
885 hex2stdout(rsp_buff, rsp_len,
886 (2 == op->do_hex) ? 0 : -1);
887 else if (pg < 0x1) {
888 printf("Supported diagnostic pages response:\n");
889 if (op->do_hex)
890 hex2stdout(rsp_buff, rsp_len, 1);
891 else {
892 for (k = 0; k < (rsp_len - 4); ++k) {
893 pg = rsp_buff[k + 4];
894 cp = find_page_code_desc(pg);
895 if (NULL == cp)
896 cp = (pg < 0x80) ? "<unknown>" :
897 "<vendor specific>";
898 printf(" 0x%02x %s\n", pg, cp);
899 }
900 }
901 } else {
902 cp = find_page_code_desc(pg);
903 if (cp)
904 printf("%s diagnostic page [0x%x] response in "
905 "hex:\n", cp, pg);
906 else
907 printf("diagnostic page 0x%x response in hex:\n", pg);
908 hex2stdout(rsp_buff, rsp_len, 1);
909 }
910 } else {
911 ret = res;
912 pr2serr("RECEIVE DIAGNOSTIC RESULTS command failed\n");
913 goto err_out9;
914 }
915 } else {
916 ret = res;
917 goto err_out;
918 }
919 } else if (op->do_raw) {
920 res = do_senddiag(sg_fd, 0, op->do_pf, false, false, false, read_in,
921 read_in_len, op->timeout, 1, vb);
922 if (res) {
923 ret = res;
924 goto err_out;
925 }
926 } else {
927 res = do_senddiag(sg_fd, op->do_selftest, op->do_pf, op->do_deftest,
928 op->do_doff, op->do_uoff, NULL, 0, op->timeout, 1,
929 vb);
930 if (0 == res) {
931 if ((5 == op->do_selftest) || (6 == op->do_selftest))
932 printf("Foreground self-test returned GOOD status\n");
933 else if (op->do_deftest && (! op->do_doff) && (! op->do_uoff))
934 printf("Default self-test returned GOOD status\n");
935 } else {
936 ret = res;
937 goto err_out;
938 }
939 }
940 goto close_fini;
941
942 err_out:
943 if (SG_LIB_CAT_UNIT_ATTENTION == res)
944 pr2serr("SEND DIAGNOSTIC, unit attention\n");
945 else if (SG_LIB_CAT_ABORTED_COMMAND == res)
946 pr2serr("SEND DIAGNOSTIC, aborted command\n");
947 else if (SG_LIB_CAT_NOT_READY == res)
948 pr2serr("SEND DIAGNOSTIC, device not ready\n");
949 else
950 pr2serr("SEND DIAGNOSTIC command, failed\n");
951 err_out9:
952 if (vb < 2)
953 pr2serr(" try again with '-vv' for more information\n");
954 close_fini:
955 if (sg_fd >= 0) {
956 res = sg_cmds_close_device(sg_fd);
957 if (0 == ret)
958 ret = sg_convert_errno(-res);
959 }
960 fini:
961 if (free_read_in)
962 free(free_read_in);
963 if (free_rsp_buff)
964 free(free_rsp_buff);
965 if (0 == vb) {
966 if (! sg_if_can2stderr("sg_senddiag failed: ", ret))
967 pr2serr("Some error occurred, try again with '-v' "
968 "or '-vv' for more information\n");
969 }
970 return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
971 }
972