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