]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - usr.sbin/ctladm/ctladm.c
MFC 227470:
[FreeBSD/stable/9.git] / usr.sbin / ctladm / ctladm.c
1 /*-
2  * Copyright (c) 2003, 2004 Silicon Graphics International Corp.
3  * Copyright (c) 1997-2007 Kenneth D. Merry
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer,
11  *    without modification.
12  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13  *    substantially similar to the "NO WARRANTY" disclaimer below
14  *    ("Disclaimer") and any redistribution must be conditioned upon
15  *    including a substantially similar Disclaimer requirement for further
16  *    binary redistribution.
17  *
18  * NO WARRANTY
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
28  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGES.
30  *
31  * $Id: //depot/users/kenm/FreeBSD-test2/usr.sbin/ctladm/ctladm.c#4 $
32  */
33 /*
34  * CAM Target Layer exercise program.
35  *
36  * Author: Ken Merry <ken@FreeBSD.org>
37  */
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD$");
41
42 #include <sys/ioctl.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <sys/param.h>
46 #include <sys/queue.h>
47 #include <sys/callout.h>
48 #include <sys/sbuf.h>
49 #include <stdint.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <fcntl.h>
54 #include <getopt.h>
55 #include <string.h>
56 #include <errno.h>
57 #include <err.h>
58 #include <ctype.h>
59 #include <bsdxml.h>
60 #include <cam/scsi/scsi_all.h>
61 #include <cam/scsi/scsi_message.h>
62 #include <cam/ctl/ctl.h>
63 #include <cam/ctl/ctl_io.h>
64 #include <cam/ctl/ctl_frontend_internal.h>
65 #include <cam/ctl/ctl_backend.h>
66 #include <cam/ctl/ctl_ioctl.h>
67 #include <cam/ctl/ctl_backend_block.h>
68 #include <cam/ctl/ctl_util.h>
69 #include <cam/ctl/ctl_scsi_all.h>
70 #include <camlib.h>
71 #include "ctladm.h"
72
73 #ifdef min
74 #undef min
75 #endif
76 #define min(x,y) (x < y) ? x : y
77
78 typedef enum {
79         CTLADM_CMD_TUR,
80         CTLADM_CMD_INQUIRY,
81         CTLADM_CMD_REQ_SENSE,
82         CTLADM_CMD_ARRAYLIST,
83         CTLADM_CMD_REPORT_LUNS,
84         CTLADM_CMD_HELP,
85         CTLADM_CMD_DEVLIST,
86         CTLADM_CMD_ADDDEV,
87         CTLADM_CMD_RM,
88         CTLADM_CMD_CREATE,
89         CTLADM_CMD_READ,
90         CTLADM_CMD_WRITE,
91         CTLADM_CMD_PORT,
92         CTLADM_CMD_READCAPACITY,
93         CTLADM_CMD_MODESENSE,
94         CTLADM_CMD_DUMPOOA,
95         CTLADM_CMD_DUMPSTRUCTS,
96         CTLADM_CMD_START,
97         CTLADM_CMD_STOP,
98         CTLADM_CMD_SYNC_CACHE,
99         CTLADM_CMD_SHUTDOWN,
100         CTLADM_CMD_STARTUP,
101         CTLADM_CMD_LUNLIST,
102         CTLADM_CMD_HARDSTOP,
103         CTLADM_CMD_HARDSTART,
104         CTLADM_CMD_DELAY,
105         CTLADM_CMD_REALSYNC,
106         CTLADM_CMD_SETSYNC,
107         CTLADM_CMD_GETSYNC,
108         CTLADM_CMD_ERR_INJECT,
109         CTLADM_CMD_BBRREAD,
110         CTLADM_CMD_PRES_IN,
111         CTLADM_CMD_PRES_OUT,
112         CTLADM_CMD_INQ_VPD_DEVID,
113         CTLADM_CMD_RTPG
114 } ctladm_cmdfunction;
115
116 typedef enum {
117         CTLADM_ARG_NONE         = 0x0000000,
118         CTLADM_ARG_AUTOSENSE    = 0x0000001,
119         CTLADM_ARG_DEVICE       = 0x0000002,
120         CTLADM_ARG_ARRAYSIZE    = 0x0000004,
121         CTLADM_ARG_BACKEND      = 0x0000008,
122         CTLADM_ARG_CDBSIZE      = 0x0000010,
123         CTLADM_ARG_DATALEN      = 0x0000020,
124         CTLADM_ARG_FILENAME     = 0x0000040,
125         CTLADM_ARG_LBA          = 0x0000080,
126         CTLADM_ARG_PC           = 0x0000100,
127         CTLADM_ARG_PAGE_CODE    = 0x0000200,
128         CTLADM_ARG_PAGE_LIST    = 0x0000400,
129         CTLADM_ARG_SUBPAGE      = 0x0000800,
130         CTLADM_ARG_PAGELIST     = 0x0001000,
131         CTLADM_ARG_DBD          = 0x0002000,
132         CTLADM_ARG_TARG_LUN     = 0x0004000,
133         CTLADM_ARG_BLOCKSIZE    = 0x0008000,
134         CTLADM_ARG_IMMED        = 0x0010000,
135         CTLADM_ARG_RELADR       = 0x0020000,
136         CTLADM_ARG_RETRIES      = 0x0040000,
137         CTLADM_ARG_ONOFFLINE    = 0x0080000,
138         CTLADM_ARG_ONESHOT      = 0x0100000,
139         CTLADM_ARG_TIMEOUT      = 0x0200000,
140         CTLADM_ARG_INITIATOR    = 0x0400000,
141         CTLADM_ARG_NOCOPY       = 0x0800000,
142         CTLADM_ARG_NEED_TL      = 0x1000000
143 } ctladm_cmdargs;
144
145 struct ctladm_opts {
146         const char      *optname;
147         uint32_t        cmdnum;
148         ctladm_cmdargs  argnum;
149         const char      *subopt;
150 };
151
152 typedef enum {
153         CC_OR_NOT_FOUND,
154         CC_OR_AMBIGUOUS,
155         CC_OR_FOUND
156 } ctladm_optret;
157
158 static const char rw_opts[] = "Nb:c:d:f:l:";
159 static const char startstop_opts[] = "io";
160
161 struct ctladm_opts option_table[] = {
162         {"adddev", CTLADM_CMD_ADDDEV, CTLADM_ARG_NONE, NULL},
163         {"bbrread", CTLADM_CMD_BBRREAD, CTLADM_ARG_NEED_TL, "d:l:"},
164         {"create", CTLADM_CMD_CREATE, CTLADM_ARG_NONE, "b:B:d:l:o:s:S:t:"},
165         {"delay", CTLADM_CMD_DELAY, CTLADM_ARG_NEED_TL, "T:l:t:"},
166         {"devid", CTLADM_CMD_INQ_VPD_DEVID, CTLADM_ARG_NEED_TL, NULL},
167         {"devlist", CTLADM_CMD_DEVLIST, CTLADM_ARG_NONE, "b:vx"},
168         {"dumpooa", CTLADM_CMD_DUMPOOA, CTLADM_ARG_NONE, NULL},
169         {"dumpstructs", CTLADM_CMD_DUMPSTRUCTS, CTLADM_ARG_NONE, NULL},
170         {"getsync", CTLADM_CMD_GETSYNC, CTLADM_ARG_NEED_TL, NULL},
171         {"hardstart", CTLADM_CMD_HARDSTART, CTLADM_ARG_NONE, NULL},
172         {"hardstop", CTLADM_CMD_HARDSTOP, CTLADM_ARG_NONE, NULL},
173         {"help", CTLADM_CMD_HELP, CTLADM_ARG_NONE, NULL},
174         {"inject", CTLADM_CMD_ERR_INJECT, CTLADM_ARG_NEED_TL, "cd:i:p:r:s:"},
175         {"inquiry", CTLADM_CMD_INQUIRY, CTLADM_ARG_NEED_TL, NULL},
176         {"lunlist", CTLADM_CMD_LUNLIST, CTLADM_ARG_NONE, NULL},
177         {"modesense", CTLADM_CMD_MODESENSE, CTLADM_ARG_NEED_TL, "P:S:dlm:c:"},
178         {"port", CTLADM_CMD_PORT, CTLADM_ARG_NONE, "lo:p:qt:w:W:x"},
179         {"prin", CTLADM_CMD_PRES_IN, CTLADM_ARG_NEED_TL, "a:"},
180         {"prout", CTLADM_CMD_PRES_OUT, CTLADM_ARG_NEED_TL, "a:k:r:s:"},
181         {"read", CTLADM_CMD_READ, CTLADM_ARG_NEED_TL, rw_opts},
182         {"readcapacity", CTLADM_CMD_READCAPACITY, CTLADM_ARG_NEED_TL, "c:"},
183         {"realsync", CTLADM_CMD_REALSYNC, CTLADM_ARG_NONE, NULL},
184         {"remove", CTLADM_CMD_RM, CTLADM_ARG_NONE, "b:l:o:"},
185         {"reportluns", CTLADM_CMD_REPORT_LUNS, CTLADM_ARG_NEED_TL, NULL},
186         {"reqsense", CTLADM_CMD_REQ_SENSE, CTLADM_ARG_NEED_TL, NULL},
187         {"rtpg", CTLADM_CMD_RTPG, CTLADM_ARG_NEED_TL, NULL},
188         {"setsync", CTLADM_CMD_SETSYNC, CTLADM_ARG_NEED_TL, "i:"},
189         {"shutdown", CTLADM_CMD_SHUTDOWN, CTLADM_ARG_NONE, NULL},
190         {"start", CTLADM_CMD_START, CTLADM_ARG_NEED_TL, startstop_opts},
191         {"startup", CTLADM_CMD_STARTUP, CTLADM_ARG_NONE, NULL},
192         {"stop", CTLADM_CMD_STOP, CTLADM_ARG_NEED_TL, startstop_opts},
193         {"synccache", CTLADM_CMD_SYNC_CACHE, CTLADM_ARG_NEED_TL, "b:c:il:r"},
194         {"tur", CTLADM_CMD_TUR, CTLADM_ARG_NEED_TL, NULL},
195         {"write", CTLADM_CMD_WRITE, CTLADM_ARG_NEED_TL, rw_opts},
196         {"-?", CTLADM_CMD_HELP, CTLADM_ARG_NONE, NULL},
197         {"-h", CTLADM_CMD_HELP, CTLADM_ARG_NONE, NULL},
198         {NULL, 0, 0, NULL}
199 };
200
201
202 ctladm_optret getoption(struct ctladm_opts *table, char *arg, uint32_t *cmdnum,
203                         ctladm_cmdargs *argnum, const char **subopt);
204 static int cctl_parse_tl(char *str, int *target, int *lun);
205 static int cctl_dump_ooa(int fd, int argc, char **argv);
206 static int cctl_port_dump(int fd, int quiet, int xml, int32_t fe_num,
207                           ctl_port_type port_type);
208 static int cctl_port(int fd, int argc, char **argv, char *combinedopt);
209 static int cctl_do_io(int fd, int retries, union ctl_io *io, const char *func);
210 static int cctl_delay(int fd, int target, int lun, int argc, char **argv,
211                       char *combinedopt);
212 static int cctl_lunlist(int fd);
213 static void cctl_cfi_mt_statusstr(cfi_mt_status status, char *str, int str_len);
214 static void cctl_cfi_bbr_statusstr(cfi_bbrread_status, char *str, int str_len);
215 static int cctl_hardstopstart(int fd, ctladm_cmdfunction command);
216 static int cctl_bbrread(int fd, int target, int lun, int iid, int argc,
217                         char **argv, char *combinedopt);
218 static int cctl_startup_shutdown(int fd, int target, int lun, int iid,
219                                  ctladm_cmdfunction command);
220 static int cctl_sync_cache(int fd, int target, int lun, int iid, int retries,
221                            int argc, char **argv, char *combinedopt);
222 static int cctl_start_stop(int fd, int target, int lun, int iid, int retries,
223                            int start, int argc, char **argv, char *combinedopt);
224 static int cctl_mode_sense(int fd, int target, int lun, int iid, int retries, 
225                            int argc, char **argv, char *combinedopt);
226 static int cctl_read_capacity(int fd, int target, int lun, int iid,
227                               int retries, int argc, char **argv,
228                               char *combinedopt);
229 static int cctl_read_write(int fd, int target, int lun, int iid, int retries,
230                            int argc, char **argv, char *combinedopt,
231                            ctladm_cmdfunction command);
232 static int cctl_get_luns(int fd, int target, int lun, int iid, int retries,
233                          struct scsi_report_luns_data **lun_data,
234                          uint32_t *num_luns);
235 static int cctl_report_luns(int fd, int target, int lun, int iid, int retries);
236 static int cctl_tur(int fd, int target, int lun, int iid, int retries);
237 static int cctl_get_inquiry(int fd, int target, int lun, int iid, int retries,
238                             char *path_str, int path_len,
239                             struct scsi_inquiry_data *inq_data);
240 static int cctl_inquiry(int fd, int target, int lun, int iid, int retries);
241 static int cctl_req_sense(int fd, int target, int lun, int iid, int retries);
242 static int cctl_persistent_reserve_in(int fd, int target, int lun,
243                                       int initiator, int argc, char **argv,
244                                       char *combinedopt, int retry_count);
245 static int cctl_persistent_reserve_out(int fd, int target, int lun, 
246                                        int initiator, int argc, char **argv,
247                                        char *combinedopt, int retry_count);
248 static int cctl_create_lun(int fd, int argc, char **argv, char *combinedopt);
249 static int cctl_inquiry_vpd_devid(int fd, int target, int lun, int initiator);
250 static int cctl_report_target_port_group(int fd, int target, int lun,
251                                          int initiator);
252
253 ctladm_optret
254 getoption(struct ctladm_opts *table, char *arg, uint32_t *cmdnum,
255           ctladm_cmdargs *argnum, const char **subopt)
256 {
257         struct ctladm_opts *opts;
258         int num_matches = 0;
259
260         for (opts = table; (opts != NULL) && (opts->optname != NULL);
261              opts++) {
262                 if (strncmp(opts->optname, arg, strlen(arg)) == 0) {
263                         *cmdnum = opts->cmdnum;
264                         *argnum = opts->argnum;
265                         *subopt = opts->subopt;
266
267                         if (strcmp(opts->optname, arg) == 0)
268                                 return (CC_OR_FOUND);
269
270                         if (++num_matches > 1)
271                                 return(CC_OR_AMBIGUOUS);
272                 }
273         }
274
275         if (num_matches > 0)
276                 return(CC_OR_FOUND);
277         else
278                 return(CC_OR_NOT_FOUND);
279 }
280
281
282 static int
283 cctl_parse_tl(char *str, int *target, int *lun)
284 {
285         char *tmpstr;
286         int retval;
287
288         retval = 0;
289
290         while (isspace(*str) && (*str != '\0'))
291                 str++;
292
293         tmpstr = (char *)strtok(str, ":");
294         if ((tmpstr != NULL) && (*tmpstr != '\0')) {
295                 *target = strtol(tmpstr, NULL, 0);
296                 tmpstr = (char *)strtok(NULL, ":");
297                 if ((tmpstr != NULL) && (*tmpstr != '\0')) {
298                         *lun = strtol(tmpstr, NULL, 0);
299                 } else
300                         retval = -1;
301         } else
302                 retval = -1;
303
304         return (retval);
305 }
306
307 static int
308 cctl_dump_ooa(int fd, int argc, char **argv)
309 {
310         struct ctl_ooa ooa;
311         long double cmd_latency;
312         int num_entries, len;
313         int target = -1, lun = -1;
314         int retval;
315         unsigned int i;
316
317         num_entries = 104;
318
319         if ((argc > 2)
320          && (isdigit(argv[2][0]))) {
321                 retval = cctl_parse_tl(argv[2], &target, &lun);
322                 if (retval != 0)
323                         warnx("invalid target:lun argument %s", argv[2]);
324         }
325 retry:
326
327         len = num_entries * sizeof(struct ctl_ooa_entry);
328
329         bzero(&ooa, sizeof(ooa));
330
331         ooa.entries = malloc(len);
332
333         if (ooa.entries == NULL) {
334                 warn("%s: error mallocing %d bytes", __func__, len);
335                 return (1);
336         }
337
338         if (argc > 2) {
339                 ooa.lun_num = lun;
340         } else
341                 ooa.flags |= CTL_OOA_FLAG_ALL_LUNS;
342
343         ooa.alloc_len = len;
344         ooa.alloc_num = num_entries;
345         if (ioctl(fd, CTL_GET_OOA, &ooa) == -1) {
346                 warn("%s: CTL_GET_OOA ioctl failed", __func__);
347                 retval = 1;
348                 goto bailout;
349         }
350
351         if (ooa.status == CTL_OOA_NEED_MORE_SPACE) {
352                 num_entries = num_entries * 2;
353                 free(ooa.entries);
354                 ooa.entries = NULL;
355                 goto retry;
356         }
357
358         if (ooa.status != CTL_OOA_OK) {
359                 warnx("%s: CTL_GET_OOA ioctl returned error %d", __func__,
360                       ooa.status);
361                 retval = 1;
362                 goto bailout;
363         }
364
365         fprintf(stdout, "Dumping OOA queues\n");
366         for (i = 0; i < ooa.fill_num; i++) {
367                 struct ctl_ooa_entry *entry;
368                 char cdb_str[(SCSI_MAX_CDBLEN * 3) +1];
369                 struct bintime delta_bt;
370                 struct timespec ts;
371
372                 entry = &ooa.entries[i];
373
374                 delta_bt = ooa.cur_bt;
375                 bintime_sub(&delta_bt, &entry->start_bt);
376                 bintime2timespec(&delta_bt, &ts);
377                 cmd_latency = ts.tv_sec * 1000;
378                 if (ts.tv_nsec > 0)
379                         cmd_latency += ts.tv_nsec / 1000000;
380                 
381                 fprintf(stdout, "LUN %jd tag 0x%04x%s%s%s%s%s: %s. CDB: %s "
382                         "(%0.0Lf ms)\n",
383                         (intmax_t)entry->lun_num, entry->tag_num,
384                         (entry->cmd_flags & CTL_OOACMD_FLAG_BLOCKED) ?
385                          " BLOCKED" : "",
386                         (entry->cmd_flags & CTL_OOACMD_FLAG_DMA) ? " DMA" : "",
387                         (entry->cmd_flags & CTL_OOACMD_FLAG_DMA_QUEUED) ?
388                          " DMAQUEUED" : "",
389                         (entry->cmd_flags & CTL_OOACMD_FLAG_ABORT) ?
390                          " ABORT" : "",
391                         (entry->cmd_flags & CTL_OOACMD_FLAG_RTR) ? " RTR" :"",
392                         scsi_op_desc(entry->cdb[0], NULL),
393                         scsi_cdb_string(entry->cdb, cdb_str, sizeof(cdb_str)),
394                         cmd_latency);
395         }
396         fprintf(stdout, "OOA queues dump done\n");
397 #if 0
398         if (ioctl(fd, CTL_DUMP_OOA) == -1) {
399                 warn("%s: CTL_DUMP_OOA ioctl failed", __func__);
400                 return (1);
401         }
402 #endif
403
404 bailout:
405         free(ooa.entries);
406
407         return (0);
408 }
409
410 static int
411 cctl_dump_structs(int fd, ctladm_cmdargs cmdargs __unused)
412 {
413         if (ioctl(fd, CTL_DUMP_STRUCTS) == -1) {
414                 warn(__func__);
415                 return (1);
416         }
417         return (0);
418 }
419
420 static int
421 cctl_port_dump(int fd, int quiet, int xml, int32_t targ_port,
422                ctl_port_type port_type)
423 {
424         struct ctl_port_list port_list;
425         struct ctl_port_entry *entries;
426         struct sbuf *sb = NULL;
427         int num_entries;
428         int did_print = 0;
429         unsigned int i;
430
431         num_entries = 16;
432
433 retry:
434
435         entries = malloc(sizeof(*entries) * num_entries);
436         bzero(&port_list, sizeof(port_list));
437         port_list.entries = entries;
438         port_list.alloc_num = num_entries;
439         port_list.alloc_len = num_entries * sizeof(*entries);
440         if (ioctl(fd, CTL_GET_PORT_LIST, &port_list) != 0) {
441                 warn("%s: CTL_GET_PORT_LIST ioctl failed", __func__);
442                 return (1);
443         }
444         if (port_list.status == CTL_PORT_LIST_NEED_MORE_SPACE) {
445                 printf("%s: allocated %d, need %d, retrying\n", __func__,
446                        num_entries, port_list.fill_num + port_list.dropped_num);
447                 free(entries);
448                 num_entries = port_list.fill_num + port_list.dropped_num;
449                 goto retry;
450         }
451
452         if ((quiet == 0)
453          && (xml == 0))
454                 printf("Port Online Type     Name         pp vp %-18s %-18s\n",
455                        "WWNN", "WWPN");
456
457         if (xml != 0) {
458                 sb = sbuf_new_auto();
459                 sbuf_printf(sb, "<ctlfelist>\n");
460         }
461         for (i = 0; i < port_list.fill_num; i++) {
462                 struct ctl_port_entry *entry;
463                 const char *type;
464
465                 entry = &entries[i];
466
467                 switch (entry->port_type) {
468                 case CTL_PORT_FC:
469                         type = "FC";
470                         break;
471                 case CTL_PORT_SCSI:
472                         type = "SCSI";
473                         break;
474                 case CTL_PORT_IOCTL:
475                         type = "IOCTL";
476                         break;
477                 case CTL_PORT_INTERNAL:
478                         type = "INTERNAL";
479                         break;
480                 case CTL_PORT_ISC:
481                         type = "ISC";
482                         break;
483                 default:
484                         type = "UNKNOWN";
485                         break;
486                 }
487
488                 /*
489                  * If the user specified a frontend number or a particular
490                  * frontend type, only print out that particular frontend
491                  * or frontend type.
492                  */
493                 if ((targ_port != -1)
494                  && (targ_port != entry->targ_port))
495                         continue;
496                 else if ((port_type != CTL_PORT_NONE)
497                       && ((port_type & entry->port_type) == 0))
498                         continue;
499
500                 did_print = 1;
501
502 #if 0
503                 printf("Num: %ju Type: %s (%#x) Name: %s Physical Port: %d "
504                        "Virtual Port: %d\n", (uintmax_t)entry->fe_num, type,
505                        entry->port_type, entry->fe_name, entry->physical_port,
506                        entry->virtual_port);
507                 printf("WWNN %#jx WWPN %#jx Online: %s\n",
508                        (uintmax_t)entry->wwnn, (uintmax_t)entry->wwpn,
509                        (entry->online) ? "YES" : "NO" );
510 #endif
511                 if (xml == 0) {
512                         printf("%-4d %-6s %-8s %-12s %-2d %-2d %#-18jx "
513                                "%#-18jx\n",
514                                entry->targ_port, (entry->online) ? "YES" : "NO",
515                                type, entry->port_name, entry->physical_port,
516                                entry->virtual_port, (uintmax_t)entry->wwnn,
517                                (uintmax_t)entry->wwpn);
518                 } else {
519                         sbuf_printf(sb, "<targ_port id=\"%d\">\n",
520                                     entry->targ_port);
521                         sbuf_printf(sb, "<online>%s</online>\n",
522                                     (entry->online) ? "YES" : "NO");
523                         sbuf_printf(sb, "<port_type>%s</port_type>\n", type);
524                         sbuf_printf(sb, "<port_name>%s</port_name>\n",
525                                     entry->port_name);
526                         sbuf_printf(sb, "<physical_port>%d</physical_port>\n",
527                                     entry->physical_port);
528                         sbuf_printf(sb, "<virtual_port>%d</virtual_port>\n",
529                                     entry->virtual_port);
530                         sbuf_printf(sb, "<wwnn>%#jx</wwnn>\n",
531                                     (uintmax_t)entry->wwnn);
532                         sbuf_printf(sb, "<wwpn>%#jx</wwpn>\n",
533                                     (uintmax_t)entry->wwpn);
534                         sbuf_printf(sb, "</targ_port>\n");
535                 }
536
537         }
538         if (xml != 0) {
539                 sbuf_printf(sb, "</ctlfelist>\n");
540                 sbuf_finish(sb);
541                 printf("%s", sbuf_data(sb));
542                 sbuf_delete(sb);
543         }
544
545         /*
546          * Give some indication that we didn't find the frontend or
547          * frontend type requested by the user.  We could print something
548          * out, but it would probably be better to hide that behind a
549          * verbose flag.
550          */
551         if ((did_print == 0)
552          && ((targ_port != -1)
553           || (port_type != CTL_PORT_NONE)))
554                 return (1);
555         else
556                 return (0);
557 }
558
559 typedef enum {
560         CCTL_PORT_MODE_NONE,
561         CCTL_PORT_MODE_LIST,
562         CCTL_PORT_MODE_SET,
563         CCTL_PORT_MODE_ON,
564         CCTL_PORT_MODE_OFF
565 } cctl_port_mode;
566
567 struct ctladm_opts cctl_fe_table[] = {
568         {"fc", CTL_PORT_FC, CTLADM_ARG_NONE, NULL},
569         {"scsi", CTL_PORT_SCSI, CTLADM_ARG_NONE, NULL},
570         {"internal", CTL_PORT_INTERNAL, CTLADM_ARG_NONE, NULL},
571         {"all", CTL_PORT_ALL, CTLADM_ARG_NONE, NULL},
572         {NULL, 0, 0, NULL}
573 };
574
575 static int
576 cctl_port(int fd, int argc, char **argv, char *combinedopt)
577 {
578         int c;
579         int32_t targ_port = -1;
580         int retval = 0;
581         int wwnn_set = 0, wwpn_set = 0;
582         uint64_t wwnn = 0, wwpn = 0;
583         cctl_port_mode port_mode = CCTL_PORT_MODE_NONE;
584         struct ctl_port_entry entry;
585         ctl_port_type port_type = CTL_PORT_NONE;
586         int quiet = 0, xml = 0;
587
588         while ((c = getopt(argc, argv, combinedopt)) != -1) {
589                 switch (c) {
590                 case 'l':
591                         if (port_mode != CCTL_PORT_MODE_NONE)
592                                 goto bailout_badarg;
593
594                         port_mode = CCTL_PORT_MODE_LIST;
595                         break;
596                 case 'o':
597                         if (port_mode != CCTL_PORT_MODE_NONE)
598                                 goto bailout_badarg;
599                         
600                         if (strcasecmp(optarg, "on") == 0)
601                                 port_mode = CCTL_PORT_MODE_ON;
602                         else if (strcasecmp(optarg, "off") == 0)
603                                 port_mode = CCTL_PORT_MODE_OFF;
604                         else {
605                                 warnx("Invalid -o argument %s, \"on\" or "
606                                       "\"off\" are the only valid args",
607                                       optarg);
608                                 retval = 1;
609                                 goto bailout;
610                         }
611                         break;
612                 case 'p':
613                         targ_port = strtol(optarg, NULL, 0);
614                         break;
615                 case 'q':
616                         quiet = 1;
617                         break;
618                 case 't': {
619                         ctladm_optret optret;
620                         ctladm_cmdargs argnum;
621                         const char *subopt;
622                         ctl_port_type tmp_port_type;
623
624                         optret = getoption(cctl_fe_table, optarg, &tmp_port_type,
625                                            &argnum, &subopt);
626                         if (optret == CC_OR_AMBIGUOUS) {
627                                 warnx("%s: ambiguous frontend type %s",
628                                       __func__, optarg);
629                                 retval = 1;
630                                 goto bailout;
631                         } else if (optret == CC_OR_NOT_FOUND) {
632                                 warnx("%s: invalid frontend type %s",
633                                       __func__, optarg);
634                                 retval = 1;
635                                 goto bailout;
636                         }
637
638                         port_type |= tmp_port_type;
639                         break;
640                 }
641                 case 'w':
642                         if ((port_mode != CCTL_PORT_MODE_NONE)
643                          && (port_mode != CCTL_PORT_MODE_SET))
644                                 goto bailout_badarg;
645
646                         port_mode = CCTL_PORT_MODE_SET;
647
648                         wwnn = strtoull(optarg, NULL, 0);
649                         wwnn_set = 1;
650                         break;
651                 case 'W':
652                         if ((port_mode != CCTL_PORT_MODE_NONE)
653                          && (port_mode != CCTL_PORT_MODE_SET))
654                                 goto bailout_badarg;
655
656                         port_mode = CCTL_PORT_MODE_SET;
657
658                         wwpn = strtoull(optarg, NULL, 0);
659                         wwpn_set = 1;
660                         break;
661                 case 'x':
662                         xml = 1;
663                         break;
664                 }
665         }
666
667         /*
668          * The user can specify either one or more frontend types (-t), or
669          * a specific frontend, but not both.
670          *
671          * If the user didn't specify a frontend type or number, set it to
672          * all.  This is primarily needed for the enable/disable ioctls.
673          * This will be a no-op for the listing code.  For the set ioctl,
674          * we'll throw an error, since that only works on one port at a time.
675          */
676         if ((port_type != CTL_PORT_NONE) && (targ_port != -1)) {
677                 warnx("%s: can only specify one of -t or -n", __func__);
678                 retval = 1;
679                 goto bailout;
680         } else if ((targ_port == -1) && (port_type == CTL_PORT_NONE))
681                 port_type = CTL_PORT_ALL;
682
683         bzero(&entry, sizeof(&entry));
684
685         /*
686          * These are needed for all but list/dump mode.
687          */
688         entry.port_type = port_type;
689         entry.targ_port = targ_port;
690
691         switch (port_mode) {
692         case CCTL_PORT_MODE_LIST:
693                 cctl_port_dump(fd, quiet, xml, targ_port, port_type);
694                 break;
695         case CCTL_PORT_MODE_SET:
696                 if (targ_port == -1) {
697                         warnx("%s: -w and -W require -n", __func__);
698                         retval = 1;
699                         goto bailout;
700                 }
701
702                 if (wwnn_set) {
703                         entry.flags |= CTL_PORT_WWNN_VALID;
704                         entry.wwnn = wwnn;
705                 }
706                 if (wwpn_set) {
707                         entry.flags |= CTL_PORT_WWPN_VALID;
708                         entry.wwpn = wwpn;
709                 }
710
711                 if (ioctl(fd, CTL_SET_PORT_WWNS, &entry) == -1) {
712                         warn("%s: CTL_SET_PORT_WWNS ioctl failed", __func__);
713                         retval = 1;
714                         goto bailout;
715                 }
716                 break;
717         case CCTL_PORT_MODE_ON:
718                 if (ioctl(fd, CTL_ENABLE_PORT, &entry) == -1) {
719                         warn("%s: CTL_ENABLE_PORT ioctl failed", __func__);
720                         retval = 1;
721                         goto bailout;
722                 }
723                 fprintf(stdout, "Front End Ports enabled\n");
724                 break;
725         case CCTL_PORT_MODE_OFF:
726                 if (ioctl(fd, CTL_DISABLE_PORT, &entry) == -1) {
727                         warn("%s: CTL_DISABLE_PORT ioctl failed", __func__);
728                         retval = 1;
729                         goto bailout;
730                 }
731                 fprintf(stdout, "Front End Ports disabled\n");
732                 break;
733         default:
734                 warnx("%s: one of -l, -o or -w/-W must be specified", __func__);
735                 retval = 1;
736                 goto bailout;
737                 break;
738         }
739
740 bailout:
741
742         return (retval);
743
744 bailout_badarg:
745         warnx("%s: only one of -l, -o or -w/-W may be specified", __func__);
746         return (1);
747 }
748
749 static int
750 cctl_do_io(int fd, int retries, union ctl_io *io, const char *func)
751 {
752         do {
753                 if (ioctl(fd, CTL_IO, io) == -1) {
754                         warn("%s: error sending CTL_IO ioctl", func);
755                         return (-1);
756                 }
757         } while (((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)
758               && (retries-- > 0));
759
760         return (0);
761 }
762
763 static int
764 cctl_delay(int fd, int target, int lun, int argc, char **argv,
765            char *combinedopt)
766 {
767         int datamove_delay;
768         struct ctl_io_delay_info delay_info;
769         char *delayloc = NULL;
770         char *delaytype = NULL;
771         int delaytime = -1;
772         int retval;
773         int c;
774
775         retval = 0;
776         datamove_delay = 0;
777
778         memset(&delay_info, 0, sizeof(delay_info));
779
780         while ((c = getopt(argc, argv, combinedopt)) != -1) {
781                 switch (c) {
782                 case 'T':
783                         delaytype = strdup(optarg);
784                         break;
785                 case 'l':
786                         delayloc = strdup(optarg);
787                         break;
788                 case 't':
789                         delaytime = strtoul(optarg, NULL, 0);
790                         break;
791                 }
792         }
793
794         if (delaytime == -1) {
795                 warnx("%s: you must specify the delaytime with -t", __func__);
796                 retval = 1;
797                 goto bailout;
798         }
799
800         if (strcasecmp(delayloc, "datamove") == 0)
801                 delay_info.delay_loc = CTL_DELAY_LOC_DATAMOVE;
802         else if (strcasecmp(delayloc, "done") == 0)
803                 delay_info.delay_loc = CTL_DELAY_LOC_DONE;
804         else {
805                 warnx("%s: invalid delay location %s", __func__, delayloc);
806                 retval = 1;
807                 goto bailout;
808         }
809
810         if ((delaytype == NULL)
811          || (strcmp(delaytype, "oneshot") == 0))
812                 delay_info.delay_type = CTL_DELAY_TYPE_ONESHOT;
813         else if (strcmp(delaytype, "cont") == 0)
814                 delay_info.delay_type = CTL_DELAY_TYPE_CONT;
815         else {
816                 warnx("%s: invalid delay type %s", __func__, delaytype);
817                 retval = 1;
818                 goto bailout;
819         }
820
821         delay_info.target_id = target;
822         delay_info.lun_id = lun;
823         delay_info.delay_secs = delaytime;
824
825         if (ioctl(fd, CTL_DELAY_IO, &delay_info) == -1) {
826                 warn("%s: CTL_DELAY_IO ioctl failed", __func__);
827                 retval = 1;
828                 goto bailout;
829         }
830         switch (delay_info.status) {
831         case CTL_DELAY_STATUS_NONE:
832                 warnx("%s: no delay status??", __func__);
833                 retval = 1;
834                 break;
835         case CTL_DELAY_STATUS_OK:
836                 break;
837         case CTL_DELAY_STATUS_INVALID_LUN:
838                 warnx("%s: invalid lun %d", __func__, lun);
839                 retval = 1;
840                 break;
841         case CTL_DELAY_STATUS_INVALID_TYPE:
842                 warnx("%s: invalid delay type %d", __func__,
843                       delay_info.delay_type);
844                 retval = 1;
845                 break;
846         case CTL_DELAY_STATUS_INVALID_LOC:
847                 warnx("%s: delay location %s not implemented?", __func__,
848                       delayloc);
849                 retval = 1;
850                 break;
851         case CTL_DELAY_STATUS_NOT_IMPLEMENTED:
852                 warnx("%s: delay not implemented in the kernel", __func__);
853                 warnx("%s: recompile with the CTL_IO_DELAY flag set", __func__);
854                 retval = 1;
855                 break;
856         default:
857                 warnx("%s: unknown delay return status %d", __func__,
858                       delay_info.status);
859                 retval = 1;
860                 break;
861         }
862 bailout:
863
864         /* delayloc should never be NULL, but just in case...*/
865         if (delayloc != NULL)
866                 free(delayloc);
867
868         return (retval);
869 }
870
871 static int
872 cctl_realsync(int fd, int argc, char **argv)
873 {
874         int syncstate;
875         int retval;
876         char *syncarg;
877
878         retval = 0;
879
880         if (argc != 3) {
881                 warnx("%s %s takes exactly one argument", argv[0], argv[1]);
882                 retval = 1;
883                 goto bailout;
884         }
885
886         syncarg = argv[2];
887
888         if (strncasecmp(syncarg, "query", min(strlen(syncarg),
889                         strlen("query"))) == 0) {
890                 if (ioctl(fd, CTL_REALSYNC_GET, &syncstate) == -1) {
891                         warn("%s: CTL_REALSYNC_GET ioctl failed", __func__);
892                         retval = 1;
893                         goto bailout;
894                 }
895                 fprintf(stdout, "SYNCHRONIZE CACHE support is: ");
896                 switch (syncstate) {
897                 case 0:
898                         fprintf(stdout, "OFF\n");
899                         break;
900                 case 1:
901                         fprintf(stdout, "ON\n");
902                         break;
903                 default:
904                         fprintf(stdout, "unknown (%d)\n", syncstate);
905                         break;
906                 }
907                 goto bailout;
908         } else if (strcasecmp(syncarg, "on") == 0) {
909                 syncstate = 1;
910         } else if (strcasecmp(syncarg, "off") == 0) {
911                 syncstate = 0;
912         } else {
913                 warnx("%s: invalid realsync argument %s", __func__, syncarg);
914                 retval = 1;
915                 goto bailout;
916         }
917
918         if (ioctl(fd, CTL_REALSYNC_SET, &syncstate) == -1) {
919                 warn("%s: CTL_REALSYNC_SET ioctl failed", __func__);
920                 retval = 1;
921                 goto bailout;
922         }
923 bailout:
924         return (retval);
925 }
926
927 static int
928 cctl_getsetsync(int fd, int target, int lun, ctladm_cmdfunction command,
929                 int argc, char **argv, char *combinedopt)
930 {
931         struct ctl_sync_info sync_info;
932         uint32_t ioctl_cmd;
933         int sync_interval = -1;
934         int retval;
935         int c;
936
937         retval = 0;
938
939         memset(&sync_info, 0, sizeof(sync_info));
940         sync_info.target_id = target;
941         sync_info.lun_id = lun;
942
943         while ((c = getopt(argc, argv, combinedopt)) != -1) {
944                 switch (c) {
945                 case 'i':
946                         sync_interval = strtoul(optarg, NULL, 0);
947                         break;
948                 default:
949                         break;
950                 }
951         }
952
953         if (command == CTLADM_CMD_SETSYNC) {
954                 if (sync_interval == -1) {
955                         warnx("%s: you must specify the sync interval with -i",
956                               __func__);
957                         retval = 1;
958                         goto bailout;
959                 }
960                 sync_info.sync_interval = sync_interval;
961                 ioctl_cmd = CTL_SETSYNC;
962         } else {
963                 ioctl_cmd = CTL_GETSYNC;
964         }
965
966         if (ioctl(fd, ioctl_cmd, &sync_info) == -1) {
967                 warn("%s: CTL_%sSYNC ioctl failed", __func__,
968                      (command == CTLADM_CMD_SETSYNC) ? "SET" : "GET");
969                 retval = 1;
970                 goto bailout;
971         }
972
973         switch (sync_info.status) {
974         case CTL_GS_SYNC_OK:
975                 if (command == CTLADM_CMD_GETSYNC) {
976                         fprintf(stdout, "%d:%d: sync interval: %d\n",
977                                 target, lun, sync_info.sync_interval);
978                 }
979                 break;
980         case CTL_GS_SYNC_NO_LUN:
981                 warnx("%s: unknown target:LUN %d:%d", __func__, target, lun);
982                 retval = 1;
983                 break;
984         case CTL_GS_SYNC_NONE:
985         default:
986                 warnx("%s: unknown CTL_%sSYNC status %d", __func__,
987                       (command == CTLADM_CMD_SETSYNC) ? "SET" : "GET",
988                       sync_info.status);
989                 retval = 1;
990                 break;
991         }
992 bailout:
993         return (retval);
994 }
995
996 struct ctladm_opts cctl_err_types[] = {
997         {"aborted", CTL_LUN_INJ_ABORTED, CTLADM_ARG_NONE, NULL},
998         {"mediumerr", CTL_LUN_INJ_MEDIUM_ERR, CTLADM_ARG_NONE, NULL},
999         {"ua", CTL_LUN_INJ_UA, CTLADM_ARG_NONE, NULL},
1000         {"custom", CTL_LUN_INJ_CUSTOM, CTLADM_ARG_NONE, NULL},
1001         {NULL, 0, 0, NULL}
1002
1003 };
1004
1005 struct ctladm_opts cctl_err_patterns[] = {
1006         {"read", CTL_LUN_PAT_READ, CTLADM_ARG_NONE, NULL},
1007         {"write", CTL_LUN_PAT_WRITE, CTLADM_ARG_NONE, NULL},
1008         {"rw", CTL_LUN_PAT_READWRITE, CTLADM_ARG_NONE, NULL},
1009         {"readwrite", CTL_LUN_PAT_READWRITE, CTLADM_ARG_NONE, NULL},
1010         {"readcap", CTL_LUN_PAT_READCAP, CTLADM_ARG_NONE, NULL},
1011         {"tur", CTL_LUN_PAT_TUR, CTLADM_ARG_NONE, NULL},
1012         {"any", CTL_LUN_PAT_ANY, CTLADM_ARG_NONE, NULL},
1013 #if 0
1014         {"cmd", CTL_LUN_PAT_CMD,  CTLADM_ARG_NONE, NULL},
1015 #endif
1016         {NULL, 0, 0, NULL}
1017 };
1018
1019 static int
1020 cctl_error_inject(int fd, uint32_t target, uint32_t lun, int argc, char **argv, 
1021                   char *combinedopt)
1022 {
1023         int retval;
1024         struct ctl_error_desc err_desc;
1025         uint64_t lba = 0;
1026         uint32_t len = 0;
1027         uint64_t delete_id = 0;
1028         int delete_id_set = 0;
1029         int continuous = 0;
1030         int sense_len = 0;
1031         int fd_sense = 0;
1032         int c;
1033
1034         bzero(&err_desc, sizeof(err_desc));
1035         err_desc.target_id = target;
1036         err_desc.lun_id = lun;
1037
1038         while ((c = getopt(argc, argv, combinedopt)) != -1) {
1039                 switch (c) {
1040                 case 'c':
1041                         continuous = 1;
1042                         break;
1043                 case 'd':
1044                         delete_id = strtoull(optarg, NULL, 0);
1045                         delete_id_set = 1;
1046                         break;
1047                 case 'i':
1048                 case 'p': {
1049                         ctladm_optret optret;
1050                         ctladm_cmdargs argnum;
1051                         const char *subopt;
1052
1053                         if (c == 'i') {
1054                                 ctl_lun_error err_type;
1055
1056                                 if (err_desc.lun_error != CTL_LUN_INJ_NONE) {
1057                                         warnx("%s: can't specify multiple -i "
1058                                               "arguments", __func__);
1059                                         retval = 1;
1060                                         goto bailout;
1061                                 }
1062                                 optret = getoption(cctl_err_types, optarg,
1063                                                    &err_type, &argnum, &subopt);
1064                                 err_desc.lun_error = err_type;
1065                         } else {
1066                                 ctl_lun_error_pattern pattern;
1067
1068                                 optret = getoption(cctl_err_patterns, optarg,
1069                                                    &pattern, &argnum, &subopt);
1070                                 err_desc.error_pattern |= pattern;
1071                         }
1072
1073                         if (optret == CC_OR_AMBIGUOUS) {
1074                                 warnx("%s: ambiguous argument %s", __func__,
1075                                       optarg);
1076                                 retval = 1;
1077                                 goto bailout;
1078                         } else if (optret == CC_OR_NOT_FOUND) {
1079                                 warnx("%s: argument %s not found", __func__,
1080                                       optarg);
1081                                 retval = 1;
1082                                 goto bailout;
1083                         }
1084                         break;
1085                 }
1086                 case 'r': {
1087                         char *tmpstr, *tmpstr2;
1088
1089                         tmpstr = strdup(optarg);
1090                         if (tmpstr == NULL) {
1091                                 warn("%s: error duplicating string %s",
1092                                      __func__, optarg);
1093                                 retval = 1;
1094                                 goto bailout;
1095                         }
1096
1097                         tmpstr2 = strsep(&tmpstr, ",");
1098                         if (tmpstr2 == NULL) {
1099                                 warnx("%s: invalid -r argument %s", __func__,
1100                                       optarg);
1101                                 retval = 1;
1102                                 free(tmpstr);
1103                                 goto bailout;
1104                         }
1105                         lba = strtoull(tmpstr2, NULL, 0);
1106                         tmpstr2 = strsep(&tmpstr, ",");
1107                         if (tmpstr2 == NULL) {
1108                                 warnx("%s: no len argument for -r lba,len, got"
1109                                       " %s", __func__, optarg);
1110                                 retval = 1;
1111                                 free(tmpstr);
1112                                 goto bailout;
1113                         }
1114                         len = strtoul(tmpstr2, NULL, 0);
1115                         free(tmpstr);
1116                         break;
1117                 }
1118                 case 's': {
1119                         struct get_hook hook;
1120                         char *sensestr;
1121
1122                         sense_len = strtol(optarg, NULL, 0);
1123                         if (sense_len <= 0) {
1124                                 warnx("invalid number of sense bytes %d",
1125                                       sense_len);
1126                                 retval = 1;
1127                                 goto bailout;
1128                         }
1129
1130                         sense_len = MIN(sense_len, SSD_FULL_SIZE);
1131
1132                         hook.argc = argc - optind;
1133                         hook.argv = argv + optind;
1134                         hook.got = 0;
1135
1136                         sensestr = cget(&hook, NULL);
1137                         if ((sensestr != NULL)
1138                          && (sensestr[0] == '-')) {
1139                                 fd_sense = 1;
1140                         } else {
1141                                 buff_encode_visit(
1142                                     (uint8_t *)&err_desc.custom_sense,
1143                                     sense_len, sensestr, iget, &hook);
1144                         }
1145                         optind += hook.got;
1146                         break;
1147                 }
1148                 default:
1149                         break;
1150                 }
1151         }
1152
1153         if (delete_id_set != 0) {
1154                 err_desc.serial = delete_id;
1155                 if (ioctl(fd, CTL_ERROR_INJECT_DELETE, &err_desc) == -1) {
1156                         warn("%s: error issuing CTL_ERROR_INJECT_DELETE ioctl",
1157                              __func__);
1158                         retval = 1;
1159                 }
1160                 goto bailout;
1161         }
1162
1163         if (err_desc.lun_error == CTL_LUN_INJ_NONE) {
1164                 warnx("%s: error injection command (-i) needed",
1165                       __func__);
1166                 retval = 1;
1167                 goto bailout;
1168         } else if ((err_desc.lun_error == CTL_LUN_INJ_CUSTOM)
1169                 && (sense_len == 0)) {
1170                 warnx("%s: custom error requires -s", __func__);
1171                 retval = 1;
1172                 goto bailout;
1173         }
1174
1175         if (continuous != 0)
1176                 err_desc.lun_error |= CTL_LUN_INJ_CONTINUOUS;
1177
1178         /*
1179          * If fd_sense is set, we need to read the sense data the user
1180          * wants returned from stdin.
1181          */
1182         if (fd_sense == 1) {
1183                 ssize_t amt_read;
1184                 int amt_to_read = sense_len;
1185                 u_int8_t *buf_ptr = (uint8_t *)&err_desc.custom_sense;
1186
1187                 for (amt_read = 0; amt_to_read > 0;
1188                      amt_read = read(STDIN_FILENO, buf_ptr, amt_to_read)) {
1189                         if (amt_read == -1) {
1190                                 warn("error reading sense data from stdin");
1191                                 retval = 1;
1192                                 goto bailout;
1193                         }
1194                         amt_to_read -= amt_read;
1195                         buf_ptr += amt_read;
1196                 }
1197         }
1198
1199         if (err_desc.error_pattern == CTL_LUN_PAT_NONE) {
1200                 warnx("%s: command pattern (-p) needed", __func__);
1201                 retval = 1;
1202                 goto bailout;
1203         }
1204
1205         if (len != 0) {
1206                 err_desc.error_pattern |= CTL_LUN_PAT_RANGE;
1207                 /*
1208                  * We could check here to see whether it's a read/write
1209                  * command, but that will be pointless once we allow
1210                  * custom patterns.  At that point, the user could specify
1211                  * a READ(6) CDB type, and we wouldn't have an easy way here
1212                  * to verify whether range checking is possible there.  The
1213                  * user will just figure it out when his error never gets
1214                  * executed.
1215                  */
1216 #if 0
1217                 if ((err_desc.pattern & CTL_LUN_PAT_READWRITE) == 0) {
1218                         warnx("%s: need read and/or write pattern if range "
1219                               "is specified", __func__);
1220                         retval = 1;
1221                         goto bailout;
1222                 }
1223 #endif
1224                 err_desc.lba_range.lba = lba;
1225                 err_desc.lba_range.len = len;
1226         }
1227
1228         if (ioctl(fd, CTL_ERROR_INJECT, &err_desc) == -1) {
1229                 warn("%s: error issuing CTL_ERROR_INJECT ioctl", __func__);
1230                 retval = 1;
1231         } else {
1232                 printf("Error injection succeeded, serial number is %ju\n",
1233                        (uintmax_t)err_desc.serial);
1234         }
1235 bailout:
1236
1237         return (retval);
1238 }
1239
1240 static int
1241 cctl_lunlist(int fd)
1242 {
1243         struct scsi_report_luns_data *lun_data;
1244         struct scsi_inquiry_data *inq_data;
1245         uint32_t num_luns;
1246         int target;
1247         int initid;
1248         unsigned int i;
1249         int retval;
1250
1251         retval = 0;
1252         inq_data = NULL;
1253
1254         target = 6;
1255         initid = 7;
1256
1257         /*
1258          * XXX KDM assuming LUN 0 is fine, but we may need to change this
1259          * if we ever acquire the ability to have multiple targets.
1260          */
1261         if ((retval = cctl_get_luns(fd, target, /*lun*/ 0, initid,
1262                                     /*retries*/ 2, &lun_data, &num_luns)) != 0)
1263                 goto bailout;
1264
1265         inq_data = malloc(sizeof(*inq_data));
1266         if (inq_data == NULL) {
1267                 warn("%s: couldn't allocate memory for inquiry data\n",
1268                      __func__);
1269                 retval = 1;
1270                 goto bailout;
1271         }
1272         for (i = 0; i < num_luns; i++) {
1273                 char scsi_path[40];
1274                 int lun_val;
1275
1276                 switch (lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) {
1277                 case RPL_LUNDATA_ATYP_PERIPH:
1278                         lun_val = lun_data->luns[i].lundata[1];
1279                         break;
1280                 case RPL_LUNDATA_ATYP_FLAT:
1281                         lun_val = (lun_data->luns[i].lundata[0] &
1282                                 RPL_LUNDATA_FLAT_LUN_MASK) |
1283                                 (lun_data->luns[i].lundata[1] <<
1284                                 RPL_LUNDATA_FLAT_LUN_BITS);
1285                         break;
1286                 case RPL_LUNDATA_ATYP_LUN:
1287                 case RPL_LUNDATA_ATYP_EXTLUN:
1288                 default:
1289                         fprintf(stdout, "Unsupported LUN format %d\n",
1290                                 lun_data->luns[i].lundata[0] &
1291                                 RPL_LUNDATA_ATYP_MASK);
1292                         lun_val = -1;
1293                         break;
1294                 }
1295                 if (lun_val == -1)
1296                         continue;
1297
1298                 if ((retval = cctl_get_inquiry(fd, target, lun_val, initid,
1299                                                /*retries*/ 2, scsi_path,
1300                                                sizeof(scsi_path),
1301                                                inq_data)) != 0) {
1302                         goto bailout;
1303                 }
1304                 printf("%s", scsi_path);
1305                 scsi_print_inquiry(inq_data);
1306         }
1307 bailout:
1308
1309         if (lun_data != NULL)
1310                 free(lun_data);
1311
1312         if (inq_data != NULL)
1313                 free(inq_data);
1314
1315         return (retval);
1316 }
1317
1318 static void
1319 cctl_cfi_mt_statusstr(cfi_mt_status status, char *str, int str_len)
1320 {
1321         switch (status) {
1322         case CFI_MT_PORT_OFFLINE:
1323                 snprintf(str, str_len, "Port Offline");
1324                 break;
1325         case CFI_MT_ERROR:
1326                 snprintf(str, str_len, "Error");
1327                 break;
1328         case CFI_MT_SUCCESS:
1329                 snprintf(str, str_len, "Success");
1330                 break;
1331         case CFI_MT_NONE:
1332                 snprintf(str, str_len, "None??");
1333                 break;
1334         default:
1335                 snprintf(str, str_len, "Unknown status: %d", status);
1336                 break;
1337         }
1338 }
1339
1340 static void
1341 cctl_cfi_bbr_statusstr(cfi_bbrread_status status, char *str, int str_len)
1342 {
1343         switch (status) {
1344         case CFI_BBR_SUCCESS:
1345                 snprintf(str, str_len, "Success");
1346                 break;
1347         case CFI_BBR_LUN_UNCONFIG:
1348                 snprintf(str, str_len, "LUN not configured");
1349                 break;
1350         case CFI_BBR_NO_LUN:
1351                 snprintf(str, str_len, "LUN does not exist");
1352                 break;
1353         case CFI_BBR_NO_MEM:
1354                 snprintf(str, str_len, "Memory allocation error");
1355                 break;
1356         case CFI_BBR_BAD_LEN:
1357                 snprintf(str, str_len, "Length is not a multiple of blocksize");
1358                 break;
1359         case CFI_BBR_RESERV_CONFLICT:
1360                 snprintf(str, str_len, "Reservation conflict");
1361                 break;
1362         case CFI_BBR_LUN_STOPPED:
1363                 snprintf(str, str_len, "LUN is powered off");
1364                 break;
1365         case CFI_BBR_LUN_OFFLINE_CTL:
1366                 snprintf(str, str_len, "LUN is offline");
1367                 break;
1368         case CFI_BBR_LUN_OFFLINE_RC:
1369                 snprintf(str, str_len, "RAIDCore array is offline (double "
1370                          "failure?)");
1371                 break;
1372         case CFI_BBR_SCSI_ERROR:
1373                 snprintf(str, str_len, "SCSI Error");
1374                 break;
1375         case CFI_BBR_ERROR:
1376                 snprintf(str, str_len, "Error");
1377                 break;
1378         default:
1379                 snprintf(str, str_len, "Unknown status: %d", status);
1380                 break;
1381         }
1382 }
1383
1384 static int
1385 cctl_hardstopstart(int fd, ctladm_cmdfunction command)
1386 {
1387         struct ctl_hard_startstop_info hs_info;
1388         char error_str[256];
1389         int do_start;
1390         int retval;
1391
1392         retval = 0;
1393
1394         if (command == CTLADM_CMD_HARDSTART)
1395                 do_start = 1;
1396         else
1397                 do_start = 0;
1398
1399         if (ioctl(fd, (do_start == 1) ? CTL_HARD_START : CTL_HARD_STOP,
1400                   &hs_info) == -1) {
1401                 warn("%s: CTL_HARD_%s ioctl failed", __func__,
1402                      (do_start == 1) ? "START" : "STOP");
1403                 retval = 1;
1404                 goto bailout;
1405         }
1406
1407         fprintf(stdout, "Hard %s Status: ", (command == CTLADM_CMD_HARDSTOP) ?
1408                 "Stop" : "Start");
1409         cctl_cfi_mt_statusstr(hs_info.status, error_str, sizeof(error_str));
1410         fprintf(stdout, "%s\n", error_str);
1411         fprintf(stdout, "Total LUNs: %d\n", hs_info.total_luns);
1412         fprintf(stdout, "LUNs complete: %d\n", hs_info.luns_complete);
1413         fprintf(stdout, "LUNs failed: %d\n", hs_info.luns_failed);
1414
1415 bailout:
1416         return (retval);
1417 }
1418
1419 static int
1420 cctl_bbrread(int fd, int target __unused, int lun, int iid __unused,
1421              int argc, char **argv, char *combinedopt)
1422 {
1423         struct ctl_bbrread_info bbr_info;
1424         char error_str[256];
1425         int datalen = -1;
1426         uint64_t lba = 0;
1427         int lba_set = 0;
1428         int retval;
1429         int c;
1430
1431         retval = 0;
1432
1433         while ((c = getopt(argc, argv, combinedopt)) != -1) {
1434                 switch (c) {
1435                 case 'd':
1436                         datalen = strtoul(optarg, NULL, 0);
1437                         break;
1438                 case 'l':
1439                         lba = strtoull(optarg, NULL, 0);
1440                         lba_set = 1;
1441                         break;
1442                 default:
1443                         break;
1444                 }
1445         }
1446
1447         if (lba_set == 0) {
1448                 warnx("%s: you must specify an LBA with -l", __func__);
1449                 retval = 1;
1450                 goto bailout;
1451         }
1452
1453         if (datalen == -1) {
1454                 warnx("%s: you must specify a length with -d", __func__);
1455                 retval = 1;
1456                 goto bailout;
1457         }
1458
1459         bbr_info.lun_num = lun;
1460         bbr_info.lba = lba;
1461         /*
1462          * XXX KDM get the blocksize first??
1463          */
1464         if ((datalen % 512) != 0) {
1465                 warnx("%s: data length %d is not a multiple of 512 bytes",
1466                      __func__, datalen);
1467                 retval = 1;
1468                 goto bailout;
1469         }
1470         bbr_info.len = datalen;
1471
1472         if (ioctl(fd, CTL_BBRREAD, &bbr_info) == -1) {
1473                 warn("%s: CTL_BBRREAD ioctl failed", __func__);
1474                 retval = 1;
1475                 goto bailout;
1476         }
1477         cctl_cfi_mt_statusstr(bbr_info.status, error_str, sizeof(error_str));
1478         fprintf(stdout, "BBR Read Overall Status: %s\n", error_str);
1479         cctl_cfi_bbr_statusstr(bbr_info.bbr_status, error_str,
1480                                sizeof(error_str));
1481         fprintf(stdout, "BBR Read Status: %s\n", error_str);
1482         /*
1483          * XXX KDM should we bother printing out SCSI status if we get
1484          * CFI_BBR_SCSI_ERROR back?
1485          *
1486          * Return non-zero if this fails?
1487          */
1488 bailout:
1489         return (retval);
1490 }
1491
1492 static int
1493 cctl_startup_shutdown(int fd, int target, int lun, int iid,
1494                       ctladm_cmdfunction command)
1495 {
1496         union ctl_io *io;
1497         struct ctl_id id;
1498         struct scsi_report_luns_data *lun_data;
1499         struct scsi_inquiry_data *inq_data;
1500         uint32_t num_luns;
1501         unsigned int i;
1502         int retval;
1503
1504         retval = 0;
1505         inq_data = NULL;
1506
1507         /*
1508          * - report luns
1509          * - step through each lun, do an inquiry
1510          * - check OOA queue on direct access luns
1511          * - send stop with offline bit to each direct access device with a
1512          *   clear OOA queue
1513          *   - if we get a reservation conflict, reset the LUN to clear it
1514          *     and reissue the stop with the offline bit set
1515          */
1516
1517         id.id = iid;
1518
1519         io = ctl_scsi_alloc_io(id);
1520         if (io == NULL) {
1521                 warnx("%s: can't allocate memory", __func__);
1522                 return (1);
1523         }
1524
1525         if ((retval = cctl_get_luns(fd, target, lun, iid, /*retries*/ 2,
1526                                     &lun_data, &num_luns)) != 0)
1527                 goto bailout;
1528
1529         inq_data = malloc(sizeof(*inq_data));
1530         if (inq_data == NULL) {
1531                 warn("%s: couldn't allocate memory for inquiry data\n",
1532                      __func__);
1533                 retval = 1;
1534                 goto bailout;
1535         }
1536         for (i = 0; i < num_luns; i++) {
1537                 char scsi_path[40];
1538                 int lun_val;
1539
1540                 /*
1541                  * XXX KDM figure out a way to share this code with
1542                  * cctl_lunlist()?
1543                  */
1544                 switch (lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) {
1545                 case RPL_LUNDATA_ATYP_PERIPH:
1546                         lun_val = lun_data->luns[i].lundata[1];
1547                         break;
1548                 case RPL_LUNDATA_ATYP_FLAT:
1549                         lun_val = (lun_data->luns[i].lundata[0] &
1550                                 RPL_LUNDATA_FLAT_LUN_MASK) |
1551                                 (lun_data->luns[i].lundata[1] <<
1552                                 RPL_LUNDATA_FLAT_LUN_BITS);
1553                         break;
1554                 case RPL_LUNDATA_ATYP_LUN:
1555                 case RPL_LUNDATA_ATYP_EXTLUN:
1556                 default:
1557                         fprintf(stdout, "Unsupported LUN format %d\n",
1558                                 lun_data->luns[i].lundata[0] &
1559                                 RPL_LUNDATA_ATYP_MASK);
1560                         lun_val = -1;
1561                         break;
1562                 }
1563                 if (lun_val == -1)
1564                         continue;
1565
1566                 if ((retval = cctl_get_inquiry(fd, target, lun_val, iid,
1567                                                /*retries*/ 2, scsi_path,
1568                                                sizeof(scsi_path),
1569                                                inq_data)) != 0) {
1570                         goto bailout;
1571                 }
1572                 printf("%s", scsi_path);
1573                 scsi_print_inquiry(inq_data);
1574                 /*
1575                  * We only want to shutdown direct access devices.
1576                  */
1577                 if (SID_TYPE(inq_data) != T_DIRECT) {
1578                         printf("%s LUN is not direct access, skipped\n",
1579                                scsi_path);
1580                         continue;
1581                 }
1582
1583                 if (command == CTLADM_CMD_SHUTDOWN) {
1584                         struct ctl_ooa_info ooa_info;
1585
1586                         ooa_info.target_id = target;
1587                         ooa_info.lun_id = lun_val;
1588
1589                         if (ioctl(fd, CTL_CHECK_OOA, &ooa_info) == -1) {
1590                                 printf("%s CTL_CHECK_OOA ioctl failed\n",
1591                                        scsi_path);
1592                                 continue;
1593                         }
1594
1595                         if (ooa_info.status != CTL_OOA_SUCCESS) {
1596                                 printf("%s CTL_CHECK_OOA returned status %d\n", 
1597                                        scsi_path, ooa_info.status);
1598                                 continue;
1599                         }
1600                         if (ooa_info.num_entries != 0) {
1601                                 printf("%s %d entr%s in the OOA queue, "
1602                                        "skipping shutdown\n", scsi_path,
1603                                        ooa_info.num_entries,
1604                                        (ooa_info.num_entries > 1)?"ies" : "y" );
1605                                 continue;
1606                         }
1607                 }
1608                 
1609                 ctl_scsi_start_stop(/*io*/ io,
1610                                     /*start*/(command == CTLADM_CMD_STARTUP) ?
1611                                               1 : 0,
1612                                     /*load_eject*/ 0,
1613                                     /*immediate*/ 0,
1614                                     /*power_conditions*/ SSS_PC_START_VALID,
1615                                     /*onoffline*/ 1,
1616                                     /*ctl_tag_type*/
1617                                     (command == CTLADM_CMD_STARTUP) ?
1618                                     CTL_TAG_SIMPLE :CTL_TAG_ORDERED,
1619                                     /*control*/ 0);
1620
1621                 io->io_hdr.nexus.targ_target.id = target;
1622                 io->io_hdr.nexus.targ_lun = lun_val;
1623                 io->io_hdr.nexus.initid = id;
1624
1625                 if (cctl_do_io(fd, /*retries*/ 3, io, __func__) != 0) {
1626                         retval = 1;
1627                         goto bailout;
1628                 }
1629
1630                 if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)
1631                         ctl_io_error_print(io, inq_data, stderr);
1632                 else {
1633                         printf("%s LUN is now %s\n", scsi_path,
1634                                (command == CTLADM_CMD_STARTUP) ? "online" :
1635                                "offline");
1636                 }
1637         }
1638 bailout:
1639         if (lun_data != NULL)
1640                 free(lun_data);
1641
1642         if (inq_data != NULL)
1643                 free(inq_data);
1644
1645         if (io != NULL)
1646                 ctl_scsi_free_io(io);
1647
1648         return (retval);
1649 }
1650
1651 static int
1652 cctl_sync_cache(int fd, int target, int lun, int iid, int retries,
1653                 int argc, char **argv, char *combinedopt)
1654 {
1655         union ctl_io *io;
1656         struct ctl_id id;
1657         int cdb_size = -1;
1658         int retval;
1659         uint64_t our_lba = 0;
1660         uint32_t our_block_count = 0;
1661         int reladr = 0, immed = 0; 
1662         int c;
1663
1664         id.id = iid;
1665         retval = 0;
1666
1667         io = ctl_scsi_alloc_io(id);
1668         if (io == NULL) {
1669                 warnx("%s: can't allocate memory", __func__);
1670                 return (1);
1671         }
1672
1673         while ((c = getopt(argc, argv, combinedopt)) != -1) {
1674                 switch (c) {
1675                 case 'b':
1676                         our_block_count = strtoul(optarg, NULL, 0);
1677                         break;
1678                 case 'c':
1679                         cdb_size = strtol(optarg, NULL, 0);
1680                         break;
1681                 case 'i':
1682                         immed = 1;
1683                         break;
1684                 case 'l':
1685                         our_lba = strtoull(optarg, NULL, 0);
1686                         break;
1687                 case 'r':
1688                         reladr = 1;
1689                         break;
1690                 default:
1691                         break;
1692                 }
1693         }
1694
1695         if (cdb_size != -1) {
1696                 switch (cdb_size) {
1697                 case 10:
1698                 case 16:
1699                         break;
1700                 default:
1701                         warnx("%s: invalid cdbsize %d, valid sizes are 10 "
1702                               "and 16", __func__, cdb_size);
1703                         retval = 1;
1704                         goto bailout;
1705                         break; /* NOTREACHED */
1706                 }
1707         } else
1708                 cdb_size = 10;
1709
1710         ctl_scsi_sync_cache(/*io*/ io,
1711                             /*immed*/ immed,
1712                             /*reladr*/ reladr,
1713                             /*minimum_cdb_size*/ cdb_size,
1714                             /*starting_lba*/ our_lba,
1715                             /*block_count*/ our_block_count,
1716                             /*tag_type*/ CTL_TAG_SIMPLE,
1717                             /*control*/ 0);
1718
1719         io->io_hdr.nexus.targ_target.id = target;
1720         io->io_hdr.nexus.targ_lun = lun;
1721         io->io_hdr.nexus.initid = id;
1722
1723         if (cctl_do_io(fd, retries, io, __func__) != 0) {
1724                 retval = 1;
1725                 goto bailout;
1726         }
1727
1728         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
1729                 fprintf(stdout, "Cache synchronized successfully\n");
1730         } else
1731                 ctl_io_error_print(io, NULL, stderr);
1732 bailout:
1733         ctl_scsi_free_io(io);
1734
1735         return (retval);
1736 }
1737
1738 static int
1739 cctl_start_stop(int fd, int target, int lun, int iid, int retries, int start,
1740                 int argc, char **argv, char *combinedopt)
1741 {
1742         union ctl_io *io;
1743         struct ctl_id id;
1744         char scsi_path[40];
1745         int immed = 0, onoffline = 0;
1746         int retval, c;
1747
1748         id.id = iid;
1749         retval = 0;
1750
1751         io = ctl_scsi_alloc_io(id);
1752         if (io == NULL) {
1753                 warnx("%s: can't allocate memory", __func__);
1754                 return (1);
1755         }
1756
1757         while ((c = getopt(argc, argv, combinedopt)) != -1) {
1758                 switch (c) {
1759                 case 'i':
1760                         immed = 1;
1761                         break;
1762                 case 'o':
1763                         onoffline = 1;
1764                         break;
1765                 default:
1766                         break;
1767                 }
1768         }
1769         /*
1770          * Use an ordered tag for the stop command, to guarantee that any
1771          * pending I/O will finish before the stop command executes.  This
1772          * would normally be the case anyway, since CTL will basically
1773          * treat the start/stop command as an ordered command with respect
1774          * to any other command except an INQUIRY.  (See ctl_ser_table.c.)
1775          */
1776         ctl_scsi_start_stop(/*io*/ io,
1777                             /*start*/ start,
1778                             /*load_eject*/ 0,
1779                             /*immediate*/ immed,
1780                             /*power_conditions*/ SSS_PC_START_VALID,
1781                             /*onoffline*/ onoffline,
1782                             /*ctl_tag_type*/ start ? CTL_TAG_SIMPLE :
1783                                                      CTL_TAG_ORDERED,
1784                             /*control*/ 0);
1785
1786         io->io_hdr.nexus.targ_target.id = target;
1787         io->io_hdr.nexus.targ_lun = lun;
1788         io->io_hdr.nexus.initid = id;
1789
1790         if (cctl_do_io(fd, retries, io, __func__) != 0) {
1791                 retval = 1;
1792                 goto bailout;
1793         }
1794
1795         ctl_scsi_path_string(io, scsi_path, sizeof(scsi_path));
1796         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
1797                 fprintf(stdout, "%s LUN %s successfully\n", scsi_path,
1798                         (start) ?  "started" : "stopped");
1799         } else
1800                 ctl_io_error_print(io, NULL, stderr);
1801
1802 bailout:
1803         ctl_scsi_free_io(io);
1804
1805         return (retval);
1806 }
1807
1808 static int
1809 cctl_mode_sense(int fd, int target, int lun, int iid, int retries, 
1810                 int argc, char **argv, char *combinedopt)
1811 {
1812         union ctl_io *io;
1813         struct ctl_id id;
1814         uint32_t datalen;
1815         uint8_t *dataptr;
1816         int pc = -1, cdbsize, retval, dbd = 0, subpage = -1;
1817         int list = 0;
1818         int page_code = -1;
1819         int c;
1820
1821         id.id = iid;
1822         cdbsize = 0;
1823         retval = 0;
1824         dataptr = NULL;
1825
1826         io = ctl_scsi_alloc_io(id);
1827         if (io == NULL) {
1828                 warn("%s: can't allocate memory", __func__);
1829                 return (1);
1830         }
1831
1832         while ((c = getopt(argc, argv, combinedopt)) != -1) {
1833                 switch (c) {
1834                 case 'P':
1835                         pc = strtoul(optarg, NULL, 0);
1836                         break;
1837                 case 'S':
1838                         subpage = strtoul(optarg, NULL, 0);
1839                         break;
1840                 case 'd':
1841                         dbd = 1;
1842                         break;
1843                 case 'l':
1844                         list = 1;
1845                         break;
1846                 case 'm':
1847                         page_code = strtoul(optarg, NULL, 0);
1848                         break;
1849                 case 'c':
1850                         cdbsize = strtol(optarg, NULL, 0);
1851                         break;
1852                 default:
1853                         break;
1854                 }
1855         }
1856
1857         if (((list == 0) && (page_code == -1))
1858          || ((list != 0) && (page_code != -1))) {
1859                 warnx("%s: you must specify either a page code (-m) or -l",
1860                       __func__);
1861                 retval = 1;
1862                 goto bailout;
1863         }
1864
1865         if ((page_code != -1)
1866          && ((page_code > SMS_ALL_PAGES_PAGE)
1867           || (page_code < 0))) {
1868                 warnx("%s: page code %d is out of range", __func__,
1869                       page_code);
1870                 retval = 1;
1871                 goto bailout;
1872         }
1873
1874         if (list == 1) {
1875                 page_code = SMS_ALL_PAGES_PAGE;
1876                 if (pc != -1) {
1877                         warnx("%s: arg -P makes no sense with -l",
1878                               __func__);
1879                         retval = 1;
1880                         goto bailout;
1881                 }
1882                 if (subpage != -1) {
1883                         warnx("%s: arg -S makes no sense with -l", __func__);
1884                         retval = 1;
1885                         goto bailout;
1886                 }
1887         }
1888
1889         if (pc == -1)
1890                 pc = SMS_PAGE_CTRL_CURRENT;
1891         else {
1892                 if ((pc > 3)
1893                  || (pc < 0)) {
1894                         warnx("%s: page control value %d is out of range: 0-3",
1895                               __func__, pc);
1896                         retval = 1;
1897                         goto bailout;
1898                 }
1899         }
1900
1901
1902         if ((subpage != -1)
1903          && ((subpage > 255)
1904           || (subpage < 0))) {
1905                 warnx("%s: subpage code %d is out of range: 0-255", __func__,
1906                       subpage);
1907                 retval = 1;
1908                 goto bailout;
1909         }
1910         if (cdbsize != 0) {
1911                 switch (cdbsize) {
1912                 case 6:
1913                 case 10:
1914                         break;
1915                 default:
1916                         warnx("%s: invalid cdbsize %d, valid sizes are 6 "
1917                               "and 10", __func__, cdbsize);
1918                         retval = 1;
1919                         goto bailout;
1920                         break;
1921                 }
1922         } else
1923                 cdbsize = 6;
1924
1925         if (subpage == -1)
1926                 subpage = 0;
1927
1928         if (cdbsize == 6)
1929                 datalen = 255;
1930         else
1931                 datalen = 65535;
1932
1933         dataptr = (uint8_t *)malloc(datalen);
1934         if (dataptr == NULL) {
1935                 warn("%s: can't allocate %d bytes", __func__, datalen);
1936                 retval = 1;
1937                 goto bailout;
1938         }
1939
1940         memset(dataptr, 0, datalen);
1941
1942         ctl_scsi_mode_sense(io,
1943                             /*data_ptr*/ dataptr,
1944                             /*data_len*/ datalen,
1945                             /*dbd*/ dbd,
1946                             /*llbaa*/ 0,
1947                             /*page_code*/ page_code,
1948                             /*pc*/ pc << 6,
1949                             /*subpage*/ subpage,
1950                             /*minimum_cdb_size*/ cdbsize,
1951                             /*tag_type*/ CTL_TAG_SIMPLE,
1952                             /*control*/ 0);
1953
1954         io->io_hdr.nexus.targ_target.id = target;
1955         io->io_hdr.nexus.targ_lun = lun;
1956         io->io_hdr.nexus.initid = id;
1957
1958         if (cctl_do_io(fd, retries, io, __func__) != 0) {
1959                 retval = 1;
1960                 goto bailout;
1961         }
1962
1963         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
1964                 int pages_len, used_len;
1965                 uint32_t returned_len;
1966                 uint8_t *ndataptr;
1967
1968                 if (io->scsiio.cdb[0] == MODE_SENSE_6) {
1969                         struct scsi_mode_hdr_6 *hdr6;
1970                         int bdlen;
1971
1972                         hdr6 = (struct scsi_mode_hdr_6 *)dataptr;
1973
1974                         returned_len = hdr6->datalen + 1;
1975                         bdlen = hdr6->block_descr_len;
1976
1977                         ndataptr = (uint8_t *)((uint8_t *)&hdr6[1] + bdlen);
1978                 } else {
1979                         struct scsi_mode_hdr_10 *hdr10;
1980                         int bdlen;
1981
1982                         hdr10 = (struct scsi_mode_hdr_10 *)dataptr;
1983
1984                         returned_len = scsi_2btoul(hdr10->datalen) + 2;
1985                         bdlen = scsi_2btoul(hdr10->block_descr_len);
1986
1987                         ndataptr = (uint8_t *)((uint8_t *)&hdr10[1] + bdlen);
1988                 }
1989                 /* just in case they can give us more than we allocated for */
1990                 returned_len = min(returned_len, datalen);
1991                 pages_len = returned_len - (ndataptr - dataptr);
1992 #if 0
1993                 fprintf(stdout, "returned_len = %d, pages_len = %d\n",
1994                         returned_len, pages_len);
1995 #endif
1996                 if (list == 1) {
1997                         fprintf(stdout, "Supported mode pages:\n");
1998                         for (used_len = 0; used_len < pages_len;) {
1999                                 struct scsi_mode_page_header *header;
2000
2001                                 header = (struct scsi_mode_page_header *)
2002                                         &ndataptr[used_len];
2003                                 fprintf(stdout, "%d\n", header->page_code);
2004                                 used_len += header->page_length + 2;
2005                         }
2006                 } else {
2007                         for (used_len = 0; used_len < pages_len; used_len++) {
2008                                 fprintf(stdout, "0x%x ", ndataptr[used_len]);
2009                                 if (((used_len+1) % 16) == 0)
2010                                         fprintf(stdout, "\n");
2011                         }
2012                         fprintf(stdout, "\n");
2013                 }
2014         } else
2015                 ctl_io_error_print(io, NULL, stderr);
2016 bailout:
2017
2018         ctl_scsi_free_io(io);
2019
2020         if (dataptr != NULL)
2021                 free(dataptr);
2022
2023         return (retval);
2024 }
2025
2026 static int
2027 cctl_read_capacity(int fd, int target, int lun, int iid, int retries, 
2028                    int argc, char **argv, char *combinedopt)
2029 {
2030         union ctl_io *io;
2031         struct ctl_id id;
2032         struct scsi_read_capacity_data *data;
2033         struct scsi_read_capacity_data_long *longdata;
2034         int cdbsize = -1, retval;
2035         uint8_t *dataptr;
2036         int c;
2037
2038         cdbsize = 10;
2039         dataptr = NULL;
2040         retval = 0;
2041         id.id = iid;
2042
2043         io = ctl_scsi_alloc_io(id);
2044         if (io == NULL) {
2045                 warn("%s: can't allocate memory\n", __func__);
2046                 return (1);
2047         }
2048
2049         while ((c = getopt(argc, argv, combinedopt)) != -1) {
2050                 switch (c) {
2051                 case 'c':
2052                         cdbsize = strtol(optarg, NULL, 0);
2053                         break;
2054                 default:
2055                         break;
2056                 }
2057         }
2058         if (cdbsize != -1) {
2059                 switch (cdbsize) {
2060                 case 10:
2061                 case 16:
2062                         break;
2063                 default:
2064                         warnx("%s: invalid cdbsize %d, valid sizes are 10 "
2065                               "and 16", __func__, cdbsize);
2066                         retval = 1;
2067                         goto bailout;
2068                         break; /* NOTREACHED */
2069                 }
2070         } else
2071                 cdbsize = 10;
2072
2073         dataptr = (uint8_t *)malloc(sizeof(*longdata));
2074         if (dataptr == NULL) {
2075                 warn("%s: can't allocate %zd bytes\n", __func__,
2076                      sizeof(*longdata));
2077                 retval = 1;
2078                 goto bailout;
2079         }
2080         memset(dataptr, 0, sizeof(*longdata));
2081
2082 retry:
2083
2084         switch (cdbsize) {
2085         case 10:
2086                 ctl_scsi_read_capacity(io,
2087                                        /*data_ptr*/ dataptr,
2088                                        /*data_len*/ sizeof(*longdata),
2089                                        /*addr*/ 0,
2090                                        /*reladr*/ 0,
2091                                        /*pmi*/ 0,
2092                                        /*tag_type*/ CTL_TAG_SIMPLE,
2093                                        /*control*/ 0);
2094                 break;
2095         case 16:
2096                 ctl_scsi_read_capacity_16(io,
2097                                           /*data_ptr*/ dataptr,
2098                                           /*data_len*/ sizeof(*longdata),
2099                                           /*addr*/ 0,
2100                                           /*reladr*/ 0,
2101                                           /*pmi*/ 0,
2102                                           /*tag_type*/ CTL_TAG_SIMPLE,
2103                                           /*control*/ 0);
2104                 break;
2105         }
2106
2107         io->io_hdr.nexus.initid = id;
2108         io->io_hdr.nexus.targ_target.id = target;
2109         io->io_hdr.nexus.targ_lun = lun;
2110
2111         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2112                 retval = 1;
2113                 goto bailout;
2114         }
2115
2116         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2117                 uint64_t maxlba;
2118                 uint32_t blocksize;
2119
2120                 if (cdbsize == 10) {
2121
2122                         data = (struct scsi_read_capacity_data *)dataptr;
2123
2124                         maxlba = scsi_4btoul(data->addr);
2125                         blocksize = scsi_4btoul(data->length);
2126
2127                         if (maxlba == 0xffffffff) {
2128                                 cdbsize = 16;
2129                                 goto retry;
2130                         }
2131                 } else {
2132                         longdata=(struct scsi_read_capacity_data_long *)dataptr;
2133
2134                         maxlba = scsi_8btou64(longdata->addr);
2135                         blocksize = scsi_4btoul(longdata->length);
2136                 }
2137
2138                 fprintf(stdout, "Disk Capacity: %ju, Blocksize: %d\n",
2139                         (uintmax_t)maxlba, blocksize);
2140         } else {
2141                 ctl_io_error_print(io, NULL, stderr);
2142         }
2143 bailout:
2144         ctl_scsi_free_io(io);
2145
2146         if (dataptr != NULL)
2147                 free(dataptr);
2148
2149         return (retval);
2150 }
2151
2152 static int
2153 cctl_read_write(int fd, int target, int lun, int iid, int retries,
2154                 int argc, char **argv, char *combinedopt,
2155                 ctladm_cmdfunction command)
2156 {
2157         union ctl_io *io;
2158         struct ctl_id id;
2159         int file_fd, do_stdio;
2160         int cdbsize = -1, databytes;
2161         uint8_t *dataptr;
2162         char *filename = NULL;
2163         int datalen = -1, blocksize = -1;
2164         uint64_t lba = 0;
2165         int lba_set = 0;
2166         int retval;
2167         int c;
2168
2169         retval = 0;
2170         do_stdio = 0;
2171         dataptr = NULL;
2172         file_fd = -1;
2173         id.id = iid;
2174
2175         io = ctl_scsi_alloc_io(id);
2176         if (io == NULL) {
2177                 warn("%s: can't allocate memory\n", __func__);
2178                 return (1);
2179         }
2180
2181         while ((c = getopt(argc, argv, combinedopt)) != -1) {
2182                 switch (c) {
2183                 case 'N':
2184                         io->io_hdr.flags |= CTL_FLAG_NO_DATAMOVE;
2185                         break;
2186                 case 'b':
2187                         blocksize = strtoul(optarg, NULL, 0);
2188                         break;
2189                 case 'c':
2190                         cdbsize = strtoul(optarg, NULL, 0);
2191                         break;
2192                 case 'd':
2193                         datalen = strtoul(optarg, NULL, 0);
2194                         break;
2195                 case 'f':
2196                         filename = strdup(optarg);
2197                         break;
2198                 case 'l':
2199                         lba = strtoull(optarg, NULL, 0);
2200                         lba_set = 1;
2201                         break;
2202                 default:
2203                         break;
2204                 }
2205         }
2206         if (filename == NULL) {
2207                 warnx("%s: you must supply a filename using -f", __func__);
2208                 retval = 1;
2209                 goto bailout;
2210         }
2211
2212         if (datalen == -1) {
2213                 warnx("%s: you must specify the data length with -d", __func__);
2214                 retval = 1;
2215                 goto bailout;
2216         }
2217
2218         if (lba_set == 0) {
2219                 warnx("%s: you must specify the LBA with -l", __func__);
2220                 retval = 1;
2221                 goto bailout;
2222         }
2223
2224         if (blocksize == -1) {
2225                 warnx("%s: you must specify the blocksize with -b", __func__);
2226                 retval = 1;
2227                 goto bailout;
2228         }
2229
2230         if (cdbsize != -1) {
2231                 switch (cdbsize) {
2232                 case 6:
2233                 case 10:
2234                 case 12:
2235                 case 16:
2236                         break;
2237                 default:
2238                         warnx("%s: invalid cdbsize %d, valid sizes are 6, "
2239                               "10, 12 or 16", __func__, cdbsize);
2240                         retval = 1;
2241                         goto bailout;
2242                         break; /* NOTREACHED */
2243                 }
2244         } else
2245                 cdbsize = 6;
2246
2247         databytes = datalen * blocksize;
2248         dataptr = (uint8_t *)malloc(databytes);
2249
2250         if (dataptr == NULL) {
2251                 warn("%s: can't allocate %d bytes\n", __func__, databytes);
2252                 retval = 1;
2253                 goto bailout;
2254         }
2255         if (strcmp(filename, "-") == 0) {
2256                 if (command == CTLADM_CMD_READ)
2257                         file_fd = STDOUT_FILENO;
2258                 else
2259                         file_fd = STDIN_FILENO;
2260                 do_stdio = 1;
2261         } else {
2262                 file_fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
2263                 if (file_fd == -1) {
2264                         warn("%s: can't open file %s", __func__, filename);
2265                         retval = 1;
2266                         goto bailout;
2267                 }
2268         }
2269
2270         memset(dataptr, 0, databytes);
2271
2272         if (command == CTLADM_CMD_WRITE) {
2273                 int bytes_read;
2274
2275                 bytes_read = read(file_fd, dataptr, databytes);
2276                 if (bytes_read == -1) {
2277                         warn("%s: error reading file %s", __func__, filename);
2278                         retval = 1;
2279                         goto bailout;
2280                 }
2281                 if (bytes_read != databytes) {
2282                         warnx("%s: only read %d bytes from file %s",
2283                               __func__, bytes_read, filename);
2284                         retval = 1;
2285                         goto bailout;
2286                 }
2287         }
2288         ctl_scsi_read_write(io,
2289                             /*data_ptr*/ dataptr,
2290                             /*data_len*/ databytes,
2291                             /*read_op*/ (command == CTLADM_CMD_READ) ? 1 : 0,
2292                             /*byte2*/ 0,
2293                             /*minimum_cdb_size*/ cdbsize,
2294                             /*lba*/ lba,
2295                             /*num_blocks*/ datalen,
2296                             /*tag_type*/ CTL_TAG_SIMPLE,
2297                             /*control*/ 0);
2298
2299         io->io_hdr.nexus.targ_target.id = target;
2300         io->io_hdr.nexus.targ_lun = lun;
2301         io->io_hdr.nexus.initid = id;
2302
2303         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2304                 retval = 1;
2305                 goto bailout;
2306         }
2307
2308         if (((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS)
2309          && (command == CTLADM_CMD_READ)) {
2310                 int bytes_written;
2311
2312                 bytes_written = write(file_fd, dataptr, databytes);
2313                 if (bytes_written == -1) {
2314                         warn("%s: can't write to %s", __func__, filename);
2315                         goto bailout;
2316                 }
2317         } else if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)
2318                 ctl_io_error_print(io, NULL, stderr);
2319
2320
2321 bailout:
2322
2323         ctl_scsi_free_io(io);
2324
2325         if (dataptr != NULL)
2326                 free(dataptr);
2327
2328         if ((do_stdio == 0)
2329          && (file_fd != -1))
2330                 close(file_fd);
2331
2332         return (retval);
2333 }
2334
2335 static int
2336 cctl_get_luns(int fd, int target, int lun, int iid, int retries, struct
2337               scsi_report_luns_data **lun_data, uint32_t *num_luns)
2338 {
2339         union ctl_io *io;
2340         struct ctl_id id;
2341         uint32_t nluns;
2342         int lun_datalen;
2343         int retval;
2344
2345         retval = 0;
2346         id.id = iid;
2347
2348         io = ctl_scsi_alloc_io(id);
2349         if (io == NULL) {
2350                 warnx("%s: can't allocate memory", __func__);
2351                 return (1);
2352         }
2353
2354         /*
2355          * lun_data includes space for 1 lun, allocate space for 4 initially.
2356          * If that isn't enough, we'll allocate more.
2357          */
2358         nluns = 4;
2359 retry:
2360         lun_datalen = sizeof(*lun_data) +
2361                 (nluns * sizeof(struct scsi_report_luns_lundata));
2362         *lun_data = malloc(lun_datalen);
2363
2364         if (*lun_data == NULL) {
2365                 warnx("%s: can't allocate memory", __func__);
2366                 ctl_scsi_free_io(io);
2367                 return (1);
2368         }
2369
2370         ctl_scsi_report_luns(io,
2371                              /*data_ptr*/ (uint8_t *)*lun_data,
2372                              /*data_len*/ lun_datalen,
2373                              /*select_report*/ RPL_REPORT_ALL,
2374                              /*tag_type*/ CTL_TAG_SIMPLE,
2375                              /*control*/ 0);
2376
2377         io->io_hdr.nexus.initid = id;
2378         io->io_hdr.nexus.targ_target.id = target;
2379         io->io_hdr.nexus.targ_lun = lun;
2380
2381         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2382                 retval = 1;
2383                 goto bailout;
2384         }
2385
2386         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2387                 uint32_t returned_len, returned_luns;
2388
2389                 returned_len = scsi_4btoul((*lun_data)->length);
2390                 returned_luns = returned_len / 8;
2391                 if (returned_luns > nluns) {
2392                         nluns = returned_luns;
2393                         free(*lun_data);
2394                         goto retry;
2395                 }
2396                 /* These should be the same */
2397                 *num_luns = MIN(returned_luns, nluns);
2398         } else {
2399                 ctl_io_error_print(io, NULL, stderr);
2400                 retval = 1;
2401         }
2402 bailout:
2403         ctl_scsi_free_io(io);
2404
2405         return (retval);
2406 }
2407
2408 static int
2409 cctl_report_luns(int fd, int target, int lun, int iid, int retries)
2410 {
2411         struct scsi_report_luns_data *lun_data;
2412         uint32_t num_luns, i;
2413         int retval;
2414
2415         lun_data = NULL;
2416
2417         if ((retval = cctl_get_luns(fd, target, lun, iid, retries, &lun_data,
2418                                    &num_luns)) != 0)
2419                 goto bailout;
2420
2421         fprintf(stdout, "%u LUNs returned\n", num_luns);
2422         for (i = 0; i < num_luns; i++) {
2423                 int lun_val;
2424
2425                 /*
2426                  * XXX KDM figure out a way to share this code with
2427                  * cctl_lunlist()?
2428                  */
2429                 switch (lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) {
2430                 case RPL_LUNDATA_ATYP_PERIPH:
2431                         lun_val = lun_data->luns[i].lundata[1];
2432                         break;
2433                 case RPL_LUNDATA_ATYP_FLAT:
2434                         lun_val = (lun_data->luns[i].lundata[0] &
2435                                 RPL_LUNDATA_FLAT_LUN_MASK) |
2436                                 (lun_data->luns[i].lundata[1] <<
2437                                 RPL_LUNDATA_FLAT_LUN_BITS);
2438                         break;
2439                 case RPL_LUNDATA_ATYP_LUN:
2440                 case RPL_LUNDATA_ATYP_EXTLUN:
2441                 default:
2442                         fprintf(stdout, "Unsupported LUN format %d\n",
2443                                 lun_data->luns[i].lundata[0] &
2444                                 RPL_LUNDATA_ATYP_MASK);
2445                         lun_val = -1;
2446                         break;
2447                 }
2448                 if (lun_val == -1)
2449                         continue;
2450
2451                 fprintf(stdout, "%d\n", lun_val);
2452         }
2453
2454 bailout:
2455         if (lun_data != NULL)
2456                 free(lun_data);
2457
2458         return (retval);
2459 }
2460
2461 static int
2462 cctl_tur(int fd, int target, int lun, int iid, int retries)
2463 {
2464         union ctl_io *io;
2465         struct ctl_id id;
2466
2467         id.id = iid;
2468
2469         io = ctl_scsi_alloc_io(id);
2470         if (io == NULL) {
2471                 fprintf(stderr, "can't allocate memory\n");
2472                 return (1);
2473         }
2474
2475         ctl_scsi_tur(io,
2476                      /* tag_type */ CTL_TAG_SIMPLE,
2477                      /* control */ 0);
2478
2479         io->io_hdr.nexus.targ_target.id = target;
2480         io->io_hdr.nexus.targ_lun = lun;
2481         io->io_hdr.nexus.initid = id;
2482
2483         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2484                 ctl_scsi_free_io(io);
2485                 return (1);
2486         }
2487
2488         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS)
2489                 fprintf(stdout, "Unit is ready\n");
2490         else
2491                 ctl_io_error_print(io, NULL, stderr);
2492
2493         return (0);
2494 }
2495
2496 static int
2497 cctl_get_inquiry(int fd, int target, int lun, int iid, int retries, 
2498                  char *path_str, int path_len,
2499                  struct scsi_inquiry_data *inq_data)
2500 {
2501         union ctl_io *io;
2502         struct ctl_id id;
2503         int retval;
2504
2505         retval = 0;
2506
2507         id.id = iid;
2508
2509         io = ctl_scsi_alloc_io(id);
2510         if (io == NULL) {
2511                 warnx("cctl_inquiry: can't allocate memory\n");
2512                 return (1);
2513         }
2514
2515         ctl_scsi_inquiry(/*io*/ io,
2516                          /*data_ptr*/ (uint8_t *)inq_data,
2517                          /*data_len*/ sizeof(*inq_data),
2518                          /*byte2*/ 0,
2519                          /*page_code*/ 0,
2520                          /*tag_type*/ CTL_TAG_SIMPLE,
2521                          /*control*/ 0);
2522
2523         io->io_hdr.nexus.targ_target.id = target;
2524         io->io_hdr.nexus.targ_lun = lun;
2525         io->io_hdr.nexus.initid = id;
2526
2527         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2528                 retval = 1;
2529                 goto bailout;
2530         }
2531
2532         if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS) {
2533                 retval = 1;
2534                 ctl_io_error_print(io, NULL, stderr);
2535         } else if (path_str != NULL)
2536                 ctl_scsi_path_string(io, path_str, path_len);
2537
2538 bailout:
2539         ctl_scsi_free_io(io);
2540
2541         return (retval);
2542 }
2543
2544 static int
2545 cctl_inquiry(int fd, int target, int lun, int iid, int retries)
2546 {
2547         struct scsi_inquiry_data *inq_data;
2548         char scsi_path[40];
2549         int retval;
2550
2551         retval = 0;
2552
2553         inq_data = malloc(sizeof(*inq_data));
2554         if (inq_data == NULL) {
2555                 warnx("%s: can't allocate inquiry data", __func__);
2556                 retval = 1;
2557                 goto bailout;
2558         }
2559
2560         if ((retval = cctl_get_inquiry(fd, target, lun, iid, retries, scsi_path,
2561                                        sizeof(scsi_path), inq_data)) != 0)
2562                 goto bailout;
2563
2564         printf("%s", scsi_path);
2565         scsi_print_inquiry(inq_data);
2566
2567 bailout:
2568         if (inq_data != NULL)
2569                 free(inq_data);
2570
2571         return (retval);
2572 }
2573
2574 static int
2575 cctl_req_sense(int fd, int target, int lun, int iid, int retries)
2576 {
2577         union ctl_io *io;
2578         struct scsi_sense_data *sense_data;
2579         struct ctl_id id;
2580         int retval;
2581
2582         retval = 0;
2583
2584         id.id = iid;
2585
2586         io = ctl_scsi_alloc_io(id);
2587         if (io == NULL) {
2588                 warnx("cctl_req_sense: can't allocate memory\n");
2589                 return (1);
2590         }
2591         sense_data = malloc(sizeof(*sense_data));
2592         memset(sense_data, 0, sizeof(*sense_data));
2593
2594         ctl_scsi_request_sense(/*io*/ io,
2595                                /*data_ptr*/ (uint8_t *)sense_data,
2596                                /*data_len*/ sizeof(*sense_data),
2597                                /*byte2*/ 0,
2598                                /*tag_type*/ CTL_TAG_SIMPLE,
2599                                /*control*/ 0);
2600
2601         io->io_hdr.nexus.targ_target.id = target;
2602         io->io_hdr.nexus.targ_lun = lun;
2603         io->io_hdr.nexus.initid = id;
2604
2605         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2606                 retval = 1;
2607                 goto bailout;
2608         }
2609
2610         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2611                 bcopy(sense_data, &io->scsiio.sense_data, sizeof(*sense_data));
2612                 io->scsiio.sense_len = sizeof(*sense_data);
2613                 ctl_scsi_sense_print(&io->scsiio, NULL, stdout);
2614         } else
2615                 ctl_io_error_print(io, NULL, stderr);
2616
2617 bailout:
2618
2619         ctl_scsi_free_io(io);
2620         free(sense_data);
2621
2622         return (retval);
2623 }
2624
2625 static int
2626 cctl_report_target_port_group(int fd, int target, int lun, int initiator)
2627 {
2628         union ctl_io *io;
2629         struct ctl_id id;
2630         uint32_t datalen;
2631         uint8_t *dataptr;
2632         int retval;
2633
2634         id.id = initiator;
2635         dataptr = NULL;
2636         retval = 0;
2637
2638         io = ctl_scsi_alloc_io(id);
2639         if (io == NULL) {
2640                 warn("%s: can't allocate memory", __func__);
2641                 return (1);
2642         }
2643
2644         datalen = 64;
2645         dataptr = (uint8_t *)malloc(datalen);
2646         if (dataptr == NULL) {
2647                 warn("%s: can't allocate %d bytes", __func__, datalen);
2648                 retval = 1;
2649                 goto bailout;
2650         }
2651
2652         memset(dataptr, 0, datalen);
2653
2654         ctl_scsi_maintenance_in(/*io*/ io,
2655                                 /*data_ptr*/ dataptr,
2656                                 /*data_len*/ datalen,
2657                                 /*action*/ SA_RPRT_TRGT_GRP,
2658                                 /*tag_type*/ CTL_TAG_SIMPLE,
2659                                 /*control*/ 0);
2660
2661         io->io_hdr.nexus.targ_target.id = target;
2662         io->io_hdr.nexus.targ_lun = lun;
2663         io->io_hdr.nexus.initid = id;
2664
2665         if (cctl_do_io(fd, 0, io, __func__) != 0) {
2666                 retval = 1;
2667                 goto bailout;
2668         }
2669
2670         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2671                 int returned_len, used_len;
2672
2673                 returned_len = scsi_4btoul(&dataptr[0]) + 4;
2674
2675                 for (used_len = 0; used_len < returned_len; used_len++) {
2676                         fprintf(stdout, "0x%02x ", dataptr[used_len]);
2677                         if (((used_len+1) % 8) == 0)
2678                                 fprintf(stdout, "\n");
2679                 }
2680                 fprintf(stdout, "\n");
2681         } else
2682                 ctl_io_error_print(io, NULL, stderr);
2683
2684 bailout:
2685         ctl_scsi_free_io(io);
2686
2687         if (dataptr != NULL)
2688                 free(dataptr);
2689
2690         return (retval);
2691 }
2692
2693 static int
2694 cctl_inquiry_vpd_devid(int fd, int target, int lun, int initiator)
2695 {
2696         union ctl_io *io;
2697         struct ctl_id id;
2698         uint32_t datalen;
2699         uint8_t *dataptr;
2700         int retval;
2701
2702         id.id = initiator;
2703         retval = 0;
2704         dataptr = NULL;
2705
2706         io = ctl_scsi_alloc_io(id);
2707         if (io == NULL) {
2708                 warn("%s: can't allocate memory", __func__);
2709                 return (1);
2710         }
2711
2712         datalen = 256;
2713         dataptr = (uint8_t *)malloc(datalen);
2714         if (dataptr == NULL) {
2715                 warn("%s: can't allocate %d bytes", __func__, datalen);
2716                 retval = 1;
2717                 goto bailout;
2718         }
2719
2720         memset(dataptr, 0, datalen);
2721
2722         ctl_scsi_inquiry(/*io*/        io,
2723                          /*data_ptr*/  dataptr,
2724                          /*data_len*/  datalen,
2725                          /*byte2*/     SI_EVPD,
2726                          /*page_code*/ SVPD_DEVICE_ID,
2727                          /*tag_type*/  CTL_TAG_SIMPLE,
2728                          /*control*/   0);
2729
2730         io->io_hdr.nexus.targ_target.id = target;
2731         io->io_hdr.nexus.targ_lun = lun;
2732         io->io_hdr.nexus.initid = id;
2733
2734         if (cctl_do_io(fd, 0, io, __func__) != 0) {
2735                 retval = 1;
2736                 goto bailout;
2737         }
2738
2739         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2740                 int returned_len, used_len;
2741
2742                 returned_len = scsi_2btoul(&dataptr[2]) + 4;
2743
2744                 for (used_len = 0; used_len < returned_len; used_len++) {
2745                         fprintf(stdout, "0x%02x ", dataptr[used_len]);
2746                         if (((used_len+1) % 8) == 0)
2747                                 fprintf(stdout, "\n");
2748                 }
2749                 fprintf(stdout, "\n");
2750         } else
2751                 ctl_io_error_print(io, NULL, stderr);
2752
2753 bailout:
2754         ctl_scsi_free_io(io);
2755
2756         if (dataptr != NULL)
2757                 free(dataptr);
2758
2759         return (retval);
2760 }
2761
2762 static int
2763 cctl_persistent_reserve_in(int fd, int target, int lun, int initiator, 
2764                            int argc, char **argv, char *combinedopt,
2765                            int retry_count)
2766 {
2767         union ctl_io *io;
2768         struct ctl_id id;
2769         uint32_t datalen;
2770         uint8_t *dataptr;
2771         int action = -1;
2772         int retval;
2773         int c;
2774
2775         id.id = initiator;
2776         retval = 0;
2777         dataptr = NULL;
2778
2779         io = ctl_scsi_alloc_io(id);
2780         if (io == NULL) {
2781                 warn("%s: can't allocate memory", __func__);
2782                 return (1);
2783         }
2784
2785         while ((c = getopt(argc, argv, combinedopt)) != -1) {
2786                 switch (c) {
2787                 case 'a':
2788                         action = strtol(optarg, NULL, 0);
2789                         break;
2790                 default:
2791                         break;
2792                 }
2793         }
2794
2795         if (action < 0 || action > 2) {
2796                 warn("action must be specified and in the range: 0-2");
2797                 retval = 1;
2798                 goto bailout;
2799         }
2800
2801
2802         datalen = 256;
2803         dataptr = (uint8_t *)malloc(datalen);
2804         if (dataptr == NULL) {
2805                 warn("%s: can't allocate %d bytes", __func__, datalen);
2806                 retval = 1;
2807                 goto bailout;
2808         }
2809
2810         memset(dataptr, 0, datalen);
2811
2812         ctl_scsi_persistent_res_in(io,
2813                                    /*data_ptr*/ dataptr,
2814                                    /*data_len*/ datalen,
2815                                    /*action*/   action,
2816                                    /*tag_type*/ CTL_TAG_SIMPLE,
2817                                    /*control*/  0);
2818
2819         io->io_hdr.nexus.targ_target.id = target;
2820         io->io_hdr.nexus.targ_lun = lun;
2821         io->io_hdr.nexus.initid = id;
2822
2823         if (cctl_do_io(fd, retry_count, io, __func__) != 0) {
2824                 retval = 1;
2825                 goto bailout;
2826         }
2827
2828         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2829                 int returned_len, used_len;
2830
2831                 returned_len = 0;
2832
2833                 switch (action) {
2834                 case 0:
2835                         returned_len = scsi_4btoul(&dataptr[4]) + 8;
2836                         returned_len = min(returned_len, 256);
2837                         break;
2838                 case 1:
2839                         returned_len = scsi_4btoul(&dataptr[4]) + 8;
2840                         break;
2841                 case 2:
2842                         returned_len = 8;
2843                         break;
2844                 default:
2845                         warnx("%s: invalid action %d", __func__, action);
2846                         goto bailout;
2847                         break; /* NOTREACHED */
2848                 }
2849
2850                 for (used_len = 0; used_len < returned_len; used_len++) {
2851                         fprintf(stdout, "0x%02x ", dataptr[used_len]);
2852                         if (((used_len+1) % 8) == 0)
2853                                 fprintf(stdout, "\n");
2854                 }
2855                 fprintf(stdout, "\n");
2856         } else
2857                 ctl_io_error_print(io, NULL, stderr);
2858
2859 bailout:
2860         ctl_scsi_free_io(io);
2861
2862         if (dataptr != NULL)
2863                 free(dataptr);
2864
2865         return (retval);
2866 }
2867
2868 static int
2869 cctl_persistent_reserve_out(int fd, int target, int lun, int initiator, 
2870                             int argc, char **argv, char *combinedopt, 
2871                             int retry_count)
2872 {
2873         union ctl_io *io;
2874         struct ctl_id id;
2875         uint32_t datalen;
2876         uint64_t key = 0, sa_key = 0;
2877         int action = -1, restype = -1;
2878         uint8_t *dataptr;
2879         int retval;
2880         int c;
2881
2882         id.id = initiator;
2883         retval = 0;
2884         dataptr = NULL;
2885
2886         io = ctl_scsi_alloc_io(id);
2887         if (io == NULL) {
2888                 warn("%s: can't allocate memory", __func__);
2889                 return (1);
2890         }
2891
2892         while ((c = getopt(argc, argv, combinedopt)) != -1) {
2893                 switch (c) {
2894                 case 'a':
2895                         action = strtol(optarg, NULL, 0);
2896                         break;
2897                 case 'k':
2898                         key = strtoull(optarg, NULL, 0);
2899                         break;
2900                 case 'r':
2901                         restype = strtol(optarg, NULL, 0);
2902                         break;
2903                 case 's':
2904                         sa_key = strtoull(optarg, NULL, 0);
2905                         break;
2906                 default:
2907                         break;
2908                 }
2909         }
2910         if (action < 0 || action > 5) {
2911                 warn("action must be specified and in the range: 0-5");
2912                 retval = 1;
2913                 goto bailout;
2914         }
2915
2916         if (restype < 0 || restype > 5) {
2917                 if (action != 0 && action != 5 && action != 3) {
2918                         warn("'restype' must specified and in the range: 0-5");
2919                         retval = 1;
2920                         goto bailout;
2921                 }
2922         }
2923
2924         datalen = 24;
2925         dataptr = (uint8_t *)malloc(datalen);
2926         if (dataptr == NULL) {
2927                 warn("%s: can't allocate %d bytes", __func__, datalen);
2928                 retval = 1;
2929                 goto bailout;
2930         }
2931
2932         memset(dataptr, 0, datalen);
2933
2934         ctl_scsi_persistent_res_out(io,
2935                                     /*data_ptr*/ dataptr,
2936                                     /*data_len*/ datalen,
2937                                     /*action*/   action,
2938                                     /*type*/     restype,
2939                                     /*key*/      key,
2940                                     /*sa key*/   sa_key,
2941                                     /*tag_type*/ CTL_TAG_SIMPLE,
2942                                     /*control*/  0);
2943
2944         io->io_hdr.nexus.targ_target.id = target;
2945         io->io_hdr.nexus.targ_lun = lun;
2946         io->io_hdr.nexus.initid = id;
2947
2948         if (cctl_do_io(fd, retry_count, io, __func__) != 0) {
2949                 retval = 1;
2950                 goto bailout;
2951         }
2952         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2953                 char scsi_path[40];
2954                 ctl_scsi_path_string(io, scsi_path, sizeof(scsi_path));
2955                 fprintf( stdout, "%sPERSISTENT RESERVE OUT executed "
2956                         "successfully\n", scsi_path);
2957         } else
2958                 ctl_io_error_print(io, NULL, stderr);
2959
2960 bailout:
2961         ctl_scsi_free_io(io);
2962
2963         if (dataptr != NULL)
2964                 free(dataptr);
2965
2966         return (retval);
2967 }
2968
2969 struct cctl_req_option {
2970         char                         *name;
2971         int                           namelen;
2972         char                         *value;
2973         int                           vallen;
2974         STAILQ_ENTRY(cctl_req_option) links;
2975 };
2976
2977 static int
2978 cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
2979 {
2980         struct ctl_lun_req req;
2981         int device_type = -1;
2982         uint64_t lun_size = 0;
2983         uint32_t blocksize = 0, req_lun_id = 0;
2984         char *serial_num = NULL;
2985         char *device_id = NULL;
2986         int lun_size_set = 0, blocksize_set = 0, lun_id_set = 0;
2987         char *backend_name = NULL;
2988         STAILQ_HEAD(, cctl_req_option) option_list;
2989         int num_options = 0;
2990         int retval = 0, c;
2991
2992         STAILQ_INIT(&option_list);
2993
2994         while ((c = getopt(argc, argv, combinedopt)) != -1) {
2995                 switch (c) {
2996                 case 'b':
2997                         backend_name = strdup(optarg);
2998                         break;
2999                 case 'B':
3000                         blocksize = strtoul(optarg, NULL, 0);
3001                         blocksize_set = 1;
3002                         break;
3003                 case 'd':
3004                         device_id = strdup(optarg);
3005                         break;
3006                 case 'l':
3007                         req_lun_id = strtoul(optarg, NULL, 0);
3008                         lun_id_set = 1;
3009                         break;
3010                 case 'o': {
3011                         struct cctl_req_option *option;
3012                         char *tmpstr;
3013                         char *name, *value;
3014
3015                         tmpstr = strdup(optarg);
3016                         name = strsep(&tmpstr, "=");
3017                         if (name == NULL) {
3018                                 warnx("%s: option -o takes \"name=value\""
3019                                       "argument", __func__);
3020                                 retval = 1;
3021                                 goto bailout;
3022                         }
3023                         value = strsep(&tmpstr, "=");
3024                         if (value == NULL) {
3025                                 warnx("%s: option -o takes \"name=value\""
3026                                       "argument", __func__);
3027                                 retval = 1;
3028                                 goto bailout;
3029                         }
3030                         option = malloc(sizeof(*option));
3031                         if (option == NULL) {
3032                                 warn("%s: error allocating %zd bytes",
3033                                      __func__, sizeof(*option));
3034                                 retval = 1;
3035                                 goto bailout;
3036                         }
3037                         option->name = strdup(name);
3038                         option->namelen = strlen(name) + 1;
3039                         option->value = strdup(value);
3040                         option->vallen = strlen(value) + 1;
3041                         free(tmpstr);
3042
3043                         STAILQ_INSERT_TAIL(&option_list, option, links);
3044                         num_options++;
3045                         break;
3046                 }
3047                 case 's':
3048                         lun_size = strtoull(optarg, NULL, 0);
3049                         lun_size_set = 1;
3050                         break;
3051                 case 'S':
3052                         serial_num = strdup(optarg);
3053                         break;
3054                 case 't':
3055                         device_type = strtoul(optarg, NULL, 0);
3056                         break;
3057                 default:
3058                         break;
3059                 }
3060         }
3061
3062         if (backend_name == NULL) {
3063                 warnx("%s: backend name (-b) must be specified", __func__);
3064                 retval = 1;
3065                 goto bailout;
3066         }
3067
3068         bzero(&req, sizeof(req));
3069
3070         strlcpy(req.backend, backend_name, sizeof(req.backend));
3071         req.reqtype = CTL_LUNREQ_CREATE;
3072
3073         if (blocksize_set != 0)
3074                 req.reqdata.create.blocksize_bytes = blocksize;
3075
3076         if (lun_size_set != 0)
3077                 req.reqdata.create.lun_size_bytes = lun_size;
3078
3079         if (lun_id_set != 0) {
3080                 req.reqdata.create.flags |= CTL_LUN_FLAG_ID_REQ;
3081                 req.reqdata.create.req_lun_id = req_lun_id;
3082         }
3083
3084         req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE;
3085
3086         if (device_type != -1)
3087                 req.reqdata.create.device_type = device_type;
3088         else
3089                 req.reqdata.create.device_type = T_DIRECT;
3090
3091         if (serial_num != NULL) {
3092                 strlcpy(req.reqdata.create.serial_num, serial_num,
3093                         sizeof(req.reqdata.create.serial_num));
3094                 req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM;
3095         }
3096
3097         if (device_id != NULL) {
3098                 strlcpy(req.reqdata.create.device_id, device_id,
3099                         sizeof(req.reqdata.create.device_id));
3100                 req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID;
3101         }
3102
3103         req.num_be_args = num_options;
3104         if (num_options > 0) {
3105                 struct cctl_req_option *option, *next_option;
3106                 int i;
3107
3108                 req.be_args = malloc(num_options * sizeof(*req.be_args));
3109                 if (req.be_args == NULL) {
3110                         warn("%s: error allocating %zd bytes", __func__,
3111                              num_options * sizeof(*req.be_args));
3112                         retval = 1;
3113                         goto bailout;
3114                 }
3115
3116                 for (i = 0, option = STAILQ_FIRST(&option_list);
3117                      i < num_options; i++, option = next_option) {
3118                         next_option = STAILQ_NEXT(option, links);
3119
3120                         req.be_args[i].namelen = option->namelen;
3121                         req.be_args[i].name = strdup(option->name);
3122                         req.be_args[i].vallen = option->vallen;
3123                         req.be_args[i].value = strdup(option->value);
3124                         /*
3125                          * XXX KDM do we want a way to specify a writeable
3126                          * flag of some sort?  Do we want a way to specify
3127                          * binary data?
3128                          */
3129                         req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
3130
3131                         STAILQ_REMOVE(&option_list, option, cctl_req_option,
3132                                       links);
3133                         free(option->name);
3134                         free(option->value);
3135                         free(option);
3136                 }
3137         }
3138
3139         if (ioctl(fd, CTL_LUN_REQ, &req) == -1) {
3140                 warn("%s: error issuing CTL_LUN_REQ ioctl", __func__);
3141                 retval = 1;
3142                 goto bailout;
3143         }
3144
3145         if (req.status == CTL_LUN_ERROR) {
3146                 warnx("%s: error returned from LUN creation request:\n%s",
3147                       __func__, req.error_str);
3148                 retval = 1;
3149                 goto bailout;
3150         } else if (req.status != CTL_LUN_OK) {
3151                 warnx("%s: unknown LUN creation request status %d",
3152                       __func__, req.status);
3153                 retval = 1;
3154                 goto bailout;
3155         }
3156
3157         fprintf(stdout, "LUN created successfully\n");
3158         fprintf(stdout, "backend:       %s\n", req.backend);
3159         fprintf(stdout, "device type:   %d\n",req.reqdata.create.device_type);
3160         fprintf(stdout, "LUN size:      %ju bytes\n",
3161                 (uintmax_t)req.reqdata.create.lun_size_bytes);
3162         fprintf(stdout, "blocksize      %u bytes\n",
3163                 req.reqdata.create.blocksize_bytes);
3164         fprintf(stdout, "LUN ID:        %d\n", req.reqdata.create.req_lun_id);
3165         fprintf(stdout, "Serial Number: %s\n", req.reqdata.create.serial_num);
3166         fprintf(stdout, "Device ID;     %s\n", req.reqdata.create.device_id);
3167
3168 bailout:
3169         return (retval);
3170 }
3171
3172 static int
3173 cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
3174 {
3175         struct ctl_lun_req req;
3176         uint32_t lun_id = 0;
3177         int lun_id_set = 0;
3178         char *backend_name = NULL;
3179         STAILQ_HEAD(, cctl_req_option) option_list;
3180         int num_options = 0;
3181         int retval = 0, c;
3182
3183         STAILQ_INIT(&option_list);
3184
3185         while ((c = getopt(argc, argv, combinedopt)) != -1) {
3186                 switch (c) {
3187                 case 'b':
3188                         backend_name = strdup(optarg);
3189                         break;
3190                 case 'l':
3191                         lun_id = strtoul(optarg, NULL, 0);
3192                         lun_id_set = 1;
3193                         break;
3194                 case 'o': {
3195                         struct cctl_req_option *option;
3196                         char *tmpstr;
3197                         char *name, *value;
3198
3199                         tmpstr = strdup(optarg);
3200                         name = strsep(&tmpstr, "=");
3201                         if (name == NULL) {
3202                                 warnx("%s: option -o takes \"name=value\""
3203                                       "argument", __func__);
3204                                 retval = 1;
3205                                 goto bailout;
3206                         }
3207                         value = strsep(&tmpstr, "=");
3208                         if (value == NULL) {
3209                                 warnx("%s: option -o takes \"name=value\""
3210                                       "argument", __func__);
3211                                 retval = 1;
3212                                 goto bailout;
3213                         }
3214                         option = malloc(sizeof(*option));
3215                         if (option == NULL) {
3216                                 warn("%s: error allocating %zd bytes",
3217                                      __func__, sizeof(*option));
3218                                 retval = 1;
3219                                 goto bailout;
3220                         }
3221                         option->name = strdup(name);
3222                         option->namelen = strlen(name) + 1;
3223                         option->value = strdup(value);
3224                         option->vallen = strlen(value) + 1;
3225                         free(tmpstr);
3226
3227                         STAILQ_INSERT_TAIL(&option_list, option, links);
3228                         num_options++;
3229                         break;
3230                 }
3231                 default:
3232                         break;
3233                 }
3234         }
3235
3236         if (backend_name == NULL)
3237                 errx(1, "%s: backend name (-b) must be specified", __func__);
3238
3239         if (lun_id_set == 0)
3240                 errx(1, "%s: LUN id (-l) must be specified", __func__);
3241
3242         bzero(&req, sizeof(req));
3243
3244         strlcpy(req.backend, backend_name, sizeof(req.backend));
3245         req.reqtype = CTL_LUNREQ_RM;
3246
3247         req.reqdata.rm.lun_id = lun_id;
3248
3249         req.num_be_args = num_options;
3250         if (num_options > 0) {
3251                 struct cctl_req_option *option, *next_option;
3252                 int i;
3253
3254                 req.be_args = malloc(num_options * sizeof(*req.be_args));
3255                 if (req.be_args == NULL) {
3256                         warn("%s: error allocating %zd bytes", __func__,
3257                              num_options * sizeof(*req.be_args));
3258                         retval = 1;
3259                         goto bailout;
3260                 }
3261
3262                 for (i = 0, option = STAILQ_FIRST(&option_list);
3263                      i < num_options; i++, option = next_option) {
3264                         next_option = STAILQ_NEXT(option, links);
3265
3266                         req.be_args[i].namelen = option->namelen;
3267                         req.be_args[i].name = strdup(option->name);
3268                         req.be_args[i].vallen = option->vallen;
3269                         req.be_args[i].value = strdup(option->value);
3270                         /*
3271                          * XXX KDM do we want a way to specify a writeable
3272                          * flag of some sort?  Do we want a way to specify
3273                          * binary data?
3274                          */
3275                         req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
3276
3277                         STAILQ_REMOVE(&option_list, option, cctl_req_option,
3278                                       links);
3279                         free(option->name);
3280                         free(option->value);
3281                         free(option);
3282                 }
3283         }
3284
3285         if (ioctl(fd, CTL_LUN_REQ, &req) == -1) {
3286                 warn("%s: error issuing CTL_LUN_REQ ioctl", __func__);
3287                 retval = 1;
3288                 goto bailout;
3289         }
3290
3291         if (req.status == CTL_LUN_ERROR) {
3292                 warnx("%s: error returned from LUN creation request:\n%s",
3293                       __func__, req.error_str);
3294                 retval = 1;
3295                 goto bailout;
3296         } else if (req.status != CTL_LUN_OK) {
3297                 warnx("%s: unknown LUN creation request status %d",
3298                       __func__, req.status);
3299                 retval = 1;
3300                 goto bailout;
3301         }
3302
3303         printf("LUN %d deleted successfully\n", lun_id);
3304
3305 bailout:
3306         return (retval);
3307 }
3308
3309 /*
3310  * Name/value pair used for per-LUN attributes.
3311  */
3312 struct cctl_lun_nv {
3313         char *name;
3314         char *value;
3315         STAILQ_ENTRY(cctl_lun_nv) links;
3316 };
3317
3318 /*
3319  * Backend LUN information.  
3320  */
3321 struct cctl_lun {
3322         uint64_t lun_id;
3323         char *backend_type;
3324         uint64_t size_blocks;
3325         uint32_t blocksize;
3326         char *serial_number;
3327         char *device_id;
3328         STAILQ_HEAD(,cctl_lun_nv) attr_list;
3329         STAILQ_ENTRY(cctl_lun) links;
3330 };
3331
3332 struct cctl_devlist_data {
3333         int num_luns;
3334         STAILQ_HEAD(,cctl_lun) lun_list;
3335         struct cctl_lun *cur_lun;
3336         int level;
3337         struct sbuf *cur_sb[32];
3338 };
3339
3340 static void
3341 cctl_start_element(void *user_data, const char *name, const char **attr)
3342 {
3343         int i;
3344         struct cctl_devlist_data *devlist;
3345         struct cctl_lun *cur_lun;
3346
3347         devlist = (struct cctl_devlist_data *)user_data;
3348         cur_lun = devlist->cur_lun;
3349         devlist->level++;
3350         if ((u_int)devlist->level > (sizeof(devlist->cur_sb) /
3351             sizeof(devlist->cur_sb[0])))
3352                 errx(1, "%s: too many nesting levels, %zd max", __func__,
3353                      sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]));
3354
3355         devlist->cur_sb[devlist->level] = sbuf_new_auto();
3356         if (devlist->cur_sb[devlist->level] == NULL)
3357                 err(1, "%s: Unable to allocate sbuf", __func__);
3358
3359         if (strcmp(name, "lun") == 0) {
3360                 if (cur_lun != NULL)
3361                         errx(1, "%s: improper lun element nesting", __func__);
3362
3363                 cur_lun = calloc(1, sizeof(*cur_lun));
3364                 if (cur_lun == NULL)
3365                         err(1, "%s: cannot allocate %zd bytes", __func__,
3366                             sizeof(*cur_lun));
3367
3368                 devlist->num_luns++;
3369                 devlist->cur_lun = cur_lun;
3370
3371                 STAILQ_INIT(&cur_lun->attr_list);
3372                 STAILQ_INSERT_TAIL(&devlist->lun_list, cur_lun, links);
3373
3374                 for (i = 0; attr[i] != NULL; i += 2) {
3375                         if (strcmp(attr[i], "id") == 0) {
3376                                 cur_lun->lun_id = strtoull(attr[i+1], NULL, 0);
3377                         } else {
3378                                 errx(1, "%s: invalid LUN attribute %s = %s",
3379                                      __func__, attr[i], attr[i+1]);
3380                         }
3381                 }
3382         }
3383 }
3384
3385 static void
3386 cctl_end_element(void *user_data, const char *name)
3387 {
3388         struct cctl_devlist_data *devlist;
3389         struct cctl_lun *cur_lun;
3390         char *str;
3391
3392         devlist = (struct cctl_devlist_data *)user_data;
3393         cur_lun = devlist->cur_lun;
3394
3395         if ((cur_lun == NULL)
3396          && (strcmp(name, "ctllunlist") != 0))
3397                 errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name);
3398
3399         if (devlist->cur_sb[devlist->level] == NULL)
3400                 errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
3401                      devlist->level, name);
3402
3403         sbuf_finish(devlist->cur_sb[devlist->level]);
3404         str = strdup(sbuf_data(devlist->cur_sb[devlist->level]));
3405         if (str == NULL)
3406                 err(1, "%s can't allocate %zd bytes for string", __func__,
3407                     sbuf_len(devlist->cur_sb[devlist->level]));
3408
3409         if (strlen(str) == 0) {
3410                 free(str);
3411                 str = NULL;
3412         }
3413
3414         sbuf_delete(devlist->cur_sb[devlist->level]);
3415         devlist->cur_sb[devlist->level] = NULL;
3416         devlist->level--;
3417
3418         if (strcmp(name, "backend_type") == 0) {
3419                 cur_lun->backend_type = str;
3420                 str = NULL;
3421         } else if (strcmp(name, "size") == 0) {
3422                 cur_lun->size_blocks = strtoull(str, NULL, 0);
3423         } else if (strcmp(name, "blocksize") == 0) {
3424                 cur_lun->blocksize = strtoul(str, NULL, 0);
3425         } else if (strcmp(name, "serial_number") == 0) {
3426                 cur_lun->serial_number = str;
3427                 str = NULL;
3428         } else if (strcmp(name, "device_id") == 0) {
3429                 cur_lun->device_id = str;
3430                 str = NULL;
3431         } else if (strcmp(name, "lun") == 0) {
3432                 devlist->cur_lun = NULL;
3433         } else if (strcmp(name, "ctllunlist") == 0) {
3434                 
3435         } else {
3436                 struct cctl_lun_nv *nv;
3437
3438                 nv = calloc(1, sizeof(*nv));
3439                 if (nv == NULL)
3440                         err(1, "%s: can't allocate %zd bytes for nv pair",
3441                             __func__, sizeof(*nv));
3442
3443                 nv->name = strdup(name);
3444                 if (nv->name == NULL)
3445                         err(1, "%s: can't allocated %zd bytes for string",
3446                             __func__, strlen(name));
3447
3448                 nv->value = str;
3449                 str = NULL;
3450                 STAILQ_INSERT_TAIL(&cur_lun->attr_list, nv, links);
3451         }
3452
3453         free(str);
3454 }
3455
3456 static void
3457 cctl_char_handler(void *user_data, const XML_Char *str, int len)
3458 {
3459         struct cctl_devlist_data *devlist;
3460
3461         devlist = (struct cctl_devlist_data *)user_data;
3462
3463         sbuf_bcat(devlist->cur_sb[devlist->level], str, len);
3464 }
3465
3466 static int
3467 cctl_devlist(int fd, int argc, char **argv, char *combinedopt)
3468 {
3469         struct ctl_lun_list list;
3470         struct cctl_devlist_data devlist;
3471         struct cctl_lun *lun;
3472         XML_Parser parser;
3473         char *lun_str;
3474         int lun_len;
3475         int dump_xml = 0;
3476         int retval, c;
3477         char *backend = NULL;
3478         int verbose = 0;
3479
3480         retval = 0;
3481         lun_len = 4096;
3482
3483         bzero(&devlist, sizeof(devlist));
3484         STAILQ_INIT(&devlist.lun_list);
3485
3486         while ((c = getopt(argc, argv, combinedopt)) != -1) {
3487                 switch (c) {
3488                 case 'b':
3489                         backend = strdup(optarg);
3490                         break;
3491                 case 'v':
3492                         verbose++;
3493                         break;
3494                 case 'x':
3495                         dump_xml = 1;
3496                         break;
3497                 default:
3498                         break;
3499                 }
3500         }
3501
3502 retry:
3503         lun_str = malloc(lun_len);
3504
3505         bzero(&list, sizeof(list));
3506         list.alloc_len = lun_len;
3507         list.status = CTL_LUN_LIST_NONE;
3508         list.lun_xml = lun_str;
3509
3510         if (ioctl(fd, CTL_LUN_LIST, &list) == -1) {
3511                 warn("%s: error issuing CTL_LUN_LIST ioctl", __func__);
3512                 retval = 1;
3513                 goto bailout;
3514         }
3515
3516         if (list.status == CTL_LUN_LIST_ERROR) {
3517                 warnx("%s: error returned from CTL_LUN_LIST ioctl:\n%s",
3518                       __func__, list.error_str);
3519         } else if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
3520                 lun_len = lun_len << 1;
3521                 goto retry;
3522         }
3523
3524         if (dump_xml != 0) {
3525                 printf("%s", lun_str);
3526                 goto bailout;
3527         }
3528
3529         parser = XML_ParserCreate(NULL);
3530         if (parser == NULL) {
3531                 warn("%s: Unable to create XML parser", __func__);
3532                 retval = 1;
3533                 goto bailout;
3534         }
3535
3536         XML_SetUserData(parser, &devlist);
3537         XML_SetElementHandler(parser, cctl_start_element, cctl_end_element);
3538         XML_SetCharacterDataHandler(parser, cctl_char_handler);
3539
3540         retval = XML_Parse(parser, lun_str, strlen(lun_str), 1);
3541         XML_ParserFree(parser);
3542         if (retval != 1) {
3543                 retval = 1;
3544                 goto bailout;
3545         }
3546
3547         printf("LUN Backend  %18s %4s %-16s %-16s\n", "Size (Blocks)", "BS",
3548                "Serial Number", "Device ID");
3549         STAILQ_FOREACH(lun, &devlist.lun_list, links) {
3550                 struct cctl_lun_nv *nv;
3551
3552                 if ((backend != NULL)
3553                  && (strcmp(lun->backend_type, backend) != 0))
3554                         continue;
3555
3556                 printf("%3ju %-8s %18ju %4u %-16s %-16s\n",
3557                        (uintmax_t)lun->lun_id,
3558                        lun->backend_type, (uintmax_t)lun->size_blocks,
3559                        lun->blocksize, lun->serial_number, lun->device_id);
3560
3561                 if (verbose == 0)
3562                         continue;
3563
3564                 STAILQ_FOREACH(nv, &lun->attr_list, links) {
3565                         printf("      %s=%s\n", nv->name, nv->value);
3566                 }
3567         }
3568 bailout:
3569         free(lun_str);
3570
3571         return (retval);
3572 }
3573
3574 void
3575 usage(int error)
3576 {
3577         fprintf(error ? stderr : stdout,
3578 "Usage:\n"
3579 "Primary commands:\n"
3580 "         ctladm tur         [dev_id][general options]\n"
3581 "         ctladm inquiry     [dev_id][general options]\n"
3582 "         ctladm devid       [dev_id][general options]\n"
3583 "         ctladm reqsense    [dev_id][general options]\n"
3584 "         ctladm reportluns  [dev_id][general options]\n"
3585 "         ctladm read        [dev_id][general options] <-l lba> <-d len>\n"
3586 "                            <-f file|-> <-b blocksize> [-c cdbsize][-N]\n"
3587 "         ctladm write       [dev_id][general options] <-l lba> <-d len>\n"
3588 "                            <-f file|-> <-b blocksize> [-c cdbsize][-N]\n"
3589 "         ctladm readcap     [dev_id][general options] [-c cdbsize]\n"
3590 "         ctladm modesense   [dev_id][general options] <-m page|-l> [-P pc]\n"
3591 "                            [-d] [-S subpage] [-c cdbsize]\n"
3592 "         ctladm prin        [dev_id][general options] <-a action>\n"
3593 "         ctladm prout       [dev_id][general options] <-a action>\n"
3594 "                            <-r restype] [-k key] [-s sa_key]\n"
3595 "         ctladm rtpg        [dev_id][general options]\n"
3596 "         ctladm start       [dev_id][general options] [-i] [-o]\n"
3597 "         ctladm stop        [dev_id][general options] [-i] [-o]\n"
3598 "         ctladm synccache   [dev_id][general options] [-l lba]\n"
3599 "                            [-b blockcount] [-r] [-i] [-c cdbsize]\n"
3600 "         ctladm create      <-b backend> [-B blocksize] [-d device_id]\n"
3601 "                            [-l lun_id] [-o name=value] [-s size_bytes]\n"
3602 "                            [-S serial_num] [-t dev_type]\n"
3603 "         ctladm remove      <-b backend> <-l lun_id> [-o name=value]\n"
3604 "         ctladm devlist     [-b][-v][-x]\n"
3605 "         ctladm shutdown\n"
3606 "         ctladm startup\n"
3607 "         ctladm hardstop\n"
3608 "         ctladm hardstart\n"
3609 "         ctladm lunlist\n"
3610 "         ctladm bbrread     [dev_id] <-l lba> <-d datalen>\n"
3611 "         ctladm delay       [dev_id] <-l datamove|done> [-T oneshot|cont]\n"
3612 "                            [-t secs]\n"
3613 "         ctladm realsync    <on|off|query>\n"
3614 "         ctladm setsync     [dev_id] <-i interval>\n"
3615 "         ctladm getsync     [dev_id]\n"
3616 "         ctladm inject      [dev_id] <-i action> <-p pattern> [-r lba,len]\n"
3617 "                            [-s len fmt [args]] [-c] [-d delete_id]\n"
3618 "         ctladm port        <-l | -o <on|off> | [-w wwnn][-W wwpn]>\n"
3619 "                            [-p targ_port] [-t port_type] [-q] [-x]\n"
3620 "         ctladm dumpooa\n"
3621 "         ctladm dumpstructs\n"
3622 "         ctladm help\n"
3623 "General Options:\n"
3624 "-I intiator_id           : defaults to 7, used to change the initiator id\n"
3625 "-C retries               : specify the number of times to retry this command\n"
3626 "-D devicename            : specify the device to operate on\n"
3627 "                         : (default is %s)\n"
3628 "read/write options:\n"
3629 "-l lba                   : logical block address\n"
3630 "-d len                   : read/write length, in blocks\n"
3631 "-f file|-                : write/read data to/from file or stdout/stdin\n"
3632 "-b blocksize             : block size, in bytes\n"
3633 "-c cdbsize               : specify minimum cdb size: 6, 10, 12 or 16\n"
3634 "-N                       : do not copy data to/from userland\n"
3635 "readcapacity options:\n"
3636 "-c cdbsize               : specify minimum cdb size: 10 or 16\n"
3637 "modesense options:\n"
3638 "-m page                  : specify the mode page to view\n"
3639 "-l                       : request a list of supported pages\n"
3640 "-P pc                    : specify the page control value: 0-3 (current,\n"
3641 "                           changeable, default, saved, respectively)\n"
3642 "-d                       : disable block descriptors for mode sense\n"
3643 "-S subpage               : specify a subpage\n"
3644 "-c cdbsize               : specify minimum cdb size: 6 or 10\n"
3645 "persistent reserve in options:\n"
3646 "-a action                : specify the action value: 0-2 (read key, read\n"
3647 "                           reservation, read capabilities, respectively)\n"
3648 "persistent reserve out options:\n"
3649 "-a action                : specify the action value: 0-5 (register, reserve,\n"
3650 "                           release, clear, preempt, register and ignore)\n"
3651 "-k key                   : key value\n"
3652 "-s sa_key                : service action value\n"
3653 "-r restype               : specify the reservation type: 0-5(wr ex, ex ac,\n"
3654 "                           wr ex ro, ex ac ro, wr ex ar, ex ac ar)\n"
3655 "start/stop options:\n"
3656 "-i                       : set the immediate bit (CTL does not support this)\n"
3657 "-o                       : set the on/offline bit\n"
3658 "synccache options:\n"
3659 "-l lba                   : set the starting LBA\n"
3660 "-b blockcount            : set the length to sync in blocks\n"
3661 "-r                       : set the relative addressing bit\n"
3662 "-i                       : set the immediate bit\n"
3663 "-c cdbsize               : specify minimum cdb size: 10 or 16\n"
3664 "create options:\n"
3665 "-b backend               : backend name (\"block\", \"ramdisk\", etc.)\n"
3666 "-B blocksize             : LUN blocksize in bytes (some backends)\n"
3667 "-d device_id             : SCSI VPD page 0x83 ID\n"
3668 "-l lun_id                : requested LUN number\n"
3669 "-o name=value            : backend-specific options, multiple allowed\n"
3670 "-s size_bytes            : LUN size in bytes (some backends)\n"
3671 "-S serial_num            : SCSI VPD page 0x80 serial number\n"
3672 "-t dev_type              : SCSI device type (0=disk, 3=processor)\n"
3673 "remove options:\n"
3674 "-b backend               : backend name (\"block\", \"ramdisk\", etc.)\n"
3675 "-l lun_id                : LUN number to delete\n"
3676 "-o name=value            : backend-specific options, multiple allowed\n"
3677 "devlist options:\n"
3678 "-b backend               : list devices from specified backend only\n"
3679 "-v                       : be verbose, show backend attributes\n"
3680 "-x                       : dump raw XML\n"
3681 "delay options:\n"
3682 "-l datamove|done         : delay command at datamove or done phase\n"
3683 "-T oneshot               : delay one command, then resume normal completion\n"
3684 "-T cont                  : delay all commands\n"
3685 "-t secs                  : number of seconds to delay\n"
3686 "inject options:\n"
3687 "-i error_action          : action to perform\n"
3688 "-p pattern               : command pattern to look for\n"
3689 "-r lba,len               : LBA range for pattern\n"
3690 "-s len fmt [args]        : sense data for custom sense action\n"
3691 "-c                       : continuous operation\n"
3692 "-d delete_id             : error id to delete\n"
3693 "port options:\n"
3694 "-l                       : list frontend ports\n"
3695 "-o on|off                : turn frontend ports on or off\n"
3696 "-w wwnn                  : set WWNN for one frontend\n"
3697 "-W wwpn                  : set WWPN for one frontend\n"
3698 "-t port_type             : specify fc, scsi, ioctl, internal frontend type\n"
3699 "-p targ_port             : specify target port number\n"
3700 "-q                       : omit header in list output\n"
3701 "-x                       : output port list in XML format\n"
3702 "bbrread options:\n"
3703 "-l lba                   : starting LBA\n"
3704 "-d datalen               : length, in bytes, to read\n",
3705 CTL_DEFAULT_DEV);
3706 }
3707
3708 int
3709 main(int argc, char **argv)
3710 {
3711         int option_index, c;
3712         ctladm_cmdfunction command;
3713         ctladm_cmdargs cmdargs;
3714         ctladm_optret optreturn;
3715         char *device;
3716         const char *mainopt = "C:D:I:";
3717         const char *subopt = NULL;
3718         char combinedopt[256];
3719         int target, lun;
3720         int optstart = 2;
3721         int retval, fd;
3722         int retries, timeout;
3723         int initid;
3724
3725         option_index = 0;
3726         retval = 0;
3727         cmdargs = CTLADM_ARG_NONE;
3728         command = CTLADM_CMD_HELP;
3729         device = NULL;
3730         fd = -1;
3731         retries = 0;
3732         target = 0;
3733         lun = 0;
3734         timeout = 0;
3735         initid = 7;
3736
3737         if (argc < 2) {
3738                 usage(1);
3739                 retval = 1;
3740                 goto bailout;
3741         }
3742
3743         /*
3744          * Get the base option.
3745          */
3746         optreturn = getoption(option_table,argv[1], &command, &cmdargs,&subopt);
3747
3748         if (optreturn == CC_OR_AMBIGUOUS) {
3749                 warnx("ambiguous option %s", argv[1]);
3750                 usage(0);
3751                 exit(1);
3752         } else if (optreturn == CC_OR_NOT_FOUND) {
3753                 warnx("option %s not found", argv[1]);
3754                 usage(0);
3755                 exit(1);
3756         }
3757
3758         if (cmdargs & CTLADM_ARG_NEED_TL) {
3759                 if ((argc < 3)
3760                  || (!isdigit(argv[2][0]))) {
3761                         warnx("option %s requires a target:lun argument",
3762                               argv[1]);
3763                         usage(0);
3764                         exit(1);
3765                 }
3766                 retval = cctl_parse_tl(argv[2], &target, &lun);
3767                 if (retval != 0)
3768                         errx(1, "invalid target:lun argument %s", argv[2]);
3769
3770                 cmdargs |= CTLADM_ARG_TARG_LUN;
3771                 optstart++;
3772         }
3773
3774         /*
3775          * Ahh, getopt(3) is a pain.
3776          *
3777          * This is a gross hack.  There really aren't many other good
3778          * options (excuse the pun) for parsing options in a situation like
3779          * this.  getopt is kinda braindead, so you end up having to run
3780          * through the options twice, and give each invocation of getopt
3781          * the option string for the other invocation.
3782          *
3783          * You would think that you could just have two groups of options.
3784          * The first group would get parsed by the first invocation of
3785          * getopt, and the second group would get parsed by the second
3786          * invocation of getopt.  It doesn't quite work out that way.  When
3787          * the first invocation of getopt finishes, it leaves optind pointing
3788          * to the argument _after_ the first argument in the second group.
3789          * So when the second invocation of getopt comes around, it doesn't
3790          * recognize the first argument it gets and then bails out.
3791          *
3792          * A nice alternative would be to have a flag for getopt that says
3793          * "just keep parsing arguments even when you encounter an unknown
3794          * argument", but there isn't one.  So there's no real clean way to
3795          * easily parse two sets of arguments without having one invocation
3796          * of getopt know about the other.
3797          *
3798          * Without this hack, the first invocation of getopt would work as
3799          * long as the generic arguments are first, but the second invocation
3800          * (in the subfunction) would fail in one of two ways.  In the case
3801          * where you don't set optreset, it would fail because optind may be
3802          * pointing to the argument after the one it should be pointing at.
3803          * In the case where you do set optreset, and reset optind, it would
3804          * fail because getopt would run into the first set of options, which
3805          * it doesn't understand.
3806          *
3807          * All of this would "sort of" work if you could somehow figure out
3808          * whether optind had been incremented one option too far.  The
3809          * mechanics of that, however, are more daunting than just giving
3810          * both invocations all of the expect options for either invocation.
3811          *
3812          * Needless to say, I wouldn't mind if someone invented a better
3813          * (non-GPL!) command line parsing interface than getopt.  I
3814          * wouldn't mind if someone added more knobs to getopt to make it
3815          * work better.  Who knows, I may talk myself into doing it someday,
3816          * if the standards weenies let me.  As it is, it just leads to
3817          * hackery like this and causes people to avoid it in some cases.
3818          *
3819          * KDM, September 8th, 1998
3820          */
3821         if (subopt != NULL)
3822                 sprintf(combinedopt, "%s%s", mainopt, subopt);
3823         else
3824                 sprintf(combinedopt, "%s", mainopt);
3825
3826         /*
3827          * Start getopt processing at argv[2/3], since we've already
3828          * accepted argv[1..2] as the command name, and as a possible
3829          * device name.
3830          */
3831         optind = optstart;
3832
3833         /*
3834          * Now we run through the argument list looking for generic
3835          * options, and ignoring options that possibly belong to
3836          * subfunctions.
3837          */
3838         while ((c = getopt(argc, argv, combinedopt))!= -1){
3839                 switch (c) {
3840                 case 'C':
3841                         cmdargs |= CTLADM_ARG_RETRIES;
3842                         retries = strtol(optarg, NULL, 0);
3843                         break;
3844                 case 'D':
3845                         device = strdup(optarg);
3846                         cmdargs |= CTLADM_ARG_DEVICE;
3847                         break;
3848                 case 'I':
3849                         cmdargs |= CTLADM_ARG_INITIATOR;
3850                         initid = strtol(optarg, NULL, 0);
3851                         break;
3852                 default:
3853                         break;
3854                 }
3855         }
3856
3857         if ((cmdargs & CTLADM_ARG_INITIATOR) == 0)
3858                 initid = 7;
3859
3860         optind = optstart;
3861         optreset = 1;
3862
3863         /*
3864          * Default to opening the CTL device for now.
3865          */
3866         if (((cmdargs & CTLADM_ARG_DEVICE) == 0)
3867          && (command != CTLADM_CMD_HELP)) {
3868                 device = strdup(CTL_DEFAULT_DEV);
3869                 cmdargs |= CTLADM_ARG_DEVICE;
3870         }
3871
3872         if ((cmdargs & CTLADM_ARG_DEVICE)
3873          && (command != CTLADM_CMD_HELP)) {
3874                 fd = open(device, O_RDWR);
3875                 if (fd == -1) {
3876                         fprintf(stderr, "%s: error opening %s: %s\n",
3877                                 argv[0], device, strerror(errno));
3878                         retval = 1;
3879                         goto bailout;
3880                 }
3881         } else if ((command != CTLADM_CMD_HELP)
3882                 && ((cmdargs & CTLADM_ARG_DEVICE) == 0)) {
3883                 fprintf(stderr, "%s: you must specify a device with the "
3884                         "--device argument for this command\n", argv[0]);
3885                 command = CTLADM_CMD_HELP;
3886                 retval = 1;
3887         }
3888
3889         switch (command) {
3890         case CTLADM_CMD_TUR:
3891                 retval = cctl_tur(fd, target, lun, initid, retries);
3892                 break;
3893         case CTLADM_CMD_INQUIRY:
3894                 retval = cctl_inquiry(fd, target, lun, initid, retries);
3895                 break;
3896         case CTLADM_CMD_REQ_SENSE:
3897                 retval = cctl_req_sense(fd, target, lun, initid, retries);
3898                 break;
3899         case CTLADM_CMD_REPORT_LUNS:
3900                 retval = cctl_report_luns(fd, target, lun, initid, retries);
3901                 break;
3902         case CTLADM_CMD_CREATE:
3903                 retval = cctl_create_lun(fd, argc, argv, combinedopt);
3904                 break;
3905         case CTLADM_CMD_RM:
3906                 retval = cctl_rm_lun(fd, argc, argv, combinedopt);
3907                 break;
3908         case CTLADM_CMD_DEVLIST:
3909                 retval = cctl_devlist(fd, argc, argv, combinedopt);
3910                 break;
3911         case CTLADM_CMD_READ:
3912         case CTLADM_CMD_WRITE:
3913                 retval = cctl_read_write(fd, target, lun, initid, retries,
3914                                          argc, argv, combinedopt, command);
3915                 break;
3916         case CTLADM_CMD_PORT:
3917                 retval = cctl_port(fd, argc, argv, combinedopt);
3918                 break;
3919         case CTLADM_CMD_READCAPACITY:
3920                 retval = cctl_read_capacity(fd, target, lun, initid, retries,
3921                                             argc, argv, combinedopt);
3922                 break;
3923         case CTLADM_CMD_MODESENSE:
3924                 retval = cctl_mode_sense(fd, target, lun, initid, retries,
3925                                          argc, argv, combinedopt);
3926                 break;
3927         case CTLADM_CMD_START:
3928         case CTLADM_CMD_STOP:
3929                 retval = cctl_start_stop(fd, target, lun, initid, retries,
3930                                          (command == CTLADM_CMD_START) ? 1 : 0,
3931                                          argc, argv, combinedopt);
3932                 break;
3933         case CTLADM_CMD_SYNC_CACHE:
3934                 retval = cctl_sync_cache(fd, target, lun, initid, retries,
3935                                          argc, argv, combinedopt);
3936                 break;
3937         case CTLADM_CMD_SHUTDOWN:
3938         case CTLADM_CMD_STARTUP:
3939                 retval = cctl_startup_shutdown(fd, target, lun, initid,
3940                                                command);
3941                 break;
3942         case CTLADM_CMD_HARDSTOP:
3943         case CTLADM_CMD_HARDSTART:
3944                 retval = cctl_hardstopstart(fd, command);
3945                 break;
3946         case CTLADM_CMD_BBRREAD:
3947                 retval = cctl_bbrread(fd, target, lun, initid, argc, argv,
3948                                       combinedopt);
3949                 break;
3950         case CTLADM_CMD_LUNLIST:
3951                 retval = cctl_lunlist(fd);
3952                 break;
3953         case CTLADM_CMD_DELAY:
3954                 retval = cctl_delay(fd, target, lun, argc, argv, combinedopt);
3955                 break;
3956         case CTLADM_CMD_REALSYNC:
3957                 retval = cctl_realsync(fd, argc, argv);
3958                 break;
3959         case CTLADM_CMD_SETSYNC:
3960         case CTLADM_CMD_GETSYNC:
3961                 retval = cctl_getsetsync(fd, target, lun, command,
3962                                          argc, argv, combinedopt);
3963                 break;
3964         case CTLADM_CMD_ERR_INJECT:
3965                 retval = cctl_error_inject(fd, target, lun, argc, argv,
3966                                            combinedopt);
3967                 break;
3968         case CTLADM_CMD_DUMPOOA:
3969                 retval = cctl_dump_ooa(fd, argc, argv);
3970                 break;
3971         case CTLADM_CMD_DUMPSTRUCTS:
3972                 retval = cctl_dump_structs(fd, cmdargs);
3973                 break;
3974         case CTLADM_CMD_PRES_IN:
3975                 retval = cctl_persistent_reserve_in(fd, target, lun, initid, 
3976                                                     argc, argv, combinedopt,
3977                                                     retries);
3978                 break;
3979         case CTLADM_CMD_PRES_OUT:
3980                 retval = cctl_persistent_reserve_out(fd, target, lun, initid, 
3981                                                      argc, argv, combinedopt,
3982                                                      retries);
3983                 break;
3984         case CTLADM_CMD_INQ_VPD_DEVID:
3985                 retval = cctl_inquiry_vpd_devid(fd, target, lun, initid);
3986                 break;
3987         case CTLADM_CMD_RTPG:
3988                 retval = cctl_report_target_port_group(fd, target, lun, initid);
3989                 break;
3990         case CTLADM_CMD_HELP:
3991         default:
3992                 usage(retval);
3993                 break;
3994         }
3995 bailout:
3996
3997         if (fd != -1)
3998                 close(fd);
3999
4000         exit (retval);
4001 }
4002
4003 /*
4004  * vim: ts=8
4005  */