aboutsummaryrefslogtreecommitdiff
path: root/src/sg_ident.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_ident.c')
-rw-r--r--src/sg_ident.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/src/sg_ident.c b/src/sg_ident.c
new file mode 100644
index 00000000..2bf00bb8
--- /dev/null
+++ b/src/sg_ident.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2005-2018 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ *
+ * This program issues these SCSI commands: REPORT IDENTIFYING INFORMATION
+ * and SET IDENTIFYING INFORMATION. These commands were called REPORT
+ * DEVICE IDENTIFIER and SET DEVICE IDENTIFIER prior to spc4r07.
+ */
+
+static const char * version_str = "1.23 20180814";
+
+#define ME "sg_ident: "
+
+#define REPORT_ID_INFO_SANITY_LEN 512
+
+
+static struct option long_options[] = {
+ {"ascii", no_argument, 0, 'A'},
+ {"clear", no_argument, 0, 'C'},
+ {"help", no_argument, 0, 'h'},
+ {"itype", required_argument, 0, 'i'},
+ {"raw", no_argument, 0, 'r'},
+ {"set", no_argument, 0, 'S'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0},
+};
+
+static void
+decode_ii(const uint8_t * iip, int ii_len, int itype, bool ascii,
+ bool raw, int verbose)
+{
+ int k;
+
+ if (raw) {
+ if (ii_len > 0) {
+ int n;
+
+ if (sg_set_binary_mode(STDOUT_FILENO) < 0)
+ perror("sg_set_binary_mode");
+#if 0
+ n = fwrite(iip, 1, ii_len, stdout);
+#else
+ n = write(STDOUT_FILENO, iip, ii_len);
+#endif
+ if (verbose && (n < 1))
+ pr2serr("unable to write to stdout\n");
+ }
+ return;
+ }
+ if (0x7f == itype) { /* list of available information types */
+ for (k = 0; k < (ii_len - 3); k += 4)
+ printf(" Information type: %d, Maximum information length: "
+ "%d bytes\n", iip[k], sg_get_unaligned_be16(iip + 2));
+ } else { /* single element */
+ if (verbose)
+ printf("Information:\n");
+ if (ii_len > 0) {
+ if (ascii)
+ printf("%.*s\n", ii_len, (const char *)iip);
+ else
+ hex2stdout(iip, ii_len, 0);
+ }
+ }
+}
+
+static void
+usage(void)
+{
+ pr2serr("Usage: sg_ident [--ascii] [--clear] [--help] [--itype=IT] "
+ "[--raw] [--set]\n"
+ " [--verbose] [--version] DEVICE\n"
+ " where:\n"
+ " --ascii|-A report identifying information as ASCII "
+ "(or UTF8) string\n"
+ " --clear|-C clear (set to zero length) identifying "
+ "information\n"
+ " --help|-h print out usage message\n"
+ " --itype=IT|-i IT specify identifying information type "
+ "(def: 0)\n"
+ " --raw|-r output identifying information to "
+ "stdout\n"
+ " --set|-S invoke set identifying information with "
+ "data from stdin\n"
+ " --verbose|-v increase verbosity of output\n"
+ " --version|-V print version string and exit\n\n"
+ "Performs a SCSI REPORT (or SET) IDENTIFYING INFORMATION "
+ "command. When no\noptions are given then REPORT IDENTIFYING "
+ "INFORMATION is sent and the\nresponse is output in "
+ "hexadecimal with ASCII to the right.\n");
+}
+
+int
+main(int argc, char * argv[])
+{
+ bool ascii = false;
+ bool do_clear = false;
+ bool raw = false;
+ bool do_set = false;
+ bool verbose_given = false;
+ bool version_given = false;
+ int sg_fd, res, c, ii_len;
+ uint8_t rdi_buff[REPORT_ID_INFO_SANITY_LEN + 4];
+ char b[80];
+ uint8_t * bp = NULL;
+ int itype = 0;
+ int verbose = 0;
+ const char * device_name = NULL;
+ int ret = 0;
+
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "AChi:rSvV", long_options,
+ &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'A':
+ ascii = true;
+ break;
+ case 'C':
+ do_clear = true;
+ break;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ case 'i':
+ itype = sg_get_num(optarg);
+ if ((itype < 0) || (itype > 127)) {
+ pr2serr("argument to '--itype' should be in range 0 to 127\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'r':
+ raw = true;
+ break;
+ case 'S':
+ do_set = true;
+ break;
+ case 'v':
+ verbose_given = true;
+ ++verbose;
+ break;
+ case 'V':
+ version_given = true;
+ break;
+ 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;
+ }
+ }
+
+#ifdef DEBUG
+ pr2serr("In DEBUG mode, ");
+ if (verbose_given && version_given) {
+ pr2serr("but override: '-vV' given, zero verbose and continue\n");
+ verbose_given = false;
+ version_given = false;
+ verbose = 0;
+ } else if (! verbose_given) {
+ pr2serr("set '-vv'\n");
+ verbose = 2;
+ } else
+ pr2serr("keep verbose=%d\n", verbose);
+#else
+ if (verbose_given && version_given)
+ pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
+#endif
+ if (version_given) {
+ pr2serr(ME "version: %s\n", version_str);
+ return 0;
+ }
+
+ if (NULL == device_name) {
+ pr2serr("missing device name!\n\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if (do_set && do_clear) {
+ pr2serr("only one of '--clear' and '--set' can be given\n");
+ usage();
+ return SG_LIB_CONTRADICT;
+ }
+ if (ascii && raw) {
+ pr2serr("only one of '--ascii' and '--raw' can be given\n");
+ usage();
+ return SG_LIB_CONTRADICT;
+ }
+ if ((do_set || do_clear) && (raw || ascii)) {
+ pr2serr("'--set' cannot be used with either '--ascii' or '--raw'\n");
+ usage();
+ return SG_LIB_CONTRADICT;
+ }
+ sg_fd = sg_cmds_open_device(device_name, false /* rw=false */, verbose);
+ if (sg_fd < 0) {
+ pr2serr(ME "open error: %s: %s\n", device_name, safe_strerror(-sg_fd));
+ return sg_convert_errno(-sg_fd);
+ }
+
+ memset(rdi_buff, 0x0, sizeof(rdi_buff));
+ if (do_set || do_clear) {
+ if (do_set) {
+ res = fread(rdi_buff, 1, REPORT_ID_INFO_SANITY_LEN + 2, stdin);
+ if (res <= 0) {
+ pr2serr("no data read from stdin; to clear identifying "
+ "information use '--clear' instead\n");
+ ret = -1;
+ goto err_out;
+ } else if (res > REPORT_ID_INFO_SANITY_LEN) {
+ pr2serr("SPC-4 limits information length to 512 bytes\n");
+ ret = -1;
+ goto err_out;
+ }
+ ii_len = res;
+ res = sg_ll_set_id_info(sg_fd, itype, rdi_buff, ii_len, true,
+ verbose);
+ } else /* do_clear */
+ res = sg_ll_set_id_info(sg_fd, itype, rdi_buff, 0, true, verbose);
+ if (res) {
+ ret = res;
+ sg_get_category_sense_str(res, sizeof(b), b, verbose);
+ pr2serr("Set identifying information: %s\n", b);
+ if (0 == verbose)
+ pr2serr(" try '-v' for more information\n");
+ }
+ } else { /* do report identifying information */
+ res = sg_ll_report_id_info(sg_fd, itype, rdi_buff, 4, true, verbose);
+ if (0 == res) {
+ ii_len = sg_get_unaligned_be32(rdi_buff + 0);
+ if ((! raw) && (verbose > 0))
+ printf("Reported identifying information length = %d\n",
+ ii_len);
+ if (0 == ii_len) {
+ if (verbose > 1)
+ pr2serr(" This implies the device has an empty "
+ "information field\n");
+ goto err_out;
+ }
+ if (ii_len > REPORT_ID_INFO_SANITY_LEN) {
+ pr2serr(" That length (%d) seems too long for an "
+ "information\n", ii_len);
+ ret = -1;
+ goto err_out;
+ }
+ bp = rdi_buff;
+ res = sg_ll_report_id_info(sg_fd, itype, bp, ii_len + 4, true,
+ verbose);
+ if (0 == res) {
+ ii_len = sg_get_unaligned_be32(bp + 0);
+ decode_ii(bp + 4, ii_len, itype, ascii, raw, verbose);
+ } else
+ ret = res;
+ } else
+ ret = res;
+ if (ret) {
+ sg_get_category_sense_str(res, sizeof(b), b, verbose);
+ pr2serr("Report identifying information: %s\n", b);
+ if (0 == verbose)
+ pr2serr(" try '-v' for more information\n");
+ }
+ }
+
+err_out:
+ res = sg_cmds_close_device(sg_fd);
+ if (res < 0) {
+ pr2serr("close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ ret = sg_convert_errno(-res);
+ }
+ if (0 == verbose) {
+ if (! sg_if_can2stderr("sg_ident failed: ", ret))
+ pr2serr("Some error occurred, try again with '-v' or '-vv' for "
+ "more information\n");
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}