]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - usr.sbin/ctladm/ctladm.c
MFC r237255:
[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         struct ctl_io_delay_info delay_info;
768         char *delayloc = NULL;
769         char *delaytype = NULL;
770         int delaytime = -1;
771         int retval;
772         int c;
773
774         retval = 0;
775
776         memset(&delay_info, 0, sizeof(delay_info));
777
778         while ((c = getopt(argc, argv, combinedopt)) != -1) {
779                 switch (c) {
780                 case 'T':
781                         delaytype = strdup(optarg);
782                         break;
783                 case 'l':
784                         delayloc = strdup(optarg);
785                         break;
786                 case 't':
787                         delaytime = strtoul(optarg, NULL, 0);
788                         break;
789                 }
790         }
791
792         if (delaytime == -1) {
793                 warnx("%s: you must specify the delaytime with -t", __func__);
794                 retval = 1;
795                 goto bailout;
796         }
797
798         if (strcasecmp(delayloc, "datamove") == 0)
799                 delay_info.delay_loc = CTL_DELAY_LOC_DATAMOVE;
800         else if (strcasecmp(delayloc, "done") == 0)
801                 delay_info.delay_loc = CTL_DELAY_LOC_DONE;
802         else {
803                 warnx("%s: invalid delay location %s", __func__, delayloc);
804                 retval = 1;
805                 goto bailout;
806         }
807
808         if ((delaytype == NULL)
809          || (strcmp(delaytype, "oneshot") == 0))
810                 delay_info.delay_type = CTL_DELAY_TYPE_ONESHOT;
811         else if (strcmp(delaytype, "cont") == 0)
812                 delay_info.delay_type = CTL_DELAY_TYPE_CONT;
813         else {
814                 warnx("%s: invalid delay type %s", __func__, delaytype);
815                 retval = 1;
816                 goto bailout;
817         }
818
819         delay_info.target_id = target;
820         delay_info.lun_id = lun;
821         delay_info.delay_secs = delaytime;
822
823         if (ioctl(fd, CTL_DELAY_IO, &delay_info) == -1) {
824                 warn("%s: CTL_DELAY_IO ioctl failed", __func__);
825                 retval = 1;
826                 goto bailout;
827         }
828         switch (delay_info.status) {
829         case CTL_DELAY_STATUS_NONE:
830                 warnx("%s: no delay status??", __func__);
831                 retval = 1;
832                 break;
833         case CTL_DELAY_STATUS_OK:
834                 break;
835         case CTL_DELAY_STATUS_INVALID_LUN:
836                 warnx("%s: invalid lun %d", __func__, lun);
837                 retval = 1;
838                 break;
839         case CTL_DELAY_STATUS_INVALID_TYPE:
840                 warnx("%s: invalid delay type %d", __func__,
841                       delay_info.delay_type);
842                 retval = 1;
843                 break;
844         case CTL_DELAY_STATUS_INVALID_LOC:
845                 warnx("%s: delay location %s not implemented?", __func__,
846                       delayloc);
847                 retval = 1;
848                 break;
849         case CTL_DELAY_STATUS_NOT_IMPLEMENTED:
850                 warnx("%s: delay not implemented in the kernel", __func__);
851                 warnx("%s: recompile with the CTL_IO_DELAY flag set", __func__);
852                 retval = 1;
853                 break;
854         default:
855                 warnx("%s: unknown delay return status %d", __func__,
856                       delay_info.status);
857                 retval = 1;
858                 break;
859         }
860 bailout:
861
862         /* delayloc should never be NULL, but just in case...*/
863         if (delayloc != NULL)
864                 free(delayloc);
865
866         return (retval);
867 }
868
869 static int
870 cctl_realsync(int fd, int argc, char **argv)
871 {
872         int syncstate;
873         int retval;
874         char *syncarg;
875
876         retval = 0;
877
878         if (argc != 3) {
879                 warnx("%s %s takes exactly one argument", argv[0], argv[1]);
880                 retval = 1;
881                 goto bailout;
882         }
883
884         syncarg = argv[2];
885
886         if (strncasecmp(syncarg, "query", min(strlen(syncarg),
887                         strlen("query"))) == 0) {
888                 if (ioctl(fd, CTL_REALSYNC_GET, &syncstate) == -1) {
889                         warn("%s: CTL_REALSYNC_GET ioctl failed", __func__);
890                         retval = 1;
891                         goto bailout;
892                 }
893                 fprintf(stdout, "SYNCHRONIZE CACHE support is: ");
894                 switch (syncstate) {
895                 case 0:
896                         fprintf(stdout, "OFF\n");
897                         break;
898                 case 1:
899                         fprintf(stdout, "ON\n");
900                         break;
901                 default:
902                         fprintf(stdout, "unknown (%d)\n", syncstate);
903                         break;
904                 }
905                 goto bailout;
906         } else if (strcasecmp(syncarg, "on") == 0) {
907                 syncstate = 1;
908         } else if (strcasecmp(syncarg, "off") == 0) {
909                 syncstate = 0;
910         } else {
911                 warnx("%s: invalid realsync argument %s", __func__, syncarg);
912                 retval = 1;
913                 goto bailout;
914         }
915
916         if (ioctl(fd, CTL_REALSYNC_SET, &syncstate) == -1) {
917                 warn("%s: CTL_REALSYNC_SET ioctl failed", __func__);
918                 retval = 1;
919                 goto bailout;
920         }
921 bailout:
922         return (retval);
923 }
924
925 static int
926 cctl_getsetsync(int fd, int target, int lun, ctladm_cmdfunction command,
927                 int argc, char **argv, char *combinedopt)
928 {
929         struct ctl_sync_info sync_info;
930         uint32_t ioctl_cmd;
931         int sync_interval = -1;
932         int retval;
933         int c;
934
935         retval = 0;
936
937         memset(&sync_info, 0, sizeof(sync_info));
938         sync_info.target_id = target;
939         sync_info.lun_id = lun;
940
941         while ((c = getopt(argc, argv, combinedopt)) != -1) {
942                 switch (c) {
943                 case 'i':
944                         sync_interval = strtoul(optarg, NULL, 0);
945                         break;
946                 default:
947                         break;
948                 }
949         }
950
951         if (command == CTLADM_CMD_SETSYNC) {
952                 if (sync_interval == -1) {
953                         warnx("%s: you must specify the sync interval with -i",
954                               __func__);
955                         retval = 1;
956                         goto bailout;
957                 }
958                 sync_info.sync_interval = sync_interval;
959                 ioctl_cmd = CTL_SETSYNC;
960         } else {
961                 ioctl_cmd = CTL_GETSYNC;
962         }
963
964         if (ioctl(fd, ioctl_cmd, &sync_info) == -1) {
965                 warn("%s: CTL_%sSYNC ioctl failed", __func__,
966                      (command == CTLADM_CMD_SETSYNC) ? "SET" : "GET");
967                 retval = 1;
968                 goto bailout;
969         }
970
971         switch (sync_info.status) {
972         case CTL_GS_SYNC_OK:
973                 if (command == CTLADM_CMD_GETSYNC) {
974                         fprintf(stdout, "%d:%d: sync interval: %d\n",
975                                 target, lun, sync_info.sync_interval);
976                 }
977                 break;
978         case CTL_GS_SYNC_NO_LUN:
979                 warnx("%s: unknown target:LUN %d:%d", __func__, target, lun);
980                 retval = 1;
981                 break;
982         case CTL_GS_SYNC_NONE:
983         default:
984                 warnx("%s: unknown CTL_%sSYNC status %d", __func__,
985                       (command == CTLADM_CMD_SETSYNC) ? "SET" : "GET",
986                       sync_info.status);
987                 retval = 1;
988                 break;
989         }
990 bailout:
991         return (retval);
992 }
993
994 struct ctladm_opts cctl_err_types[] = {
995         {"aborted", CTL_LUN_INJ_ABORTED, CTLADM_ARG_NONE, NULL},
996         {"mediumerr", CTL_LUN_INJ_MEDIUM_ERR, CTLADM_ARG_NONE, NULL},
997         {"ua", CTL_LUN_INJ_UA, CTLADM_ARG_NONE, NULL},
998         {"custom", CTL_LUN_INJ_CUSTOM, CTLADM_ARG_NONE, NULL},
999         {NULL, 0, 0, NULL}
1000
1001 };
1002
1003 struct ctladm_opts cctl_err_patterns[] = {
1004         {"read", CTL_LUN_PAT_READ, CTLADM_ARG_NONE, NULL},
1005         {"write", CTL_LUN_PAT_WRITE, CTLADM_ARG_NONE, NULL},
1006         {"rw", CTL_LUN_PAT_READWRITE, CTLADM_ARG_NONE, NULL},
1007         {"readwrite", CTL_LUN_PAT_READWRITE, CTLADM_ARG_NONE, NULL},
1008         {"readcap", CTL_LUN_PAT_READCAP, CTLADM_ARG_NONE, NULL},
1009         {"tur", CTL_LUN_PAT_TUR, CTLADM_ARG_NONE, NULL},
1010         {"any", CTL_LUN_PAT_ANY, CTLADM_ARG_NONE, NULL},
1011 #if 0
1012         {"cmd", CTL_LUN_PAT_CMD,  CTLADM_ARG_NONE, NULL},
1013 #endif
1014         {NULL, 0, 0, NULL}
1015 };
1016
1017 static int
1018 cctl_error_inject(int fd, uint32_t target, uint32_t lun, int argc, char **argv, 
1019                   char *combinedopt)
1020 {
1021         int retval;
1022         struct ctl_error_desc err_desc;
1023         uint64_t lba = 0;
1024         uint32_t len = 0;
1025         uint64_t delete_id = 0;
1026         int delete_id_set = 0;
1027         int continuous = 0;
1028         int sense_len = 0;
1029         int fd_sense = 0;
1030         int c;
1031
1032         bzero(&err_desc, sizeof(err_desc));
1033         err_desc.target_id = target;
1034         err_desc.lun_id = lun;
1035
1036         while ((c = getopt(argc, argv, combinedopt)) != -1) {
1037                 switch (c) {
1038                 case 'c':
1039                         continuous = 1;
1040                         break;
1041                 case 'd':
1042                         delete_id = strtoull(optarg, NULL, 0);
1043                         delete_id_set = 1;
1044                         break;
1045                 case 'i':
1046                 case 'p': {
1047                         ctladm_optret optret;
1048                         ctladm_cmdargs argnum;
1049                         const char *subopt;
1050
1051                         if (c == 'i') {
1052                                 ctl_lun_error err_type;
1053
1054                                 if (err_desc.lun_error != CTL_LUN_INJ_NONE) {
1055                                         warnx("%s: can't specify multiple -i "
1056                                               "arguments", __func__);
1057                                         retval = 1;
1058                                         goto bailout;
1059                                 }
1060                                 optret = getoption(cctl_err_types, optarg,
1061                                                    &err_type, &argnum, &subopt);
1062                                 err_desc.lun_error = err_type;
1063                         } else {
1064                                 ctl_lun_error_pattern pattern;
1065
1066                                 optret = getoption(cctl_err_patterns, optarg,
1067                                                    &pattern, &argnum, &subopt);
1068                                 err_desc.error_pattern |= pattern;
1069                         }
1070
1071                         if (optret == CC_OR_AMBIGUOUS) {
1072                                 warnx("%s: ambiguous argument %s", __func__,
1073                                       optarg);
1074                                 retval = 1;
1075                                 goto bailout;
1076                         } else if (optret == CC_OR_NOT_FOUND) {
1077                                 warnx("%s: argument %s not found", __func__,
1078                                       optarg);
1079                                 retval = 1;
1080                                 goto bailout;
1081                         }
1082                         break;
1083                 }
1084                 case 'r': {
1085                         char *tmpstr, *tmpstr2;
1086
1087                         tmpstr = strdup(optarg);
1088                         if (tmpstr == NULL) {
1089                                 warn("%s: error duplicating string %s",
1090                                      __func__, optarg);
1091                                 retval = 1;
1092                                 goto bailout;
1093                         }
1094
1095                         tmpstr2 = strsep(&tmpstr, ",");
1096                         if (tmpstr2 == NULL) {
1097                                 warnx("%s: invalid -r argument %s", __func__,
1098                                       optarg);
1099                                 retval = 1;
1100                                 free(tmpstr);
1101                                 goto bailout;
1102                         }
1103                         lba = strtoull(tmpstr2, NULL, 0);
1104                         tmpstr2 = strsep(&tmpstr, ",");
1105                         if (tmpstr2 == NULL) {
1106                                 warnx("%s: no len argument for -r lba,len, got"
1107                                       " %s", __func__, optarg);
1108                                 retval = 1;
1109                                 free(tmpstr);
1110                                 goto bailout;
1111                         }
1112                         len = strtoul(tmpstr2, NULL, 0);
1113                         free(tmpstr);
1114                         break;
1115                 }
1116                 case 's': {
1117                         struct get_hook hook;
1118                         char *sensestr;
1119
1120                         sense_len = strtol(optarg, NULL, 0);
1121                         if (sense_len <= 0) {
1122                                 warnx("invalid number of sense bytes %d",
1123                                       sense_len);
1124                                 retval = 1;
1125                                 goto bailout;
1126                         }
1127
1128                         sense_len = MIN(sense_len, SSD_FULL_SIZE);
1129
1130                         hook.argc = argc - optind;
1131                         hook.argv = argv + optind;
1132                         hook.got = 0;
1133
1134                         sensestr = cget(&hook, NULL);
1135                         if ((sensestr != NULL)
1136                          && (sensestr[0] == '-')) {
1137                                 fd_sense = 1;
1138                         } else {
1139                                 buff_encode_visit(
1140                                     (uint8_t *)&err_desc.custom_sense,
1141                                     sense_len, sensestr, iget, &hook);
1142                         }
1143                         optind += hook.got;
1144                         break;
1145                 }
1146                 default:
1147                         break;
1148                 }
1149         }
1150
1151         if (delete_id_set != 0) {
1152                 err_desc.serial = delete_id;
1153                 if (ioctl(fd, CTL_ERROR_INJECT_DELETE, &err_desc) == -1) {
1154                         warn("%s: error issuing CTL_ERROR_INJECT_DELETE ioctl",
1155                              __func__);
1156                         retval = 1;
1157                 }
1158                 goto bailout;
1159         }
1160
1161         if (err_desc.lun_error == CTL_LUN_INJ_NONE) {
1162                 warnx("%s: error injection command (-i) needed",
1163                       __func__);
1164                 retval = 1;
1165                 goto bailout;
1166         } else if ((err_desc.lun_error == CTL_LUN_INJ_CUSTOM)
1167                 && (sense_len == 0)) {
1168                 warnx("%s: custom error requires -s", __func__);
1169                 retval = 1;
1170                 goto bailout;
1171         }
1172
1173         if (continuous != 0)
1174                 err_desc.lun_error |= CTL_LUN_INJ_CONTINUOUS;
1175
1176         /*
1177          * If fd_sense is set, we need to read the sense data the user
1178          * wants returned from stdin.
1179          */
1180         if (fd_sense == 1) {
1181                 ssize_t amt_read;
1182                 int amt_to_read = sense_len;
1183                 u_int8_t *buf_ptr = (uint8_t *)&err_desc.custom_sense;
1184
1185                 for (amt_read = 0; amt_to_read > 0;
1186                      amt_read = read(STDIN_FILENO, buf_ptr, amt_to_read)) {
1187                         if (amt_read == -1) {
1188                                 warn("error reading sense data from stdin");
1189                                 retval = 1;
1190                                 goto bailout;
1191                         }
1192                         amt_to_read -= amt_read;
1193                         buf_ptr += amt_read;
1194                 }
1195         }
1196
1197         if (err_desc.error_pattern == CTL_LUN_PAT_NONE) {
1198                 warnx("%s: command pattern (-p) needed", __func__);
1199                 retval = 1;
1200                 goto bailout;
1201         }
1202
1203         if (len != 0) {
1204                 err_desc.error_pattern |= CTL_LUN_PAT_RANGE;
1205                 /*
1206                  * We could check here to see whether it's a read/write
1207                  * command, but that will be pointless once we allow
1208                  * custom patterns.  At that point, the user could specify
1209                  * a READ(6) CDB type, and we wouldn't have an easy way here
1210                  * to verify whether range checking is possible there.  The
1211                  * user will just figure it out when his error never gets
1212                  * executed.
1213                  */
1214 #if 0
1215                 if ((err_desc.pattern & CTL_LUN_PAT_READWRITE) == 0) {
1216                         warnx("%s: need read and/or write pattern if range "
1217                               "is specified", __func__);
1218                         retval = 1;
1219                         goto bailout;
1220                 }
1221 #endif
1222                 err_desc.lba_range.lba = lba;
1223                 err_desc.lba_range.len = len;
1224         }
1225
1226         if (ioctl(fd, CTL_ERROR_INJECT, &err_desc) == -1) {
1227                 warn("%s: error issuing CTL_ERROR_INJECT ioctl", __func__);
1228                 retval = 1;
1229         } else {
1230                 printf("Error injection succeeded, serial number is %ju\n",
1231                        (uintmax_t)err_desc.serial);
1232         }
1233 bailout:
1234
1235         return (retval);
1236 }
1237
1238 static int
1239 cctl_lunlist(int fd)
1240 {
1241         struct scsi_report_luns_data *lun_data;
1242         struct scsi_inquiry_data *inq_data;
1243         uint32_t num_luns;
1244         int target;
1245         int initid;
1246         unsigned int i;
1247         int retval;
1248
1249         retval = 0;
1250         inq_data = NULL;
1251
1252         target = 6;
1253         initid = 7;
1254
1255         /*
1256          * XXX KDM assuming LUN 0 is fine, but we may need to change this
1257          * if we ever acquire the ability to have multiple targets.
1258          */
1259         if ((retval = cctl_get_luns(fd, target, /*lun*/ 0, initid,
1260                                     /*retries*/ 2, &lun_data, &num_luns)) != 0)
1261                 goto bailout;
1262
1263         inq_data = malloc(sizeof(*inq_data));
1264         if (inq_data == NULL) {
1265                 warn("%s: couldn't allocate memory for inquiry data\n",
1266                      __func__);
1267                 retval = 1;
1268                 goto bailout;
1269         }
1270         for (i = 0; i < num_luns; i++) {
1271                 char scsi_path[40];
1272                 int lun_val;
1273
1274                 switch (lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) {
1275                 case RPL_LUNDATA_ATYP_PERIPH:
1276                         lun_val = lun_data->luns[i].lundata[1];
1277                         break;
1278                 case RPL_LUNDATA_ATYP_FLAT:
1279                         lun_val = (lun_data->luns[i].lundata[0] &
1280                                 RPL_LUNDATA_FLAT_LUN_MASK) |
1281                                 (lun_data->luns[i].lundata[1] <<
1282                                 RPL_LUNDATA_FLAT_LUN_BITS);
1283                         break;
1284                 case RPL_LUNDATA_ATYP_LUN:
1285                 case RPL_LUNDATA_ATYP_EXTLUN:
1286                 default:
1287                         fprintf(stdout, "Unsupported LUN format %d\n",
1288                                 lun_data->luns[i].lundata[0] &
1289                                 RPL_LUNDATA_ATYP_MASK);
1290                         lun_val = -1;
1291                         break;
1292                 }
1293                 if (lun_val == -1)
1294                         continue;
1295
1296                 if ((retval = cctl_get_inquiry(fd, target, lun_val, initid,
1297                                                /*retries*/ 2, scsi_path,
1298                                                sizeof(scsi_path),
1299                                                inq_data)) != 0) {
1300                         goto bailout;
1301                 }
1302                 printf("%s", scsi_path);
1303                 scsi_print_inquiry(inq_data);
1304         }
1305 bailout:
1306
1307         if (lun_data != NULL)
1308                 free(lun_data);
1309
1310         if (inq_data != NULL)
1311                 free(inq_data);
1312
1313         return (retval);
1314 }
1315
1316 static void
1317 cctl_cfi_mt_statusstr(cfi_mt_status status, char *str, int str_len)
1318 {
1319         switch (status) {
1320         case CFI_MT_PORT_OFFLINE:
1321                 snprintf(str, str_len, "Port Offline");
1322                 break;
1323         case CFI_MT_ERROR:
1324                 snprintf(str, str_len, "Error");
1325                 break;
1326         case CFI_MT_SUCCESS:
1327                 snprintf(str, str_len, "Success");
1328                 break;
1329         case CFI_MT_NONE:
1330                 snprintf(str, str_len, "None??");
1331                 break;
1332         default:
1333                 snprintf(str, str_len, "Unknown status: %d", status);
1334                 break;
1335         }
1336 }
1337
1338 static void
1339 cctl_cfi_bbr_statusstr(cfi_bbrread_status status, char *str, int str_len)
1340 {
1341         switch (status) {
1342         case CFI_BBR_SUCCESS:
1343                 snprintf(str, str_len, "Success");
1344                 break;
1345         case CFI_BBR_LUN_UNCONFIG:
1346                 snprintf(str, str_len, "LUN not configured");
1347                 break;
1348         case CFI_BBR_NO_LUN:
1349                 snprintf(str, str_len, "LUN does not exist");
1350                 break;
1351         case CFI_BBR_NO_MEM:
1352                 snprintf(str, str_len, "Memory allocation error");
1353                 break;
1354         case CFI_BBR_BAD_LEN:
1355                 snprintf(str, str_len, "Length is not a multiple of blocksize");
1356                 break;
1357         case CFI_BBR_RESERV_CONFLICT:
1358                 snprintf(str, str_len, "Reservation conflict");
1359                 break;
1360         case CFI_BBR_LUN_STOPPED:
1361                 snprintf(str, str_len, "LUN is powered off");
1362                 break;
1363         case CFI_BBR_LUN_OFFLINE_CTL:
1364                 snprintf(str, str_len, "LUN is offline");
1365                 break;
1366         case CFI_BBR_LUN_OFFLINE_RC:
1367                 snprintf(str, str_len, "RAIDCore array is offline (double "
1368                          "failure?)");
1369                 break;
1370         case CFI_BBR_SCSI_ERROR:
1371                 snprintf(str, str_len, "SCSI Error");
1372                 break;
1373         case CFI_BBR_ERROR:
1374                 snprintf(str, str_len, "Error");
1375                 break;
1376         default:
1377                 snprintf(str, str_len, "Unknown status: %d", status);
1378                 break;
1379         }
1380 }
1381
1382 static int
1383 cctl_hardstopstart(int fd, ctladm_cmdfunction command)
1384 {
1385         struct ctl_hard_startstop_info hs_info;
1386         char error_str[256];
1387         int do_start;
1388         int retval;
1389
1390         retval = 0;
1391
1392         if (command == CTLADM_CMD_HARDSTART)
1393                 do_start = 1;
1394         else
1395                 do_start = 0;
1396
1397         if (ioctl(fd, (do_start == 1) ? CTL_HARD_START : CTL_HARD_STOP,
1398                   &hs_info) == -1) {
1399                 warn("%s: CTL_HARD_%s ioctl failed", __func__,
1400                      (do_start == 1) ? "START" : "STOP");
1401                 retval = 1;
1402                 goto bailout;
1403         }
1404
1405         fprintf(stdout, "Hard %s Status: ", (command == CTLADM_CMD_HARDSTOP) ?
1406                 "Stop" : "Start");
1407         cctl_cfi_mt_statusstr(hs_info.status, error_str, sizeof(error_str));
1408         fprintf(stdout, "%s\n", error_str);
1409         fprintf(stdout, "Total LUNs: %d\n", hs_info.total_luns);
1410         fprintf(stdout, "LUNs complete: %d\n", hs_info.luns_complete);
1411         fprintf(stdout, "LUNs failed: %d\n", hs_info.luns_failed);
1412
1413 bailout:
1414         return (retval);
1415 }
1416
1417 static int
1418 cctl_bbrread(int fd, int target __unused, int lun, int iid __unused,
1419              int argc, char **argv, char *combinedopt)
1420 {
1421         struct ctl_bbrread_info bbr_info;
1422         char error_str[256];
1423         int datalen = -1;
1424         uint64_t lba = 0;
1425         int lba_set = 0;
1426         int retval;
1427         int c;
1428
1429         retval = 0;
1430
1431         while ((c = getopt(argc, argv, combinedopt)) != -1) {
1432                 switch (c) {
1433                 case 'd':
1434                         datalen = strtoul(optarg, NULL, 0);
1435                         break;
1436                 case 'l':
1437                         lba = strtoull(optarg, NULL, 0);
1438                         lba_set = 1;
1439                         break;
1440                 default:
1441                         break;
1442                 }
1443         }
1444
1445         if (lba_set == 0) {
1446                 warnx("%s: you must specify an LBA with -l", __func__);
1447                 retval = 1;
1448                 goto bailout;
1449         }
1450
1451         if (datalen == -1) {
1452                 warnx("%s: you must specify a length with -d", __func__);
1453                 retval = 1;
1454                 goto bailout;
1455         }
1456
1457         bbr_info.lun_num = lun;
1458         bbr_info.lba = lba;
1459         /*
1460          * XXX KDM get the blocksize first??
1461          */
1462         if ((datalen % 512) != 0) {
1463                 warnx("%s: data length %d is not a multiple of 512 bytes",
1464                      __func__, datalen);
1465                 retval = 1;
1466                 goto bailout;
1467         }
1468         bbr_info.len = datalen;
1469
1470         if (ioctl(fd, CTL_BBRREAD, &bbr_info) == -1) {
1471                 warn("%s: CTL_BBRREAD ioctl failed", __func__);
1472                 retval = 1;
1473                 goto bailout;
1474         }
1475         cctl_cfi_mt_statusstr(bbr_info.status, error_str, sizeof(error_str));
1476         fprintf(stdout, "BBR Read Overall Status: %s\n", error_str);
1477         cctl_cfi_bbr_statusstr(bbr_info.bbr_status, error_str,
1478                                sizeof(error_str));
1479         fprintf(stdout, "BBR Read Status: %s\n", error_str);
1480         /*
1481          * XXX KDM should we bother printing out SCSI status if we get
1482          * CFI_BBR_SCSI_ERROR back?
1483          *
1484          * Return non-zero if this fails?
1485          */
1486 bailout:
1487         return (retval);
1488 }
1489
1490 static int
1491 cctl_startup_shutdown(int fd, int target, int lun, int iid,
1492                       ctladm_cmdfunction command)
1493 {
1494         union ctl_io *io;
1495         struct ctl_id id;
1496         struct scsi_report_luns_data *lun_data;
1497         struct scsi_inquiry_data *inq_data;
1498         uint32_t num_luns;
1499         unsigned int i;
1500         int retval;
1501
1502         retval = 0;
1503         inq_data = NULL;
1504
1505         /*
1506          * - report luns
1507          * - step through each lun, do an inquiry
1508          * - check OOA queue on direct access luns
1509          * - send stop with offline bit to each direct access device with a
1510          *   clear OOA queue
1511          *   - if we get a reservation conflict, reset the LUN to clear it
1512          *     and reissue the stop with the offline bit set
1513          */
1514
1515         id.id = iid;
1516
1517         io = ctl_scsi_alloc_io(id);
1518         if (io == NULL) {
1519                 warnx("%s: can't allocate memory", __func__);
1520                 return (1);
1521         }
1522
1523         if ((retval = cctl_get_luns(fd, target, lun, iid, /*retries*/ 2,
1524                                     &lun_data, &num_luns)) != 0)
1525                 goto bailout;
1526
1527         inq_data = malloc(sizeof(*inq_data));
1528         if (inq_data == NULL) {
1529                 warn("%s: couldn't allocate memory for inquiry data\n",
1530                      __func__);
1531                 retval = 1;
1532                 goto bailout;
1533         }
1534         for (i = 0; i < num_luns; i++) {
1535                 char scsi_path[40];
1536                 int lun_val;
1537
1538                 /*
1539                  * XXX KDM figure out a way to share this code with
1540                  * cctl_lunlist()?
1541                  */
1542                 switch (lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) {
1543                 case RPL_LUNDATA_ATYP_PERIPH:
1544                         lun_val = lun_data->luns[i].lundata[1];
1545                         break;
1546                 case RPL_LUNDATA_ATYP_FLAT:
1547                         lun_val = (lun_data->luns[i].lundata[0] &
1548                                 RPL_LUNDATA_FLAT_LUN_MASK) |
1549                                 (lun_data->luns[i].lundata[1] <<
1550                                 RPL_LUNDATA_FLAT_LUN_BITS);
1551                         break;
1552                 case RPL_LUNDATA_ATYP_LUN:
1553                 case RPL_LUNDATA_ATYP_EXTLUN:
1554                 default:
1555                         fprintf(stdout, "Unsupported LUN format %d\n",
1556                                 lun_data->luns[i].lundata[0] &
1557                                 RPL_LUNDATA_ATYP_MASK);
1558                         lun_val = -1;
1559                         break;
1560                 }
1561                 if (lun_val == -1)
1562                         continue;
1563
1564                 if ((retval = cctl_get_inquiry(fd, target, lun_val, iid,
1565                                                /*retries*/ 2, scsi_path,
1566                                                sizeof(scsi_path),
1567                                                inq_data)) != 0) {
1568                         goto bailout;
1569                 }
1570                 printf("%s", scsi_path);
1571                 scsi_print_inquiry(inq_data);
1572                 /*
1573                  * We only want to shutdown direct access devices.
1574                  */
1575                 if (SID_TYPE(inq_data) != T_DIRECT) {
1576                         printf("%s LUN is not direct access, skipped\n",
1577                                scsi_path);
1578                         continue;
1579                 }
1580
1581                 if (command == CTLADM_CMD_SHUTDOWN) {
1582                         struct ctl_ooa_info ooa_info;
1583
1584                         ooa_info.target_id = target;
1585                         ooa_info.lun_id = lun_val;
1586
1587                         if (ioctl(fd, CTL_CHECK_OOA, &ooa_info) == -1) {
1588                                 printf("%s CTL_CHECK_OOA ioctl failed\n",
1589                                        scsi_path);
1590                                 continue;
1591                         }
1592
1593                         if (ooa_info.status != CTL_OOA_SUCCESS) {
1594                                 printf("%s CTL_CHECK_OOA returned status %d\n", 
1595                                        scsi_path, ooa_info.status);
1596                                 continue;
1597                         }
1598                         if (ooa_info.num_entries != 0) {
1599                                 printf("%s %d entr%s in the OOA queue, "
1600                                        "skipping shutdown\n", scsi_path,
1601                                        ooa_info.num_entries,
1602                                        (ooa_info.num_entries > 1)?"ies" : "y" );
1603                                 continue;
1604                         }
1605                 }
1606                 
1607                 ctl_scsi_start_stop(/*io*/ io,
1608                                     /*start*/(command == CTLADM_CMD_STARTUP) ?
1609                                               1 : 0,
1610                                     /*load_eject*/ 0,
1611                                     /*immediate*/ 0,
1612                                     /*power_conditions*/ SSS_PC_START_VALID,
1613                                     /*onoffline*/ 1,
1614                                     /*ctl_tag_type*/
1615                                     (command == CTLADM_CMD_STARTUP) ?
1616                                     CTL_TAG_SIMPLE :CTL_TAG_ORDERED,
1617                                     /*control*/ 0);
1618
1619                 io->io_hdr.nexus.targ_target.id = target;
1620                 io->io_hdr.nexus.targ_lun = lun_val;
1621                 io->io_hdr.nexus.initid = id;
1622
1623                 if (cctl_do_io(fd, /*retries*/ 3, io, __func__) != 0) {
1624                         retval = 1;
1625                         goto bailout;
1626                 }
1627
1628                 if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)
1629                         ctl_io_error_print(io, inq_data, stderr);
1630                 else {
1631                         printf("%s LUN is now %s\n", scsi_path,
1632                                (command == CTLADM_CMD_STARTUP) ? "online" :
1633                                "offline");
1634                 }
1635         }
1636 bailout:
1637         if (lun_data != NULL)
1638                 free(lun_data);
1639
1640         if (inq_data != NULL)
1641                 free(inq_data);
1642
1643         if (io != NULL)
1644                 ctl_scsi_free_io(io);
1645
1646         return (retval);
1647 }
1648
1649 static int
1650 cctl_sync_cache(int fd, int target, int lun, int iid, int retries,
1651                 int argc, char **argv, char *combinedopt)
1652 {
1653         union ctl_io *io;
1654         struct ctl_id id;
1655         int cdb_size = -1;
1656         int retval;
1657         uint64_t our_lba = 0;
1658         uint32_t our_block_count = 0;
1659         int reladr = 0, immed = 0; 
1660         int c;
1661
1662         id.id = iid;
1663         retval = 0;
1664
1665         io = ctl_scsi_alloc_io(id);
1666         if (io == NULL) {
1667                 warnx("%s: can't allocate memory", __func__);
1668                 return (1);
1669         }
1670
1671         while ((c = getopt(argc, argv, combinedopt)) != -1) {
1672                 switch (c) {
1673                 case 'b':
1674                         our_block_count = strtoul(optarg, NULL, 0);
1675                         break;
1676                 case 'c':
1677                         cdb_size = strtol(optarg, NULL, 0);
1678                         break;
1679                 case 'i':
1680                         immed = 1;
1681                         break;
1682                 case 'l':
1683                         our_lba = strtoull(optarg, NULL, 0);
1684                         break;
1685                 case 'r':
1686                         reladr = 1;
1687                         break;
1688                 default:
1689                         break;
1690                 }
1691         }
1692
1693         if (cdb_size != -1) {
1694                 switch (cdb_size) {
1695                 case 10:
1696                 case 16:
1697                         break;
1698                 default:
1699                         warnx("%s: invalid cdbsize %d, valid sizes are 10 "
1700                               "and 16", __func__, cdb_size);
1701                         retval = 1;
1702                         goto bailout;
1703                         break; /* NOTREACHED */
1704                 }
1705         } else
1706                 cdb_size = 10;
1707
1708         ctl_scsi_sync_cache(/*io*/ io,
1709                             /*immed*/ immed,
1710                             /*reladr*/ reladr,
1711                             /*minimum_cdb_size*/ cdb_size,
1712                             /*starting_lba*/ our_lba,
1713                             /*block_count*/ our_block_count,
1714                             /*tag_type*/ CTL_TAG_SIMPLE,
1715                             /*control*/ 0);
1716
1717         io->io_hdr.nexus.targ_target.id = target;
1718         io->io_hdr.nexus.targ_lun = lun;
1719         io->io_hdr.nexus.initid = id;
1720
1721         if (cctl_do_io(fd, retries, io, __func__) != 0) {
1722                 retval = 1;
1723                 goto bailout;
1724         }
1725
1726         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
1727                 fprintf(stdout, "Cache synchronized successfully\n");
1728         } else
1729                 ctl_io_error_print(io, NULL, stderr);
1730 bailout:
1731         ctl_scsi_free_io(io);
1732
1733         return (retval);
1734 }
1735
1736 static int
1737 cctl_start_stop(int fd, int target, int lun, int iid, int retries, int start,
1738                 int argc, char **argv, char *combinedopt)
1739 {
1740         union ctl_io *io;
1741         struct ctl_id id;
1742         char scsi_path[40];
1743         int immed = 0, onoffline = 0;
1744         int retval, c;
1745
1746         id.id = iid;
1747         retval = 0;
1748
1749         io = ctl_scsi_alloc_io(id);
1750         if (io == NULL) {
1751                 warnx("%s: can't allocate memory", __func__);
1752                 return (1);
1753         }
1754
1755         while ((c = getopt(argc, argv, combinedopt)) != -1) {
1756                 switch (c) {
1757                 case 'i':
1758                         immed = 1;
1759                         break;
1760                 case 'o':
1761                         onoffline = 1;
1762                         break;
1763                 default:
1764                         break;
1765                 }
1766         }
1767         /*
1768          * Use an ordered tag for the stop command, to guarantee that any
1769          * pending I/O will finish before the stop command executes.  This
1770          * would normally be the case anyway, since CTL will basically
1771          * treat the start/stop command as an ordered command with respect
1772          * to any other command except an INQUIRY.  (See ctl_ser_table.c.)
1773          */
1774         ctl_scsi_start_stop(/*io*/ io,
1775                             /*start*/ start,
1776                             /*load_eject*/ 0,
1777                             /*immediate*/ immed,
1778                             /*power_conditions*/ SSS_PC_START_VALID,
1779                             /*onoffline*/ onoffline,
1780                             /*ctl_tag_type*/ start ? CTL_TAG_SIMPLE :
1781                                                      CTL_TAG_ORDERED,
1782                             /*control*/ 0);
1783
1784         io->io_hdr.nexus.targ_target.id = target;
1785         io->io_hdr.nexus.targ_lun = lun;
1786         io->io_hdr.nexus.initid = id;
1787
1788         if (cctl_do_io(fd, retries, io, __func__) != 0) {
1789                 retval = 1;
1790                 goto bailout;
1791         }
1792
1793         ctl_scsi_path_string(io, scsi_path, sizeof(scsi_path));
1794         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
1795                 fprintf(stdout, "%s LUN %s successfully\n", scsi_path,
1796                         (start) ?  "started" : "stopped");
1797         } else
1798                 ctl_io_error_print(io, NULL, stderr);
1799
1800 bailout:
1801         ctl_scsi_free_io(io);
1802
1803         return (retval);
1804 }
1805
1806 static int
1807 cctl_mode_sense(int fd, int target, int lun, int iid, int retries, 
1808                 int argc, char **argv, char *combinedopt)
1809 {
1810         union ctl_io *io;
1811         struct ctl_id id;
1812         uint32_t datalen;
1813         uint8_t *dataptr;
1814         int pc = -1, cdbsize, retval, dbd = 0, subpage = -1;
1815         int list = 0;
1816         int page_code = -1;
1817         int c;
1818
1819         id.id = iid;
1820         cdbsize = 0;
1821         retval = 0;
1822         dataptr = NULL;
1823
1824         io = ctl_scsi_alloc_io(id);
1825         if (io == NULL) {
1826                 warn("%s: can't allocate memory", __func__);
1827                 return (1);
1828         }
1829
1830         while ((c = getopt(argc, argv, combinedopt)) != -1) {
1831                 switch (c) {
1832                 case 'P':
1833                         pc = strtoul(optarg, NULL, 0);
1834                         break;
1835                 case 'S':
1836                         subpage = strtoul(optarg, NULL, 0);
1837                         break;
1838                 case 'd':
1839                         dbd = 1;
1840                         break;
1841                 case 'l':
1842                         list = 1;
1843                         break;
1844                 case 'm':
1845                         page_code = strtoul(optarg, NULL, 0);
1846                         break;
1847                 case 'c':
1848                         cdbsize = strtol(optarg, NULL, 0);
1849                         break;
1850                 default:
1851                         break;
1852                 }
1853         }
1854
1855         if (((list == 0) && (page_code == -1))
1856          || ((list != 0) && (page_code != -1))) {
1857                 warnx("%s: you must specify either a page code (-m) or -l",
1858                       __func__);
1859                 retval = 1;
1860                 goto bailout;
1861         }
1862
1863         if ((page_code != -1)
1864          && ((page_code > SMS_ALL_PAGES_PAGE)
1865           || (page_code < 0))) {
1866                 warnx("%s: page code %d is out of range", __func__,
1867                       page_code);
1868                 retval = 1;
1869                 goto bailout;
1870         }
1871
1872         if (list == 1) {
1873                 page_code = SMS_ALL_PAGES_PAGE;
1874                 if (pc != -1) {
1875                         warnx("%s: arg -P makes no sense with -l",
1876                               __func__);
1877                         retval = 1;
1878                         goto bailout;
1879                 }
1880                 if (subpage != -1) {
1881                         warnx("%s: arg -S makes no sense with -l", __func__);
1882                         retval = 1;
1883                         goto bailout;
1884                 }
1885         }
1886
1887         if (pc == -1)
1888                 pc = SMS_PAGE_CTRL_CURRENT;
1889         else {
1890                 if ((pc > 3)
1891                  || (pc < 0)) {
1892                         warnx("%s: page control value %d is out of range: 0-3",
1893                               __func__, pc);
1894                         retval = 1;
1895                         goto bailout;
1896                 }
1897         }
1898
1899
1900         if ((subpage != -1)
1901          && ((subpage > 255)
1902           || (subpage < 0))) {
1903                 warnx("%s: subpage code %d is out of range: 0-255", __func__,
1904                       subpage);
1905                 retval = 1;
1906                 goto bailout;
1907         }
1908         if (cdbsize != 0) {
1909                 switch (cdbsize) {
1910                 case 6:
1911                 case 10:
1912                         break;
1913                 default:
1914                         warnx("%s: invalid cdbsize %d, valid sizes are 6 "
1915                               "and 10", __func__, cdbsize);
1916                         retval = 1;
1917                         goto bailout;
1918                         break;
1919                 }
1920         } else
1921                 cdbsize = 6;
1922
1923         if (subpage == -1)
1924                 subpage = 0;
1925
1926         if (cdbsize == 6)
1927                 datalen = 255;
1928         else
1929                 datalen = 65535;
1930
1931         dataptr = (uint8_t *)malloc(datalen);
1932         if (dataptr == NULL) {
1933                 warn("%s: can't allocate %d bytes", __func__, datalen);
1934                 retval = 1;
1935                 goto bailout;
1936         }
1937
1938         memset(dataptr, 0, datalen);
1939
1940         ctl_scsi_mode_sense(io,
1941                             /*data_ptr*/ dataptr,
1942                             /*data_len*/ datalen,
1943                             /*dbd*/ dbd,
1944                             /*llbaa*/ 0,
1945                             /*page_code*/ page_code,
1946                             /*pc*/ pc << 6,
1947                             /*subpage*/ subpage,
1948                             /*minimum_cdb_size*/ cdbsize,
1949                             /*tag_type*/ CTL_TAG_SIMPLE,
1950                             /*control*/ 0);
1951
1952         io->io_hdr.nexus.targ_target.id = target;
1953         io->io_hdr.nexus.targ_lun = lun;
1954         io->io_hdr.nexus.initid = id;
1955
1956         if (cctl_do_io(fd, retries, io, __func__) != 0) {
1957                 retval = 1;
1958                 goto bailout;
1959         }
1960
1961         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
1962                 int pages_len, used_len;
1963                 uint32_t returned_len;
1964                 uint8_t *ndataptr;
1965
1966                 if (io->scsiio.cdb[0] == MODE_SENSE_6) {
1967                         struct scsi_mode_hdr_6 *hdr6;
1968                         int bdlen;
1969
1970                         hdr6 = (struct scsi_mode_hdr_6 *)dataptr;
1971
1972                         returned_len = hdr6->datalen + 1;
1973                         bdlen = hdr6->block_descr_len;
1974
1975                         ndataptr = (uint8_t *)((uint8_t *)&hdr6[1] + bdlen);
1976                 } else {
1977                         struct scsi_mode_hdr_10 *hdr10;
1978                         int bdlen;
1979
1980                         hdr10 = (struct scsi_mode_hdr_10 *)dataptr;
1981
1982                         returned_len = scsi_2btoul(hdr10->datalen) + 2;
1983                         bdlen = scsi_2btoul(hdr10->block_descr_len);
1984
1985                         ndataptr = (uint8_t *)((uint8_t *)&hdr10[1] + bdlen);
1986                 }
1987                 /* just in case they can give us more than we allocated for */
1988                 returned_len = min(returned_len, datalen);
1989                 pages_len = returned_len - (ndataptr - dataptr);
1990 #if 0
1991                 fprintf(stdout, "returned_len = %d, pages_len = %d\n",
1992                         returned_len, pages_len);
1993 #endif
1994                 if (list == 1) {
1995                         fprintf(stdout, "Supported mode pages:\n");
1996                         for (used_len = 0; used_len < pages_len;) {
1997                                 struct scsi_mode_page_header *header;
1998
1999                                 header = (struct scsi_mode_page_header *)
2000                                         &ndataptr[used_len];
2001                                 fprintf(stdout, "%d\n", header->page_code);
2002                                 used_len += header->page_length + 2;
2003                         }
2004                 } else {
2005                         for (used_len = 0; used_len < pages_len; used_len++) {
2006                                 fprintf(stdout, "0x%x ", ndataptr[used_len]);
2007                                 if (((used_len+1) % 16) == 0)
2008                                         fprintf(stdout, "\n");
2009                         }
2010                         fprintf(stdout, "\n");
2011                 }
2012         } else
2013                 ctl_io_error_print(io, NULL, stderr);
2014 bailout:
2015
2016         ctl_scsi_free_io(io);
2017
2018         if (dataptr != NULL)
2019                 free(dataptr);
2020
2021         return (retval);
2022 }
2023
2024 static int
2025 cctl_read_capacity(int fd, int target, int lun, int iid, int retries, 
2026                    int argc, char **argv, char *combinedopt)
2027 {
2028         union ctl_io *io;
2029         struct ctl_id id;
2030         struct scsi_read_capacity_data *data;
2031         struct scsi_read_capacity_data_long *longdata;
2032         int cdbsize = -1, retval;
2033         uint8_t *dataptr;
2034         int c;
2035
2036         cdbsize = 10;
2037         dataptr = NULL;
2038         retval = 0;
2039         id.id = iid;
2040
2041         io = ctl_scsi_alloc_io(id);
2042         if (io == NULL) {
2043                 warn("%s: can't allocate memory\n", __func__);
2044                 return (1);
2045         }
2046
2047         while ((c = getopt(argc, argv, combinedopt)) != -1) {
2048                 switch (c) {
2049                 case 'c':
2050                         cdbsize = strtol(optarg, NULL, 0);
2051                         break;
2052                 default:
2053                         break;
2054                 }
2055         }
2056         if (cdbsize != -1) {
2057                 switch (cdbsize) {
2058                 case 10:
2059                 case 16:
2060                         break;
2061                 default:
2062                         warnx("%s: invalid cdbsize %d, valid sizes are 10 "
2063                               "and 16", __func__, cdbsize);
2064                         retval = 1;
2065                         goto bailout;
2066                         break; /* NOTREACHED */
2067                 }
2068         } else
2069                 cdbsize = 10;
2070
2071         dataptr = (uint8_t *)malloc(sizeof(*longdata));
2072         if (dataptr == NULL) {
2073                 warn("%s: can't allocate %zd bytes\n", __func__,
2074                      sizeof(*longdata));
2075                 retval = 1;
2076                 goto bailout;
2077         }
2078         memset(dataptr, 0, sizeof(*longdata));
2079
2080 retry:
2081
2082         switch (cdbsize) {
2083         case 10:
2084                 ctl_scsi_read_capacity(io,
2085                                        /*data_ptr*/ dataptr,
2086                                        /*data_len*/ sizeof(*longdata),
2087                                        /*addr*/ 0,
2088                                        /*reladr*/ 0,
2089                                        /*pmi*/ 0,
2090                                        /*tag_type*/ CTL_TAG_SIMPLE,
2091                                        /*control*/ 0);
2092                 break;
2093         case 16:
2094                 ctl_scsi_read_capacity_16(io,
2095                                           /*data_ptr*/ dataptr,
2096                                           /*data_len*/ sizeof(*longdata),
2097                                           /*addr*/ 0,
2098                                           /*reladr*/ 0,
2099                                           /*pmi*/ 0,
2100                                           /*tag_type*/ CTL_TAG_SIMPLE,
2101                                           /*control*/ 0);
2102                 break;
2103         }
2104
2105         io->io_hdr.nexus.initid = id;
2106         io->io_hdr.nexus.targ_target.id = target;
2107         io->io_hdr.nexus.targ_lun = lun;
2108
2109         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2110                 retval = 1;
2111                 goto bailout;
2112         }
2113
2114         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2115                 uint64_t maxlba;
2116                 uint32_t blocksize;
2117
2118                 if (cdbsize == 10) {
2119
2120                         data = (struct scsi_read_capacity_data *)dataptr;
2121
2122                         maxlba = scsi_4btoul(data->addr);
2123                         blocksize = scsi_4btoul(data->length);
2124
2125                         if (maxlba == 0xffffffff) {
2126                                 cdbsize = 16;
2127                                 goto retry;
2128                         }
2129                 } else {
2130                         longdata=(struct scsi_read_capacity_data_long *)dataptr;
2131
2132                         maxlba = scsi_8btou64(longdata->addr);
2133                         blocksize = scsi_4btoul(longdata->length);
2134                 }
2135
2136                 fprintf(stdout, "Disk Capacity: %ju, Blocksize: %d\n",
2137                         (uintmax_t)maxlba, blocksize);
2138         } else {
2139                 ctl_io_error_print(io, NULL, stderr);
2140         }
2141 bailout:
2142         ctl_scsi_free_io(io);
2143
2144         if (dataptr != NULL)
2145                 free(dataptr);
2146
2147         return (retval);
2148 }
2149
2150 static int
2151 cctl_read_write(int fd, int target, int lun, int iid, int retries,
2152                 int argc, char **argv, char *combinedopt,
2153                 ctladm_cmdfunction command)
2154 {
2155         union ctl_io *io;
2156         struct ctl_id id;
2157         int file_fd, do_stdio;
2158         int cdbsize = -1, databytes;
2159         uint8_t *dataptr;
2160         char *filename = NULL;
2161         int datalen = -1, blocksize = -1;
2162         uint64_t lba = 0;
2163         int lba_set = 0;
2164         int retval;
2165         int c;
2166
2167         retval = 0;
2168         do_stdio = 0;
2169         dataptr = NULL;
2170         file_fd = -1;
2171         id.id = iid;
2172
2173         io = ctl_scsi_alloc_io(id);
2174         if (io == NULL) {
2175                 warn("%s: can't allocate memory\n", __func__);
2176                 return (1);
2177         }
2178
2179         while ((c = getopt(argc, argv, combinedopt)) != -1) {
2180                 switch (c) {
2181                 case 'N':
2182                         io->io_hdr.flags |= CTL_FLAG_NO_DATAMOVE;
2183                         break;
2184                 case 'b':
2185                         blocksize = strtoul(optarg, NULL, 0);
2186                         break;
2187                 case 'c':
2188                         cdbsize = strtoul(optarg, NULL, 0);
2189                         break;
2190                 case 'd':
2191                         datalen = strtoul(optarg, NULL, 0);
2192                         break;
2193                 case 'f':
2194                         filename = strdup(optarg);
2195                         break;
2196                 case 'l':
2197                         lba = strtoull(optarg, NULL, 0);
2198                         lba_set = 1;
2199                         break;
2200                 default:
2201                         break;
2202                 }
2203         }
2204         if (filename == NULL) {
2205                 warnx("%s: you must supply a filename using -f", __func__);
2206                 retval = 1;
2207                 goto bailout;
2208         }
2209
2210         if (datalen == -1) {
2211                 warnx("%s: you must specify the data length with -d", __func__);
2212                 retval = 1;
2213                 goto bailout;
2214         }
2215
2216         if (lba_set == 0) {
2217                 warnx("%s: you must specify the LBA with -l", __func__);
2218                 retval = 1;
2219                 goto bailout;
2220         }
2221
2222         if (blocksize == -1) {
2223                 warnx("%s: you must specify the blocksize with -b", __func__);
2224                 retval = 1;
2225                 goto bailout;
2226         }
2227
2228         if (cdbsize != -1) {
2229                 switch (cdbsize) {
2230                 case 6:
2231                 case 10:
2232                 case 12:
2233                 case 16:
2234                         break;
2235                 default:
2236                         warnx("%s: invalid cdbsize %d, valid sizes are 6, "
2237                               "10, 12 or 16", __func__, cdbsize);
2238                         retval = 1;
2239                         goto bailout;
2240                         break; /* NOTREACHED */
2241                 }
2242         } else
2243                 cdbsize = 6;
2244
2245         databytes = datalen * blocksize;
2246         dataptr = (uint8_t *)malloc(databytes);
2247
2248         if (dataptr == NULL) {
2249                 warn("%s: can't allocate %d bytes\n", __func__, databytes);
2250                 retval = 1;
2251                 goto bailout;
2252         }
2253         if (strcmp(filename, "-") == 0) {
2254                 if (command == CTLADM_CMD_READ)
2255                         file_fd = STDOUT_FILENO;
2256                 else
2257                         file_fd = STDIN_FILENO;
2258                 do_stdio = 1;
2259         } else {
2260                 file_fd = open(filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
2261                 if (file_fd == -1) {
2262                         warn("%s: can't open file %s", __func__, filename);
2263                         retval = 1;
2264                         goto bailout;
2265                 }
2266         }
2267
2268         memset(dataptr, 0, databytes);
2269
2270         if (command == CTLADM_CMD_WRITE) {
2271                 int bytes_read;
2272
2273                 bytes_read = read(file_fd, dataptr, databytes);
2274                 if (bytes_read == -1) {
2275                         warn("%s: error reading file %s", __func__, filename);
2276                         retval = 1;
2277                         goto bailout;
2278                 }
2279                 if (bytes_read != databytes) {
2280                         warnx("%s: only read %d bytes from file %s",
2281                               __func__, bytes_read, filename);
2282                         retval = 1;
2283                         goto bailout;
2284                 }
2285         }
2286         ctl_scsi_read_write(io,
2287                             /*data_ptr*/ dataptr,
2288                             /*data_len*/ databytes,
2289                             /*read_op*/ (command == CTLADM_CMD_READ) ? 1 : 0,
2290                             /*byte2*/ 0,
2291                             /*minimum_cdb_size*/ cdbsize,
2292                             /*lba*/ lba,
2293                             /*num_blocks*/ datalen,
2294                             /*tag_type*/ CTL_TAG_SIMPLE,
2295                             /*control*/ 0);
2296
2297         io->io_hdr.nexus.targ_target.id = target;
2298         io->io_hdr.nexus.targ_lun = lun;
2299         io->io_hdr.nexus.initid = id;
2300
2301         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2302                 retval = 1;
2303                 goto bailout;
2304         }
2305
2306         if (((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS)
2307          && (command == CTLADM_CMD_READ)) {
2308                 int bytes_written;
2309
2310                 bytes_written = write(file_fd, dataptr, databytes);
2311                 if (bytes_written == -1) {
2312                         warn("%s: can't write to %s", __func__, filename);
2313                         goto bailout;
2314                 }
2315         } else if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS)
2316                 ctl_io_error_print(io, NULL, stderr);
2317
2318
2319 bailout:
2320
2321         ctl_scsi_free_io(io);
2322
2323         if (dataptr != NULL)
2324                 free(dataptr);
2325
2326         if ((do_stdio == 0)
2327          && (file_fd != -1))
2328                 close(file_fd);
2329
2330         return (retval);
2331 }
2332
2333 static int
2334 cctl_get_luns(int fd, int target, int lun, int iid, int retries, struct
2335               scsi_report_luns_data **lun_data, uint32_t *num_luns)
2336 {
2337         union ctl_io *io;
2338         struct ctl_id id;
2339         uint32_t nluns;
2340         int lun_datalen;
2341         int retval;
2342
2343         retval = 0;
2344         id.id = iid;
2345
2346         io = ctl_scsi_alloc_io(id);
2347         if (io == NULL) {
2348                 warnx("%s: can't allocate memory", __func__);
2349                 return (1);
2350         }
2351
2352         /*
2353          * lun_data includes space for 1 lun, allocate space for 4 initially.
2354          * If that isn't enough, we'll allocate more.
2355          */
2356         nluns = 4;
2357 retry:
2358         lun_datalen = sizeof(*lun_data) +
2359                 (nluns * sizeof(struct scsi_report_luns_lundata));
2360         *lun_data = malloc(lun_datalen);
2361
2362         if (*lun_data == NULL) {
2363                 warnx("%s: can't allocate memory", __func__);
2364                 ctl_scsi_free_io(io);
2365                 return (1);
2366         }
2367
2368         ctl_scsi_report_luns(io,
2369                              /*data_ptr*/ (uint8_t *)*lun_data,
2370                              /*data_len*/ lun_datalen,
2371                              /*select_report*/ RPL_REPORT_ALL,
2372                              /*tag_type*/ CTL_TAG_SIMPLE,
2373                              /*control*/ 0);
2374
2375         io->io_hdr.nexus.initid = id;
2376         io->io_hdr.nexus.targ_target.id = target;
2377         io->io_hdr.nexus.targ_lun = lun;
2378
2379         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2380                 retval = 1;
2381                 goto bailout;
2382         }
2383
2384         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2385                 uint32_t returned_len, returned_luns;
2386
2387                 returned_len = scsi_4btoul((*lun_data)->length);
2388                 returned_luns = returned_len / 8;
2389                 if (returned_luns > nluns) {
2390                         nluns = returned_luns;
2391                         free(*lun_data);
2392                         goto retry;
2393                 }
2394                 /* These should be the same */
2395                 *num_luns = MIN(returned_luns, nluns);
2396         } else {
2397                 ctl_io_error_print(io, NULL, stderr);
2398                 retval = 1;
2399         }
2400 bailout:
2401         ctl_scsi_free_io(io);
2402
2403         return (retval);
2404 }
2405
2406 static int
2407 cctl_report_luns(int fd, int target, int lun, int iid, int retries)
2408 {
2409         struct scsi_report_luns_data *lun_data;
2410         uint32_t num_luns, i;
2411         int retval;
2412
2413         lun_data = NULL;
2414
2415         if ((retval = cctl_get_luns(fd, target, lun, iid, retries, &lun_data,
2416                                    &num_luns)) != 0)
2417                 goto bailout;
2418
2419         fprintf(stdout, "%u LUNs returned\n", num_luns);
2420         for (i = 0; i < num_luns; i++) {
2421                 int lun_val;
2422
2423                 /*
2424                  * XXX KDM figure out a way to share this code with
2425                  * cctl_lunlist()?
2426                  */
2427                 switch (lun_data->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) {
2428                 case RPL_LUNDATA_ATYP_PERIPH:
2429                         lun_val = lun_data->luns[i].lundata[1];
2430                         break;
2431                 case RPL_LUNDATA_ATYP_FLAT:
2432                         lun_val = (lun_data->luns[i].lundata[0] &
2433                                 RPL_LUNDATA_FLAT_LUN_MASK) |
2434                                 (lun_data->luns[i].lundata[1] <<
2435                                 RPL_LUNDATA_FLAT_LUN_BITS);
2436                         break;
2437                 case RPL_LUNDATA_ATYP_LUN:
2438                 case RPL_LUNDATA_ATYP_EXTLUN:
2439                 default:
2440                         fprintf(stdout, "Unsupported LUN format %d\n",
2441                                 lun_data->luns[i].lundata[0] &
2442                                 RPL_LUNDATA_ATYP_MASK);
2443                         lun_val = -1;
2444                         break;
2445                 }
2446                 if (lun_val == -1)
2447                         continue;
2448
2449                 fprintf(stdout, "%d\n", lun_val);
2450         }
2451
2452 bailout:
2453         if (lun_data != NULL)
2454                 free(lun_data);
2455
2456         return (retval);
2457 }
2458
2459 static int
2460 cctl_tur(int fd, int target, int lun, int iid, int retries)
2461 {
2462         union ctl_io *io;
2463         struct ctl_id id;
2464
2465         id.id = iid;
2466
2467         io = ctl_scsi_alloc_io(id);
2468         if (io == NULL) {
2469                 fprintf(stderr, "can't allocate memory\n");
2470                 return (1);
2471         }
2472
2473         ctl_scsi_tur(io,
2474                      /* tag_type */ CTL_TAG_SIMPLE,
2475                      /* control */ 0);
2476
2477         io->io_hdr.nexus.targ_target.id = target;
2478         io->io_hdr.nexus.targ_lun = lun;
2479         io->io_hdr.nexus.initid = id;
2480
2481         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2482                 ctl_scsi_free_io(io);
2483                 return (1);
2484         }
2485
2486         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS)
2487                 fprintf(stdout, "Unit is ready\n");
2488         else
2489                 ctl_io_error_print(io, NULL, stderr);
2490
2491         return (0);
2492 }
2493
2494 static int
2495 cctl_get_inquiry(int fd, int target, int lun, int iid, int retries, 
2496                  char *path_str, int path_len,
2497                  struct scsi_inquiry_data *inq_data)
2498 {
2499         union ctl_io *io;
2500         struct ctl_id id;
2501         int retval;
2502
2503         retval = 0;
2504
2505         id.id = iid;
2506
2507         io = ctl_scsi_alloc_io(id);
2508         if (io == NULL) {
2509                 warnx("cctl_inquiry: can't allocate memory\n");
2510                 return (1);
2511         }
2512
2513         ctl_scsi_inquiry(/*io*/ io,
2514                          /*data_ptr*/ (uint8_t *)inq_data,
2515                          /*data_len*/ sizeof(*inq_data),
2516                          /*byte2*/ 0,
2517                          /*page_code*/ 0,
2518                          /*tag_type*/ CTL_TAG_SIMPLE,
2519                          /*control*/ 0);
2520
2521         io->io_hdr.nexus.targ_target.id = target;
2522         io->io_hdr.nexus.targ_lun = lun;
2523         io->io_hdr.nexus.initid = id;
2524
2525         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2526                 retval = 1;
2527                 goto bailout;
2528         }
2529
2530         if ((io->io_hdr.status & CTL_STATUS_MASK) != CTL_SUCCESS) {
2531                 retval = 1;
2532                 ctl_io_error_print(io, NULL, stderr);
2533         } else if (path_str != NULL)
2534                 ctl_scsi_path_string(io, path_str, path_len);
2535
2536 bailout:
2537         ctl_scsi_free_io(io);
2538
2539         return (retval);
2540 }
2541
2542 static int
2543 cctl_inquiry(int fd, int target, int lun, int iid, int retries)
2544 {
2545         struct scsi_inquiry_data *inq_data;
2546         char scsi_path[40];
2547         int retval;
2548
2549         retval = 0;
2550
2551         inq_data = malloc(sizeof(*inq_data));
2552         if (inq_data == NULL) {
2553                 warnx("%s: can't allocate inquiry data", __func__);
2554                 retval = 1;
2555                 goto bailout;
2556         }
2557
2558         if ((retval = cctl_get_inquiry(fd, target, lun, iid, retries, scsi_path,
2559                                        sizeof(scsi_path), inq_data)) != 0)
2560                 goto bailout;
2561
2562         printf("%s", scsi_path);
2563         scsi_print_inquiry(inq_data);
2564
2565 bailout:
2566         if (inq_data != NULL)
2567                 free(inq_data);
2568
2569         return (retval);
2570 }
2571
2572 static int
2573 cctl_req_sense(int fd, int target, int lun, int iid, int retries)
2574 {
2575         union ctl_io *io;
2576         struct scsi_sense_data *sense_data;
2577         struct ctl_id id;
2578         int retval;
2579
2580         retval = 0;
2581
2582         id.id = iid;
2583
2584         io = ctl_scsi_alloc_io(id);
2585         if (io == NULL) {
2586                 warnx("cctl_req_sense: can't allocate memory\n");
2587                 return (1);
2588         }
2589         sense_data = malloc(sizeof(*sense_data));
2590         memset(sense_data, 0, sizeof(*sense_data));
2591
2592         ctl_scsi_request_sense(/*io*/ io,
2593                                /*data_ptr*/ (uint8_t *)sense_data,
2594                                /*data_len*/ sizeof(*sense_data),
2595                                /*byte2*/ 0,
2596                                /*tag_type*/ CTL_TAG_SIMPLE,
2597                                /*control*/ 0);
2598
2599         io->io_hdr.nexus.targ_target.id = target;
2600         io->io_hdr.nexus.targ_lun = lun;
2601         io->io_hdr.nexus.initid = id;
2602
2603         if (cctl_do_io(fd, retries, io, __func__) != 0) {
2604                 retval = 1;
2605                 goto bailout;
2606         }
2607
2608         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2609                 bcopy(sense_data, &io->scsiio.sense_data, sizeof(*sense_data));
2610                 io->scsiio.sense_len = sizeof(*sense_data);
2611                 ctl_scsi_sense_print(&io->scsiio, NULL, stdout);
2612         } else
2613                 ctl_io_error_print(io, NULL, stderr);
2614
2615 bailout:
2616
2617         ctl_scsi_free_io(io);
2618         free(sense_data);
2619
2620         return (retval);
2621 }
2622
2623 static int
2624 cctl_report_target_port_group(int fd, int target, int lun, int initiator)
2625 {
2626         union ctl_io *io;
2627         struct ctl_id id;
2628         uint32_t datalen;
2629         uint8_t *dataptr;
2630         int retval;
2631
2632         id.id = initiator;
2633         dataptr = NULL;
2634         retval = 0;
2635
2636         io = ctl_scsi_alloc_io(id);
2637         if (io == NULL) {
2638                 warn("%s: can't allocate memory", __func__);
2639                 return (1);
2640         }
2641
2642         datalen = 64;
2643         dataptr = (uint8_t *)malloc(datalen);
2644         if (dataptr == NULL) {
2645                 warn("%s: can't allocate %d bytes", __func__, datalen);
2646                 retval = 1;
2647                 goto bailout;
2648         }
2649
2650         memset(dataptr, 0, datalen);
2651
2652         ctl_scsi_maintenance_in(/*io*/ io,
2653                                 /*data_ptr*/ dataptr,
2654                                 /*data_len*/ datalen,
2655                                 /*action*/ SA_RPRT_TRGT_GRP,
2656                                 /*tag_type*/ CTL_TAG_SIMPLE,
2657                                 /*control*/ 0);
2658
2659         io->io_hdr.nexus.targ_target.id = target;
2660         io->io_hdr.nexus.targ_lun = lun;
2661         io->io_hdr.nexus.initid = id;
2662
2663         if (cctl_do_io(fd, 0, io, __func__) != 0) {
2664                 retval = 1;
2665                 goto bailout;
2666         }
2667
2668         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2669                 int returned_len, used_len;
2670
2671                 returned_len = scsi_4btoul(&dataptr[0]) + 4;
2672
2673                 for (used_len = 0; used_len < returned_len; used_len++) {
2674                         fprintf(stdout, "0x%02x ", dataptr[used_len]);
2675                         if (((used_len+1) % 8) == 0)
2676                                 fprintf(stdout, "\n");
2677                 }
2678                 fprintf(stdout, "\n");
2679         } else
2680                 ctl_io_error_print(io, NULL, stderr);
2681
2682 bailout:
2683         ctl_scsi_free_io(io);
2684
2685         if (dataptr != NULL)
2686                 free(dataptr);
2687
2688         return (retval);
2689 }
2690
2691 static int
2692 cctl_inquiry_vpd_devid(int fd, int target, int lun, int initiator)
2693 {
2694         union ctl_io *io;
2695         struct ctl_id id;
2696         uint32_t datalen;
2697         uint8_t *dataptr;
2698         int retval;
2699
2700         id.id = initiator;
2701         retval = 0;
2702         dataptr = NULL;
2703
2704         io = ctl_scsi_alloc_io(id);
2705         if (io == NULL) {
2706                 warn("%s: can't allocate memory", __func__);
2707                 return (1);
2708         }
2709
2710         datalen = 256;
2711         dataptr = (uint8_t *)malloc(datalen);
2712         if (dataptr == NULL) {
2713                 warn("%s: can't allocate %d bytes", __func__, datalen);
2714                 retval = 1;
2715                 goto bailout;
2716         }
2717
2718         memset(dataptr, 0, datalen);
2719
2720         ctl_scsi_inquiry(/*io*/        io,
2721                          /*data_ptr*/  dataptr,
2722                          /*data_len*/  datalen,
2723                          /*byte2*/     SI_EVPD,
2724                          /*page_code*/ SVPD_DEVICE_ID,
2725                          /*tag_type*/  CTL_TAG_SIMPLE,
2726                          /*control*/   0);
2727
2728         io->io_hdr.nexus.targ_target.id = target;
2729         io->io_hdr.nexus.targ_lun = lun;
2730         io->io_hdr.nexus.initid = id;
2731
2732         if (cctl_do_io(fd, 0, io, __func__) != 0) {
2733                 retval = 1;
2734                 goto bailout;
2735         }
2736
2737         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2738                 int returned_len, used_len;
2739
2740                 returned_len = scsi_2btoul(&dataptr[2]) + 4;
2741
2742                 for (used_len = 0; used_len < returned_len; used_len++) {
2743                         fprintf(stdout, "0x%02x ", dataptr[used_len]);
2744                         if (((used_len+1) % 8) == 0)
2745                                 fprintf(stdout, "\n");
2746                 }
2747                 fprintf(stdout, "\n");
2748         } else
2749                 ctl_io_error_print(io, NULL, stderr);
2750
2751 bailout:
2752         ctl_scsi_free_io(io);
2753
2754         if (dataptr != NULL)
2755                 free(dataptr);
2756
2757         return (retval);
2758 }
2759
2760 static int
2761 cctl_persistent_reserve_in(int fd, int target, int lun, int initiator, 
2762                            int argc, char **argv, char *combinedopt,
2763                            int retry_count)
2764 {
2765         union ctl_io *io;
2766         struct ctl_id id;
2767         uint32_t datalen;
2768         uint8_t *dataptr;
2769         int action = -1;
2770         int retval;
2771         int c;
2772
2773         id.id = initiator;
2774         retval = 0;
2775         dataptr = NULL;
2776
2777         io = ctl_scsi_alloc_io(id);
2778         if (io == NULL) {
2779                 warn("%s: can't allocate memory", __func__);
2780                 return (1);
2781         }
2782
2783         while ((c = getopt(argc, argv, combinedopt)) != -1) {
2784                 switch (c) {
2785                 case 'a':
2786                         action = strtol(optarg, NULL, 0);
2787                         break;
2788                 default:
2789                         break;
2790                 }
2791         }
2792
2793         if (action < 0 || action > 2) {
2794                 warn("action must be specified and in the range: 0-2");
2795                 retval = 1;
2796                 goto bailout;
2797         }
2798
2799
2800         datalen = 256;
2801         dataptr = (uint8_t *)malloc(datalen);
2802         if (dataptr == NULL) {
2803                 warn("%s: can't allocate %d bytes", __func__, datalen);
2804                 retval = 1;
2805                 goto bailout;
2806         }
2807
2808         memset(dataptr, 0, datalen);
2809
2810         ctl_scsi_persistent_res_in(io,
2811                                    /*data_ptr*/ dataptr,
2812                                    /*data_len*/ datalen,
2813                                    /*action*/   action,
2814                                    /*tag_type*/ CTL_TAG_SIMPLE,
2815                                    /*control*/  0);
2816
2817         io->io_hdr.nexus.targ_target.id = target;
2818         io->io_hdr.nexus.targ_lun = lun;
2819         io->io_hdr.nexus.initid = id;
2820
2821         if (cctl_do_io(fd, retry_count, io, __func__) != 0) {
2822                 retval = 1;
2823                 goto bailout;
2824         }
2825
2826         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2827                 int returned_len, used_len;
2828
2829                 returned_len = 0;
2830
2831                 switch (action) {
2832                 case 0:
2833                         returned_len = scsi_4btoul(&dataptr[4]) + 8;
2834                         returned_len = min(returned_len, 256);
2835                         break;
2836                 case 1:
2837                         returned_len = scsi_4btoul(&dataptr[4]) + 8;
2838                         break;
2839                 case 2:
2840                         returned_len = 8;
2841                         break;
2842                 default:
2843                         warnx("%s: invalid action %d", __func__, action);
2844                         goto bailout;
2845                         break; /* NOTREACHED */
2846                 }
2847
2848                 for (used_len = 0; used_len < returned_len; used_len++) {
2849                         fprintf(stdout, "0x%02x ", dataptr[used_len]);
2850                         if (((used_len+1) % 8) == 0)
2851                                 fprintf(stdout, "\n");
2852                 }
2853                 fprintf(stdout, "\n");
2854         } else
2855                 ctl_io_error_print(io, NULL, stderr);
2856
2857 bailout:
2858         ctl_scsi_free_io(io);
2859
2860         if (dataptr != NULL)
2861                 free(dataptr);
2862
2863         return (retval);
2864 }
2865
2866 static int
2867 cctl_persistent_reserve_out(int fd, int target, int lun, int initiator, 
2868                             int argc, char **argv, char *combinedopt, 
2869                             int retry_count)
2870 {
2871         union ctl_io *io;
2872         struct ctl_id id;
2873         uint32_t datalen;
2874         uint64_t key = 0, sa_key = 0;
2875         int action = -1, restype = -1;
2876         uint8_t *dataptr;
2877         int retval;
2878         int c;
2879
2880         id.id = initiator;
2881         retval = 0;
2882         dataptr = NULL;
2883
2884         io = ctl_scsi_alloc_io(id);
2885         if (io == NULL) {
2886                 warn("%s: can't allocate memory", __func__);
2887                 return (1);
2888         }
2889
2890         while ((c = getopt(argc, argv, combinedopt)) != -1) {
2891                 switch (c) {
2892                 case 'a':
2893                         action = strtol(optarg, NULL, 0);
2894                         break;
2895                 case 'k':
2896                         key = strtoull(optarg, NULL, 0);
2897                         break;
2898                 case 'r':
2899                         restype = strtol(optarg, NULL, 0);
2900                         break;
2901                 case 's':
2902                         sa_key = strtoull(optarg, NULL, 0);
2903                         break;
2904                 default:
2905                         break;
2906                 }
2907         }
2908         if (action < 0 || action > 5) {
2909                 warn("action must be specified and in the range: 0-5");
2910                 retval = 1;
2911                 goto bailout;
2912         }
2913
2914         if (restype < 0 || restype > 5) {
2915                 if (action != 0 && action != 5 && action != 3) {
2916                         warn("'restype' must specified and in the range: 0-5");
2917                         retval = 1;
2918                         goto bailout;
2919                 }
2920         }
2921
2922         datalen = 24;
2923         dataptr = (uint8_t *)malloc(datalen);
2924         if (dataptr == NULL) {
2925                 warn("%s: can't allocate %d bytes", __func__, datalen);
2926                 retval = 1;
2927                 goto bailout;
2928         }
2929
2930         memset(dataptr, 0, datalen);
2931
2932         ctl_scsi_persistent_res_out(io,
2933                                     /*data_ptr*/ dataptr,
2934                                     /*data_len*/ datalen,
2935                                     /*action*/   action,
2936                                     /*type*/     restype,
2937                                     /*key*/      key,
2938                                     /*sa key*/   sa_key,
2939                                     /*tag_type*/ CTL_TAG_SIMPLE,
2940                                     /*control*/  0);
2941
2942         io->io_hdr.nexus.targ_target.id = target;
2943         io->io_hdr.nexus.targ_lun = lun;
2944         io->io_hdr.nexus.initid = id;
2945
2946         if (cctl_do_io(fd, retry_count, io, __func__) != 0) {
2947                 retval = 1;
2948                 goto bailout;
2949         }
2950         if ((io->io_hdr.status & CTL_STATUS_MASK) == CTL_SUCCESS) {
2951                 char scsi_path[40];
2952                 ctl_scsi_path_string(io, scsi_path, sizeof(scsi_path));
2953                 fprintf( stdout, "%sPERSISTENT RESERVE OUT executed "
2954                         "successfully\n", scsi_path);
2955         } else
2956                 ctl_io_error_print(io, NULL, stderr);
2957
2958 bailout:
2959         ctl_scsi_free_io(io);
2960
2961         if (dataptr != NULL)
2962                 free(dataptr);
2963
2964         return (retval);
2965 }
2966
2967 struct cctl_req_option {
2968         char                         *name;
2969         int                           namelen;
2970         char                         *value;
2971         int                           vallen;
2972         STAILQ_ENTRY(cctl_req_option) links;
2973 };
2974
2975 static int
2976 cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
2977 {
2978         struct ctl_lun_req req;
2979         int device_type = -1;
2980         uint64_t lun_size = 0;
2981         uint32_t blocksize = 0, req_lun_id = 0;
2982         char *serial_num = NULL;
2983         char *device_id = NULL;
2984         int lun_size_set = 0, blocksize_set = 0, lun_id_set = 0;
2985         char *backend_name = NULL;
2986         STAILQ_HEAD(, cctl_req_option) option_list;
2987         int num_options = 0;
2988         int retval = 0, c;
2989
2990         STAILQ_INIT(&option_list);
2991
2992         while ((c = getopt(argc, argv, combinedopt)) != -1) {
2993                 switch (c) {
2994                 case 'b':
2995                         backend_name = strdup(optarg);
2996                         break;
2997                 case 'B':
2998                         blocksize = strtoul(optarg, NULL, 0);
2999                         blocksize_set = 1;
3000                         break;
3001                 case 'd':
3002                         device_id = strdup(optarg);
3003                         break;
3004                 case 'l':
3005                         req_lun_id = strtoul(optarg, NULL, 0);
3006                         lun_id_set = 1;
3007                         break;
3008                 case 'o': {
3009                         struct cctl_req_option *option;
3010                         char *tmpstr;
3011                         char *name, *value;
3012
3013                         tmpstr = strdup(optarg);
3014                         name = strsep(&tmpstr, "=");
3015                         if (name == NULL) {
3016                                 warnx("%s: option -o takes \"name=value\""
3017                                       "argument", __func__);
3018                                 retval = 1;
3019                                 goto bailout;
3020                         }
3021                         value = strsep(&tmpstr, "=");
3022                         if (value == NULL) {
3023                                 warnx("%s: option -o takes \"name=value\""
3024                                       "argument", __func__);
3025                                 retval = 1;
3026                                 goto bailout;
3027                         }
3028                         option = malloc(sizeof(*option));
3029                         if (option == NULL) {
3030                                 warn("%s: error allocating %zd bytes",
3031                                      __func__, sizeof(*option));
3032                                 retval = 1;
3033                                 goto bailout;
3034                         }
3035                         option->name = strdup(name);
3036                         option->namelen = strlen(name) + 1;
3037                         option->value = strdup(value);
3038                         option->vallen = strlen(value) + 1;
3039                         free(tmpstr);
3040
3041                         STAILQ_INSERT_TAIL(&option_list, option, links);
3042                         num_options++;
3043                         break;
3044                 }
3045                 case 's':
3046                         lun_size = strtoull(optarg, NULL, 0);
3047                         lun_size_set = 1;
3048                         break;
3049                 case 'S':
3050                         serial_num = strdup(optarg);
3051                         break;
3052                 case 't':
3053                         device_type = strtoul(optarg, NULL, 0);
3054                         break;
3055                 default:
3056                         break;
3057                 }
3058         }
3059
3060         if (backend_name == NULL) {
3061                 warnx("%s: backend name (-b) must be specified", __func__);
3062                 retval = 1;
3063                 goto bailout;
3064         }
3065
3066         bzero(&req, sizeof(req));
3067
3068         strlcpy(req.backend, backend_name, sizeof(req.backend));
3069         req.reqtype = CTL_LUNREQ_CREATE;
3070
3071         if (blocksize_set != 0)
3072                 req.reqdata.create.blocksize_bytes = blocksize;
3073
3074         if (lun_size_set != 0)
3075                 req.reqdata.create.lun_size_bytes = lun_size;
3076
3077         if (lun_id_set != 0) {
3078                 req.reqdata.create.flags |= CTL_LUN_FLAG_ID_REQ;
3079                 req.reqdata.create.req_lun_id = req_lun_id;
3080         }
3081
3082         req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE;
3083
3084         if (device_type != -1)
3085                 req.reqdata.create.device_type = device_type;
3086         else
3087                 req.reqdata.create.device_type = T_DIRECT;
3088
3089         if (serial_num != NULL) {
3090                 strlcpy(req.reqdata.create.serial_num, serial_num,
3091                         sizeof(req.reqdata.create.serial_num));
3092                 req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM;
3093         }
3094
3095         if (device_id != NULL) {
3096                 strlcpy(req.reqdata.create.device_id, device_id,
3097                         sizeof(req.reqdata.create.device_id));
3098                 req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID;
3099         }
3100
3101         req.num_be_args = num_options;
3102         if (num_options > 0) {
3103                 struct cctl_req_option *option, *next_option;
3104                 int i;
3105
3106                 req.be_args = malloc(num_options * sizeof(*req.be_args));
3107                 if (req.be_args == NULL) {
3108                         warn("%s: error allocating %zd bytes", __func__,
3109                              num_options * sizeof(*req.be_args));
3110                         retval = 1;
3111                         goto bailout;
3112                 }
3113
3114                 for (i = 0, option = STAILQ_FIRST(&option_list);
3115                      i < num_options; i++, option = next_option) {
3116                         next_option = STAILQ_NEXT(option, links);
3117
3118                         req.be_args[i].namelen = option->namelen;
3119                         req.be_args[i].name = strdup(option->name);
3120                         req.be_args[i].vallen = option->vallen;
3121                         req.be_args[i].value = strdup(option->value);
3122                         /*
3123                          * XXX KDM do we want a way to specify a writeable
3124                          * flag of some sort?  Do we want a way to specify
3125                          * binary data?
3126                          */
3127                         req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
3128
3129                         STAILQ_REMOVE(&option_list, option, cctl_req_option,
3130                                       links);
3131                         free(option->name);
3132                         free(option->value);
3133                         free(option);
3134                 }
3135         }
3136
3137         if (ioctl(fd, CTL_LUN_REQ, &req) == -1) {
3138                 warn("%s: error issuing CTL_LUN_REQ ioctl", __func__);
3139                 retval = 1;
3140                 goto bailout;
3141         }
3142
3143         if (req.status == CTL_LUN_ERROR) {
3144                 warnx("%s: error returned from LUN creation request:\n%s",
3145                       __func__, req.error_str);
3146                 retval = 1;
3147                 goto bailout;
3148         } else if (req.status != CTL_LUN_OK) {
3149                 warnx("%s: unknown LUN creation request status %d",
3150                       __func__, req.status);
3151                 retval = 1;
3152                 goto bailout;
3153         }
3154
3155         fprintf(stdout, "LUN created successfully\n");
3156         fprintf(stdout, "backend:       %s\n", req.backend);
3157         fprintf(stdout, "device type:   %d\n",req.reqdata.create.device_type);
3158         fprintf(stdout, "LUN size:      %ju bytes\n",
3159                 (uintmax_t)req.reqdata.create.lun_size_bytes);
3160         fprintf(stdout, "blocksize      %u bytes\n",
3161                 req.reqdata.create.blocksize_bytes);
3162         fprintf(stdout, "LUN ID:        %d\n", req.reqdata.create.req_lun_id);
3163         fprintf(stdout, "Serial Number: %s\n", req.reqdata.create.serial_num);
3164         fprintf(stdout, "Device ID;     %s\n", req.reqdata.create.device_id);
3165
3166 bailout:
3167         return (retval);
3168 }
3169
3170 static int
3171 cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
3172 {
3173         struct ctl_lun_req req;
3174         uint32_t lun_id = 0;
3175         int lun_id_set = 0;
3176         char *backend_name = NULL;
3177         STAILQ_HEAD(, cctl_req_option) option_list;
3178         int num_options = 0;
3179         int retval = 0, c;
3180
3181         STAILQ_INIT(&option_list);
3182
3183         while ((c = getopt(argc, argv, combinedopt)) != -1) {
3184                 switch (c) {
3185                 case 'b':
3186                         backend_name = strdup(optarg);
3187                         break;
3188                 case 'l':
3189                         lun_id = strtoul(optarg, NULL, 0);
3190                         lun_id_set = 1;
3191                         break;
3192                 case 'o': {
3193                         struct cctl_req_option *option;
3194                         char *tmpstr;
3195                         char *name, *value;
3196
3197                         tmpstr = strdup(optarg);
3198                         name = strsep(&tmpstr, "=");
3199                         if (name == NULL) {
3200                                 warnx("%s: option -o takes \"name=value\""
3201                                       "argument", __func__);
3202                                 retval = 1;
3203                                 goto bailout;
3204                         }
3205                         value = strsep(&tmpstr, "=");
3206                         if (value == NULL) {
3207                                 warnx("%s: option -o takes \"name=value\""
3208                                       "argument", __func__);
3209                                 retval = 1;
3210                                 goto bailout;
3211                         }
3212                         option = malloc(sizeof(*option));
3213                         if (option == NULL) {
3214                                 warn("%s: error allocating %zd bytes",
3215                                      __func__, sizeof(*option));
3216                                 retval = 1;
3217                                 goto bailout;
3218                         }
3219                         option->name = strdup(name);
3220                         option->namelen = strlen(name) + 1;
3221                         option->value = strdup(value);
3222                         option->vallen = strlen(value) + 1;
3223                         free(tmpstr);
3224
3225                         STAILQ_INSERT_TAIL(&option_list, option, links);
3226                         num_options++;
3227                         break;
3228                 }
3229                 default:
3230                         break;
3231                 }
3232         }
3233
3234         if (backend_name == NULL)
3235                 errx(1, "%s: backend name (-b) must be specified", __func__);
3236
3237         if (lun_id_set == 0)
3238                 errx(1, "%s: LUN id (-l) must be specified", __func__);
3239
3240         bzero(&req, sizeof(req));
3241
3242         strlcpy(req.backend, backend_name, sizeof(req.backend));
3243         req.reqtype = CTL_LUNREQ_RM;
3244
3245         req.reqdata.rm.lun_id = lun_id;
3246
3247         req.num_be_args = num_options;
3248         if (num_options > 0) {
3249                 struct cctl_req_option *option, *next_option;
3250                 int i;
3251
3252                 req.be_args = malloc(num_options * sizeof(*req.be_args));
3253                 if (req.be_args == NULL) {
3254                         warn("%s: error allocating %zd bytes", __func__,
3255                              num_options * sizeof(*req.be_args));
3256                         retval = 1;
3257                         goto bailout;
3258                 }
3259
3260                 for (i = 0, option = STAILQ_FIRST(&option_list);
3261                      i < num_options; i++, option = next_option) {
3262                         next_option = STAILQ_NEXT(option, links);
3263
3264                         req.be_args[i].namelen = option->namelen;
3265                         req.be_args[i].name = strdup(option->name);
3266                         req.be_args[i].vallen = option->vallen;
3267                         req.be_args[i].value = strdup(option->value);
3268                         /*
3269                          * XXX KDM do we want a way to specify a writeable
3270                          * flag of some sort?  Do we want a way to specify
3271                          * binary data?
3272                          */
3273                         req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
3274
3275                         STAILQ_REMOVE(&option_list, option, cctl_req_option,
3276                                       links);
3277                         free(option->name);
3278                         free(option->value);
3279                         free(option);
3280                 }
3281         }
3282
3283         if (ioctl(fd, CTL_LUN_REQ, &req) == -1) {
3284                 warn("%s: error issuing CTL_LUN_REQ ioctl", __func__);
3285                 retval = 1;
3286                 goto bailout;
3287         }
3288
3289         if (req.status == CTL_LUN_ERROR) {
3290                 warnx("%s: error returned from LUN creation request:\n%s",
3291                       __func__, req.error_str);
3292                 retval = 1;
3293                 goto bailout;
3294         } else if (req.status != CTL_LUN_OK) {
3295                 warnx("%s: unknown LUN creation request status %d",
3296                       __func__, req.status);
3297                 retval = 1;
3298                 goto bailout;
3299         }
3300
3301         printf("LUN %d deleted successfully\n", lun_id);
3302
3303 bailout:
3304         return (retval);
3305 }
3306
3307 /*
3308  * Name/value pair used for per-LUN attributes.
3309  */
3310 struct cctl_lun_nv {
3311         char *name;
3312         char *value;
3313         STAILQ_ENTRY(cctl_lun_nv) links;
3314 };
3315
3316 /*
3317  * Backend LUN information.  
3318  */
3319 struct cctl_lun {
3320         uint64_t lun_id;
3321         char *backend_type;
3322         uint64_t size_blocks;
3323         uint32_t blocksize;
3324         char *serial_number;
3325         char *device_id;
3326         STAILQ_HEAD(,cctl_lun_nv) attr_list;
3327         STAILQ_ENTRY(cctl_lun) links;
3328 };
3329
3330 struct cctl_devlist_data {
3331         int num_luns;
3332         STAILQ_HEAD(,cctl_lun) lun_list;
3333         struct cctl_lun *cur_lun;
3334         int level;
3335         struct sbuf *cur_sb[32];
3336 };
3337
3338 static void
3339 cctl_start_element(void *user_data, const char *name, const char **attr)
3340 {
3341         int i;
3342         struct cctl_devlist_data *devlist;
3343         struct cctl_lun *cur_lun;
3344
3345         devlist = (struct cctl_devlist_data *)user_data;
3346         cur_lun = devlist->cur_lun;
3347         devlist->level++;
3348         if ((u_int)devlist->level > (sizeof(devlist->cur_sb) /
3349             sizeof(devlist->cur_sb[0])))
3350                 errx(1, "%s: too many nesting levels, %zd max", __func__,
3351                      sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]));
3352
3353         devlist->cur_sb[devlist->level] = sbuf_new_auto();
3354         if (devlist->cur_sb[devlist->level] == NULL)
3355                 err(1, "%s: Unable to allocate sbuf", __func__);
3356
3357         if (strcmp(name, "lun") == 0) {
3358                 if (cur_lun != NULL)
3359                         errx(1, "%s: improper lun element nesting", __func__);
3360
3361                 cur_lun = calloc(1, sizeof(*cur_lun));
3362                 if (cur_lun == NULL)
3363                         err(1, "%s: cannot allocate %zd bytes", __func__,
3364                             sizeof(*cur_lun));
3365
3366                 devlist->num_luns++;
3367                 devlist->cur_lun = cur_lun;
3368
3369                 STAILQ_INIT(&cur_lun->attr_list);
3370                 STAILQ_INSERT_TAIL(&devlist->lun_list, cur_lun, links);
3371
3372                 for (i = 0; attr[i] != NULL; i += 2) {
3373                         if (strcmp(attr[i], "id") == 0) {
3374                                 cur_lun->lun_id = strtoull(attr[i+1], NULL, 0);
3375                         } else {
3376                                 errx(1, "%s: invalid LUN attribute %s = %s",
3377                                      __func__, attr[i], attr[i+1]);
3378                         }
3379                 }
3380         }
3381 }
3382
3383 static void
3384 cctl_end_element(void *user_data, const char *name)
3385 {
3386         struct cctl_devlist_data *devlist;
3387         struct cctl_lun *cur_lun;
3388         char *str;
3389
3390         devlist = (struct cctl_devlist_data *)user_data;
3391         cur_lun = devlist->cur_lun;
3392
3393         if ((cur_lun == NULL)
3394          && (strcmp(name, "ctllunlist") != 0))
3395                 errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name);
3396
3397         if (devlist->cur_sb[devlist->level] == NULL)
3398                 errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
3399                      devlist->level, name);
3400
3401         sbuf_finish(devlist->cur_sb[devlist->level]);
3402         str = strdup(sbuf_data(devlist->cur_sb[devlist->level]));
3403         if (str == NULL)
3404                 err(1, "%s can't allocate %zd bytes for string", __func__,
3405                     sbuf_len(devlist->cur_sb[devlist->level]));
3406
3407         if (strlen(str) == 0) {
3408                 free(str);
3409                 str = NULL;
3410         }
3411
3412         sbuf_delete(devlist->cur_sb[devlist->level]);
3413         devlist->cur_sb[devlist->level] = NULL;
3414         devlist->level--;
3415
3416         if (strcmp(name, "backend_type") == 0) {
3417                 cur_lun->backend_type = str;
3418                 str = NULL;
3419         } else if (strcmp(name, "size") == 0) {
3420                 cur_lun->size_blocks = strtoull(str, NULL, 0);
3421         } else if (strcmp(name, "blocksize") == 0) {
3422                 cur_lun->blocksize = strtoul(str, NULL, 0);
3423         } else if (strcmp(name, "serial_number") == 0) {
3424                 cur_lun->serial_number = str;
3425                 str = NULL;
3426         } else if (strcmp(name, "device_id") == 0) {
3427                 cur_lun->device_id = str;
3428                 str = NULL;
3429         } else if (strcmp(name, "lun") == 0) {
3430                 devlist->cur_lun = NULL;
3431         } else if (strcmp(name, "ctllunlist") == 0) {
3432                 
3433         } else {
3434                 struct cctl_lun_nv *nv;
3435
3436                 nv = calloc(1, sizeof(*nv));
3437                 if (nv == NULL)
3438                         err(1, "%s: can't allocate %zd bytes for nv pair",
3439                             __func__, sizeof(*nv));
3440
3441                 nv->name = strdup(name);
3442                 if (nv->name == NULL)
3443                         err(1, "%s: can't allocated %zd bytes for string",
3444                             __func__, strlen(name));
3445
3446                 nv->value = str;
3447                 str = NULL;
3448                 STAILQ_INSERT_TAIL(&cur_lun->attr_list, nv, links);
3449         }
3450
3451         free(str);
3452 }
3453
3454 static void
3455 cctl_char_handler(void *user_data, const XML_Char *str, int len)
3456 {
3457         struct cctl_devlist_data *devlist;
3458
3459         devlist = (struct cctl_devlist_data *)user_data;
3460
3461         sbuf_bcat(devlist->cur_sb[devlist->level], str, len);
3462 }
3463
3464 static int
3465 cctl_devlist(int fd, int argc, char **argv, char *combinedopt)
3466 {
3467         struct ctl_lun_list list;
3468         struct cctl_devlist_data devlist;
3469         struct cctl_lun *lun;
3470         XML_Parser parser;
3471         char *lun_str;
3472         int lun_len;
3473         int dump_xml = 0;
3474         int retval, c;
3475         char *backend = NULL;
3476         int verbose = 0;
3477
3478         retval = 0;
3479         lun_len = 4096;
3480
3481         bzero(&devlist, sizeof(devlist));
3482         STAILQ_INIT(&devlist.lun_list);
3483
3484         while ((c = getopt(argc, argv, combinedopt)) != -1) {
3485                 switch (c) {
3486                 case 'b':
3487                         backend = strdup(optarg);
3488                         break;
3489                 case 'v':
3490                         verbose++;
3491                         break;
3492                 case 'x':
3493                         dump_xml = 1;
3494                         break;
3495                 default:
3496                         break;
3497                 }
3498         }
3499
3500 retry:
3501         lun_str = malloc(lun_len);
3502
3503         bzero(&list, sizeof(list));
3504         list.alloc_len = lun_len;
3505         list.status = CTL_LUN_LIST_NONE;
3506         list.lun_xml = lun_str;
3507
3508         if (ioctl(fd, CTL_LUN_LIST, &list) == -1) {
3509                 warn("%s: error issuing CTL_LUN_LIST ioctl", __func__);
3510                 retval = 1;
3511                 goto bailout;
3512         }
3513
3514         if (list.status == CTL_LUN_LIST_ERROR) {
3515                 warnx("%s: error returned from CTL_LUN_LIST ioctl:\n%s",
3516                       __func__, list.error_str);
3517         } else if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) {
3518                 lun_len = lun_len << 1;
3519                 goto retry;
3520         }
3521
3522         if (dump_xml != 0) {
3523                 printf("%s", lun_str);
3524                 goto bailout;
3525         }
3526
3527         parser = XML_ParserCreate(NULL);
3528         if (parser == NULL) {
3529                 warn("%s: Unable to create XML parser", __func__);
3530                 retval = 1;
3531                 goto bailout;
3532         }
3533
3534         XML_SetUserData(parser, &devlist);
3535         XML_SetElementHandler(parser, cctl_start_element, cctl_end_element);
3536         XML_SetCharacterDataHandler(parser, cctl_char_handler);
3537
3538         retval = XML_Parse(parser, lun_str, strlen(lun_str), 1);
3539         XML_ParserFree(parser);
3540         if (retval != 1) {
3541                 retval = 1;
3542                 goto bailout;
3543         }
3544
3545         printf("LUN Backend  %18s %4s %-16s %-16s\n", "Size (Blocks)", "BS",
3546                "Serial Number", "Device ID");
3547         STAILQ_FOREACH(lun, &devlist.lun_list, links) {
3548                 struct cctl_lun_nv *nv;
3549
3550                 if ((backend != NULL)
3551                  && (strcmp(lun->backend_type, backend) != 0))
3552                         continue;
3553
3554                 printf("%3ju %-8s %18ju %4u %-16s %-16s\n",
3555                        (uintmax_t)lun->lun_id,
3556                        lun->backend_type, (uintmax_t)lun->size_blocks,
3557                        lun->blocksize, lun->serial_number, lun->device_id);
3558
3559                 if (verbose == 0)
3560                         continue;
3561
3562                 STAILQ_FOREACH(nv, &lun->attr_list, links) {
3563                         printf("      %s=%s\n", nv->name, nv->value);
3564                 }
3565         }
3566 bailout:
3567         free(lun_str);
3568
3569         return (retval);
3570 }
3571
3572 void
3573 usage(int error)
3574 {
3575         fprintf(error ? stderr : stdout,
3576 "Usage:\n"
3577 "Primary commands:\n"
3578 "         ctladm tur         [dev_id][general options]\n"
3579 "         ctladm inquiry     [dev_id][general options]\n"
3580 "         ctladm devid       [dev_id][general options]\n"
3581 "         ctladm reqsense    [dev_id][general options]\n"
3582 "         ctladm reportluns  [dev_id][general options]\n"
3583 "         ctladm read        [dev_id][general options] <-l lba> <-d len>\n"
3584 "                            <-f file|-> <-b blocksize> [-c cdbsize][-N]\n"
3585 "         ctladm write       [dev_id][general options] <-l lba> <-d len>\n"
3586 "                            <-f file|-> <-b blocksize> [-c cdbsize][-N]\n"
3587 "         ctladm readcap     [dev_id][general options] [-c cdbsize]\n"
3588 "         ctladm modesense   [dev_id][general options] <-m page|-l> [-P pc]\n"
3589 "                            [-d] [-S subpage] [-c cdbsize]\n"
3590 "         ctladm prin        [dev_id][general options] <-a action>\n"
3591 "         ctladm prout       [dev_id][general options] <-a action>\n"
3592 "                            <-r restype] [-k key] [-s sa_key]\n"
3593 "         ctladm rtpg        [dev_id][general options]\n"
3594 "         ctladm start       [dev_id][general options] [-i] [-o]\n"
3595 "         ctladm stop        [dev_id][general options] [-i] [-o]\n"
3596 "         ctladm synccache   [dev_id][general options] [-l lba]\n"
3597 "                            [-b blockcount] [-r] [-i] [-c cdbsize]\n"
3598 "         ctladm create      <-b backend> [-B blocksize] [-d device_id]\n"
3599 "                            [-l lun_id] [-o name=value] [-s size_bytes]\n"
3600 "                            [-S serial_num] [-t dev_type]\n"
3601 "         ctladm remove      <-b backend> <-l lun_id> [-o name=value]\n"
3602 "         ctladm devlist     [-b][-v][-x]\n"
3603 "         ctladm shutdown\n"
3604 "         ctladm startup\n"
3605 "         ctladm hardstop\n"
3606 "         ctladm hardstart\n"
3607 "         ctladm lunlist\n"
3608 "         ctladm bbrread     [dev_id] <-l lba> <-d datalen>\n"
3609 "         ctladm delay       [dev_id] <-l datamove|done> [-T oneshot|cont]\n"
3610 "                            [-t secs]\n"
3611 "         ctladm realsync    <on|off|query>\n"
3612 "         ctladm setsync     [dev_id] <-i interval>\n"
3613 "         ctladm getsync     [dev_id]\n"
3614 "         ctladm inject      [dev_id] <-i action> <-p pattern> [-r lba,len]\n"
3615 "                            [-s len fmt [args]] [-c] [-d delete_id]\n"
3616 "         ctladm port        <-l | -o <on|off> | [-w wwnn][-W wwpn]>\n"
3617 "                            [-p targ_port] [-t port_type] [-q] [-x]\n"
3618 "         ctladm dumpooa\n"
3619 "         ctladm dumpstructs\n"
3620 "         ctladm help\n"
3621 "General Options:\n"
3622 "-I intiator_id           : defaults to 7, used to change the initiator id\n"
3623 "-C retries               : specify the number of times to retry this command\n"
3624 "-D devicename            : specify the device to operate on\n"
3625 "                         : (default is %s)\n"
3626 "read/write options:\n"
3627 "-l lba                   : logical block address\n"
3628 "-d len                   : read/write length, in blocks\n"
3629 "-f file|-                : write/read data to/from file or stdout/stdin\n"
3630 "-b blocksize             : block size, in bytes\n"
3631 "-c cdbsize               : specify minimum cdb size: 6, 10, 12 or 16\n"
3632 "-N                       : do not copy data to/from userland\n"
3633 "readcapacity options:\n"
3634 "-c cdbsize               : specify minimum cdb size: 10 or 16\n"
3635 "modesense options:\n"
3636 "-m page                  : specify the mode page to view\n"
3637 "-l                       : request a list of supported pages\n"
3638 "-P pc                    : specify the page control value: 0-3 (current,\n"
3639 "                           changeable, default, saved, respectively)\n"
3640 "-d                       : disable block descriptors for mode sense\n"
3641 "-S subpage               : specify a subpage\n"
3642 "-c cdbsize               : specify minimum cdb size: 6 or 10\n"
3643 "persistent reserve in options:\n"
3644 "-a action                : specify the action value: 0-2 (read key, read\n"
3645 "                           reservation, read capabilities, respectively)\n"
3646 "persistent reserve out options:\n"
3647 "-a action                : specify the action value: 0-5 (register, reserve,\n"
3648 "                           release, clear, preempt, register and ignore)\n"
3649 "-k key                   : key value\n"
3650 "-s sa_key                : service action value\n"
3651 "-r restype               : specify the reservation type: 0-5(wr ex, ex ac,\n"
3652 "                           wr ex ro, ex ac ro, wr ex ar, ex ac ar)\n"
3653 "start/stop options:\n"
3654 "-i                       : set the immediate bit (CTL does not support this)\n"
3655 "-o                       : set the on/offline bit\n"
3656 "synccache options:\n"
3657 "-l lba                   : set the starting LBA\n"
3658 "-b blockcount            : set the length to sync in blocks\n"
3659 "-r                       : set the relative addressing bit\n"
3660 "-i                       : set the immediate bit\n"
3661 "-c cdbsize               : specify minimum cdb size: 10 or 16\n"
3662 "create options:\n"
3663 "-b backend               : backend name (\"block\", \"ramdisk\", etc.)\n"
3664 "-B blocksize             : LUN blocksize in bytes (some backends)\n"
3665 "-d device_id             : SCSI VPD page 0x83 ID\n"
3666 "-l lun_id                : requested LUN number\n"
3667 "-o name=value            : backend-specific options, multiple allowed\n"
3668 "-s size_bytes            : LUN size in bytes (some backends)\n"
3669 "-S serial_num            : SCSI VPD page 0x80 serial number\n"
3670 "-t dev_type              : SCSI device type (0=disk, 3=processor)\n"
3671 "remove options:\n"
3672 "-b backend               : backend name (\"block\", \"ramdisk\", etc.)\n"
3673 "-l lun_id                : LUN number to delete\n"
3674 "-o name=value            : backend-specific options, multiple allowed\n"
3675 "devlist options:\n"
3676 "-b backend               : list devices from specified backend only\n"
3677 "-v                       : be verbose, show backend attributes\n"
3678 "-x                       : dump raw XML\n"
3679 "delay options:\n"
3680 "-l datamove|done         : delay command at datamove or done phase\n"
3681 "-T oneshot               : delay one command, then resume normal completion\n"
3682 "-T cont                  : delay all commands\n"
3683 "-t secs                  : number of seconds to delay\n"
3684 "inject options:\n"
3685 "-i error_action          : action to perform\n"
3686 "-p pattern               : command pattern to look for\n"
3687 "-r lba,len               : LBA range for pattern\n"
3688 "-s len fmt [args]        : sense data for custom sense action\n"
3689 "-c                       : continuous operation\n"
3690 "-d delete_id             : error id to delete\n"
3691 "port options:\n"
3692 "-l                       : list frontend ports\n"
3693 "-o on|off                : turn frontend ports on or off\n"
3694 "-w wwnn                  : set WWNN for one frontend\n"
3695 "-W wwpn                  : set WWPN for one frontend\n"
3696 "-t port_type             : specify fc, scsi, ioctl, internal frontend type\n"
3697 "-p targ_port             : specify target port number\n"
3698 "-q                       : omit header in list output\n"
3699 "-x                       : output port list in XML format\n"
3700 "bbrread options:\n"
3701 "-l lba                   : starting LBA\n"
3702 "-d datalen               : length, in bytes, to read\n",
3703 CTL_DEFAULT_DEV);
3704 }
3705
3706 int
3707 main(int argc, char **argv)
3708 {
3709         int c;
3710         ctladm_cmdfunction command;
3711         ctladm_cmdargs cmdargs;
3712         ctladm_optret optreturn;
3713         char *device;
3714         const char *mainopt = "C:D:I:";
3715         const char *subopt = NULL;
3716         char combinedopt[256];
3717         int target, lun;
3718         int optstart = 2;
3719         int retval, fd;
3720         int retries;
3721         int initid;
3722
3723         retval = 0;
3724         cmdargs = CTLADM_ARG_NONE;
3725         command = CTLADM_CMD_HELP;
3726         device = NULL;
3727         fd = -1;
3728         retries = 0;
3729         target = 0;
3730         lun = 0;
3731         initid = 7;
3732
3733         if (argc < 2) {
3734                 usage(1);
3735                 retval = 1;
3736                 goto bailout;
3737         }
3738
3739         /*
3740          * Get the base option.
3741          */
3742         optreturn = getoption(option_table,argv[1], &command, &cmdargs,&subopt);
3743
3744         if (optreturn == CC_OR_AMBIGUOUS) {
3745                 warnx("ambiguous option %s", argv[1]);
3746                 usage(0);
3747                 exit(1);
3748         } else if (optreturn == CC_OR_NOT_FOUND) {
3749                 warnx("option %s not found", argv[1]);
3750                 usage(0);
3751                 exit(1);
3752         }
3753
3754         if (cmdargs & CTLADM_ARG_NEED_TL) {
3755                 if ((argc < 3)
3756                  || (!isdigit(argv[2][0]))) {
3757                         warnx("option %s requires a target:lun argument",
3758                               argv[1]);
3759                         usage(0);
3760                         exit(1);
3761                 }
3762                 retval = cctl_parse_tl(argv[2], &target, &lun);
3763                 if (retval != 0)
3764                         errx(1, "invalid target:lun argument %s", argv[2]);
3765
3766                 cmdargs |= CTLADM_ARG_TARG_LUN;
3767                 optstart++;
3768         }
3769
3770         /*
3771          * Ahh, getopt(3) is a pain.
3772          *
3773          * This is a gross hack.  There really aren't many other good
3774          * options (excuse the pun) for parsing options in a situation like
3775          * this.  getopt is kinda braindead, so you end up having to run
3776          * through the options twice, and give each invocation of getopt
3777          * the option string for the other invocation.
3778          *
3779          * You would think that you could just have two groups of options.
3780          * The first group would get parsed by the first invocation of
3781          * getopt, and the second group would get parsed by the second
3782          * invocation of getopt.  It doesn't quite work out that way.  When
3783          * the first invocation of getopt finishes, it leaves optind pointing
3784          * to the argument _after_ the first argument in the second group.
3785          * So when the second invocation of getopt comes around, it doesn't
3786          * recognize the first argument it gets and then bails out.
3787          *
3788          * A nice alternative would be to have a flag for getopt that says
3789          * "just keep parsing arguments even when you encounter an unknown
3790          * argument", but there isn't one.  So there's no real clean way to
3791          * easily parse two sets of arguments without having one invocation
3792          * of getopt know about the other.
3793          *
3794          * Without this hack, the first invocation of getopt would work as
3795          * long as the generic arguments are first, but the second invocation
3796          * (in the subfunction) would fail in one of two ways.  In the case
3797          * where you don't set optreset, it would fail because optind may be
3798          * pointing to the argument after the one it should be pointing at.
3799          * In the case where you do set optreset, and reset optind, it would
3800          * fail because getopt would run into the first set of options, which
3801          * it doesn't understand.
3802          *
3803          * All of this would "sort of" work if you could somehow figure out
3804          * whether optind had been incremented one option too far.  The
3805          * mechanics of that, however, are more daunting than just giving
3806          * both invocations all of the expect options for either invocation.
3807          *
3808          * Needless to say, I wouldn't mind if someone invented a better
3809          * (non-GPL!) command line parsing interface than getopt.  I
3810          * wouldn't mind if someone added more knobs to getopt to make it
3811          * work better.  Who knows, I may talk myself into doing it someday,
3812          * if the standards weenies let me.  As it is, it just leads to
3813          * hackery like this and causes people to avoid it in some cases.
3814          *
3815          * KDM, September 8th, 1998
3816          */
3817         if (subopt != NULL)
3818                 sprintf(combinedopt, "%s%s", mainopt, subopt);
3819         else
3820                 sprintf(combinedopt, "%s", mainopt);
3821
3822         /*
3823          * Start getopt processing at argv[2/3], since we've already
3824          * accepted argv[1..2] as the command name, and as a possible
3825          * device name.
3826          */
3827         optind = optstart;
3828
3829         /*
3830          * Now we run through the argument list looking for generic
3831          * options, and ignoring options that possibly belong to
3832          * subfunctions.
3833          */
3834         while ((c = getopt(argc, argv, combinedopt))!= -1){
3835                 switch (c) {
3836                 case 'C':
3837                         cmdargs |= CTLADM_ARG_RETRIES;
3838                         retries = strtol(optarg, NULL, 0);
3839                         break;
3840                 case 'D':
3841                         device = strdup(optarg);
3842                         cmdargs |= CTLADM_ARG_DEVICE;
3843                         break;
3844                 case 'I':
3845                         cmdargs |= CTLADM_ARG_INITIATOR;
3846                         initid = strtol(optarg, NULL, 0);
3847                         break;
3848                 default:
3849                         break;
3850                 }
3851         }
3852
3853         if ((cmdargs & CTLADM_ARG_INITIATOR) == 0)
3854                 initid = 7;
3855
3856         optind = optstart;
3857         optreset = 1;
3858
3859         /*
3860          * Default to opening the CTL device for now.
3861          */
3862         if (((cmdargs & CTLADM_ARG_DEVICE) == 0)
3863          && (command != CTLADM_CMD_HELP)) {
3864                 device = strdup(CTL_DEFAULT_DEV);
3865                 cmdargs |= CTLADM_ARG_DEVICE;
3866         }
3867
3868         if ((cmdargs & CTLADM_ARG_DEVICE)
3869          && (command != CTLADM_CMD_HELP)) {
3870                 fd = open(device, O_RDWR);
3871                 if (fd == -1) {
3872                         fprintf(stderr, "%s: error opening %s: %s\n",
3873                                 argv[0], device, strerror(errno));
3874                         retval = 1;
3875                         goto bailout;
3876                 }
3877         } else if ((command != CTLADM_CMD_HELP)
3878                 && ((cmdargs & CTLADM_ARG_DEVICE) == 0)) {
3879                 fprintf(stderr, "%s: you must specify a device with the "
3880                         "--device argument for this command\n", argv[0]);
3881                 command = CTLADM_CMD_HELP;
3882                 retval = 1;
3883         }
3884
3885         switch (command) {
3886         case CTLADM_CMD_TUR:
3887                 retval = cctl_tur(fd, target, lun, initid, retries);
3888                 break;
3889         case CTLADM_CMD_INQUIRY:
3890                 retval = cctl_inquiry(fd, target, lun, initid, retries);
3891                 break;
3892         case CTLADM_CMD_REQ_SENSE:
3893                 retval = cctl_req_sense(fd, target, lun, initid, retries);
3894                 break;
3895         case CTLADM_CMD_REPORT_LUNS:
3896                 retval = cctl_report_luns(fd, target, lun, initid, retries);
3897                 break;
3898         case CTLADM_CMD_CREATE:
3899                 retval = cctl_create_lun(fd, argc, argv, combinedopt);
3900                 break;
3901         case CTLADM_CMD_RM:
3902                 retval = cctl_rm_lun(fd, argc, argv, combinedopt);
3903                 break;
3904         case CTLADM_CMD_DEVLIST:
3905                 retval = cctl_devlist(fd, argc, argv, combinedopt);
3906                 break;
3907         case CTLADM_CMD_READ:
3908         case CTLADM_CMD_WRITE:
3909                 retval = cctl_read_write(fd, target, lun, initid, retries,
3910                                          argc, argv, combinedopt, command);
3911                 break;
3912         case CTLADM_CMD_PORT:
3913                 retval = cctl_port(fd, argc, argv, combinedopt);
3914                 break;
3915         case CTLADM_CMD_READCAPACITY:
3916                 retval = cctl_read_capacity(fd, target, lun, initid, retries,
3917                                             argc, argv, combinedopt);
3918                 break;
3919         case CTLADM_CMD_MODESENSE:
3920                 retval = cctl_mode_sense(fd, target, lun, initid, retries,
3921                                          argc, argv, combinedopt);
3922                 break;
3923         case CTLADM_CMD_START:
3924         case CTLADM_CMD_STOP:
3925                 retval = cctl_start_stop(fd, target, lun, initid, retries,
3926                                          (command == CTLADM_CMD_START) ? 1 : 0,
3927                                          argc, argv, combinedopt);
3928                 break;
3929         case CTLADM_CMD_SYNC_CACHE:
3930                 retval = cctl_sync_cache(fd, target, lun, initid, retries,
3931                                          argc, argv, combinedopt);
3932                 break;
3933         case CTLADM_CMD_SHUTDOWN:
3934         case CTLADM_CMD_STARTUP:
3935                 retval = cctl_startup_shutdown(fd, target, lun, initid,
3936                                                command);
3937                 break;
3938         case CTLADM_CMD_HARDSTOP:
3939         case CTLADM_CMD_HARDSTART:
3940                 retval = cctl_hardstopstart(fd, command);
3941                 break;
3942         case CTLADM_CMD_BBRREAD:
3943                 retval = cctl_bbrread(fd, target, lun, initid, argc, argv,
3944                                       combinedopt);
3945                 break;
3946         case CTLADM_CMD_LUNLIST:
3947                 retval = cctl_lunlist(fd);
3948                 break;
3949         case CTLADM_CMD_DELAY:
3950                 retval = cctl_delay(fd, target, lun, argc, argv, combinedopt);
3951                 break;
3952         case CTLADM_CMD_REALSYNC:
3953                 retval = cctl_realsync(fd, argc, argv);
3954                 break;
3955         case CTLADM_CMD_SETSYNC:
3956         case CTLADM_CMD_GETSYNC:
3957                 retval = cctl_getsetsync(fd, target, lun, command,
3958                                          argc, argv, combinedopt);
3959                 break;
3960         case CTLADM_CMD_ERR_INJECT:
3961                 retval = cctl_error_inject(fd, target, lun, argc, argv,
3962                                            combinedopt);
3963                 break;
3964         case CTLADM_CMD_DUMPOOA:
3965                 retval = cctl_dump_ooa(fd, argc, argv);
3966                 break;
3967         case CTLADM_CMD_DUMPSTRUCTS:
3968                 retval = cctl_dump_structs(fd, cmdargs);
3969                 break;
3970         case CTLADM_CMD_PRES_IN:
3971                 retval = cctl_persistent_reserve_in(fd, target, lun, initid, 
3972                                                     argc, argv, combinedopt,
3973                                                     retries);
3974                 break;
3975         case CTLADM_CMD_PRES_OUT:
3976                 retval = cctl_persistent_reserve_out(fd, target, lun, initid, 
3977                                                      argc, argv, combinedopt,
3978                                                      retries);
3979                 break;
3980         case CTLADM_CMD_INQ_VPD_DEVID:
3981                 retval = cctl_inquiry_vpd_devid(fd, target, lun, initid);
3982                 break;
3983         case CTLADM_CMD_RTPG:
3984                 retval = cctl_report_target_port_group(fd, target, lun, initid);
3985                 break;
3986         case CTLADM_CMD_HELP:
3987         default:
3988                 usage(retval);
3989                 break;
3990         }
3991 bailout:
3992
3993         if (fd != -1)
3994                 close(fd);
3995
3996         exit (retval);
3997 }
3998
3999 /*
4000  * vim: ts=8
4001  */