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