attrib.c revision 284435
12311Sjkh/*-
22311Sjkh * Copyright (c) 2014 Spectra Logic Corporation
32311Sjkh * All rights reserved.
42311Sjkh *
52311Sjkh * Redistribution and use in source and binary forms, with or without
62311Sjkh * modification, are permitted provided that the following conditions
72311Sjkh * are met:
82311Sjkh * 1. Redistributions of source code must retain the above copyright
92311Sjkh *    notice, this list of conditions, and the following disclaimer,
102311Sjkh *    without modification.
112311Sjkh * 2. Redistributions in binary form must reproduce at minimum a disclaimer
122311Sjkh *    substantially similar to the "NO WARRANTY" disclaimer below
132311Sjkh *    ("Disclaimer") and any redistribution must be conditioned upon
142311Sjkh *    including a substantially similar Disclaimer requirement for further
152311Sjkh *    binary redistribution.
162311Sjkh *
172311Sjkh * NO WARRANTY
182311Sjkh * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1950479Speter * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
202311Sjkh * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
212311Sjkh * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
222311Sjkh * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
232311Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
242311Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
252311Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
262311Sjkh * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
272311Sjkh * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
282311Sjkh * POSSIBILITY OF SUCH DAMAGES.
292311Sjkh *
302311Sjkh * Authors: Ken Merry           (Spectra Logic Corporation)
312311Sjkh */
322311Sjkh/*
332311Sjkh * SCSI Read and Write Attribute support for camcontrol(8).
342311Sjkh */
352311Sjkh
362311Sjkh#include <sys/cdefs.h>
372311Sjkh__FBSDID("$FreeBSD: stable/10/sbin/camcontrol/attrib.c 284435 2015-06-16 02:31:11Z ken $");
382311Sjkh
392311Sjkh#include <sys/ioctl.h>
402311Sjkh#include <sys/stdint.h>
412311Sjkh#include <sys/types.h>
422311Sjkh#include <sys/endian.h>
432311Sjkh#include <sys/sbuf.h>
442311Sjkh#include <sys/queue.h>
452311Sjkh#include <sys/chio.h>
462311Sjkh
472311Sjkh#include <stdio.h>
482311Sjkh#include <stdlib.h>
492311Sjkh#include <inttypes.h>
502311Sjkh#include <unistd.h>
512311Sjkh#include <string.h>
522311Sjkh#include <strings.h>
532311Sjkh#include <fcntl.h>
542311Sjkh#include <ctype.h>
552311Sjkh#include <limits.h>
562311Sjkh#include <err.h>
572311Sjkh#include <locale.h>
582311Sjkh
592311Sjkh#include <cam/cam.h>
602311Sjkh#include <cam/cam_debug.h>
612311Sjkh#include <cam/cam_ccb.h>
622311Sjkh#include <cam/scsi/scsi_all.h>
632311Sjkh#include <cam/scsi/scsi_pass.h>
642311Sjkh#include <cam/scsi/scsi_ch.h>
652311Sjkh#include <cam/scsi/scsi_message.h>
662311Sjkh#include <camlib.h>
672311Sjkh#include "camcontrol.h"
682311Sjkh
692311Sjkh#if 0
702311Sjkhstruct scsi_attr_desc {
712311Sjkh	int attr_id;
722311Sjkh
732311Sjkh	STAILQ_ENTRY(scsi_attr_desc) links;
742311Sjkh};
752311Sjkh#endif
762311Sjkh
772311Sjkhstatic struct scsi_nv elem_type_map[] = {
782311Sjkh	{ "all", ELEMENT_TYPE_ALL },
792311Sjkh	{ "picker", ELEMENT_TYPE_MT },
802311Sjkh	{ "slot", ELEMENT_TYPE_ST },
812311Sjkh	{ "portal", ELEMENT_TYPE_IE },
822311Sjkh	{ "drive", ELEMENT_TYPE_DT },
832311Sjkh};
842311Sjkh
852311Sjkhstatic struct scsi_nv sa_map[] = {
862311Sjkh	{ "attr_values", SRA_SA_ATTR_VALUES },
872311Sjkh	{ "attr_list", SRA_SA_ATTR_LIST },
882311Sjkh	{ "lv_list", SRA_SA_LOG_VOL_LIST },
892311Sjkh	{ "part_list", SRA_SA_PART_LIST },
902311Sjkh	{ "supp_attr", SRA_SA_SUPPORTED_ATTRS }
912311Sjkh};
922311Sjkh
932311Sjkhstatic struct scsi_nv output_format_map[] = {
942311Sjkh	{ "text_esc", SCSI_ATTR_OUTPUT_TEXT_ESC },
952311Sjkh	{ "text_raw", SCSI_ATTR_OUTPUT_TEXT_RAW },
962311Sjkh	{ "nonascii_esc", SCSI_ATTR_OUTPUT_NONASCII_ESC },
972311Sjkh	{ "nonascii_trim", SCSI_ATTR_OUTPUT_NONASCII_TRIM },
982311Sjkh	{ "nonascii_raw", SCSI_ATTR_OUTPUT_NONASCII_RAW },
992311Sjkh	{ "field_all", SCSI_ATTR_OUTPUT_FIELD_ALL },
1002311Sjkh	{ "field_none", SCSI_ATTR_OUTPUT_FIELD_NONE },
1012311Sjkh	{ "field_desc", SCSI_ATTR_OUTPUT_FIELD_DESC },
1022311Sjkh	{ "field_num", SCSI_ATTR_OUTPUT_FIELD_NUM },
1032311Sjkh	{ "field_size", SCSI_ATTR_OUTPUT_FIELD_SIZE },
1042311Sjkh	{ "field_rw", SCSI_ATTR_OUTPUT_FIELD_RW },
1052311Sjkh};
1062311Sjkh
1072311Sjkhint
1082311Sjkhscsiattrib(struct cam_device *device, int argc, char **argv, char *combinedopt,
1098169Sache	   int retry_count, int timeout, int verbosemode, int err_recover)
1108169Sache{
1118169Sache	union ccb *ccb = NULL;
1122311Sjkh	int attr_num = -1;
1132311Sjkh#if 0
1148169Sache	int num_attrs = 0;
1152311Sjkh#endif
1162311Sjkh	int start_attr = 0;
1172311Sjkh	int cached_attr = 0;
1182311Sjkh	int read_service_action = -1;
1192311Sjkh	int read_attr = 0, write_attr = 0;
1202311Sjkh	int element_address = 0;
1212311Sjkh	int element_type = ELEMENT_TYPE_ALL;
1222311Sjkh	int partition = 0;
1232311Sjkh	int logical_volume = 0;
1242311Sjkh	char *endptr;
1252311Sjkh	uint8_t *data_buf = NULL;
1262311Sjkh	uint32_t dxfer_len = UINT16_MAX - 1;
1272311Sjkh	uint32_t valid_len;
1282311Sjkh	uint32_t output_format;
1292311Sjkh	STAILQ_HEAD(, scsi_attr_desc) write_attr_list;
1302311Sjkh	int error = 0;
1312311Sjkh	int c;
1322311Sjkh
1332311Sjkh	ccb = cam_getccb(device);
1342311Sjkh	if (ccb == NULL) {
1352311Sjkh		warnx("%s: error allocating CCB", __func__);
1362311Sjkh		error = 1;
1372311Sjkh		goto bailout;
1382311Sjkh	}
1392311Sjkh
1402311Sjkh	bzero(&(&ccb->ccb_h)[1],
141	      sizeof(union ccb) - sizeof(struct ccb_hdr));
142
143	STAILQ_INIT(&write_attr_list);
144
145	/*
146	 * By default, when displaying attribute values, we trim out
147	 * non-ASCII characters in ASCII fields.  We display all fields
148	 * (description, attribute number, attribute size, and readonly
149	 * status).  We default to displaying raw text.
150	 *
151	 * XXX KDM need to port this to stable/10 and newer FreeBSD
152	 * versions that have iconv built in and can convert codesets.
153	 */
154	output_format = SCSI_ATTR_OUTPUT_NONASCII_TRIM |
155			SCSI_ATTR_OUTPUT_FIELD_ALL |
156			SCSI_ATTR_OUTPUT_TEXT_RAW;
157
158	data_buf = malloc(dxfer_len);
159	if (data_buf == NULL) {
160		warn("%s: error allocating %u bytes", __func__, dxfer_len);
161		error = 1;
162		goto bailout;
163	}
164
165	while ((c = getopt(argc, argv, combinedopt)) != -1) {
166		switch (c) {
167		case 'a':
168			attr_num = strtol(optarg, &endptr, 0);
169			if (*endptr != '\0') {
170				warnx("%s: invalid attribute number %s",
171				    __func__, optarg);
172				error = 1;
173				goto bailout;
174			}
175			start_attr = attr_num;
176			break;
177		case 'c':
178			cached_attr = 1;
179			break;
180		case 'e':
181			element_address = strtol(optarg, &endptr, 0);
182			if (*endptr != '\0') {
183				warnx("%s: invalid element address %s",
184				    __func__, optarg);
185				error = 1;
186				goto bailout;
187			}
188			break;
189		case 'F': {
190			scsi_nv_status status;
191			scsi_attrib_output_flags new_outflags;
192			int entry_num = 0;
193			char *tmpstr;
194
195			if (isdigit(optarg[0])) {
196				output_format = strtoul(optarg, &endptr, 0);
197				if (*endptr != '\0') {
198					warnx("%s: invalid numeric output "
199					    "format argument %s", __func__,
200					    optarg);
201					error = 1;
202					goto bailout;
203				}
204				break;
205			}
206			new_outflags = SCSI_ATTR_OUTPUT_NONE;
207
208			while ((tmpstr = strsep(&optarg, ",")) != NULL) {
209				status = scsi_get_nv(output_format_map,
210				    sizeof(output_format_map) /
211				    sizeof(output_format_map[0]), tmpstr,
212				    &entry_num, SCSI_NV_FLAG_IG_CASE);
213
214				if (status == SCSI_NV_FOUND)
215					new_outflags |=
216					    output_format_map[entry_num].value;
217				else {
218					warnx("%s: %s format option %s",
219					    __func__,
220					    (status == SCSI_NV_AMBIGUOUS) ?
221					    "ambiguous" : "invalid", tmpstr);
222					error = 1;
223					goto bailout;
224				}
225			}
226			output_format = new_outflags;
227			break;
228		}
229		case 'p':
230			partition = strtol(optarg, &endptr, 0);
231			if (*endptr != '\0') {
232				warnx("%s: invalid partition number %s",
233				    __func__, optarg);
234				error = 1;
235				goto bailout;
236			}
237			break;
238		case 'r': {
239			scsi_nv_status status;
240			int entry_num = 0;
241
242			status = scsi_get_nv(sa_map, sizeof(sa_map) /
243			    sizeof(sa_map[0]), optarg, &entry_num,
244			    SCSI_NV_FLAG_IG_CASE);
245			if (status == SCSI_NV_FOUND)
246				read_service_action = sa_map[entry_num].value;
247			else {
248				warnx("%s: %s %s option %s", __func__,
249				    (status == SCSI_NV_AMBIGUOUS) ?
250				    "ambiguous" : "invalid", "service action",
251				    optarg);
252				error = 1;
253				goto bailout;
254			}
255			read_attr = 1;
256			break;
257		}
258		case 's':
259			start_attr = strtol(optarg, &endptr, 0);
260			if (*endptr != '\0') {
261				warnx("%s: invalid starting attr argument %s",
262				    __func__, optarg);
263				error = 1;
264				goto bailout;
265			}
266			break;
267		case 'T': {
268			scsi_nv_status status;
269			int entry_num = 0;
270
271			status = scsi_get_nv(elem_type_map,
272			    sizeof(elem_type_map) / sizeof(elem_type_map[0]),
273			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
274			if (status == SCSI_NV_FOUND)
275				element_type = elem_type_map[entry_num].value;
276			else {
277				warnx("%s: %s %s option %s", __func__,
278				    (status == SCSI_NV_AMBIGUOUS) ?
279				    "ambiguous" : "invalid", "element type",
280				    optarg);
281				error = 1;
282				goto bailout;
283			}
284			break;
285		}
286		case 'w':
287			warnx("%s: writing attributes is not implemented yet",
288			      __func__);
289			error = 1;
290			goto bailout;
291			break;
292		case 'V':
293			logical_volume = strtol(optarg, &endptr, 0);
294
295			if (*endptr != '\0') {
296				warnx("%s: invalid logical volume argument %s",
297				    __func__, optarg);
298				error = 1;
299				goto bailout;
300			}
301			break;
302		default:
303			break;
304		}
305	}
306
307	/*
308	 * Default to reading attributes
309	 */
310	if (((read_attr == 0) && (write_attr == 0))
311	 || ((read_attr != 0) && (write_attr != 0))) {
312		warnx("%s: Must specify either -r or -w", __func__);
313		error = 1;
314		goto bailout;
315	}
316
317	if (read_attr != 0) {
318		scsi_read_attribute(&ccb->csio,
319				    /*retries*/ retry_count,
320				    /*cbfcnp*/ NULL,
321				    /*tag_action*/ MSG_SIMPLE_Q_TAG,
322				    /*service_action*/ read_service_action,
323				    /*element*/ element_address,
324				    /*elem_type*/ element_type,
325				    /*logical_volume*/ logical_volume,
326				    /*partition*/ partition,
327				    /*first_attribute*/ start_attr,
328				    /*cache*/ cached_attr,
329				    /*data_ptr*/ data_buf,
330				    /*length*/ dxfer_len,
331			            /*sense_len*/ SSD_FULL_SIZE,
332				    /*timeout*/ timeout ? timeout : 60000);
333#if 0
334	} else {
335#endif
336
337	}
338
339	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
340
341	if (err_recover != 0)
342		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
343
344	if (cam_send_ccb(device, ccb) < 0) {
345		warn("error sending %s ATTRIBUTE", (read_attr != 0) ?
346		    "READ" : "WRITE");
347
348		if (verbosemode != 0) {
349			cam_error_print(device, ccb, CAM_ESF_ALL,
350					CAM_EPF_ALL, stderr);
351		}
352
353		error = 1;
354		goto bailout;
355	}
356
357	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
358		if (verbosemode != 0) {
359			cam_error_print(device, ccb, CAM_ESF_ALL,
360					CAM_EPF_ALL, stderr);
361		}
362		error = 1;
363		goto bailout;
364	}
365
366	if (read_attr == 0)
367		goto bailout;
368
369	valid_len = dxfer_len - ccb->csio.resid;
370
371	switch (read_service_action) {
372	case SRA_SA_ATTR_VALUES: {
373		uint32_t len_left, hdr_len, cur_len;
374		struct scsi_read_attribute_values *hdr;
375		struct scsi_mam_attribute_header *cur_id;
376		char error_str[512];
377		uint8_t *cur_pos;
378		struct sbuf *sb;
379
380		hdr = (struct scsi_read_attribute_values *)data_buf;
381
382		if (valid_len < sizeof(*hdr)) {
383			fprintf(stdout, "No attributes returned.\n");
384			error = 0;
385			goto bailout;
386		}
387
388		sb = sbuf_new_auto();
389		if (sb == NULL) {
390			warn("%s: Unable to allocate sbuf", __func__);
391			error = 1;
392			goto bailout;
393		}
394		/*
395		 * XXX KDM grab more data if it is available.
396		 */
397		hdr_len = scsi_4btoul(hdr->length);
398
399		for (len_left = MIN(valid_len, hdr_len),
400		     cur_pos = &hdr->attribute_0[0]; len_left > sizeof(*cur_id);
401		     len_left -= cur_len, cur_pos += cur_len) {
402			int cur_attr_num;
403			cur_id = (struct scsi_mam_attribute_header *)cur_pos;
404			cur_len = scsi_2btoul(cur_id->length) + sizeof(*cur_id);
405			cur_attr_num = scsi_2btoul(cur_id->id);
406
407			if ((attr_num != -1)
408			 && (cur_attr_num != attr_num))
409				continue;
410
411			error = scsi_attrib_sbuf(sb, cur_id, len_left,
412			    /*user_table*/ NULL, /*num_user_entries*/ 0,
413			    /*prefer_user_table*/ 0, output_format, error_str,
414			    sizeof(error_str));
415			if (error != 0) {
416				warnx("%s: %s", __func__, error_str);
417				sbuf_delete(sb);
418				error = 1;
419				goto bailout;
420			}
421			if (attr_num != -1)
422				break;
423		}
424
425		sbuf_finish(sb);
426		fprintf(stdout, "%s", sbuf_data(sb));
427		sbuf_delete(sb);
428		break;
429	}
430	case SRA_SA_SUPPORTED_ATTRS:
431	case SRA_SA_ATTR_LIST: {
432		uint32_t len_left, hdr_len;
433		struct scsi_attrib_list_header *hdr;
434		struct scsi_attrib_table_entry *entry = NULL;
435		const char *sa_name = "Supported Attributes";
436		const char *at_name = "Available Attributes";
437		int attr_id;
438		uint8_t *cur_id;
439
440		hdr = (struct scsi_attrib_list_header *)data_buf;
441		if (valid_len < sizeof(*hdr)) {
442			fprintf(stdout, "No %s\n",
443				(read_service_action == SRA_SA_SUPPORTED_ATTRS)?
444				 sa_name : at_name);
445			error = 0;
446			goto bailout;
447		}
448		fprintf(stdout, "%s:\n",
449			(read_service_action == SRA_SA_SUPPORTED_ATTRS) ?
450			 sa_name : at_name);
451		hdr_len = scsi_4btoul(hdr->length);
452		for (len_left = MIN(valid_len, hdr_len),
453		     cur_id = &hdr->first_attr_0[0]; len_left > 1;
454		     len_left -= sizeof(uint16_t), cur_id += sizeof(uint16_t)) {
455			attr_id = scsi_2btoul(cur_id);
456
457			if ((attr_num != -1)
458			 && (attr_id != attr_num))
459				continue;
460
461			entry = scsi_get_attrib_entry(attr_id);
462			fprintf(stdout, "0x%.4x", attr_id);
463			if (entry == NULL)
464				fprintf(stdout, "\n");
465			else
466				fprintf(stdout, ": %s\n", entry->desc);
467
468			if (attr_num != -1)
469				break;
470		}
471		break;
472	}
473	case SRA_SA_PART_LIST:
474	case SRA_SA_LOG_VOL_LIST: {
475		struct scsi_attrib_lv_list *lv_list;
476		const char *partition_name = "Partition";
477		const char *lv_name = "Logical Volume";
478
479		if (valid_len < sizeof(*lv_list)) {
480			fprintf(stdout, "No %s list returned\n",
481				(read_service_action == SRA_SA_PART_LIST) ?
482				partition_name : lv_name);
483			error = 0;
484			goto bailout;
485		}
486
487		lv_list = (struct scsi_attrib_lv_list *)data_buf;
488
489		fprintf(stdout, "First %s: %d\n",
490			(read_service_action == SRA_SA_PART_LIST) ?
491			partition_name : lv_name,
492			lv_list->first_lv_number);
493		fprintf(stdout, "Number of %ss: %d\n",
494			(read_service_action == SRA_SA_PART_LIST) ?
495			partition_name : lv_name,
496			lv_list->num_logical_volumes);
497		break;
498	}
499	default:
500		break;
501	}
502bailout:
503	if (ccb != NULL)
504		cam_freeccb(ccb);
505
506	free(data_buf);
507
508	return (error);
509}
510