diff options
Diffstat (limited to 'src/sg_ident.c')
-rw-r--r-- | src/sg_ident.c | 318 |
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; +} |