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