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