/* * A utility program originally written for the Linux OS SCSI subsystem * Copyright (C) 2003-2022 D. Gilbert * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * SPDX-License-Identifier: GPL-2.0-or-later This program issues the SCSI SEND DIAGNOSTIC command and in one case the SCSI RECEIVE DIAGNOSTIC command to list supported diagnostic pages. */ #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_cmds_basic.h" #include "sg_cmds_extra.h" #if SG_LIB_WIN32 #include "sg_pt.h" /* needed for scsi_pt_win32_direct() */ #endif #include "sg_unaligned.h" #include "sg_pr2serr.h" static const char * version_str = "0.65 20220128"; #define ME "sg_senddiag: " #define DEF_ALLOC_LEN (1024 * 4) static struct option long_options[] = { {"doff", no_argument, 0, 'd'}, {"extdur", no_argument, 0, 'e'}, {"help", no_argument, 0, 'h'}, {"hex", no_argument, 0, 'H'}, {"list", no_argument, 0, 'l'}, {"maxlen", required_argument, 0, 'm'}, {"new", no_argument, 0, 'N'}, {"old", no_argument, 0, 'O'}, {"page", required_argument, 0, 'P'}, {"pf", no_argument, 0, 'p'}, {"raw", required_argument, 0, 'r'}, {"selftest", required_argument, 0, 's'}, {"test", no_argument, 0, 't'}, {"timeout", required_argument, 0, 'T'}, {"uoff", no_argument, 0, 'u'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0}, }; struct opts_t { bool do_deftest; bool do_doff; bool do_extdur; bool do_list; bool do_pf; bool do_raw; bool do_uoff; bool opt_new; bool verbose_given; bool version_given; int do_help; int do_hex; int maxlen; int page_code; int do_selftest; int timeout; int verbose; const char * device_name; const char * raw_arg; }; static void usage() { printf("Usage: sg_senddiag [--doff] [--extdur] [--help] [--hex] " "[--list]\n" " [--maxlen=LEN] [--page=PG] [--pf] " "[--raw=H,H...]\n" " [--selftest=ST] [--test] [--timeout=SECS] " "[--uoff]\n" " [--verbose] [--version] [DEVICE]\n" " where:\n" " --doff|-d device online (def: 0, only with '--test')\n" " --extdur|-e duration of an extended self-test (from mode " "page 0xa)\n" " --help|-h print usage message then exit\n" " --hex|-H output RDR in hex; twice: plus ASCII; thrice: " "suitable\n" " for '--raw=-' with later invocation\n" " --list|-l list supported page codes (with or without " "DEVICE)\n" " --maxlen=LEN|-m LEN parameter list length or maximum " "allocation\n" " length (default: 4096 bytes)\n" " --page=PG|-P PG do RECEIVE DIAGNOSTIC RESULTS only, set " "PCV\n" " --pf|-p set PF bit (def: 0)\n" " --raw=H,H...|-r H,H... sequence of hex bytes to form " "diag page to send\n" " --raw=-|-r - read stdin for sequence of bytes to send\n" " --selftest=ST|-s ST self-test code, default: 0 " "(inactive)\n" " 1->background short, 2->background " "extended\n" " 4->abort test\n" " 5->foreground short, 6->foreground " "extended\n" " --test|-t default self-test\n" " --timeout=SECS|-T SECS timeout for foreground self tests\n" " unit: second (def: 7200 seconds)\n" " --uoff|-u unit offline (def: 0, only with '--test')\n" " --verbose|-v increase verbosity\n" " --old|-O use old interface (use as first option)\n" " --version|-V output version string then exit\n\n" "Performs a SCSI SEND DIAGNOSTIC (and/or a RECEIVE DIAGNOSTIC " "RESULTS) command\n" ); } static void usage_old() { printf("Usage: sg_senddiag [-doff] [-e] [-h] [-H] [-l] [-pf]" " [-raw=H,H...]\n" " [-s=SF] [-t] [-T=SECS] [-uoff] [-v] [-V] " "[DEVICE]\n" " where:\n" " -doff device online (def: 0, only with '-t')\n" " -e duration of an extended self-test (from mode page " "0xa)\n" " -h output in hex\n" " -H output in hex (same as '-h')\n" " -l list supported page codes\n" " -pf set PF bit (def: 0)\n" " -raw=H,H... sequence of bytes to form diag page to " "send\n" " -raw=- read stdin for sequence of bytes to send\n" " -s=SF self-test code (def: 0)\n" " 1->background short, 2->background extended," " 4->abort test\n" " 5->foreground short, 6->foreground extended\n" " -t default self-test\n" " -T SECS timeout for foreground self tests\n" " -uoff unit offline (def: 0, only with '-t')\n" " -v increase verbosity (print issued SCSI cmds)\n" " -V output version string\n" " -N|--new use new interface\n" " -? output this usage message\n\n" "Performs a SCSI SEND DIAGNOSTIC (and/or a RECEIVE DIAGNOSTIC " "RESULTS) command\n" ); } static int new_parse_cmd_line(struct opts_t * op, int argc, char * argv[]) { int c, n; while (1) { int option_index = 0; c = getopt_long(argc, argv, "dehHlm:NOpP:r:s:tT:uvV", long_options, &option_index); if (c == -1) break; switch (c) { case 'd': op->do_doff = true; break; case 'e': op->do_extdur = true; break; case 'h': case '?': ++op->do_help; break; case 'H': ++op->do_hex; break; case 'l': op->do_list = true; break; case 'm': n = sg_get_num(optarg); if ((n < 0) || (n > 0xffff)) { pr2serr("bad argument to '--maxlen=' or greater than 65535 " "[0xffff]\n"); return SG_LIB_SYNTAX_ERROR; } op->maxlen = n; break; case 'N': break; /* ignore */ case 'O': op->opt_new = false; return 0; case 'p': op->do_pf = true; break; case 'P': n = sg_get_num(optarg); if ((n < 0) || (n > 0xff)) { pr2serr("bad argument to '--page=' or greater than 255 " "[0xff]\n"); return SG_LIB_SYNTAX_ERROR; } op->page_code = n; break; case 'r': op->raw_arg = optarg; op->do_raw = true; break; case 's': n = sg_get_num(optarg); if ((n < 0) || (n > 7)) { pr2serr("bad argument to '--selftest='\n"); usage(); return SG_LIB_SYNTAX_ERROR; } op->do_selftest = n; break; case 't': op->do_deftest = true; break; case 'T': n = sg_get_num(optarg); if (n < 0) { pr2serr("bad argument to '--timeout=SECS'\n"); return SG_LIB_SYNTAX_ERROR; } op->timeout = n; break; case 'u': op->do_uoff = true; break; case 'v': op->verbose_given = true; ++op->verbose; break; case 'V': op->version_given = true; break; default: pr2serr("unrecognised option code %c [0x%x]\n", c, c); if (op->do_help) break; usage(); return SG_LIB_SYNTAX_ERROR; } } if (optind < argc) { if (NULL == op->device_name) { op->device_name = argv[optind]; ++optind; } if (optind < argc) { for (; optind < argc; ++optind) pr2serr("Unexpected extra argument: %s\n", argv[optind]); usage(); return SG_LIB_SYNTAX_ERROR; } } return 0; } static int old_parse_cmd_line(struct opts_t * op, int argc, char * argv[]) { bool jmp_out; int k, plen, num, n; unsigned int u; const char * cp; for (k = 1; k < argc; ++k) { cp = argv[k]; plen = strlen(cp); if (plen <= 0) continue; if ('-' == *cp) { for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) { switch (*cp) { case 'd': if (0 == strncmp("doff", cp, 4)) { op->do_doff = true; cp += 3; plen -= 3; } else jmp_out = true; break; case 'e': op->do_extdur = true; break; case 'h': case 'H': ++op->do_hex; break; case 'l': op->do_list = true; break; case 'N': op->opt_new = true; return 0; case 'O': break; case 'p': if (0 == strncmp("pf", cp, 2)) { op->do_pf = true; ++cp; --plen; } else jmp_out = true; break; case 't': op->do_deftest = true; break; case 'u': if (0 == strncmp("uoff", cp, 4)) { op->do_uoff = true; cp += 3; plen -= 3; } else jmp_out = true; break; case 'v': op->verbose_given = true; ++op->verbose; break; case 'V': op->version_given = true; break; case '?': ++op->do_help; break; default: jmp_out = true; break; } if (jmp_out) break; } if (plen <= 0) continue; if (0 == strncmp("raw=", cp, 4)) { op->raw_arg = cp + 4; op->do_raw = true; } else if (0 == strncmp("s=", cp, 2)) { num = sscanf(cp + 2, "%x", &u); if ((1 != num) || (u > 7)) { printf("Bad page code after '-s=' option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } op->do_selftest = u; } else if (0 == strncmp("T=", cp, 2)) { num = sscanf(cp + 2, "%d", &n); if ((1 != num) || (n < 0)) { printf("Bad page code after '-T=SECS' option\n"); usage_old(); return SG_LIB_SYNTAX_ERROR; } op->timeout = n; } else if (0 == strncmp("-old", cp, 5)) ; else if (jmp_out) { pr2serr("Unrecognized option: %s\n", cp); usage_old(); return SG_LIB_SYNTAX_ERROR; } } else if (0 == op->device_name) op->device_name = cp; else { pr2serr("too many arguments, got: %s, not expecting: %s\n", op->device_name, cp); usage_old(); return SG_LIB_SYNTAX_ERROR; } } return 0; } static int parse_cmd_line(struct opts_t * op, int argc, char * argv[]) { int res; char * cp; cp = getenv("SG3_UTILS_OLD_OPTS"); if (cp) { op->opt_new = false; res = old_parse_cmd_line(op, argc, argv); if ((0 == res) && op->opt_new) res = new_parse_cmd_line(op, argc, argv); } else { op->opt_new = true; res = new_parse_cmd_line(op, argc, argv); if ((0 == res) && (! op->opt_new)) res = old_parse_cmd_line(op, argc, argv); } return res; } /* Return of 0 -> success, otherwise see sg_ll_send_diag() */ 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) { int long_duration = 0; if ((0 == sf_bit) && ((5 == sf_code) || (6 == sf_code))) { /* foreground self-tests */ if (tmout <= 0) long_duration = 1; else long_duration = tmout; } return sg_ll_send_diag(sg_fd, sf_code, pf_bit, sf_bit, devofl_bit, unitofl_bit, long_duration, outgoing_pg, outgoing_len, noisy, verbose); } /* Get expected extended self-test time from mode page 0xa (for '-e') */ static int do_modes_0a(int sg_fd, void * resp, int mx_resp_len, bool mode6, bool noisy, int verbose) { int res; int resid = 0; if (mode6) res = sg_ll_mode_sense6(sg_fd, true /* dbd */, false /* pc */, 0xa /* page */, false, resp, mx_resp_len, noisy, verbose); else res = sg_ll_mode_sense10_v2(sg_fd, false /* llbaa */, true /* dbd */, false, 0xa, false, resp, mx_resp_len, 0, &resid, noisy, verbose); if (res) { char b[80]; sg_get_category_sense_str(res, sizeof(b), b, verbose); pr2serr("Mode sense (%s): %s\n", (mode6 ? "6" : "10"), b); } else { mx_resp_len -= resid; if (mx_resp_len < 4) { pr2serr("%s: response length (%d) too small (resid=%d)\n", __func__, mx_resp_len, resid); res = SG_LIB_WILD_RESID; } } return res; } /* Read hex numbers from command line (comma separated list) or from */ /* stdin (one per line, comma separated list or space separated list). */ /* Returns 0 if ok, or 1 if error. */ static int build_diag_page(const char * inp, uint8_t * mp_arr, int * mp_arr_len, int max_arr_len) { int in_len, k, j, m; unsigned int h; const char * lcp; char * cp; char * c2p; if ((NULL == inp) || (NULL == mp_arr) || (NULL == mp_arr_len)) return 1; lcp = inp; in_len = strlen(inp); if (0 == in_len) *mp_arr_len = 0; if ('-' == inp[0]) { /* read from stdin */ bool split_line; int off = 0; char line[512]; char carry_over[4]; carry_over[0] = 0; for (j = 0; j < 512; ++j) { if (NULL == fgets(line, sizeof(line), stdin)) break; in_len = strlen(line); if (in_len > 0) { if ('\n' == line[in_len - 1]) { --in_len; line[in_len] = '\0'; split_line = false; } else split_line = true; } if (in_len < 1) { carry_over[0] = 0; continue; } if (carry_over[0]) { if (isxdigit((uint8_t)line[0])) { carry_over[1] = line[0]; carry_over[2] = '\0'; if (1 == sscanf(carry_over, "%x", &h)) mp_arr[off - 1] = h; /* back up and overwrite */ else { pr2serr("build_diag_page: carry_over error ['%s'] " "around line %d\n", carry_over, j + 1); return 1; } lcp = line + 1; --in_len; } else lcp = line; carry_over[0] = 0; } else lcp = line; m = strspn(lcp, " \t"); if (m == in_len) continue; lcp += m; in_len -= m; if ('#' == *lcp) continue; k = strspn(lcp, "0123456789aAbBcCdDeEfF ,\t"); if ((k < in_len) && ('#' != lcp[k])) { pr2serr("build_diag_page: syntax error at line %d, pos %d\n", j + 1, m + k + 1); return 1; } for (k = 0; k < 1024; ++k) { if (1 == sscanf(lcp, "%x", &h)) { if (h > 0xff) { pr2serr("build_diag_page: hex number larger than " "0xff in line %d, pos %d\n", j + 1, (int)(lcp - line + 1)); return 1; } if (split_line && (1 == strlen(lcp))) { /* single trailing hex digit might be a split pair */ carry_over[0] = *lcp; } if ((off + k) >= max_arr_len) { pr2serr("build_diag_page: array length exceeded\n"); return 1; } mp_arr[off + k] = h; lcp = strpbrk(lcp, " ,\t"); if (NULL == lcp) break; lcp += strspn(lcp, " ,\t"); if ('\0' == *lcp) break; } else { if ('#' == *lcp) { --k; break; } pr2serr("build_diag_page: error in line %d, at pos %d\n", j + 1, (int)(lcp - line + 1)); return 1; } } off += (k + 1); } *mp_arr_len = off; } else { /* hex string on command line */ k = strspn(inp, "0123456789aAbBcCdDeEfF, "); if (in_len != k) { pr2serr("build_diag_page: error at pos %d\n", k + 1); return 1; } for (k = 0; k < max_arr_len; ++k) { if (1 == sscanf(lcp, "%x", &h)) { if (h > 0xff) { pr2serr("build_diag_page: hex number larger than 0xff at " "pos %d\n", (int)(lcp - inp + 1)); return 1; } mp_arr[k] = h; cp = (char *)strchr(lcp, ','); c2p = (char *)strchr(lcp, ' '); if (NULL == cp) cp = c2p; if (NULL == cp) break; if (c2p && (c2p < cp)) cp = c2p; lcp = cp + 1; } else { pr2serr("build_diag_page: error at pos %d\n", (int)(lcp - inp + 1)); return 1; } } *mp_arr_len = k + 1; if (k == max_arr_len) { pr2serr("build_diag_page: array length exceeded\n"); return 1; } } return 0; } struct page_code_desc { int page_code; const char * desc; }; static struct page_code_desc pc_desc_arr[] = { {0x0, "Supported diagnostic pages"}, {0x1, "Configuration (SES)"}, {0x2, "Enclosure status/control (SES)"}, {0x3, "Help text (SES)"}, {0x4, "String In/Out (SES)"}, {0x5, "Threshold In/Out (SES)"}, {0x6, "Array Status/Control (SES, obsolete)"}, {0x7, "Element descriptor (SES)"}, {0x8, "Short enclosure status (SES)"}, {0x9, "Enclosure busy (SES-2)"}, {0xa, "Additional (device) element status (SES-2)"}, {0xb, "Subenclosure help text (SES-2)"}, {0xc, "Subenclosure string In/Out (SES-2)"}, {0xd, "Supported SES diagnostic pages (SES-2)"}, {0xe, "Download microcode diagnostic pages (SES-2)"}, {0xf, "Subenclosure nickname diagnostic pages (SES-2)"}, {0x3f, "Protocol specific (SAS transport)"}, {0x40, "Translate address (direct access)"}, {0x41, "Device status (direct access)"}, {0x42, "Rebuild assist (direct access)"}, /* sbc3r31 */ }; static const char * find_page_code_desc(int page_num) { int k; int num = SG_ARRAY_SIZE(pc_desc_arr); const struct page_code_desc * pcdp = &pc_desc_arr[0]; for (k = 0; k < num; ++k, ++pcdp) { if (page_num == pcdp->page_code) return pcdp->desc; else if (page_num < pcdp->page_code) return NULL; } return NULL; } static void list_page_codes() { int k; int num = SG_ARRAY_SIZE(pc_desc_arr); const struct page_code_desc * pcdp = &pc_desc_arr[0]; printf("Page_Code Description\n"); for (k = 0; k < num; ++k, ++pcdp) printf(" 0x%02x %s\n", pcdp->page_code, (pcdp->desc ? pcdp->desc : "")); } int main(int argc, char * argv[]) { int k, num, rsp_len, res, rsp_buff_size, pg, bd_len, resid, vb; int sg_fd = -1; int read_in_len = 0; int ret = 0; struct opts_t opts; struct opts_t * op; uint8_t * rsp_buff = NULL; uint8_t * free_rsp_buff = NULL; const char * cp; uint8_t * read_in = NULL; uint8_t * free_read_in = NULL; op = &opts; memset(op, 0, sizeof(opts)); op->maxlen = DEF_ALLOC_LEN; op->page_code = -1; res = parse_cmd_line(op, argc, argv); if (res) return SG_LIB_SYNTAX_ERROR; if (op->do_help) { if (op->opt_new) usage(); else usage_old(); return 0; } #ifdef DEBUG pr2serr("In DEBUG mode, "); if (op->verbose_given && op->version_given) { pr2serr("but override: '-vV' given, zero verbose and continue\n"); op->verbose_given = false; op->version_given = false; op->verbose = 0; } else if (! op->verbose_given) { pr2serr("set '-vv'\n"); op->verbose = 2; } else pr2serr("keep verbose=%d\n", op->verbose); #else if (op->verbose_given && op->version_given) pr2serr("Not in DEBUG mode, so '-vV' has no special action\n"); #endif if (op->version_given) { pr2serr("Version string: %s\n", version_str); return 0; } rsp_buff_size = op->maxlen; if (NULL == op->device_name) { if (op->do_list) { list_page_codes(); return 0; } pr2serr("No DEVICE argument given\n\n"); if (op->opt_new) usage(); else usage_old(); return SG_LIB_SYNTAX_ERROR; } vb = op->verbose; if (op->do_raw) { read_in = sg_memalign(op->maxlen, 0, &free_read_in, vb > 3); if (NULL == read_in) { pr2serr("unable to allocate %d bytes\n", op->maxlen); return SG_LIB_CAT_OTHER; } if (build_diag_page(op->raw_arg, read_in, &read_in_len, op->maxlen)) { if (op->opt_new) { printf("Bad sequence after '--raw=' option\n"); usage(); } else { printf("Bad sequence after '-raw=' option\n"); usage_old(); } ret = SG_LIB_SYNTAX_ERROR; goto fini; } } if ((op->do_doff || op->do_uoff) && (! op->do_deftest)) { if (op->opt_new) { printf("setting --doff or --uoff only useful when -t is set\n"); usage(); } else { printf("setting -doff or -uoff only useful when -t is set\n"); usage_old(); } ret = SG_LIB_CONTRADICT; goto fini; } if ((op->do_selftest > 0) && op->do_deftest) { if (op->opt_new) { printf("either set --selftest=SF or --test (not both)\n"); usage(); } else { printf("either set -s=SF or -t (not both)\n"); usage_old(); } ret = SG_LIB_CONTRADICT; goto fini; } if (op->do_raw) { if ((op->do_selftest > 0) || op->do_deftest || op->do_extdur || op->do_list) { if (op->opt_new) { printf("'--raw=' cannot be used with self-tests, '-e' or " "'-l'\n"); usage(); } else { printf("'-raw=' cannot be used with self-tests, '-e' or " "'-l'\n"); usage_old(); } ret = SG_LIB_CONTRADICT; goto fini; } if (! op->do_pf) { if (op->opt_new) printf(">>> warning, '--pf' probably should be used with " "'--raw='\n"); else printf(">>> warning, '-pf' probably should be used with " "'-raw='\n"); } } #ifdef SG_LIB_WIN32 #ifdef SG_LIB_WIN32_DIRECT if (vb > 4) pr2serr("Initial win32 SPT interface state: %s\n", scsi_pt_win32_spt_state() ? "direct" : "indirect"); if (op->maxlen >= 16384) scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */); #endif #endif if ((sg_fd = sg_cmds_open_device(op->device_name, false /* rw */, vb)) < 0) { if (vb) pr2serr(ME "error opening file: %s: %s\n", op->device_name, safe_strerror(-sg_fd)); ret = sg_convert_errno(-sg_fd); goto fini; } rsp_buff = sg_memalign(op->maxlen, 0, &free_rsp_buff, vb > 3); if (NULL == rsp_buff) { pr2serr("unable to allocate %d bytes (2)\n", op->maxlen); ret = SG_LIB_CAT_OTHER; goto close_fini; } if (op->do_extdur) { /* fetch Extended self-test time from Control * mode page with Mode Sense(10) command*/ res = do_modes_0a(sg_fd, rsp_buff, 32, false /* mode6 */, true /* noisy */, vb); if (0 == res) { /* Mode sense(10) response, step over any block descriptors */ num = sg_msense_calc_length(rsp_buff, 32, false, &bd_len); num -= (8 /* MS(10) header length */ + bd_len); if (num >= 0xc) { int secs = sg_get_unaligned_be16(rsp_buff + 8 + bd_len + 10); if (0xffff == secs) { if (op->verbose > 1) printf("Expected extended self-test duration's value " "[65535] indicates the\nsimilarly named field " "in the Extended Inquiry VPD page should be " "used\n"); } else { #ifdef SG_LIB_MINGW printf("Expected extended self-test duration=%d seconds " "(%g minutes)\n", secs, secs / 60.0); #else printf("Expected extended self-test duration=%d seconds " "(%.2f minutes)\n", secs, secs / 60.0); #endif } } else printf("Extended self-test duration not available\n"); } else { ret = res; printf("Extended self-test duration (mode page 0xa) failed\n"); goto err_out9; } } else if (op->do_list || (op->page_code >= 0x0)) { pg = op->page_code; if (pg < 0) res = do_senddiag(sg_fd, 0, true /* pf */, false, false, false, rsp_buff, 4, op->timeout, 1, vb); else res = 0; if (0 == res) { resid = 0; if (0 == sg_ll_receive_diag_v2(sg_fd, (pg >= 0x0), ((pg >= 0x0) ? pg : 0), rsp_buff, rsp_buff_size, 0, &resid, true, vb)) { rsp_buff_size -= resid; if (rsp_buff_size < 4) { pr2serr("RD resid (%d) indicates response too small " "(lem=%d)\n", resid, rsp_buff_size); goto err_out; } rsp_len = sg_get_unaligned_be16(rsp_buff + 2) + 4; rsp_len= (rsp_len < rsp_buff_size) ? rsp_len : rsp_buff_size; if (op->do_hex > 1) hex2stdout(rsp_buff, rsp_len, (2 == op->do_hex) ? 0 : -1); else if (pg < 0x1) { printf("Supported diagnostic pages response:\n"); if (op->do_hex) hex2stdout(rsp_buff, rsp_len, 1); else { for (k = 0; k < (rsp_len - 4); ++k) { pg = rsp_buff[k + 4]; cp = find_page_code_desc(pg); if (NULL == cp) cp = (pg < 0x80) ? "" : ""; printf(" 0x%02x %s\n", pg, cp); } } } else { cp = find_page_code_desc(pg); if (cp) printf("%s diagnostic page [0x%x] response in " "hex:\n", cp, pg); else printf("diagnostic page 0x%x response in hex:\n", pg); hex2stdout(rsp_buff, rsp_len, 1); } } else { ret = res; pr2serr("RECEIVE DIAGNOSTIC RESULTS command failed\n"); goto err_out9; } } else { ret = res; goto err_out; } } else if (op->do_raw) { res = do_senddiag(sg_fd, 0, op->do_pf, false, false, false, read_in, read_in_len, op->timeout, 1, vb); if (res) { ret = res; goto err_out; } } else { res = do_senddiag(sg_fd, op->do_selftest, op->do_pf, op->do_deftest, op->do_doff, op->do_uoff, NULL, 0, op->timeout, 1, vb); if (0 == res) { if ((5 == op->do_selftest) || (6 == op->do_selftest)) printf("Foreground self-test returned GOOD status\n"); else if (op->do_deftest && (! op->do_doff) && (! op->do_uoff)) printf("Default self-test returned GOOD status\n"); } else { ret = res; goto err_out; } } goto close_fini; err_out: if (SG_LIB_CAT_UNIT_ATTENTION == res) pr2serr("SEND DIAGNOSTIC, unit attention\n"); else if (SG_LIB_CAT_ABORTED_COMMAND == res) pr2serr("SEND DIAGNOSTIC, aborted command\n"); else if (SG_LIB_CAT_NOT_READY == res) pr2serr("SEND DIAGNOSTIC, device not ready\n"); else pr2serr("SEND DIAGNOSTIC command, failed\n"); err_out9: if (vb < 2) pr2serr(" try again with '-vv' for more information\n"); close_fini: if (sg_fd >= 0) { res = sg_cmds_close_device(sg_fd); if (0 == ret) ret = sg_convert_errno(-res); } fini: if (free_read_in) free(free_read_in); if (free_rsp_buff) free(free_rsp_buff); if (0 == vb) { if (! sg_if_can2stderr("sg_senddiag failed: ", ret)) pr2serr("Some error occurred, try again with '-v' " "or '-vv' for more information\n"); } return (ret >= 0) ? ret : SG_LIB_CAT_OTHER; }