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