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