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