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