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