aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COVERAGE3
-rw-r--r--ChangeLog3
-rw-r--r--README21
-rw-r--r--TODO2
-rw-r--r--debian/changelog2
-rw-r--r--doc/sg3_utils.837
-rw-r--r--doc/sg_compare_and_write.82
-rw-r--r--doc/sg_logs.82
-rw-r--r--doc/sg_luns.82
-rw-r--r--doc/sg_read_attr.8212
-rw-r--r--doc/sg_rep_zones.82
-rw-r--r--doc/sg_write_same.82
-rw-r--r--lib/Makefile.am2
-rw-r--r--sg3_utils.spec2
-rw-r--r--src/Makefile.am20
-rw-r--r--src/Makefile.in54
-rw-r--r--src/sg_read_attr.c1138
-rw-r--r--src/sg_rep_zones.c9
18 files changed, 1453 insertions, 62 deletions
diff --git a/COVERAGE b/COVERAGE
index 3b38b75f..7ddc5e5e 100644
--- a/COVERAGE
+++ b/COVERAGE
@@ -42,6 +42,7 @@ READ(6) sg_dd, sgm_dd, sgp_dd, sg_read
READ(10) sg_dd, sgm_dd, sgp_dd, sg_read
READ(12) sg_dd, sgm_dd, sgp_dd, sg_read
READ(16) sg_dd, sgm_dd, sgp_dd, sg_read
+READ ATTRUBUTE sg_read_attr
READ BLOCK LIMITS sg_read_block_limits, ++
READ BUFFER(10) sg_rbuf, sg_test_rwbuf, sg_read_buffer, sg_safte, ++
READ BUFFER(16) sg_read_buffer
@@ -131,4 +132,4 @@ THIRD PARTY COPY IN (0x83).
Douglas Gilbert
-1st December 2015
+3rd February 2016
diff --git a/ChangeLog b/ChangeLog
index ec3dea75..777db3fc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,8 +2,9 @@ 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 sg3_utils-1.42 [20160203] [svn: r659]
+Changelog for sg3_utils-1.42 [20160207] [svn: r660]
- sg_timestamp: new, to report or set timestamp
+ - sg_read_attr: new, supported by tape drives
- sg_stpg: fix truncation of target port field
- sg_inq: cope with unicode strings, udev fixes
- update version descriptor list to 20160125
diff --git a/README b/README
index 7f0cda19..ed514761 100644
--- a/README
+++ b/README
@@ -233,9 +233,9 @@ subdirectory of the sg3_utils package:
sg_decode_sense, sg_emc_trespass, sg_format, sg_get_config,
sg_get_lba_status, sg_ident, sg_inq, sg_logs, sg_luns, sg_map, sg_map26,
sg_modes, sg_opcodes, sg_persist, sg_prevent, sg_raw, sg_rbuf, sg_rdac,
- sg_read, sg_readcap, sg_read_block_limits, sg_read_buffer, sg_read_long,
- sg_reassign, sg_referrals, sg_request, sg_reset, sg_rmsn, sg_rtpg,
- sg_safte, sg_sanitize, sg_sat_identify, sg_sat_phy_event,
+ sg_read, sg_read_attr, sg_readcap, sg_read_block_limits, sg_read_buffer,
+ sg_read_long, sg_reassign, sg_referrals, sg_request, sg_reset, sg_rmsn,
+ sg_rtpg, sg_safte, sg_sanitize, sg_sat_identify, sg_sat_phy_event,
sg_sat_read_gplog, sg_sat_set_features, sg_scan, sg_senddiag, sg_ses,
sg_ses_microcode, sg_start, sg_stpg, sg_sync, sg_test_rwbuff,
sg_timestamp, sg_turs, sg_unmap, sg_verify, sg_vpd, sg_write_buffer,
@@ -384,12 +384,13 @@ or using '-O' as the first command line option.
The more recent utilities that use "getopt_long" only are:
- sg_compare_and_write, sg_decode_sense, sg_format, sg_get_config,
sg_get_lba_status, sg_ident, sg_luns, sg_map26, sg_persist, sg_prevent,
- sg_raw, sg_read_block_limits, sg_read_buffer, sg_read_long, sg_reassign,
- sg_referrals, sg_requests, sg_rmsn, sg_rtpg, sg_safte, sg_sanitize,
- sg_sat_identify, sg_sat_phy_event, sg_sat_read_gplog, sg_sat_set_features,
- sg_scan(w), sg_ses, sg_ses_microcode, sg_stpg, sg_sync, sg_test_rwbuf,
- sg_timestamp, sg_unmap, sg_verify, sg_vpd, sg_write_buffer,
- sg_write_long, sg_write_same, sg_write_verify, sg_wr_mode, sg_zone
+ sg_raw, sg_read_attr, sg_read_block_limits, sg_read_buffer, sg_read_long,
+ sg_reassign, sg_referrals, sg_requests, sg_rmsn, sg_rtpg, sg_safte,
+ sg_sanitize, sg_sat_identify, sg_sat_phy_event, sg_sat_read_gplog,
+ sg_sat_set_features, sg_scan(w), sg_ses, sg_ses_microcode, sg_stpg,
+ sg_sync, sg_test_rwbuf, sg_timestamp, sg_unmap, sg_verify, sg_vpd,
+ sg_write_buffer, sg_write_long, sg_write_same, sg_write_verify,
+ sg_wr_mode, sg_zone
Dangerous code
@@ -413,4 +414,4 @@ See http://sg.danny.cz/sg/tools.html
Douglas Gilbert
-1st February 2016
+7th February 2016
diff --git a/TODO b/TODO
deleted file mode 100644
index 43c73e00..00000000
--- a/TODO
+++ /dev/null
@@ -1,2 +0,0 @@
-Some suggestions and shortcomings:
-
diff --git a/debian/changelog b/debian/changelog
index 68d211f4..0265d471 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@ sg3-utils (1.42-0.1) unstable; urgency=low
* New upstream version
- -- Douglas Gilbert <dgilbert@interlog.com> Mon, 01 Feb 2016 23:00:00 -0500
+ -- Douglas Gilbert <dgilbert@interlog.com> Sun, 07 Feb 2016 22:00:00 -0500
sg3-utils (1.41-0.1) unstable; urgency=low
diff --git a/doc/sg3_utils.8 b/doc/sg3_utils.8
index f1d37423..233a9e96 100644
--- a/doc/sg3_utils.8
+++ b/doc/sg3_utils.8
@@ -3,8 +3,9 @@
sg3_utils \- a package of utilities for sending SCSI commands
.SH SYNOPSIS
.B sg_*
-[\fI\-\-enumerate\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-maxlen=LEN\fR]
-[\fI\-\-raw\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] [\fIOTHER_OPTIONS\fR]
+[\fI\-\-enumerate\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR] [\fI\-\-in=FN\fR]
+[\fI\-\-maxlen=LEN\fR] [\fI\-\-raw\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR]
+[\fIOTHER_OPTIONS\fR]
\fIDEVICE\fR
.SH DESCRIPTION
.\" Add any additional description here
@@ -373,6 +374,32 @@ response in ASCII hexadecimal. To produce hexadecimal that can be parsed
by other utilities (e.g. without a relative address to the left and without
trailing ASCII) use this option three or four times.
.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIFN\fR
+many SCSI commands fetch a significant amount of data (returned in the
+data\-in buffer) which several of these utilities decode (e.g. sg_vpd and
+sg_logs). To separate the two steps of fetching the data from a SCSI device
+and then decoding it, this option has been added. The first step (fetching
+the data) can be done using the \fI\-\-hex\fR or \fI\-\-raw\fR option and
+redirecting the command line output to a file (often done with ">" in Unix
+based operating systems). The difference between \fI\-\-hex\fR and
+\fI\-\-raw\fR is that the former produces output in ASCII hexadecimal
+while \fI\-\-raw\fR produces its output in "raw" binary.
+.br
+The second step (i.e. decoding the SCSI response data now held in a file)
+can be done using this \fI\-\-in=FN\fR option where the file name is
+\fIFN\fR. If "\-" is used for \fIFN\fR then stdin is assumed, again this
+allows for command line redirection (or piping). That file (or stdin)
+is assumed to contain ASCII hexadecimal unless the \fI\-\-raw\fR option is
+also given in which case it is assumed to be binary. Notice that the meaning
+of the \fI\-\-raw\fR option is "flipped" when used with \fI\-\-in=FN\fR to
+act on the input, typically it acts on the output data.
+.br
+Since the structure of the data returned by SCSI commands varies
+considerably then the usage information or manpage of the utility being
+used should be checked. In some cases \fI\-\-hex\fR may need to be used
+multiple times (and is more conveniently given as '\-HH' or '\-HHH). In
+other cases the name of this option is \fI\-\-inhex=FN\fR.
+.TP
\fB\-m\fR, \fB\-\-maxlen\fR=\fILEN\fR
several important SCSI commands (e.g. INQUIRY and MODE SENSE) have response
lengths that vary depending on many factors, only some of which these
@@ -393,8 +420,10 @@ usually sent to stderr so as to not interfere with the output from this
option.
.br
Some utilities that consume data to send to the \fIDEVICE\fR along with the
-SCSI command, use this option. This option may indicate that binary data
-can be read from stdin or a nominated file.
+SCSI command, use this option. Alernatively the \fI\-\-in=FN\fR option causes
+\fIDEVICE\fR to be ignored and the response data (to be decoded) fetched
+from a file named \fIFN\fR. In these cases this option may indicate that
+binary data can be read from stdin or from a nominated file (e.g. \fIFN\fR).
.TP
\fB\-v\fR, \fB\-\-verbose\fR
increase the level of verbosity, (i.e. debug output). Can be used multiple
diff --git a/doc/sg_compare_and_write.8 b/doc/sg_compare_and_write.8
index dbdbd1a0..47083189 100644
--- a/doc/sg_compare_and_write.8
+++ b/doc/sg_compare_and_write.8
@@ -78,7 +78,7 @@ just the compare buffer (when the \fI\-\-inw=WF\fR option is given). If
.TP
\fB\-D\fR, \fB\-\-inw\fR=\fIWF\fR
read data (binary) from file named \fIWF\fR. This will the write buffer
-that will become the second half of the data-out buffer sent to the
+that will become the second half of the data\-out buffer sent to the
\fIDEVICE\fR associated with the COMPARE AND WRITE command. Note that
when this option is given then the \fI\-\-in=IF\fR is expected to hold
the associated compare buffer.
diff --git a/doc/sg_logs.8 b/doc/sg_logs.8
index e2342f4c..2356d03d 100644
--- a/doc/sg_logs.8
+++ b/doc/sg_logs.8
@@ -266,7 +266,7 @@ non zero when the Parameter list length is zero.
The \fI\-\-select\fR option is required to issue a LOG SELECT command. If
the \fI\-\-in=FN\fR option is not given (or \fIFN\fR is effectively empty)
then the Parameter list length field is set to zero. If the \fI\-\-in=FN\fR
-option is is given then its decoded data is placed in the data-out buffer
+option is is given then its decoded data is placed in the data\-out buffer
and its length in bytes is placed in the Parameter list length field.
.PP
Other options that are active with the LOG SELECT command are
diff --git a/doc/sg_luns.8 b/doc/sg_luns.8
index e383a750..4d14ca6e 100644
--- a/doc/sg_luns.8
+++ b/doc/sg_luns.8
@@ -77,7 +77,7 @@ Without the \fI\-\-quiet\fR option, there is header information printed
before the LUN listing.
.TP
\fB\-r\fR, \fB\-\-raw\fR
-output the SCSI response (i.e. the data-out buffer) in binary (to stdout).
+output the SCSI response (i.e. the data\-out buffer) in binary (to stdout).
.TP
\fB\-R\fR, \fB\-\-readonly\fR
open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
diff --git a/doc/sg_read_attr.8 b/doc/sg_read_attr.8
new file mode 100644
index 00000000..153ae221
--- /dev/null
+++ b/doc/sg_read_attr.8
@@ -0,0 +1,212 @@
+.TH SG_READ_ATTR "8" "February 2016" "sg3_utils\-1.42" SG3_UTILS
+.SH NAME
+sg_read_attr \- send SCSI READ ATTRIBUTE command
+.SH SYNOPSIS
+.B sg_read_attr
+[\fI\-\-cache\fR] [\fI\-\-enumerate\fR] [\fI\-\-ea=EA\fR]
+[\fI\-\-filter=FL\fR] [\fI\-\-first=FAI\fR] [\fI\-\-help\fR] [\fI\-\-hex\fR]
+[\fI\-\-in=FN\fR] [\fI\-\-lvn=LVN\fR] [\fI\-\-maxlen=LEN\fR] [\fI\-\-pn=PN\fR]
+[\fI\-\-quiet\fR] [\fI\-\-raw\fR] [\fI\-\-readonly\fR] [\fI\-\-sa=SA\fR]
+[\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+.SH DESCRIPTION
+.\" Add any additional description here
+.PP
+Sends a SCSI READ ATTRIBUTE command to \fIDEVICE\fR and outputs the data
+returned. This command is found in the SPC\-5 draft standard, revision
+8 (spc5r08.pdf).
+.SH OPTIONS
+Arguments to long options are mandatory for short options as well.
+.TP
+\fB\-c\fR, \fB\-\-cache\fR
+sets the CACHE bit in the READ ATTRIBUTE cdb. This instructs the device
+server to return cached attributes. By default that bit is cleared
+which instructs the device server not to return cached attributes.
+.TP
+\fB\-e\fR, \fB\-\-enumerate\fR
+enumerates all known attributes and service actions. Attributes include
+an identifier, length, format and a name as defined by T10. If \fIDEVICE\fR
+is given then it is ignored.
+.TP
+\fB\-E\fR, \fB\-\-ea\fR=\fIEA\fR
+where \fIEA\fR is an element address which is placed in the READ ATTRIBUTE
+cdb. This field is only found in SMC\-2 and SMC\-3 drafts for medium
+changers usually associated with tape libraries. By default this field
+is set to zero.
+.TP
+\fB\-f\fR, \fB\-\-filter\fR=\fIFL\fR
+where \fIFL\fR is an attribute identifier in the range 0 to 65535 or \-1.
+Attribute identifiers are typical given in hexadecimal in which case the
+hex number should be prefixed by "0x" ot has a trailing "h". "\-1" is
+the default value and means 'match all'; for all other values of \fIFL\fR
+on the matching attribute is output.
+.TP
+\fB\-F\fR, \fB\-\-first\fR=\fIFAI\fR
+where \fIFAI\fR is the "first attribute identifier" field in the cdb. It
+seems as though the intent of this field is that only attributes whose
+identifiers are equal to or greater than \fIFAI\fR are returned. The default
+value of \fIFAI\fR is zero. Attributes are returned in ascending identifier
+order.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+output the usage message then exit.
+.TP
+\fB\-H\fR, \fB\-\-hex\fR
+output the response in hexadecimal to stdout. When used once the whole
+response is output in ASCII hexadecimal with a leading address (starting at
+0) on each line. When used twice each attribute descriptor in the response
+is output separately in hexadecimal. When used thrice the whole response is
+output in hexadecimal with no leading address (on each line).
+.br
+Output generated by '\-HHH' (or \fI\-\-hex\fR used three times) can be
+redirected to a file. That file will be in suitable format for \fI\-\-in=FN\fR
+to use in a later invocation.
+.TP
+\fB\-i\fR, \fB\-\-in\fR=\fIFN\fR
+\fIFN\fR is treated as a file name (or '\-' for stdin) which contains ASCII
+hexadecimal or binary representing the response to a READ ATTRIBUTE command
+with service action 0x0 (i.e (fetch) attribute values). When this option is
+given then \fIDEVICE\fR (if also given) is ignored.
+.br
+By default \fIFN\fR is assumed to contain ASCII hexadecimal arranged as
+bytes which a space, tab or comma delimited. All characters from (and
+including) "#" to the end of line are ignored. If the \fI\-\-raw\fR option
+is also given then \fIFN\fR is assumed to contain binary data. When the
+\fI\-\-raw\fR option is given then after processing the input the
+internal raw variable is reset to 0 so it has no effect on the output.
+.br
+Since the READ ATTRIBUTE response does not contain the service action number
+that it is a response to, then the \fI\-\-sa=SA\fR should be given (if not
+service action 0 (attribute values) is assumed.
+.TP
+\fB\-l\fR, \fB\-\-lvn\fR=\fILVN\fR
+where \fILVN\fR is placed in the "logical volume number" field of the cdb.
+The default value is zero which is required to be the logical volume number
+if the device only has one volume.
+.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 (or \fILEN\fR is zero)
+then 8192 is used. The maximum allowed value of \fILEN\fR is 1048576.
+.TP
+\fB\-p\fR, \fB\-\-pn\fR=\fIPN\fR
+where \fIPN\fR is placed in the "partition number" field of the cdb. If
+the \fIDEVICE\fR only has one partition then its partition number must be
+zero. The default value of \fIPN\fR is zero.
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+this option reduces the amount of information output. For example when
+used once (\fISA\fR=0), it suppresses the header line announcing the
+output of attributes; when used twice it suppresses the name of each
+attribute, leaving only the associated attribute values (or strings).
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+output the SCSI response (i.e. the data\-out buffer) in binary (to stdout).
+.TP
+\fB\-R\fR, \fB\-\-readonly\fR
+open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
+The default is to open it read\-write.
+.TP
+\fB\-s\fR, \fB\-\-sa\fR=\fISA\fR
+where \fISA\fR is placed on the "service action" field of the cdb. Values
+of 0 to 63 are accepted with a default of 0. spc5r08.pdf defines five
+service actions: 0 for attributes values ; 1 for an attribute list (names,
+not values), 2 for the logical volume list; 3 for the partition list; 4
+is restricted for SMC\-3; and 5 for the supported attribute list.
+.br
+Alternatively an acronym can be given for \fISA\fR. The acronym should be
+one of "av", "al", "lvl", "pn", "smc" or "sa" for service actions 0 to 5
+respectively. The acronyms can also be given in upper case.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+increase the level of verbosity, (i.e. debug output).
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+print the version string and then exit.
+.SH NOTES
+Only tape systems seem to implement the SCSI READ ATTRIBUTE command. The vast
+majority of its definition is in the SPC standard so other device types could
+use it.
+.PP
+Much of the information provided by READ ATTRIBUTE can also be found in
+pages returned by LOG SENSE (see the sg_logs utility) and in the VPD
+pages returned by the INQUIRY command.
+.SH EXAMPLES
+To list the attributes of a tape drive whose xxxx is /dev/sg1 the following
+could be used:
+.PP
+# sg_read_attr \-s al /dev/sg1
+.br
+Attribute list:
+.br
+ Remaining capacity in partition [MiB]
+.br
+ Maximum capacity in partition [MiB]
+.br
+ TapeAlert flags
+.br
+ Load count
+.br
+ MAM space remaining [B]
+.br
+ Assigning organization
+.br
+ Format density code
+.br
+ ...
+.PP
+To check the number of partitions:
+.PP
+# sg_read_attr \-s pl /dev/sg1
+.br
+Partition number list:
+.br
+ First partition number: 0
+.br
+ Number of partitions available: 2
+.PP
+And to see the attribute values (which is the default service action):
+.PP
+# sg_read_attr /dev/sg1
+.br
+Attribute values:
+.br
+ Remaining capacity in partition [MiB]: 1386103
+.br
+ Maximum capacity in partition [MiB]: 1386103
+.br
+ TapeAlert flags: 0
+.br
+ ....
+.PP
+To redirect the attribute values response to a file for later decoding:
+.PP
+# sg_read_attr \-HHH /dev/sg1 > av.hex
+.PP
+And later the response held in the av.hex file could be decoded with:
+.PP
+# sg_read_attr \-s av \-\-in=av.hex
+.br
+Attribute values:
+.br
+ Remaining capacity in partition [MiB]: 1386103
+.br
+ Maximum capacity in partition [MiB]: 1386103
+.br
+ TapeAlert flags: 0
+.br
+ ....
+.PP
+.SH EXIT STATUS
+The exit status of sg_read_attr is 0 when it is successful. Otherwise see
+the sg3_utils(8) man page.
+.SH AUTHORS
+Written by Douglas Gilbert.
+.SH "REPORTING BUGS"
+Report bugs to <dgilbert at interlog dot com>.
+.SH COPYRIGHT
+Copyright \(co 2016 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.
+.SH "SEE ALSO"
+.B sg_vpd,sg_logs(sg3_utils)
diff --git a/doc/sg_rep_zones.8 b/doc/sg_rep_zones.8
index b8e9ad7e..df734d06 100644
--- a/doc/sg_rep_zones.8
+++ b/doc/sg_rep_zones.8
@@ -34,7 +34,7 @@ then 8192 is used. The maximum allowed value of \fILEN\fR is 1048576.
set the PARTIAL bit in the cdb.
.TP
\fB\-r\fR, \fB\-\-raw\fR
-output the SCSI response (i.e. the data-out buffer) in binary (to stdout).
+output the SCSI response (i.e. the data\-out buffer) in binary (to stdout).
.TP
\fB\-R\fR, \fB\-\-readonly\fR
open the \fIDEVICE\fR read\-only (e.g. in Unix with the O_RDONLY flag).
diff --git a/doc/sg_write_same.8 b/doc/sg_write_same.8
index 5f30b9ae..1ad6242c 100644
--- a/doc/sg_write_same.8
+++ b/doc/sg_write_same.8
@@ -31,7 +31,7 @@ or the \fI\-\-unmap\fR option is given then WRITE SAME(16) is sent.
The \fI\-\-10\fR, \fI\-\-16\fR and \fI\-\-32\fR options are mutually
exclusive.
.PP
-SBC\-3 revision 35d introduced a "no data-out buffer" (NDOB) bit which, if
+SBC\-3 revision 35d introduced a "no data\-out buffer" (NDOB) bit which, if
set, bypasses the requirement to send a single block of data to the
\fIDEVICE\fR together with the command. Only WRITE SAME (16 and 32 byte)
support the NDOB bit. If given, a user block of zeros is assumed; if
diff --git a/lib/Makefile.am b/lib/Makefile.am
index dffdcf73..c8ab0ccd 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -34,7 +34,7 @@ libsgutils2_la_SOURCES += sg_pt_osf1.c
endif
# For C++/clang testing
-## CC = g++
+## CC = gcc
## CC = g++
## CC = clang
## CC = clang++
diff --git a/sg3_utils.spec b/sg3_utils.spec
index eb7f1256..85808cc5 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -79,7 +79,7 @@ fi
%{_libdir}/*.la
%changelog
-* Mon Feb 01 2016 - dgilbert at interlog dot com
+* Sun Feb 07 2016 - dgilbert at interlog dot com
- track t10 changes
* sg3_utils-1.42
diff --git a/src/Makefile.am b/src/Makefile.am
index 2a34c71d..0fbf84ec 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,15 +1,15 @@
bin_PROGRAMS = \
sg_compare_and_write sg_decode_sense sg_format sg_get_config \
- sg_get_lba_status sg_ident sg_inq sg_logs sg_luns sg_modes sg_opcodes \
- sg_persist sg_prevent sg_raw sg_rdac sg_read_block_limits \
- sg_read_buffer sg_read_long sg_readcap sg_reassign sg_referrals \
- sg_rep_zones sg_requests sg_reset_wp sg_rmsn sg_rtpg sg_safte \
- sg_sanitize sg_sat_identify sg_sat_phy_event sg_sat_read_gplog \
- sg_sat_set_features sg_senddiag sg_ses sg_ses_microcode sg_start \
- sg_stpg sg_sync sg_timestamp sg_turs sg_unmap sg_verify sg_vpd \
- sg_wr_mode sg_write_buffer sg_write_long sg_write_same \
- sg_write_verify sg_zone
+ sg_get_lba_status sg_ident sg_inq sg_logs sg_luns sg_modes \
+ sg_opcodes sg_persist sg_prevent sg_raw sg_rdac sg_read_attr \
+ sg_read_block_limits sg_read_buffer sg_read_long sg_readcap \
+ sg_reassign sg_referrals sg_rep_zones sg_requests sg_reset_wp \
+ sg_rmsn sg_rtpg sg_safte sg_sanitize sg_sat_identify \
+ sg_sat_phy_event sg_sat_read_gplog sg_sat_set_features sg_senddiag \
+ sg_ses sg_ses_microcode sg_start sg_stpg sg_sync sg_timestamp \
+ sg_turs sg_unmap sg_verify sg_vpd sg_wr_mode sg_write_buffer \
+ sg_write_long sg_write_same sg_write_verify sg_zone
sg_scan_SOURCES =
@@ -97,6 +97,8 @@ sg_rdac_LDADD = ../lib/libsgutils2.la @os_libs@
sg_read_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_read_attr_LDADD = ../lib/libsgutils2.la @os_libs@
+
sg_readcap_LDADD = ../lib/libsgutils2.la @os_libs@
sg_read_block_limits_LDADD = ../lib/libsgutils2.la @os_libs@
diff --git a/src/Makefile.in b/src/Makefile.in
index 9c54f216..cc268d28 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -93,12 +93,12 @@ bin_PROGRAMS = sg_compare_and_write$(EXEEXT) sg_decode_sense$(EXEEXT) \
sg_get_lba_status$(EXEEXT) sg_ident$(EXEEXT) sg_inq$(EXEEXT) \
sg_logs$(EXEEXT) sg_luns$(EXEEXT) sg_modes$(EXEEXT) \
sg_opcodes$(EXEEXT) sg_persist$(EXEEXT) sg_prevent$(EXEEXT) \
- sg_raw$(EXEEXT) sg_rdac$(EXEEXT) sg_read_block_limits$(EXEEXT) \
- sg_read_buffer$(EXEEXT) sg_read_long$(EXEEXT) \
- sg_readcap$(EXEEXT) sg_reassign$(EXEEXT) sg_referrals$(EXEEXT) \
- sg_rep_zones$(EXEEXT) sg_requests$(EXEEXT) \
- sg_reset_wp$(EXEEXT) sg_rmsn$(EXEEXT) sg_rtpg$(EXEEXT) \
- sg_safte$(EXEEXT) sg_sanitize$(EXEEXT) \
+ sg_raw$(EXEEXT) sg_rdac$(EXEEXT) sg_read_attr$(EXEEXT) \
+ sg_read_block_limits$(EXEEXT) sg_read_buffer$(EXEEXT) \
+ sg_read_long$(EXEEXT) sg_readcap$(EXEEXT) sg_reassign$(EXEEXT) \
+ sg_referrals$(EXEEXT) sg_rep_zones$(EXEEXT) \
+ sg_requests$(EXEEXT) sg_reset_wp$(EXEEXT) sg_rmsn$(EXEEXT) \
+ sg_rtpg$(EXEEXT) sg_safte$(EXEEXT) sg_sanitize$(EXEEXT) \
sg_sat_identify$(EXEEXT) sg_sat_phy_event$(EXEEXT) \
sg_sat_read_gplog$(EXEEXT) sg_sat_set_features$(EXEEXT) \
sg_senddiag$(EXEEXT) sg_ses$(EXEEXT) sg_ses_microcode$(EXEEXT) \
@@ -209,6 +209,9 @@ sg_rdac_DEPENDENCIES = ../lib/libsgutils2.la
sg_read_SOURCES = sg_read.c
sg_read_OBJECTS = sg_read.$(OBJEXT)
sg_read_DEPENDENCIES = ../lib/libsgutils2.la
+sg_read_attr_SOURCES = sg_read_attr.c
+sg_read_attr_OBJECTS = sg_read_attr.$(OBJEXT)
+sg_read_attr_DEPENDENCIES = ../lib/libsgutils2.la
sg_read_block_limits_SOURCES = sg_read_block_limits.c
sg_read_block_limits_OBJECTS = sg_read_block_limits.$(OBJEXT)
sg_read_block_limits_DEPENDENCIES = ../lib/libsgutils2.la
@@ -376,14 +379,14 @@ SOURCES = sg_compare_and_write.c sg_copy_results.c sg_dd.c \
sg_get_config.c sg_get_lba_status.c sg_ident.c \
$(sg_inq_SOURCES) sg_logs.c sg_luns.c sg_map.c sg_map26.c \
sg_modes.c sg_opcodes.c sg_persist.c sg_prevent.c sg_raw.c \
- sg_rbuf.c sg_rdac.c sg_read.c sg_read_block_limits.c \
- sg_read_buffer.c sg_read_long.c sg_readcap.c sg_reassign.c \
- sg_referrals.c sg_rep_zones.c sg_requests.c sg_reset.c \
- sg_reset_wp.c sg_rmsn.c sg_rtpg.c sg_safte.c sg_sanitize.c \
- sg_sat_identify.c sg_sat_phy_event.c sg_sat_read_gplog.c \
- sg_sat_set_features.c $(sg_scan_SOURCES) sg_senddiag.c \
- sg_ses.c sg_ses_microcode.c sg_start.c sg_stpg.c sg_sync.c \
- sg_test_rwbuf.c sg_timestamp.c sg_turs.c sg_unmap.c \
+ sg_rbuf.c sg_rdac.c sg_read.c sg_read_attr.c \
+ sg_read_block_limits.c sg_read_buffer.c sg_read_long.c \
+ sg_readcap.c sg_reassign.c sg_referrals.c sg_rep_zones.c \
+ sg_requests.c sg_reset.c sg_reset_wp.c sg_rmsn.c sg_rtpg.c \
+ sg_safte.c sg_sanitize.c sg_sat_identify.c sg_sat_phy_event.c \
+ sg_sat_read_gplog.c sg_sat_set_features.c $(sg_scan_SOURCES) \
+ sg_senddiag.c sg_ses.c sg_ses_microcode.c sg_start.c sg_stpg.c \
+ sg_sync.c sg_test_rwbuf.c sg_timestamp.c sg_turs.c sg_unmap.c \
sg_verify.c $(sg_vpd_SOURCES) sg_wr_mode.c sg_write_buffer.c \
sg_write_long.c sg_write_same.c sg_write_verify.c sg_xcopy.c \
sg_zone.c sginfo.c sgm_dd.c sgp_dd.c
@@ -392,14 +395,15 @@ DIST_SOURCES = sg_compare_and_write.c sg_copy_results.c sg_dd.c \
sg_get_config.c sg_get_lba_status.c sg_ident.c \
$(sg_inq_SOURCES) sg_logs.c sg_luns.c sg_map.c sg_map26.c \
sg_modes.c sg_opcodes.c sg_persist.c sg_prevent.c sg_raw.c \
- sg_rbuf.c sg_rdac.c sg_read.c sg_read_block_limits.c \
- sg_read_buffer.c sg_read_long.c sg_readcap.c sg_reassign.c \
- sg_referrals.c sg_rep_zones.c sg_requests.c sg_reset.c \
- sg_reset_wp.c sg_rmsn.c sg_rtpg.c sg_safte.c sg_sanitize.c \
- sg_sat_identify.c sg_sat_phy_event.c sg_sat_read_gplog.c \
- sg_sat_set_features.c $(am__sg_scan_SOURCES_DIST) \
- sg_senddiag.c sg_ses.c sg_ses_microcode.c sg_start.c sg_stpg.c \
- sg_sync.c sg_test_rwbuf.c sg_timestamp.c sg_turs.c sg_unmap.c \
+ sg_rbuf.c sg_rdac.c sg_read.c sg_read_attr.c \
+ sg_read_block_limits.c sg_read_buffer.c sg_read_long.c \
+ sg_readcap.c sg_reassign.c sg_referrals.c sg_rep_zones.c \
+ sg_requests.c sg_reset.c sg_reset_wp.c sg_rmsn.c sg_rtpg.c \
+ sg_safte.c sg_sanitize.c sg_sat_identify.c sg_sat_phy_event.c \
+ sg_sat_read_gplog.c sg_sat_set_features.c \
+ $(am__sg_scan_SOURCES_DIST) sg_senddiag.c sg_ses.c \
+ sg_ses_microcode.c sg_start.c sg_stpg.c sg_sync.c \
+ sg_test_rwbuf.c sg_timestamp.c sg_turs.c sg_unmap.c \
sg_verify.c $(sg_vpd_SOURCES) sg_wr_mode.c sg_write_buffer.c \
sg_write_long.c sg_write_same.c sg_write_verify.c sg_xcopy.c \
sg_zone.c sginfo.c sgm_dd.c sgp_dd.c
@@ -583,6 +587,7 @@ sg_raw_LDADD = ../lib/libsgutils2.la @os_libs@
sg_rbuf_LDADD = ../lib/libsgutils2.la @os_libs@
sg_rdac_LDADD = ../lib/libsgutils2.la @os_libs@
sg_read_LDADD = ../lib/libsgutils2.la @os_libs@
+sg_read_attr_LDADD = ../lib/libsgutils2.la @os_libs@
sg_readcap_LDADD = ../lib/libsgutils2.la @os_libs@
sg_read_block_limits_LDADD = ../lib/libsgutils2.la @os_libs@
sg_read_buffer_LDADD = ../lib/libsgutils2.la @os_libs@
@@ -795,6 +800,10 @@ sg_read$(EXEEXT): $(sg_read_OBJECTS) $(sg_read_DEPENDENCIES) $(EXTRA_sg_read_DEP
@rm -f sg_read$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(sg_read_OBJECTS) $(sg_read_LDADD) $(LIBS)
+sg_read_attr$(EXEEXT): $(sg_read_attr_OBJECTS) $(sg_read_attr_DEPENDENCIES) $(EXTRA_sg_read_attr_DEPENDENCIES)
+ @rm -f sg_read_attr$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(sg_read_attr_OBJECTS) $(sg_read_attr_LDADD) $(LIBS)
+
sg_read_block_limits$(EXEEXT): $(sg_read_block_limits_OBJECTS) $(sg_read_block_limits_DEPENDENCIES) $(EXTRA_sg_read_block_limits_DEPENDENCIES)
@rm -f sg_read_block_limits$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(sg_read_block_limits_OBJECTS) $(sg_read_block_limits_LDADD) $(LIBS)
@@ -988,6 +997,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_rbuf.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_rdac.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_read.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_read_attr.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_read_block_limits.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_read_buffer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_read_long.Po@am__quote@
diff --git a/src/sg_read_attr.c b/src/sg_read_attr.c
new file mode 100644
index 00000000..bd02a493
--- /dev/null
+++ b/src/sg_read_attr.c
@@ -0,0 +1,1138 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <errno.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_lib_data.h"
+#include "sg_pt.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues the SCSI READ ATTRIBUTE command to the given SCSI device
+ * and decodes the response. Based on spc5r08.pdf
+ */
+
+static const char * version_str = "1.00 20160207";
+
+#define MAX_RATTR_BUFF_LEN (1024 * 1024)
+#define DEF_RATTR_BUFF_LEN (1024 * 8)
+
+#define SG_READ_ATTRIBUTE_CMD 0x8c
+#define SG_READ_ATTRIBUTE_CMDLEN 16
+
+#define RA_ATTR_VAL_SA 0x0
+#define RA_ATTR_LIST_SA 0x1
+#define RA_LV_LIST_SA 0x2
+#define RA_PART_LIST_SA 0x3
+#define RA_SMC2_SA 0x4
+#define RA_SUP_ATTR_SA 0x5
+#define RA_HIGHEST_SA 0x5
+
+#define RA_FMT_BINARY 0x0
+#define RA_FMT_ASCII 0x1
+#define RA_FMT_TEXT 0x2 /* takes into account locale */
+#define RA_FMT_RES 0x3 /* reserved */
+
+
+#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
+#define DEF_PT_TIMEOUT 60 /* 60 seconds */
+
+struct opts_t {
+ int cache;
+ int ea;
+ int enumerate;
+ int filter;
+ int fai;
+ int do_hex;
+ int lvn;
+ int maxlen;
+ int pn;
+ int quiet;
+ int do_raw;
+ int o_readonly;
+ int sa;
+ int verbose;
+};
+
+struct acron_nv_t {
+ const char * acron;
+ const char * name;
+ int val;
+};
+
+struct attr_name_info_t {
+ int id;
+ const char * name; /* tab ('\t') suggest line break */
+ int format; /* RA_FMT_BINARY and friends, -1 --> unknown */
+ int len; /* -1 --> not fixed (variable) */
+ int process; /* 0 --> print decimal if binary, 1 --> print hex,
+ * 2 --> further processing */
+};
+
+static struct option long_options[] = {
+ {"cache", no_argument, 0, 'c'},
+ {"enumerate", no_argument, 0, 'e'},
+ {"element", required_argument, 0, 'E'}, /* SMC-3 element address */
+ {"filter", required_argument, 0, 'f'},
+ {"first", required_argument, 0, 'F'},
+ {"help", no_argument, 0, 'h'},
+ {"hex", no_argument, 0, 'H'},
+ {"in", required_argument, 0, 'i'},
+ {"lvn", required_argument, 0, 'l'},
+ {"maxlen", required_argument, 0, 'm'},
+ {"partition", required_argument, 0, 'p'},
+ {"quiet", required_argument, 0, 'q'},
+ {"raw", no_argument, 0, 'r'},
+ {"readonly", no_argument, 0, 'R'},
+ {"sa", required_argument, 0, 's'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0}, /* sentinel */
+};
+
+static struct acron_nv_t sa_acron_arr[] = {
+ {"av", "attribute values", 0},
+ {"al", "attribute list", 1},
+ {"lvl", "logical volume list", 2},
+ {"pl", "partition list", 3},
+ {"smc", "SMC-2 should define this", 4},
+ {"sa", "supported attributes", 5},
+ {NULL, NULL, -1}, /* sentinel */
+};
+
+static struct attr_name_info_t attr_name_arr[] = {
+/* Device type attributes */
+ {0x0, "Remaining capacity in partition [MiB]", RA_FMT_BINARY, 8, 0},
+ {0x1, "Maximum capacity in partition [MiB]", RA_FMT_BINARY, 8, 0},
+ {0x2, "TapeAlert flags", RA_FMT_BINARY, 8, 0}, /* SSC-4 */
+ {0x3, "Load count", RA_FMT_BINARY, 8, 0},
+ {0x4, "MAM space remaining [B]", RA_FMT_BINARY, 8, 0},
+ {0x5, "Assigning organization", RA_FMT_ASCII, 8, 0}, /* SSC-4 */
+ {0x6, "Format density code", RA_FMT_BINARY, 1, 1}, /* SSC-4 */
+ {0x7, "Initialization count", RA_FMT_BINARY, 2, 0},
+ {0x8, "Volume identifier", RA_FMT_ASCII, 32, 0},
+ {0x9, "Volume change reference", RA_FMT_BINARY, -1, 1}, /* SSC-4 */
+ {0x20a, "Density vendor/serial number at last load", RA_FMT_ASCII, 40, 0},
+ {0x20b, "Density vendor/serial number at load-1", RA_FMT_ASCII, 40, 0},
+ {0x20c, "Density vendor/serial number at load-2", RA_FMT_ASCII, 40, 0},
+ {0x20d, "Density vendor/serial number at load-3", RA_FMT_ASCII, 40, 0},
+ {0x220, "Total MiB written in medium life", RA_FMT_BINARY, 8, 0},
+ {0x221, "Total MiB read in medium life", RA_FMT_BINARY, 8, 0},
+ {0x222, "Total MiB written in current/last load", RA_FMT_BINARY, 8, 0},
+ {0x223, "Total MiB read in current/last load", RA_FMT_BINARY, 8, 0},
+ {0x224, "Logical position of first encrypted block", RA_FMT_BINARY, 8, 2},
+ {0x225, "Logical position of first unencrypted block\tafter first "
+ "encrypted block", RA_FMT_BINARY, 8, 2},
+ {0x340, "Medium usage history", RA_FMT_BINARY, 90, 2},
+ {0x341, "Partition usage history", RA_FMT_BINARY, 60, 2},
+
+/* Medium type attributes */
+ {0x400, "Medium manufacturer", RA_FMT_ASCII, 8, 0},
+ {0x401, "Medium serial number", RA_FMT_ASCII, 32, 0},
+ {0x402, "Medium length [m]", RA_FMT_BINARY, 4, 0}, /* SSC-4 */
+ {0x403, "Medium width [0.1 mm]", RA_FMT_BINARY, 4, 0}, /* SSC-4 */
+ {0x404, "Assigning organization", RA_FMT_ASCII, 8, 0}, /* SSC-4 */
+ {0x405, "Medium density code", RA_FMT_BINARY, 1, 1}, /* SSC-4 */
+ {0x406, "Medium manufacture date", RA_FMT_ASCII, 8, 0},
+ {0x407, "MAM capacity [B]", RA_FMT_BINARY, 8, 0},
+ {0x408, "Medium type", RA_FMT_BINARY, 1, 1},
+ {0x409, "Medium type information", RA_FMT_BINARY, 2, 1},
+ {0x40a, "Numeric medium serial number", -1, -1, 1},
+
+/* Host type attributes */
+ {0x800, "Application vendor", RA_FMT_ASCII, 8, 0},
+ {0x801, "Application name", RA_FMT_ASCII, 32, 0},
+ {0x802, "Application version", RA_FMT_ASCII, 8, 0},
+ {0x803, "User medium text label", RA_FMT_TEXT, 160, 0},
+ {0x804, "Date and time last written", RA_FMT_ASCII, 12, 0},
+ {0x805, "Text localization identifier", RA_FMT_BINARY, 1, 0},
+ {0x806, "Barcode", RA_FMT_ASCII, 32, 0},
+ {0x807, "Owning host textual name", RA_FMT_TEXT, 80, 0},
+ {0x808, "Media pool", RA_FMT_TEXT, 160, 0},
+ {0x809, "Partition user text label", RA_FMT_ASCII, 16, 0},
+ {0x80a, "Load/unload at partition", RA_FMT_BINARY, 1, 0},
+ {0x80a, "Application format version", RA_FMT_ASCII, 16, 0},
+ {0x80c, "Volume coherency information", RA_FMT_BINARY, -1, 1},
+ /* SSC-5 */
+ {0x820, "Medium globally unique identifier", RA_FMT_BINARY, 36, 1},
+ {0x821, "Media pool globally unique identifier", RA_FMT_BINARY, 36, 1},
+
+ {-1, NULL, -1, -1, 0},
+};
+
+
+static void
+usage()
+{
+ pr2serr("Usage: sg_read_attr [--cache] [--element=EA] [--enumerate] "
+ "[--filter=FL]\n"
+ " [--first=FAI] [--help] [--hex] [--in=FN] "
+ "[--lvn-LVN]\n"
+ " [--maxlen=LEN] [--partition=PN] [--quiet] "
+ "[--raw]\n"
+ " [--readonly] [--sa=SA] [--verbose] "
+ "[--version]\n"
+ " DEVICE\n");
+ pr2serr(" where:\n"
+ " --cache|-c set CACHE bit in cdn (def: clear)\n"
+ " --enumerate|-e enumerate known attributes and service "
+ "actions\n"
+ " --element=EA|-E EA EA is placed in 'element address' "
+ "field in\n"
+ " cdb [SMC-3] (def: 0)\n"
+ " --filter=FL|-f FL FL is parameter code to match (def: "
+ "-1 -> all)\n"
+ " --first=FAI|-F FAI FAI is placed in 'first attribute "
+ "identifier'\n"
+ " field in cdb (def: 0)\n"
+ " --help|-h print out usage message\n"
+ " --hex|-H output response in hexadecimal; used "
+ "twice\n"
+ " shows decoded values in hex\n"
+ " --in=FN|-i FN FN is a filename containing attribute "
+ "values in\n"
+ " ASCII hex or binary if --raw also "
+ "given\n"
+ " --lvn=LVN|-l LVN logical volume number (LVN) (def:0)\n"
+ " --maxlen=LEN|-m LEN max response length (allocation "
+ "length in cdb)\n"
+ " (def: 0 -> 8192 bytes)\n"
+ " --partition=PN|-p PN partition number (PN) (def:0)\n"
+ " --quiet|-q reduce the amount of output, can use "
+ "more than once\n"
+ " --raw|-r output response in binary\n"
+ " --readonly|-R open DEVICE read-only (def: read-write)\n"
+ " --sa=SA|-s SA SA is service action (def: 0)\n"
+ " --verbose|-v increase verbosity\n"
+ " --version|-V print version string and exit\n\n"
+ "Performs a SCSI READ ATTRIBUTE command. It is typically used "
+ "on tape\nsystems.\n");
+}
+
+/* Invokes a SCSI READ ATTRIBUTE command (SPC+SMC). Return of 0 -> success,
+ * various SG_LIB_CAT_* positive values or -1 -> other errors */
+static int
+sg_ll_read_attr(int sg_fd, void * resp, int * residp,
+ const struct opts_t * op)
+{
+ int k, ret, res, sense_cat;
+ int noisy = 1;
+ unsigned char raCmdBlk[SG_READ_ATTRIBUTE_CMDLEN] =
+ {SG_READ_ATTRIBUTE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0};
+ unsigned char sense_b[SENSE_BUFF_LEN];
+ struct sg_pt_base * ptvp;
+
+ raCmdBlk[1] = 0x1f & op->sa;
+ if (op->ea)
+ sg_put_unaligned_be16(op->ea, raCmdBlk + 2);
+ if (op->lvn)
+ raCmdBlk[5] = 0xff & op->lvn;
+ if (op->pn)
+ raCmdBlk[7] = 0xff & op->pn;
+ if (op->fai)
+ sg_put_unaligned_be16(op->fai, raCmdBlk + 8);
+ sg_put_unaligned_be32((uint32_t)op->maxlen, raCmdBlk + 10);
+ if (op->cache)
+ raCmdBlk[14] |= 0x1;
+ if (op->verbose) {
+ pr2serr(" Read attribute cdb: ");
+ for (k = 0; k < SG_READ_ATTRIBUTE_CMDLEN; ++k)
+ pr2serr("%02x ", raCmdBlk[k]);
+ pr2serr("\n");
+ }
+
+ ptvp = construct_scsi_pt_obj();
+ if (NULL == ptvp) {
+ pr2serr("%s: out of memory\n", __func__);
+ return -1;
+ }
+ set_scsi_pt_cdb(ptvp, raCmdBlk, sizeof(raCmdBlk));
+ set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b));
+ set_scsi_pt_data_in(ptvp, (unsigned char *)resp, op->maxlen);
+ res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, op->verbose);
+ ret = sg_cmds_process_resp(ptvp, "read attribute", res, op->maxlen,
+ sense_b, noisy, op->verbose, &sense_cat);
+ if (-1 == ret)
+ ;
+ else if (-2 == ret) {
+ switch (sense_cat) {
+ case SG_LIB_CAT_RECOVERED:
+ case SG_LIB_CAT_NO_SENSE:
+ ret = 0;
+ break;
+ default:
+ ret = sense_cat;
+ break;
+ }
+ } else
+ ret = 0;
+ if (residp)
+ *residp = get_scsi_pt_resid(ptvp);
+ destruct_scsi_pt_obj(ptvp);
+ return ret;
+}
+
+static void
+dStrRaw(const char* str, int len)
+{
+ int k;
+
+ for (k = 0 ; k < len; ++k)
+ printf("%c", str[k]);
+}
+
+static int
+find_sa_acron(const char * cp)
+{
+ int k;
+ const struct acron_nv_t * anvp;
+ const char * mp;
+
+ for (anvp = sa_acron_arr; anvp->acron ; ++anvp) {
+ for (mp = cp, k = 0; *mp; ++mp, ++k) {
+ if (0 == anvp->acron[k])
+ return anvp->val;
+ if (tolower(*mp) != anvp->acron[k])
+ break;
+ }
+ if ((0 == *mp) && (0 == anvp->acron[k]))
+ return anvp->val;
+ }
+ return -1; /* not found */
+}
+
+const char * a_format[] = {
+ "binary",
+ "ascii",
+ "text",
+ "format[0x3]",
+};
+
+static void
+enum_attributes(void)
+{
+ const struct attr_name_info_t * anip;
+ const char * cp;
+ char b[32];
+
+ printf("Attribute ID\tLength\tFormat\tName\n");
+ printf("------------------------------------------\n");
+ for (anip = attr_name_arr; anip->name ; ++anip) {
+ if (anip->format < 0)
+ snprintf(b, sizeof(b), "unknown");
+ else
+ snprintf(b, sizeof(b), "%s", a_format[0x3 & anip->format]);
+ printf(" 0x%04x:\t%d\t%s\t", anip->id, anip->len, b);
+ cp = strchr(anip->name, '\t');
+ if (cp ) {
+ printf("%.*s\n", (int)(cp - anip->name), anip->name);
+ printf("\t\t\t\t%s\n", cp + 1);
+ } else
+ printf("%s\n", anip->name);
+ }
+}
+
+static void
+enum_sa_acrons(void)
+{
+ const struct acron_nv_t * anvp;
+
+ printf("SA_value\tAcronym\tDescription\n");
+ printf("------------------------------------------\n");
+ for (anvp = sa_acron_arr; anvp->acron ; ++anvp)
+ printf(" %d:\t\t%s\t%s\n", anvp->val, anvp->acron, anvp->name);
+}
+
+/* Read ASCII hex bytes or binary from fname (a file named '-' taken as
+ * stdin). If reading ASCII hex then there should be either one entry per
+ * line or a comma, space or tab separated list of bytes. If no_space is
+ * set then a string of ACSII hex digits is expected, 2 per byte. Everything
+ * from and including a '#' on a line is ignored. Returns 0 if ok, or 1 if
+ * error. */
+static int
+f2hex_arr(const char * fname, int as_binary, int no_space,
+ uint8_t * mp_arr, int * mp_arr_len, int max_arr_len)
+{
+ int fn_len, in_len, k, j, m, split_line, fd, has_stdin;
+ unsigned int h;
+ const char * lcp;
+ FILE * fp;
+ char line[512];
+ char carry_over[4];
+ int off = 0;
+
+ if ((NULL == fname) || (NULL == mp_arr) || (NULL == mp_arr_len))
+ return 1;
+ fn_len = strlen(fname);
+ if (0 == fn_len)
+ return 1;
+ has_stdin = ((1 == fn_len) && ('-' == fname[0])); /* read from stdin */
+ if (as_binary) {
+ if (has_stdin) {
+ fd = STDIN_FILENO;
+ if (sg_set_binary_mode(STDIN_FILENO) < 0)
+ perror("sg_set_binary_mode");
+ } else {
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ pr2serr("unable to open binary file %s: %s\n", fname,
+ safe_strerror(errno));
+ return 1;
+ } else if (sg_set_binary_mode(fd) < 0)
+ perror("sg_set_binary_mode");
+ }
+ k = read(fd, mp_arr, max_arr_len);
+ if (k <= 0) {
+ if (0 == k)
+ pr2serr("read 0 bytes from binary file %s\n", fname);
+ else
+ pr2serr("read from binary file %s: %s\n", fname,
+ safe_strerror(errno));
+ if (! has_stdin)
+ close(fd);
+ return 1;
+ }
+ *mp_arr_len = k;
+ if (! has_stdin)
+ close(fd);
+ return 0;
+ } else { /* So read the file as ASCII hex */
+ if (has_stdin)
+ fp = stdin;
+ else {
+ fp = fopen(fname, "r");
+ if (NULL == fp) {
+ pr2serr("Unable to open %s for reading\n", fname);
+ return 1;
+ }
+ }
+ }
+
+ carry_over[0] = 0;
+ for (j = 0; j < 512; ++j) {
+ if (NULL == fgets(line, sizeof(line), fp))
+ break;
+ in_len = strlen(line);
+ if (in_len > 0) {
+ if ('\n' == line[in_len - 1]) {
+ --in_len;
+ line[in_len] = '\0';
+ split_line = 0;
+ } else
+ split_line = 1;
+ }
+ if (in_len < 1) {
+ carry_over[0] = 0;
+ continue;
+ }
+ if (carry_over[0]) {
+ if (isxdigit(line[0])) {
+ carry_over[1] = line[0];
+ carry_over[2] = '\0';
+ if (1 == sscanf(carry_over, "%4x", &h))
+ mp_arr[off - 1] = h; /* back up and overwrite */
+ else {
+ pr2serr("%s: carry_over error ['%s'] around line %d\n",
+ __func__, carry_over, j + 1);
+ goto bad;
+ }
+ 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]) && ('\r' != lcp[k])) {
+ pr2serr("%s: syntax error at line %d, pos %d\n", __func__,
+ j + 1, m + k + 1);
+ goto bad;
+ }
+ if (no_space) {
+ for (k = 0; isxdigit(*lcp) && isxdigit(*(lcp + 1));
+ ++k, lcp += 2) {
+ if (1 != sscanf(lcp, "%2x", &h)) {
+ pr2serr("%s: bad hex number in line %d, pos %d\n",
+ __func__, j + 1, (int)(lcp - line + 1));
+ goto bad;
+ }
+ if ((off + k) >= max_arr_len) {
+ pr2serr("%s: array length exceeded\n", __func__);
+ goto bad;
+ }
+ mp_arr[off + k] = h;
+ }
+ if (isxdigit(*lcp) && (! isxdigit(*(lcp + 1))))
+ carry_over[0] = *lcp;
+ off += k;
+ } else {
+ for (k = 0; k < 1024; ++k) {
+ if (1 == sscanf(lcp, "%4x", &h)) {
+ if (h > 0xff) {
+ pr2serr("%s: hex number larger than 0xff in line %d, "
+ "pos %d\n", __func__, j + 1,
+ (int)(lcp - line + 1));
+ goto bad;
+ }
+ 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("%s: array length exceeded\n", __func__);
+ goto bad;
+ }
+ mp_arr[off + k] = h;
+ lcp = strpbrk(lcp, " ,\t");
+ if (NULL == lcp)
+ break;
+ lcp += strspn(lcp, " ,\t");
+ if ('\0' == *lcp)
+ break;
+ } else {
+ if (('#' == *lcp) || ('\r' == *lcp)) {
+ --k;
+ break;
+ }
+ pr2serr("%s: error in line %d, at pos %d\n", __func__,
+ j + 1, (int)(lcp - line + 1));
+ goto bad;
+ }
+ }
+ off += (k + 1);
+ }
+ }
+ *mp_arr_len = off;
+ if (stdin != fp)
+ fclose(fp);
+ return 0;
+bad:
+ if (stdin != fp)
+ fclose(fp);
+ return 1;
+}
+
+/* Returns 1 if 'ucp' all 0xff bytes, returns 2 is all 0xff bytes apart
+ * from last being 0xfe; otherwise returns 0. */
+static int
+all_ffs_or_last_fe(const unsigned char * ucp, int len)
+{
+ for ( ; len > 0; ++ucp, --len) {
+ if (*ucp < 0xfe)
+ return 0;
+ if (0xfe == *ucp)
+ return (1 == len) ? 2 : 0;
+
+ }
+ return 1;
+}
+
+static char *
+attr_id_lookup(unsigned int id, const struct attr_name_info_t ** anipp,
+ int blen, char * b)
+{
+ const struct attr_name_info_t * anip;
+
+ for (anip = attr_name_arr; anip->name; ++anip) {
+ if (id == (unsigned int)anip->id)
+ break;
+ }
+ if (anip->name) {
+ snprintf(b, blen, "%s", anip->name);
+ if (anipp)
+ *anipp = anip;
+ return b;
+ }
+ if (anipp)
+ *anipp = NULL;
+ if (id < 0x400)
+ snprintf(b, blen, "Unknown device attribute 0x%x", id);
+ else if (id < 0x800)
+ snprintf(b, blen, "Unknown medium attribute 0x%x", id);
+ else if (id < 0xc00)
+ snprintf(b, blen, "Unknown host attribute 0x%x", id);
+ else if (id < 0x1000)
+ snprintf(b, blen, "Vendor specific device attribute 0x%x", id);
+ else if (id < 0x1400)
+ snprintf(b, blen, "Vendor specific medium attribute 0x%x", id);
+ else if (id < 0x1800)
+ snprintf(b, blen, "Vendor specific host attribute 0x%x", id);
+ else
+ snprintf(b, blen, "Reserved attribute 0x%x", id);
+ return b;
+}
+
+static void
+decode_attr_list(const unsigned char * alp, int len, bool supported,
+ const struct opts_t * op)
+{
+ int id;
+ char b[160];
+ char * cp;
+ char * c2p;
+ const char * leadin = supported ? "Supported a" : "A";
+
+ if (op->verbose)
+ printf("%sttribute list: [len=%d]\n", leadin, len);
+ else if (0 == op->quiet)
+ printf("%sttribute list:\n", leadin);
+ if (op->do_hex) {
+ dStrHex((const char *)alp, len, 0);
+ return;
+ }
+ for ( ; len > 0; alp += 2, len -= 2) {
+ id = sg_get_unaligned_be16(alp + 0);
+ if ((op->filter >= 0) && (op->filter != id))
+ continue;
+ if (op->verbose)
+ printf(" 0x%.4x:\t", id);
+ cp = attr_id_lookup(id, NULL, sizeof(b), b);
+ c2p = strchr(cp, '\t');
+ if (c2p) {
+ printf(" %.*s -\n", (int)(c2p - cp), cp);
+ if (op->verbose)
+ printf("\t\t %s\n", c2p + 1);
+ else
+ printf(" %s\n", c2p + 1);
+ } else
+ printf(" %s\n", cp);
+ }
+}
+
+static void
+helper_full_attr(const unsigned char * alp, int len, int id,
+ const struct attr_name_info_t * anip,
+ const struct opts_t * op)
+{
+ int k;
+ const unsigned char * ucp;
+
+ if (op->verbose)
+ printf("[r%c] ", (0x80 & alp[2]) ? 'o' : 'w');
+ if (op->verbose > 3)
+ pr2serr("%s: id=0x%x, len=%d, anip->format=%d, anip->len=%d\n",
+ __func__, id, len, anip->format, anip->len);
+ switch (id) {
+ case 0x224: /* logical position of first encrypted block */
+ k = all_ffs_or_last_fe(alp + 5, len - 5);
+ if (1 == k)
+ printf("<unknown> [ff]\n");
+ else if (2 == k)
+ printf("<unknown [fe]>\n");
+ else {
+ if ((len - 5) <= 8)
+ printf("%" PRIx64, sg_get_unaligned_be(len - 5, alp + 5));
+ else {
+ printf("\n");
+ dStrHex((const char *)(alp + 5), len - 5, 0);
+ }
+ }
+ break;
+ case 0x225: /* logical position of first unencrypted block
+ * after first encrypted block */
+ k = all_ffs_or_last_fe(alp + 5, len - 5);
+ if (1 == k)
+ printf("<unknown> [ff]\n");
+ else if (2 == k)
+ printf("<unknown [fe]>\n");
+ else {
+ if ((len - 5) <= 8)
+ printf("%" PRIx64, sg_get_unaligned_be(len - 5, alp + 5));
+ else {
+ printf("\n");
+ dStrHex((const char *)(alp + 5), len - 5, 0);
+ }
+ }
+ break;
+ case 0x340: /* Medium Usage history */
+ ucp = alp + 5;
+ printf("\n");
+ if ((len - 5) < 90) {
+ pr2serr("%s: expected 90 bytes, got %d\n", __func__, len - 5);
+ break;
+ }
+ printf(" Current amount of data written [MiB]: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 0));
+ printf(" Current write retry count: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 6));
+ printf(" Current amount of data read [MiB]: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 12));
+ printf(" Current read retry count: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 18));
+ printf(" Previous amount of data written [MiB]: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 24));
+ printf(" Previous write retry count: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 30));
+ printf(" Previous amount of data read [MiB]: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 36));
+ printf(" Previous read retry count: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 42));
+ printf(" Total amount of data written [MiB]: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 48));
+ printf(" Total write retry count: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 54));
+ printf(" Total amount of data read [MiB]: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 60));
+ printf(" Total read retry count: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 66));
+ printf(" Load count: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 72));
+ printf(" Total change partition count: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 78));
+ printf(" Total partition initialization count: %" PRIu64 "\n",
+ sg_get_unaligned_be48(ucp + 84));
+ break;
+ case 0x341: /* Partition Usage history */
+ ucp = alp + 5;
+ printf("\n");
+ if ((len - 5) < 60) {
+ pr2serr("%s: expected 60 bytes, got %d\n", __func__, len - 5);
+ break;
+ }
+ printf(" Current amount of data written [MiB]: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 0));
+ printf(" Current write retry count: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 4));
+ printf(" Current amount of data read [MiB]: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 8));
+ printf(" Current read retry count: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 12));
+ printf(" Previous amount of data written [MiB]: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 16));
+ printf(" Previous write retry count: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 20));
+ printf(" Previous amount of data read [MiB]: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 24));
+ printf(" Previous read retry count: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 28));
+ printf(" Total amount of data written [MiB]: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 32));
+ printf(" Total write retry count: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 36));
+ printf(" Total amount of data read [MiB]: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 40));
+ printf(" Total read retry count: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 44));
+ printf(" Load count: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 48));
+ printf(" change partition count: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 52));
+ printf(" partition initialization count: %" PRIu32 "\n",
+ sg_get_unaligned_be32(ucp + 56));
+ break;
+ default:
+ pr2serr("%s: unknown attribute id: 0x%x\n", __func__, id);
+ printf(" In hex:\n");
+ dStrHex((const char *)alp, len, 0);
+ break;
+ }
+}
+
+static void
+decode_attr_vals(const unsigned char * alp, int len, const struct opts_t * op)
+{
+ int bump, id, alen;
+ uint64_t ull;
+ char * cp;
+ char * c2p;
+ const struct attr_name_info_t * anip;
+ char b[160];
+
+ if (op->verbose)
+ printf("Attribute values: [len=%d]\n", len);
+ else if (op->filter < 0) {
+ if (0 == op->quiet)
+ printf("Attribute values:\n");
+ if (op->do_hex) { /* only expect -HH to get through here */
+ dStrHex((const char *)alp, len, 0);
+ return;
+ }
+ }
+ for ( ; len > 4; alp += bump, len -= bump) {
+ id = sg_get_unaligned_be16(alp + 0);
+ bump = sg_get_unaligned_be16(alp + 3) + 5;
+ alen = bump - 5;
+ if ((op->filter >= 0) && (op->filter != id)) {
+ if (id < op->filter)
+ continue;
+ else
+ break; /* Assume array is ascending id order */
+ }
+ anip = NULL;
+ cp = attr_id_lookup(id, &anip, sizeof(b), b);
+ if (op->quiet < 2) {
+ c2p = strchr(cp, '\t');
+ if (c2p) {
+ printf(" %.*s -\n", (int)(c2p - cp), cp);
+ printf(" %s: ", c2p + 1);
+ } else
+ printf(" %s: ", cp);
+ }
+ if (op->verbose)
+ printf("[r%c] ", (0x80 & alp[2]) ? 'o' : 'w');
+ if (anip) {
+ if ((RA_FMT_BINARY == anip->format) && (bump <= 13)) {
+ ull = sg_get_unaligned_be(alen, alp + 5);
+ if (0 == anip->process)
+ printf("%" PRIu64 "\n", ull);
+ else if (1 == anip->process)
+ printf("0x%" PRIx64 "\n", ull);
+ else
+ helper_full_attr(alp, bump, id, anip, op);
+ if (op->verbose) {
+ if ((anip->len > 0) && (alen > 0) && (alen != anip->len))
+ printf(" <<< T10 length (%d) differs from length in "
+ "response (%d) >>>\n", anip->len, alen);
+ }
+ } else if (RA_FMT_BINARY == anip->format) {
+ if (2 == anip->process)
+ helper_full_attr(alp, bump, id, anip, op);
+ else {
+ printf("\n");
+ dStrHex((const char *)(alp + 5), alen, 0);
+ }
+ } else {
+ if (2 == anip->process)
+ helper_full_attr(alp, bump, id, anip, op);
+ else {
+ printf("%.*s\n", alen, alp + 5);
+ if (op->verbose) {
+ if ((anip->len > 0) && (alen > 0) &&
+ (alen != anip->len))
+ printf(" <<< T10 length (%d) differs from length "
+ "in response (%d) >>>\n", anip->len, alen);
+ }
+ }
+ }
+ } else {
+ if (op->verbose > 1)
+ printf("Attribute id lookup failed, in hex:\n");
+ else
+ printf("\n");
+ dStrHex((const char *)(alp + 5), alen, 0);
+ }
+ }
+ if (op->verbose && (len > 0) && (len <= 4))
+ pr2serr("warning: iterate of attributes should end a residual of "
+ "%d\n", len);
+}
+
+static void
+decode_all_sa_s(const unsigned char * rabp, int len, const struct opts_t * op)
+{
+ if (op->do_hex && (2 != op->do_hex)) {
+ dStrHex((const char *)rabp, len, ((1 == op->do_hex) ? 1 : -1));
+ return;
+ }
+ switch (op->sa) {
+ case RA_ATTR_VAL_SA:
+ decode_attr_vals(rabp + 4, len - 4, op);
+ break;
+ case RA_ATTR_LIST_SA:
+ decode_attr_list(rabp + 4, len - 4, false, op);
+ break;
+ case RA_LV_LIST_SA:
+ if ((0 == op->quiet) || op->verbose)
+ printf("Logical volume list:\n");
+ if (len < 4) {
+ pr2serr(">>> response length unexpectedly short: %d bytes\n",
+ len);
+ break;
+ }
+ printf(" First logical volume number: %u\n", rabp[2]);
+ printf(" Number of logical volumes available: %u\n", rabp[3]);
+ break;
+ case RA_PART_LIST_SA:
+ if ((0 == op->quiet) || op->verbose)
+ printf("Partition number list:\n");
+ if (len < 4) {
+ pr2serr(">>> response length unexpectedly short: %d bytes\n",
+ len);
+ break;
+ }
+ printf(" First partition number: %u\n", rabp[2]);
+ printf(" Number of partitions available: %u\n", rabp[3]);
+ break;
+ case RA_SMC2_SA:
+ printf("Used by SMC-2, not information, output in hex:\n");
+ dStrHex((const char *)rabp, len, 0);
+ break;
+ case RA_SUP_ATTR_SA:
+ decode_attr_list(rabp + 4, len - 4, true, op);
+ break;
+ default:
+ printf("Unrecognized service action [0x%x], response in hex:\n",
+ op->sa);
+ dStrHex((const char *)rabp, len, 0);
+ break;
+ }
+}
+
+int
+main(int argc, char * argv[])
+{
+ int sg_fd, res, c, len, resid, rlen, in_len;
+ unsigned int ra_len;
+ int ret = 0;
+ const char * device_name = NULL;
+ const char * fname = NULL;
+ unsigned char * rabp = NULL;
+ struct opts_t opts;
+ struct opts_t * op;
+ char b[80];
+
+ op = &opts;
+ memset(op, 0, sizeof(opts));
+ op->filter = -1;
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "ceE:f:F:hHi:l:m:p:qrRs:vV",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'c':
+ ++op->cache;
+ break;
+ case 'e':
+ ++op->enumerate;
+ break;
+ case 'E':
+ op->ea = sg_get_num(optarg);
+ if ((op->ea < 0) || (op->ea > 65535)) {
+ pr2serr("bad argument to '--ea=EA', expect 0 to 65535\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'f':
+ op->filter = sg_get_num(optarg);
+ if ((op->filter < -3) || (op->filter > 65535)) {
+ pr2serr("bad argument to '--filter=FL', expect -3 to "
+ "65535\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'F':
+ op->fai = sg_get_num(optarg);
+ if ((op->fai < 0) || (op->fai > 65535)) {
+ pr2serr("bad argument to '--first=FAI', expect 0 to 65535\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ case 'H':
+ ++op->do_hex;
+ break;
+ case 'i':
+ fname = optarg;
+ break;
+ case 'l':
+ op->lvn = sg_get_num(optarg);
+ if ((op->lvn < 0) || (op->lvn > 255)) {
+ pr2serr("bad argument to '--lvn=LVN', expect 0 to 255\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'm':
+ op->maxlen = sg_get_num(optarg);
+ if ((op->maxlen < 0) || (op->maxlen > MAX_RATTR_BUFF_LEN)) {
+ pr2serr("argument to '--maxlen' should be %d or "
+ "less\n", MAX_RATTR_BUFF_LEN);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'p':
+ op->pn = sg_get_num(optarg);
+ if ((op->pn < 0) || (op->pn > 255)) {
+ pr2serr("bad argument to '--pn=PN', expect 0 to 255\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'q':
+ ++op->quiet;
+ break;
+ case 'r':
+ ++op->do_raw;
+ break;
+ case 'R':
+ ++op->o_readonly;
+ break;
+ case 's':
+ if (isdigit(*optarg)) {
+ op->sa = sg_get_num(optarg);
+ if ((op->sa < 0) || (op->sa > 63)) {
+ pr2serr("bad argument to '--sa=SA', expect 0 to 63\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ } else {
+ res = find_sa_acron(optarg);
+ if (res < 0) {
+ enum_sa_acrons();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->sa = res;
+ }
+ break;
+ case 'v':
+ ++op->verbose;
+ break;
+ case 'V':
+ pr2serr("version: %s\n", version_str);
+ return 0;
+ default:
+ pr2serr("unrecognised option code 0x%x ??\n", c);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (optind < argc) {
+ if (NULL == device_name) {
+ 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;
+ }
+ }
+
+ if (op->enumerate) {
+ enum_attributes();
+ printf("\n");
+ enum_sa_acrons();
+ return 0;
+ }
+
+ if (fname && device_name) {
+ pr2serr("since '--in=FN' given, ignoring DEVICE\n");
+ device_name = NULL;
+ }
+
+ if (0 == op->maxlen)
+ op->maxlen = DEF_RATTR_BUFF_LEN;
+ rabp = (unsigned char *)calloc(1, op->maxlen);
+ if (NULL == rabp) {
+ pr2serr("unable to calloc %d bytes\n", op->maxlen);
+ return SG_LIB_CAT_OTHER;
+ }
+
+ if (NULL == device_name) {
+ if (fname) {
+ if (f2hex_arr(fname, op->do_raw, 0, rabp, &in_len, op->maxlen))
+ return SG_LIB_FILE_ERROR;
+ if (op->do_raw)
+ op->do_raw = 0; /* can interfere on decode */
+ if (in_len < 4) {
+ pr2serr("--in=%s only decoded %d bytes (needs 4 at least)\n",
+ fname, in_len);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ decode_all_sa_s(rabp, in_len, op);
+ goto clean_up;
+ }
+ pr2serr("missing device name!\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+
+ if (op->do_raw) {
+ if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+ perror("sg_set_binary_mode");
+ return SG_LIB_FILE_ERROR;
+ }
+ }
+
+ sg_fd = sg_cmds_open_device(device_name, op->o_readonly, op->verbose);
+ if (sg_fd < 0) {
+ pr2serr("open error: %s: %s\n", device_name,
+ safe_strerror(-sg_fd));
+ return SG_LIB_FILE_ERROR;
+ }
+
+ res = sg_ll_read_attr(sg_fd, rabp, &resid, op);
+ ret = res;
+ if (0 == res) {
+ rlen = op->maxlen - resid;
+ if (rlen < 4) {
+ pr2serr("Response length (%d) too short\n", rlen);
+ ret = SG_LIB_CAT_MALFORMED;
+ goto close_then_end;
+ }
+ if ((op->sa <= RA_HIGHEST_SA) && (op->sa != RA_SMC2_SA)) {
+ ra_len = ((RA_LV_LIST_SA == op->sa) ||
+ (RA_PART_LIST_SA == op->sa)) ?
+ (unsigned int)sg_get_unaligned_be16(rabp + 0) :
+ sg_get_unaligned_be32(rabp + 0) + 2;
+ ra_len += 2;
+ } else
+ ra_len = rlen;
+ if ((int)ra_len > rlen) {
+ if (op->verbose)
+ pr2serr("ra_len available is %d, response length is %d\n",
+ ra_len, rlen);
+ len = rlen;
+ } else
+ len = (int)ra_len;
+ if (op->do_raw) {
+ dStrRaw((const char *)rabp, len);
+ goto close_then_end;
+ }
+ decode_all_sa_s(rabp, len, op);
+ } else if (SG_LIB_CAT_INVALID_OP == res)
+ pr2serr("Read attribute command not supported\n");
+ else {
+ sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+ pr2serr("Read attribute command: %s\n", b);
+ }
+
+close_then_end:
+ res = sg_cmds_close_device(sg_fd);
+ if (res < 0) {
+ pr2serr("close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ ret = SG_LIB_FILE_ERROR;
+ }
+clean_up:
+ if (rabp)
+ free(rabp);
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}
diff --git a/src/sg_rep_zones.c b/src/sg_rep_zones.c
index 62fb57b5..bdb2d4a6 100644
--- a/src/sg_rep_zones.c
+++ b/src/sg_rep_zones.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015 Douglas Gilbert.
+ * Copyright (c) 2014-2016 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.
@@ -32,7 +32,7 @@
* and decodes the response. Based on zbc-r02.pdf
*/
-static const char * version_str = "1.07 20151219";
+static const char * version_str = "1.08 20160203";
#define MAX_RZONES_BUFF_LEN (1024 * 1024)
#define DEF_RZONES_BUFF_LEN (1024 * 8)
@@ -116,7 +116,7 @@ sg_ll_report_zones(int sg_fd, uint64_t zs_lba, int partial, int report_opts,
ptvp = construct_scsi_pt_obj();
if (NULL == ptvp) {
- pr2serr("Report zones: out of memory\n");
+ pr2serr("%s: out of memory\n", __func__);
return -1;
}
set_scsi_pt_cdb(ptvp, rzCmdBlk, sizeof(rzCmdBlk));
@@ -329,8 +329,7 @@ main(int argc, char * argv[])
}
if (optind < argc) {
for (; optind < argc; ++optind)
- pr2serr("Unexpected extra argument: %s\n",
- argv[optind]);
+ pr2serr("Unexpected extra argument: %s\n", argv[optind]);
usage();
return SG_LIB_SYNTAX_ERROR;
}