1/*-
2 * Copyright (c) 2013 Spectra Logic Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions, and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    substantially similar to the "NO WARRANTY" disclaimer below
13 *    ("Disclaimer") and any redistribution must be conditioned upon
14 *    including a substantially similar Disclaimer requirement for further
15 *    binary redistribution.
16 *
17 * NO WARRANTY
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGES.
29 *
30 * Authors: Ken Merry           (Spectra Logic Corporation)
31 */
32/*
33 * SCSI Persistent Reservation support for camcontrol(8).
34 */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD: releng/11.0/sbin/camcontrol/persist.c 300547 2016-05-24 00:57:11Z truckman $");
38
39#include <sys/ioctl.h>
40#include <sys/stdint.h>
41#include <sys/types.h>
42#include <sys/endian.h>
43#include <sys/sbuf.h>
44#include <sys/queue.h>
45
46#include <stdio.h>
47#include <stdlib.h>
48#include <inttypes.h>
49#include <unistd.h>
50#include <string.h>
51#include <strings.h>
52#include <fcntl.h>
53#include <ctype.h>
54#include <limits.h>
55#include <err.h>
56
57#include <cam/cam.h>
58#include <cam/cam_debug.h>
59#include <cam/cam_ccb.h>
60#include <cam/scsi/scsi_all.h>
61#include <cam/scsi/scsi_pass.h>
62#include <cam/scsi/scsi_message.h>
63#include <camlib.h>
64#include "camcontrol.h"
65
66struct persist_transport_id {
67	struct scsi_transportid_header *hdr;
68	unsigned int alloc_len;
69	STAILQ_ENTRY(persist_transport_id) links;
70};
71
72/*
73 * Service Actions for PERSISTENT RESERVE IN.
74 */
75static struct scsi_nv persist_in_actions[] = {
76	{ "read_keys", SPRI_RK },
77	{ "read_reservation", SPRI_RR },
78	{ "report_capabilities", SPRI_RC },
79	{ "read_full_status", SPRI_RS }
80};
81
82/*
83 * Service Actions for PERSISTENT RESERVE OUT.
84 */
85static struct scsi_nv persist_out_actions[] = {
86	{ "register", SPRO_REGISTER },
87	{ "reserve", SPRO_RESERVE },
88	{ "release" , SPRO_RELEASE },
89	{ "clear", SPRO_CLEAR },
90	{ "preempt", SPRO_PREEMPT },
91	{ "preempt_abort", SPRO_PRE_ABO },
92	{ "register_ignore", SPRO_REG_IGNO },
93	{ "register_move", SPRO_REG_MOVE },
94	{ "replace_lost", SPRO_REPL_LOST_RES }
95};
96
97/*
98 * Known reservation scopes.  As of SPC-4, only LU_SCOPE is used in the
99 * spec.  The others are obsolete.
100 */
101static struct scsi_nv persist_scope_table[] = {
102	{ "lun", SPR_LU_SCOPE },
103	{ "extent", SPR_EXTENT_SCOPE },
104	{ "element", SPR_ELEMENT_SCOPE }
105};
106
107/*
108 * Reservation types.  The longer name for a given reservation type is
109 * listed first, so that it makes more sense when we print out the
110 * reservation type.  We step through the table linearly when looking for
111 * the text name for a particular numeric reservation type value.
112 */
113static struct scsi_nv persist_type_table[] = {
114	{ "read_shared", SPR_TYPE_RD_SHARED },
115	{ "write_exclusive", SPR_TYPE_WR_EX },
116	{ "wr_ex", SPR_TYPE_WR_EX },
117	{ "read_exclusive", SPR_TYPE_RD_EX },
118	{ "rd_ex", SPR_TYPE_RD_EX },
119	{ "exclusive_access", SPR_TYPE_EX_AC },
120	{ "ex_ac", SPR_TYPE_EX_AC },
121	{ "write_exclusive_reg_only", SPR_TYPE_WR_EX_RO },
122	{ "wr_ex_ro", SPR_TYPE_WR_EX_RO },
123	{ "exclusive_access_reg_only", SPR_TYPE_EX_AC_RO },
124	{ "ex_ac_ro", SPR_TYPE_EX_AC_RO },
125	{ "write_exclusive_all_regs", SPR_TYPE_WR_EX_AR },
126	{ "wr_ex_ar", SPR_TYPE_WR_EX_AR },
127	{ "exclusive_access_all_regs", SPR_TYPE_EX_AC_AR },
128	{ "ex_ac_ar", SPR_TYPE_EX_AC_AR }
129};
130
131/*
132 * Print out the standard scope/type field.
133 */
134static void
135persist_print_scopetype(uint8_t scopetype)
136{
137	const char *tmpstr;
138	int num_entries;
139
140	num_entries = sizeof(persist_scope_table) /
141		      sizeof(persist_scope_table[0]);
142	tmpstr = scsi_nv_to_str(persist_scope_table, num_entries,
143				scopetype & SPR_SCOPE_MASK);
144	fprintf(stdout, "Scope: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
145		"Unknown", (scopetype & SPR_SCOPE_MASK) >> SPR_SCOPE_SHIFT);
146
147	num_entries = sizeof(persist_type_table) /
148		      sizeof(persist_type_table[0]);
149	tmpstr = scsi_nv_to_str(persist_type_table, num_entries,
150				scopetype & SPR_TYPE_MASK);
151	fprintf(stdout, "Type: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
152		"Unknown", scopetype & SPR_TYPE_MASK);
153}
154
155static void
156persist_print_transportid(uint8_t *buf, uint32_t len)
157{
158	struct sbuf *sb;
159
160	sb = sbuf_new_auto();
161	if (sb == NULL)
162		fprintf(stderr, "Unable to allocate sbuf\n");
163
164	scsi_transportid_sbuf(sb, (struct scsi_transportid_header *)buf, len);
165
166	sbuf_finish(sb);
167
168	fprintf(stdout, "%s\n", sbuf_data(sb));
169
170	sbuf_delete(sb);
171}
172
173/*
174 * Print out a persistent reservation.  This is used with the READ
175 * RESERVATION (0x01) service action of the PERSISTENT RESERVE IN command.
176 */
177static void
178persist_print_res(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
179{
180	uint32_t length;
181	struct scsi_per_res_in_rsrv *res;
182
183	length = scsi_4btoul(hdr->length);
184	length = MIN(length, valid_len);
185
186	res = (struct scsi_per_res_in_rsrv *)hdr;
187
188	if (length < sizeof(res->data) - sizeof(res->data.extent_length)) {
189		if (length == 0)
190			fprintf(stdout, "No reservations.\n");
191		else
192			warnx("unable to print reservation, only got %u "
193			      "valid bytes", length);
194		return;
195	}
196	fprintf(stdout, "PRgeneration: %#x\n",
197		scsi_4btoul(res->header.generation));
198	fprintf(stdout, "Reservation Key: %#jx\n",
199		(uintmax_t)scsi_8btou64(res->data.reservation));
200	fprintf(stdout, "Scope address: %#x\n",
201		scsi_4btoul(res->data.scope_addr));
202
203	persist_print_scopetype(res->data.scopetype);
204
205	fprintf(stdout, "Extent length: %u\n",
206		scsi_2btoul(res->data.extent_length));
207}
208
209/*
210 * Print out persistent reservation keys.  This is used with the READ KEYS
211 * service action of the PERSISTENT RESERVE IN command.
212 */
213static void
214persist_print_keys(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
215{
216	uint32_t length, num_keys, i;
217	struct scsi_per_res_key *key;
218
219	length = scsi_4btoul(hdr->length);
220	length = MIN(length, valid_len);
221
222	num_keys = length / sizeof(*key);
223
224	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
225	fprintf(stdout, "%u key%s%s\n", num_keys, (num_keys == 1) ? "" : "s",
226		(num_keys == 0) ? "." : ":");
227
228	for (i = 0, key = (struct scsi_per_res_key *)&hdr[1]; i < num_keys;
229	     i++, key++) {
230		fprintf(stdout, "%u: %#jx\n", i,
231			(uintmax_t)scsi_8btou64(key->key));
232	}
233}
234
235/*
236 * Print out persistent reservation capabilities.  This is used with the
237 * REPORT CAPABILITIES service action of the PERSISTENT RESERVE IN command.
238 */
239static void
240persist_print_cap(struct scsi_per_res_cap *cap, uint32_t valid_len)
241{
242	uint32_t length;
243	int check_type_mask = 0;
244
245	length = scsi_2btoul(cap->length);
246	length = MIN(length, valid_len);
247
248	if (length < __offsetof(struct scsi_per_res_cap, type_mask)) {
249		fprintf(stdout, "Insufficient data (%u bytes) to report "
250			"full capabilities\n", length);
251		return;
252	}
253	if (length >= __offsetof(struct scsi_per_res_cap, reserved))
254		check_type_mask = 1;
255
256	fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n",
257		(cap->flags1 & SPRI_RLR_C) ? 1 : 0);
258	fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n",
259		(cap->flags1 & SPRI_CRH) ? 1 : 0);
260	fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n",
261		(cap->flags1 & SPRI_SIP_C) ? 1 : 0);
262	fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n",
263		(cap->flags1 & SPRI_ATP_C) ? 1 : 0);
264	fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n",
265		(cap->flags1 & SPRI_PTPL_C) ? 1 : 0);
266	fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n",
267		(cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT);
268	/*
269	 * These cases are cut-and-pasted from SPC4r36l.  There is no
270	 * succinct way to describe these otherwise, and even with the
271	 * verbose description, the user will probably have to refer to
272	 * the spec to fully understand what is going on.
273	 */
274	switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) {
275	case SPRI_ALLOW_1:
276		fprintf(stdout,
277"    The device server allows the TEST UNIT READY command through Write\n"
278"    Exclusive type reservations and Exclusive Access type reservations\n"
279"    and does not provide information about whether the following commands\n"
280"    are allowed through Write Exclusive type reservations:\n"
281"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
282"           command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n"
283"           RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n"
284"           and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n"
285"        b) the READ DEFECT DATA command (see SBC-3).\n");
286		break;
287	case SPRI_ALLOW_2:
288		fprintf(stdout,
289"    The device server allows the TEST UNIT READY command through Write\n"
290"    Exclusive type reservations and Exclusive Access type reservations\n"
291"    and does not allow the following commands through Write Exclusive type\n"
292"    reservations:\n"
293"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
294"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
295"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
296"           FUNCTION command; and\n"
297"        b) the READ DEFECT DATA command.\n"
298"    The device server does not allow the RECEIVE COPY RESULTS command\n"
299"    through Write Exclusive type reservations or Exclusive Access type\n"
300"    reservations.\n");
301		break;
302	case SPRI_ALLOW_3:
303		fprintf(stdout,
304"    The device server allows the TEST UNIT READY command through Write\n"
305"    Exclusive type reservations and Exclusive Access type reservations\n"
306"    and allows the following commands through Write Exclusive type\n"
307"    reservations:\n"
308"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
309"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
310"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
311"           FUNCTION command; and\n"
312"        b) the READ DEFECT DATA command.\n"
313"    The device server does not allow the RECEIVE COPY RESULTS command\n"
314"    through Write Exclusive type reservations or Exclusive Access type\n"
315"    reservations.\n");
316		break;
317	case SPRI_ALLOW_4:
318		fprintf(stdout,
319"    The device server allows the TEST UNIT READY command and the RECEIVE\n"
320"    COPY RESULTS command through Write Exclusive type reservations and\n"
321"    Exclusive Access type reservations and allows the following commands\n"
322"    through Write Exclusive type reservations:\n"
323"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
324"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
325"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
326"           FUNCTION command; and\n"
327"        b) the READ DEFECT DATA command.\n");
328		break;
329	case SPRI_ALLOW_NA:
330		fprintf(stdout,
331"    No information is provided about whether certain commands are allowed\n"
332"    through certain types of persistent reservations.\n");
333		break;
334	default:
335		fprintf(stdout,
336"    Unknown ALLOW COMMANDS value %#x\n",
337			(cap->flags2 & SPRI_ALLOW_CMD_MASK) >>
338			SPRI_ALLOW_CMD_SHIFT);
339		break;
340	}
341	fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n",
342		(cap->flags2 & SPRI_PTPL_A) ? 1 : 0);
343	if ((check_type_mask != 0)
344	 && (cap->flags2 & SPRI_TMV)) {
345		fprintf(stdout, "Supported Persistent Reservation Types:\n");
346		fprintf(stdout, "    Write Exclusive - All Registrants "
347			"(WR_EX_AR): %d\n",
348			(cap->type_mask[0] & SPRI_TM_WR_EX_AR)? 1 : 0);
349		fprintf(stdout, "    Exclusive Access - Registrants Only "
350			"(EX_AC_RO): %d\n",
351			(cap->type_mask[0] & SPRI_TM_EX_AC_RO) ? 1 : 0);
352		fprintf(stdout, "    Write Exclusive - Registrants Only "
353			"(WR_EX_RO): %d\n",
354			(cap->type_mask[0] & SPRI_TM_WR_EX_RO)? 1 : 0);
355		fprintf(stdout, "    Exclusive Access (EX_AC): %d\n",
356			(cap->type_mask[0] & SPRI_TM_EX_AC) ? 1 : 0);
357		fprintf(stdout, "    Write Exclusive (WR_EX): %d\n",
358			(cap->type_mask[0] & SPRI_TM_WR_EX) ? 1 : 0);
359		fprintf(stdout, "    Exclusive Access - All Registrants "
360			"(EX_AC_AR): %d\n",
361			(cap->type_mask[1] & SPRI_TM_EX_AC_AR) ? 1 : 0);
362	} else {
363		fprintf(stdout, "Persistent Reservation Type Mask is NOT "
364			"valid\n");
365	}
366
367
368}
369
370static void
371persist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
372{
373	uint32_t length, len_to_go = 0;
374	struct scsi_per_res_in_full_desc *desc;
375	uint8_t *cur_pos;
376	int i;
377
378	length = scsi_4btoul(hdr->length);
379	length = MIN(length, valid_len);
380
381	if (length < sizeof(*desc)) {
382		if (length == 0)
383			fprintf(stdout, "No reservations.\n");
384		else
385			warnx("unable to print reservation, only got %u "
386			      "valid bytes", length);
387		return;
388	}
389
390	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
391	cur_pos = (uint8_t *)&hdr[1];
392	for (len_to_go = length, i = 0,
393	     desc = (struct scsi_per_res_in_full_desc *)cur_pos;
394	     len_to_go >= sizeof(*desc);
395	     desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) {
396		uint32_t additional_length, cur_length;
397
398
399		fprintf(stdout, "Reservation Key: %#jx\n",
400			(uintmax_t)scsi_8btou64(desc->res_key.key));
401		fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n",
402			(desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0);
403		fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n",
404			(desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0);
405
406		if (desc->flags & SPRI_FULL_R_HOLDER)
407			persist_print_scopetype(desc->scopetype);
408
409		if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0)
410			fprintf(stdout, "Relative Target Port ID: %#x\n",
411				scsi_2btoul(desc->rel_trgt_port_id));
412
413		additional_length = scsi_4btoul(desc->additional_length);
414
415		persist_print_transportid(desc->transport_id,
416					  additional_length);
417
418		cur_length = sizeof(*desc) + additional_length;
419		len_to_go -= cur_length;
420		cur_pos += cur_length;
421	}
422}
423
424int
425scsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt,
426	    int retry_count, int timeout, int verbosemode, int err_recover)
427{
428	union ccb *ccb = NULL;
429	int c, in = 0, out = 0;
430	int action = -1, num_ids = 0;
431	int error = 0;
432	uint32_t res_len = 0;
433	unsigned long rel_tgt_port = 0;
434	uint8_t *res_buf = NULL;
435	int scope = SPR_LU_SCOPE, res_type = 0;
436	struct persist_transport_id *id, *id2;
437	STAILQ_HEAD(, persist_transport_id) transport_id_list;
438	uint64_t key = 0, sa_key = 0;
439	struct scsi_nv *table = NULL;
440	size_t table_size = 0, id_len = 0;
441	uint32_t valid_len = 0;
442	int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
443
444	STAILQ_INIT(&transport_id_list);
445
446	ccb = cam_getccb(device);
447	if (ccb == NULL) {
448		warnx("%s: error allocating CCB", __func__);
449		error = 1;
450		goto bailout;
451	}
452
453	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
454
455	while ((c = getopt(argc, argv, combinedopt)) != -1) {
456		switch (c) {
457		case 'a':
458			all_tg_pt = 1;
459			break;
460		case 'I': {
461			int error_str_len = 128;
462			char error_str[error_str_len];
463			char *id_str;
464
465			id = malloc(sizeof(*id));
466			if (id == NULL) {
467				warnx("%s: error allocating %zu bytes",
468				    __func__, sizeof(*id));
469				error = 1;
470				goto bailout;
471			}
472			bzero(id, sizeof(*id));
473
474			id_str = strdup(optarg);
475			if (id_str == NULL) {
476				warnx("%s: error duplicating string %s",
477				    __func__, optarg);
478				free(id);
479				error = 1;
480				goto bailout;
481			}
482			error = scsi_parse_transportid(id_str, &id->hdr,
483			    &id->alloc_len, error_str, error_str_len);
484			if (error != 0) {
485				warnx("%s", error_str);
486				error = 1;
487				free(id);
488				free(id_str);
489				goto bailout;
490			}
491			free(id_str);
492
493			STAILQ_INSERT_TAIL(&transport_id_list, id, links);
494			num_ids++;
495			id_len += id->alloc_len;
496			break;
497		}
498		case 'k':
499		case 'K': {
500			char *endptr;
501			uint64_t tmpval;
502
503			tmpval = strtoumax(optarg, &endptr, 0);
504			if (*endptr != '\0') {
505				warnx("%s: invalid key argument %s", __func__,
506				    optarg);
507				error = 1;
508				goto bailout;
509			}
510			if (c == 'k') {
511				key = tmpval;
512			} else {
513				sa_key = tmpval;
514			}
515			break;
516		}
517		case 'i':
518		case 'o': {
519			scsi_nv_status status;
520			int table_entry = 0;
521
522			if (c == 'i') {
523				in = 1;
524				table = persist_in_actions;
525				table_size = sizeof(persist_in_actions) /
526					sizeof(persist_in_actions[0]);
527			} else {
528				out = 1;
529				table = persist_out_actions;
530				table_size = sizeof(persist_out_actions) /
531					sizeof(persist_out_actions[0]);
532			}
533
534			if ((in + out) > 1) {
535				warnx("%s: only one in (-i) or out (-o) "
536				    "action is allowed", __func__);
537				error = 1;
538				goto bailout;
539			}
540
541			status = scsi_get_nv(table, table_size, optarg,
542					     &table_entry,SCSI_NV_FLAG_IG_CASE);
543			if (status == SCSI_NV_FOUND)
544				action = table[table_entry].value;
545			else {
546				warnx("%s: %s %s option %s", __func__,
547				    (status == SCSI_NV_AMBIGUOUS) ?
548				    "ambiguous" : "invalid", in ? "in" :
549				    "out", optarg);
550				error = 1;
551				goto bailout;
552			}
553			break;
554		}
555		case 'p':
556			aptpl = 1;
557			break;
558		case 'R': {
559			char *endptr;
560
561			rel_tgt_port = strtoul(optarg, &endptr, 0);
562			if (*endptr != '\0') {
563				warnx("%s: invalid relative target port %s",
564				    __func__, optarg);
565				error = 1;
566				goto bailout;
567			}
568			rel_port_set = 1;
569			break;
570		}
571		case 's': {
572			size_t scope_size;
573			struct scsi_nv *scope_table = NULL;
574			scsi_nv_status status;
575			int table_entry = 0;
576			char *endptr;
577
578			/*
579			 * First check to see if the user gave us a numeric
580			 * argument.  If so, we'll try using it.
581			 */
582			if (isdigit(optarg[0])) {
583				scope = strtol(optarg, &endptr, 0);
584				if (*endptr != '\0') {
585					warnx("%s: invalid scope %s",
586					       __func__, optarg);
587					error = 1;
588					goto bailout;
589				}
590				scope = (scope << SPR_SCOPE_SHIFT) &
591				    SPR_SCOPE_MASK;
592				break;
593			}
594
595			scope_size = sizeof(persist_scope_table) /
596				     sizeof(persist_scope_table[0]);
597			scope_table = persist_scope_table;
598			status = scsi_get_nv(scope_table, scope_size, optarg,
599					     &table_entry,SCSI_NV_FLAG_IG_CASE);
600			if (status == SCSI_NV_FOUND)
601				scope = scope_table[table_entry].value;
602			else {
603				warnx("%s: %s scope %s", __func__,
604				      (status == SCSI_NV_AMBIGUOUS) ?
605				      "ambiguous" : "invalid", optarg);
606				error = 1;
607				goto bailout;
608			}
609			break;
610		}
611		case 'S':
612			spec_i_pt = 1;
613			break;
614		case 'T': {
615			size_t res_type_size;
616			struct scsi_nv *rtype_table = NULL;
617			scsi_nv_status status;
618			char *endptr;
619			int table_entry = 0;
620
621			/*
622			 * First check to see if the user gave us a numeric
623			 * argument.  If so, we'll try using it.
624			 */
625			if (isdigit(optarg[0])) {
626				res_type = strtol(optarg, &endptr, 0);
627				if (*endptr != '\0') {
628					warnx("%s: invalid reservation type %s",
629					       __func__, optarg);
630					error = 1;
631					goto bailout;
632				}
633				break;
634			}
635
636			res_type_size = sizeof(persist_type_table) /
637					sizeof(persist_type_table[0]);
638			rtype_table = persist_type_table;
639			status = scsi_get_nv(rtype_table, res_type_size,
640					     optarg, &table_entry,
641					     SCSI_NV_FLAG_IG_CASE);
642			if (status == SCSI_NV_FOUND)
643				res_type = rtype_table[table_entry].value;
644			else {
645				warnx("%s: %s reservation type %s", __func__,
646				      (status == SCSI_NV_AMBIGUOUS) ?
647				      "ambiguous" : "invalid", optarg);
648				error = 1;
649				goto bailout;
650			}
651			break;
652		}
653		case 'U':
654			unreg = 1;
655			break;
656		default:
657			break;
658		}
659	}
660
661	if ((in + out) != 1) {
662		warnx("%s: you must specify one of -i or -o", __func__);
663		error = 1;
664		goto bailout;
665	}
666
667	/*
668	 * Note that we don't really try to figure out whether the user
669	 * needs to specify one or both keys.  There are a number of
670	 * scenarios, and sometimes 0 is a valid and desired value.
671	 */
672	if (in != 0) {
673		switch (action) {
674		case SPRI_RK:
675		case SPRI_RR:
676		case SPRI_RS:
677			/*
678			 * Allocate the maximum length possible for these
679			 * service actions.  According to the spec, the
680			 * target is supposed to return the available
681			 * length in the header, regardless of the
682			 * allocation length.  In practice, though, with
683			 * the READ FULL STATUS (SPRI_RS) service action,
684			 * some Seagate drives (in particular a
685			 * Constellation ES, <SEAGATE ST32000444SS 0006>)
686			 * don't return the available length if you only
687			 * allocate the length of the header.  So just
688			 * allocate the maximum here so we don't miss
689			 * anything.
690			 */
691			res_len = SPRI_MAX_LEN;
692			break;
693		case SPRI_RC:
694			res_len = sizeof(struct scsi_per_res_cap);
695			break;
696		default:
697			/* In theory we should catch this above */
698			warnx("%s: invalid action %d", __func__, action);
699			error = 1;
700			goto bailout;
701			break;
702		}
703	} else {
704
705		/*
706		 * XXX KDM need to add length for transport IDs for the
707		 * register and move service action and the register
708		 * service action with the SPEC_I_PT bit set.
709		 */
710		if (action == SPRO_REG_MOVE) {
711			if (num_ids != 1) {
712			    	warnx("%s: register and move requires a "
713				    "single transport ID (-I)", __func__);
714				error = 1;
715				goto bailout;
716			}
717			if (rel_port_set == 0) {
718				warnx("%s: register and move requires a "
719				    "relative target port (-R)", __func__);
720				error = 1;
721				goto bailout;
722			}
723			res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
724		} else {
725			res_len = sizeof(struct scsi_per_res_out_parms);
726			if ((action == SPRO_REGISTER)
727			 && (num_ids != 0)) {
728				/*
729				 * If the user specifies any IDs with the
730				 * register service action, turn on the
731				 * spec_i_pt bit.
732				 */
733				spec_i_pt = 1;
734				res_len += id_len;
735				res_len +=
736				    sizeof(struct scsi_per_res_out_trans_ids);
737			}
738		}
739	}
740retry:
741	if (res_buf != NULL) {
742		free(res_buf);
743		res_buf = NULL;
744	}
745	res_buf = malloc(res_len);
746	if (res_buf == NULL) {
747		warn("%s: error allocating %d bytes", __func__, res_len);
748		error = 1;
749		goto bailout;
750	}
751	bzero(res_buf, res_len);
752
753	if (in != 0) {
754		scsi_persistent_reserve_in(&ccb->csio,
755					   /*retries*/ retry_count,
756					   /*cbfcnp*/ NULL,
757					   /*tag_action*/ MSG_SIMPLE_Q_TAG,
758					   /*service_action*/ action,
759					   /*data_ptr*/ res_buf,
760					   /*dxfer_len*/ res_len,
761					   /*sense_len*/ SSD_FULL_SIZE,
762					   /*timeout*/ timeout ? timeout :5000);
763
764	} else {
765		switch (action) {
766		case SPRO_REGISTER:
767			if (spec_i_pt != 0) {
768				struct scsi_per_res_out_trans_ids *id_hdr;
769				uint8_t *bufptr;
770
771				bufptr = res_buf +
772				    sizeof(struct scsi_per_res_out_parms) +
773				    sizeof(struct scsi_per_res_out_trans_ids);
774				STAILQ_FOREACH(id, &transport_id_list, links) {
775					bcopy(id->hdr, bufptr, id->alloc_len);
776					bufptr += id->alloc_len;
777				}
778				id_hdr = (struct scsi_per_res_out_trans_ids *)
779				    (res_buf +
780				    sizeof(struct scsi_per_res_out_parms));
781				scsi_ulto4b(id_len, id_hdr->additional_length);
782			}
783		case SPRO_REG_IGNO:
784		case SPRO_PREEMPT:
785		case SPRO_PRE_ABO:
786		case SPRO_RESERVE:
787		case SPRO_RELEASE:
788		case SPRO_CLEAR:
789		case SPRO_REPL_LOST_RES: {
790			struct scsi_per_res_out_parms *parms;
791
792			parms = (struct scsi_per_res_out_parms *)res_buf;
793
794			scsi_u64to8b(key, parms->res_key.key);
795			scsi_u64to8b(sa_key, parms->serv_act_res_key);
796			if (spec_i_pt != 0)
797				parms->flags |= SPR_SPEC_I_PT;
798			if (all_tg_pt != 0)
799				parms->flags |= SPR_ALL_TG_PT;
800			if (aptpl != 0)
801				parms->flags |= SPR_APTPL;
802			break;
803		}
804		case SPRO_REG_MOVE: {
805			struct scsi_per_res_reg_move *reg_move;
806			uint8_t *bufptr;
807
808			reg_move = (struct scsi_per_res_reg_move *)res_buf;
809
810			scsi_u64to8b(key, reg_move->res_key.key);
811			scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
812			if (unreg != 0)
813				reg_move->flags |= SPR_REG_MOVE_UNREG;
814			if (aptpl != 0)
815				reg_move->flags |= SPR_REG_MOVE_APTPL;
816			scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
817			id = STAILQ_FIRST(&transport_id_list);
818			/*
819			 * This shouldn't happen, since we already checked
820			 * the number of IDs above.
821			 */
822			if (id == NULL) {
823				warnx("%s: No transport IDs found!", __func__);
824				error = 1;
825				goto bailout;
826			}
827			bufptr = (uint8_t *)&reg_move[1];
828			bcopy(id->hdr, bufptr, id->alloc_len);
829			scsi_ulto4b(id->alloc_len,
830			    reg_move->transport_id_length);
831			break;
832		}
833		default:
834			break;
835		}
836		scsi_persistent_reserve_out(&ccb->csio,
837					    /*retries*/ retry_count,
838					    /*cbfcnp*/ NULL,
839					    /*tag_action*/ MSG_SIMPLE_Q_TAG,
840					    /*service_action*/ action,
841					    /*scope*/ scope,
842					    /*res_type*/ res_type,
843					    /*data_ptr*/ res_buf,
844					    /*dxfer_len*/ res_len,
845					    /*sense_len*/ SSD_FULL_SIZE,
846					    /*timeout*/ timeout ?timeout :5000);
847	}
848
849	/* Disable freezing the device queue */
850	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
851
852	if (err_recover != 0)
853		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
854
855	if (cam_send_ccb(device, ccb) < 0) {
856		warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
857		    "IN" : "OUT");
858
859		if (verbosemode != 0) {
860			cam_error_print(device, ccb, CAM_ESF_ALL,
861					CAM_EPF_ALL, stderr);
862		}
863
864		error = 1;
865		goto bailout;
866	}
867
868	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
869		if (verbosemode != 0) {
870			cam_error_print(device, ccb, CAM_ESF_ALL,
871					CAM_EPF_ALL, stderr);
872		}
873		error = 1;
874		goto bailout;
875	}
876
877	if (in == 0)
878		goto bailout;
879
880	valid_len = res_len - ccb->csio.resid;
881
882	switch (action) {
883	case SPRI_RK:
884	case SPRI_RR:
885	case SPRI_RS: {
886		struct scsi_per_res_in_header *hdr;
887		uint32_t hdr_len;
888
889		if (valid_len < sizeof(*hdr)) {
890			warnx("%s: only got %d valid bytes, need %zd",
891			      __func__, valid_len, sizeof(*hdr));
892			error = 1;
893			goto bailout;
894		}
895		hdr = (struct scsi_per_res_in_header *)res_buf;
896		hdr_len = scsi_4btoul(hdr->length);
897
898		if (hdr_len > (res_len - sizeof(*hdr))) {
899			res_len = hdr_len + sizeof(*hdr);
900			goto retry;
901		}
902
903		if (action == SPRI_RK) {
904			persist_print_keys(hdr, valid_len);
905		} else if (action == SPRI_RR) {
906			persist_print_res(hdr, valid_len);
907		} else {
908			persist_print_full(hdr, valid_len);
909		}
910		break;
911	}
912	case SPRI_RC: {
913		struct scsi_per_res_cap *cap;
914		uint32_t cap_len;
915
916		if (valid_len < sizeof(*cap)) {
917			warnx("%s: only got %u valid bytes, need %zd",
918			      __func__, valid_len, sizeof(*cap));
919			error = 1;
920			goto bailout;
921		}
922		cap = (struct scsi_per_res_cap *)res_buf;
923		cap_len = scsi_2btoul(cap->length);
924		if (cap_len != sizeof(*cap)) {
925			/*
926			 * We should be able to deal with this,
927			 * it's just more trouble.
928			 */
929			warnx("%s: reported size %u is different "
930			    "than expected size %zd", __func__,
931			    cap_len, sizeof(*cap));
932		}
933
934		/*
935		 * If there is more data available, grab it all,
936		 * even though we don't really know what to do with
937		 * the extra data since it obviously wasn't in the
938		 * spec when this code was written.
939		 */
940		if (cap_len > res_len) {
941			res_len = cap_len;
942			goto retry;
943		}
944		persist_print_cap(cap, valid_len);
945		break;
946	}
947	default:
948		break;
949	}
950
951bailout:
952	free(res_buf);
953
954	if (ccb != NULL)
955		cam_freeccb(ccb);
956
957	STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
958		STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
959		    links);
960		free(id);
961	}
962	return (error);
963}
964