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: stable/11/sbin/camcontrol/persist.c 352289 2019-09-13 15:13:21Z mav $");
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	uint32_t type_mask;
245
246	length = scsi_2btoul(cap->length);
247	length = MIN(length, valid_len);
248	type_mask = scsi_2btoul(cap->type_mask);
249
250	if (length < __offsetof(struct scsi_per_res_cap, type_mask)) {
251		fprintf(stdout, "Insufficient data (%u bytes) to report "
252			"full capabilities\n", length);
253		return;
254	}
255	if (length >= __offsetof(struct scsi_per_res_cap, reserved))
256		check_type_mask = 1;
257
258	fprintf(stdout, "Replace Lost Reservation Capable (RLR_C): %d\n",
259		(cap->flags1 & SPRI_RLR_C) ? 1 : 0);
260	fprintf(stdout, "Compatible Reservation Handling (CRH): %d\n",
261		(cap->flags1 & SPRI_CRH) ? 1 : 0);
262	fprintf(stdout, "Specify Initiator Ports Capable (SIP_C): %d\n",
263		(cap->flags1 & SPRI_SIP_C) ? 1 : 0);
264	fprintf(stdout, "All Target Ports Capable (ATP_C): %d\n",
265		(cap->flags1 & SPRI_ATP_C) ? 1 : 0);
266	fprintf(stdout, "Persist Through Power Loss Capable (PTPL_C): %d\n",
267		(cap->flags1 & SPRI_PTPL_C) ? 1 : 0);
268	fprintf(stdout, "ALLOW COMMANDS field: (%#x)\n",
269		(cap->flags2 & SPRI_ALLOW_CMD_MASK) >> SPRI_ALLOW_CMD_SHIFT);
270	/*
271	 * These cases are cut-and-pasted from SPC4r36l.  There is no
272	 * succinct way to describe these otherwise, and even with the
273	 * verbose description, the user will probably have to refer to
274	 * the spec to fully understand what is going on.
275	 */
276	switch (cap->flags2 & SPRI_ALLOW_CMD_MASK) {
277	case SPRI_ALLOW_1:
278		fprintf(stdout,
279"    The device server allows the TEST UNIT READY command through Write\n"
280"    Exclusive type reservations and Exclusive Access type reservations\n"
281"    and does not provide information about whether the following commands\n"
282"    are allowed through Write Exclusive type reservations:\n"
283"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
284"           command, RECEIVE COPY RESULTS command, RECEIVE DIAGNOSTIC\n"
285"           RESULTS command, REPORT SUPPORTED OPERATION CODES command,\n"
286"           and REPORT SUPPORTED TASK MANAGEMENT FUNCTION command; and\n"
287"        b) the READ DEFECT DATA command (see SBC-3).\n");
288		break;
289	case SPRI_ALLOW_2:
290		fprintf(stdout,
291"    The device server allows the TEST UNIT READY command through Write\n"
292"    Exclusive type reservations and Exclusive Access type reservations\n"
293"    and does not allow the following commands through Write Exclusive type\n"
294"    reservations:\n"
295"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
296"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
297"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
298"           FUNCTION command; and\n"
299"        b) the READ DEFECT DATA command.\n"
300"    The device server does not allow the RECEIVE COPY RESULTS command\n"
301"    through Write Exclusive type reservations or Exclusive Access type\n"
302"    reservations.\n");
303		break;
304	case SPRI_ALLOW_3:
305		fprintf(stdout,
306"    The device server allows the TEST UNIT READY command through Write\n"
307"    Exclusive type reservations and Exclusive Access type reservations\n"
308"    and allows the following commands through Write Exclusive type\n"
309"    reservations:\n"
310"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
311"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
312"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
313"           FUNCTION command; and\n"
314"        b) the READ DEFECT DATA command.\n"
315"    The device server does not allow the RECEIVE COPY RESULTS command\n"
316"    through Write Exclusive type reservations or Exclusive Access type\n"
317"    reservations.\n");
318		break;
319	case SPRI_ALLOW_4:
320		fprintf(stdout,
321"    The device server allows the TEST UNIT READY command and the RECEIVE\n"
322"    COPY RESULTS command through Write Exclusive type reservations and\n"
323"    Exclusive Access type reservations and allows the following commands\n"
324"    through Write Exclusive type reservations:\n"
325"        a) the MODE SENSE command, READ ATTRIBUTE command, READ BUFFER\n"
326"           command, RECEIVE DIAGNOSTIC RESULTS command, REPORT SUPPORTED\n"
327"           OPERATION CODES command, and REPORT SUPPORTED TASK MANAGEMENT\n"
328"           FUNCTION command; and\n"
329"        b) the READ DEFECT DATA command.\n");
330		break;
331	case SPRI_ALLOW_NA:
332		fprintf(stdout,
333"    No information is provided about whether certain commands are allowed\n"
334"    through certain types of persistent reservations.\n");
335		break;
336	default:
337		fprintf(stdout,
338"    Unknown ALLOW COMMANDS value %#x\n",
339			(cap->flags2 & SPRI_ALLOW_CMD_MASK) >>
340			SPRI_ALLOW_CMD_SHIFT);
341		break;
342	}
343	fprintf(stdout, "Persist Through Power Loss Activated (PTPL_A): %d\n",
344		(cap->flags2 & SPRI_PTPL_A) ? 1 : 0);
345	if ((check_type_mask != 0)
346	 && (cap->flags2 & SPRI_TMV)) {
347		fprintf(stdout, "Supported Persistent Reservation Types:\n");
348		fprintf(stdout, "    Write Exclusive - All Registrants "
349			"(WR_EX_AR): %d\n",
350			(type_mask & SPRI_TM_WR_EX_AR)? 1 : 0);
351		fprintf(stdout, "    Exclusive Access - Registrants Only "
352			"(EX_AC_RO): %d\n",
353			(type_mask & SPRI_TM_EX_AC_RO) ? 1 : 0);
354		fprintf(stdout, "    Write Exclusive - Registrants Only "
355			"(WR_EX_RO): %d\n",
356			(type_mask & SPRI_TM_WR_EX_RO)? 1 : 0);
357		fprintf(stdout, "    Exclusive Access (EX_AC): %d\n",
358			(type_mask & SPRI_TM_EX_AC) ? 1 : 0);
359		fprintf(stdout, "    Write Exclusive (WR_EX): %d\n",
360			(type_mask & SPRI_TM_WR_EX) ? 1 : 0);
361		fprintf(stdout, "    Exclusive Access - All Registrants "
362			"(EX_AC_AR): %d\n",
363			(type_mask & SPRI_TM_EX_AC_AR) ? 1 : 0);
364	} else {
365		fprintf(stdout, "Persistent Reservation Type Mask is NOT "
366			"valid\n");
367	}
368
369
370}
371
372static void
373persist_print_full(struct scsi_per_res_in_header *hdr, uint32_t valid_len)
374{
375	uint32_t length, len_to_go = 0;
376	struct scsi_per_res_in_full_desc *desc;
377	uint8_t *cur_pos;
378	int i;
379
380	length = scsi_4btoul(hdr->length);
381	length = MIN(length, valid_len);
382
383	if (length < sizeof(*desc)) {
384		if (length == 0)
385			fprintf(stdout, "No reservations.\n");
386		else
387			warnx("unable to print reservation, only got %u "
388			      "valid bytes", length);
389		return;
390	}
391
392	fprintf(stdout, "PRgeneration: %#x\n", scsi_4btoul(hdr->generation));
393	cur_pos = (uint8_t *)&hdr[1];
394	for (len_to_go = length, i = 0,
395	     desc = (struct scsi_per_res_in_full_desc *)cur_pos;
396	     len_to_go >= sizeof(*desc);
397	     desc = (struct scsi_per_res_in_full_desc *)cur_pos, i++) {
398		uint32_t additional_length, cur_length;
399
400
401		fprintf(stdout, "Reservation Key: %#jx\n",
402			(uintmax_t)scsi_8btou64(desc->res_key.key));
403		fprintf(stdout, "All Target Ports (ALL_TG_PT): %d\n",
404			(desc->flags & SPRI_FULL_ALL_TG_PT) ? 1 : 0);
405		fprintf(stdout, "Reservation Holder (R_HOLDER): %d\n",
406			(desc->flags & SPRI_FULL_R_HOLDER) ? 1 : 0);
407
408		if (desc->flags & SPRI_FULL_R_HOLDER)
409			persist_print_scopetype(desc->scopetype);
410
411		if ((desc->flags & SPRI_FULL_ALL_TG_PT) == 0)
412			fprintf(stdout, "Relative Target Port ID: %#x\n",
413				scsi_2btoul(desc->rel_trgt_port_id));
414
415		additional_length = scsi_4btoul(desc->additional_length);
416
417		persist_print_transportid(desc->transport_id,
418					  additional_length);
419
420		cur_length = sizeof(*desc) + additional_length;
421		len_to_go -= cur_length;
422		cur_pos += cur_length;
423	}
424}
425
426int
427scsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt,
428	    int task_attr, int retry_count, int timeout, int verbosemode,
429	    int err_recover)
430{
431	union ccb *ccb = NULL;
432	int c, in = 0, out = 0;
433	int action = -1, num_ids = 0;
434	int error = 0;
435	uint32_t res_len = 0;
436	unsigned long rel_tgt_port = 0;
437	uint8_t *res_buf = NULL;
438	int scope = SPR_LU_SCOPE, res_type = 0;
439	struct persist_transport_id *id, *id2;
440	STAILQ_HEAD(, persist_transport_id) transport_id_list;
441	uint64_t key = 0, sa_key = 0;
442	struct scsi_nv *table = NULL;
443	size_t table_size = 0, id_len = 0;
444	uint32_t valid_len = 0;
445	int all_tg_pt = 0, aptpl = 0, spec_i_pt = 0, unreg = 0,rel_port_set = 0;
446
447	STAILQ_INIT(&transport_id_list);
448
449	ccb = cam_getccb(device);
450	if (ccb == NULL) {
451		warnx("%s: error allocating CCB", __func__);
452		error = 1;
453		goto bailout;
454	}
455
456	CCB_CLEAR_ALL_EXCEPT_HDR(&ccb->csio);
457
458	while ((c = getopt(argc, argv, combinedopt)) != -1) {
459		switch (c) {
460		case 'a':
461			all_tg_pt = 1;
462			break;
463		case 'I': {
464			int error_str_len = 128;
465			char error_str[error_str_len];
466			char *id_str;
467
468			id = malloc(sizeof(*id));
469			if (id == NULL) {
470				warnx("%s: error allocating %zu bytes",
471				    __func__, sizeof(*id));
472				error = 1;
473				goto bailout;
474			}
475			bzero(id, sizeof(*id));
476
477			id_str = strdup(optarg);
478			if (id_str == NULL) {
479				warnx("%s: error duplicating string %s",
480				    __func__, optarg);
481				free(id);
482				error = 1;
483				goto bailout;
484			}
485			error = scsi_parse_transportid(id_str, &id->hdr,
486			    &id->alloc_len, error_str, error_str_len);
487			if (error != 0) {
488				warnx("%s", error_str);
489				error = 1;
490				free(id);
491				free(id_str);
492				goto bailout;
493			}
494			free(id_str);
495
496			STAILQ_INSERT_TAIL(&transport_id_list, id, links);
497			num_ids++;
498			id_len += id->alloc_len;
499			break;
500		}
501		case 'k':
502		case 'K': {
503			char *endptr;
504			uint64_t tmpval;
505
506			tmpval = strtoumax(optarg, &endptr, 0);
507			if (*endptr != '\0') {
508				warnx("%s: invalid key argument %s", __func__,
509				    optarg);
510				error = 1;
511				goto bailout;
512			}
513			if (c == 'k') {
514				key = tmpval;
515			} else {
516				sa_key = tmpval;
517			}
518			break;
519		}
520		case 'i':
521		case 'o': {
522			scsi_nv_status status;
523			int table_entry = 0;
524
525			if (c == 'i') {
526				in = 1;
527				table = persist_in_actions;
528				table_size = sizeof(persist_in_actions) /
529					sizeof(persist_in_actions[0]);
530			} else {
531				out = 1;
532				table = persist_out_actions;
533				table_size = sizeof(persist_out_actions) /
534					sizeof(persist_out_actions[0]);
535			}
536
537			if ((in + out) > 1) {
538				warnx("%s: only one in (-i) or out (-o) "
539				    "action is allowed", __func__);
540				error = 1;
541				goto bailout;
542			}
543
544			status = scsi_get_nv(table, table_size, optarg,
545					     &table_entry,SCSI_NV_FLAG_IG_CASE);
546			if (status == SCSI_NV_FOUND)
547				action = table[table_entry].value;
548			else {
549				warnx("%s: %s %s option %s", __func__,
550				    (status == SCSI_NV_AMBIGUOUS) ?
551				    "ambiguous" : "invalid", in ? "in" :
552				    "out", optarg);
553				error = 1;
554				goto bailout;
555			}
556			break;
557		}
558		case 'p':
559			aptpl = 1;
560			break;
561		case 'R': {
562			char *endptr;
563
564			rel_tgt_port = strtoul(optarg, &endptr, 0);
565			if (*endptr != '\0') {
566				warnx("%s: invalid relative target port %s",
567				    __func__, optarg);
568				error = 1;
569				goto bailout;
570			}
571			rel_port_set = 1;
572			break;
573		}
574		case 's': {
575			size_t scope_size;
576			struct scsi_nv *scope_table = NULL;
577			scsi_nv_status status;
578			int table_entry = 0;
579			char *endptr;
580
581			/*
582			 * First check to see if the user gave us a numeric
583			 * argument.  If so, we'll try using it.
584			 */
585			if (isdigit(optarg[0])) {
586				scope = strtol(optarg, &endptr, 0);
587				if (*endptr != '\0') {
588					warnx("%s: invalid scope %s",
589					       __func__, optarg);
590					error = 1;
591					goto bailout;
592				}
593				scope = (scope << SPR_SCOPE_SHIFT) &
594				    SPR_SCOPE_MASK;
595				break;
596			}
597
598			scope_size = sizeof(persist_scope_table) /
599				     sizeof(persist_scope_table[0]);
600			scope_table = persist_scope_table;
601			status = scsi_get_nv(scope_table, scope_size, optarg,
602					     &table_entry,SCSI_NV_FLAG_IG_CASE);
603			if (status == SCSI_NV_FOUND)
604				scope = scope_table[table_entry].value;
605			else {
606				warnx("%s: %s scope %s", __func__,
607				      (status == SCSI_NV_AMBIGUOUS) ?
608				      "ambiguous" : "invalid", optarg);
609				error = 1;
610				goto bailout;
611			}
612			break;
613		}
614		case 'S':
615			spec_i_pt = 1;
616			break;
617		case 'T': {
618			size_t res_type_size;
619			struct scsi_nv *rtype_table = NULL;
620			scsi_nv_status status;
621			char *endptr;
622			int table_entry = 0;
623
624			/*
625			 * First check to see if the user gave us a numeric
626			 * argument.  If so, we'll try using it.
627			 */
628			if (isdigit(optarg[0])) {
629				res_type = strtol(optarg, &endptr, 0);
630				if (*endptr != '\0') {
631					warnx("%s: invalid reservation type %s",
632					       __func__, optarg);
633					error = 1;
634					goto bailout;
635				}
636				break;
637			}
638
639			res_type_size = sizeof(persist_type_table) /
640					sizeof(persist_type_table[0]);
641			rtype_table = persist_type_table;
642			status = scsi_get_nv(rtype_table, res_type_size,
643					     optarg, &table_entry,
644					     SCSI_NV_FLAG_IG_CASE);
645			if (status == SCSI_NV_FOUND)
646				res_type = rtype_table[table_entry].value;
647			else {
648				warnx("%s: %s reservation type %s", __func__,
649				      (status == SCSI_NV_AMBIGUOUS) ?
650				      "ambiguous" : "invalid", optarg);
651				error = 1;
652				goto bailout;
653			}
654			break;
655		}
656		case 'U':
657			unreg = 1;
658			break;
659		default:
660			break;
661		}
662	}
663
664	if ((in + out) != 1) {
665		warnx("%s: you must specify one of -i or -o", __func__);
666		error = 1;
667		goto bailout;
668	}
669
670	/*
671	 * Note that we don't really try to figure out whether the user
672	 * needs to specify one or both keys.  There are a number of
673	 * scenarios, and sometimes 0 is a valid and desired value.
674	 */
675	if (in != 0) {
676		switch (action) {
677		case SPRI_RK:
678		case SPRI_RR:
679		case SPRI_RS:
680			/*
681			 * Allocate the maximum length possible for these
682			 * service actions.  According to the spec, the
683			 * target is supposed to return the available
684			 * length in the header, regardless of the
685			 * allocation length.  In practice, though, with
686			 * the READ FULL STATUS (SPRI_RS) service action,
687			 * some Seagate drives (in particular a
688			 * Constellation ES, <SEAGATE ST32000444SS 0006>)
689			 * don't return the available length if you only
690			 * allocate the length of the header.  So just
691			 * allocate the maximum here so we don't miss
692			 * anything.
693			 */
694			res_len = SPRI_MAX_LEN;
695			break;
696		case SPRI_RC:
697			res_len = sizeof(struct scsi_per_res_cap);
698			break;
699		default:
700			/* In theory we should catch this above */
701			warnx("%s: invalid action %d", __func__, action);
702			error = 1;
703			goto bailout;
704			break;
705		}
706	} else {
707
708		/*
709		 * XXX KDM need to add length for transport IDs for the
710		 * register and move service action and the register
711		 * service action with the SPEC_I_PT bit set.
712		 */
713		if (action == SPRO_REG_MOVE) {
714			if (num_ids != 1) {
715			    	warnx("%s: register and move requires a "
716				    "single transport ID (-I)", __func__);
717				error = 1;
718				goto bailout;
719			}
720			if (rel_port_set == 0) {
721				warnx("%s: register and move requires a "
722				    "relative target port (-R)", __func__);
723				error = 1;
724				goto bailout;
725			}
726			res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
727		} else {
728			res_len = sizeof(struct scsi_per_res_out_parms);
729			if ((action == SPRO_REGISTER)
730			 && (num_ids != 0)) {
731				/*
732				 * If the user specifies any IDs with the
733				 * register service action, turn on the
734				 * spec_i_pt bit.
735				 */
736				spec_i_pt = 1;
737				res_len += id_len;
738				res_len +=
739				    sizeof(struct scsi_per_res_out_trans_ids);
740			}
741		}
742	}
743retry:
744	if (res_buf != NULL) {
745		free(res_buf);
746		res_buf = NULL;
747	}
748	res_buf = malloc(res_len);
749	if (res_buf == NULL) {
750		warn("%s: error allocating %d bytes", __func__, res_len);
751		error = 1;
752		goto bailout;
753	}
754	bzero(res_buf, res_len);
755
756	if (in != 0) {
757		scsi_persistent_reserve_in(&ccb->csio,
758					   /*retries*/ retry_count,
759					   /*cbfcnp*/ NULL,
760					   /*tag_action*/ task_attr,
761					   /*service_action*/ action,
762					   /*data_ptr*/ res_buf,
763					   /*dxfer_len*/ res_len,
764					   /*sense_len*/ SSD_FULL_SIZE,
765					   /*timeout*/ timeout ? timeout :5000);
766
767	} else {
768		switch (action) {
769		case SPRO_REGISTER:
770			if (spec_i_pt != 0) {
771				struct scsi_per_res_out_trans_ids *id_hdr;
772				uint8_t *bufptr;
773
774				bufptr = res_buf +
775				    sizeof(struct scsi_per_res_out_parms) +
776				    sizeof(struct scsi_per_res_out_trans_ids);
777				STAILQ_FOREACH(id, &transport_id_list, links) {
778					bcopy(id->hdr, bufptr, id->alloc_len);
779					bufptr += id->alloc_len;
780				}
781				id_hdr = (struct scsi_per_res_out_trans_ids *)
782				    (res_buf +
783				    sizeof(struct scsi_per_res_out_parms));
784				scsi_ulto4b(id_len, id_hdr->additional_length);
785			}
786		case SPRO_REG_IGNO:
787		case SPRO_PREEMPT:
788		case SPRO_PRE_ABO:
789		case SPRO_RESERVE:
790		case SPRO_RELEASE:
791		case SPRO_CLEAR:
792		case SPRO_REPL_LOST_RES: {
793			struct scsi_per_res_out_parms *parms;
794
795			parms = (struct scsi_per_res_out_parms *)res_buf;
796
797			scsi_u64to8b(key, parms->res_key.key);
798			scsi_u64to8b(sa_key, parms->serv_act_res_key);
799			if (spec_i_pt != 0)
800				parms->flags |= SPR_SPEC_I_PT;
801			if (all_tg_pt != 0)
802				parms->flags |= SPR_ALL_TG_PT;
803			if (aptpl != 0)
804				parms->flags |= SPR_APTPL;
805			break;
806		}
807		case SPRO_REG_MOVE: {
808			struct scsi_per_res_reg_move *reg_move;
809			uint8_t *bufptr;
810
811			reg_move = (struct scsi_per_res_reg_move *)res_buf;
812
813			scsi_u64to8b(key, reg_move->res_key.key);
814			scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
815			if (unreg != 0)
816				reg_move->flags |= SPR_REG_MOVE_UNREG;
817			if (aptpl != 0)
818				reg_move->flags |= SPR_REG_MOVE_APTPL;
819			scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
820			id = STAILQ_FIRST(&transport_id_list);
821			/*
822			 * This shouldn't happen, since we already checked
823			 * the number of IDs above.
824			 */
825			if (id == NULL) {
826				warnx("%s: No transport IDs found!", __func__);
827				error = 1;
828				goto bailout;
829			}
830			bufptr = (uint8_t *)&reg_move[1];
831			bcopy(id->hdr, bufptr, id->alloc_len);
832			scsi_ulto4b(id->alloc_len,
833			    reg_move->transport_id_length);
834			break;
835		}
836		default:
837			break;
838		}
839		scsi_persistent_reserve_out(&ccb->csio,
840					    /*retries*/ retry_count,
841					    /*cbfcnp*/ NULL,
842					    /*tag_action*/ task_attr,
843					    /*service_action*/ action,
844					    /*scope*/ scope,
845					    /*res_type*/ res_type,
846					    /*data_ptr*/ res_buf,
847					    /*dxfer_len*/ res_len,
848					    /*sense_len*/ SSD_FULL_SIZE,
849					    /*timeout*/ timeout ?timeout :5000);
850	}
851
852	/* Disable freezing the device queue */
853	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
854
855	if (err_recover != 0)
856		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
857
858	if (cam_send_ccb(device, ccb) < 0) {
859		warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
860		    "IN" : "OUT");
861		error = 1;
862		goto bailout;
863	}
864
865	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
866		if (verbosemode != 0) {
867			cam_error_print(device, ccb, CAM_ESF_ALL,
868					CAM_EPF_ALL, stderr);
869		}
870		error = 1;
871		goto bailout;
872	}
873
874	if (in == 0)
875		goto bailout;
876
877	valid_len = res_len - ccb->csio.resid;
878
879	switch (action) {
880	case SPRI_RK:
881	case SPRI_RR:
882	case SPRI_RS: {
883		struct scsi_per_res_in_header *hdr;
884		uint32_t hdr_len;
885
886		if (valid_len < sizeof(*hdr)) {
887			warnx("%s: only got %d valid bytes, need %zd",
888			      __func__, valid_len, sizeof(*hdr));
889			error = 1;
890			goto bailout;
891		}
892		hdr = (struct scsi_per_res_in_header *)res_buf;
893		hdr_len = scsi_4btoul(hdr->length);
894
895		if (hdr_len > (res_len - sizeof(*hdr))) {
896			res_len = hdr_len + sizeof(*hdr);
897			goto retry;
898		}
899
900		if (action == SPRI_RK) {
901			persist_print_keys(hdr, valid_len);
902		} else if (action == SPRI_RR) {
903			persist_print_res(hdr, valid_len);
904		} else {
905			persist_print_full(hdr, valid_len);
906		}
907		break;
908	}
909	case SPRI_RC: {
910		struct scsi_per_res_cap *cap;
911		uint32_t cap_len;
912
913		if (valid_len < sizeof(*cap)) {
914			warnx("%s: only got %u valid bytes, need %zd",
915			      __func__, valid_len, sizeof(*cap));
916			error = 1;
917			goto bailout;
918		}
919		cap = (struct scsi_per_res_cap *)res_buf;
920		cap_len = scsi_2btoul(cap->length);
921		if (cap_len != sizeof(*cap)) {
922			/*
923			 * We should be able to deal with this,
924			 * it's just more trouble.
925			 */
926			warnx("%s: reported size %u is different "
927			    "than expected size %zd", __func__,
928			    cap_len, sizeof(*cap));
929		}
930
931		/*
932		 * If there is more data available, grab it all,
933		 * even though we don't really know what to do with
934		 * the extra data since it obviously wasn't in the
935		 * spec when this code was written.
936		 */
937		if (cap_len > res_len) {
938			res_len = cap_len;
939			goto retry;
940		}
941		persist_print_cap(cap, valid_len);
942		break;
943	}
944	default:
945		break;
946	}
947
948bailout:
949	free(res_buf);
950
951	if (ccb != NULL)
952		cam_freeccb(ccb);
953
954	STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
955		STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
956		    links);
957		free(id);
958	}
959	return (error);
960}
961