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