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