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