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 <err.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 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 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 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 102 static char * 103 default_initiator_name(void) 104 { 105 char *name; 106 size_t namelen; 107 int error; 108 109 namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN); 110 111 name = calloc(1, namelen + 1); 112 if (name == NULL) 113 err(1, "calloc"); 114 strcpy(name, DEFAULT_IQN); 115 error = gethostname(name + strlen(DEFAULT_IQN), 116 namelen - strlen(DEFAULT_IQN)); 117 if (error != 0) 118 err(1, "gethostname"); 119 120 return (name); 121 } 122 123 static bool 124 valid_hex(const char ch) 125 { 126 switch (ch) { 127 case '0': 128 case '1': 129 case '2': 130 case '3': 131 case '4': 132 case '5': 133 case '6': 134 case '7': 135 case '8': 136 case '9': 137 case 'a': 138 case 'A': 139 case 'b': 140 case 'B': 141 case 'c': 142 case 'C': 143 case 'd': 144 case 'D': 145 case 'e': 146 case 'E': 147 case 'f': 148 case 'F': 149 return (true); 150 default: 151 return (false); 152 } 153 } 154 155 bool 156 valid_iscsi_name(const char *name) 157 { 158 int i; 159 160 if (strlen(name) >= MAX_NAME_LEN) { 161 warnx("overlong name for \"%s\"; max length allowed " 162 "by iSCSI specification is %d characters", 163 name, MAX_NAME_LEN); 164 return (false); 165 } 166 167 /* 168 * In the cases below, we don't return an error, just in case the admin 169 * was right, and we're wrong. 170 */ 171 if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) { 172 for (i = strlen("iqn."); name[i] != '\0'; i++) { 173 /* 174 * XXX: We should verify UTF-8 normalisation, as defined 175 * by 3.2.6.2: iSCSI Name Encoding. 176 */ 177 if (isalnum(name[i])) 178 continue; 179 if (name[i] == '-' || name[i] == '.' || name[i] == ':') 180 continue; 181 warnx("invalid character \"%c\" in iSCSI name " 182 "\"%s\"; allowed characters are letters, digits, " 183 "'-', '.', and ':'", name[i], name); 184 break; 185 } 186 /* 187 * XXX: Check more stuff: valid date and a valid reversed domain. 188 */ 189 } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) { 190 if (strlen(name) != strlen("eui.") + 16) 191 warnx("invalid iSCSI name \"%s\"; the \"eui.\" " 192 "should be followed by exactly 16 hexadecimal " 193 "digits", name); 194 for (i = strlen("eui."); name[i] != '\0'; i++) { 195 if (!valid_hex(name[i])) { 196 warnx("invalid character \"%c\" in iSCSI " 197 "name \"%s\"; allowed characters are 1-9 " 198 "and A-F", name[i], name); 199 break; 200 } 201 } 202 } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) { 203 if (strlen(name) > strlen("naa.") + 32) 204 warnx("invalid iSCSI name \"%s\"; the \"naa.\" " 205 "should be followed by at most 32 hexadecimal " 206 "digits", name); 207 for (i = strlen("naa."); name[i] != '\0'; i++) { 208 if (!valid_hex(name[i])) { 209 warnx("invalid character \"%c\" in ISCSI " 210 "name \"%s\"; allowed characters are 1-9 " 211 "and A-F", name[i], name); 212 break; 213 } 214 } 215 } else { 216 warnx("invalid iSCSI name \"%s\"; should start with " 217 "either \".iqn\", \"eui.\", or \"naa.\"", 218 name); 219 } 220 return (true); 221 } 222 223 void 224 conf_verify(struct conf *conf) 225 { 226 struct target *targ; 227 228 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { 229 assert(targ->t_nickname != NULL); 230 if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED) 231 targ->t_session_type = SESSION_TYPE_NORMAL; 232 if (targ->t_session_type == SESSION_TYPE_NORMAL && 233 targ->t_name == NULL) 234 errx(1, "missing TargetName for target \"%s\"", 235 targ->t_nickname); 236 if (targ->t_session_type == SESSION_TYPE_DISCOVERY && 237 targ->t_name != NULL) 238 errx(1, "cannot specify TargetName for discovery " 239 "sessions for target \"%s\"", targ->t_nickname); 240 if (targ->t_name != NULL) { 241 if (valid_iscsi_name(targ->t_name) == false) 242 errx(1, "invalid target name \"%s\"", 243 targ->t_name); 244 } 245 if (targ->t_protocol == PROTOCOL_UNSPECIFIED) 246 targ->t_protocol = PROTOCOL_ISCSI; 247 if (targ->t_address == NULL) 248 errx(1, "missing TargetAddress for target \"%s\"", 249 targ->t_nickname); 250 if (targ->t_initiator_name == NULL) 251 targ->t_initiator_name = default_initiator_name(); 252 if (valid_iscsi_name(targ->t_initiator_name) == false) 253 errx(1, "invalid initiator name \"%s\"", 254 targ->t_initiator_name); 255 if (targ->t_header_digest == DIGEST_UNSPECIFIED) 256 targ->t_header_digest = DIGEST_NONE; 257 if (targ->t_data_digest == DIGEST_UNSPECIFIED) 258 targ->t_data_digest = DIGEST_NONE; 259 if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) { 260 if (targ->t_user != NULL || targ->t_secret != NULL || 261 targ->t_mutual_user != NULL || 262 targ->t_mutual_secret != NULL) 263 targ->t_auth_method = 264 AUTH_METHOD_CHAP; 265 else 266 targ->t_auth_method = 267 AUTH_METHOD_NONE; 268 } 269 if (targ->t_auth_method == AUTH_METHOD_CHAP) { 270 if (targ->t_user == NULL) { 271 errx(1, "missing chapIName for target \"%s\"", 272 targ->t_nickname); 273 } 274 if (targ->t_secret == NULL) 275 errx(1, "missing chapSecret for target \"%s\"", 276 targ->t_nickname); 277 if (targ->t_mutual_user != NULL || 278 targ->t_mutual_secret != NULL) { 279 if (targ->t_mutual_user == NULL) 280 errx(1, "missing tgtChapName for " 281 "target \"%s\"", targ->t_nickname); 282 if (targ->t_mutual_secret == NULL) 283 errx(1, "missing tgtChapSecret for " 284 "target \"%s\"", targ->t_nickname); 285 } 286 } 287 } 288 } 289 290 static void 291 conf_from_target(struct iscsi_session_conf *conf, 292 const struct target *targ) 293 { 294 memset(conf, 0, sizeof(*conf)); 295 296 /* 297 * XXX: Check bounds and return error instead of silently truncating. 298 */ 299 if (targ->t_initiator_name != NULL) 300 strlcpy(conf->isc_initiator, targ->t_initiator_name, 301 sizeof(conf->isc_initiator)); 302 if (targ->t_initiator_address != NULL) 303 strlcpy(conf->isc_initiator_addr, targ->t_initiator_address, 304 sizeof(conf->isc_initiator_addr)); 305 if (targ->t_initiator_alias != NULL) 306 strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias, 307 sizeof(conf->isc_initiator_alias)); 308 if (targ->t_name != NULL) 309 strlcpy(conf->isc_target, targ->t_name, 310 sizeof(conf->isc_target)); 311 if (targ->t_address != NULL) 312 strlcpy(conf->isc_target_addr, targ->t_address, 313 sizeof(conf->isc_target_addr)); 314 if (targ->t_user != NULL) 315 strlcpy(conf->isc_user, targ->t_user, 316 sizeof(conf->isc_user)); 317 if (targ->t_secret != NULL) 318 strlcpy(conf->isc_secret, targ->t_secret, 319 sizeof(conf->isc_secret)); 320 if (targ->t_mutual_user != NULL) 321 strlcpy(conf->isc_mutual_user, targ->t_mutual_user, 322 sizeof(conf->isc_mutual_user)); 323 if (targ->t_mutual_secret != NULL) 324 strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret, 325 sizeof(conf->isc_mutual_secret)); 326 if (targ->t_session_type == SESSION_TYPE_DISCOVERY) 327 conf->isc_discovery = 1; 328 if (targ->t_protocol == PROTOCOL_ISER) 329 conf->isc_iser = 1; 330 if (targ->t_offload != NULL) 331 strlcpy(conf->isc_offload, targ->t_offload, 332 sizeof(conf->isc_offload)); 333 if (targ->t_header_digest == DIGEST_CRC32C) 334 conf->isc_header_digest = ISCSI_DIGEST_CRC32C; 335 else 336 conf->isc_header_digest = ISCSI_DIGEST_NONE; 337 if (targ->t_data_digest == DIGEST_CRC32C) 338 conf->isc_data_digest = ISCSI_DIGEST_CRC32C; 339 else 340 conf->isc_data_digest = ISCSI_DIGEST_NONE; 341 } 342 343 static int 344 kernel_add(int iscsi_fd, const struct target *targ) 345 { 346 struct iscsi_session_add isa; 347 int error; 348 349 memset(&isa, 0, sizeof(isa)); 350 conf_from_target(&isa.isa_conf, targ); 351 error = ioctl(iscsi_fd, ISCSISADD, &isa); 352 if (error != 0) 353 warn("ISCSISADD"); 354 return (error); 355 } 356 357 static int 358 kernel_modify(int iscsi_fd, unsigned int session_id, const struct target *targ) 359 { 360 struct iscsi_session_modify ism; 361 int error; 362 363 memset(&ism, 0, sizeof(ism)); 364 ism.ism_session_id = session_id; 365 conf_from_target(&ism.ism_conf, targ); 366 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); 367 if (error != 0) 368 warn("ISCSISMODIFY"); 369 return (error); 370 } 371 372 static void 373 kernel_modify_some(int iscsi_fd, unsigned int session_id, const char *target, 374 const char *target_addr, const char *user, const char *secret) 375 { 376 struct iscsi_session_state *states = NULL; 377 struct iscsi_session_state *state; 378 struct iscsi_session_conf *conf; 379 struct iscsi_session_list isl; 380 struct iscsi_session_modify ism; 381 unsigned int i, nentries = 1; 382 int error; 383 384 for (;;) { 385 states = realloc(states, 386 nentries * sizeof(struct iscsi_session_state)); 387 if (states == NULL) 388 err(1, "realloc"); 389 390 memset(&isl, 0, sizeof(isl)); 391 isl.isl_nentries = nentries; 392 isl.isl_pstates = states; 393 394 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 395 if (error != 0 && errno == EMSGSIZE) { 396 nentries *= 4; 397 continue; 398 } 399 break; 400 } 401 if (error != 0) 402 errx(1, "ISCSISLIST"); 403 404 for (i = 0; i < isl.isl_nentries; i++) { 405 state = &states[i]; 406 407 if (state->iss_id == session_id) 408 break; 409 } 410 if (i == isl.isl_nentries) 411 errx(1, "session-id %u not found", session_id); 412 413 conf = &state->iss_conf; 414 415 if (target != NULL) 416 strlcpy(conf->isc_target, target, sizeof(conf->isc_target)); 417 if (target_addr != NULL) 418 strlcpy(conf->isc_target_addr, target_addr, 419 sizeof(conf->isc_target_addr)); 420 if (user != NULL) 421 strlcpy(conf->isc_user, user, sizeof(conf->isc_user)); 422 if (secret != NULL) 423 strlcpy(conf->isc_secret, secret, sizeof(conf->isc_secret)); 424 425 memset(&ism, 0, sizeof(ism)); 426 ism.ism_session_id = session_id; 427 memcpy(&ism.ism_conf, conf, sizeof(ism.ism_conf)); 428 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); 429 if (error != 0) 430 warn("ISCSISMODIFY"); 431 } 432 433 static int 434 kernel_remove(int iscsi_fd, const struct target *targ) 435 { 436 struct iscsi_session_remove isr; 437 int error; 438 439 memset(&isr, 0, sizeof(isr)); 440 conf_from_target(&isr.isr_conf, targ); 441 error = ioctl(iscsi_fd, ISCSISREMOVE, &isr); 442 if (error != 0) 443 warn("ISCSISREMOVE"); 444 return (error); 445 } 446 447 /* 448 * XXX: Add filtering. 449 */ 450 static int 451 kernel_list(int iscsi_fd, const struct target *targ __unused, 452 int verbose) 453 { 454 struct iscsi_session_state *states = NULL; 455 const struct iscsi_session_state *state; 456 const struct iscsi_session_conf *conf; 457 struct iscsi_session_list isl; 458 unsigned int i, nentries = 1; 459 int error; 460 461 for (;;) { 462 states = realloc(states, 463 nentries * sizeof(struct iscsi_session_state)); 464 if (states == NULL) 465 err(1, "realloc"); 466 467 memset(&isl, 0, sizeof(isl)); 468 isl.isl_nentries = nentries; 469 isl.isl_pstates = states; 470 471 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 472 if (error != 0 && errno == EMSGSIZE) { 473 nentries *= 4; 474 continue; 475 } 476 break; 477 } 478 if (error != 0) { 479 warn("ISCSISLIST"); 480 return (error); 481 } 482 483 if (verbose != 0) { 484 for (i = 0; i < isl.isl_nentries; i++) { 485 state = &states[i]; 486 conf = &state->iss_conf; 487 488 printf("Session ID: %u\n", state->iss_id); 489 printf("Initiator name: %s\n", conf->isc_initiator); 490 printf("Initiator portal: %s\n", 491 conf->isc_initiator_addr); 492 printf("Initiator alias: %s\n", 493 conf->isc_initiator_alias); 494 printf("Target name: %s\n", conf->isc_target); 495 printf("Target portal: %s\n", 496 conf->isc_target_addr); 497 printf("Target alias: %s\n", 498 state->iss_target_alias); 499 printf("User: %s\n", conf->isc_user); 500 printf("Secret: %s\n", conf->isc_secret); 501 printf("Mutual user: %s\n", 502 conf->isc_mutual_user); 503 printf("Mutual secret: %s\n", 504 conf->isc_mutual_secret); 505 printf("Session type: %s\n", 506 conf->isc_discovery ? "Discovery" : "Normal"); 507 printf("Session state: %s\n", 508 state->iss_connected ? 509 "Connected" : "Disconnected"); 510 printf("Failure reason: %s\n", state->iss_reason); 511 printf("Header digest: %s\n", 512 state->iss_header_digest == ISCSI_DIGEST_CRC32C ? 513 "CRC32C" : "None"); 514 printf("Data digest: %s\n", 515 state->iss_data_digest == ISCSI_DIGEST_CRC32C ? 516 "CRC32C" : "None"); 517 printf("DataSegmentLen: %d\n", 518 state->iss_max_data_segment_length); 519 printf("ImmediateData: %s\n", 520 state->iss_immediate_data ? "Yes" : "No"); 521 printf("iSER (RDMA): %s\n", 522 conf->isc_iser ? "Yes" : "No"); 523 printf("Offload driver: %s\n", state->iss_offload); 524 printf("Device nodes: "); 525 print_periphs(state->iss_id); 526 printf("\n\n"); 527 } 528 } else { 529 printf("%-36s %-16s %s\n", 530 "Target name", "Target portal", "State"); 531 for (i = 0; i < isl.isl_nentries; i++) { 532 state = &states[i]; 533 conf = &state->iss_conf; 534 535 printf("%-36s %-16s ", 536 conf->isc_target, conf->isc_target_addr); 537 538 if (state->iss_reason[0] != '\0') { 539 printf("%s\n", state->iss_reason); 540 } else { 541 if (conf->isc_discovery) { 542 printf("Discovery\n"); 543 } else if (state->iss_connected) { 544 printf("Connected: "); 545 print_periphs(state->iss_id); 546 printf("\n"); 547 } else { 548 printf("Disconnected\n"); 549 } 550 } 551 } 552 } 553 554 return (0); 555 } 556 557 static void 558 usage(void) 559 { 560 561 fprintf(stderr, "usage: iscsictl -A -p portal -t target " 562 "[-u user -s secret]\n"); 563 fprintf(stderr, " iscsictl -A -d discovery-host " 564 "[-u user -s secret]\n"); 565 fprintf(stderr, " iscsictl -A -a [-c path]\n"); 566 fprintf(stderr, " iscsictl -A -n nickname [-c path]\n"); 567 fprintf(stderr, " iscsictl -M -i session-id [-p portal] " 568 "[-t target] [-u user] [-s secret]\n"); 569 fprintf(stderr, " iscsictl -M -i session-id -n nickname " 570 "[-c path]\n"); 571 fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n"); 572 fprintf(stderr, " iscsictl -R -a\n"); 573 fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); 574 fprintf(stderr, " iscsictl -L [-v]\n"); 575 exit(1); 576 } 577 578 char * 579 checked_strdup(const char *s) 580 { 581 char *c; 582 583 c = strdup(s); 584 if (c == NULL) 585 err(1, "strdup"); 586 return (c); 587 } 588 589 int 590 main(int argc, char **argv) 591 { 592 int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0; 593 const char *conf_path = DEFAULT_CONFIG_PATH; 594 char *nickname = NULL, *discovery_host = NULL, *portal = NULL, 595 *target = NULL, *user = NULL, *secret = NULL; 596 long long session_id = -1; 597 char *end; 598 int ch, error, iscsi_fd, retval, saved_errno; 599 int failed = 0; 600 struct conf *conf; 601 struct target *targ; 602 603 while ((ch = getopt(argc, argv, "AMRLac:d:i:n:p:t:u:s:v")) != -1) { 604 switch (ch) { 605 case 'A': 606 Aflag = 1; 607 break; 608 case 'M': 609 Mflag = 1; 610 break; 611 case 'R': 612 Rflag = 1; 613 break; 614 case 'L': 615 Lflag = 1; 616 break; 617 case 'a': 618 aflag = 1; 619 break; 620 case 'c': 621 conf_path = optarg; 622 break; 623 case 'd': 624 discovery_host = optarg; 625 break; 626 case 'i': 627 session_id = strtol(optarg, &end, 10); 628 if ((size_t)(end - optarg) != strlen(optarg)) 629 errx(1, "trailing characters after session-id"); 630 if (session_id < 0) 631 errx(1, "session-id cannot be negative"); 632 if (session_id > UINT_MAX) 633 errx(1, "session-id cannot be greater than %u", 634 UINT_MAX); 635 break; 636 case 'n': 637 nickname = optarg; 638 break; 639 case 'p': 640 portal = optarg; 641 break; 642 case 't': 643 target = optarg; 644 break; 645 case 'u': 646 user = optarg; 647 break; 648 case 's': 649 secret = optarg; 650 break; 651 case 'v': 652 vflag = 1; 653 break; 654 case '?': 655 default: 656 usage(); 657 } 658 } 659 argc -= optind; 660 if (argc != 0) 661 usage(); 662 663 if (Aflag + Mflag + Rflag + Lflag == 0) 664 Lflag = 1; 665 if (Aflag + Mflag + Rflag + Lflag > 1) 666 errx(1, "at most one of -A, -M, -R, or -L may be specified"); 667 668 /* 669 * Note that we ignore unneccessary/inapplicable "-c" flag; so that 670 * people can do something like "alias ISCSICTL="iscsictl -c path" 671 * in shell scripts. 672 */ 673 if (Aflag != 0) { 674 if (aflag != 0) { 675 if (portal != NULL) 676 errx(1, "-a and -p and mutually exclusive"); 677 if (target != NULL) 678 errx(1, "-a and -t and mutually exclusive"); 679 if (user != NULL) 680 errx(1, "-a and -u and mutually exclusive"); 681 if (secret != NULL) 682 errx(1, "-a and -s and mutually exclusive"); 683 if (nickname != NULL) 684 errx(1, "-a and -n and mutually exclusive"); 685 if (discovery_host != NULL) 686 errx(1, "-a and -d and mutually exclusive"); 687 } else if (nickname != NULL) { 688 if (portal != NULL) 689 errx(1, "-n and -p and mutually exclusive"); 690 if (target != NULL) 691 errx(1, "-n and -t and mutually exclusive"); 692 if (user != NULL) 693 errx(1, "-n and -u and mutually exclusive"); 694 if (secret != NULL) 695 errx(1, "-n and -s and mutually exclusive"); 696 if (discovery_host != NULL) 697 errx(1, "-n and -d and mutually exclusive"); 698 } else if (discovery_host != NULL) { 699 if (portal != NULL) 700 errx(1, "-d and -p and mutually exclusive"); 701 if (target != NULL) 702 errx(1, "-d and -t and mutually exclusive"); 703 } else { 704 if (target == NULL && portal == NULL) 705 errx(1, "must specify -a, -n or -t/-p"); 706 707 if (target != NULL && portal == NULL) 708 errx(1, "-t must always be used with -p"); 709 if (portal != NULL && target == NULL) 710 errx(1, "-p must always be used with -t"); 711 } 712 713 if (user != NULL && secret == NULL) 714 errx(1, "-u must always be used with -s"); 715 if (secret != NULL && user == NULL) 716 errx(1, "-s must always be used with -u"); 717 718 if (session_id != -1) 719 errx(1, "-i cannot be used with -A"); 720 if (vflag != 0) 721 errx(1, "-v cannot be used with -A"); 722 723 } else if (Mflag != 0) { 724 if (session_id == -1) 725 errx(1, "-M requires -i"); 726 727 if (discovery_host != NULL) 728 errx(1, "-M and -d are mutually exclusive"); 729 if (aflag != 0) 730 errx(1, "-M and -a are mutually exclusive"); 731 if (nickname != NULL) { 732 if (portal != NULL) 733 errx(1, "-n and -p and mutually exclusive"); 734 if (target != NULL) 735 errx(1, "-n and -t and mutually exclusive"); 736 if (user != NULL) 737 errx(1, "-n and -u and mutually exclusive"); 738 if (secret != NULL) 739 errx(1, "-n and -s and mutually exclusive"); 740 } 741 742 if (vflag != 0) 743 errx(1, "-v cannot be used with -M"); 744 745 } else if (Rflag != 0) { 746 if (user != NULL) 747 errx(1, "-R and -u are mutually exclusive"); 748 if (secret != NULL) 749 errx(1, "-R and -s are mutually exclusive"); 750 if (discovery_host != NULL) 751 errx(1, "-R and -d are mutually exclusive"); 752 753 if (aflag != 0) { 754 if (portal != NULL) 755 errx(1, "-a and -p and mutually exclusive"); 756 if (target != NULL) 757 errx(1, "-a and -t and mutually exclusive"); 758 if (nickname != NULL) 759 errx(1, "-a and -n and mutually exclusive"); 760 } else if (nickname != NULL) { 761 if (portal != NULL) 762 errx(1, "-n and -p and mutually exclusive"); 763 if (target != NULL) 764 errx(1, "-n and -t and mutually exclusive"); 765 } else if (target == NULL && portal == NULL) { 766 errx(1, "must specify either -a, -n, -t, or -p"); 767 } 768 769 if (session_id != -1) 770 errx(1, "-i cannot be used with -R"); 771 if (vflag != 0) 772 errx(1, "-v cannot be used with -R"); 773 774 } else { 775 assert(Lflag != 0); 776 777 if (portal != NULL) 778 errx(1, "-L and -p and mutually exclusive"); 779 if (target != NULL) 780 errx(1, "-L and -t and mutually exclusive"); 781 if (user != NULL) 782 errx(1, "-L and -u and mutually exclusive"); 783 if (secret != NULL) 784 errx(1, "-L and -s and mutually exclusive"); 785 if (nickname != NULL) 786 errx(1, "-L and -n and mutually exclusive"); 787 if (discovery_host != NULL) 788 errx(1, "-L and -d and mutually exclusive"); 789 790 if (session_id != -1) 791 errx(1, "-i cannot be used with -L"); 792 } 793 794 iscsi_fd = open(ISCSI_PATH, O_RDWR); 795 if (iscsi_fd < 0 && errno == ENOENT) { 796 saved_errno = errno; 797 retval = kldload("iscsi"); 798 if (retval != -1) 799 iscsi_fd = open(ISCSI_PATH, O_RDWR); 800 else 801 errno = saved_errno; 802 } 803 if (iscsi_fd < 0) 804 err(1, "failed to open %s", ISCSI_PATH); 805 806 if (Aflag != 0 && aflag != 0) { 807 conf = conf_new_from_file(conf_path); 808 809 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) 810 failed += kernel_add(iscsi_fd, targ); 811 } else if (nickname != NULL) { 812 conf = conf_new_from_file(conf_path); 813 targ = target_find(conf, nickname); 814 if (targ == NULL) 815 errx(1, "target %s not found in %s", 816 nickname, conf_path); 817 818 if (Aflag != 0) 819 failed += kernel_add(iscsi_fd, targ); 820 else if (Mflag != 0) 821 failed += kernel_modify(iscsi_fd, session_id, targ); 822 else if (Rflag != 0) 823 failed += kernel_remove(iscsi_fd, targ); 824 else 825 failed += kernel_list(iscsi_fd, targ, vflag); 826 } else if (Mflag != 0) { 827 kernel_modify_some(iscsi_fd, session_id, target, portal, 828 user, secret); 829 } else { 830 if (Aflag != 0 && target != NULL) { 831 if (valid_iscsi_name(target) == false) 832 errx(1, "invalid target name \"%s\"", target); 833 } 834 conf = conf_new(); 835 targ = target_new(conf); 836 targ->t_initiator_name = default_initiator_name(); 837 targ->t_header_digest = DIGEST_NONE; 838 targ->t_data_digest = DIGEST_NONE; 839 targ->t_name = target; 840 if (discovery_host != NULL) { 841 targ->t_session_type = SESSION_TYPE_DISCOVERY; 842 targ->t_address = discovery_host; 843 } else { 844 targ->t_session_type = SESSION_TYPE_NORMAL; 845 targ->t_address = portal; 846 } 847 targ->t_user = user; 848 targ->t_secret = secret; 849 850 if (Aflag != 0) 851 failed += kernel_add(iscsi_fd, targ); 852 else if (Rflag != 0) 853 failed += kernel_remove(iscsi_fd, targ); 854 else 855 failed += kernel_list(iscsi_fd, targ, vflag); 856 } 857 858 error = close(iscsi_fd); 859 if (error != 0) 860 err(1, "close"); 861 862 if (failed > 0) 863 return (1); 864 return (0); 865 } 866