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