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