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 conf->isc_enable != 0) { 610 xo_emit("{V:state/%s}\n", state->iss_reason); 611 } else { 612 if (conf->isc_discovery) { 613 xo_emit("{V:state}\n", "Discovery"); 614 } else if (conf->isc_enable == 0) { 615 xo_emit("{V:state}\n", "Disabled"); 616 } else if (state->iss_connected) { 617 xo_emit("{V:state}: ", "Connected"); 618 print_periphs(state->iss_id); 619 xo_emit("\n"); 620 } else { 621 xo_emit("{V:state}\n", "Disconnected"); 622 } 623 } 624 xo_close_instance("session"); 625 } 626 if (isl.isl_nentries != 0) 627 xo_close_list("session"); 628 } 629 630 return (0); 631 } 632 633 static int 634 kernel_wait(int iscsi_fd, int timeout) 635 { 636 struct iscsi_session_state *states = NULL; 637 const struct iscsi_session_state *state; 638 struct iscsi_session_list isl; 639 unsigned int i, nentries = 1; 640 bool all_connected; 641 int error; 642 643 for (;;) { 644 for (;;) { 645 states = realloc(states, 646 nentries * sizeof(struct iscsi_session_state)); 647 if (states == NULL) 648 xo_err(1, "realloc"); 649 650 memset(&isl, 0, sizeof(isl)); 651 isl.isl_nentries = nentries; 652 isl.isl_pstates = states; 653 654 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 655 if (error != 0 && errno == EMSGSIZE) { 656 nentries *= 4; 657 continue; 658 } 659 break; 660 } 661 if (error != 0) { 662 xo_warn("ISCSISLIST"); 663 return (error); 664 } 665 666 all_connected = true; 667 for (i = 0; i < isl.isl_nentries; i++) { 668 state = &states[i]; 669 670 if (!state->iss_connected) { 671 all_connected = false; 672 break; 673 } 674 } 675 676 if (all_connected) 677 return (0); 678 679 sleep(1); 680 681 if (timeout > 0) { 682 timeout--; 683 if (timeout == 0) 684 return (1); 685 } 686 } 687 } 688 689 static void 690 usage(void) 691 { 692 693 fprintf(stderr, "usage: iscsictl -A -p portal -t target " 694 "[-u user -s secret] [-w timeout] [-e on | off]\n"); 695 fprintf(stderr, " iscsictl -A -d discovery-host " 696 "[-u user -s secret] [-e on | off]\n"); 697 fprintf(stderr, " iscsictl -A -a [-c path]\n"); 698 fprintf(stderr, " iscsictl -A -n nickname [-c path]\n"); 699 fprintf(stderr, " iscsictl -M -i session-id [-p portal] " 700 "[-t target] [-u user] [-s secret] [-e on | off]\n"); 701 fprintf(stderr, " iscsictl -M -i session-id -n nickname " 702 "[-c path]\n"); 703 fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n"); 704 fprintf(stderr, " iscsictl -R -a\n"); 705 fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); 706 fprintf(stderr, " iscsictl -L [-v] [-w timeout]\n"); 707 exit(1); 708 } 709 710 int 711 main(int argc, char **argv) 712 { 713 int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, 714 rflag = 0, vflag = 0; 715 const char *conf_path = DEFAULT_CONFIG_PATH; 716 char *nickname = NULL, *discovery_host = NULL, *portal = NULL, 717 *target = NULL, *user = NULL, *secret = NULL; 718 int timeout = -1, enable = ENABLE_UNSPECIFIED; 719 long long session_id = -1; 720 char *end; 721 int ch, error, iscsi_fd, retval, saved_errno; 722 int failed = 0; 723 struct conf *conf; 724 struct target *targ; 725 726 argc = xo_parse_args(argc, argv); 727 xo_open_container("iscsictl"); 728 729 while ((ch = getopt(argc, argv, "AMRLac:d:e:i:n:p:rt:u:s:vw:")) != -1) { 730 switch (ch) { 731 case 'A': 732 Aflag = 1; 733 break; 734 case 'M': 735 Mflag = 1; 736 break; 737 case 'R': 738 Rflag = 1; 739 break; 740 case 'L': 741 Lflag = 1; 742 break; 743 case 'a': 744 aflag = 1; 745 break; 746 case 'c': 747 conf_path = optarg; 748 break; 749 case 'd': 750 discovery_host = optarg; 751 break; 752 case 'e': 753 enable = parse_enable(optarg); 754 if (enable == ENABLE_UNSPECIFIED) { 755 xo_errx(1, "invalid argument to -e, " 756 "must be either \"on\" or \"off\""); 757 } 758 break; 759 case 'i': 760 session_id = strtol(optarg, &end, 10); 761 if ((size_t)(end - optarg) != strlen(optarg)) 762 xo_errx(1, "trailing characters after session-id"); 763 if (session_id < 0) 764 xo_errx(1, "session-id cannot be negative"); 765 if (session_id > UINT_MAX) 766 xo_errx(1, "session-id cannot be greater than %u", 767 UINT_MAX); 768 break; 769 case 'n': 770 nickname = optarg; 771 break; 772 case 'p': 773 portal = optarg; 774 break; 775 case 'r': 776 rflag = 1; 777 break; 778 case 't': 779 target = optarg; 780 break; 781 case 'u': 782 user = optarg; 783 break; 784 case 's': 785 secret = optarg; 786 break; 787 case 'v': 788 vflag = 1; 789 break; 790 case 'w': 791 timeout = strtol(optarg, &end, 10); 792 if ((size_t)(end - optarg) != strlen(optarg)) 793 xo_errx(1, "trailing characters after timeout"); 794 if (timeout < 0) 795 xo_errx(1, "timeout cannot be negative"); 796 break; 797 case '?': 798 default: 799 usage(); 800 } 801 } 802 argc -= optind; 803 if (argc != 0) 804 usage(); 805 806 if (Aflag + Mflag + Rflag + Lflag == 0) 807 Lflag = 1; 808 if (Aflag + Mflag + Rflag + Lflag > 1) 809 xo_errx(1, "at most one of -A, -M, -R, or -L may be specified"); 810 811 /* 812 * Note that we ignore unnecessary/inapplicable "-c" flag; so that 813 * people can do something like "alias ISCSICTL="iscsictl -c path" 814 * in shell scripts. 815 */ 816 if (Aflag != 0) { 817 if (aflag != 0) { 818 if (enable != ENABLE_UNSPECIFIED) 819 xo_errx(1, "-a and -e are mutually exclusive"); 820 if (portal != NULL) 821 xo_errx(1, "-a and -p are mutually exclusive"); 822 if (target != NULL) 823 xo_errx(1, "-a and -t are mutually exclusive"); 824 if (user != NULL) 825 xo_errx(1, "-a and -u are mutually exclusive"); 826 if (secret != NULL) 827 xo_errx(1, "-a and -s are mutually exclusive"); 828 if (nickname != NULL) 829 xo_errx(1, "-a and -n are mutually exclusive"); 830 if (discovery_host != NULL) 831 xo_errx(1, "-a and -d are mutually exclusive"); 832 if (rflag != 0) 833 xo_errx(1, "-a and -r are mutually exclusive"); 834 } else if (nickname != NULL) { 835 if (enable != ENABLE_UNSPECIFIED) 836 xo_errx(1, "-n and -e are mutually exclusive"); 837 if (portal != NULL) 838 xo_errx(1, "-n and -p are mutually exclusive"); 839 if (target != NULL) 840 xo_errx(1, "-n and -t are mutually exclusive"); 841 if (user != NULL) 842 xo_errx(1, "-n and -u are mutually exclusive"); 843 if (secret != NULL) 844 xo_errx(1, "-n and -s are mutually exclusive"); 845 if (discovery_host != NULL) 846 xo_errx(1, "-n and -d are mutually exclusive"); 847 if (rflag != 0) 848 xo_errx(1, "-n and -r are mutually exclusive"); 849 } else if (discovery_host != NULL) { 850 if (portal != NULL) 851 xo_errx(1, "-d and -p are mutually exclusive"); 852 if (target != NULL) 853 xo_errx(1, "-d and -t are mutually exclusive"); 854 } else { 855 if (target == NULL && portal == NULL) 856 xo_errx(1, "must specify -a, -n or -t/-p"); 857 858 if (target != NULL && portal == NULL) 859 xo_errx(1, "-t must always be used with -p"); 860 if (portal != NULL && target == NULL) 861 xo_errx(1, "-p must always be used with -t"); 862 } 863 864 if (user != NULL && secret == NULL) 865 xo_errx(1, "-u must always be used with -s"); 866 if (secret != NULL && user == NULL) 867 xo_errx(1, "-s must always be used with -u"); 868 869 if (session_id != -1) 870 xo_errx(1, "-i cannot be used with -A"); 871 if (vflag != 0) 872 xo_errx(1, "-v cannot be used with -A"); 873 874 } else if (Mflag != 0) { 875 if (session_id == -1) 876 xo_errx(1, "-M requires -i"); 877 878 if (nickname != NULL) { 879 if (enable != ENABLE_UNSPECIFIED) 880 xo_errx(1, "-n and -e are mutually exclusive"); 881 if (portal != NULL) 882 xo_errx(1, "-n and -p are mutually exclusive"); 883 if (target != NULL) 884 xo_errx(1, "-n and -t are mutually exclusive"); 885 if (user != NULL) 886 xo_errx(1, "-n and -u are mutually exclusive"); 887 if (secret != NULL) 888 xo_errx(1, "-n and -s are mutually exclusive"); 889 } 890 891 if (aflag != 0) 892 xo_errx(1, "-a cannot be used with -M"); 893 if (discovery_host != NULL) 894 xo_errx(1, "-d cannot be used with -M"); 895 if (rflag != 0) 896 xo_errx(1, "-r cannot be used with -M"); 897 if (vflag != 0) 898 xo_errx(1, "-v cannot be used with -M"); 899 if (timeout != -1) 900 xo_errx(1, "-w cannot be used with -M"); 901 902 } else if (Rflag != 0) { 903 if (aflag != 0) { 904 if (portal != NULL) 905 xo_errx(1, "-a and -p are mutually exclusive"); 906 if (target != NULL) 907 xo_errx(1, "-a and -t are mutually exclusive"); 908 if (nickname != NULL) 909 xo_errx(1, "-a and -n are mutually exclusive"); 910 } else if (nickname != NULL) { 911 if (portal != NULL) 912 xo_errx(1, "-n and -p are mutually exclusive"); 913 if (target != NULL) 914 xo_errx(1, "-n and -t are mutually exclusive"); 915 } else if (target == NULL && portal == NULL) { 916 xo_errx(1, "must specify either -a, -n, -t, or -p"); 917 } 918 919 if (discovery_host != NULL) 920 xo_errx(1, "-d cannot be used with -R"); 921 if (enable != ENABLE_UNSPECIFIED) 922 xo_errx(1, "-e cannot be used with -R"); 923 if (session_id != -1) 924 xo_errx(1, "-i cannot be used with -R"); 925 if (rflag != 0) 926 xo_errx(1, "-r cannot be used with -R"); 927 if (user != NULL) 928 xo_errx(1, "-u cannot be used with -R"); 929 if (secret != NULL) 930 xo_errx(1, "-s cannot be used with -R"); 931 if (vflag != 0) 932 xo_errx(1, "-v cannot be used with -R"); 933 if (timeout != -1) 934 xo_errx(1, "-w cannot be used with -R"); 935 936 } else { 937 assert(Lflag != 0); 938 939 if (discovery_host != NULL) 940 xo_errx(1, "-d cannot be used with -L"); 941 if (session_id != -1) 942 xo_errx(1, "-i cannot be used with -L"); 943 if (nickname != NULL) 944 xo_errx(1, "-n cannot be used with -L"); 945 if (portal != NULL) 946 xo_errx(1, "-p cannot be used with -L"); 947 if (rflag != 0) 948 xo_errx(1, "-r cannot be used with -L"); 949 if (target != NULL) 950 xo_errx(1, "-t cannot be used with -L"); 951 if (user != NULL) 952 xo_errx(1, "-u cannot be used with -L"); 953 if (secret != NULL) 954 xo_errx(1, "-s cannot be used with -L"); 955 } 956 957 iscsi_fd = open(ISCSI_PATH, O_RDWR); 958 if (iscsi_fd < 0 && errno == ENOENT) { 959 saved_errno = errno; 960 retval = kldload("iscsi"); 961 if (retval != -1) 962 iscsi_fd = open(ISCSI_PATH, O_RDWR); 963 else 964 errno = saved_errno; 965 } 966 if (iscsi_fd < 0) 967 xo_err(1, "failed to open %s", ISCSI_PATH); 968 969 if (Aflag != 0 && aflag != 0) { 970 conf = conf_new_from_file(conf_path); 971 972 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) 973 failed += kernel_add(iscsi_fd, targ); 974 } else if (nickname != NULL) { 975 conf = conf_new_from_file(conf_path); 976 targ = target_find(conf, nickname); 977 if (targ == NULL) 978 xo_errx(1, "target %s not found in %s", 979 nickname, conf_path); 980 981 if (Aflag != 0) 982 failed += kernel_add(iscsi_fd, targ); 983 else if (Mflag != 0) 984 failed += kernel_modify(iscsi_fd, session_id, targ); 985 else if (Rflag != 0) 986 failed += kernel_remove(iscsi_fd, targ); 987 else 988 failed += kernel_list(iscsi_fd, targ, vflag); 989 } else if (Mflag != 0) { 990 kernel_modify_some(iscsi_fd, session_id, target, portal, 991 user, secret, enable); 992 } else { 993 if (Aflag != 0 && target != NULL) { 994 if (valid_iscsi_name(target) == false) 995 xo_errx(1, "invalid target name \"%s\"", target); 996 } 997 conf = conf_new(); 998 targ = target_new(conf); 999 targ->t_initiator_name = default_initiator_name(); 1000 targ->t_header_digest = DIGEST_NONE; 1001 targ->t_data_digest = DIGEST_NONE; 1002 targ->t_name = target; 1003 if (discovery_host != NULL) { 1004 targ->t_session_type = SESSION_TYPE_DISCOVERY; 1005 targ->t_address = discovery_host; 1006 } else { 1007 targ->t_session_type = SESSION_TYPE_NORMAL; 1008 targ->t_address = portal; 1009 } 1010 targ->t_enable = enable; 1011 if (rflag != 0) 1012 targ->t_protocol = PROTOCOL_ISER; 1013 targ->t_user = user; 1014 targ->t_secret = secret; 1015 1016 if (Aflag != 0) 1017 failed += kernel_add(iscsi_fd, targ); 1018 else if (Rflag != 0) 1019 failed += kernel_remove(iscsi_fd, targ); 1020 else 1021 failed += kernel_list(iscsi_fd, targ, vflag); 1022 } 1023 1024 if (timeout != -1) 1025 failed += kernel_wait(iscsi_fd, timeout); 1026 1027 error = close(iscsi_fd); 1028 if (error != 0) 1029 xo_err(1, "close"); 1030 1031 xo_close_container("iscsictl"); 1032 xo_finish(); 1033 1034 if (failed != 0) 1035 return (1); 1036 1037 return (0); 1038 } 1039