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