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