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