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