aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2022-04-21 02:37:15 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2022-04-21 02:37:15 +0000
commit8cd2200ba1758cd127a1b95ab1808cde4f56713c (patch)
tree81f7e8e9c995548351865b77c13092ebf40d7174
parentf26bb872bf4f043a1b817c808a6947fcb03ecda4 (diff)
downloadsg3_utils-8cd2200ba1758cd127a1b95ab1808cde4f56713c.tar.gz
sg_opcodes: add --inhex=FN to process earlier -HHH, add experimental --json[=JO] option; sg_turs: change nanosleep() to Sleep() in MinGW
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@945 6180dd3e-e324-4e3e-922d-17de1ae2f315
-rw-r--r--ChangeLog7
-rw-r--r--doc/sg_logs.85
-rw-r--r--doc/sg_opcodes.848
-rw-r--r--include/sg_pr2serr.h76
-rw-r--r--inhex/opcodes.hex27
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/Makefile.in46
-rw-r--r--lib/sg_json_builder.c4
-rw-r--r--lib/sg_json_builder.h17
-rw-r--r--lib/sg_lib.c14
-rw-r--r--lib/sg_pr2serr.c242
-rw-r--r--src/sg_dd.c4
-rw-r--r--src/sg_opcodes.c596
-rw-r--r--src/sg_turs.c19
-rw-r--r--testing/Makefile6
-rw-r--r--testing/sg_json_builder_test.c73
16 files changed, 958 insertions, 227 deletions
diff --git a/ChangeLog b/ChangeLog
index bd6145af..0ca8cb77 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@ 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 [20220417] [svn: r944]
+Changelog for pre-release sg3_utils-1.48 [20220420] [svn: r945]
- 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
@@ -39,22 +39,27 @@ Changelog for pre-release sg3_utils-1.48 [20220417] [svn: r944]
- sg_inq, sg_vpd: Device Identication VPD page, change
"IEEE Company_id" to "AOI" as per spc6r06.pdf
- 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
SAS address
+ - sg_turs: change nanosleep() to Sleep() in MinGW
- inhex/logs_last_n.hex: new, tests for the 4 "Last n ..."
log (sub)pages
- sg_lib: add sg_pdt_s_eq() to cope with ZBC disks which may
be either PDT_ZBC (if host managed) or PDT_DISK
- add hex2fp(), similar to hex2str() but outputs to FILE
- cleanup masks for PDT [0x1f] and group_number [0x3f]
+ - new sg_json_builder.[hc] files local to lib folder
- initialize all sense buffers to 0
- rework main README file
- rev 921+922 are bugfix revs on release 1.47 [r919,920]
- configure.ac: map msys to mingw
- repeat tweak to accept uclinux as linux
- sg_dd: change uint type to uint32_t
+ - remove unused out2_off in main()
- sg_pt_dummy.c: remove problematic include
- round of coverity identified issue fixes (and non-issues)
diff --git a/doc/sg_logs.8 b/doc/sg_logs.8
index 84ccccd1..60f4faa6 100644
--- a/doc/sg_logs.8
+++ b/doc/sg_logs.8
@@ -1,4 +1,4 @@
-.TH SG_LOGS "8" "March 2022" "sg3_utils\-1.48" SG3_UTILS
+.TH SG_LOGS "8" "April 2022" "sg3_utils\-1.48" SG3_UTILS
.SH NAME
sg_logs \- access log pages with SCSI LOG SENSE command
.SH SYNOPSIS
@@ -184,6 +184,9 @@ arranged as 1 or 2 digits representing a byte each of which is whitespace or
comma separated. Anything from and including a hash mark to the end of line
is ignored. If the \fI\-\-raw\fR option is also given then \fIFN\fR is
treated as binary.
+.br
+For compatibility with other utilities in this package "inhex" may also be
+used as this (long) option name.
.TP
\fB\-l\fR, \fB\-\-list\fR
lists the names of the logs sense pages supported by this device. This is
diff --git a/doc/sg_opcodes.8 b/doc/sg_opcodes.8
index bdae3b5c..e7a1f19d 100644
--- a/doc/sg_opcodes.8
+++ b/doc/sg_opcodes.8
@@ -1,19 +1,20 @@
-.TH SG_OPCODES "8" "October 2021" "sg3_utils\-1.47" SG3_UTILS
+.TH SG_OPCODES "8" "April 2022" "sg3_utils\-1.48" SG3_UTILS
.SH NAME
sg_opcodes \- report supported SCSI commands or task management functions
.SH SYNOPSIS
.B sg_opcodes
[\fI\-\-alpha\fR] [\fI\-\-compact\fR] [\fI\-\-enumerate\fR] [\fI\-\-help\fR]
-[\fI\-\-hex\fR] [\fI\-\-mask\fR] [\fI\-\-mlu\fR] [\fI\-\-no-inquiry\fR]
-[\fI\-\-opcode=OP[,SA]\fR] [\fI\-\-pdt=DT\fR] [\fI\-\-raw\fR]
-[\fI\-\-rctd\fR] [\fI\-\-repd\fR] [\fI\-\-sa=SA\fR] [\fI\-\-tmf\fR]
-[\fI\-\-unsorted\fR] [\fI\-\-verbose\fR] [\fI\-\-version\fR] \fIDEVICE\fR
+[\fI\-\-hex\fR] [\fI\-\-inhex=FN\fR] [\fI\-\-json[=JO\fR]] [\fI\-\-mask\fR]
+[\fI\-\-mlu\fR] [\fI\-\-no-inquiry\fR] [\fI\-\-opcode=OP[,SA]\fR]
+[\fI\-\-pdt=DT\fR] [\fI\-\-raw\fR] [\fI\-\-rctd\fR] [\fI\-\-repd\fR]
+[\fI\-\-sa=SA\fR] [\fI\-\-tmf\fR] [\fI\-\-unsorted\fR] [\fI\-\-verbose\fR]
+[\fI\-\-version\fR] \fIDEVICE\fR
.PP
.B sg_opcodes
-[\fI\-a\fR] [\fI\-c\fR] [\fI\-e\fR] [\fI\-H\fR] [\fI\-m\fR] [\fI\-M\fR]
-[\fI\-n\fR] [\fI\-o=OP\fR] [\fI\-p=DT\fR] [\fI\-q\fR] [\fI\-R\fR]
-[\fI\-s=SA\fR] [\fI\-t\fR] [\fI\-u\fR] [\fI\-v\fR] [\fI\-V\fR] [\fI\-?\fR]
-\fIDEVICE\fR
+[\fI\-a\fR] [\fI\-c\fR] [\fI\-e\fR] [\fI\-H\fR] [\fI\-i=FN\fR] [\fI\-j\fR]
+[\fI\-m\fR] [\fI\-M\fR] [\fI\-n\fR] [\fI\-o=OP\fR] [\fI\-p=DT\fR] [\fI\-q\fR]
+[\fI\-R\fR] [\fI\-s=SA\fR] [\fI\-t\fR] [\fI\-u\fR] [\fI\-v\fR] [\fI\-V\fR]
+[\fI\-?\fR] \fIDEVICE\fR
.SH DESCRIPTION
.\" Add any additional description here
.PP
@@ -74,7 +75,27 @@ outputs the usage message summarizing command line options
then exits. Ignores \fIDEVICE\fR if given.
.TP
\fB\-H\fR, \fB\-\-hex\fR
-outputs the response in ASCII hexadecimal to stdout.
+outputs the response in ASCII hexadecimal to stdout. When used once or
+twice, each line starts with a relative (hex) address starting at 0
+on the first hex line output. The difference is when used twice the
+hexadecimal bytes are rendered in ASCII at the right of each line;
+non\-printable characters are replaced by "." .
+.br
+When used three or more times, there is no leading relative address
+on each line. This output is suitable for being redirected to a file
+which can later by given to the \fI\-\-inhex=FN\fR option.
+.TP
+\fB\-i\fR, \fB\-\-inhex\fR=\fIFN\fR
+where \fIFN\fR is a file name whose contents are assumed to be ASCII
+hexadecimal. If \fIDEVICE\fR is also given then \fIDEVICE\fR is ignored,
+a warning is issued and the utility continues, decoding the file named
+\fIFN\fR. See the "FORMAT OF FILES CONTAINING ASCII HEX" section in the
+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\-\-mask\fR
additionally prints out the cdb mask in hex. So a 12 byte cdb will have
@@ -127,8 +148,9 @@ suppress this utility doing an INQUIRY command since the main reason
for doing that is to find the peripheral device type of the \fIDEVICE\fR.
.TP
\fB\-r\fR, \fB\-\-raw\fR
-output the response in binary to stdout. Error messages and warnings, if
-any, are sent to stderr.
+output response in binary (to stdout) unless the \fI\-\-inhex=FN\fR option
+is also given. In that case the input file name (\fIFN\fR) is decoded as
+binary (and the output is _not_ in binary (but may be hex)).
.TP
\fB\-R\fR, \fB\-\-rctd\fR
set report command timeout descriptor (RCTD) bit in the cdb. The response
@@ -309,7 +331,7 @@ Written by Douglas Gilbert
.SH "REPORTING BUGS"
Report bugs to <dgilbert at interlog dot com>.
.SH COPYRIGHT
-Copyright \(co 2004\-2021 Douglas Gilbert
+Copyright \(co 2004\-2022 Douglas Gilbert
.br
This software is distributed under the GPL version 2. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/include/sg_pr2serr.h b/include/sg_pr2serr.h
index 70592781..afac7c09 100644
--- a/include/sg_pr2serr.h
+++ b/include/sg_pr2serr.h
@@ -27,12 +27,13 @@
#include <stdio.h>
+#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
-#if 0
+#if 1
enum sg_json_separator_t {
SG_JSON_SEP_NONE = 0,
SG_JSON_SEP_SPACE_1,
@@ -46,6 +47,23 @@ enum sg_json_separator_t {
};
#endif
+typedef void * sg_json_opaque_p;
+
+typedef struct sg_json_state_t {
+ bool pr_as_json;
+ bool pr_pretty;
+ bool pr_sorted;
+ bool pr_output;
+ bool pr_implemented;
+ bool pr_unimplemented;
+ char pr_format;
+ int pr_indent_size;
+ int verbose;
+ sg_json_opaque_p basep;
+ sg_json_opaque_p outputp;
+ sg_json_opaque_p userp;
+} sg_json_state;
+
#if defined(__GNUC__) || defined(__clang__)
#ifdef SG_LIB_MINGW
@@ -54,8 +72,17 @@ int pr2serr(const char * fmt, ...);
int pr2ws(const char * fmt, ...);
+/* Want safer, 'n += snprintf(b + n, blen - n, ...)' style sequence of
+ * functions. Returns number of chars placed in cp excluding the trailing null
+ * char. So for cp_max_len > 0 the return value is always < cp_max_len; for
+ * cp_max_len <= 1 the return value is 0 and no chars are written to cp. Note
+ * this means that when cp_max_len = 1, this function assumes that cp[0] is
+ * the null character and does nothing (and returns 0). Linux kernel has a
+ * similar function called scnprintf(). */
int sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...);
+void sgj_pr_hr(sg_json_state * jsp, const char * fmt, ...);
+
#else /* GNU/clang other than MinGW */
int pr2serr(const char * fmt, ...)
@@ -66,6 +93,9 @@ int pr2ws(const char * fmt, ...)
int sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...)
__attribute__ ((format (printf, 3, 4)));
+
+void sgj_pr_hr(sg_json_state * jsp, const char * fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
#endif
#else /* not GNU (and not clang) */
@@ -76,15 +106,51 @@ int pr2ws(const char * fmt, ...);
int sg_scnpr(char * cp, int cp_max_len, const char * fmt, ...);
+void sgj_pr_hr(sg_json_state * jsp, const char * fmt, ...);
+
#endif
-#if 0
+void sg_json_init_state(sg_json_state * jstp);
+
+sg_json_opaque_p sg_json_start(const char * util_name, const char * ver_str,
+ int argc, char *argv[], sg_json_state * jstp);
+
+/* Newly created object is un-attached */
+sg_json_opaque_p sgj_new_object(sg_json_state * jsp);
+
+sg_json_opaque_p sgj_new_named_object(sg_json_state * jsp,
+ sg_json_opaque_p jop,
+ const char * name);
+
+sg_json_opaque_p sgj_add_array_element(sg_json_state * jsp,
+ sg_json_opaque_p jap,
+ sg_json_opaque_p ejop);
+
+sg_json_opaque_p sgj_new_named_array(sg_json_state * jsp,
+ sg_json_opaque_p jop,
+ const char * name);
+
+sg_json_opaque_p sgj_add_name_vs(sg_json_state * jsp, sg_json_opaque_p jop,
+ const char * name, const char * value);
+
+sg_json_opaque_p sgj_add_name_vi(sg_json_state * jsp, sg_json_opaque_p jop,
+ const char * name, int64_t value);
+
+sg_json_opaque_p sgj_add_name_vb(sg_json_state * jsp, sg_json_opaque_p jop,
+ const char * name, bool value);
+
+void sgj_pr2file(sg_json_opaque_p jop, sg_json_state * jstp, FILE * fp);
+
+void sg_json_free(sg_json_opaque_p jop);
+
+#if 1
/* Print function for normal and/or json output. "hr" stands for human
* readable (only); "j" for JSON (only). */
-void pr_j_simple(int leadin_sp, const char * name,
+void pr_j_simple(sg_json_opaque_p jop, int leadin_sp, const char * name,
enum sg_json_separator_t sep, const char * value);
-void pr_j_hr_line(const char * hr_line, const char * jname,
- const char * jvalue);
+void pr_j_hr_line(sg_json_opaque_p jop, const char * hr_line,
+ const char * name, const char * value);
+
#endif
diff --git a/inhex/opcodes.hex b/inhex/opcodes.hex
new file mode 100644
index 00000000..8833ab0b
--- /dev/null
+++ b/inhex/opcodes.hex
@@ -0,0 +1,27 @@
+00 00 01 a0 12 00 00 00 00 00 00 06 a0 00 00 00
+00 00 00 0c 03 00 00 00 00 00 00 06 00 00 00 00
+00 00 00 06 5a 00 00 00 00 00 00 0a 1a 00 00 00
+00 00 00 06 55 00 00 00 00 00 00 0a 15 00 00 00
+00 00 00 06 4d 00 00 00 00 00 00 0a 25 00 00 00
+00 00 00 0a 88 00 00 00 00 00 00 10 28 00 00 00
+00 00 00 0a 08 00 00 00 00 00 00 06 a8 00 00 00
+00 00 00 0c 8a 00 00 00 00 00 00 10 2a 00 00 00
+00 00 00 0a 0a 00 00 00 00 00 00 06 aa 00 00 00
+00 00 00 0c 1b 00 00 00 00 00 00 06 9e 00 00 10
+00 01 00 10 9e 00 00 12 00 01 00 10 9f 00 00 12
+00 01 00 10 a3 00 00 0a 00 01 00 0c a3 00 00 0c
+00 01 00 0c a3 00 00 0d 00 01 00 0c 8f 00 00 00
+00 00 00 10 2f 00 00 00 00 00 00 0a 7f 00 00 09
+00 01 00 20 7f 00 00 0b 00 01 00 20 7f 00 00 11
+00 01 00 20 56 00 00 00 00 00 00 0a 16 00 00 00
+00 00 00 06 57 00 00 00 00 00 00 0a 17 00 00 00
+00 00 00 06 1e 00 00 00 00 00 00 06 01 00 00 00
+00 00 00 06 1d 00 00 02 00 00 00 06 42 00 00 00
+00 00 00 0a 3b 00 00 00 00 00 00 0a 41 00 00 00
+00 00 00 0a 93 00 00 00 00 00 00 10 35 00 00 00
+00 00 00 0a 91 00 00 00 00 00 00 10 89 00 00 00
+00 00 00 10 34 00 00 00 00 00 00 0a 90 00 00 00
+00 00 00 10 94 00 00 03 00 01 00 10 94 00 00 01
+00 01 00 10 94 00 00 02 00 01 00 10 94 00 00 04
+00 01 00 10 95 00 00 00 00 01 00 10 95 00 00 06
+00 01 00 10
diff --git a/lib/Makefile.am b/lib/Makefile.am
index b481816d..77e56408 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,5 +1,6 @@
libsgutils2_la_SOURCES = \
sg_lib.c \
+ sg_pr2serr.c \
sg_lib_data.c \
sg_lib_names.c \
sg_cmds_basic.c \
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 1a6dbeee..64cf1d08 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -144,7 +144,7 @@ am__uninstall_files_from_dir = { \
}
am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
-am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \
+am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_pr2serr.c sg_lib_data.c \
sg_lib_names.c sg_cmds_basic.c sg_cmds_basic2.c \
sg_cmds_extra.c sg_cmds_mmc.c sg_pt_common.c sg_json_builder.c \
sg_pt_dummy.c sg_pt_linux.c sg_io_linux.c sg_pt_linux_nvme.c \
@@ -165,14 +165,14 @@ am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \
@OS_NETBSD_TRUE@am__objects_11 = sg_pt_dummy.lo
@OS_OPENBSD_TRUE@am__objects_12 = sg_pt_dummy.lo
@OS_OTHER_TRUE@am__objects_13 = sg_pt_dummy.lo
-am_libsgutils2_la_OBJECTS = sg_lib.lo sg_lib_data.lo sg_lib_names.lo \
- sg_cmds_basic.lo sg_cmds_basic2.lo sg_cmds_extra.lo \
- sg_cmds_mmc.lo sg_pt_common.lo sg_json_builder.lo \
- $(am__objects_1) $(am__objects_2) $(am__objects_3) \
- $(am__objects_4) $(am__objects_5) $(am__objects_6) \
- $(am__objects_7) $(am__objects_8) $(am__objects_9) \
- $(am__objects_10) $(am__objects_11) $(am__objects_12) \
- $(am__objects_13)
+am_libsgutils2_la_OBJECTS = sg_lib.lo sg_pr2serr.lo sg_lib_data.lo \
+ sg_lib_names.lo sg_cmds_basic.lo sg_cmds_basic2.lo \
+ sg_cmds_extra.lo sg_cmds_mmc.lo sg_pt_common.lo \
+ sg_json_builder.lo $(am__objects_1) $(am__objects_2) \
+ $(am__objects_3) $(am__objects_4) $(am__objects_5) \
+ $(am__objects_6) $(am__objects_7) $(am__objects_8) \
+ $(am__objects_9) $(am__objects_10) $(am__objects_11) \
+ $(am__objects_12) $(am__objects_13)
libsgutils2_la_OBJECTS = $(am_libsgutils2_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -202,11 +202,11 @@ am__depfiles_remade = ./$(DEPDIR)/sg_cmds_basic.Plo \
./$(DEPDIR)/sg_cmds_mmc.Plo ./$(DEPDIR)/sg_io_linux.Plo \
./$(DEPDIR)/sg_json_builder.Plo ./$(DEPDIR)/sg_lib.Plo \
./$(DEPDIR)/sg_lib_data.Plo ./$(DEPDIR)/sg_lib_names.Plo \
- ./$(DEPDIR)/sg_pt_common.Plo ./$(DEPDIR)/sg_pt_dummy.Plo \
- ./$(DEPDIR)/sg_pt_freebsd.Plo ./$(DEPDIR)/sg_pt_haiku.Plo \
- ./$(DEPDIR)/sg_pt_linux.Plo ./$(DEPDIR)/sg_pt_linux_nvme.Plo \
- ./$(DEPDIR)/sg_pt_osf1.Plo ./$(DEPDIR)/sg_pt_solaris.Plo \
- ./$(DEPDIR)/sg_pt_win32.Plo
+ ./$(DEPDIR)/sg_pr2serr.Plo ./$(DEPDIR)/sg_pt_common.Plo \
+ ./$(DEPDIR)/sg_pt_dummy.Plo ./$(DEPDIR)/sg_pt_freebsd.Plo \
+ ./$(DEPDIR)/sg_pt_haiku.Plo ./$(DEPDIR)/sg_pt_linux.Plo \
+ ./$(DEPDIR)/sg_pt_linux_nvme.Plo ./$(DEPDIR)/sg_pt_osf1.Plo \
+ ./$(DEPDIR)/sg_pt_solaris.Plo ./$(DEPDIR)/sg_pt_win32.Plo
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -377,13 +377,14 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-libsgutils2_la_SOURCES = sg_lib.c sg_lib_data.c sg_lib_names.c \
- sg_cmds_basic.c sg_cmds_basic2.c sg_cmds_extra.c sg_cmds_mmc.c \
- sg_pt_common.c sg_json_builder.c $(am__append_1) \
- $(am__append_2) $(am__append_3) $(am__append_4) \
- $(am__append_5) $(am__append_6) $(am__append_7) \
- $(am__append_8) $(am__append_9) $(am__append_10) \
- $(am__append_11) $(am__append_12) $(am__append_13)
+libsgutils2_la_SOURCES = sg_lib.c sg_pr2serr.c sg_lib_data.c \
+ sg_lib_names.c sg_cmds_basic.c sg_cmds_basic2.c \
+ sg_cmds_extra.c sg_cmds_mmc.c sg_pt_common.c sg_json_builder.c \
+ $(am__append_1) $(am__append_2) $(am__append_3) \
+ $(am__append_4) $(am__append_5) $(am__append_6) \
+ $(am__append_7) $(am__append_8) $(am__append_9) \
+ $(am__append_10) $(am__append_11) $(am__append_12) \
+ $(am__append_13)
@DEBUG_FALSE@DBG_CFLAGS =
# This is active if --enable-debug given to ./configure
@@ -496,6 +497,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_data.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_names.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pr2serr.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_common.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_dummy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_pt_freebsd.Plo@am__quote@ # am--include-marker
@@ -675,6 +677,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/sg_lib.Plo
-rm -f ./$(DEPDIR)/sg_lib_data.Plo
-rm -f ./$(DEPDIR)/sg_lib_names.Plo
+ -rm -f ./$(DEPDIR)/sg_pr2serr.Plo
-rm -f ./$(DEPDIR)/sg_pt_common.Plo
-rm -f ./$(DEPDIR)/sg_pt_dummy.Plo
-rm -f ./$(DEPDIR)/sg_pt_freebsd.Plo
@@ -738,6 +741,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/sg_lib.Plo
-rm -f ./$(DEPDIR)/sg_lib_data.Plo
-rm -f ./$(DEPDIR)/sg_lib_names.Plo
+ -rm -f ./$(DEPDIR)/sg_pr2serr.Plo
-rm -f ./$(DEPDIR)/sg_pt_common.Plo
-rm -f ./$(DEPDIR)/sg_pt_dummy.Plo
-rm -f ./$(DEPDIR)/sg_pt_freebsd.Plo
diff --git a/lib/sg_json_builder.c b/lib/sg_json_builder.c
index 4d616989..ed2d1397 100644
--- a/lib/sg_json_builder.c
+++ b/lib/sg_json_builder.c
@@ -35,6 +35,10 @@
#include <stdlib.h>
#include <stdio.h>
+/* This code was fetched from https://github.com/json-parser/json-builder
+ * and comes with the 2 clause BSD license (shown above) which is the same
+ * license that most of the rest of this package uses. */
+
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
diff --git a/lib/sg_json_builder.h b/lib/sg_json_builder.h
index 9d83b026..9027ed5b 100644
--- a/lib/sg_json_builder.h
+++ b/lib/sg_json_builder.h
@@ -28,11 +28,20 @@
* SUCH DAMAGE.
*/
-#ifndef _JSON_BUILDER_H
-#define _JSON_BUILDER_H
+#ifndef SG_JSON_BUILDER_H
+#define SG_JSON_BUILDER_H
+
+/* This code was fetched from https://github.com/json-parser/json-builder
+ * and comes with the 2 clause BSD license (shown above) which is the same
+ * license that most of the rest of this package uses.
+ *
+ * This header file is in this 'lib' directory so its interface is _not_
+ * published with sg3_utils other header files found in the 'include'
+ * directory. Currently only this header's implementation (i.e.
+ * sg_json_builder.c) and sg_pr2serr.c are the only users of this header. */
/*
- * Used to require json.h from json-parser but what was need as been
+ * Used to require json.h from json-parser but what was needed as been
* included in this header.
* https://github.com/udp/json-parser
*/
@@ -318,7 +327,7 @@ void json_builder_free (json_value *);
}
#endif
-#endif
+#endif /* SG_JSON_BUILDER_H */
diff --git a/lib/sg_lib.c b/lib/sg_lib.c
index 36af6e6f..86fd37dc 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -75,20 +75,6 @@ pr2ws(const char * fmt, ...)
return n;
}
-/* Users of the sg_pr2serr.h header need this function definition */
-int
-pr2serr(const char * fmt, ...)
-{
- va_list args;
- int n;
-
- va_start(args, fmt);
- n = vfprintf(stderr, fmt, args);
- va_end(args);
- return n;
-}
-
-
/* Want safe, 'n += snprintf(b + n, blen - n, ...)' style sequence of
* functions. Returns number of chars placed in cp excluding the
* trailing null char. So for cp_max_len > 0 the return value is always
diff --git a/lib/sg_pr2serr.c b/lib/sg_pr2serr.c
new file mode 100644
index 00000000..5bb87acf
--- /dev/null
+++ b/lib/sg_pr2serr.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 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.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "sg_pr2serr.h"
+#include "sg_json_builder.h"
+
+/*
+ * #define json_serialize_mode_multiline 0
+ * #define json_serialize_mode_single_line 1
+ * #define json_serialize_mode_packed 2
+ *
+ * #define json_serialize_opt_CRLF (1 << 1)
+ * #define json_serialize_opt_pack_brackets (1 << 2)
+ * #define json_serialize_opt_no_space_after_comma (1 << 3)
+ * #define json_serialize_opt_no_space_after_colon (1 << 4)
+ * #define json_serialize_opt_use_tabs (1 << 5)
+ */
+
+
+static const json_serialize_opts def_out_settings = {
+ json_serialize_mode_multiline, /* one of serialize_mode_* */
+ 0, /* serialize_opt_* OR-ed together */
+ 4 /* indent size */
+};
+
+/* Users of the sg_pr2serr.h header need this function definition */
+int
+pr2serr(const char * fmt, ...)
+{
+ va_list args;
+ int n;
+
+ va_start(args, fmt);
+ n = vfprintf(stderr, fmt, args);
+ va_end(args);
+ return n;
+}
+
+void
+sg_json_init_state(sg_json_state * jstp)
+{
+ jstp->pr_as_json = true;
+ jstp->pr_pretty = true;
+ jstp->pr_sorted = false;
+ jstp->pr_output = false;
+ jstp->pr_implemented = false;
+ jstp->pr_unimplemented = false;
+ jstp->pr_format = 0;
+ jstp->verbose = 0;
+ jstp->pr_indent_size = 4;
+ jstp->basep = NULL;
+ jstp->outputp = NULL;
+ jstp->userp = NULL;
+}
+
+sg_json_opaque_p
+sg_json_start(const char * util_name, const char * ver_str, int argc,
+ char *argv[], sg_json_state * jstp)
+{
+ int k;
+ json_value * jvp = json_object_new(0);
+ json_value * jv2p;
+ json_value * jap = json_array_new(0);
+
+ if ((NULL == jvp) || (NULL == jap)) {
+ if (jvp)
+ json_builder_free(jvp);
+ if (jap)
+ json_builder_free(jap);
+ return NULL;
+ }
+ jstp->basep = jvp;
+ json_array_push(jap, json_integer_new(1));
+ json_array_push(jap, json_integer_new(0));
+ json_object_push(jvp, "json_format_version", jap);
+ jap = json_array_new(0);
+ for (k = 0; k < argc; ++k)
+ json_array_push(jap, json_string_new(argv[k]));
+ jv2p = json_object_push(jvp, util_name, json_object_new(0));
+ if (ver_str)
+ json_object_push(jv2p, "version_date", json_string_new(ver_str));
+ else
+ json_object_push(jv2p, "version_date", json_string_new("0.0"));
+ json_object_push(jv2p, "argv", jap);
+ if (jstp->pr_output)
+ jstp->outputp = json_object_push(jv2p, "output", json_array_new(0));
+ return jvp;
+}
+
+void
+sgj_pr2file(sg_json_opaque_p jop, sg_json_state * jstp, FILE * fp)
+{
+ size_t len;
+ char * b;
+ json_value * jvp = (json_value *)jop;
+ json_serialize_opts out_settings;
+
+ memcpy(&out_settings, &def_out_settings, sizeof(out_settings));
+ if (jstp->pr_indent_size != def_out_settings.indent_size)
+ out_settings.indent_size = jstp->pr_indent_size;
+ if (! jstp->pr_pretty)
+ out_settings.mode = json_serialize_mode_single_line;
+
+ len = json_measure_ex(jvp, out_settings);
+ if (len < 1)
+ return;
+ if (jstp->verbose > 3)
+ fprintf(fp, "%s: serialization length: %zu bytes\n", __func__, len);
+ b = calloc(len, 1);
+ if (NULL == b) {
+ if (jstp->verbose > 3)
+ pr2serr("%s: unable to get %zu bytes on heap\n", __func__, len);
+ return;
+ }
+ json_serialize_ex(b, jvp, out_settings);
+ if (jstp->verbose > 3)
+ fprintf(fp, "json serialized:\n");
+ fprintf(fp, "%s\n", b);
+}
+
+void
+sg_json_free(sg_json_opaque_p jop)
+{
+ json_value * jvp = (json_value *)jop;
+
+ json_builder_free(jvp);
+}
+
+void
+sgj_pr_hr(sg_json_state * jsp, const char * fmt, ...)
+{
+ va_list args;
+
+ if (jsp->pr_as_json && jsp->pr_output) {
+ size_t len;
+ char b[256];
+
+ va_start(args, fmt);
+ len = vsnprintf(b, sizeof(b), fmt, args);
+ if ((len > 0) && (len < sizeof(b))) {
+ const char * cp = b;
+
+ if (b[len - 1] == '\n') {
+ b[len - 1] = '\0';
+ --len;
+ }
+ if ((len > 0) && ('\n' == b[0]))
+ ++cp;
+ json_array_push(jsp->outputp, json_string_new(cp));
+ }
+ va_end(args);
+ } else if (jsp->pr_as_json) {
+ va_start(args, fmt);
+ va_end(args);
+ } else {
+ va_start(args, fmt);
+ vfprintf(stdout, fmt, args);
+ va_end(args);
+ }
+}
+
+sg_json_opaque_p
+sgj_new_named_object(sg_json_state * jsp, sg_json_opaque_p jop,
+ const char * name)
+{
+ sg_json_opaque_p resp = NULL;
+
+ if (jsp->pr_as_json)
+ resp = json_object_push(jop, name, json_object_new(0));
+ return resp;
+}
+
+sg_json_opaque_p
+sgj_new_named_array(sg_json_state * jsp, sg_json_opaque_p jop,
+ const char * name)
+{
+ sg_json_opaque_p resp = NULL;
+
+ if (jsp->pr_as_json)
+ resp = json_object_push(jop, name, json_array_new(0));
+ return resp;
+}
+
+sg_json_opaque_p
+sgj_add_array_element(sg_json_state * jsp, sg_json_opaque_p jap,
+ sg_json_opaque_p ejop)
+{
+ if (jsp->pr_as_json)
+ return json_array_push(jap, ejop);
+ else
+ return NULL;
+}
+
+/* Newly created object is un-attached */
+sg_json_opaque_p
+sgj_new_object(sg_json_state * jsp)
+{
+ return jsp->pr_as_json ? json_object_new(0) : NULL;
+}
+
+sg_json_opaque_p
+sgj_add_name_vs(sg_json_state * jsp, sg_json_opaque_p jop, const char * name,
+ const char * value)
+{
+ if (jsp->pr_as_json)
+ return json_object_push(jop, name, json_string_new(value));
+ else
+ return NULL;
+}
+
+sg_json_opaque_p
+sgj_add_name_vi(sg_json_state * jsp, sg_json_opaque_p jop, const char * name,
+ int64_t value)
+{
+ if (jsp->pr_as_json)
+ return json_object_push(jop, name, json_integer_new(value));
+ else
+ return NULL;
+}
+
+sg_json_opaque_p
+sgj_add_name_vb(sg_json_state * jsp, sg_json_opaque_p jop, const char * name,
+ bool value)
+{
+ if (jsp->pr_as_json)
+ return json_object_push(jop, name, json_boolean_new(value));
+ else
+ return NULL;
+}
diff --git a/src/sg_dd.c b/src/sg_dd.c
index 65f76985..10ede6fd 100644
--- a/src/sg_dd.c
+++ b/src/sg_dd.c
@@ -70,7 +70,7 @@
#include "sg_unaligned.h"
#include "sg_pr2serr.h"
-static const char * version_str = "6.32 20220118";
+static const char * version_str = "6.33 20220418";
#define ME "sg_dd: "
@@ -1803,7 +1803,6 @@ main(int argc, char * argv[])
int ret = 0;
int64_t skip = 0;
int64_t seek = 0;
- int64_t out2_off = 0;
int64_t in_num_sect = -1;
int64_t out_num_sect = -1;
char * key;
@@ -2486,7 +2485,6 @@ main(int argc, char * argv[])
break;
}
bytes_of2 = res;
- out2_off += res;
}
if (oflag.sparse && (dd_count > blocks) &&
diff --git a/src/sg_opcodes.c b/src/sg_opcodes.c
index 55530511..14216a5e 100644
--- a/src/sg_opcodes.c
+++ b/src/sg_opcodes.c
@@ -8,8 +8,8 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This program outputs information provided by a SCSI REPORT SUPPORTED
- * OPERATION CODES [0xa3/0xc] and REPORT SUPPORTED TASK MANAGEMENT
- * FUNCTIONS [0xa3/0xd] commands.
+ * OPERATION CODES [0xa3/0xc] (RSOC) and REPORT SUPPORTED TASK MANAGEMENT
+ * FUNCTIONS [0xa3/0xd] (RSTMF) commands.
*/
#include <unistd.h>
@@ -33,8 +33,9 @@
#include "sg_pt.h"
-static const char * version_str = "0.74 20220312"; /* spc6r06 */
+static const char * version_str = "0.75 20220420"; /* spc6r06 */
+#define MY_NAME "sg_opcodes"
#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
#define DEF_TIMEOUT_SECS 60
@@ -59,6 +60,9 @@ static struct option long_options[] = {
{"enumerate", no_argument, 0, 'e'},
{"help", no_argument, 0, 'h'},
{"hex", no_argument, 0, 'H'},
+ {"inhex", required_argument, 0, 'i'},
+ {"in", required_argument, 0, 'i'},
+ {"json", optional_argument, 0, 'j'},
{"mask", no_argument, 0, 'm'},
{"mlu", no_argument, 0, 'M'}, /* added in spc5r20 */
{"no-inquiry", no_argument, 0, 'n'},
@@ -86,7 +90,7 @@ struct opts_t {
bool do_mask;
bool do_mlu;
bool do_raw;
- bool do_rctd;
+ bool do_rctd; /* Return command timeout descriptor */
bool do_repd;
bool do_unsorted;
bool do_taskman;
@@ -99,6 +103,8 @@ struct opts_t {
int servact;
int verbose;
const char * device_name;
+ const char * inhex_fn;
+ sg_json_state json_st;
};
@@ -107,12 +113,13 @@ usage()
{
pr2serr("Usage: sg_opcodes [--alpha] [--compact] [--enumerate] "
"[--help] [--hex]\n"
- " [--mask] [--mlu] [--no-inquiry] "
- "[--opcode=OP[,SA]]\n"
- " [--pdt=DT] [--raw] [--rctd] [--repd] "
- "[--sa=SA] [--tmf]\n"
- " [--unsorted] [--verbose] [--version] "
- "DEVICE\n"
+ " [--inhex=FN] [--json[=JO]] [--mask] [--mlu] "
+ "[--no-inquiry]\n"
+ " [--opcode=OP[,SA]] [--pdt=DT] [--raw] "
+ "[--rctd]\n"
+ " [--repd] [--sa=SA] [--tmf] [--unsorted] "
+ "[--verbose]\n"
+ " [--version] DEVICE\n"
" where:\n"
" --alpha|-a output list of operation codes sorted "
"alphabetically\n"
@@ -121,7 +128,17 @@ usage()
"name,\n"
" ignore DEVICE\n"
" --help|-h print usage message then exit\n"
- " --hex|-H output response in hex\n"
+ " --hex|-H output response in hex, use -HHH for "
+ "hex\n"
+ " suitable for later use of --inhex= "
+ "option\n"
+ " --inhex=FN|-i FN contents of file FN treated as hex "
+ "and used\n"
+ " instead of DEVICE which is ignored\n"
+ " --json[=JO]|-jJO output in JSON instead of human "
+ "readable\n"
+ " text. Optional argument JO see manpage "
+ "sg3_utils\n"
" --mask|-m show cdb usage data (a mask) when "
"all listed\n"
" --mlu|-M show MLU bit when all listed\n"
@@ -131,7 +148,10 @@ usage()
" --pdt=DT|-p DT give peripheral device type for "
"'--no-inquiry'\n"
" '--enumerate'\n"
- " --raw|-r output response in binary to stdout\n"
+ " --raw|-r output response in binary to stdout unless "
+ "--inhex=FN\n"
+ " is given then FN is parsed as binary "
+ "instead\n"
" --rctd|-R set RCTD (return command timeout "
"descriptor) bit\n"
" --repd|-q set Report Extended Parameter Data bit, "
@@ -154,10 +174,11 @@ usage()
static void
usage_old()
{
- pr2serr("Usage: sg_opcodes [-a] [-c] [-e] [-H] [-m] [-M] [-n] [-o=OP] "
- "[-p=DT]\n"
- " [-q] [-r] [-R] [-s=SA] [-t] [-u] [-v] [-V] "
- "DEVICE\n"
+ pr2serr("Usage: sg_opcodes [-a] [-c] [-e] [-H] [-j] [-m] [-M] [-n] "
+ "[-o=OP]\n"
+ " [-p=DT] [-q] [-r] [-R] [-s=SA] [-t] [-u] "
+ "[-v] [-V]\n"
+ " DEVICE\n"
" where:\n"
" -a output list of operation codes sorted "
"alphabetically\n"
@@ -165,6 +186,7 @@ usage_old()
" -e use '--opcode=' and '--pdt=' to look up name, "
"ignore DEVICE\n"
" -H print response in hex\n"
+ " -j print response in JSON\n"
" -m show cdb usage data (a mask) when all listed\n"
" -M show MLU bit when all listed\n"
" -n don't output INQUIRY information\n"
@@ -312,15 +334,18 @@ do_rstmf(struct sg_pt_base * ptvp, bool repd, void * resp, int mx_resp_len,
static int
new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
{
- int c, n;
+ bool bad_arg = false;
+ bool pr_verbose = false;
+ char bad_ch;
+ int c, n, k;
char * cp;
char b[32];
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "acehHmMnNo:Op:qrRs:tuvV", long_options,
- &option_index);
+ c = getopt_long(argc, argv, "acehHi:j::mMnNo:Op:qrRs:tuvV",
+ long_options, &option_index);
if (c == -1)
break;
@@ -341,6 +366,58 @@ new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
case 'H':
++op->do_hex;
break;
+ case 'i':
+ op->inhex_fn = optarg;
+ break;
+ case 'j':
+ sg_json_init_state(&op->json_st);
+ if (optarg) {
+ for (k = 0; optarg[k]; ++k) {
+ switch (optarg[k]) {
+ case '0':
+ op->json_st.pr_indent_size = 16;
+ break;
+ case '2':
+ op->json_st.pr_indent_size = 2;
+ break;
+ case '4':
+ op->json_st.pr_indent_size = 4;
+ break;
+ case '8':
+ op->json_st.pr_indent_size = 8;
+ break;
+ case 'c':
+ op->json_st.pr_pretty = false;
+ break;
+ case 'g':
+ op->json_st.pr_format = 'g';
+ break;
+ case 'i':
+ op->json_st.pr_implemented = true;
+ break;
+ case 'o':
+ op->json_st.pr_output = true;
+ break;
+ case 's':
+ op->json_st.pr_sorted = true;
+ break;
+ case 'u':
+ op->json_st.pr_unimplemented = true;
+ break;
+ case 'v':
+ pr_verbose = true;
+ break;
+ case 'y':
+ op->json_st.pr_format = 'g';
+ break;
+ default:
+ bad_arg = true;
+ bad_ch = optarg[k];
+ break;
+ }
+ }
+ }
+ break;
case 'm':
op->do_mask = true;
break;
@@ -450,6 +527,17 @@ new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
return SG_LIB_SYNTAX_ERROR;
}
}
+ if (pr_verbose) {
+ if (op->verbose_given)
+ op->json_st.verbose = op->verbose;
+ else
+ op->json_st.verbose = 4;
+ }
+ if (bad_arg) {
+ pr2serr("Unrecognized argument character: %c given to --json=\n",
+ bad_ch);
+ return SG_LIB_SYNTAX_ERROR;
+ }
return 0;
}
@@ -480,6 +568,9 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
case 'H':
++op->do_hex;
break;
+ case 'j': /* don't accept argument with this old syntax */
+ sg_json_init_state(&op->json_st);
+ break;
case 'm':
op->do_mask = true;
break;
@@ -529,7 +620,9 @@ old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
}
if (plen <= 0)
continue;
- if (0 == strncmp("o=", cp, 2)) {
+ if (0 == strncmp("i=", cp, 2))
+ op->inhex_fn = cp + 2;
+ else if (0 == strncmp("o=", cp, 2)) {
num = sscanf(cp + 2, "%x", (unsigned int *)&n);
if ((1 != num) || (n > 255)) {
pr2serr("Bad number after 'o=' option\n");
@@ -664,62 +757,72 @@ opcode_alpha_compare(const void * left, const void * right)
return strncmp(l_name_buff, r_name_buff, NAME_BUFF_SZ);
}
+/* For decoding a RSOC command's "All_commands" parameter data */
static int
list_all_codes(uint8_t * rsoc_buff, int rsoc_len, struct opts_t * op,
struct sg_pt_base * ptvp)
{
bool sa_v;
- int k, j, m, cd_len, serv_act, len, act_len, opcode, res;
+ int k, j, m, n, cd_len, serv_act, len, act_len, opcode, res;
uint8_t byt5;
unsigned int timeout;
uint8_t * bp;
uint8_t ** sort_arr = NULL;
+ sg_json_state * jsp = &op->json_st;
+ sg_json_opaque_p jap = NULL;
+ sg_json_opaque_p jop = NULL;
char name_buff[NAME_BUFF_SZ];
char sa_buff[8];
+ char b[192];
+ const int blen = sizeof(b);
cd_len = sg_get_unaligned_be32(rsoc_buff + 0);
if (cd_len > (rsoc_len - 4)) {
- printf("sg_opcodes: command data length=%d, allocation=%d; "
- "truncate\n", cd_len, rsoc_len - 4);
+ sgj_pr_hr(jsp, "sg_opcodes: command data length=%d, allocation=%d; "
+ "truncate\n", cd_len, rsoc_len - 4);
cd_len = ((rsoc_len - 4) / 8) * 8;
}
if (0 == cd_len) {
- printf("sg_opcodes: no commands to display\n");
+ sgj_pr_hr(jsp, "sg_opcodes: no commands to display\n");
return 0;
}
if (op->do_rctd) { /* Return command timeout descriptor */
if (op->do_compact) {
- printf("\nOpcode,sa Nominal Recommended Name\n");
- printf( " (hex) timeout timeout(sec) \n");
- printf("-----------------------------------------------"
- "---------\n");
+ sgj_pr_hr(jsp, "\nOpcode,sa Nominal Recommended Name\n");
+ sgj_pr_hr(jsp, " (hex) timeout timeout(sec) \n");
+ sgj_pr_hr(jsp, "-----------------------------------------------"
+ "---------\n");
} else {
- printf("\nOpcode Service CDB Nominal Recommended Name\n");
- printf( "(hex) action(h) size timeout timeout(sec) \n");
- printf("-------------------------------------------------------"
- "---------\n");
+ sgj_pr_hr(jsp, "\nOpcode Service CDB Nominal Recommended "
+ "Name\n");
+ sgj_pr_hr(jsp, "(hex) action(h) size timeout timeout(sec) "
+ " \n");
+ sgj_pr_hr(jsp, "-------------------------------------------------"
+ "---------------\n");
}
} else { /* RCTD clear in cdb */
if (op->do_compact) {
- printf("\nOpcode,sa Name\n");
- printf( " (hex) \n");
- printf("---------------------------------------\n");
+ sgj_pr_hr(jsp, "\nOpcode,sa Name\n");
+ sgj_pr_hr(jsp, " (hex) \n");
+ sgj_pr_hr(jsp, "---------------------------------------\n");
} else if (op->do_mlu) {
- printf("\nOpcode Service CDB MLU Name\n");
- printf( "(hex) action(h) size \n");
- printf("-----------------------------------------------\n");
+ sgj_pr_hr(jsp, "\nOpcode Service CDB MLU Name\n");
+ sgj_pr_hr(jsp, "(hex) action(h) size \n");
+ sgj_pr_hr(jsp, "-------------------------------------------"
+ "----\n");
} else {
- printf("\nOpcode Service CDB RWCDLP, Name\n");
- printf( "(hex) action(h) size CDLP \n");
- printf("-----------------------------------------------\n");
+ sgj_pr_hr(jsp, "\nOpcode Service CDB RWCDLP, Name\n");
+ sgj_pr_hr(jsp, "(hex) action(h) size CDLP \n");
+ sgj_pr_hr(jsp, "-------------------------------------------"
+ "----\n");
}
}
/* SPC-4 does _not_ require any ordering of opcodes in the response */
if (! op->do_unsorted) {
sort_arr = (uint8_t **)calloc(cd_len, sizeof(uint8_t *));
if (NULL == sort_arr) {
- printf("sg_opcodes: no memory to sort operation codes, "
- "try '-u'\n");
+ pr2serr("sg_opcodes: no memory to sort operation codes, "
+ "try '-u'\n");
return sg_convert_errno(ENOMEM);
}
memset(sort_arr, 0, cd_len * sizeof(uint8_t *));
@@ -731,13 +834,18 @@ list_all_codes(uint8_t * rsoc_buff, int rsoc_len, struct opts_t * op,
qsort(sort_arr, j, sizeof(uint8_t *),
(op->do_alpha ? opcode_alpha_compare : opcode_num_compare));
}
+
+ jap = sgj_new_named_array(jsp, jsp->basep, "all_command_descriptor");
for (k = 0, j = 0; k < cd_len; ++j, k += len) {
+ jop = sgj_new_object(jsp);
+
bp = op->do_unsorted ? (rsoc_buff + 4 + k) : sort_arr[j];
byt5 = bp[5];
len = (byt5 & 0x2) ? 20 : 8;
opcode = bp[0];
- sa_v = !!(byt5 & 1);
+ sa_v = !!(byt5 & 1); /* service action valid */
serv_act = 0;
+ name_buff[0] = '\0';
if (sa_v) {
serv_act = sg_get_unaligned_be16(bp + 2);
sg_get_opcode_sa_name(opcode, serv_act, peri_dtype, NAME_BUFF_SZ,
@@ -751,33 +859,35 @@ list_all_codes(uint8_t * rsoc_buff, int rsoc_len, struct opts_t * op,
memset(sa_buff, ' ', sizeof(sa_buff));
}
if (op->do_rctd) {
+ n = 0;
if (byt5 & 0x2) { /* CTDP set */
/* don't show CDLP because it makes line too long */
if (op->do_compact)
- printf(" %.2x%c%.4s", opcode, (sa_v ? ',' : ' '),
- sa_buff);
+ n += sg_scnpr(b + n, blen - n, " %.2x%c%.4s", opcode,
+ (sa_v ? ',' : ' '), sa_buff);
else
- printf(" %.2x %.4s %3d", opcode, sa_buff,
- sg_get_unaligned_be16(bp + 6));
+ n += sg_scnpr(b + n, blen - n, " %.2x %.4s %3d",
+ opcode,
+ sa_buff, sg_get_unaligned_be16(bp + 6));
timeout = sg_get_unaligned_be32(bp + 12);
if (0 == timeout)
- printf(" -");
+ n += sg_scnpr(b + n, blen - n, " -");
else
- printf(" %8u", timeout);
+ n += sg_scnpr(b + n, blen - n, " %8u", timeout);
timeout = sg_get_unaligned_be32(bp + 16);
if (0 == timeout)
- printf(" -");
+ n += sg_scnpr(b + n, blen - n, " -");
else
- printf(" %8u", timeout);
- printf(" %s\n", name_buff);
+ n += sg_scnpr(b + n, blen - n, " %8u", timeout);
+ sgj_pr_hr(jsp, "%s %s\n", b, name_buff);
} else /* CTDP clear */
if (op->do_compact)
- printf(" %.2x%c%.4s %s\n", opcode,
- (sa_v ? ',' : ' '), sa_buff, name_buff);
+ sgj_pr_hr(jsp, " %.2x%c%.4s %s\n",
+ opcode, (sa_v ? ',' : ' '), sa_buff, name_buff);
else
- printf(" %.2x %.4s %3d "
- "%s\n", opcode, sa_buff,
- sg_get_unaligned_be16(bp + 6), name_buff);
+ sgj_pr_hr(jsp, " %.2x %.4s %3d "
+ " %s\n", opcode, sa_buff,
+ sg_get_unaligned_be16(bp + 6), name_buff);
} else { /* RCTD clear in cdb */
/* before version 0.69 treated RWCDLP (1 bit) and CDLP (2 bits),
* as a 3 bit field, now break them out separately */
@@ -785,36 +895,73 @@ list_all_codes(uint8_t * rsoc_buff, int rsoc_len, struct opts_t * op,
int cdlp = !!(0x40 & byt5);
if (op->do_compact)
- printf(" %.2x%c%.4s %s\n", bp[0], (sa_v ? ',' : ' '),
- sa_buff, name_buff);
+ sgj_pr_hr(jsp, " %.2x%c%.4s %s\n", bp[0],
+ (sa_v ? ',' : ' '), sa_buff, name_buff);
else if (op->do_mlu)
- printf(" %.2x %.4s %3d %3d %s\n", bp[0],
- sa_buff, sg_get_unaligned_be16(bp + 6),
- ((byt5 >> 4) & 0x3), name_buff);
+ sgj_pr_hr(jsp, " %.2x %.4s %3d %3d %s\n",
+ bp[0], sa_buff, sg_get_unaligned_be16(bp + 6),
+ ((byt5 >> 4) & 0x3), name_buff);
else
- printf(" %.2x %.4s %3d %d,%d %s\n", bp[0],
- sa_buff, sg_get_unaligned_be16(bp + 6), rwcdlp, cdlp,
- name_buff);
+ sgj_pr_hr(jsp, " %.2x %.4s %3d %d,%d %s\n",
+ bp[0], sa_buff, sg_get_unaligned_be16(bp + 6),
+ rwcdlp, cdlp, name_buff);
}
- if (op->do_mask) {
+ if (jsp->pr_as_json) {
+ snprintf(b, blen, "0x%x", opcode);
+ sgj_add_name_vs(jsp, jop, "operation_code", b);
+ if (sa_v) {
+ snprintf(b, blen, "0x%x", serv_act);
+ sgj_add_name_vs(jsp, jop, "service_action", b);
+ }
+ if (name_buff[0])
+ sgj_add_name_vs(jsp, jop, "name", name_buff);
+ sgj_add_name_vi(jsp, jop, "rwcdlp", (byt5 >> 6) & 0x1);
+ sgj_add_name_vi(jsp, jop, "mlu", (byt5 >> 4) & 0x3);
+ sgj_add_name_vi(jsp, jop, "cdlp", (byt5 >> 2) & 0x3);
+ sgj_add_name_vi(jsp, jop, "ctdp", (byt5 >> 1) & 0x1);
+ sgj_add_name_vi(jsp, jop, "servactv", byt5 & 0x1);
+ sgj_add_name_vi(jsp, jop, "cdb_length",
+ sg_get_unaligned_be16(bp + 6));
+
+ sgj_add_array_element(jsp, jap, jop);
+ }
+
+ if (op->do_mask && ptvp) {
int cdb_sz;
- uint8_t b[64];
+ uint8_t d[64];
- memset(b, 0, sizeof(b));
+ n = 0;
+ memset(d, 0, sizeof(d));
res = do_rsoc(ptvp, false, (sa_v ? 2 : 1), opcode, serv_act,
- b, sizeof(b), &act_len, true, op->verbose);
+ d, sizeof(d), &act_len, true, op->verbose);
if (0 == res) {
- cdb_sz = sg_get_unaligned_be16(b + 2);
+ int nn;
+
+ cdb_sz = sg_get_unaligned_be16(d + 2);
cdb_sz = (cdb_sz < act_len) ? cdb_sz : act_len;
if ((cdb_sz > 0) && (cdb_sz <= 80)) {
if (op->do_compact)
- printf(" usage: ");
+ n += sg_scnpr(b + n, blen - n,
+ " usage: ");
else
- printf(" cdb usage: ");
- for (m = 0; (m < cdb_sz) && ((4 + m) < (int)sizeof(b));
+ n += sg_scnpr(b + n, blen - n, " cdb usage: ");
+ nn = n;
+ for (m = 0; (m < cdb_sz) && ((4 + m) < (int)sizeof(d));
++m)
- printf("%.2x ", b[4 + m]);
- printf("\n");
+ n += sg_scnpr(b + n, blen - n, "%.2x ", d[4 + m]);
+ sgj_pr_hr(jsp, "%s\n", b);
+ if (jsp->pr_as_json) {
+ int l;
+ char *b2p = b + nn;
+ sg_json_opaque_p jo2p = sgj_new_named_object(jsp, jop,
+ "one_command_descriptor");
+
+ l = strlen(b2p);
+ if ((l > 0) && (' ' == b2p[l - 1]))
+ b2p[l - 1] = '\0';
+ sgj_add_name_vi(jsp, jo2p, "cdb_size", cdb_sz);
+ sgj_add_name_vs(jsp, jo2p, "cdb_usage_data", b2p);
+ }
}
} else
goto err_out;
@@ -828,10 +975,12 @@ err_out:
}
static void
-decode_cmd_timeout_desc(uint8_t * dp, int max_b_len, char * b)
+decode_cmd_timeout_desc(uint8_t * dp, int max_b_len, char * b,
+ struct opts_t * op)
{
int len;
unsigned int timeout;
+ sg_json_state * jsp = &op->json_st;
if ((max_b_len < 2) || (NULL == dp))
return;
@@ -848,6 +997,11 @@ decode_cmd_timeout_desc(uint8_t * dp, int max_b_len, char * b)
snprintf(b, max_b_len, "no nominal timeout, ");
else
snprintf(b, max_b_len, "nominal timeout: %u secs, ", timeout);
+ if (jsp->pr_as_json) {
+ sgj_add_name_vi(jsp, jsp->userp, "command_specific", dp[3]);
+ sgj_add_name_vi(jsp, jsp->userp,
+ "nominal_command_processing_timeout", timeout);
+ }
len = strlen(b);
max_b_len -= len;
b += len;
@@ -856,33 +1010,45 @@ decode_cmd_timeout_desc(uint8_t * dp, int max_b_len, char * b)
snprintf(b, max_b_len, "no recommended timeout");
else
snprintf(b, max_b_len, "recommended timeout: %u secs", timeout);
+ if (jsp->pr_as_json)
+ sgj_add_name_vi(jsp, jsp->userp,
+ "recommended_command_timeout", timeout);
return;
}
-/* One command descriptor (includes cdb usage data) */
+/* For decoding a RSOC command's "One_command" parameter data which includes
+ * cdb usage data. */
static void
list_one(uint8_t * rsoc_buff, int cd_len, int rep_opts,
struct opts_t * op)
{
bool valid = false;
- int k, mlu, rwcdlp;
+ int k, mlu, cdlp, rwcdlp, support, ctdp;
+ int n = 0;
uint8_t * bp;
const char * cp;
const char * dlp;
const char * mlu_p;
+ sg_json_state * jsp = &op->json_st;
+ sg_json_opaque_p jop = NULL;
char name_buff[NAME_BUFF_SZ];
- char b[64];
+ char d[64];
+ char b[192];
+ const int blen = sizeof(b);
- printf("\n Opcode=0x%.2x", op->opcode);
+ jop = sgj_new_named_object(jsp, jsp->basep, "one_command_descriptor");
+ n += sg_scnpr(b + n, blen - n, "\n Opcode=0x%.2x", op->opcode);
if (rep_opts > 1)
- printf(" Service_action=0x%.4x", op->servact);
- printf("\n");
+ n += sg_scnpr(b + n, blen - n, " Service_action=0x%.4x", op->servact);
+ sgj_pr_hr(jsp, "%s\n", b);
sg_get_opcode_sa_name(((op->opcode > 0) ? op->opcode : 0),
((op->servact > 0) ? op->servact : 0),
peri_dtype, NAME_BUFF_SZ, name_buff);
- printf(" Command_name: %s\n", name_buff);
- switch((int)(rsoc_buff[1] & 7)) { /* SUPPORT field */
+ sgj_pr_hr(jsp, " Command_name: %s\n", name_buff);
+ ctdp = !!(0x80 & rsoc_buff[1]);
+ support = rsoc_buff[1] & 7;
+ switch(support) {
case 0:
cp = "not currently available";
break;
@@ -903,9 +1069,9 @@ list_one(uint8_t * rsoc_buff, int cd_len, int rep_opts,
cp = name_buff;
break;
}
- k = 0x3 & (rsoc_buff[1] >> 3);
+ cdlp = 0x3 & (rsoc_buff[1] >> 3);
rwcdlp = rsoc_buff[0] & 1;
- switch (k) { /* CDLP field */
+ switch (cdlp) {
case 0:
if (rwcdlp)
dlp = "Reserved [RWCDLP=1, CDLP=0]";
@@ -928,8 +1094,8 @@ list_one(uint8_t * rsoc_buff, int cd_len, int rep_opts,
dlp = "reserved [CDLP=3]";
break;
}
- printf(" Command is %s\n", cp);
- printf(" %s\n", dlp);
+ sgj_pr_hr(jsp, " Command is %s\n", cp);
+ sgj_pr_hr(jsp, " %s\n", dlp);
mlu = 0x3 & (rsoc_buff[1] >> 5);
switch (mlu) {
case 0:
@@ -945,22 +1111,49 @@ list_one(uint8_t * rsoc_buff, int cd_len, int rep_opts,
mlu_p = "affects all LUs in this target";
break;
default:
- snprintf(b, sizeof(b), "reserved [MLU=%d]", mlu);
- mlu_p = b;
+ snprintf(d, sizeof(d), "reserved [MLU=%d]", mlu);
+ mlu_p = d;
break;
}
- printf(" Multiple Logical Units (MLU): %s\n", mlu_p);
+ sgj_pr_hr(jsp, " Multiple Logical Units (MLU): %s\n", mlu_p);
if (valid) {
- printf(" Usage data: ");
+ n = 0;
+ n += sg_scnpr(b + n, blen - n, " Usage data: ");
bp = rsoc_buff + 4;
for (k = 0; k < cd_len; ++k)
- printf("%.2x ", bp[k]);
- printf("\n");
+ n += sg_scnpr(b + n, blen - n, "%.2x ", bp[k]);
+ sgj_pr_hr(jsp, "%s\n", b);
+ }
+ if (jsp->pr_as_json) {
+ int l;
+
+ snprintf(b, blen, "0x%x", op->opcode);
+ sgj_add_name_vs(jsp, jop, "operation_code", b);
+ if (rep_opts > 1) {
+ snprintf(b, blen, "0x%x", op->servact);
+ sgj_add_name_vs(jsp, jop, "service_action", b);
+ }
+ sgj_add_name_vi(jsp, jop, "rwcdlp", rwcdlp);
+ sgj_add_name_vi(jsp, jop, "ctdp", ctdp);
+ sgj_add_name_vi(jsp, jop, "mlu", mlu);
+ sgj_add_name_vi(jsp, jop, "cdlp", cdlp);
+ sgj_add_name_vi(jsp, jop, "support", support);
+ sgj_add_name_vs(jsp, jop, "support_str", cp);
+ sgj_add_name_vi(jsp, jop, "cdb_size", cd_len);
+ n = 0;
+ for (k = 0; k < cd_len; ++k)
+ n += sg_scnpr(b + n, blen - n, "%.2x ", rsoc_buff[k + 4]);
+ l = strlen(b);
+ if ((l > 0) && (' ' == b[l - 1]))
+ b[l - 1] = '\0';
+ sgj_add_name_vs(jsp, jop, "cdb_usage_data", b);
}
- if (0x80 & rsoc_buff[1]) { /* CTDP */
+ if (ctdp) {
+ jsp->userp = sgj_new_named_object(jsp, jsp->basep,
+ "command_timeouts_descriptor");
bp = rsoc_buff + 4 + cd_len;
- decode_cmd_timeout_desc(bp, NAME_BUFF_SZ, name_buff);
- printf(" %s\n", name_buff);
+ decode_cmd_timeout_desc(bp, NAME_BUFF_SZ, name_buff, op);
+ sgj_pr_hr(jsp, " %s\n", name_buff);
}
}
@@ -968,7 +1161,8 @@ list_one(uint8_t * rsoc_buff, int cd_len, int rep_opts,
int
main(int argc, char * argv[])
{
- int cd_len, res, len, act_len, rq_len, vb;
+ bool as_json;
+ int cd_len, res, len, act_len, rq_len, in_len, vb;
int rep_opts = 0;
int sg_fd = -1;
const char * cp;
@@ -977,6 +1171,8 @@ main(int argc, char * argv[])
uint8_t * rsoc_buff = NULL;
uint8_t * free_rsoc_buff = NULL;
struct sg_pt_base * ptvp = NULL;
+ sg_json_state * jsp;
+ sg_json_opaque_p jop = NULL;
char buff[48];
char b[80];
struct sg_simple_inquiry_resp inq_resp;
@@ -996,6 +1192,11 @@ main(int argc, char * argv[])
usage_old();
return 0;
}
+ as_json = op->json_st.pr_as_json;
+ jsp = &op->json_st;
+ if (as_json) {
+ jop = sg_json_start(MY_NAME, version_str, argc, argv, &op->json_st);
+ }
#ifdef DEBUG
pr2serr("In DEBUG mode, ");
if (op->verbose_given && op->version_given) {
@@ -1014,35 +1215,9 @@ main(int argc, char * argv[])
#endif
if (op->version_given) {
pr2serr("Version string: %s\n", version_str);
- return 0;
+ goto fini;
}
vb = op->verbose;
-
- if ((NULL == op->device_name) && (! op->do_enumerate)) {
- pr2serr("No DEVICE argument given\n");
- if (op->opt_new)
- usage();
- else
- usage_old();
- return SG_LIB_SYNTAX_ERROR;
- }
- if ((-1 != op->servact) && (-1 == op->opcode)) {
- pr2serr("When '-s' is chosen, so must '-o' be chosen\n");
- if (op->opt_new)
- usage();
- else
- usage_old();
- return SG_LIB_CONTRADICT;
- }
- if (op->do_unsorted && op->do_alpha)
- pr2serr("warning: unsorted ('-u') and alpha ('-a') options chosen, "
- "ignoring alpha\n");
- if (op->do_taskman && ((-1 != op->opcode) || op->do_alpha ||
- op->do_unsorted)) {
- pr2serr("warning: task management functions ('-t') chosen so alpha "
- "('-a'),\n unsorted ('-u') and opcode ('-o') "
- "options ignored\n");
- }
if (op->do_enumerate) {
char name_buff[NAME_BUFF_SZ];
@@ -1067,6 +1242,39 @@ main(int argc, char * argv[])
printf(" %s\n", name_buff);
}
goto fini;
+ } else if (op->inhex_fn) {
+ if (op->device_name) {
+ if (! as_json)
+ pr2serr("ignoring DEVICE, best to give DEVICE or "
+ "--inhex=FN, but not both\n");
+ op->device_name = NULL;
+ }
+ } else if (NULL == op->device_name) {
+ pr2serr("No DEVICE argument given\n\n");
+ if (op->opt_new)
+ usage();
+ else
+ usage_old();
+ res = SG_LIB_SYNTAX_ERROR;
+ goto err_out;
+ }
+ if ((-1 != op->servact) && (-1 == op->opcode)) {
+ pr2serr("When '-s' is chosen, so must '-o' be chosen\n");
+ if (op->opt_new)
+ usage();
+ else
+ usage_old();
+ res = SG_LIB_CONTRADICT;
+ goto err_out;
+ }
+ if (op->do_unsorted && op->do_alpha)
+ pr2serr("warning: unsorted ('-u') and alpha ('-a') options chosen, "
+ "ignoring alpha\n");
+ if (op->do_taskman && ((-1 != op->opcode) || op->do_alpha ||
+ op->do_unsorted)) {
+ pr2serr("warning: task management functions ('-t') chosen so alpha "
+ "('-a'),\n unsorted ('-u') and opcode ('-o') "
+ "options ignored\n");
}
op_name = op->do_taskman ? "Report supported task management functions" :
"Report supported operation codes";
@@ -1080,6 +1288,29 @@ main(int argc, char * argv[])
goto err_out;
}
+ if (op->inhex_fn) {
+ if ((res = sg_f2hex_arr(op->inhex_fn, op->do_raw, false, rsoc_buff,
+ &in_len, MX_ALLOC_LEN))) {
+ if (SG_LIB_LBA_OUT_OF_RANGE == res)
+ pr2serr("decode buffer [%d] not large enough??\n",
+ MX_ALLOC_LEN);
+ goto err_out;
+ }
+ if (op->verbose > 2)
+ pr2serr("Read %d [0x%x] bytes of user supplied data\n",
+ in_len, in_len);
+ if (op->do_raw)
+ op->do_raw = false; /* can interfere on decode */
+ if (in_len < 4) {
+ pr2serr("--inhex=%s only decoded %d bytes (needs 4 at "
+ "least)\n", op->inhex_fn, in_len);
+ res = SG_LIB_SYNTAX_ERROR;
+ goto err_out;
+ }
+ res = 0;
+ act_len = in_len;
+ goto start_response;
+ }
if (op->opcode < 0) {
/* Try to open read-only */
if ((sg_fd = scsi_pt_open_device(op->device_name, true, vb)) < 0) {
@@ -1111,7 +1342,8 @@ main(int argc, char * argv[])
;
else if (0 == sg_simple_inquiry_pt(ptvp, &inq_resp, true, vb)) {
peri_dtype = inq_resp.peripheral_type;
- if (! (op->do_raw || op->no_inquiry)) {
+ if (! (as_json || op->do_raw || op->no_inquiry ||
+ (op->do_hex > 2))) {
printf(" %.8s %.16s %.4s\n", inq_resp.vendor,
inq_resp.product, inq_resp.revision);
cp = sg_get_pdt_str(peri_dtype, sizeof(buff), buff);
@@ -1168,38 +1400,63 @@ open_rw: /* if not already open */
goto err_out;
}
act_len = (rq_len < act_len) ? rq_len : act_len;
+
+start_response:
if (op->do_taskman) {
if (op->do_raw) {
dStrRaw((const char *)rsoc_buff, act_len);
goto fini;
}
- printf("\nTask Management Functions supported by device:\n");
if (op->do_hex) {
- hex2stdout(rsoc_buff, act_len, 1);
+ if (op->do_hex > 2)
+ hex2stdout(rsoc_buff, act_len, -1);
+ else {
+ printf("\nTask Management Functions supported by device:\n");
+ if (2 == op->do_hex)
+ hex2stdout(rsoc_buff, act_len, 0);
+ else
+ hex2stdout(rsoc_buff, act_len, 1);
+ }
goto fini;
}
+ if (jsp->pr_as_json) {
+ sgj_add_name_vb(jsp, jop, "ats", rsoc_buff[0] & 0x80);
+ sgj_add_name_vb(jsp, jop, "atss", rsoc_buff[0] & 0x40);
+ sgj_add_name_vb(jsp, jop, "cacas", rsoc_buff[0] & 0x20);
+ sgj_add_name_vb(jsp, jop, "ctss", rsoc_buff[0] & 0x10);
+ sgj_add_name_vb(jsp, jop, "lurs", rsoc_buff[0] & 0x8);
+ sgj_add_name_vb(jsp, jop, "qts", rsoc_buff[0] & 0x4);
+ sgj_add_name_vb(jsp, jop, "trs", rsoc_buff[0] & 0x2);
+ sgj_add_name_vb(jsp, jop, "ws", rsoc_buff[0] & 0x1);
+ sgj_add_name_vb(jsp, jop, "qaes", rsoc_buff[1] & 0x4);
+ sgj_add_name_vb(jsp, jop, "qtss", rsoc_buff[1] & 0x2);
+ sgj_add_name_vb(jsp, jop, "itnrs", rsoc_buff[1] & 0x1);
+ if (! jsp->pr_output)
+ goto fini;
+ }
+ sgj_pr_hr(jsp, "\nTask Management Functions supported by device:\n");
if (rsoc_buff[0] & 0x80)
- printf(" Abort task\n");
+ sgj_pr_hr(jsp, " Abort task\n");
if (rsoc_buff[0] & 0x40)
- printf(" Abort task set\n");
+ sgj_pr_hr(jsp, " Abort task set\n");
if (rsoc_buff[0] & 0x20)
- printf(" Clear ACA\n");
+ sgj_pr_hr(jsp, " Clear ACA\n");
if (rsoc_buff[0] & 0x10)
- printf(" Clear task set\n");
+ sgj_pr_hr(jsp, " Clear task set\n");
if (rsoc_buff[0] & 0x8)
- printf(" Logical unit reset\n");
+ sgj_pr_hr(jsp, " Logical unit reset\n");
if (rsoc_buff[0] & 0x4)
- printf(" Query task\n");
+ sgj_pr_hr(jsp, " Query task\n");
if (rsoc_buff[0] & 0x2)
- printf(" Target reset (obsolete)\n");
+ sgj_pr_hr(jsp, " Target reset (obsolete)\n");
if (rsoc_buff[0] & 0x1)
- printf(" Wakeup (obsolete)\n");
+ sgj_pr_hr(jsp, " Wakeup (obsolete)\n");
if (rsoc_buff[1] & 0x4)
- printf(" Query asynchronous event\n");
+ sgj_pr_hr(jsp, " Query asynchronous event\n");
if (rsoc_buff[1] & 0x2)
- printf(" Query task set\n");
+ sgj_pr_hr(jsp, " Query task set\n");
if (rsoc_buff[1] & 0x1)
- printf(" I_T nexus reset\n");
+ sgj_pr_hr(jsp, " I_T nexus reset\n");
if (op->do_repd) {
if (rsoc_buff[3] < 0xc) {
pr2serr("when REPD given, byte 3 of response should be >= "
@@ -1208,21 +1465,21 @@ open_rw: /* if not already open */
no_final_msg = true;
goto err_out;
} else
- printf(" Extended parameter data:\n");
- printf(" TMFTMOV=%d\n", !!(rsoc_buff[4] & 0x1));
- printf(" ATTS=%d\n", !!(rsoc_buff[6] & 0x80));
- printf(" ATSTS=%d\n", !!(rsoc_buff[6] & 0x40));
- printf(" CACATS=%d\n", !!(rsoc_buff[6] & 0x20));
- printf(" CTSTS=%d\n", !!(rsoc_buff[6] & 0x10));
- printf(" LURTS=%d\n", !!(rsoc_buff[6] & 0x8));
- printf(" QTTS=%d\n", !!(rsoc_buff[6] & 0x4));
- printf(" QAETS=%d\n", !!(rsoc_buff[7] & 0x4));
- printf(" QTSTS=%d\n", !!(rsoc_buff[7] & 0x2));
- printf(" ITNRTS=%d\n", !!(rsoc_buff[7] & 0x1));
- printf(" tmf long timeout: %u (100 ms units)\n",
- sg_get_unaligned_be32(rsoc_buff + 8));
- printf(" tmf short timeout: %u (100 ms units)\n",
- sg_get_unaligned_be32(rsoc_buff + 12));
+ sgj_pr_hr(jsp, " Extended parameter data:\n");
+ sgj_pr_hr(jsp, " TMFTMOV=%d\n", !!(rsoc_buff[4] & 0x1));
+ sgj_pr_hr(jsp, " ATTS=%d\n", !!(rsoc_buff[6] & 0x80));
+ sgj_pr_hr(jsp, " ATSTS=%d\n", !!(rsoc_buff[6] & 0x40));
+ sgj_pr_hr(jsp, " CACATS=%d\n", !!(rsoc_buff[6] & 0x20));
+ sgj_pr_hr(jsp, " CTSTS=%d\n", !!(rsoc_buff[6] & 0x10));
+ sgj_pr_hr(jsp, " LURTS=%d\n", !!(rsoc_buff[6] & 0x8));
+ sgj_pr_hr(jsp, " QTTS=%d\n", !!(rsoc_buff[6] & 0x4));
+ sgj_pr_hr(jsp, " QAETS=%d\n", !!(rsoc_buff[7] & 0x4));
+ sgj_pr_hr(jsp, " QTSTS=%d\n", !!(rsoc_buff[7] & 0x2));
+ sgj_pr_hr(jsp, " ITNRTS=%d\n", !!(rsoc_buff[7] & 0x1));
+ sgj_pr_hr(jsp, " tmf long timeout: %u (100 ms units)\n",
+ sg_get_unaligned_be32(rsoc_buff + 8));
+ sgj_pr_hr(jsp, " tmf short timeout: %u (100 ms units)\n",
+ sg_get_unaligned_be32(rsoc_buff + 12));
}
} else if (0 == rep_opts) { /* list all supported operation codes */
len = sg_get_unaligned_be32(rsoc_buff + 0) + 4;
@@ -1232,7 +1489,12 @@ open_rw: /* if not already open */
goto fini;
}
if (op->do_hex) {
- hex2stdout(rsoc_buff, len, 1);
+ if (op->do_hex > 2)
+ hex2stdout(rsoc_buff, len, -1);
+ else if (2 == op->do_hex)
+ hex2stdout(rsoc_buff, len, 0);
+ else
+ hex2stdout(rsoc_buff, len, 1);
goto fini;
}
list_all_codes(rsoc_buff, len, op, ptvp);
@@ -1246,7 +1508,12 @@ open_rw: /* if not already open */
goto fini;
}
if (op->do_hex) {
- hex2stdout(rsoc_buff, len, 1);
+ if (op->do_hex > 2)
+ hex2stdout(rsoc_buff, len, -1);
+ else if (2 == op->do_hex)
+ hex2stdout(rsoc_buff, len, 0);
+ else
+ hex2stdout(rsoc_buff, len, 1);
goto fini;
}
list_one(rsoc_buff, cd_len, rep_opts, op);
@@ -1257,10 +1524,17 @@ fini:
err_out:
if (free_rsoc_buff)
free(free_rsoc_buff);
- if (ptvp)
- destruct_scsi_pt_obj(ptvp);
- if (sg_fd >= 0)
- scsi_pt_close_device(sg_fd);
+ if (! op->inhex_fn) {
+ if (ptvp)
+ destruct_scsi_pt_obj(ptvp);
+ if (sg_fd >= 0)
+ scsi_pt_close_device(sg_fd);
+ }
+ if (as_json && jop) {
+ if (0 == op->do_hex)
+ sgj_pr2file(jop, &op->json_st, stdout);
+ sg_json_free(jop);
+ }
if ((0 == op->verbose) && (! no_final_msg)) {
if (! sg_if_can2stderr("sg_opcodes failed: ", res))
pr2serr("Some error occurred, try again with '-v' "
diff --git a/src/sg_turs.c b/src/sg_turs.c
index 7b575911..3c7c7a1f 100644
--- a/src/sg_turs.c
+++ b/src/sg_turs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2000-2021 D. Gilbert
+ * Copyright (C) 2000-2022 D. Gilbert
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
@@ -45,7 +45,7 @@
#include "sg_pr2serr.h"
-static const char * version_str = "3.49 20210830";
+static const char * version_str = "3.50 20220418";
#define DEF_PT_TIMEOUT 60 /* 60 seconds */
@@ -330,6 +330,19 @@ parse_cmd_line(struct opts_t * op, int argc, char * argv[])
return res;
}
+#ifdef SG_LIB_MINGW
+
+#include <windows.h>
+
+static void
+wait_millisecs(int millisecs)
+{
+ /* MinGW requires pthreads library for nanosleep, use Sleep() instead */
+ Sleep(millisecs);
+}
+
+#else
+
static void
wait_millisecs(int millisecs)
{
@@ -341,6 +354,8 @@ wait_millisecs(int millisecs)
wait_period = rem;
}
+#endif
+
/* Returns true if prints estimate of duration to ready */
bool
check_for_lu_becoming(struct sg_pt_base * ptvp)
diff --git a/testing/Makefile b/testing/Makefile
index 5d05ad5a..aec375f6 100644
--- a/testing/Makefile
+++ b/testing/Makefile
@@ -45,12 +45,14 @@ CFLAGS = -g -O2 -W -Wall
LDFLAGS =
-LIBFILESOLD = ../lib/sg_lib.o ../lib/sg_lib_data.o ../lib/sg_io_linux.o
+LIBFILESOLD = ../lib/sg_lib.o ../lib/sg_lib_data.o ../lib/sg_io_linux.o \
+ ../lib/sg_json_builder.o ../lib/sg_pr2serr.o
+
LIBFILESNEW = ../lib/sg_pt_linux_nvme.o ../lib/sg_lib.o ../lib/sg_lib_data.o \
../lib/sg_pt_linux.o ../lib/sg_io_linux.o \
../lib/sg_pt_common.o ../lib/sg_cmds_basic.o \
../lib/sg_cmds_basic2.o ../lib/sg_lib_names.o \
- ../lib/sg_json_builder.o
+ ../lib/sg_json_builder.o ../lib/sg_pr2serr.o
all: $(EXECS)
diff --git a/testing/sg_json_builder_test.c b/testing/sg_json_builder_test.c
new file mode 100644
index 00000000..a43b9f78
--- /dev/null
+++ b/testing/sg_json_builder_test.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * Simple streaming JSON writer
+ *
+ * This takes care of the annoying bits of JSON syntax like the commas
+ * after elements
+ *
+ * Authors: Stephen Hemminger <stephen@networkplumber.org>
+ *
+ * Borrowed from Linux kernel [5.17.0]: tools/bpf/bpftool/json_writer.[hc]
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <malloc.h>
+#include <inttypes.h>
+#include <stdint.h>
+
+#include "../lib/sg_json_builder.h"
+
+int main(int argc, char **argv)
+{
+ json_writer_t *wr = jsonw_new(stdout);
+
+ jsonw_start_object(wr);
+ jsonw_pretty(wr, true);
+ jsonw_name(wr, "Vyatta");
+ jsonw_start_object(wr);
+ jsonw_string_field(wr, "url", "http://vyatta.com");
+ jsonw_uint_field(wr, "downloads", 2000000ul);
+ jsonw_float_field(wr, "stock", 8.16);
+
+ jsonw_name(wr, "ARGV");
+ jsonw_start_array(wr);
+ while (--argc)
+ jsonw_string(wr, *++argv);
+ jsonw_end_array(wr);
+
+ jsonw_name(wr, "empty");
+ jsonw_start_array(wr);
+ jsonw_end_array(wr);
+
+ jsonw_name(wr, "NIL");
+ jsonw_start_object(wr);
+ jsonw_end_object(wr);
+
+ jsonw_null_field(wr, "my_null");
+
+ jsonw_name(wr, "special chars");
+ jsonw_start_array(wr);
+ jsonw_string_field(wr, "slash", "/");
+ jsonw_string_field(wr, "newline", "\n");
+ jsonw_string_field(wr, "tab", "\t");
+ jsonw_string_field(wr, "ff", "\f");
+ jsonw_string_field(wr, "quote", "\"");
+ jsonw_string_field(wr, "tick", "\'");
+ jsonw_string_field(wr, "backslash", "\\");
+ jsonw_end_array(wr);
+
+jsonw_name(wr, "ARGV");
+jsonw_start_array(wr);
+jsonw_string(wr, "boo: appended or new entry?");
+jsonw_end_array(wr);
+
+ jsonw_end_object(wr);
+
+ jsonw_end_object(wr);
+ jsonw_destroy(&wr);
+ return 0;
+}
+