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