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