1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2012 The FreeBSD Foundation 5 * All rights reserved. 6 * 7 * This software was developed by Edward Tomasz Napierala under sponsorship 8 * from the FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/ioctl.h> 37 #include <sys/param.h> 38 #include <sys/linker.h> 39 #include <assert.h> 40 #include <ctype.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <limits.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 #include <libxo/xo.h> 49 50 #include <iscsi_ioctl.h> 51 #include "iscsictl.h" 52 53 struct conf * 54 conf_new(void) 55 { 56 struct conf *conf; 57 58 conf = calloc(1, sizeof(*conf)); 59 if (conf == NULL) 60 xo_err(1, "calloc"); 61 62 TAILQ_INIT(&conf->conf_targets); 63 64 return (conf); 65 } 66 67 struct target * 68 target_find(struct conf *conf, const char *nickname) 69 { 70 struct target *targ; 71 72 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { 73 if (targ->t_nickname != NULL && 74 strcasecmp(targ->t_nickname, nickname) == 0) 75 return (targ); 76 } 77 78 return (NULL); 79 } 80 81 struct target * 82 target_new(struct conf *conf) 83 { 84 struct target *targ; 85 86 targ = calloc(1, sizeof(*targ)); 87 if (targ == NULL) 88 xo_err(1, "calloc"); 89 targ->t_conf = conf; 90 TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); 91 92 return (targ); 93 } 94 95 void 96 target_delete(struct target *targ) 97 { 98 99 TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); 100 free(targ); 101 } 102 103 static char * 104 default_initiator_name(void) 105 { 106 char *name; 107 size_t namelen; 108 int error; 109 110 namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN); 111 112 name = calloc(1, namelen + 1); 113 if (name == NULL) 114 xo_err(1, "calloc"); 115 strcpy(name, DEFAULT_IQN); 116 error = gethostname(name + strlen(DEFAULT_IQN), 117 namelen - strlen(DEFAULT_IQN)); 118 if (error != 0) 119 xo_err(1, "gethostname"); 120 121 return (name); 122 } 123 124 static bool 125 valid_hex(const char ch) 126 { 127 switch (ch) { 128 case '0': 129 case '1': 130 case '2': 131 case '3': 132 case '4': 133 case '5': 134 case '6': 135 case '7': 136 case '8': 137 case '9': 138 case 'a': 139 case 'A': 140 case 'b': 141 case 'B': 142 case 'c': 143 case 'C': 144 case 'd': 145 case 'D': 146 case 'e': 147 case 'E': 148 case 'f': 149 case 'F': 150 return (true); 151 default: 152 return (false); 153 } 154 } 155 156 int 157 parse_enable(const char *enable) 158 { 159 if (enable == NULL) 160 return (ENABLE_UNSPECIFIED); 161 162 if (strcasecmp(enable, "on") == 0 || 163 strcasecmp(enable, "yes") == 0) 164 return (ENABLE_ON); 165 166 if (strcasecmp(enable, "off") == 0 || 167 strcasecmp(enable, "no") == 0) 168 return (ENABLE_OFF); 169 170 return (ENABLE_UNSPECIFIED); 171 } 172 173 bool 174 valid_iscsi_name(const char *name) 175 { 176 int i; 177 178 if (strlen(name) >= MAX_NAME_LEN) { 179 xo_warnx("overlong name for \"%s\"; max length allowed " 180 "by iSCSI specification is %d characters", 181 name, MAX_NAME_LEN); 182 return (false); 183 } 184 185 /* 186 * In the cases below, we don't return an error, just in case the admin 187 * was right, and we're wrong. 188 */ 189 if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) { 190 for (i = strlen("iqn."); name[i] != '\0'; i++) { 191 /* 192 * XXX: We should verify UTF-8 normalisation, as defined 193 * by 3.2.6.2: iSCSI Name Encoding. 194 */ 195 if (isalnum(name[i])) 196 continue; 197 if (name[i] == '-' || name[i] == '.' || name[i] == ':') 198 continue; 199 xo_warnx("invalid character \"%c\" in iSCSI name " 200 "\"%s\"; allowed characters are letters, digits, " 201 "'-', '.', and ':'", name[i], name); 202 break; 203 } 204 /* 205 * XXX: Check more stuff: valid date and a valid reversed domain. 206 */ 207 } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) { 208 if (strlen(name) != strlen("eui.") + 16) 209 xo_warnx("invalid iSCSI name \"%s\"; the \"eui.\" " 210 "should be followed by exactly 16 hexadecimal " 211 "digits", name); 212 for (i = strlen("eui."); name[i] != '\0'; i++) { 213 if (!valid_hex(name[i])) { 214 xo_warnx("invalid character \"%c\" in iSCSI " 215 "name \"%s\"; allowed characters are 1-9 " 216 "and A-F", name[i], name); 217 break; 218 } 219 } 220 } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) { 221 if (strlen(name) > strlen("naa.") + 32) 222 xo_warnx("invalid iSCSI name \"%s\"; the \"naa.\" " 223 "should be followed by at most 32 hexadecimal " 224 "digits", name); 225 for (i = strlen("naa."); name[i] != '\0'; i++) { 226 if (!valid_hex(name[i])) { 227 xo_warnx("invalid character \"%c\" in ISCSI " 228 "name \"%s\"; allowed characters are 1-9 " 229 "and A-F", name[i], name); 230 break; 231 } 232 } 233 } else { 234 xo_warnx("invalid iSCSI name \"%s\"; should start with " 235 "either \".iqn\", \"eui.\", or \"naa.\"", 236 name); 237 } 238 return (true); 239 } 240 241 void 242 conf_verify(struct conf *conf) 243 { 244 struct target *targ; 245 246 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { 247 assert(targ->t_nickname != NULL); 248 if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED) 249 targ->t_session_type = SESSION_TYPE_NORMAL; 250 if (targ->t_session_type == SESSION_TYPE_NORMAL && 251 targ->t_name == NULL) 252 xo_errx(1, "missing TargetName for target \"%s\"", 253 targ->t_nickname); 254 if (targ->t_session_type == SESSION_TYPE_DISCOVERY && 255 targ->t_name != NULL) 256 xo_errx(1, "cannot specify TargetName for discovery " 257 "sessions for target \"%s\"", targ->t_nickname); 258 if (targ->t_name != NULL) { 259 if (valid_iscsi_name(targ->t_name) == false) 260 xo_errx(1, "invalid target name \"%s\"", 261 targ->t_name); 262 } 263 if (targ->t_protocol == PROTOCOL_UNSPECIFIED) 264 targ->t_protocol = PROTOCOL_ISCSI; 265 if (targ->t_address == NULL) 266 xo_errx(1, "missing TargetAddress for target \"%s\"", 267 targ->t_nickname); 268 if (targ->t_initiator_name == NULL) 269 targ->t_initiator_name = default_initiator_name(); 270 if (valid_iscsi_name(targ->t_initiator_name) == false) 271 xo_errx(1, "invalid initiator name \"%s\"", 272 targ->t_initiator_name); 273 if (targ->t_header_digest == DIGEST_UNSPECIFIED) 274 targ->t_header_digest = DIGEST_NONE; 275 if (targ->t_data_digest == DIGEST_UNSPECIFIED) 276 targ->t_data_digest = DIGEST_NONE; 277 if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) { 278 if (targ->t_user != NULL || targ->t_secret != NULL || 279 targ->t_mutual_user != NULL || 280 targ->t_mutual_secret != NULL) 281 targ->t_auth_method = 282 AUTH_METHOD_CHAP; 283 else 284 targ->t_auth_method = 285 AUTH_METHOD_NONE; 286 } 287 if (targ->t_auth_method == AUTH_METHOD_CHAP) { 288 if (targ->t_user == NULL) { 289 xo_errx(1, "missing chapIName for target \"%s\"", 290 targ->t_nickname); 291 } 292 if (targ->t_secret == NULL) 293 xo_errx(1, "missing chapSecret for target \"%s\"", 294 targ->t_nickname); 295 if (targ->t_mutual_user != NULL || 296 targ->t_mutual_secret != NULL) { 297 if (targ->t_mutual_user == NULL) 298 xo_errx(1, "missing tgtChapName for " 299 "target \"%s\"", targ->t_nickname); 300 if (targ->t_mutual_secret == NULL) 301 xo_errx(1, "missing tgtChapSecret for " 302 "target \"%s\"", targ->t_nickname); 303 } 304 } 305 } 306 } 307 308 static void 309 conf_from_target(struct iscsi_session_conf *conf, 310 const struct target *targ) 311 { 312 memset(conf, 0, sizeof(*conf)); 313 314 /* 315 * XXX: Check bounds and return error instead of silently truncating. 316 */ 317 if (targ->t_initiator_name != NULL) 318 strlcpy(conf->isc_initiator, targ->t_initiator_name, 319 sizeof(conf->isc_initiator)); 320 if (targ->t_initiator_address != NULL) 321 strlcpy(conf->isc_initiator_addr, targ->t_initiator_address, 322 sizeof(conf->isc_initiator_addr)); 323 if (targ->t_initiator_alias != NULL) 324 strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias, 325 sizeof(conf->isc_initiator_alias)); 326 if (targ->t_name != NULL) 327 strlcpy(conf->isc_target, targ->t_name, 328 sizeof(conf->isc_target)); 329 if (targ->t_address != NULL) 330 strlcpy(conf->isc_target_addr, targ->t_address, 331 sizeof(conf->isc_target_addr)); 332 if (targ->t_user != NULL) 333 strlcpy(conf->isc_user, targ->t_user, 334 sizeof(conf->isc_user)); 335 if (targ->t_secret != NULL) 336 strlcpy(conf->isc_secret, targ->t_secret, 337 sizeof(conf->isc_secret)); 338 if (targ->t_mutual_user != NULL) 339 strlcpy(conf->isc_mutual_user, targ->t_mutual_user, 340 sizeof(conf->isc_mutual_user)); 341 if (targ->t_mutual_secret != NULL) 342 strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret, 343 sizeof(conf->isc_mutual_secret)); 344 if (targ->t_session_type == SESSION_TYPE_DISCOVERY) 345 conf->isc_discovery = 1; 346 if (targ->t_enable != ENABLE_OFF) 347 conf->isc_enable = 1; 348 if (targ->t_protocol == PROTOCOL_ISER) 349 conf->isc_iser = 1; 350 if (targ->t_offload != NULL) 351 strlcpy(conf->isc_offload, targ->t_offload, 352 sizeof(conf->isc_offload)); 353 if (targ->t_header_digest == DIGEST_CRC32C) 354 conf->isc_header_digest = ISCSI_DIGEST_CRC32C; 355 else 356 conf->isc_header_digest = ISCSI_DIGEST_NONE; 357 if (targ->t_data_digest == DIGEST_CRC32C) 358 conf->isc_data_digest = ISCSI_DIGEST_CRC32C; 359 else 360 conf->isc_data_digest = ISCSI_DIGEST_NONE; 361 } 362 363 static int 364 kernel_add(int iscsi_fd, const struct target *targ) 365 { 366 struct iscsi_session_add isa; 367 int error; 368 369 memset(&isa, 0, sizeof(isa)); 370 conf_from_target(&isa.isa_conf, targ); 371 error = ioctl(iscsi_fd, ISCSISADD, &isa); 372 if (error != 0) 373 xo_warn("ISCSISADD"); 374 return (error); 375 } 376 377 static int 378 kernel_modify(int iscsi_fd, unsigned int session_id, const struct target *targ) 379 { 380 struct iscsi_session_modify ism; 381 int error; 382 383 memset(&ism, 0, sizeof(ism)); 384 ism.ism_session_id = session_id; 385 conf_from_target(&ism.ism_conf, targ); 386 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); 387 if (error != 0) 388 xo_warn("ISCSISMODIFY"); 389 return (error); 390 } 391 392 static void 393 kernel_modify_some(int iscsi_fd, unsigned int session_id, const char *target, 394 const char *target_addr, const char *user, const char *secret, int enable) 395 { 396 struct iscsi_session_state *states = NULL; 397 struct iscsi_session_state *state; 398 struct iscsi_session_conf *conf; 399 struct iscsi_session_list isl; 400 struct iscsi_session_modify ism; 401 unsigned int i, nentries = 1; 402 int error; 403 404 for (;;) { 405 states = realloc(states, 406 nentries * sizeof(struct iscsi_session_state)); 407 if (states == NULL) 408 xo_err(1, "realloc"); 409 410 memset(&isl, 0, sizeof(isl)); 411 isl.isl_nentries = nentries; 412 isl.isl_pstates = states; 413 414 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 415 if (error != 0 && errno == EMSGSIZE) { 416 nentries *= 4; 417 continue; 418 } 419 break; 420 } 421 if (error != 0) 422 xo_errx(1, "ISCSISLIST"); 423 424 for (i = 0; i < isl.isl_nentries; i++) { 425 state = &states[i]; 426 427 if (state->iss_id == session_id) 428 break; 429 } 430 if (i == isl.isl_nentries) 431 xo_errx(1, "session-id %u not found", session_id); 432 433 conf = &state->iss_conf; 434 435 if (target != NULL) 436 strlcpy(conf->isc_target, target, sizeof(conf->isc_target)); 437 if (target_addr != NULL) 438 strlcpy(conf->isc_target_addr, target_addr, 439 sizeof(conf->isc_target_addr)); 440 if (user != NULL) 441 strlcpy(conf->isc_user, user, sizeof(conf->isc_user)); 442 if (secret != NULL) 443 strlcpy(conf->isc_secret, secret, sizeof(conf->isc_secret)); 444 if (enable == ENABLE_ON) 445 conf->isc_enable = 1; 446 else if (enable == ENABLE_OFF) 447 conf->isc_enable = 0; 448 449 memset(&ism, 0, sizeof(ism)); 450 ism.ism_session_id = session_id; 451 memcpy(&ism.ism_conf, conf, sizeof(ism.ism_conf)); 452 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); 453 if (error != 0) 454 xo_warn("ISCSISMODIFY"); 455 } 456 457 static int 458 kernel_remove(int iscsi_fd, const struct target *targ) 459 { 460 struct iscsi_session_remove isr; 461 int error; 462 463 memset(&isr, 0, sizeof(isr)); 464 conf_from_target(&isr.isr_conf, targ); 465 error = ioctl(iscsi_fd, ISCSISREMOVE, &isr); 466 if (error != 0) 467 xo_warn("ISCSISREMOVE"); 468 return (error); 469 } 470 471 /* 472 * XXX: Add filtering. 473 */ 474 static int 475 kernel_list(int iscsi_fd, const struct target *targ __unused, 476 int verbose) 477 { 478 struct iscsi_session_state *states = NULL; 479 const struct iscsi_session_state *state; 480 const struct iscsi_session_conf *conf; 481 struct iscsi_session_list isl; 482 unsigned int i, nentries = 1; 483 int error; 484 485 for (;;) { 486 states = realloc(states, 487 nentries * sizeof(struct iscsi_session_state)); 488 if (states == NULL) 489 xo_err(1, "realloc"); 490 491 memset(&isl, 0, sizeof(isl)); 492 isl.isl_nentries = nentries; 493 isl.isl_pstates = states; 494 495 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 496 if (error != 0 && errno == EMSGSIZE) { 497 nentries *= 4; 498 continue; 499 } 500 break; 501 } 502 if (error != 0) { 503 xo_warn("ISCSISLIST"); 504 return (error); 505 } 506 507 if (verbose != 0) { 508 xo_open_list("session"); 509 for (i = 0; i < isl.isl_nentries; i++) { 510 state = &states[i]; 511 conf = &state->iss_conf; 512 513 xo_open_instance("session"); 514 515 /* 516 * Display-only modifier as this information 517 * is also present within the 'session' container 518 */ 519 xo_emit("{L:/%-26s}{V:sessionId/%u}\n", 520 "Session ID:", state->iss_id); 521 522 xo_open_container("initiator"); 523 xo_emit("{L:/%-26s}{V:name/%s}\n", 524 "Initiator name:", conf->isc_initiator); 525 xo_emit("{L:/%-26s}{V:portal/%s}\n", 526 "Initiator portal:", conf->isc_initiator_addr); 527 xo_emit("{L:/%-26s}{V:alias/%s}\n", 528 "Initiator alias:", conf->isc_initiator_alias); 529 xo_close_container("initiator"); 530 531 xo_open_container("target"); 532 xo_emit("{L:/%-26s}{V:name/%s}\n", 533 "Target name:", conf->isc_target); 534 xo_emit("{L:/%-26s}{V:portal/%s}\n", 535 "Target portal:", conf->isc_target_addr); 536 xo_emit("{L:/%-26s}{V:alias/%s}\n", 537 "Target alias:", state->iss_target_alias); 538 xo_close_container("target"); 539 540 xo_open_container("auth"); 541 xo_emit("{L:/%-26s}{V:user/%s}\n", 542 "User:", conf->isc_user); 543 xo_emit("{L:/%-26s}{V:secret/%s}\n", 544 "Secret:", conf->isc_secret); 545 xo_emit("{L:/%-26s}{V:mutualUser/%s}\n", 546 "Mutual user:", conf->isc_mutual_user); 547 xo_emit("{L:/%-26s}{V:mutualSecret/%s}\n", 548 "Mutual secret:", conf->isc_mutual_secret); 549 xo_close_container("auth"); 550 551 xo_emit("{L:/%-26s}{V:type/%s}\n", 552 "Session type:", 553 conf->isc_discovery ? "Discovery" : "Normal"); 554 xo_emit("{L:/%-26s}{V:enable/%s}\n", 555 "Enable:", 556 conf->isc_enable ? "Yes" : "No"); 557 xo_emit("{L:/%-26s}{V:state/%s}\n", 558 "Session state:", 559 state->iss_connected ? "Connected" : "Disconnected"); 560 xo_emit("{L:/%-26s}{V:failureReason/%s}\n", 561 "Failure reason:", state->iss_reason); 562 xo_emit("{L:/%-26s}{V:headerDigest/%s}\n", 563 "Header digest:", 564 state->iss_header_digest == ISCSI_DIGEST_CRC32C ? 565 "CRC32C" : "None"); 566 xo_emit("{L:/%-26s}{V:dataDigest/%s}\n", 567 "Data digest:", 568 state->iss_data_digest == ISCSI_DIGEST_CRC32C ? 569 "CRC32C" : "None"); 570 xo_emit("{L:/%-26s}{V:recvDataSegmentLen/%d}\n", 571 "MaxRecvDataSegmentLength:", 572 state->iss_max_recv_data_segment_length); 573 xo_emit("{L:/%-26s}{V:sendDataSegmentLen/%d}\n", 574 "MaxSendDataSegmentLength:", 575 state->iss_max_send_data_segment_length); 576 xo_emit("{L:/%-26s}{V:maxBurstLen/%d}\n", 577 "MaxBurstLen:", state->iss_max_burst_length); 578 xo_emit("{L:/%-26s}{V:firstBurstLen/%d}\n", 579 "FirstBurstLen:", state->iss_first_burst_length); 580 xo_emit("{L:/%-26s}{V:immediateData/%s}\n", 581 "ImmediateData:", state->iss_immediate_data ? "Yes" : "No"); 582 xo_emit("{L:/%-26s}{V:iSER/%s}\n", 583 "iSER (RDMA):", conf->isc_iser ? "Yes" : "No"); 584 xo_emit("{L:/%-26s}{V:offloadDriver/%s}\n", 585 "Offload driver:", state->iss_offload); 586 xo_emit("{L:/%-26s}", 587 "Device nodes:"); 588 print_periphs(state->iss_id); 589 xo_emit("\n\n"); 590 xo_close_instance("session"); 591 } 592 xo_close_list("session"); 593 } else { 594 xo_emit("{T:/%-36s} {T:/%-16s} {T:/%s}\n", 595 "Target name", "Target portal", "State"); 596 597 if (isl.isl_nentries != 0) 598 xo_open_list("session"); 599 for (i = 0; i < isl.isl_nentries; i++) { 600 601 state = &states[i]; 602 conf = &state->iss_conf; 603 604 xo_open_instance("session"); 605 xo_emit("{V:name/%-36s/%s} {V:portal/%-16s/%s} ", 606 conf->isc_target, conf->isc_target_addr); 607 608 if (state->iss_reason[0] != '\0') { 609 xo_emit("{V:state/%s}\n", state->iss_reason); 610 } else { 611 if (conf->isc_discovery) { 612 xo_emit("{V:state}\n", "Discovery"); 613 } else if (conf->isc_enable == 0) { 614 xo_emit("{V:state}\n", "Disabled"); 615 } else if (state->iss_connected) { 616 xo_emit("{V:state}: ", "Connected"); 617 print_periphs(state->iss_id); 618 xo_emit("\n"); 619 } else { 620 xo_emit("{V:state}\n", "Disconnected"); 621 } 622 } 623 xo_close_instance("session"); 624 } 625 if (isl.isl_nentries != 0) 626 xo_close_list("session"); 627 } 628 629 return (0); 630 } 631 632 static int 633 kernel_wait(int iscsi_fd, int timeout) 634 { 635 struct iscsi_session_state *states = NULL; 636 const struct iscsi_session_state *state; 637 struct iscsi_session_list isl; 638 unsigned int i, nentries = 1; 639 bool all_connected; 640 int error; 641 642 for (;;) { 643 for (;;) { 644 states = realloc(states, 645 nentries * sizeof(struct iscsi_session_state)); 646 if (states == NULL) 647 xo_err(1, "realloc"); 648 649 memset(&isl, 0, sizeof(isl)); 650 isl.isl_nentries = nentries; 651 isl.isl_pstates = states; 652 653 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 654 if (error != 0 && errno == EMSGSIZE) { 655 nentries *= 4; 656 continue; 657 } 658 break; 659 } 660 if (error != 0) { 661 xo_warn("ISCSISLIST"); 662 return (error); 663 } 664 665 all_connected = true; 666 for (i = 0; i < isl.isl_nentries; i++) { 667 state = &states[i]; 668 669 if (!state->iss_connected) { 670 all_connected = false; 671 break; 672 } 673 } 674 675 if (all_connected) 676 return (0); 677 678 sleep(1); 679 680 if (timeout > 0) { 681 timeout--; 682 if (timeout == 0) 683 return (1); 684 } 685 } 686 } 687 688 static void 689 usage(void) 690 { 691 692 fprintf(stderr, "usage: iscsictl -A -p portal -t target " 693 "[-u user -s secret] [-w timeout] [-e on | off]\n"); 694 fprintf(stderr, " iscsictl -A -d discovery-host " 695 "[-u user -s secret] [-e on | off]\n"); 696 fprintf(stderr, " iscsictl -A -a [-c path]\n"); 697 fprintf(stderr, " iscsictl -A -n nickname [-c path]\n"); 698 fprintf(stderr, " iscsictl -M -i session-id [-p portal] " 699 "[-t target] [-u user] [-s secret] [-e on | off]\n"); 700 fprintf(stderr, " iscsictl -M -i session-id -n nickname " 701 "[-c path]\n"); 702 fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n"); 703 fprintf(stderr, " iscsictl -R -a\n"); 704 fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); 705 fprintf(stderr, " iscsictl -L [-v] [-w timeout]\n"); 706 exit(1); 707 } 708 709 int 710 main(int argc, char **argv) 711 { 712 int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, 713 rflag = 0, vflag = 0; 714 const char *conf_path = DEFAULT_CONFIG_PATH; 715 char *nickname = NULL, *discovery_host = NULL, *portal = NULL, 716 *target = NULL, *user = NULL, *secret = NULL; 717 int timeout = -1, enable = ENABLE_UNSPECIFIED; 718 long long session_id = -1; 719 char *end; 720 int ch, error, iscsi_fd, retval, saved_errno; 721 int failed = 0; 722 struct conf *conf; 723 struct target *targ; 724 725 argc = xo_parse_args(argc, argv); 726 xo_open_container("iscsictl"); 727 728 while ((ch = getopt(argc, argv, "AMRLac:d:e:i:n:p:rt:u:s:vw:")) != -1) { 729 switch (ch) { 730 case 'A': 731 Aflag = 1; 732 break; 733 case 'M': 734 Mflag = 1; 735 break; 736 case 'R': 737 Rflag = 1; 738 break; 739 case 'L': 740 Lflag = 1; 741 break; 742 case 'a': 743 aflag = 1; 744 break; 745 case 'c': 746 conf_path = optarg; 747 break; 748 case 'd': 749 discovery_host = optarg; 750 break; 751 case 'e': 752 enable = parse_enable(optarg); 753 if (enable == ENABLE_UNSPECIFIED) { 754 xo_errx(1, "invalid argument to -e, " 755 "must be either \"on\" or \"off\""); 756 } 757 break; 758 case 'i': 759 session_id = strtol(optarg, &end, 10); 760 if ((size_t)(end - optarg) != strlen(optarg)) 761 xo_errx(1, "trailing characters after session-id"); 762 if (session_id < 0) 763 xo_errx(1, "session-id cannot be negative"); 764 if (session_id > UINT_MAX) 765 xo_errx(1, "session-id cannot be greater than %u", 766 UINT_MAX); 767 break; 768 case 'n': 769 nickname = optarg; 770 break; 771 case 'p': 772 portal = optarg; 773 break; 774 case 'r': 775 rflag = 1; 776 break; 777 case 't': 778 target = optarg; 779 break; 780 case 'u': 781 user = optarg; 782 break; 783 case 's': 784 secret = optarg; 785 break; 786 case 'v': 787 vflag = 1; 788 break; 789 case 'w': 790 timeout = strtol(optarg, &end, 10); 791 if ((size_t)(end - optarg) != strlen(optarg)) 792 xo_errx(1, "trailing characters after timeout"); 793 if (timeout < 0) 794 xo_errx(1, "timeout cannot be negative"); 795 break; 796 case '?': 797 default: 798 usage(); 799 } 800 } 801 argc -= optind; 802 if (argc != 0) 803 usage(); 804 805 if (Aflag + Mflag + Rflag + Lflag == 0) 806 Lflag = 1; 807 if (Aflag + Mflag + Rflag + Lflag > 1) 808 xo_errx(1, "at most one of -A, -M, -R, or -L may be specified"); 809 810 /* 811 * Note that we ignore unnecessary/inapplicable "-c" flag; so that 812 * people can do something like "alias ISCSICTL="iscsictl -c path" 813 * in shell scripts. 814 */ 815 if (Aflag != 0) { 816 if (aflag != 0) { 817 if (enable != ENABLE_UNSPECIFIED) 818 xo_errx(1, "-a and -e are mutually exclusive"); 819 if (portal != NULL) 820 xo_errx(1, "-a and -p are mutually exclusive"); 821 if (target != NULL) 822 xo_errx(1, "-a and -t are mutually exclusive"); 823 if (user != NULL) 824 xo_errx(1, "-a and -u are mutually exclusive"); 825 if (secret != NULL) 826 xo_errx(1, "-a and -s are mutually exclusive"); 827 if (nickname != NULL) 828 xo_errx(1, "-a and -n are mutually exclusive"); 829 if (discovery_host != NULL) 830 xo_errx(1, "-a and -d are mutually exclusive"); 831 if (rflag != 0) 832 xo_errx(1, "-a and -r are mutually exclusive"); 833 } else if (nickname != NULL) { 834 if (enable != ENABLE_UNSPECIFIED) 835 xo_errx(1, "-n and -e are mutually exclusive"); 836 if (portal != NULL) 837 xo_errx(1, "-n and -p are mutually exclusive"); 838 if (target != NULL) 839 xo_errx(1, "-n and -t are mutually exclusive"); 840 if (user != NULL) 841 xo_errx(1, "-n and -u are mutually exclusive"); 842 if (secret != NULL) 843 xo_errx(1, "-n and -s are mutually exclusive"); 844 if (discovery_host != NULL) 845 xo_errx(1, "-n and -d are mutually exclusive"); 846 if (rflag != 0) 847 xo_errx(1, "-n and -r are mutually exclusive"); 848 } else if (discovery_host != NULL) { 849 if (portal != NULL) 850 xo_errx(1, "-d and -p are mutually exclusive"); 851 if (target != NULL) 852 xo_errx(1, "-d and -t are mutually exclusive"); 853 } else { 854 if (target == NULL && portal == NULL) 855 xo_errx(1, "must specify -a, -n or -t/-p"); 856 857 if (target != NULL && portal == NULL) 858 xo_errx(1, "-t must always be used with -p"); 859 if (portal != NULL && target == NULL) 860 xo_errx(1, "-p must always be used with -t"); 861 } 862 863 if (user != NULL && secret == NULL) 864 xo_errx(1, "-u must always be used with -s"); 865 if (secret != NULL && user == NULL) 866 xo_errx(1, "-s must always be used with -u"); 867 868 if (session_id != -1) 869 xo_errx(1, "-i cannot be used with -A"); 870 if (vflag != 0) 871 xo_errx(1, "-v cannot be used with -A"); 872 873 } else if (Mflag != 0) { 874 if (session_id == -1) 875 xo_errx(1, "-M requires -i"); 876 877 if (nickname != NULL) { 878 if (enable != ENABLE_UNSPECIFIED) 879 xo_errx(1, "-n and -e are mutually exclusive"); 880 if (portal != NULL) 881 xo_errx(1, "-n and -p are mutually exclusive"); 882 if (target != NULL) 883 xo_errx(1, "-n and -t are mutually exclusive"); 884 if (user != NULL) 885 xo_errx(1, "-n and -u are mutually exclusive"); 886 if (secret != NULL) 887 xo_errx(1, "-n and -s are mutually exclusive"); 888 } 889 890 if (aflag != 0) 891 xo_errx(1, "-a cannot be used with -M"); 892 if (discovery_host != NULL) 893 xo_errx(1, "-d cannot be used with -M"); 894 if (rflag != 0) 895 xo_errx(1, "-r cannot be used with -M"); 896 if (vflag != 0) 897 xo_errx(1, "-v cannot be used with -M"); 898 if (timeout != -1) 899 xo_errx(1, "-w cannot be used with -M"); 900 901 } else if (Rflag != 0) { 902 if (aflag != 0) { 903 if (portal != NULL) 904 xo_errx(1, "-a and -p are mutually exclusive"); 905 if (target != NULL) 906 xo_errx(1, "-a and -t are mutually exclusive"); 907 if (nickname != NULL) 908 xo_errx(1, "-a and -n are mutually exclusive"); 909 } else if (nickname != NULL) { 910 if (portal != NULL) 911 xo_errx(1, "-n and -p are mutually exclusive"); 912 if (target != NULL) 913 xo_errx(1, "-n and -t are mutually exclusive"); 914 } else if (target == NULL && portal == NULL) { 915 xo_errx(1, "must specify either -a, -n, -t, or -p"); 916 } 917 918 if (discovery_host != NULL) 919 xo_errx(1, "-d cannot be used with -R"); 920 if (enable != ENABLE_UNSPECIFIED) 921 xo_errx(1, "-e cannot be used with -R"); 922 if (session_id != -1) 923 xo_errx(1, "-i cannot be used with -R"); 924 if (rflag != 0) 925 xo_errx(1, "-r cannot be used with -R"); 926 if (user != NULL) 927 xo_errx(1, "-u cannot be used with -R"); 928 if (secret != NULL) 929 xo_errx(1, "-s cannot be used with -R"); 930 if (vflag != 0) 931 xo_errx(1, "-v cannot be used with -R"); 932 if (timeout != -1) 933 xo_errx(1, "-w cannot be used with -R"); 934 935 } else { 936 assert(Lflag != 0); 937 938 if (discovery_host != NULL) 939 xo_errx(1, "-d cannot be used with -L"); 940 if (session_id != -1) 941 xo_errx(1, "-i cannot be used with -L"); 942 if (nickname != NULL) 943 xo_errx(1, "-n cannot be used with -L"); 944 if (portal != NULL) 945 xo_errx(1, "-p cannot be used with -L"); 946 if (rflag != 0) 947 xo_errx(1, "-r cannot be used with -L"); 948 if (target != NULL) 949 xo_errx(1, "-t cannot be used with -L"); 950 if (user != NULL) 951 xo_errx(1, "-u cannot be used with -L"); 952 if (secret != NULL) 953 xo_errx(1, "-s cannot be used with -L"); 954 } 955 956 iscsi_fd = open(ISCSI_PATH, O_RDWR); 957 if (iscsi_fd < 0 && errno == ENOENT) { 958 saved_errno = errno; 959 retval = kldload("iscsi"); 960 if (retval != -1) 961 iscsi_fd = open(ISCSI_PATH, O_RDWR); 962 else 963 errno = saved_errno; 964 } 965 if (iscsi_fd < 0) 966 xo_err(1, "failed to open %s", ISCSI_PATH); 967 968 if (Aflag != 0 && aflag != 0) { 969 conf = conf_new_from_file(conf_path); 970 971 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) 972 failed += kernel_add(iscsi_fd, targ); 973 } else if (nickname != NULL) { 974 conf = conf_new_from_file(conf_path); 975 targ = target_find(conf, nickname); 976 if (targ == NULL) 977 xo_errx(1, "target %s not found in %s", 978 nickname, conf_path); 979 980 if (Aflag != 0) 981 failed += kernel_add(iscsi_fd, targ); 982 else if (Mflag != 0) 983 failed += kernel_modify(iscsi_fd, session_id, targ); 984 else if (Rflag != 0) 985 failed += kernel_remove(iscsi_fd, targ); 986 else 987 failed += kernel_list(iscsi_fd, targ, vflag); 988 } else if (Mflag != 0) { 989 kernel_modify_some(iscsi_fd, session_id, target, portal, 990 user, secret, enable); 991 } else { 992 if (Aflag != 0 && target != NULL) { 993 if (valid_iscsi_name(target) == false) 994 xo_errx(1, "invalid target name \"%s\"", target); 995 } 996 conf = conf_new(); 997 targ = target_new(conf); 998 targ->t_initiator_name = default_initiator_name(); 999 targ->t_header_digest = DIGEST_NONE; 1000 targ->t_data_digest = DIGEST_NONE; 1001 targ->t_name = target; 1002 if (discovery_host != NULL) { 1003 targ->t_session_type = SESSION_TYPE_DISCOVERY; 1004 targ->t_address = discovery_host; 1005 } else { 1006 targ->t_session_type = SESSION_TYPE_NORMAL; 1007 targ->t_address = portal; 1008 } 1009 targ->t_enable = enable; 1010 if (rflag != 0) 1011 targ->t_protocol = PROTOCOL_ISER; 1012 targ->t_user = user; 1013 targ->t_secret = secret; 1014 1015 if (Aflag != 0) 1016 failed += kernel_add(iscsi_fd, targ); 1017 else if (Rflag != 0) 1018 failed += kernel_remove(iscsi_fd, targ); 1019 else 1020 failed += kernel_list(iscsi_fd, targ, vflag); 1021 } 1022 1023 if (timeout != -1) 1024 failed += kernel_wait(iscsi_fd, timeout); 1025 1026 error = close(iscsi_fd); 1027 if (error != 0) 1028 xo_err(1, "close"); 1029 1030 xo_close_container("iscsictl"); 1031 xo_finish(); 1032 1033 if (failed != 0) 1034 return (1); 1035 1036 return (0); 1037 } 1038