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