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