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