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