aboutsummaryrefslogtreecommitdiff
path: root/src/sg_copy_results.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_copy_results.c')
-rw-r--r--src/sg_copy_results.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/src/sg_copy_results.c b/src/sg_copy_results.c
new file mode 100644
index 00000000..17012be2
--- /dev/null
+++ b/src/sg_copy_results.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2011-2018 Hannes Reinecke, SUSE Labs
+ * 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 <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.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 for the Linux OS SCSI subsystem.
+ * Copyright (C) 2004-2010 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)
+ * any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program issues the SCSI command RECEIVE COPY RESULTS to a given
+ * SCSI device.
+ * It sends the command with the service action passed as the sa argument,
+ * and the optional list identifier passed as the list_id argument.
+ */
+
+static const char * version_str = "1.23 20180625";
+
+
+#define MAX_XFER_LEN 10000
+
+
+#define ME "sg_copy_results: "
+
+#define EBUFF_SZ 256
+
+struct descriptor_type {
+ int code;
+ char desc[124];
+};
+
+struct descriptor_type target_descriptor_codes[] = {
+ { 0xe0, "Fibre Channel N_Port_Name"},
+ { 0xe1, "Fibre Channel N_port_ID"},
+ { 0xe2, "Fibre Channesl N_port_ID with N_Port_Name checking"},
+ { 0xe3, "Parallel Interface T_L" },
+ { 0xe4, "Identification descriptor" },
+ { 0xe5, "IPv4" },
+ { 0xe6, "Alias" },
+ { 0xe7, "RDMA" },
+ { 0xe8, "IEEE 1395 EUI-64" },
+ { 0xe9, "SAS Serial SCSI Protocol" },
+ { 0xea, "IPv6" },
+ { 0xeb, "IP Copy Service" },
+ { -1, "" }
+};
+
+struct descriptor_type segment_descriptor_codes [] = {
+ { 0x00, "Copy from block device to stream device" },
+ { 0x01, "Copy from stream device to block device" },
+ { 0x02, "Copy from block device to block device" },
+ { 0x03, "Copy from stream device to stream device" },
+ { 0x04, "Copy inline data to stream device" },
+ { 0x05, "Copy embedded data to stream device" },
+ { 0x06, "Read from stream device and discard" },
+ { 0x07, "Verify block or stream device operation" },
+ { 0x08, "Copy block device with offset to stream device" },
+ { 0x09, "Copy stream device to block device with offset" },
+ { 0x0A, "Copy block device with offset to block device with offset" },
+ { 0x0B, "Copy from block device to stream device "
+ "and hold a copy of processed data for the application client" },
+ { 0x0C, "Copy from stream device to block device "
+ "and hold a copy of processed data for the application client" },
+ { 0x0D, "Copy from block device to block device "
+ "and hold a copy of processed data for the application client" },
+ { 0x0E, "Copy from stream device to stream device "
+ "and hold a copy of processed data for the application client" },
+ { 0x0F, "Read from stream device "
+ "and hold a copy of processed data for the application client" },
+ { 0x10, "Write filemarks to sequential-access device" },
+ { 0x11, "Space records or filemarks on sequential-access device" },
+ { 0x12, "Locate on sequential-access device" },
+ { 0x13, "Image copy from sequential-access device to sequential-access "
+ "device" },
+ { 0x14, "Register persistent reservation key" },
+ { 0x15, "Third party persistent reservations source I_T nexus" },
+ { -1, "" }
+};
+
+
+static void
+scsi_failed_segment_details(uint8_t *rcBuff, unsigned int rcBuffLen)
+{
+ int senseLen;
+ unsigned int len;
+ char senseBuff[1024];
+
+ if (rcBuffLen < 4) {
+ pr2serr(" <<not enough data to procedd report>>\n");
+ return;
+ }
+ len = sg_get_unaligned_be32(rcBuff + 0);
+ if (len + 4 > rcBuffLen) {
+ pr2serr(" <<report len %d > %d too long for internal buffer, output "
+ "truncated\n", len, rcBuffLen);
+ }
+ if (len < 52) {
+ pr2serr(" <<no segment details, response data length %d\n", len);
+ return;
+ }
+ printf("Receive copy results (failed segment details):\n");
+ printf(" Extended copy command status: %d\n", rcBuff[56]);
+ senseLen = sg_get_unaligned_be16(rcBuff + 58);
+ sg_get_sense_str(" ", &rcBuff[60], senseLen, 0, 1024, senseBuff);
+ printf("%s", senseBuff);
+}
+
+static void
+scsi_copy_status(uint8_t *rcBuff, unsigned int rcBuffLen)
+{
+ unsigned int len;
+
+ if (rcBuffLen < 4) {
+ pr2serr(" <<not enough data to proceed report>>\n");
+ return;
+ }
+ len = sg_get_unaligned_be32(rcBuff + 0);
+ if (len + 4 > rcBuffLen) {
+ pr2serr(" <<report len %d > %d too long for internal buffer, output "
+ "truncated\n", len, rcBuffLen);
+ }
+ printf("Receive copy results (copy status):\n");
+ printf(" Held data discarded: %s\n", rcBuff[4] & 0x80 ? "Yes":"No");
+ printf(" Copy manager status: ");
+ switch (rcBuff[4] & 0x7f) {
+ case 0:
+ printf("Operation in progress\n");
+ break;
+ case 1:
+ printf("Operation completed without errors\n");
+ break;
+ case 2:
+ printf("Operation completed with errors\n");
+ break;
+ default:
+ printf("Unknown/Reserved\n");
+ break;
+ }
+ printf(" Segments processed: %u\n", sg_get_unaligned_be16(rcBuff + 5));
+ printf(" Transfer count units: %u\n", rcBuff[7]);
+ printf(" Transfer count: %u\n", sg_get_unaligned_be32(rcBuff + 8));
+}
+
+static void
+scsi_operating_parameters(uint8_t *rcBuff, unsigned int rcBuffLen)
+{
+ unsigned int len, n;
+
+ len = sg_get_unaligned_be32(rcBuff + 0);
+ if (len + 4 > rcBuffLen) {
+ pr2serr(" <<report len %d > %d too long for internal buffer, output "
+ "truncated\n", len, rcBuffLen);
+ }
+ printf("Receive copy results (report operating parameters):\n");
+ printf(" Supports no list identifier (SNLID): %s\n",
+ rcBuff[4] & 1 ? "yes" : "no");
+ n = sg_get_unaligned_be16(rcBuff + 8);
+ printf(" Maximum target descriptor count: %u\n", n);
+ n = sg_get_unaligned_be16(rcBuff + 10);
+ printf(" Maximum segment descriptor count: %u\n", n);
+ n = sg_get_unaligned_be32(rcBuff + 12);
+ printf(" Maximum descriptor list length: %u bytes\n", n);
+ n = sg_get_unaligned_be32(rcBuff + 16);
+ printf(" Maximum segment length: %u bytes\n", n);
+ n = sg_get_unaligned_be32(rcBuff + 20);
+ if (n == 0) {
+ printf(" Inline data not supported\n");
+ } else {
+ printf(" Maximum inline data length: %u bytes\n", n);
+ }
+ n = sg_get_unaligned_be32(rcBuff + 24);
+ printf(" Held data limit: %u bytes\n", n);
+ n = sg_get_unaligned_be32(rcBuff + 28);
+ printf(" Maximum stream device transfer size: %u bytes\n", n);
+ n = sg_get_unaligned_be16(rcBuff + 34);
+ printf(" Total concurrent copies: %u\n", n);
+ printf(" Maximum concurrent copies: %u\n", rcBuff[36]);
+ if (rcBuff[37] > 30)
+ printf(" Data segment granularity: 2**%u bytes\n", rcBuff[37]);
+ else
+ printf(" Data segment granularity: %u bytes\n",
+ (unsigned int)(1 << rcBuff[37]));
+ if (rcBuff[38] > 30)
+ printf(" Inline data granularity: %u bytes\n", rcBuff[38]);
+ else
+ printf(" Inline data granularity: %u bytes\n",
+ (unsigned int)(1 << rcBuff[38]));
+ if (rcBuff[39] > 30)
+ printf(" Held data granularity: 2**%u bytes\n", rcBuff[39]);
+ else
+ printf(" Held data granularity: %u bytes\n",
+ (unsigned int)(1 << rcBuff[39]));
+
+ printf(" Implemented descriptor list:\n");
+ for (n = 0; n < rcBuff[43]; n++) {
+ int code = rcBuff[44 + n];
+
+ if (code < 0x16) {
+ struct descriptor_type *seg_desc = segment_descriptor_codes;
+ while (strlen(seg_desc->desc)) {
+ if (seg_desc->code == code)
+ break;
+ seg_desc++;
+ }
+ printf(" Segment descriptor 0x%02x: %s\n", code,
+ strlen(seg_desc->desc) ? seg_desc->desc : "Reserved");
+ } else if (code < 0xc0) {
+ printf(" Segment descriptor 0x%02x: Reserved\n", code);
+ } else if (code < 0xe0) {
+ printf(" Vendor specific descriptor 0x%02x\n", code);
+ } else {
+ struct descriptor_type *tgt_desc = target_descriptor_codes;
+
+ while (strlen(tgt_desc->desc)) {
+ if (tgt_desc->code == code)
+ break;
+ tgt_desc++;
+ }
+ printf(" Target descriptor 0x%02x: %s\n", code,
+ strlen(tgt_desc->desc) ? tgt_desc->desc : "Reserved");
+ }
+ }
+ printf("\n");
+}
+
+static struct option long_options[] = {
+ {"failed", no_argument, 0, 'f'},
+ {"help", no_argument, 0, 'h'},
+ {"hex", no_argument, 0, 'H'},
+ {"list_id", required_argument, 0, 'l'},
+ {"list-id", required_argument, 0, 'l'},
+ {"params", no_argument, 0, 'p'},
+ {"readonly", no_argument, 0, 'R'},
+ {"receive", no_argument, 0, 'r'},
+ {"status", no_argument, 0, 's'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {"xfer_len", required_argument, 0, 'x'},
+ {0, 0, 0, 0},
+};
+
+static void
+usage()
+{
+ pr2serr("Usage: "
+ "sg_copy_results [--failed|--params|--receive|--status] [--help]\n"
+ " [--hex] [--list_id=ID] [--readonly] "
+ "[--verbose]\n"
+ " [--version] [--xfer_len=BTL] DEVICE\n"
+ " where:\n"
+ " --failed|-f use FAILED SEGMENT DETAILS service "
+ "action\n"
+ " --help|-h print out usage message\n"
+ " --hex|-H print out response buffer in hex\n"
+ " --list_id=ID|-l ID list identifier (default: 0)\n"
+ " --params|-p use OPERATING PARAMETERS service "
+ "action\n"
+ " --readonly|-R open DEVICE read-only (def: read-write)\n"
+ " --receive|-r use RECEIVE DATA service action\n"
+ " --status|-s use COPY STATUS service action\n"
+ " --verbose|-v increase verbosity\n"
+ " --version|-V print version string then exit\n"
+ " --xfer_len=BTL|-x BTL byte transfer length (< 10000) "
+ "(default:\n"
+ " 520 bytes)\n\n"
+ "Performs a SCSI RECEIVE COPY RESULTS command. Returns the "
+ "response as\nspecified by the service action parameters.\n"
+ );
+}
+
+static const char * rec_copy_name_arr[] = {
+ "Receive copy status(LID1)",
+ "Receive copy data(LID1)",
+ "Receive copy [0x2]",
+ "Receive copy operating parameters",
+ "Receive copy failure details(LID1)",
+};
+
+int
+main(int argc, char * argv[])
+{
+ bool do_hex = false;
+ bool o_readonly = false;
+ bool verbose_given = false;
+ bool version_given = false;
+ int res, c, k;
+ int ret = 1;
+ int sa = 3;
+ int sg_fd = -1;
+ int verbose = 0;
+ int xfer_len = 520;
+ uint32_t list_id = 0;
+ const char * cp;
+ uint8_t * cpResultBuff = NULL;
+ uint8_t * free_cprb = NULL;
+ const char * device_name = NULL;
+ char file_name[256];
+
+ memset(file_name, 0, sizeof file_name);
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "fhHl:prRsvVx:", long_options,
+ &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'f':
+ sa = 4;
+ break;
+ case 'H':
+ do_hex = true;
+ break;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ case 'l':
+ k = sg_get_num(optarg);
+ if (-1 == k) {
+ pr2serr("bad argument to '--list_id'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ list_id = (uint32_t)k;
+ break;
+ case 'p':
+ sa = 3;
+ break;
+ case 'r':
+ sa = 1;
+ break;
+ case 'R':
+ o_readonly = true;
+ break;
+ case 's':
+ sa = 0;
+ break;
+ case 'v':
+ ++verbose;
+ verbose_given = true;
+ break;
+ case 'V':
+ version_given = true;
+ break;
+ case 'x':
+ xfer_len = sg_get_num(optarg);
+ if (-1 == xfer_len) {
+ pr2serr("bad argument to '--xfer_len'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ 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 (xfer_len >= MAX_XFER_LEN) {
+ pr2serr("xfer_len (%d) is out of range ( < %d)\n", xfer_len,
+ MAX_XFER_LEN);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+
+ cpResultBuff = (uint8_t *)sg_memalign(xfer_len, 0, &free_cprb,
+ verbose > 3);
+ if (NULL == cpResultBuff) {
+ pr2serr(ME "out of memory\n");
+ return sg_convert_errno(ENOMEM);
+ }
+
+ sg_fd = sg_cmds_open_device(device_name, o_readonly, verbose);
+ if (sg_fd < 0) {
+ if (verbose)
+ pr2serr(ME "open error: %s: %s\n", device_name,
+ safe_strerror(-sg_fd));
+ ret = sg_convert_errno(-sg_fd);
+ goto finish;
+ }
+
+ if ((sa < 0) || (sa >= (int)SG_ARRAY_SIZE(rec_copy_name_arr)))
+ cp = "Out of range service action";
+ else
+ cp = rec_copy_name_arr[sa];
+ if (verbose)
+ pr2serr(ME "issue %s to device %s\n\t\txfer_len= %d (0x%x), list_id=%"
+ PRIu32 "\n", cp, device_name, xfer_len, xfer_len, list_id);
+
+ res = sg_ll_receive_copy_results(sg_fd, sa, list_id, cpResultBuff,
+ xfer_len, true, verbose);
+ ret = res;
+ if (res) {
+ char b[80];
+
+ sg_get_category_sense_str(res, sizeof(b), b, verbose);
+ pr2serr(" SCSI %s failed: %s\n", cp, b);
+ goto finish;
+ }
+ if (do_hex) {
+ hex2stdout(cpResultBuff, xfer_len, 1);
+ goto finish;
+ }
+ switch (sa) {
+ case 4: /* Failed segment details */
+ scsi_failed_segment_details(cpResultBuff, xfer_len);
+ break;
+ case 3: /* Operating parameters */
+ scsi_operating_parameters(cpResultBuff, xfer_len);
+ break;
+ case 0: /* Copy status */
+ scsi_copy_status(cpResultBuff, xfer_len);
+ break;
+ default:
+ hex2stdout(cpResultBuff, xfer_len, 1);
+ break;
+ }
+
+finish:
+ if (free_cprb)
+ free(free_cprb);
+ if (sg_fd >= 0) {
+ res = sg_cmds_close_device(sg_fd);
+ if (res < 0) {
+ pr2serr(ME "close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ ret = sg_convert_errno(-res);
+ }
+ }
+ if (0 == verbose) {
+ if (! sg_if_can2stderr("sg_copy_results failed: ", ret))
+ pr2serr("Some error occurred, try again with '-v' or '-vv' for "
+ "more information\n");
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}