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