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