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