summaryrefslogtreecommitdiffstats
path: root/sbin/camcontrol/attrib.c
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/camcontrol/attrib.c')
-rw-r--r--sbin/camcontrol/attrib.c509
1 files changed, 509 insertions, 0 deletions
diff --git a/sbin/camcontrol/attrib.c b/sbin/camcontrol/attrib.c
new file mode 100644
index 0000000..62d4b1e
--- /dev/null
+++ b/sbin/camcontrol/attrib.c
@@ -0,0 +1,509 @@
+/*-
+ * Copyright (c) 2014 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * substantially similar to the "NO WARRANTY" disclaimer below
+ * ("Disclaimer") and any redistribution must be conditioned upon
+ * including a substantially similar Disclaimer requirement for further
+ * binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * Authors: Ken Merry (Spectra Logic Corporation)
+ */
+/*
+ * SCSI Read and Write Attribute support for camcontrol(8).
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/ioctl.h>
+#include <sys/stdint.h>
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/sbuf.h>
+#include <sys/queue.h>
+#include <sys/chio.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <limits.h>
+#include <err.h>
+#include <locale.h>
+
+#include <cam/cam.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_ccb.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_pass.h>
+#include <cam/scsi/scsi_ch.h>
+#include <cam/scsi/scsi_message.h>
+#include <camlib.h>
+#include "camcontrol.h"
+
+#if 0
+struct scsi_attr_desc {
+ int attr_id;
+
+ STAILQ_ENTRY(scsi_attr_desc) links;
+};
+#endif
+
+static struct scsi_nv elem_type_map[] = {
+ { "all", ELEMENT_TYPE_ALL },
+ { "picker", ELEMENT_TYPE_MT },
+ { "slot", ELEMENT_TYPE_ST },
+ { "portal", ELEMENT_TYPE_IE },
+ { "drive", ELEMENT_TYPE_DT },
+};
+
+static struct scsi_nv sa_map[] = {
+ { "attr_values", SRA_SA_ATTR_VALUES },
+ { "attr_list", SRA_SA_ATTR_LIST },
+ { "lv_list", SRA_SA_LOG_VOL_LIST },
+ { "part_list", SRA_SA_PART_LIST },
+ { "supp_attr", SRA_SA_SUPPORTED_ATTRS }
+};
+
+static struct scsi_nv output_format_map[] = {
+ { "text_esc", SCSI_ATTR_OUTPUT_TEXT_ESC },
+ { "text_raw", SCSI_ATTR_OUTPUT_TEXT_RAW },
+ { "nonascii_esc", SCSI_ATTR_OUTPUT_NONASCII_ESC },
+ { "nonascii_trim", SCSI_ATTR_OUTPUT_NONASCII_TRIM },
+ { "nonascii_raw", SCSI_ATTR_OUTPUT_NONASCII_RAW },
+ { "field_all", SCSI_ATTR_OUTPUT_FIELD_ALL },
+ { "field_none", SCSI_ATTR_OUTPUT_FIELD_NONE },
+ { "field_desc", SCSI_ATTR_OUTPUT_FIELD_DESC },
+ { "field_num", SCSI_ATTR_OUTPUT_FIELD_NUM },
+ { "field_size", SCSI_ATTR_OUTPUT_FIELD_SIZE },
+ { "field_rw", SCSI_ATTR_OUTPUT_FIELD_RW },
+};
+
+int
+scsiattrib(struct cam_device *device, int argc, char **argv, char *combinedopt,
+ int retry_count, int timeout, int verbosemode, int err_recover)
+{
+ union ccb *ccb = NULL;
+ int attr_num = -1;
+#if 0
+ int num_attrs = 0;
+#endif
+ int start_attr = 0;
+ int cached_attr = 0;
+ int read_service_action = -1;
+ int read_attr = 0, write_attr = 0;
+ int element_address = 0;
+ int element_type = ELEMENT_TYPE_ALL;
+ int partition = 0;
+ int logical_volume = 0;
+ char *endptr;
+ uint8_t *data_buf = NULL;
+ uint32_t dxfer_len = UINT16_MAX - 1;
+ uint32_t valid_len;
+ uint32_t output_format;
+ STAILQ_HEAD(, scsi_attr_desc) write_attr_list;
+ int error = 0;
+ int c;
+
+ ccb = cam_getccb(device);
+ if (ccb == NULL) {
+ warnx("%s: error allocating CCB", __func__);
+ error = 1;
+ goto bailout;
+ }
+
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(union ccb) - sizeof(struct ccb_hdr));
+
+ STAILQ_INIT(&write_attr_list);
+
+ /*
+ * By default, when displaying attribute values, we trim out
+ * non-ASCII characters in ASCII fields. We display all fields
+ * (description, attribute number, attribute size, and readonly
+ * status). We default to displaying raw text.
+ *
+ * XXX KDM need to port this to stable/10 and newer FreeBSD
+ * versions that have iconv built in and can convert codesets.
+ */
+ output_format = SCSI_ATTR_OUTPUT_NONASCII_TRIM |
+ SCSI_ATTR_OUTPUT_FIELD_ALL |
+ SCSI_ATTR_OUTPUT_TEXT_RAW;
+
+ data_buf = malloc(dxfer_len);
+ if (data_buf == NULL) {
+ warn("%s: error allocating %u bytes", __func__, dxfer_len);
+ error = 1;
+ goto bailout;
+ }
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch (c) {
+ case 'a':
+ attr_num = strtol(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ warnx("%s: invalid attribute number %s",
+ __func__, optarg);
+ error = 1;
+ goto bailout;
+ }
+ start_attr = attr_num;
+ break;
+ case 'c':
+ cached_attr = 1;
+ break;
+ case 'e':
+ element_address = strtol(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ warnx("%s: invalid element address %s",
+ __func__, optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ case 'F': {
+ scsi_nv_status status;
+ scsi_attrib_output_flags new_outflags;
+ int entry_num = 0;
+ char *tmpstr;
+
+ if (isdigit(optarg[0])) {
+ output_format = strtoul(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ warnx("%s: invalid numeric output "
+ "format argument %s", __func__,
+ optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ }
+ new_outflags = SCSI_ATTR_OUTPUT_NONE;
+
+ while ((tmpstr = strsep(&optarg, ",")) != NULL) {
+ status = scsi_get_nv(output_format_map,
+ sizeof(output_format_map) /
+ sizeof(output_format_map[0]), tmpstr,
+ &entry_num, SCSI_NV_FLAG_IG_CASE);
+
+ if (status == SCSI_NV_FOUND)
+ new_outflags |=
+ output_format_map[entry_num].value;
+ else {
+ warnx("%s: %s format option %s",
+ __func__,
+ (status == SCSI_NV_AMBIGUOUS) ?
+ "ambiguous" : "invalid", tmpstr);
+ error = 1;
+ goto bailout;
+ }
+ }
+ output_format = new_outflags;
+ break;
+ }
+ case 'p':
+ partition = strtol(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ warnx("%s: invalid partition number %s",
+ __func__, optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ case 'r': {
+ scsi_nv_status status;
+ int entry_num = 0;
+
+ status = scsi_get_nv(sa_map, sizeof(sa_map) /
+ sizeof(sa_map[0]), optarg, &entry_num,
+ SCSI_NV_FLAG_IG_CASE);
+ if (status == SCSI_NV_FOUND)
+ read_service_action = sa_map[entry_num].value;
+ else {
+ warnx("%s: %s %s option %s", __func__,
+ (status == SCSI_NV_AMBIGUOUS) ?
+ "ambiguous" : "invalid", "service action",
+ optarg);
+ error = 1;
+ goto bailout;
+ }
+ read_attr = 1;
+ break;
+ }
+ case 's':
+ start_attr = strtol(optarg, &endptr, 0);
+ if (*endptr != '\0') {
+ warnx("%s: invalid starting attr argument %s",
+ __func__, optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ case 'T': {
+ scsi_nv_status status;
+ int entry_num = 0;
+
+ status = scsi_get_nv(elem_type_map,
+ sizeof(elem_type_map) / sizeof(elem_type_map[0]),
+ optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
+ if (status == SCSI_NV_FOUND)
+ element_type = elem_type_map[entry_num].value;
+ else {
+ warnx("%s: %s %s option %s", __func__,
+ (status == SCSI_NV_AMBIGUOUS) ?
+ "ambiguous" : "invalid", "element type",
+ optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ }
+ case 'w':
+ warnx("%s: writing attributes is not implemented yet",
+ __func__);
+ error = 1;
+ goto bailout;
+ break;
+ case 'V':
+ logical_volume = strtol(optarg, &endptr, 0);
+
+ if (*endptr != '\0') {
+ warnx("%s: invalid logical volume argument %s",
+ __func__, optarg);
+ error = 1;
+ goto bailout;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Default to reading attributes
+ */
+ if (((read_attr == 0) && (write_attr == 0))
+ || ((read_attr != 0) && (write_attr != 0))) {
+ warnx("%s: Must specify either -r or -w", __func__);
+ error = 1;
+ goto bailout;
+ }
+
+ if (read_attr != 0) {
+ scsi_read_attribute(&ccb->csio,
+ /*retries*/ retry_count,
+ /*cbfcnp*/ NULL,
+ /*tag_action*/ MSG_SIMPLE_Q_TAG,
+ /*service_action*/ read_service_action,
+ /*element*/ element_address,
+ /*elem_type*/ element_type,
+ /*logical_volume*/ logical_volume,
+ /*partition*/ partition,
+ /*first_attribute*/ start_attr,
+ /*cache*/ cached_attr,
+ /*data_ptr*/ data_buf,
+ /*length*/ dxfer_len,
+ /*sense_len*/ SSD_FULL_SIZE,
+ /*timeout*/ timeout ? timeout : 60000);
+#if 0
+ } else {
+#endif
+
+ }
+
+ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
+
+ if (err_recover != 0)
+ ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
+
+ if (cam_send_ccb(device, ccb) < 0) {
+ warn("error sending %s ATTRIBUTE", (read_attr != 0) ?
+ "READ" : "WRITE");
+
+ if (verbosemode != 0) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+
+ error = 1;
+ goto bailout;
+ }
+
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ if (verbosemode != 0) {
+ cam_error_print(device, ccb, CAM_ESF_ALL,
+ CAM_EPF_ALL, stderr);
+ }
+ error = 1;
+ goto bailout;
+ }
+
+ if (read_attr == 0)
+ goto bailout;
+
+ valid_len = dxfer_len - ccb->csio.resid;
+
+ switch (read_service_action) {
+ case SRA_SA_ATTR_VALUES: {
+ uint32_t len_left, hdr_len, cur_len;
+ struct scsi_read_attribute_values *hdr;
+ struct scsi_mam_attribute_header *cur_id;
+ char error_str[512];
+ uint8_t *cur_pos;
+ struct sbuf *sb;
+
+ hdr = (struct scsi_read_attribute_values *)data_buf;
+
+ if (valid_len < sizeof(*hdr)) {
+ fprintf(stdout, "No attributes returned.\n");
+ error = 0;
+ goto bailout;
+ }
+
+ sb = sbuf_new_auto();
+ if (sb == NULL) {
+ warn("%s: Unable to allocate sbuf", __func__);
+ error = 1;
+ goto bailout;
+ }
+ /*
+ * XXX KDM grab more data if it is available.
+ */
+ hdr_len = scsi_4btoul(hdr->length);
+
+ for (len_left = MIN(valid_len, hdr_len),
+ cur_pos = &hdr->attribute_0[0]; len_left > sizeof(*cur_id);
+ len_left -= cur_len, cur_pos += cur_len) {
+ int cur_attr_num;
+ cur_id = (struct scsi_mam_attribute_header *)cur_pos;
+ cur_len = scsi_2btoul(cur_id->length) + sizeof(*cur_id);
+ cur_attr_num = scsi_2btoul(cur_id->id);
+
+ if ((attr_num != -1)
+ && (cur_attr_num != attr_num))
+ continue;
+
+ error = scsi_attrib_sbuf(sb, cur_id, len_left,
+ /*user_table*/ NULL, /*num_user_entries*/ 0,
+ /*prefer_user_table*/ 0, output_format, error_str,
+ sizeof(error_str));
+ if (error != 0) {
+ warnx("%s: %s", __func__, error_str);
+ sbuf_delete(sb);
+ error = 1;
+ goto bailout;
+ }
+ if (attr_num != -1)
+ break;
+ }
+
+ sbuf_finish(sb);
+ fprintf(stdout, "%s", sbuf_data(sb));
+ sbuf_delete(sb);
+ break;
+ }
+ case SRA_SA_SUPPORTED_ATTRS:
+ case SRA_SA_ATTR_LIST: {
+ uint32_t len_left, hdr_len;
+ struct scsi_attrib_list_header *hdr;
+ struct scsi_attrib_table_entry *entry = NULL;
+ const char *sa_name = "Supported Attributes";
+ const char *at_name = "Available Attributes";
+ int attr_id;
+ uint8_t *cur_id;
+
+ hdr = (struct scsi_attrib_list_header *)data_buf;
+ if (valid_len < sizeof(*hdr)) {
+ fprintf(stdout, "No %s\n",
+ (read_service_action == SRA_SA_SUPPORTED_ATTRS)?
+ sa_name : at_name);
+ error = 0;
+ goto bailout;
+ }
+ fprintf(stdout, "%s:\n",
+ (read_service_action == SRA_SA_SUPPORTED_ATTRS) ?
+ sa_name : at_name);
+ hdr_len = scsi_4btoul(hdr->length);
+ for (len_left = MIN(valid_len, hdr_len),
+ cur_id = &hdr->first_attr_0[0]; len_left > 1;
+ len_left -= sizeof(uint16_t), cur_id += sizeof(uint16_t)) {
+ attr_id = scsi_2btoul(cur_id);
+
+ if ((attr_num != -1)
+ && (attr_id != attr_num))
+ continue;
+
+ entry = scsi_get_attrib_entry(attr_id);
+ fprintf(stdout, "0x%.4x", attr_id);
+ if (entry == NULL)
+ fprintf(stdout, "\n");
+ else
+ fprintf(stdout, ": %s\n", entry->desc);
+
+ if (attr_num != -1)
+ break;
+ }
+ break;
+ }
+ case SRA_SA_PART_LIST:
+ case SRA_SA_LOG_VOL_LIST: {
+ struct scsi_attrib_lv_list *lv_list;
+ const char *partition_name = "Partition";
+ const char *lv_name = "Logical Volume";
+
+ if (valid_len < sizeof(*lv_list)) {
+ fprintf(stdout, "No %s list returned\n",
+ (read_service_action == SRA_SA_PART_LIST) ?
+ partition_name : lv_name);
+ error = 0;
+ goto bailout;
+ }
+
+ lv_list = (struct scsi_attrib_lv_list *)data_buf;
+
+ fprintf(stdout, "First %s: %d\n",
+ (read_service_action == SRA_SA_PART_LIST) ?
+ partition_name : lv_name,
+ lv_list->first_lv_number);
+ fprintf(stdout, "Number of %ss: %d\n",
+ (read_service_action == SRA_SA_PART_LIST) ?
+ partition_name : lv_name,
+ lv_list->num_logical_volumes);
+ break;
+ }
+ default:
+ break;
+ }
+bailout:
+ if (ccb != NULL)
+ cam_freeccb(ccb);
+
+ free(data_buf);
+
+ return (error);
+}
OpenPOWER on IntegriCloud