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