]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sbin/camcontrol/persist.c
MFC r311897: Add checks for received mode page length.
[FreeBSD/stable/10.git] / sbin / camcontrol / persist.c
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$");
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
66 struct 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  */
75 static 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  */
85 static 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  */
101 static 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  */
113 static 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  */
134 static void
135 persist_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
155 static void
156 persist_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  */
177 static void
178 persist_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  */
213 static void
214 persist_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  */
239 static void
240 persist_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
370 static void
371 persist_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
424 int
425 scsipersist(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, key_set = 0, sa_key_set = 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                                 key_set = 1;
513                         } else {
514                                 sa_key = tmpval;
515                                 sa_key_set = 1;
516                         }
517                         break;
518                 }
519                 case 'i':
520                 case 'o': {
521                         scsi_nv_status status;
522                         int table_entry = 0;
523
524                         if (c == 'i') {
525                                 in = 1;
526                                 table = persist_in_actions;
527                                 table_size = sizeof(persist_in_actions) /
528                                         sizeof(persist_in_actions[0]);
529                         } else {
530                                 out = 1;
531                                 table = persist_out_actions;
532                                 table_size = sizeof(persist_out_actions) /
533                                         sizeof(persist_out_actions[0]);
534                         }
535
536                         if ((in + out) > 1) {
537                                 warnx("%s: only one in (-i) or out (-o) "
538                                     "action is allowed", __func__);
539                                 error = 1;
540                                 goto bailout;
541                         }
542
543                         status = scsi_get_nv(table, table_size, optarg,
544                                              &table_entry,SCSI_NV_FLAG_IG_CASE);
545                         if (status == SCSI_NV_FOUND)
546                                 action = table[table_entry].value;
547                         else {
548                                 warnx("%s: %s %s option %s", __func__,
549                                     (status == SCSI_NV_AMBIGUOUS) ?
550                                     "ambiguous" : "invalid", in ? "in" :
551                                     "out", optarg);
552                                 error = 1;
553                                 goto bailout;
554                         }
555                         break;
556                 }
557                 case 'p':
558                         aptpl = 1;
559                         break;
560                 case 'R': {
561                         char *endptr;
562
563                         rel_tgt_port = strtoul(optarg, &endptr, 0);
564                         if (*endptr != '\0') {
565                                 warnx("%s: invalid relative target port %s",
566                                     __func__, optarg);
567                                 error = 1;
568                                 goto bailout;
569                         }
570                         rel_port_set = 1;
571                         break;
572                 }
573                 case 's': {
574                         size_t scope_size;
575                         struct scsi_nv *scope_table = NULL;
576                         scsi_nv_status status;
577                         int table_entry = 0;
578                         char *endptr;
579
580                         /*
581                          * First check to see if the user gave us a numeric
582                          * argument.  If so, we'll try using it.
583                          */
584                         if (isdigit(optarg[0])) {
585                                 scope = strtol(optarg, &endptr, 0);
586                                 if (*endptr != '\0') {
587                                         warnx("%s: invalid scope %s",
588                                                __func__, optarg);
589                                         error = 1;
590                                         goto bailout;
591                                 }
592                                 scope = (scope << SPR_SCOPE_SHIFT) &
593                                     SPR_SCOPE_MASK;
594                                 break;
595                         }
596
597                         scope_size = sizeof(persist_scope_table) /
598                                      sizeof(persist_scope_table[0]);
599                         scope_table = persist_scope_table;
600                         status = scsi_get_nv(scope_table, scope_size, optarg,
601                                              &table_entry,SCSI_NV_FLAG_IG_CASE);
602                         if (status == SCSI_NV_FOUND)
603                                 scope = scope_table[table_entry].value;
604                         else {
605                                 warnx("%s: %s scope %s", __func__,
606                                       (status == SCSI_NV_AMBIGUOUS) ?
607                                       "ambiguous" : "invalid", optarg);
608                                 error = 1;
609                                 goto bailout;
610                         }
611                         break;
612                 }
613                 case 'S':
614                         spec_i_pt = 1;
615                         break;
616                 case 'T': {
617                         size_t res_type_size;
618                         struct scsi_nv *rtype_table = NULL;
619                         scsi_nv_status status;
620                         char *endptr;
621                         int table_entry = 0;
622
623                         /*
624                          * First check to see if the user gave us a numeric
625                          * argument.  If so, we'll try using it.
626                          */
627                         if (isdigit(optarg[0])) {
628                                 res_type = strtol(optarg, &endptr, 0);
629                                 if (*endptr != '\0') {
630                                         warnx("%s: invalid reservation type %s",
631                                                __func__, optarg);
632                                         error = 1;
633                                         goto bailout;
634                                 }
635                                 break;
636                         }
637
638                         res_type_size = sizeof(persist_type_table) /
639                                         sizeof(persist_type_table[0]);
640                         rtype_table = persist_type_table;
641                         status = scsi_get_nv(rtype_table, res_type_size,
642                                              optarg, &table_entry,
643                                              SCSI_NV_FLAG_IG_CASE);
644                         if (status == SCSI_NV_FOUND)
645                                 res_type = rtype_table[table_entry].value;
646                         else {
647                                 warnx("%s: %s reservation type %s", __func__,
648                                       (status == SCSI_NV_AMBIGUOUS) ?
649                                       "ambiguous" : "invalid", optarg);
650                                 error = 1;
651                                 goto bailout;
652                         }
653                         break;
654                 }
655                 case 'U':
656                         unreg = 1;
657                         break;
658                 default:
659                         break;
660                 }
661         }
662
663         if ((in + out) != 1) {
664                 warnx("%s: you must specify one of -i or -o", __func__);
665                 error = 1;
666                 goto bailout;
667         }
668
669         /*
670          * Note that we don't really try to figure out whether the user
671          * needs to specify one or both keys.  There are a number of
672          * scenarios, and sometimes 0 is a valid and desired value.
673          */
674         if (in != 0) {
675                 switch (action) {
676                 case SPRI_RK:
677                 case SPRI_RR:
678                 case SPRI_RS:
679                         /*
680                          * Allocate the maximum length possible for these
681                          * service actions.  According to the spec, the
682                          * target is supposed to return the available
683                          * length in the header, regardless of the
684                          * allocation length.  In practice, though, with
685                          * the READ FULL STATUS (SPRI_RS) service action,
686                          * some Seagate drives (in particular a
687                          * Constellation ES, <SEAGATE ST32000444SS 0006>)
688                          * don't return the available length if you only
689                          * allocate the length of the header.  So just
690                          * allocate the maximum here so we don't miss
691                          * anything.
692                          */
693                         res_len = SPRI_MAX_LEN;
694                         break;
695                 case SPRI_RC:
696                         res_len = sizeof(struct scsi_per_res_cap);
697                         break;
698                 default:
699                         /* In theory we should catch this above */
700                         warnx("%s: invalid action %d", __func__, action);
701                         error = 1;
702                         goto bailout;
703                         break;
704                 }
705         } else {
706
707                 /*
708                  * XXX KDM need to add length for transport IDs for the
709                  * register and move service action and the register
710                  * service action with the SPEC_I_PT bit set.
711                  */
712                 if (action == SPRO_REG_MOVE) {
713                         if (num_ids != 1) {
714                                 warnx("%s: register and move requires a "
715                                     "single transport ID (-I)", __func__);
716                                 error = 1;
717                                 goto bailout;
718                         }
719                         if (rel_port_set == 0) {
720                                 warnx("%s: register and move requires a "
721                                     "relative target port (-R)", __func__);
722                                 error = 1;
723                                 goto bailout;
724                         }
725                         res_len = sizeof(struct scsi_per_res_reg_move) + id_len;
726                 } else {
727                         res_len = sizeof(struct scsi_per_res_out_parms);
728                         if ((action == SPRO_REGISTER)
729                          && (num_ids != 0)) {
730                                 /*
731                                  * If the user specifies any IDs with the
732                                  * register service action, turn on the
733                                  * spec_i_pt bit.
734                                  */
735                                 spec_i_pt = 1;
736                                 res_len += id_len;
737                                 res_len +=
738                                     sizeof(struct scsi_per_res_out_trans_ids);
739                         }
740                 }
741         }
742 retry:
743         if (res_buf != NULL) {
744                 free(res_buf);
745                 res_buf = NULL;
746         }
747         res_buf = malloc(res_len);
748         if (res_buf == NULL) {
749                 warn("%s: error allocating %d bytes", __func__, res_len);
750                 error = 1;
751                 goto bailout;
752         }
753         bzero(res_buf, res_len);
754
755         if (in != 0) {
756                 scsi_persistent_reserve_in(&ccb->csio,
757                                            /*retries*/ retry_count,
758                                            /*cbfcnp*/ NULL,
759                                            /*tag_action*/ MSG_SIMPLE_Q_TAG,
760                                            /*service_action*/ action,
761                                            /*data_ptr*/ res_buf,
762                                            /*dxfer_len*/ res_len,
763                                            /*sense_len*/ SSD_FULL_SIZE,
764                                            /*timeout*/ timeout ? timeout :5000);
765
766         } else {
767                 switch (action) {
768                 case SPRO_REGISTER:
769                         if (spec_i_pt != 0) {
770                                 struct scsi_per_res_out_trans_ids *id_hdr;
771                                 uint8_t *bufptr;
772
773                                 bufptr = res_buf +
774                                     sizeof(struct scsi_per_res_out_parms) +
775                                     sizeof(struct scsi_per_res_out_trans_ids);
776                                 STAILQ_FOREACH(id, &transport_id_list, links) {
777                                         bcopy(id->hdr, bufptr, id->alloc_len);
778                                         bufptr += id->alloc_len;
779                                 }
780                                 id_hdr = (struct scsi_per_res_out_trans_ids *)
781                                     (res_buf +
782                                     sizeof(struct scsi_per_res_out_parms));
783                                 scsi_ulto4b(id_len, id_hdr->additional_length);
784                         }
785                 case SPRO_REG_IGNO:
786                 case SPRO_PREEMPT:
787                 case SPRO_PRE_ABO:
788                 case SPRO_RESERVE:
789                 case SPRO_RELEASE:
790                 case SPRO_CLEAR:
791                 case SPRO_REPL_LOST_RES: {
792                         struct scsi_per_res_out_parms *parms;
793
794                         parms = (struct scsi_per_res_out_parms *)res_buf;
795
796                         scsi_u64to8b(key, parms->res_key.key);
797                         scsi_u64to8b(sa_key, parms->serv_act_res_key);
798                         if (spec_i_pt != 0)
799                                 parms->flags |= SPR_SPEC_I_PT;
800                         if (all_tg_pt != 0)
801                                 parms->flags |= SPR_ALL_TG_PT;
802                         if (aptpl != 0)
803                                 parms->flags |= SPR_APTPL;
804                         break;
805                 }
806                 case SPRO_REG_MOVE: {
807                         struct scsi_per_res_reg_move *reg_move;
808                         uint8_t *bufptr;
809
810                         reg_move = (struct scsi_per_res_reg_move *)res_buf;
811
812                         scsi_u64to8b(key, reg_move->res_key.key);
813                         scsi_u64to8b(sa_key, reg_move->serv_act_res_key);
814                         if (unreg != 0)
815                                 reg_move->flags |= SPR_REG_MOVE_UNREG;
816                         if (aptpl != 0)
817                                 reg_move->flags |= SPR_REG_MOVE_APTPL;
818                         scsi_ulto2b(rel_tgt_port, reg_move->rel_trgt_port_id);
819                         id = STAILQ_FIRST(&transport_id_list);
820                         /*
821                          * This shouldn't happen, since we already checked
822                          * the number of IDs above.
823                          */
824                         if (id == NULL) {
825                                 warnx("%s: No transport IDs found!", __func__);
826                                 error = 1;
827                                 goto bailout;
828                         }
829                         bufptr = (uint8_t *)&reg_move[1];
830                         bcopy(id->hdr, bufptr, id->alloc_len);
831                         scsi_ulto4b(id->alloc_len,
832                             reg_move->transport_id_length);
833                         break;
834                 }
835                 default:
836                         break;
837                 }
838                 scsi_persistent_reserve_out(&ccb->csio,
839                                             /*retries*/ retry_count,
840                                             /*cbfcnp*/ NULL,
841                                             /*tag_action*/ MSG_SIMPLE_Q_TAG,
842                                             /*service_action*/ action,
843                                             /*scope*/ scope,
844                                             /*res_type*/ res_type,
845                                             /*data_ptr*/ res_buf,
846                                             /*dxfer_len*/ res_len,
847                                             /*sense_len*/ SSD_FULL_SIZE,
848                                             /*timeout*/ timeout ?timeout :5000);
849         }
850
851         /* Disable freezing the device queue */
852         ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
853
854         if (err_recover != 0)
855                 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
856
857         if (cam_send_ccb(device, ccb) < 0) {
858                 warn("error sending PERSISTENT RESERVE %s", (in != 0) ?
859                     "IN" : "OUT");
860
861                 if (verbosemode != 0) {
862                         cam_error_print(device, ccb, CAM_ESF_ALL,
863                                         CAM_EPF_ALL, stderr);
864                 }
865
866                 error = 1;
867                 goto bailout;
868         }
869
870         if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
871                 if (verbosemode != 0) {
872                         cam_error_print(device, ccb, CAM_ESF_ALL,
873                                         CAM_EPF_ALL, stderr);
874                 }
875                 error = 1;
876                 goto bailout;
877         }
878
879         if (in == 0)
880                 goto bailout;
881
882         valid_len = res_len - ccb->csio.resid;
883
884         switch (action) {
885         case SPRI_RK:
886         case SPRI_RR:
887         case SPRI_RS: {
888                 struct scsi_per_res_in_header *hdr;
889                 uint32_t hdr_len;
890
891                 if (valid_len < sizeof(*hdr)) {
892                         warnx("%s: only got %d valid bytes, need %zd",
893                               __func__, valid_len, sizeof(*hdr));
894                         error = 1;
895                         goto bailout;
896                 }
897                 hdr = (struct scsi_per_res_in_header *)res_buf;
898                 hdr_len = scsi_4btoul(hdr->length);
899
900                 if (hdr_len > (res_len - sizeof(*hdr))) {
901                         res_len = hdr_len + sizeof(*hdr);
902                         goto retry;
903                 }
904
905                 if (action == SPRI_RK) {
906                         persist_print_keys(hdr, valid_len);
907                 } else if (action == SPRI_RR) {
908                         persist_print_res(hdr, valid_len);
909                 } else {
910                         persist_print_full(hdr, valid_len);
911                 }
912                 break;
913         }
914         case SPRI_RC: {
915                 struct scsi_per_res_cap *cap;
916                 uint32_t cap_len;
917
918                 if (valid_len < sizeof(*cap)) {
919                         warnx("%s: only got %u valid bytes, need %zd",
920                               __func__, valid_len, sizeof(*cap));
921                         error = 1;
922                         goto bailout;
923                 }
924                 cap = (struct scsi_per_res_cap *)res_buf;
925                 cap_len = scsi_2btoul(cap->length);
926                 if (cap_len != sizeof(*cap)) {
927                         /*
928                          * We should be able to deal with this,
929                          * it's just more trouble.
930                          */
931                         warnx("%s: reported size %u is different "
932                             "than expected size %zd", __func__,
933                             cap_len, sizeof(*cap));
934                 }
935
936                 /*
937                  * If there is more data available, grab it all,
938                  * even though we don't really know what to do with
939                  * the extra data since it obviously wasn't in the
940                  * spec when this code was written.
941                  */
942                 if (cap_len > res_len) {
943                         res_len = cap_len;
944                         goto retry;
945                 }
946                 persist_print_cap(cap, valid_len);
947                 break;
948         }
949         default:
950                 break;
951         }
952
953 bailout:
954         free(res_buf);
955
956         if (ccb != NULL)
957                 cam_freeccb(ccb);
958
959         STAILQ_FOREACH_SAFE(id, &transport_id_list, links, id2) {
960                 STAILQ_REMOVE(&transport_id_list, id, persist_transport_id,
961                     links);
962                 free(id);
963         }
964         return (error);
965 }