/
githubmirror
/
sg3_utils
Обзор
Документация
Войти
/
githubmirror
/
sg3_utils
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
lib/sg_json_sg_lib.c
955 строк
38 KB
Douglas Gilbert
sg_lib: add sg_strscpy() to replace strncpy(); sg_json: add sgj_haj_vs_nex(); fixes from regression testing against version 1.48
23 июн 2026, 06:39
23 июн 2026, 06:39
174643d
Код
Авторство
О чём код?
/* * Copyright (c) 2022-2026 Douglas Gilbert. * All rights reserved. * * SPDX-License-Identifier: BSD-2-Clause */ /* * Functions in this file support JSON output (but with some settings will * output only human readable form) and have dependencies on major parts * of the sg3_utils library (sgutils2). JSON support functions that do * _not_ depend on the rest of the sg3_utils library (apart from * sg_json_builder.[hc]) can be found in the sg_json.c file. * For non-JSON output the jsp pointer is assumed to be valid and point * to a sgj_state object that has JSON output deselected (e.g. in its * default state). */ #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <stdbool.h> #include <string.h> #include <ctype.h> #include "sg_json_sg_lib.h" #include "sg_pr2serr.h" #include "sg_lib.h" #include "sg_lib_data.h" #include "sg_unaligned.h" #include "sg_json_builder.h" static const char * ts_s = "too short"; static const char * dts_s = "descriptor too short"; static const char * sksv_s = "sense-key specific valid"; static const char * dde_s = "designation_descriptor_error"; static const char * naa_ex_s = "Network Address Authority"; static const char * aoi_ex_s = "IEEE-Administered Organizational Identifier"; static const char * assoc_s = "Association"; static const char * dt_s = "Designator type"; static const char * dcs_s = "Code set"; static const char * piv_s = "PIV"; static const char * piv_l_s = "Protocol Identifier Valid"; static const char * pi_s = "Protocol identifier"; static const char * vsh_s = "Vendor specific hexbytes"; static const char * t10vi_s = "T10 vendor identification"; static const char * eui_l_s = "EUI-64 based designator length"; static const char * iei_s = "IEEE identifier"; static const char * did_s = "Directory identifier"; static const char * iex_s = "Identifer extension"; static const char * dfa_s = "decoding failure"; static const char * fnh_s = "Full NAA hexbytes"; static const char * vsi_s = "Vendor specific identifier"; static const char * vsie_s = "Vendor specific identifier extension"; static const char * vsia_s = "Vendor specific identifier A"; static const char * vsib_s = "Vendor specific identifier B"; static const char * lav_s = "Locally administered value"; static const char * rtph_s = "Relative target port hexbytes"; static const char * rtpi_s = "Relative target port identifier"; static const char * tpgh_s = "Target port group hexbytes"; static const char * tpgi_s = "Target port group identifier"; static const char * lugh_s = "Logical unit group hexbytes"; static const char * lug_s = "Logical unit group"; static const char * md5i_s = "MD5 logical unit identifier"; static const char * snsh_s = "SCSI name string hexbytes"; static const char * sns_s = "SCSI name string"; static const char * pspih_s = "Protocol specific port identifier hexbytes"; static const char * pspi_s = "Protocol specific port identifier"; static const char * uuih_s = "UUID hexbytes"; static const char * uui_s = "UUID"; static const char * rdh_s = "Reserved designator hexbytes"; /* Simply a shorter function name for sgj_convert2snake() */ static char * snake(const char * in_name, char * name_so_s, int max_sname_len) { return sgj_convert2snake(in_name, name_so_s, max_sname_len); } /* This function decodes one designation descriptor which starts at 'ddp' * and is 'dd_len' bytes long. Designation descriptors are mainly found * in the Device identification VPD page [0x83] but do arise in other * situations (e.g. SCSI ports VPD page [0x86]). This function produces * either human readable output (to stdout) or JSON output or both. The * both variant is when JSON output is selected and additionally * jsp->pr_out_hr is true (e.g. with the '-j=o' option). In this case, * human readable output is placed in a JSON array called * 'plain_text_output' with each line in a single array element. * Implementation note: the designation descriptor is parsed twice in the * "both' case. Each line of human readable output is prefixed by (at least) * leadin_sp spaces. Returns true if successful, else false. */ bool sgj_haj_designation_descriptor(sgj_state * jsp, sgj_opaque_p jop, int leadin_sp, bool pr_assoc, const uint8_t * ddp, int dd_len) { bool as_json = jsp && jsp->pr_as_json; bool has_control_char; int p_id, piv, c_set, assoc, desig_type, d_id, naa; int k, n, aoi, vsi, dlen; uint64_t ull; const uint8_t * ip; char e[80]; char b[512]; char c2s[132]; const char * cp; const char * naa_sp; sgj_opaque_p jo2p; static const int blen = sizeof(b); static const int elen = sizeof(e); static const int c2s_len = sizeof(c2s); if (dd_len < 4) { sgj_haj_vs(jsp, jop, leadin_sp, dde_s, SGJ_SEP_COLON_1_SPACE, ts_s); return false; } if ((! as_json) || (as_json && jsp->pr_out_hr)) { n = (leadin_sp < 64) ? leadin_sp : 64; for (k = 0; k < n; ++k) e[k] = ' '; e[k] = '\0'; n = sg_get_designation_descriptor_str(e, ddp, dd_len, pr_assoc, jsp->z_counter > 0, // do_long blen, b); if (as_json) sgj_hr_str_out(jsp, b, n); else printf("%.*s", n, b); } if (! as_json) return true; dlen = ddp[3]; if (dlen > (dd_len - 4)) { snprintf(e, elen, "too long: says it is %d bytes, but given %d " "bytes\n", dlen, dd_len - 4); sgj_js_nv_s(jsp, jop, dde_s, e); return false; } ip = ddp + 4; p_id = ((ddp[0] >> 4) & 0xf); c_set = (ddp[0] & 0xf); piv = ((ddp[1] & 0x80) ? 1 : 0); assoc = ((ddp[1] >> 4) & 0x3); desig_type = (ddp[1] & 0xf); cp = sg_get_desig_assoc_str(assoc); sgj_js_nv_ihexstr(jsp, jop, snake(assoc_s, c2s, c2s_len), assoc, NULL, cp); cp = sg_get_desig_type_str(desig_type); if (NULL == cp) cp = "unknown"; sgj_js_nv_ihexstr(jsp, jop, snake(dt_s, c2s, c2s_len), desig_type, NULL, cp); cp = sg_get_desig_code_set_str(c_set); if (NULL == cp) cp = "unknown"; sg_get_trans_proto_str(p_id, elen, e); sgj_js_nv_ihexstr(jsp, jop, snake(dcs_s, c2s, c2s_len), c_set, NULL, cp); sgj_js_nv_ihexstr_nex(jsp, jop, snake(piv_s, c2s, c2s_len), piv, false, NULL, (piv ? "true" : "false"), piv_l_s); sgj_js_nv_ihexstr(jsp, jop, snake(pi_s, c2s, c2s_len), p_id, NULL, e); switch (desig_type) { case 0: /* vendor specific */ sgj_js_nv_hex_bytes(jsp, jop, snake(vsh_s, c2s, c2s_len), ip, dlen); break; case 1: /* T10 vendor identification */ n = (dlen < 8) ? dlen : 8; snprintf(b, blen, "%.*s", n, ip); sgj_js_nv_s(jsp, jop, snake(t10vi_s, c2s, c2s_len), b); snake(vsi_s, c2s, c2s_len); if (dlen <= 8) { sgj_js_nv_s(jsp, jop, c2s, ""); break; } if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */ sgj_js_nv_s_len(jsp, jop, c2s, (const char *)ip + 8, dlen - 8); break; } /* assume Vendor specific identifier (vsi) is in binary */ if (dlen <= 16) { ull = sg_get_unaligned_be(dlen - 8, ip + 8); sgj_js_nv_ihex(jsp, jop, c2s, ull); } else { sgj_js_nv_hex_bytes(jsp, jop, c2s, ip + 8, dlen - 8); } break; case 2: /* EUI-64 based */ sgj_js_nv_i(jsp, jop, snake(eui_l_s, c2s, c2s_len), dlen); ull = sg_get_unaligned_be64(ip); switch (dlen) { case 8: sgj_js_nv_ihex(jsp, jop, snake(iei_s, c2s, c2s_len), ull); break; case 12: sgj_js_nv_ihex(jsp, jop, snake(iei_s, c2s, c2s_len), ull); ull = sg_get_unaligned_be32(ip + 8); sgj_js_nv_ihex(jsp, jop, snake(did_s, c2s, c2s_len), ull); break; case 16: sgj_js_nv_ihex(jsp, jop, snake(iex_s, c2s, c2s_len), ull); ull = sg_get_unaligned_be64(ip + 8); sgj_js_nv_ihex(jsp, jop, snake(iei_s, c2s, c2s_len), ull); break; default: sgj_js_nv_s(jsp, jop, "eui_64", dfa_s); break; } break; case 3: /* NAA <n> */ if (jsp->pr_hex) { sgj_js_nv_hex_bytes(jsp, jop, snake(fnh_s, c2s, c2s_len), ip, dlen); } naa = (ip[0] >> 4) & 0xff; switch (naa) { case 2: naa_sp = "IEEE Extended"; sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp, naa_ex_s); d_id = (((ip[0] & 0xf) << 8) | ip[1]); sgj_js_nv_ihex(jsp, jop, snake(vsia_s, c2s, c2s_len), d_id); aoi = sg_get_unaligned_be24(ip + 2); sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_ex_s); vsi = sg_get_unaligned_be24(ip + 5); sgj_js_nv_ihex(jsp, jop, snake(vsib_s, c2s, c2s_len), vsi); break; case 3: naa_sp = "Locally Assigned"; sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp, naa_ex_s); ull = sg_get_unaligned_be64(ip + 0) & 0xfffffffffffffffULL; sgj_js_nv_ihex(jsp, jop, snake(lav_s, c2s, c2s_len), ull); break; case 5: naa_sp = "IEEE Registered"; sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp, naa_ex_s); aoi = (sg_get_unaligned_be32(ip + 0) >> 4) & 0xffffff; sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_ex_s); ull = sg_get_unaligned_be48(ip + 2) & 0xfffffffffULL; sgj_js_nv_ihex(jsp, jop, snake(vsi_s, c2s,c2s_len), ull); break; case 6: naa_sp = "IEEE Registered Extended"; sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, false, NULL, naa_sp, naa_ex_s); aoi = (sg_get_unaligned_be32(ip + 0) >> 4) & 0xffffff; sgj_js_nv_ihex_nex(jsp, jop, "aoi", aoi, true, aoi_ex_s); ull = sg_get_unaligned_be48(ip + 2) & 0xfffffffffULL; sgj_js_nv_ihex(jsp, jop, snake(vsi_s, c2s,c2s_len), ull); ull = sg_get_unaligned_be64(ip + 8); sgj_js_nv_ihex(jsp, jop, snake(vsie_s, c2s,c2s_len), ull); break; default: snprintf(b, blen, "unknown NAA value=0x%x", naa); sgj_js_nv_ihexstr_nex(jsp, jop, "naa", naa, true, NULL, b, naa_ex_s); sgj_js_nv_hex_bytes(jsp, jop, snake(fnh_s, c2s, c2s_len), ip, dlen); break; } break; case 4: /* Relative target port */ if (jsp->pr_hex) { sgj_js_nv_hex_bytes(jsp, jop, snake(rtph_s, c2s, c2s_len), ip, dlen); } ull = sg_get_unaligned_be16(ip + 2); sgj_js_nv_ihex(jsp, jop, snake(rtpi_s, c2s, c2s_len), ull); break; case 5: /* (primary) Target port group */ if (jsp->pr_hex) { sgj_js_nv_hex_bytes(jsp, jop, snake(tpgh_s, c2s, c2s_len), ip, dlen); } ull = sg_get_unaligned_be16(ip + 2); sgj_js_nv_ihex(jsp, jop, snake(tpgi_s, c2s, c2s_len), ull); break; case 6: /* Logical unit group */ if (jsp->pr_hex) { sgj_js_nv_hex_bytes(jsp, jop, snake(lugh_s, c2s, c2s_len), ip, dlen); } ull = sg_get_unaligned_be16(ip + 2); sgj_js_nv_ihex(jsp, jop, snake(lug_s, c2s, c2s_len), ull); break; case 7: /* MD5 logical unit identifier */ sgj_js_nv_hex_bytes(jsp, jop, snake(md5i_s, c2s, c2s_len), ip, dlen); break; case 8: /* SCSI name string (must be UTF-8) */ has_control_char = sg_has_control_char(ip, dlen); if (jsp->verbose) { if (has_control_char) pr2serr("Warning: %s contains ASCII control characters, " "convert to spaces\n", sns_s); if (c_set != 0x3) pr2serr("Warning: %s should be UTF-8 (3) is %d (2 is " "ASCII)\n", sns_s, c_set); } if (jsp->pr_hex) sgj_js_nv_hex_bytes(jsp, jop, snake(snsh_s, c2s, c2s_len), ip, dlen); snprintf(b, blen, "%.*s", dlen, ip); if (has_control_char) { /* replace control chars with space */ for (k = 0; (k < dlen) && b[k]; ++k) { if ((b[k] < 0x20) || (b[k] == 0x7f)) b[k] = ' '; } } else k = dlen; sgj_js_nv_s_len(jsp, jop, snake(sns_s, c2s, c2s_len), b, k); break; case 9: /* Protocol specific port identifier */ if (jsp->pr_hex) { sgj_js_nv_hex_bytes(jsp, jop, snake(pspih_s, c2s, c2s_len), ip, dlen); } if (TPROTO_UAS == p_id) { jo2p = sgj_named_subobject_r(jsp, jop, "usb_target_port_identifier"); sgj_js_nv_ihex(jsp, jo2p, "device_address", 0x7f & ip[0]); sgj_js_nv_ihex(jsp, jo2p, "interface_number", ip[2]); } else if (TPROTO_SOP == p_id) { jo2p = sgj_named_subobject_r(jsp, jop, "pci_express_routing_id"); ull = sg_get_unaligned_be16(ip + 0); sgj_js_nv_ihex(jsp, jo2p, "routing_id", ull); } else { sgj_js_nv_s(jsp, jop, snake(pspi_s, c2s, c2s_len), dfa_s); } break; case 0xa: /* UUID identifier */ if (jsp->pr_hex) { sgj_js_nv_hex_bytes(jsp, jop, snake(uuih_s, c2s, c2s_len), ip, dlen); } sg_t10_uuid_desig2str(ip, dlen, c_set, false, true, NULL, blen, b); n = strlen(b); if ((n > 0) && ('\n' == b[n - 1])) b[n - 1] = '\0'; sgj_js_nv_s(jsp, jop, snake(uui_s, c2s, c2s_len), b); break; default: /* reserved */ sgj_js_nv_hex_bytes(jsp, jop, snake(rdh_s, c2s, c2s_len), ip, dlen); break; } return true; } static void sgj_progress_indication(sgj_state * jsp, sgj_opaque_p jop, uint16_t prog_indic, bool is_another) { uint32_t progress, pr, rem; sgj_opaque_p jo2p; char b[64]; if (is_another) jo2p = sgj_named_subobject_r(jsp, jop, "another_progress_indication"); else jo2p = sgj_named_subobject_r(jsp, jop, "progress_indication"); if (NULL == jo2p) return; progress = prog_indic; sgj_js_nv_i(jsp, jo2p, "i", progress); snprintf(b, sizeof(b), "%x", progress); sgj_js_nv_s(jsp, jo2p, "hex", b); progress *= 100; pr = progress / 65536; rem = (progress % 65536) / 656; snprintf(b, sizeof(b), "%d.02%d%%\n", pr, rem); sgj_js_nv_s(jsp, jo2p, "percentage", b); } static bool sgj_decode_sks(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * dp, int dlen, int sense_key) { switch (sense_key) { case SPC_SK_ILLEGAL_REQUEST: if (dlen < 3) { sgj_js_nv_s(jsp, jop, "illegal_request_sks", dts_s); return false; } sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false, sksv_s); sgj_js_nv_ihex_nex(jsp, jop, "c_d", !! (dp[0] & 0x40), false, "c: cdb; d: data-out"); sgj_js_nv_ihex_nex(jsp, jop, "bpv", !! (dp[0] & 0x8), false, "bit pointer (index) valid"); sgj_js_nv_i(jsp, jop, "bit_pointer", dp[0] & 0x7); sgj_js_nv_ihex(jsp, jop, "field_pointer", sg_get_unaligned_be16(dp + 1)); break; case SPC_SK_HARDWARE_ERROR: case SPC_SK_MEDIUM_ERROR: case SPC_SK_RECOVERED_ERROR: if (dlen < 3) { sgj_js_nv_s(jsp, jop, "actual_retry_count_sks", dts_s); return false; } sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false, sksv_s); sgj_js_nv_ihex(jsp, jop, "actual_retry_count", sg_get_unaligned_be16(dp + 1)); break; case SPC_SK_NO_SENSE: case SPC_SK_NOT_READY: if (dlen < 7) { sgj_js_nv_s(jsp, jop, "progress_indication_sks", dts_s); return false; } sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false, sksv_s); sgj_progress_indication(jsp, jop, sg_get_unaligned_be16(dp + 1), false); break; case SPC_SK_COPY_ABORTED: if (dlen < 7) { sgj_js_nv_s(jsp, jop, "segment_indication_sks", dts_s); return false; } sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false, sksv_s); sgj_js_nv_ihex_nex(jsp, jop, "sd", !! (dp[0] & 0x20), false, "field pointer relative to: 1->segment " "descriptor, 0->parameter list"); sgj_js_nv_ihex_nex(jsp, jop, "bpv", !! (dp[0] & 0x8), false, "bit pointer (index) valid"); sgj_js_nv_i(jsp, jop, "bit_pointer", dp[0] & 0x7); sgj_js_nv_ihex(jsp, jop, "field_pointer", sg_get_unaligned_be16(dp + 1)); break; case SPC_SK_UNIT_ATTENTION: if (dlen < 7) { sgj_js_nv_s(jsp, jop, "segment_indication_sks", dts_s); return false; } sgj_js_nv_ihex_nex(jsp, jop, "sksv", !! (dp[0] & 0x80), false, sksv_s); sgj_js_nv_i(jsp, jop, "overflow", !! (dp[0] & 0x80)); break; default: sgj_js_nv_ihex(jsp, jop, "unexpected_sense_key", sense_key); return false; } return true; } #define TPGS_STATE_OPTIMIZED 0x0 #define TPGS_STATE_NONOPTIMIZED 0x1 #define TPGS_STATE_STANDBY 0x2 #define TPGS_STATE_UNAVAILABLE 0x3 #define TPGS_STATE_OFFLINE 0xe #define TPGS_STATE_TRANSITIONING 0xf static int decode_tpgs_state(int st, char * b, int blen) { switch (st) { case TPGS_STATE_OPTIMIZED: return sg_scnpr(b, blen, "active/optimized"); case TPGS_STATE_NONOPTIMIZED: return sg_scnpr(b, blen, "active/non optimized"); case TPGS_STATE_STANDBY: return sg_scnpr(b, blen, "standby"); case TPGS_STATE_UNAVAILABLE: return sg_scnpr(b, blen, "unavailable"); case TPGS_STATE_OFFLINE: return sg_scnpr(b, blen, "offline"); case TPGS_STATE_TRANSITIONING: return sg_scnpr(b, blen, "transitioning between states"); default: return sg_scnpr(b, blen, "unknown: 0x%x", st); } } static bool sgj_uds_referral_descriptor(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * dp, int alen) { int dlen = alen - 2; int k, j, g, aas; uint64_t ull; const uint8_t * tp; sgj_opaque_p jap, jo2p, ja2p, jo3p; char c[40]; sgj_js_nv_ihex_nex(jsp, jop, "not_all_r", (dp[2] & 0x1), false, "Not all referrals"); dp += 4; jap = sgj_named_subarray_r(jsp, jop, "user_data_segment_referral_descriptor_list"); for (k = 0; (k + 4) < dlen; k += g, dp += g) { int ntpgd = dp[3]; jo2p = sgj_new_unattached_object_r(jsp); g = (ntpgd * 4) + 20; sgj_js_nv_ihex(jsp, jo2p, "number_of_target_port_group_descriptors", ntpgd); if ((k + g) > dlen) { sgj_js_nv_i(jsp, jo2p, "truncated_descriptor_dlen", dlen); sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); return false; } ull = sg_get_unaligned_be64(dp + 4); sgj_js_nv_ihex(jsp, jo2p, "first_user_date_sgment_lba", ull); ull = sg_get_unaligned_be64(dp + 12); sgj_js_nv_ihex(jsp, jo2p, "last_user_date_sgment_lba", ull); ja2p = sgj_named_subarray_r(jsp, jo2p, "target_port_group_descriptor_list"); for (j = 0; j < ntpgd; ++j) { jo3p = sgj_new_unattached_object_r(jsp); tp = dp + 20 + (j * 4); aas = tp[0] & 0xf; decode_tpgs_state(aas, c, sizeof(c)); sgj_js_nv_ihexstr(jsp, jo3p, "asymmetric_access_state", aas, NULL, c); sgj_js_nv_ihex(jsp, jo3p, "target_port_group", sg_get_unaligned_be16(tp + 2)); sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); } sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); } return true; } /* Copy of static array in sg_lib.c */ static const char * dd_usage_reason_str_arr[] = { "Unknown", "resend this and further commands to:", "resend this command to:", "new subsidiary lu added to this administrative lu:", "administrative lu associated with a preferred binding:", }; static bool sgj_js_sense_descriptors(sgj_state * jsp, sgj_opaque_p jop, const struct sg_scsi_sense_hdr * sshp, const uint8_t * sbp, int sb_len) { bool processed = true; int add_sb_len, desc_len, k, dt, sense_key, n, sds; uint16_t sct_sc; uint64_t ull; const uint8_t * descp; const char * cp; sgj_opaque_p jap, jo2p, jo3p; char b[80]; static const int blen = sizeof(b); static const char * parsing = "parsing_error"; add_sb_len = sshp->additional_length; add_sb_len = (add_sb_len < sb_len) ? add_sb_len : sb_len; sense_key = sshp->sense_key; jap = sgj_named_subarray_r(jsp, jop, "sense_data_descriptor_list"); for (descp = sbp, k = 0; (k < add_sb_len); k += desc_len, descp += desc_len) { int add_d_len = (k < (add_sb_len - 1)) ? descp[1] : -1; jo2p = sgj_new_unattached_object_r(jsp); if ((k + add_d_len + 2) > add_sb_len) add_d_len = add_sb_len - k - 2; desc_len = add_d_len + 2; processed = true; dt = descp[0]; switch (dt) { case 0: sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Information"); if (add_d_len >= 10) { int valid = !! (0x80 & descp[2]); sgj_js_nv_ihexstr(jsp, jo2p, "valid", valid, NULL, valid ? "as per T10" : "Vendor specific"); sgj_js_nv_ihex(jsp, jo2p, "information", sg_get_unaligned_be64(descp + 4)); } else { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; } break; case 1: sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Command specific"); if (add_d_len >= 10) { sgj_js_nv_ihex(jsp, jo2p, "command_specific_information", sg_get_unaligned_be64(descp + 4)); } else { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; } break; case 2: /* Sense Key Specific */ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Sense key specific"); processed = sgj_decode_sks(jsp, jo2p, descp + 4, desc_len - 4, sense_key); break; case 3: sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Field replaceable unit code"); if (add_d_len >= 2) sgj_js_nv_ihex(jsp, jo2p, "field_replaceable_unit_code", descp[3]); else { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; } break; case 4: sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Stream commands"); if (add_d_len >= 2) { sgj_js_nv_i(jsp, jo2p, "filemark", !! (descp[3] & 0x80)); sgj_js_nv_ihex_nex(jsp, jo2p, "eom", !! (descp[3] & 0x40), false, "End Of Medium"); sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (descp[3] & 0x20), false, "Incorrect Length Indicator"); } else { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; } break; case 5: sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Block commands"); if (add_d_len >= 2) sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (descp[3] & 0x20), false, "Incorrect Length Indicator"); else { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; } break; case 6: sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "OSD object identification"); sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported"); processed = false; break; case 7: sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "OSD response integrity check value"); sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported"); break; case 8: sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "OSD attribute identification"); sgj_js_nv_s(jsp, jo2p, parsing, "Unsupported"); processed = false; break; case 9: /* this is defined in SAT (SAT-2) */ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "ATA status return"); if (add_d_len >= 12) { sgj_js_nv_i(jsp, jo2p, "extend", !! (descp[2] & 1)); sgj_js_nv_ihex(jsp, jo2p, "error", descp[3]); sgj_js_nv_ihex(jsp, jo2p, "count", sg_get_unaligned_be16(descp + 4)); ull = ((uint64_t)descp[10] << 40) | ((uint64_t)descp[8] << 32) | (descp[6] << 24) | (descp[11] << 16) | (descp[9] << 8) | descp[7]; sgj_js_nv_ihex(jsp, jo2p, "lba", ull); sgj_js_nv_ihex(jsp, jo2p, "device", descp[12]); sgj_js_nv_ihex(jsp, jo2p, "status", descp[13]); } else { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; } break; case 0xa: /* Added in SPC-4 rev 17, became 'Another ...' in rev 34 */ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Another progress indication"); if (add_d_len < 6) { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; break; } sgj_js_nv_ihex(jsp, jo2p, "another_sense_key", descp[2]); sgj_js_nv_ihex(jsp, jo2p, "another_additional_sense_code", descp[3]); sgj_js_nv_ihex(jsp, jo2p, "another_additional_sense_code_qualifier", descp[4]); sgj_progress_indication(jsp, jo2p, sg_get_unaligned_be16(descp + 6), true); break; case 0xb: /* Added in SPC-4 rev 23, defined in SBC-3 rev 22 */ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "User data segment referral"); if (add_d_len < 2) { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; break; } if (! sgj_uds_referral_descriptor(jsp, jo2p, descp, add_d_len)) { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; } break; case 0xc: /* Added in SPC-4 rev 28 */ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Forwarded sense data"); if (add_d_len < 2) { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; break; } sgj_js_nv_ihex_nex(jsp, jo2p, "fsdt", !! (0x80 & descp[2]), false, "Forwarded Sense Data Truncated"); sds = (0x7 & descp[2]); if (sds < 1) snprintf(b, blen, "%s [%d]", "Unknown", sds); else if (sds > 9) snprintf(b, blen, "%s [%d]", "Reserved", sds); else { n = sg_scnpr(b, blen, "EXTENDED COPY command copy %s", (sds == 1) ? "source" : "destination"); if (sds > 1) sg_scn3pr(b, blen, n, " %d", sds - 1); } sgj_js_nv_ihexstr(jsp, jo2p, "sense_data_source", (0x7 & descp[2]), NULL, b); jo3p = sgj_named_subobject_r(jsp, jo2p, "forwarded_sense_data"); sgj_js_sense(jsp, jo3p, descp + 4, desc_len - 4); break; case 0xd: /* Added in SBC-3 rev 36d */ /* this descriptor combines descriptors 0, 1, 2 and 3 */ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Direct-access block device"); if (add_d_len < 28) { sgj_js_nv_s(jsp, jo2p, parsing, dts_s); processed = false; break; } sgj_js_nv_i(jsp, jo2p, "valid", (0x80 & descp[2])); sgj_js_nv_ihex_nex(jsp, jo2p, "ili", !! (0x20 & descp[2]), false, "Incorrect Length Indicator"); processed = sgj_decode_sks(jsp, jo2p, descp + 4, desc_len - 4, sense_key); sgj_js_nv_ihex(jsp, jo2p, "field_replaceable_unit_code", descp[7]); sgj_js_nv_ihex(jsp, jo2p, "information", sg_get_unaligned_be64(descp + 8)); sgj_js_nv_ihex(jsp, jo2p, "command_specific_information", sg_get_unaligned_be64(descp + 16)); break; case 0xe: /* Added in SPC-5 rev 6 (for Bind/Unbind) */ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Device designation"); n = descp[3]; cp = (n < (int)SG_ARRAY_SIZE(dd_usage_reason_str_arr)) ? dd_usage_reason_str_arr[n] : "Unknown (reserved)"; sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_usage_reason", n, NULL, cp); jo3p = sgj_named_subobject_r(jsp, jo2p, "device_designation_descriptor"); sgj_haj_designation_descriptor(jsp, jo3p, 2, true, descp + 4, desc_len - 4); break; case 0xf: /* Added in SPC-5 rev 10 (for Write buffer) */ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "Microcode activation"); if (add_d_len < 6) { sgj_js_nv_s(jsp, jop, parsing, dts_s); processed = false; break; } sgj_js_nv_ihex(jsp, jo2p, "microcode_activation_time", sg_get_unaligned_be16(descp + 6)); break; case 0xde: /* NVME Status Field; vendor (sg3_utils) specific */ sgj_js_nv_ihexstr(jsp, jo2p, "descriptor_type", dt, NULL, "NVME status (sg3_utils)"); if (add_d_len < 6) { sgj_js_nv_s(jsp, jop, parsing, dts_s); processed = false; break; } sgj_js_nv_ihex_nex(jsp, jo2p, "dnr", !! (0x80 & descp[5]), false, "Do not retry"); sgj_js_nv_ihex_nex(jsp, jo2p, "m", !! (0x40 & descp[5]), false, "More"); sct_sc = sg_get_unaligned_be16(descp + 6); sgj_js_nv_ihexstr_nex (jsp, jo2p, "sct_sc", sct_sc, true, NULL, sg_get_nvme_cmd_status_str(sct_sc, blen, b), "Status Code Type (upper 8 bits) and Status Code"); break; default: if (dt >= 0x80) sgj_js_nv_ihex(jsp, jo2p, "vendor_specific_descriptor_type", dt); else sgj_js_nv_ihex(jsp, jo2p, "unknown_descriptor_type", dt); sgj_js_nv_hex_bytes(jsp, jo2p, "descriptor_hexbytes", descp, desc_len); processed = false; break; } sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); } return processed; } #define ASCQ_ATA_PT_INFO_AVAILABLE 0x1d /* corresponding ASC is 0 */ /* Fetch sense information */ bool sgj_js_sense(sgj_state * jsp, sgj_opaque_p jop, const uint8_t * sbp, int sb_len) { bool descriptor_format = false; bool sdat_ovfl = false; bool ret = true; bool valid_info_fld; int len, n; uint32_t info; uint8_t resp_code; const char * ebp = NULL; char ebuff[64]; char b[256]; struct sg_scsi_sense_hdr ssh; static int blen = sizeof(b); static int elen = sizeof(ebuff); if ((NULL == sbp) || (sb_len < 1)) { snprintf(ebuff, elen, "sense buffer empty\n"); ebp = ebuff; ret = false; goto fini; } resp_code = 0x7f & sbp[0]; valid_info_fld = !!(sbp[0] & 0x80); len = sb_len; if (! sg_scsi_normalize_sense(sbp, sb_len, &ssh)) { ebp = "unable to normalize sense buffer"; ret = false; goto fini; } /* We have been able to normalize the sense buffer */ switch (resp_code) { case 0x70: /* fixed, current */ ebp = "Fixed format, current"; len = (sb_len > 7) ? (sbp[7] + 8) : sb_len; len = (len > sb_len) ? sb_len : len; sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false; break; case 0x71: /* fixed, deferred */ /* error related to a previous command */ ebp = "Fixed format, <<<deferred>>>"; len = (sb_len > 7) ? (sbp[7] + 8) : sb_len; len = (len > sb_len) ? sb_len : len; sdat_ovfl = (len > 2) ? !!(sbp[2] & 0x10) : false; break; case 0x72: /* descriptor, current */ descriptor_format = true; ebp = "Descriptor format, current"; sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false; break; case 0x73: /* descriptor, deferred */ descriptor_format = true; ebp = "Descriptor format, <<<deferred>>>"; sdat_ovfl = (sb_len > 4) ? !!(sbp[4] & 0x80) : false; break; default: sg_scnpr(ebuff, elen, "Unknown code: 0x%x", resp_code); ebp = ebuff; break; } sgj_js_nv_ihexstr(jsp, jop, "response_code", resp_code, NULL, ebp); sgj_js_nv_b(jsp, jop, "descriptor_format", descriptor_format); sgj_js_nv_ihex_nex(jsp, jop, "sdat_ovfl", sdat_ovfl, false, "Sense data overflow"); sgj_js_nv_ihexstr(jsp, jop, "sense_key", ssh.sense_key, NULL, sg_lib_sense_key_desc[ssh.sense_key]); sgj_js_nv_ihex(jsp, jop, "additional_sense_code", ssh.asc); sgj_js_nv_ihex(jsp, jop, "additional_sense_code_qualifier", ssh.ascq); sgj_js_nv_s(jsp, jop, "additional_sense_str", sg_get_additional_sense_str(ssh.asc, ssh.ascq, false, blen, b)); if (descriptor_format) { if (len > 8) { ret = sgj_js_sense_descriptors(jsp, jop, &ssh, sbp + 8, len - 8); if (ret == false) { ebp = "unable to decode sense descriptor"; goto fini; } } } else if ((len > 12) && (0 == ssh.asc) && (ASCQ_ATA_PT_INFO_AVAILABLE == ssh.ascq)) { /* SAT ATA PASS-THROUGH fixed format */ sgj_js_nv_ihex(jsp, jop, "error", sbp[3]); sgj_js_nv_ihex(jsp, jop, "status", sbp[4]); sgj_js_nv_ihex(jsp, jop, "device", sbp[5]); sgj_js_nv_i(jsp, jop, "extend", !! (0x80 & sbp[8])); sgj_js_nv_i(jsp, jop, "count_upper_nonzero", !! (0x40 & sbp[8])); sgj_js_nv_i(jsp, jop, "lba_upper_nonzero", !! (0x20 & sbp[8])); sgj_js_nv_i(jsp, jop, "log_index", (0xf & sbp[8])); sgj_js_nv_i(jsp, jop, "lba", sg_get_unaligned_le24(sbp + 9)); } else if (len > 2) { /* fixed format */ sgj_js_nv_i(jsp, jop, "valid", valid_info_fld); sgj_js_nv_i(jsp, jop, "filemark", !! (sbp[2] & 0x80)); sgj_js_nv_ihex_nex(jsp, jop, "eom", !! (sbp[2] & 0x40), false, "End Of Medium"); sgj_js_nv_ihex_nex(jsp, jop, "ili", !! (sbp[2] & 0x20), false, "Incorrect Length Indicator"); info = sg_get_unaligned_be32(sbp + 3); sgj_js_nv_ihex(jsp, jop, "information", info); sgj_js_nv_ihex(jsp, jop, "additional_sense_length", sbp[7]); if (sb_len > 11) { info = sg_get_unaligned_be32(sbp + 8); sgj_js_nv_ihex(jsp, jop, "command_specific_information", info); } if (sb_len > 14) sgj_js_nv_ihex(jsp, jop, "field_replaceable_unit_code", sbp[14]); if (sb_len > 17) sgj_decode_sks(jsp, jop, sbp + 15, sb_len - 15, ssh.sense_key); n = sbp[7]; n = (sb_len > n) ? n : sb_len; sgj_js_nv_ihex(jsp, jop, "number_of_bytes_beyond_18", (n > 18) ? n - 18 : 0); } else { snprintf(ebuff, sizeof(ebuff), "sb_len=%d %s", sb_len, ts_s); ebp = ebuff; ret = false; } fini: if ((! ret) && ebp) sgj_js_nv_s(jsp, jop, "sense_decode_error", ebp); return ret; } /* Converts exit_status into a string and hands them both to * sgj_js2file_estr(). fp assumed to be valid (usually pointing to stdout. */ void sgj_js2file(sgj_state * jsp, sgj_opaque_p jop, int exit_status, FILE * fp) { const char * estr = NULL; char d[128]; static const int dlen = sizeof(d); if (sg_exit2str(exit_status, jsp->verbose, dlen, d)) { if (strlen(d) > 0) estr = d; } sgj_js2file_estr(jsp, jop, exit_status, estr, fp); }