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#include <sys/ioctl.h>
38#include <sys/stdint.h>
39#include <sys/types.h>
40#include <sys/endian.h>
41#include <sys/sbuf.h>
42#include <sys/queue.h>
43
44#include <stdio.h>
45#include <stdlib.h>
46#include <inttypes.h>
47#include <unistd.h>
48#include <string.h>
49#include <strings.h>
50#include <fcntl.h>
51#include <ctype.h>
52#include <limits.h>
53#include <err.h>
54
55#include <cam/cam.h>
56#include <cam/cam_debug.h>
57#include <cam/cam_ccb.h>
58#include <cam/scsi/scsi_all.h>
59#include <cam/scsi/scsi_pass.h>
60#include <cam/scsi/scsi_message.h>
61#include <camlib.h>
62#include "camcontrol.h"
63
64struct persist_transport_id {
65	struct scsi_transportid_header *hdr;
66	unsigned int alloc_len;
67	STAILQ_ENTRY(persist_transport_id) links;
68};
69
70/*
71 * Service Actions for PERSISTENT RESERVE IN.
72 */
73static struct scsi_nv persist_in_actions[] = {
74	{ "read_keys", SPRI_RK },
75	{ "read_reservation", SPRI_RR },
76	{ "report_capabilities", SPRI_RC },
77	{ "read_full_status", SPRI_RS }
78};
79
80/*
81 * Service Actions for PERSISTENT RESERVE OUT.
82 */
83static struct scsi_nv persist_out_actions[] = {
84	{ "register", SPRO_REGISTER },
85	{ "reserve", SPRO_RESERVE },
86	{ "release" , SPRO_RELEASE },
87	{ "clear", SPRO_CLEAR },
88	{ "preempt", SPRO_PREEMPT },
89	{ "preempt_abort", SPRO_PRE_ABO },
90	{ "register_ignore", SPRO_REG_IGNO },
91	{ "register_move", SPRO_REG_MOVE },
92	{ "replace_lost", SPRO_REPL_LOST_RES }
93};
94
95/*
96 * Known reservation scopes.  As of SPC-4, only LU_SCOPE is used in the
97 * spec.  The others are obsolete.
98 */
99static struct scsi_nv persist_scope_table[] = {
100	{ "lun", SPR_LU_SCOPE },
101	{ "extent", SPR_EXTENT_SCOPE },
102	{ "element", SPR_ELEMENT_SCOPE }
103};
104
105/*
106 * Reservation types.  The longer name for a given reservation type is
107 * listed first, so that it makes more sense when we print out the
108 * reservation type.  We step through the table linearly when looking for
109 * the text name for a particular numeric reservation type value.
110 */
111static struct scsi_nv persist_type_table[] = {
112	{ "read_shared", SPR_TYPE_RD_SHARED },
113	{ "write_exclusive", SPR_TYPE_WR_EX },
114	{ "wr_ex", SPR_TYPE_WR_EX },
115	{ "read_exclusive", SPR_TYPE_RD_EX },
116	{ "rd_ex", SPR_TYPE_RD_EX },
117	{ "exclusive_access", SPR_TYPE_EX_AC },
118	{ "ex_ac", SPR_TYPE_EX_AC },
119	{ "write_exclusive_reg_only", SPR_TYPE_WR_EX_RO },
120	{ "wr_ex_ro", SPR_TYPE_WR_EX_RO },
121	{ "exclusive_access_reg_only", SPR_TYPE_EX_AC_RO },
122	{ "ex_ac_ro", SPR_TYPE_EX_AC_RO },
123	{ "write_exclusive_all_regs", SPR_TYPE_WR_EX_AR },
124	{ "wr_ex_ar", SPR_TYPE_WR_EX_AR },
125	{ "exclusive_access_all_regs", SPR_TYPE_EX_AC_AR },
126	{ "ex_ac_ar", SPR_TYPE_EX_AC_AR }
127};
128
129/*
130 * Print out the standard scope/type field.
131 */
132static void
133persist_print_scopetype(uint8_t scopetype)
134{
135	const char *tmpstr;
136	int num_entries;
137
138	num_entries = sizeof(persist_scope_table) /
139		      sizeof(persist_scope_table[0]);
140	tmpstr = scsi_nv_to_str(persist_scope_table, num_entries,
141				scopetype & SPR_SCOPE_MASK);
142	fprintf(stdout, "Scope: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
143		"Unknown", (scopetype & SPR_SCOPE_MASK) >> SPR_SCOPE_SHIFT);
144
145	num_entries = sizeof(persist_type_table) /
146		      sizeof(persist_type_table[0]);
147	tmpstr = scsi_nv_to_str(persist_type_table, num_entries,
148				scopetype & SPR_TYPE_MASK);
149	fprintf(stdout, "Type: %s (%#x)\n", (tmpstr != NULL) ? tmpstr :
150		"Unknown", scopetype & SPR_TYPE_MASK);
151}
152
153static void
154persist_print_transportid(uint8_t *buf, uint32_t len)
155{
156	struct sbuf *sb;
157
158	sb = sbuf_new_auto();
159	if (sb == NULL)
160		fprintf(stderr, "Unable to allocate sbuf\n");
161
162	scsi_transportid_sbuf(sb, (struct scsi_transportid_header *)buf, len);
163
164	sbuf_finish(sb);
165
166	fprintf(stdout, "%s\n", sbuf_data(sb));
167
168	sbuf_delete(sb);
169}
170
171/*
172 * Print out a persistent reservation.  This is used with the READ
173 * RESERVATION (0x01) service action of the PERSISTENT RESERVE IN command.
174 */
175static void
176persist_print_res(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
177{
178	uint32_t length;
179	struct scsi_per_res_in_rsrv *res;
180
181	length = scsi_4btoul(hdr->length);
182	length = MIN(length, valid_len);
183
184	res = (struct scsi_per_res_in_rsrv *)hdr;
185
186	if (length < sizeof(res->data) - sizeof(res->data.extent_length)) {
187		if (length == 0)
188			fprintf(stdout, "No reservations.\n");
189		else
190			warnx("unable to print reservation, only got %u "
191			      "valid bytes", length);
192		return;
193	}
194	fprintf(stdout, "PRgeneration: %#x\n",
195		scsi_4btoul(res->header.generation));
196	fprintf(stdout, "Reservation Key: %#jx\n",
197		(uintmax_t)scsi_8btou64(res->data.reservation));
198	fprintf(stdout, "Scope address: %#x\n",
199		scsi_4btoul(res->data.scope_addr));
200
201	persist_print_scopetype(res->data.scopetype);
202
203	fprintf(stdout, "Extent length: %u\n",
204		scsi_2btoul(res->data.extent_length));
205}
206
207/*
208 * Print out persistent reservation keys.  This is used with the READ KEYS
209 * service action of the PERSISTENT RESERVE IN command.
210 */
211static void
212persist_print_keys(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
213{
214	uint32_t length, num_keys, i;
215	struct scsi_per_res_key *key;
216
217	length = scsi_4btoul(hdr->length);
218	length = MIN(length, valid_len);
219
220	num_keys = length / sizeof(*key);
221
222	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
223	fprintf(stdout, "%u key%s%s\n", num_keys, (num_keys == 1) ? "" : "s",
224		(num_keys == 0) ? "." : ":");
225
226	for (i = 0, key = (struct scsi_per_res_key *)&hdr[1]; i < num_keys;
227	     i++, key++) {
228		fprintf(stdout, "%u: %#jx\n", i,
229			(uintmax_t)scsi_8btou64(key->key));
230	}
231}
232
233/*
234 * Print out persistent reservation capabilities.  This is used with the
235 * REPORT CAPABILITIES service action of the PERSISTENT RESERVE IN command.
236 */
237static void
238persist_print_cap(struct scsi_per_res_cap *cap, uint32_t valid_len)
239{
240	uint32_t length;
241	int check_type_mask = 0;
242	uint32_t type_mask;
243
244	length = scsi_2btoul(cap->length);
245	length = MIN(length, valid_len);
246	type_mask = scsi_2btoul(cap->type_mask);
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			(type_mask & SPRI_TM_WR_EX_AR)? 1 : 0);
349		fprintf(stdout, "    Exclusive Access - Registrants Only "
350			"(EX_AC_RO): %d\n",
351			(type_mask & SPRI_TM_EX_AC_RO) ? 1 : 0);
352		fprintf(stdout, "    Write Exclusive - Registrants Only "
353			"(WR_EX_RO): %d\n",
354			(type_mask & SPRI_TM_WR_EX_RO)? 1 : 0);
355		fprintf(stdout, "    Exclusive Access (EX_AC): %d\n",
356			(type_mask & SPRI_TM_EX_AC) ? 1 : 0);
357		fprintf(stdout, "    Write Exclusive (WR_EX): %d\n",
358			(type_mask & SPRI_TM_WR_EX) ? 1 : 0);
359		fprintf(stdout, "    Exclusive Access - All Registrants "
360			"(EX_AC_AR): %d\n",
361			(type_mask & 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 task_attr, int retry_count, int timeout, int verbosemode,
427	    int err_recover)
428{
429	union ccb *ccb = NULL;
430	int c, in = 0, out = 0;
431	int action = -1, num_ids = 0;
432	int error = 0;
433	uint32_t res_len = 0;
434	unsigned long rel_tgt_port = 0;
435	uint8_t *res_buf = NULL;
436	int scope = SPR_LU_SCOPE, res_type = 0;
437	struct persist_transport_id *id, *id2;
438	STAILQ_HEAD(, persist_transport_id) transport_id_list;
439	uint64_t key = 0, sa_key = 0;
440	struct scsi_nv *table = NULL;
441	size_t table_size = 0, id_len = 0;
442	uint32_t valid_len = 0;
443	int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
444
445	STAILQ_INIT(&transport_id_list);
446
447	ccb = cam_getccb(device);
448	if (ccb == NULL) {
449		warnx("%s: error allocating CCB", __func__);
450		error = 1;
451		goto bailout;
452	}
453
454	while ((c = getopt(argc, argv, combinedopt)) != -1) {
455		switch (c) {
456		case 'a':
457			all_tg_pt = 1;
458			break;
459		case 'I': {
460			int error_str_len = 128;
461			char error_str[error_str_len];
462			char *id_str;
463
464			id = malloc(sizeof(*id));
465			if (id == NULL) {
466				warnx("%s: error allocating %zu bytes",
467				    __func__, sizeof(*id));
468				error = 1;
469				goto bailout;
470			}
471			bzero(id, sizeof(*id));
472
473			id_str = strdup(optarg);
474			if (id_str == NULL) {
475				warnx("%s: error duplicating string %s",
476				    __func__, optarg);
477				free(id);
478				error = 1;
479				goto bailout;
480			}
481			error = scsi_parse_transportid(id_str, &id->hdr,
482			    &id->alloc_len, error_str, error_str_len);
483			if (error != 0) {
484				warnx("%s", error_str);
485				error = 1;
486				free(id);
487				free(id_str);
488				goto bailout;
489			}
490			free(id_str);
491
492			STAILQ_INSERT_TAIL(&transport_id_list, id, links);
493			num_ids++;
494			id_len += id->alloc_len;
495			break;
496		}
497		case 'k':
498		case 'K': {
499			char *endptr;
500			uint64_t tmpval;
501
502			tmpval = strtoumax(optarg, &endptr, 0);
503			if (*endptr != '\0') {
504				warnx("%s: invalid key argument %s", __func__,
505				    optarg);
506				error = 1;
507				goto bailout;
508			}
509			if (c == 'k') {
510				key = tmpval;
511			} else {
512				sa_key = tmpval;
513			}
514			break;
515		}
516		case 'i':
517		case 'o': {
518			scsi_nv_status status;
519			int table_entry = 0;
520
521			if (c == 'i') {
522				in = 1;
523				table = persist_in_actions;
524				table_size = sizeof(persist_in_actions) /
525					sizeof(persist_in_actions[0]);
526			} else {
527				out = 1;
528				table = persist_out_actions;
529				table_size = sizeof(persist_out_actions) /
530					sizeof(persist_out_actions[0]);
531			}
532
533			if ((in + out) > 1) {
534				warnx("%s: only one in (-i) or out (-o) "
535				    "action is allowed", __func__);
536				error = 1;
537				goto bailout;
538			}
539
540			status = scsi_get_nv(table, table_size, optarg,
541					     &table_entry,SCSI_NV_FLAG_IG_CASE);
542			if (status == SCSI_NV_FOUND)
543				action = table[table_entry].value;
544			else {
545				warnx("%s: %s %s option %s", __func__,
546				    (status == SCSI_NV_AMBIGUOUS) ?
547				    "ambiguous" : "invalid", in ? "in" :
548				    "out", optarg);
549				error = 1;
550				goto bailout;
551			}
552			break;
553		}
554		case 'p':
555			aptpl = 1;
556			break;
557		case 'R': {
558			char *endptr;
559
560			rel_tgt_port = strtoul(optarg, &endptr, 0);
561			if (*endptr != '\0') {
562				warnx("%s: invalid relative target port %s",
563				    __func__, optarg);
564				error = 1;
565				goto bailout;
566			}
567			rel_port_set = 1;
568			break;
569		}
570		case 's': {
571			size_t scope_size;
572			struct scsi_nv *scope_table = NULL;
573			scsi_nv_status status;
574			int table_entry = 0;
575			char *endptr;
576
577			/*
578			 * First check to see if the user gave us a numeric
579			 * argument.  If so, we'll try using it.
580			 */
581			if (isdigit(optarg[0])) {
582				scope = strtol(optarg, &endptr, 0);
583				if (*endptr != '\0') {
584					warnx("%s: invalid scope %s",
585					       __func__, optarg);
586					error = 1;
587					goto bailout;
588				}
589				scope = (scope << SPR_SCOPE_SHIFT) &
590				    SPR_SCOPE_MASK;
591				break;
592			}
593
594			scope_size = sizeof(persist_scope_table) /
595				     sizeof(persist_scope_table[0]);
596			scope_table = persist_scope_table;
597			status = scsi_get_nv(scope_table, scope_size, optarg,
598					     &table_entry,SCSI_NV_FLAG_IG_CASE);
599			if (status == SCSI_NV_FOUND)
600				scope = scope_table[table_entry].value;
601			else {
602				warnx("%s: %s scope %s", __func__,
603				      (status == SCSI_NV_AMBIGUOUS) ?
604				      "ambiguous" : "invalid", optarg);
605				error = 1;
606				goto bailout;
607			}
608			break;
609		}
610		case 'S':
611			spec_i_pt = 1;
612			break;
613		case 'T': {
614			size_t res_type_size;
615			struct scsi_nv *rtype_table = NULL;
616			scsi_nv_status status;
617			char *endptr;
618			int table_entry = 0;
619
620			/*
621			 * First check to see if the user gave us a numeric
622			 * argument.  If so, we'll try using it.
623			 */
624			if (isdigit(optarg[0])) {
625				res_type = strtol(optarg, &endptr, 0);
626				if (*endptr != '\0') {
627					warnx("%s: invalid reservation type %s",
628					       __func__, optarg);
629					error = 1;
630					goto bailout;
631				}
632				break;
633			}
634
635			res_type_size = sizeof(persist_type_table) /
636					sizeof(persist_type_table[0]);
637			rtype_table = persist_type_table;
638			status = scsi_get_nv(rtype_table, res_type_size,
639					     optarg, &table_entry,
640					     SCSI_NV_FLAG_IG_CASE);
641			if (status == SCSI_NV_FOUND)
642				res_type = rtype_table[table_entry].value;
643			else {
644				warnx("%s: %s reservation type %s", __func__,
645				      (status == SCSI_NV_AMBIGUOUS) ?
646				      "ambiguous" : "invalid", optarg);
647				error = 1;
648				goto bailout;
649			}
650			break;
651		}
652		case 'U':
653			unreg = 1;
654			break;
655		default:
656			break;
657		}
658	}
659
660	if ((in + out) != 1) {
661		warnx("%s: you must specify one of -i or -o", __func__);
662		error = 1;
663		goto bailout;
664	}
665
666	/*
667	 * Note that we don't really try to figure out whether the user
668	 * needs to specify one or both keys.  There are a number of
669	 * scenarios, and sometimes 0 is a valid and desired value.
670	 */
671	if (in != 0) {
672		switch (action) {
673		case SPRI_RK:
674		case SPRI_RR:
675		case SPRI_RS:
676			/*
677			 * Allocate the maximum length possible for these
678			 * service actions.  According to the spec, the
679			 * target is supposed to return the available
680			 * length in the header, regardless of the
681			 * allocation length.  In practice, though, with
682			 * the READ FULL STATUS (SPRI_RS) service action,
683			 * some Seagate drives (in particular a
684			 * Constellation ES, <SEAGATE ST32000444SS 0006>)
685			 * don't return the available length if you only
686			 * allocate the length of the header.  So just
687			 * allocate the maximum here so we don't miss
688			 * anything.
689			 */
690			res_len = SPRI_MAX_LEN;
691			break;
692		case SPRI_RC:
693			res_len = sizeof(struct scsi_per_res_cap);
694			break;
695		default:
696			/* In theory we should catch this above */
697			warnx("%s: invalid action %d", __func__, action);
698			error = 1;
699			goto bailout;
700			break;
701		}
702	} else {
703
704		/*
705		 * XXX KDM need to add length for transport IDs for the
706		 * register and move service action and the register
707		 * service action with the SPEC_I_PT bit set.
708		 */
709		if (action == SPRO_REG_MOVE) {
710			if (num_ids != 1) {
711			    	warnx("%s: register and move requires a "
712				    "single transport ID (-I)", __func__);
713				error = 1;
714				goto bailout;
715			}
716			if (rel_port_set == 0) {
717				warnx("%s: register and move requires a "
718				    "relative target port (-R)", __func__);
719				error = 1;
720				goto bailout;
721			}
722			res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
723		} else {
724			res_len = sizeof(struct scsi_per_res_out_parms);
725			if ((action == SPRO_REGISTER)
726			 && (num_ids != 0)) {
727				/*
728				 * If the user specifies any IDs with the
729				 * register service action, turn on the
730				 * spec_i_pt bit.
731				 */
732				spec_i_pt = 1;
733				res_len += id_len;
734				res_len +=
735				    sizeof(struct scsi_per_res_out_trans_ids);
736			}
737		}
738	}
739retry:
740	if (res_buf != NULL) {
741		free(res_buf);
742		res_buf = NULL;
743	}
744	res_buf = malloc(res_len);
745	if (res_buf == NULL) {
746		warn("%s: error allocating %d bytes", __func__, res_len);
747		error = 1;
748		goto bailout;
749	}
750	bzero(res_buf, res_len);
751
752	if (in != 0) {
753		scsi_persistent_reserve_in(&ccb->csio,
754					   /*retries*/ retry_count,
755					   /*cbfcnp*/ NULL,
756					   /*tag_action*/ task_attr,
757					   /*service_action*/ action,
758					   /*data_ptr*/ res_buf,
759					   /*dxfer_len*/ res_len,
760					   /*sense_len*/ SSD_FULL_SIZE,
761					   /*timeout*/ timeout ? timeout :5000);
762
763	} else {
764		switch (action) {
765		case SPRO_REGISTER:
766			if (spec_i_pt != 0) {
767				struct scsi_per_res_out_trans_ids *id_hdr;
768				uint8_t *bufptr;
769
770				bufptr = res_buf +
771				    sizeof(struct scsi_per_res_out_parms) +
772				    sizeof(struct scsi_per_res_out_trans_ids);
773				STAILQ_FOREACH(id, &transport_id_list, links) {
774					bcopy(id->hdr, bufptr, id->alloc_len);
775					bufptr += id->alloc_len;
776				}
777				id_hdr = (struct scsi_per_res_out_trans_ids *)
778				    (res_buf +
779				    sizeof(struct scsi_per_res_out_parms));
780				scsi_ulto4b(id_len, id_hdr->additional_length);
781			}
782		case SPRO_REG_IGNO:
783		case SPRO_PREEMPT:
784		case SPRO_PRE_ABO:
785		case SPRO_RESERVE:
786		case SPRO_RELEASE:
787		case SPRO_CLEAR:
788		case SPRO_REPL_LOST_RES: {
789			struct scsi_per_res_out_parms *parms;
790
791			parms = (struct scsi_per_res_out_parms *)res_buf;
792
793			scsi_u64to8b(key, parms->res_key.key);
794			scsi_u64to8b(sa_key, parms->serv_act_res_key);
795			if (spec_i_pt != 0)
796				parms->flags |= SPR_SPEC_I_PT;
797			if (all_tg_pt != 0)
798				parms->flags |= SPR_ALL_TG_PT;
799			if (aptpl != 0)
800				parms->flags |= SPR_APTPL;
801			break;
802		}
803		case SPRO_REG_MOVE: {
804			struct scsi_per_res_reg_move *reg_move;
805			uint8_t *bufptr;
806
807			reg_move = (struct scsi_per_res_reg_move *)res_buf;
808
809			scsi_u64to8b(key, reg_move->res_key.key);
810			scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
811			if (unreg != 0)
812				reg_move->flags |= SPR_REG_MOVE_UNREG;
813			if (aptpl != 0)
814				reg_move->flags |= SPR_REG_MOVE_APTPL;
815			scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
816			id = STAILQ_FIRST(&transport_id_list);
817			/*
818			 * This shouldn't happen, since we already checked
819			 * the number of IDs above.
820			 */
821			if (id == NULL) {
822				warnx("%s: No transport IDs found!", __func__);
823				error = 1;
824				goto bailout;
825			}
826			bufptr = (uint8_t *)&reg_move[1];
827			bcopy(id->hdr, bufptr, id->alloc_len);
828			scsi_ulto4b(id->alloc_len,
829			    reg_move->transport_id_length);
830			break;
831		}
832		default:
833			break;
834		}
835		scsi_persistent_reserve_out(&ccb->csio,
836					    /*retries*/ retry_count,
837					    /*cbfcnp*/ NULL,
838					    /*tag_action*/ task_attr,
839					    /*service_action*/ action,
840					    /*scope*/ scope,
841					    /*res_type*/ res_type,
842					    /*data_ptr*/ res_buf,
843					    /*dxfer_len*/ res_len,
844					    /*sense_len*/ SSD_FULL_SIZE,
845					    /*timeout*/ timeout ?timeout :5000);
846	}
847
848	/* Disable freezing the device queue */
849	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
850
851	if (err_recover != 0)
852		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
853
854	if (cam_send_ccb(device, ccb) < 0) {
855		warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
856		    "IN" : "OUT");
857		error = 1;
858		goto bailout;
859	}
860
861	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
862		if (verbosemode != 0) {
863			cam_error_print(device, ccb, CAM_ESF_ALL,
864					CAM_EPF_ALL, stderr);
865		}
866		error = 1;
867		goto bailout;
868	}
869
870	if (in == 0)
871		goto bailout;
872
873	valid_len = res_len - ccb->csio.resid;
874
875	switch (action) {
876	case SPRI_RK:
877	case SPRI_RR:
878	case SPRI_RS: {
879		struct scsi_per_res_in_header *hdr;
880		uint32_t hdr_len;
881
882		if (valid_len < sizeof(*hdr)) {
883			warnx("%s: only got %d valid bytes, need %zd",
884			      __func__, valid_len, sizeof(*hdr));
885			error = 1;
886			goto bailout;
887		}
888		hdr = (struct scsi_per_res_in_header *)res_buf;
889		hdr_len = scsi_4btoul(hdr->length);
890
891		if (hdr_len > (res_len - sizeof(*hdr))) {
892			res_len = hdr_len + sizeof(*hdr);
893			goto retry;
894		}
895
896		if (action == SPRI_RK) {
897			persist_print_keys(hdr, valid_len);
898		} else if (action == SPRI_RR) {
899			persist_print_res(hdr, valid_len);
900		} else {
901			persist_print_full(hdr, valid_len);
902		}
903		break;
904	}
905	case SPRI_RC: {
906		struct scsi_per_res_cap *cap;
907		uint32_t cap_len;
908
909		if (valid_len < sizeof(*cap)) {
910			warnx("%s: only got %u valid bytes, need %zd",
911			      __func__, valid_len, sizeof(*cap));
912			error = 1;
913			goto bailout;
914		}
915		cap = (struct scsi_per_res_cap *)res_buf;
916		cap_len = scsi_2btoul(cap->length);
917		if (cap_len != sizeof(*cap)) {
918			/*
919			 * We should be able to deal with this,
920			 * it's just more trouble.
921			 */
922			warnx("%s: reported size %u is different "
923			    "than expected size %zd", __func__,
924			    cap_len, sizeof(*cap));
925		}
926
927		/*
928		 * If there is more data available, grab it all,
929		 * even though we don't really know what to do with
930		 * the extra data since it obviously wasn't in the
931		 * spec when this code was written.
932		 */
933		if (cap_len > res_len) {
934			res_len = cap_len;
935			goto retry;
936		}
937		persist_print_cap(cap, valid_len);
938		break;
939	}
940	default:
941		break;
942	}
943
944bailout:
945	free(res_buf);
946
947	if (ccb != NULL)
948		cam_freeccb(ccb);
949
950	STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
951		STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
952		    links);
953		free(id);
954	}
955	return (error);
956}
957