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