aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2022-05-24 21:13:26 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2022-05-24 21:13:26 +0000
commit6ff667a8941417f14c1dff8e60564c15ec8eb7e0 (patch)
tree758a10605b283d506264f769eed73236797e2bf2
parent404cd7cc68c42c49076623e3ec13bb1f42f74ec7 (diff)
downloadsg3_utils-6ff667a8941417f14c1dff8e60564c15ec8eb7e0.tar.gz
sg_get_elem_status: change '--maxlen=' option default to 1056 (was 32), other cleanups
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@953 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--ChangeLog7
-rw-r--r--doc/sg_get_elem_status.867
-rw-r--r--doc/sg_rep_zones.814
-rw-r--r--include/sg_pr2serr.h20
-rw-r--r--inhex/get_elem_status.hex42
-rw-r--r--lib/sg_pr2serr.c55
-rw-r--r--src/sg_get_elem_status.c254
-rw-r--r--src/sg_get_lba_status.c7
-rw-r--r--src/sg_raw.c1
9 files changed, 284 insertions, 183 deletions
diff --git a/ChangeLog b/ChangeLog
index f1290f82..3dfa9804 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,8 @@ Each utility has its own version number, date of last change and
some description at the top of its ".c" file. All utilities in the main
directory have their own "man" pages. There is also a sg3_utils man page.
-Changelog for pre-release sg3_utils-1.48 [20220512] [svn: r952]
+Changelog for pre-release sg3_utils-1.48 [20220524] [svn: r953]
+ - some utilities: add experimental --json[=JO] option
- sg_z_act_query: new utility for sending either a
Zone activate or Zone query command
- sg_rep_density: new utility for decoding the response of
@@ -25,7 +26,8 @@ Changelog for pre-release sg3_utils-1.48 [20220512] [svn: r952]
- add --find ZT option to find the first occurrence of
ZT; if ZT prefixed by - or ! find first not equal to ZT
- add --statistics option
- - add experimental --json[=JO] option and generation
+ - sg_get_elem_status: change '--maxlen=' option default to
+ 1056 (header plus 32 physical element status descriptors)
- sg_decode_sense: add --nodecode option
- sg_logs: tweak the meaning of --list option to more closely
reflect the contents of log pages 0x0 and 0x0,0xff
@@ -44,7 +46,6 @@ Changelog for pre-release sg3_utils-1.48 [20220512] [svn: r952]
- add support for Hitachi/HP open-v ldev names
- sg_opcodes: cleanup error reporting
- add --inhex=FN to process earlier -HHH
- - add experimental --json[=JO] option and generation
- sg_format: allow disk formats on ZBC (zoned) disks
- sg_read_buffer: add --eh_code= and --no_output options
- sg_ses: add exp_sas_addr acronym for getting expander's
diff --git a/doc/sg_get_elem_status.8 b/doc/sg_get_elem_status.8
index d00c9ec7..df5f0c61 100644
--- a/doc/sg_get_elem_status.8
+++ b/doc/sg_get_elem_status.8
@@ -1,22 +1,32 @@
-.TH SG_GET_ELEM_STATUS "8" "August 2021" "sg3_utils\-1.47" SG3_UTILS
+.TH SG_GET_ELEM_STATUS "8" "May 2022" "sg3_utils\-1.48" SG3_UTILS
.SH NAME
sg_get_elem_status \- send SCSI GET PHYSICAL ELEMENT STATUS command
.SH SYNOPSIS
.B sg_get_elem_status
[\fI\-\-brief\fR] [\fI\-\-filter=FLT\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
-[\fI\-\-inhex=FN\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-raw\fR]
-[\fI\-\-readonly\fR] [\fI\-\-report\-type=RT\fR] [\fI\-\-starting=ELEM\fR]
-[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+[\fI\-\-inhex=FN\fR] [\fI\-\-json[=JO\fR]] [\fI\-\-maxlen=LEN\fR]
+[\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-report\-type=RT\fR]
+[\fI\-\-starting=ELEM\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
+\fIDEVICE\fR
.SH DESCRIPTION
.\" Add any additional description here
.PP
Send the SCSI GET PHYSICAL ELEMENT STATUS command to the \fIDEVICE\fR and
-output the response. The command was introduced in (draft) SBC\-4 revision
+output the response. That command was introduced in (draft) SBC\-4 revision
16.
.PP
-The default action is to decode the response into one physical element
-status descriptor per line then output a header and the status descriptors
-to stdout. The amount of output can be reduced by the \fI\-\-brief\fR option.
+T10 drafts now speak on both physical and storage elements. The latter term
+is more specific and refers to some part of the device that controls storage
+and may or may not be addressable with LBAs. An example of storage elements
+is the heads on a spinning hard disk, with each head being associated with a
+different storage element identifier. When a storage element has been
+"depopulated" its former storage accessed by LBAs are no longer available.
+Physical elements are more general and includes storage elements and might
+include disk storage used for "saved" mode page settings amongst other things.
+.PP
+The default action is to decode the response into a header and up to 32
+physical element status descriptors. The status descriptors are output one
+per line. The amount of output can be reduced by the \fI\-\-brief\fR option.
.PP
Rather than send this SCSI command to \fIDEVICE\fR, if the \fI\-\-inhex=FN\fR
option is given, then the contents of the file named \fIFN\fR are decoded
@@ -26,7 +36,11 @@ as if it was the response of this command.
Arguments to long options are mandatory for short options as well.
.TP
\fB\-b\fR, \fB\-\-brief\fR
-tbd
+when used once, the output of each physical element status descriptor is
+reduced to: <element_id>: <element_type>,<element_health> . All three are
+output as decimal integers. When used twice the "Element descriptors:"
+lines introducing the status descriptors is not output. When used three
+or more times only the response header is output.
.TP
\fB\-f\fR, \fB\-\-filter\fR=\fIFLT\fR
where \fIFLT\fR is placed in a two bit field called FILTER in the GET
@@ -40,7 +54,14 @@ the number of element descriptors returned. The default value is zero.
output the usage message then exit.
.TP
\fB\-H\fR, \fB\-\-hex\fR
-output response to this command in ASCII hex.
+output response to this command in ASCII hex. Each line of 16 bytes is
+preceded by an address or index, starting at 0 and the address is also in
+hex. If given twice then an ASCII rendering of each byte is appended to
+the line output. If given three or more times then only the ASCII hex
+of each byte is output, 16 bytes per line (i.e. so no leading address nor
+trailing ASCII rendering). This latter form is suitable for placing in
+a file and being used with the \fI\-\-inhex=FN\fR option in a later
+invocation.
.TP
\fB\-i\fR, \fB\-\-inhex\fR=\fIFN\fR
where \fIFN\fR is a file name whose contents are assumed to be ASCII
@@ -50,11 +71,16 @@ a warning is issued and the utility continues, decoding the file named
sg3_utils manpage for more information. If the \fI\-\-raw\fR option is
also given then the contents of \fIFN\fR are treated as binary.
.TP
+\fB\-j\fR, \fB\-\-json[\fR=\fIJO\fR]
+output is in JSON format instead of human readable form. This is an
+EXPERIMENTAL feature; more information to follow.
+.TP
\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
where \fILEN\fR is the (maximum) response length in bytes. It is placed in
-the cdb's "allocation length" field. If not given then 32 is used. 32 is
-enough space for the response header only.
-\fILEN\fR should be a multiple of 32 (e.g. 32, 64, and 96 are suitable).
+the cdb's "allocation length" field. If not given then 1056 is used. 1056 is
+enough space for the response header and 32 physical element status
+descriptors. \fILEN\fR should be a multiple of 32 (e.g. 32, 64, and 96 are
+suitable).
.TP
\fB\-r\fR, \fB\-\-raw\fR
output response in binary (to stdout) unless the \fI\-\-inhex=FN\fR option
@@ -85,6 +111,19 @@ caused by this option is sent to stderr.
.TP
\fB\-V\fR, \fB\-\-version\fR
print the version string and then exit.
+.SH NOTES
+The "Warning - physical element status change" additional sense
+code [0xb,0x14] is special and should prompt an application client to call
+this SCSI command. How this is triggered depends on the setting in the
+Informational Exceptions Control mode page [0xc, 0x0]. Reports about hard
+disks reporting this warning suggest that the following invocation of
+this utility should use \fI\-\-filter=1 \-\-report\-type=1\fR to try and
+locate an "out\-of\-spec" _storage_ element.
+.PP
+After detecting one or more out\-of\-spec storage elements the disk in
+question should either be decommissioned or have the REMOVE ELEMENT AND
+TRUNCATE (or ... AND MODIFY ZONES) command invoked to repair (and reduce
+the storge capacity) of the disk.
.SH EXIT STATUS
The exit status of sg_get_elem_status is 0 when it is successful. Otherwise
see the sg3_utils(8) man page.
@@ -93,7 +132,7 @@ Written by Douglas Gilbert.
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2019\-2021 Douglas Gilbert
+Copyright \(co 2019\-2022 Douglas Gilbert
.br
This software is distributed under a FreeBSD license. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/doc/sg_rep_zones.8 b/doc/sg_rep_zones.8
index 0f76ee4d..312bb611 100644
--- a/doc/sg_rep_zones.8
+++ b/doc/sg_rep_zones.8
@@ -4,11 +4,11 @@ sg_rep_zones \- send SCSI REPORT ZONES, REALMS or ZONE DOMAINS command
.SH SYNOPSIS
.B sg_rep_zones
[\fI\-\-brief\fR] [\fI\-\-domain\fR] [\fI\-\-find=ZT\fR] [\fI\-\-force\fR]
-[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-inhex=FN\fR] [\fI\-\-locator=LBA\fR]
-[\fI\-\-maxlen=LEN\fR] [\fI\-\-num=NUM\fR] [\fI\-\-partial\fR] [\fI\-\-raw\fR]
-[\fI\-\-readonly\fR] [\fI\-\-realm\fR] [\fI\-\-report=OPT\fR]
-[\fI\-\-start=LBA\fR] [\fI\-\-statistics\fR] [\fI\-\-verbose\fR]
-[\fI\-\-version\fR] [\fI\-\-wp\fR] \fIDEVICE\fR
+[\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-inhex=FN\fR] [\fI\-\-json[=JO\fR]]
+[\fI\-\-locator=LBA\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-num=NUM\fR]
+[\fI\-\-partial\fR] [\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-realm\fR]
+[\fI\-\-report=OPT\fR] [\fI\-\-start=LBA\fR] [\fI\-\-statistics\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fI\-\-wp\fR] \fIDEVICE\fR
.SH DESCRIPTION
.\" Add any additional description here
.PP
@@ -102,6 +102,10 @@ Note that by default this utility assumes then contents are the response
from a REPORT ZONES command. Use the \fI\-\-domain\fR or \fI\-\-realm\fR
option for decoding the other two commands.
.TP
+\fB\-j\fR, \fB\-\-json[\fR=\fIJO\fR]
+output is in JSON format instead of human readable form. This is an
+EXPERIMENTAL feature; more information to follow.
+.TP
\fB\-l\fR, \fB\-\-locator\fR=\fILBA\fR
where \fILBA\fR plays a similar role as it does in \fI\-\-start=LBA\fR.
It is the field name used in the REPORT REALMS and REPORT ZONE DOMAINS
diff --git a/include/sg_pr2serr.h b/include/sg_pr2serr.h
index d5a4809c..a7a09f86 100644
--- a/include/sg_pr2serr.h
+++ b/include/sg_pr2serr.h
@@ -66,12 +66,15 @@ typedef void * sgj_opaque_p;
* from the argument given to --json= . If there is no argument then
* they initialized as shown. */
typedef struct sgj_state_t {
+ /* the following set by default, the SG3_UTILS_JSON_OPTS envirinment
+ * variable or command line argument to --json option, in that order. */
bool pr_as_json; /* = false */
- bool pr_hex; /* 'h' (def: true) */
+ bool pr_exit_status; /* 'e' (def: true) */
+ bool pr_hex; /* 'h' (def: false) */
bool pr_leadin; /* 'l' (def: true) */
bool pr_output; /* 'o' (def: false) */
bool pr_pretty; /* 'p' (def: true) */
- bool pr_trailer; /* 't' (def: true) */
+ bool pr_string; /* 's' (def: true) */
char pr_format; /* (def: '\0') */
int pr_indent_size; /* digit (def: 4) */
int verbose; /* 'v' (def: 0) incremented each appearance */
@@ -220,18 +223,19 @@ void sgj_add_name_pair_ihex(sgj_state * jsp, sgj_opaque_p jop,
/* This function only produces JSON output if jsp is non-NULL and
* jsp->pr_as_json is true. It adds a named object at 'jop' (or jop->basep
- * if jop is NULL) along with a value that has two sub-objects. One is
- * named "i" with 'val_i' rendered as a JSON integer. The other is named
- * 'str_name' (or "string" if that is NULL) with 'val_s' randered as a
- * JSON string. */
+ * if jop is NULL) along with a value. If jsp->pr_string is true then that
+ * value is two sub-objects, one named 'i' with a 'val_i' as a JSON integer,
+ * the other one named str_name with val_s rendered as a JSON string.
+ * If jsp->pr_string is false the there are no sub-objects and the 'val_i' is
+ * rendered as JSON integer. */
void sgj_add_name_pair_istr(sgj_state * jsp, sgj_opaque_p jop,
const char * name, int64_t val_i,
const char * str_name, const char * val_s);
/* Nothing in the in-core JSON tree is actually printed to 'fp' (typically
* stdout) until this call is made. If jsp is NULL, jsp->pr_as_json is false
- * or jsp->basep is NULL then this function does nothing. If jsp->trailer is
- * true then a new JSON object named "exit_status" and the 'exit_status'
+ * or jsp->basep is NULL then this function does nothing. If jsp->exit_status
+ * is true then a new JSON object named "exit_status" and the 'exit_status'
* value rendered as a JSON integer is appended to jsp->basep. The in-core
* JSON tree with jsp->basep as its root is streamed to 'fp'. */
void sgj_pr2file(sgj_state * jsp, sgj_opaque_p jop, int exit_status,
diff --git a/inhex/get_elem_status.hex b/inhex/get_elem_status.hex
index 7db83c81..72154455 100644
--- a/inhex/get_elem_status.hex
+++ b/inhex/get_elem_status.hex
@@ -1,35 +1,9 @@
-# To test sg_get_elem_status with dummy response to the SCSI
-# GET PHYSICAL ELEMENT SATUS command
-
-0 0 0 4
-0 0 0 2
-0 0 0 2
-
-0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-
-# first descriptor
-0 0 0 0
-0 0 0 1
-
-0 0 0 0 0
-0
-1
-2c
-0 0 0 0 11 22 33 0
-
-0 0 0 0 0 0 0 0
-
-# second descriptor
-0 0 0 0
-0 0 0 2
-
-0 0 0 0 0
-1
-1
-fe
-ff ff ff ff ff ff ff ff
-
-0 0 0 0 0 0 0 0
-
-
+#
+# This is a real response to the SCSI GET PHYSICAL ELEMENT STATUS command.
+# sg_get_elem_status --filter=1 --maxlen=1k --report-type=1 -HHH <dev>
+
+00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 0a 00 00 00 00 00 00 01 65
+ff ff ff ff ff ff ff ff 00 00 00 00 00 00 00 00
diff --git a/lib/sg_pr2serr.c b/lib/sg_pr2serr.c
index 8acfa278..5c869aee 100644
--- a/lib/sg_pr2serr.c
+++ b/lib/sg_pr2serr.c
@@ -18,6 +18,9 @@
#include "sg_pr2serr.h"
#include "sg_json_builder.h"
+
+#define sgj_opts_ev "SG3_UTILS_JSON_OPTS"
+
/*
* #define json_serialize_mode_multiline 0
* #define json_serialize_mode_single_line 1
@@ -74,25 +77,24 @@ sgj_parse_opts(sgj_state * jsp, const char * j_optarg)
bool negate;
int k, c;
- for (k = 0; j_optarg[k]; ++k) {
+ for (k = 0; j_optarg[k]; ++k) { /* step over leading whitespace */
+ if (! isspace(j_optarg[k]))
+ break;
+ }
+ for ( ; j_optarg[k]; ++k) {
c = j_optarg[k];
negate = false;
switch (c) {
case '=':
- case ' ':
if (0 == k)
- break; /* allow and ignore leading '=' or ' ' */
+ break; /* allow and ignore leading '=' */
bad_arg = true;
if (0 == jsp->first_bad_char)
jsp->first_bad_char = c;
break;
case '!':
- negate = true;
- break;
case '~':
- negate = true;
- break;
- case 'N':
+ case '-': /* '-' is probably most practical negation symbol */
negate = true;
break;
case '0':
@@ -108,6 +110,9 @@ sgj_parse_opts(sgj_state * jsp, const char * j_optarg)
case '8':
jsp->pr_indent_size = 8;
break;
+ case 'e':
+ jsp->pr_exit_status = ! prev_negate;
+ break;
case 'g':
jsp->pr_format = 'g';
break;
@@ -123,8 +128,8 @@ sgj_parse_opts(sgj_state * jsp, const char * j_optarg)
case 'p':
jsp->pr_pretty = ! prev_negate;
break;
- case 't':
- jsp->pr_trailer = ! prev_negate;
+ case 's':
+ jsp->pr_string = ! prev_negate;
break;
case 'v':
++jsp->verbose;
@@ -143,23 +148,40 @@ sgj_parse_opts(sgj_state * jsp, const char * j_optarg)
return ! bad_arg;
}
-bool
-sgj_init_state(sgj_state * jsp, const char * j_optarg)
+static void
+sgj_def_opts(sgj_state * jsp)
{
jsp->pr_as_json = true;
+ jsp->pr_exit_status = true;
jsp->pr_hex = false;
jsp->pr_leadin = true;
jsp->pr_output = false;
jsp->pr_pretty = true;
- jsp->pr_trailer = true;
+ jsp->pr_string = true;
jsp->pr_format = 0;
jsp->first_bad_char = 0;
jsp->verbose = 0;
jsp->pr_indent_size = 4;
+}
+
+bool
+sgj_init_state(sgj_state * jsp, const char * j_optarg)
+{
+ const char * cp;
+
+ sgj_def_opts(jsp);
jsp->basep = NULL;
jsp->outputp = NULL;
jsp->userp = NULL;
+ cp = getenv(sgj_opts_ev);
+ if (cp) {
+ if (! sgj_parse_opts(jsp, cp)) {
+ pr2ws("error parsing %s environment variable, ignore\n",
+ sgj_opts_ev);
+ sgj_def_opts(jsp);
+ }
+ }
return j_optarg ? sgj_parse_opts(jsp, j_optarg) : true;
}
@@ -229,7 +251,7 @@ sgj_pr2file(sgj_state * jsp, sgj_opaque_p jop, int exit_status, FILE * fp)
fprintf(fp, "%s: all NULL pointers ??\n", __func__);
return;
}
- if ((NULL == jop) && jsp->pr_leadin)
+ if ((NULL == jop) && jsp->pr_exit_status)
json_object_push(jvp, "exit_status", json_integer_new(exit_status));
memcpy(&out_settings, &def_out_settings, sizeof(out_settings));
@@ -437,7 +459,7 @@ sgj_add_name_pair_istr(sgj_state * jsp, sgj_opaque_p jop,
{
if ((NULL == jsp) || (! jsp->pr_as_json))
return;
- else {
+ else if (jsp->pr_string) {
sgj_opaque_p jo2p =
sgj_new_named_object(jsp, (jop ? jop : jsp->basep), name);
if (NULL == jo2p)
@@ -445,7 +467,8 @@ sgj_add_name_pair_istr(sgj_state * jsp, sgj_opaque_p jop,
sgj_add_val_i(jsp, jo2p, "i", (int64_t)value);
if (str)
sgj_add_val_s(jsp, jo2p, str_name ? str_name : "string", str);
- }
+ } else
+ sgj_add_val_i(jsp, jop, name, value);
}
/* Returns number of characters placed in 'out' excluding trailing NULL */
diff --git a/src/sg_get_elem_status.c b/src/sg_get_elem_status.c
index ea5b7d0c..bafb7028 100644
--- a/src/sg_get_elem_status.c
+++ b/src/sg_get_elem_status.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019-2021 Douglas Gilbert.
+ * Copyright (c) 2019-2022 Douglas Gilbert.
* All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the BSD_LICENSE file.
@@ -37,7 +37,9 @@
* given SCSI device.
*/
-static const char * version_str = "1.08 20211114"; /* sbc5r01 */
+static const char * version_str = "1.09 20220524"; /* sbc5r01 */
+
+#define MY_NAME "sg_get_elem_status"
#ifndef UINT32_MAX
@@ -45,7 +47,7 @@ static const char * version_str = "1.08 20211114"; /* sbc5r01 */
#endif
#define GET_PHY_ELEM_STATUS_SA 0x17
-#define DEF_GPES_BUFF_LEN 32
+#define DEF_GPES_BUFF_LEN (1024 + 32)
#define MAX_GPES_BUFF_LEN ((1024 * 1024) + DEF_GPES_BUFF_LEN)
#define GPES_DESC_OFFSET 32 /* descriptors starts at this byte offset */
#define GPES_DESC_LEN 32
@@ -66,21 +68,22 @@ static uint8_t gpesBuff[DEF_GPES_BUFF_LEN];
static struct option long_options[] = {
- {"brief", no_argument, 0, 'b'},
- {"filter", required_argument, 0, 'f'},
- {"help", no_argument, 0, 'h'},
- {"hex", no_argument, 0, 'H'},
- {"in", required_argument, 0, 'i'}, /* silent, same as --inhex= */
- {"inhex", required_argument, 0, 'i'},
- {"maxlen", required_argument, 0, 'm'},
- {"raw", no_argument, 0, 'r'},
- {"readonly", no_argument, 0, 'R'},
- {"report-type", required_argument, 0, 't'},
- {"report_type", required_argument, 0, 't'},
- {"starting", required_argument, 0, 's'},
- {"verbose", no_argument, 0, 'v'},
- {"version", no_argument, 0, 'V'},
- {0, 0, 0, 0},
+ {"brief", no_argument, 0, 'b'},
+ {"filter", required_argument, 0, 'f'},
+ {"help", no_argument, 0, 'h'},
+ {"hex", no_argument, 0, 'H'},
+ {"in", required_argument, 0, 'i'}, /* silent, same as --inhex= */
+ {"inhex", required_argument, 0, 'i'},
+ {"json", optional_argument, 0, 'j'},
+ {"maxlen", required_argument, 0, 'm'},
+ {"raw", no_argument, 0, 'r'},
+ {"readonly", no_argument, 0, 'R'},
+ {"report-type", required_argument, 0, 't'},
+ {"report_type", required_argument, 0, 't'},
+ {"starting", required_argument, 0, 's'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0},
};
static void
@@ -88,11 +91,13 @@ usage()
{
pr2serr("Usage: sg_get_elem_status [--brief] [--filter=FLT] [--help] "
"[--hex]\n"
- " [--inhex=FN] [--maxlen=LEN] [--raw] "
- "[--readonly]\n"
- " [--report-type=RT] [--starting=ELEM] "
- "[--verbose]\n"
- " [--version] DEVICE\n"
+ " [--inhex=FN] [--json[=JO]] "
+ "[--maxlen=LEN]\n"
+ " [--raw] [--readonly] "
+ "[--report-type=RT]\n"
+ " [--starting=ELEM] [--verbose] "
+ "[--version]\n"
+ " DEVICE\n"
" where:\n"
" --brief|-b one descriptor per line\n"
" --filter=FLT|-f FLT FLT is 0 (def) for all physical "
@@ -105,6 +110,8 @@ usage()
"DEVICE,\n"
" assumed to be ASCII hex or, if --raw, "
"in binary\n"
+ " --json[=JO]|-j[JO] output in JSON instead of human "
+ "readable text\n"
" --maxlen=LEN|-m LEN max response length (allocation "
"length in cdb)\n"
" (def: 0 -> %d bytes)\n",
@@ -225,6 +232,40 @@ decode_elem_status_desc(const uint8_t * bp, struct gpes_desc_t * pedp)
pedp->assoc_cap = sg_get_unaligned_be64(bp + 16);
}
+static bool
+fetch_health_str(uint8_t health, char * bp, int max_blen)
+{
+ bool add_val = false;
+ const char * cp = NULL;
+
+ if (0 == health)
+ cp = "not reported";
+ else if (health < 0x64) {
+ cp = "within manufacturer's specification limits";
+ add_val = true;
+ } else if (0x64 == health) {
+ cp = "at manufacturer's specification limits";
+ add_val = true;
+ } else if (health < 0xd0) {
+ cp = "outside manufacturer's specification limits";
+ add_val = true;
+ } else if (health < 0xfb) {
+ cp = "reserved";
+ add_val = true;
+ } else if (0xfb == health)
+ cp = "depopulation revocation completed, errors detected";
+ else if (0xfc == health)
+ cp = "depopulation revocation in progress";
+ else if (0xfd == health)
+ cp = "depopulation completed, errors detected";
+ else if (0xfe == health)
+ cp = "depopulation operations in progress";
+ else if (0xff == health)
+ cp = "depopulation completed, no errors";
+ snprintf(bp, max_blen, "%s", cp);
+ return add_val;
+}
+
int
main(int argc, char * argv[])
@@ -234,7 +275,7 @@ main(int argc, char * argv[])
bool o_readonly = false;
bool verbose_given = false;
bool version_given = false;
- int k, j, n, res, c, rlen, in_len;
+ int k, j, m, n, res, c, rlen, in_len;
int sg_fd = -1;
int do_brief = 0;
int do_hex = 0;
@@ -245,20 +286,27 @@ main(int argc, char * argv[])
uint8_t filter = 0;
uint8_t rt = 0;
uint32_t num_desc, num_desc_ret, id_elem_depop;
- uint32_t d_blocks = 0;
uint32_t starting_elem = 0;
int64_t ll;
const char * device_name = NULL;
const char * in_fn = NULL;
+ const char * cp;
const uint8_t * bp;
uint8_t * gpesBuffp = gpesBuff;
uint8_t * free_gpesBuffp = NULL;
+ sgj_opaque_p jop = NULL;
+ sgj_opaque_p jo2p;
+ sgj_opaque_p jap = NULL;
struct gpes_desc_t a_ped;
+ sgj_state json_st = {0};
+ sgj_state * jsp = &json_st;
+ char b[64];
+ static const int blen = sizeof(b);
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "bf:hHi:m:rRs:St:TvV", long_options,
+ c = getopt_long(argc, argv, "bf:hHi:j::m:rRs:St:TvV", long_options,
&option_index);
if (c == -1)
break;
@@ -286,6 +334,13 @@ main(int argc, char * argv[])
case 'i':
in_fn = optarg;
break;
+ case 'j':
+ if (! sgj_init_state(&json_st, optarg)) {
+ pr2serr("bad argument to --json= option, unrecognized "
+ "character '%c'\n", json_st.first_bad_char);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
case 'm':
maxlen = sg_get_num(optarg);
if ((maxlen < 0) || (maxlen > MAX_GPES_BUFF_LEN)) {
@@ -370,6 +425,8 @@ main(int argc, char * argv[])
pr2serr("version: %s\n", version_str);
return 0;
}
+ if (jsp->pr_as_json)
+ jop = sgj_start(MY_NAME, version_str, argc, argv, jsp);
if (maxlen > DEF_GPES_BUFF_LEN) {
gpesBuffp = (uint8_t *)sg_memalign(maxlen, 0, &free_gpesBuffp,
@@ -448,9 +505,12 @@ start_response:
pr2serr("Response too short (%d bytes) due to resid (%d)\n", k,
resid);
if ((k > 0) && (do_raw || do_hex)) {
- if (do_hex)
- hex2stdout(gpesBuffp, k, 1);
- else
+ if (do_hex) {
+ if (do_hex > 2)
+ hex2stdout(gpesBuffp, k, -1);
+ else
+ hex2stdout(gpesBuffp, k, (2 == do_hex) ? 0 : 1);
+ } else
dStrRaw((const char *)gpesBuffp, k);
}
ret = SG_LIB_CAT_MALFORMED;
@@ -480,91 +540,77 @@ start_response:
goto fini;
}
if (do_hex) {
- hex2stdout(gpesBuffp, rlen, 1);
- goto fini;
- }
-
-#if 0
- if (do_brief > 1) {
- if (rlen < 24) {
- pr2serr("Need maxlen and response length to be at least 24, "
- "have %d bytes\n", rlen);
- ret = SG_LIB_CAT_OTHER;
- goto fini;
- }
- res = decode_lba_status_desc(gpesBuffp + 8, &d_lba, &d_blocks,
- &add_status);
- if ((res < 0) || (res > 15)) {
- pr2serr("first LBA status descriptor returned %d ??\n", res);
- ret = SG_LIB_LOGIC_ERROR;
- goto fini;
- }
- if ((lba < d_lba) || (lba >= (d_lba + d_blocks))) {
- pr2serr("given LBA not in range of first descriptor:\n"
- " descriptor LBA: 0x");
- for (j = 0; j < 8; ++j)
- pr2serr("%02x", gpesBuffp[8 + j]);
- pr2serr(" blocks: 0x%x p_status: %d add_status: 0x%x\n",
- (unsigned int)d_blocks, res,
- (unsigned int)add_status);
- ret = SG_LIB_CAT_OTHER;
- goto fini;
- }
- printf("%d\n", res);
+ if (do_hex > 2)
+ hex2stdout(gpesBuffp, rlen, -1);
+ else
+ hex2stdout(gpesBuffp, rlen, (2 == do_hex) ? 0 : 1);
goto fini;
}
-#endif
- printf("Number of descriptors: %u\n", num_desc);
- printf("Number of descriptors returned: %u\n", num_desc_ret);
- printf("Identifier of element being depopulated: %u\n", id_elem_depop);
+ sgj_pr_twin_vi(jsp, jop, 0, "Number of descriptors",
+ SGJ_SEP_COLON_1_SPACE, num_desc);
+ sgj_pr_twin_vi(jsp, jop, 0, "Number of descriptors returned",
+ SGJ_SEP_COLON_1_SPACE, num_desc_ret);
+ sgj_pr_twin_vi(jsp, jop, 0, "Identifier of element being depopulated",
+ SGJ_SEP_COLON_1_SPACE, id_elem_depop);
if (rlen < 64) {
- printf("No complete physical element status descriptors available\n");
+ sgj_pr_hr(jsp, "No complete physical element status descriptors "
+ "available\n");
goto fini;
- } else
- printf("\n");
+ } else {
+ if (do_brief > 2)
+ goto fini;
+ sgj_pr_hr(jsp, "\n");
+ }
+ if (jsp->pr_as_json)
+ jap = sgj_new_named_array(jsp, jop,
+ "physical_element_status_descriptor");
for (bp = gpesBuffp + GPES_DESC_OFFSET, k = 0; k < (int)num_desc_ret;
bp += GPES_DESC_LEN, ++k) {
- if (0 == k)
- printf("Element descriptors:\n");
+ if ((0 == k) && (do_brief < 2))
+ sgj_pr_hr(jsp, "Element descriptors:\n");
decode_elem_status_desc(bp, &a_ped);
- if (do_brief) {
- printf("0x");
- for (j = 0; j < 8; ++j)
- printf("%02x", bp[j]);
- printf(" 0x%x %d\n", (unsigned int)d_blocks, res);
+ if (jsp->pr_as_json) {
+ jo2p = sgj_new_unattached_object(jsp);
+ sgj_add_name_pair_ihex(jsp, jo2p, "element_identifier",
+ (int64_t)a_ped.elem_id);
+ cp = (1 == a_ped.phys_elem_type) ? "storage" : "reserved";
+ sgj_add_name_pair_istr(jsp, jo2p, "physical_element_type",
+ a_ped.phys_elem_type, "meaning", cp);
+ j = a_ped.phys_elem_health;
+ fetch_health_str(j, b, blen);
+ sgj_add_name_pair_istr(jsp, jo2p, "physical_element_health", j,
+ "meaning", b);
+ sgj_add_name_pair_ihex(jsp, jo2p, "associated_capacity",
+ (int64_t)a_ped.assoc_cap);
+ sgj_add_val_o(jsp, jap, NULL /* name */, jo2p);
+ } else if (do_brief) {
+ sgj_pr_hr(jsp, "%u: %u,%u\n", a_ped.elem_id, a_ped.phys_elem_type,
+ a_ped.phys_elem_health);
} else {
- printf("[%d] identifier: 0x%06x", k + 1, a_ped.elem_id);
+ char b2[144];
+ static const int b2len = sizeof(b2);
+
+ m = 0;
+ m += sg_scnpr(b2 + m, b2len - m, "[%d] identifier: 0x%06x",
+ k + 1, a_ped.elem_id);
if (sg_all_ffs((const uint8_t *)&a_ped.assoc_cap, 8))
- printf(" associated LBs: not specified ");
+ m += sg_scnpr(b2 + m, b2len - m,
+ " associated LBs: not specified; ");
else
- printf(" associated LBs: 0x%" PRIx64 " ", a_ped.assoc_cap);
- printf("health: ");
+ m += sg_scnpr(b2 + m, b2len - m, " associated LBs: 0x%"
+ PRIx64 "; ", a_ped.assoc_cap);
+ m += sg_scnpr(b2 + m, b2len - m, "health: ");
j = a_ped.phys_elem_health;
- if (0 == j)
- printf("not reported");
- else if (j < 0x64)
- printf("within manufacturer's specification limits <%d>", j);
- else if (0x64 == j)
- printf("at manufacturer's specification limits <%d>", j);
- else if (j < 0xd0)
- printf("outside manufacturer's specification limits <%d>", j);
- else if (j < 0xfb)
- printf("reserved [0x%x]", j);
- else if (0xfb == j)
- printf("depopulation revocation completed, errors detected");
- else if (0xfc == j)
- printf("depopulation revocation in progress");
- else if (0xfd == j)
- printf("depopulation completed, errors detected");
- else if (0xfe == j)
- printf("depopulation operations in progress");
- else if (0xff == j)
- printf("depopulation completed, no errors");
+ if (fetch_health_str(j, b, blen))
+ m += sg_scnpr(b2 + m, b2len - m, "%s <%d>", b, j);
+ else
+ m += sg_scnpr(b2 + m, b2len - m, "%s", b);
if (a_ped.restoration_allowed)
- printf(" [restoration allowed [RALWD]]");
- printf("\n");
+ m += sg_scnpr(b2 + m, b2len - m,
+ " [restoration allowed [RALWD]]");
+ sgj_pr_hr(jsp, "%s\n", b2);
}
}
goto fini;
@@ -597,5 +643,11 @@ fini:
pr2serr("Some error occurred, try again with '-v' or '-vv' for "
"more information\n");
}
- return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+ ret = (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+ if (jsp->pr_as_json) {
+ if (0 == do_hex)
+ sgj_pr2file(jsp, NULL, ret, stdout);
+ sgj_finish(jsp);
+ }
+ return ret;
}
diff --git a/src/sg_get_lba_status.c b/src/sg_get_lba_status.c
index dae508a2..6a905793 100644
--- a/src/sg_get_lba_status.c
+++ b/src/sg_get_lba_status.c
@@ -35,7 +35,7 @@
* device.
*/
-static const char * version_str = "1.24 20220218"; /* sbc4r15 */
+static const char * version_str = "1.25 20220520"; /* sbc4r15 */
#ifndef UINT32_MAX
#define UINT32_MAX ((uint32_t)-1)
@@ -416,7 +416,10 @@ start_response:
goto fini;
}
if (do_hex) {
- hex2stdout(glbasBuffp, k, 1);
+ if (do_hex > 2)
+ hex2stdout(glbasBuffp, k, -1);
+ else
+ hex2stdout(glbasBuffp, k, (2 == do_hex) ? 0 : 1);
goto fini;
}
if (maxlen < 4) {
diff --git a/src/sg_raw.c b/src/sg_raw.c
index e0a6ba98..19ab56f9 100644
--- a/src/sg_raw.c
+++ b/src/sg_raw.c
@@ -318,6 +318,7 @@ parse_cmd_line(struct opts_t * op, int argc, char *argv[])
char *opt = argv[optind++];
char *endptr;
int cmd = strtol(opt, &endptr, 16);
+
if (*opt == '\0' || *endptr != '\0' || cmd < 0x00 || cmd > 0xff) {
pr2serr("Invalid command byte '%s'\n", opt);
return SG_LIB_SYNTAX_ERROR;