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_header_digest == DIGEST_CRC32C) 331 conf->isc_header_digest = ISCSI_DIGEST_CRC32C; 332 else 333 conf->isc_header_digest = ISCSI_DIGEST_NONE; 334 if (targ->t_data_digest == DIGEST_CRC32C) 335 conf->isc_data_digest = ISCSI_DIGEST_CRC32C; 336 else 337 conf->isc_data_digest = ISCSI_DIGEST_NONE; 338 } 339 340 static int 341 kernel_add(int iscsi_fd, const struct target *targ) 342 { 343 struct iscsi_session_add isa; 344 int error; 345 346 memset(&isa, 0, sizeof(isa)); 347 conf_from_target(&isa.isa_conf, targ); 348 error = ioctl(iscsi_fd, ISCSISADD, &isa); 349 if (error != 0) 350 warn("ISCSISADD"); 351 return (error); 352 } 353 354 static int 355 kernel_modify(int iscsi_fd, unsigned int session_id, const struct target *targ) 356 { 357 struct iscsi_session_modify ism; 358 int error; 359 360 memset(&ism, 0, sizeof(ism)); 361 ism.ism_session_id = session_id; 362 conf_from_target(&ism.ism_conf, targ); 363 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); 364 if (error != 0) 365 warn("ISCSISMODIFY"); 366 return (error); 367 } 368 369 static void 370 kernel_modify_some(int iscsi_fd, unsigned int session_id, const char *target, 371 const char *target_addr, const char *user, const char *secret) 372 { 373 struct iscsi_session_state *states = NULL; 374 struct iscsi_session_state *state; 375 struct iscsi_session_conf *conf; 376 struct iscsi_session_list isl; 377 struct iscsi_session_modify ism; 378 unsigned int i, nentries = 1; 379 int error; 380 381 for (;;) { 382 states = realloc(states, 383 nentries * sizeof(struct iscsi_session_state)); 384 if (states == NULL) 385 err(1, "realloc"); 386 387 memset(&isl, 0, sizeof(isl)); 388 isl.isl_nentries = nentries; 389 isl.isl_pstates = states; 390 391 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 392 if (error != 0 && errno == EMSGSIZE) { 393 nentries *= 4; 394 continue; 395 } 396 break; 397 } 398 if (error != 0) 399 errx(1, "ISCSISLIST"); 400 401 for (i = 0; i < isl.isl_nentries; i++) { 402 state = &states[i]; 403 404 if (state->iss_id == session_id) 405 break; 406 } 407 if (i == isl.isl_nentries) 408 errx(1, "session-id %u not found", session_id); 409 410 conf = &state->iss_conf; 411 412 if (target != NULL) 413 strlcpy(conf->isc_target, target, sizeof(conf->isc_target)); 414 if (target_addr != NULL) 415 strlcpy(conf->isc_target_addr, target_addr, 416 sizeof(conf->isc_target_addr)); 417 if (user != NULL) 418 strlcpy(conf->isc_user, user, sizeof(conf->isc_user)); 419 if (secret != NULL) 420 strlcpy(conf->isc_secret, secret, sizeof(conf->isc_secret)); 421 422 memset(&ism, 0, sizeof(ism)); 423 ism.ism_session_id = session_id; 424 memcpy(&ism.ism_conf, conf, sizeof(ism.ism_conf)); 425 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); 426 if (error != 0) 427 warn("ISCSISMODIFY"); 428 } 429 430 static int 431 kernel_remove(int iscsi_fd, const struct target *targ) 432 { 433 struct iscsi_session_remove isr; 434 int error; 435 436 memset(&isr, 0, sizeof(isr)); 437 conf_from_target(&isr.isr_conf, targ); 438 error = ioctl(iscsi_fd, ISCSISREMOVE, &isr); 439 if (error != 0) 440 warn("ISCSISREMOVE"); 441 return (error); 442 } 443 444 /* 445 * XXX: Add filtering. 446 */ 447 static int 448 kernel_list(int iscsi_fd, const struct target *targ __unused, 449 int verbose) 450 { 451 struct iscsi_session_state *states = NULL; 452 const struct iscsi_session_state *state; 453 const struct iscsi_session_conf *conf; 454 struct iscsi_session_list isl; 455 unsigned int i, nentries = 1; 456 int error; 457 458 for (;;) { 459 states = realloc(states, 460 nentries * sizeof(struct iscsi_session_state)); 461 if (states == NULL) 462 err(1, "realloc"); 463 464 memset(&isl, 0, sizeof(isl)); 465 isl.isl_nentries = nentries; 466 isl.isl_pstates = states; 467 468 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 469 if (error != 0 && errno == EMSGSIZE) { 470 nentries *= 4; 471 continue; 472 } 473 break; 474 } 475 if (error != 0) { 476 warn("ISCSISLIST"); 477 return (error); 478 } 479 480 if (verbose != 0) { 481 for (i = 0; i < isl.isl_nentries; i++) { 482 state = &states[i]; 483 conf = &state->iss_conf; 484 485 printf("Session ID: %u\n", state->iss_id); 486 printf("Initiator name: %s\n", conf->isc_initiator); 487 printf("Initiator portal: %s\n", 488 conf->isc_initiator_addr); 489 printf("Initiator alias: %s\n", 490 conf->isc_initiator_alias); 491 printf("Target name: %s\n", conf->isc_target); 492 printf("Target portal: %s\n", 493 conf->isc_target_addr); 494 printf("Target alias: %s\n", 495 state->iss_target_alias); 496 printf("User: %s\n", conf->isc_user); 497 printf("Secret: %s\n", conf->isc_secret); 498 printf("Mutual user: %s\n", 499 conf->isc_mutual_user); 500 printf("Mutual secret: %s\n", 501 conf->isc_mutual_secret); 502 printf("Session type: %s\n", 503 conf->isc_discovery ? "Discovery" : "Normal"); 504 printf("Session state: %s\n", 505 state->iss_connected ? 506 "Connected" : "Disconnected"); 507 printf("Failure reason: %s\n", state->iss_reason); 508 printf("Header digest: %s\n", 509 state->iss_header_digest == ISCSI_DIGEST_CRC32C ? 510 "CRC32C" : "None"); 511 printf("Data digest: %s\n", 512 state->iss_data_digest == ISCSI_DIGEST_CRC32C ? 513 "CRC32C" : "None"); 514 printf("DataSegmentLen: %d\n", 515 state->iss_max_data_segment_length); 516 printf("ImmediateData: %s\n", 517 state->iss_immediate_data ? "Yes" : "No"); 518 printf("iSER (RDMA): %s\n", 519 conf->isc_iser ? "Yes" : "No"); 520 printf("Device nodes: "); 521 print_periphs(state->iss_id); 522 printf("\n\n"); 523 } 524 } else { 525 printf("%-36s %-16s %s\n", 526 "Target name", "Target portal", "State"); 527 for (i = 0; i < isl.isl_nentries; i++) { 528 state = &states[i]; 529 conf = &state->iss_conf; 530 531 printf("%-36s %-16s ", 532 conf->isc_target, conf->isc_target_addr); 533 534 if (state->iss_reason[0] != '\0') { 535 printf("%s\n", state->iss_reason); 536 } else { 537 if (conf->isc_discovery) { 538 printf("Discovery\n"); 539 } else if (state->iss_connected) { 540 printf("Connected: "); 541 print_periphs(state->iss_id); 542 printf("\n"); 543 } else { 544 printf("Disconnected\n"); 545 } 546 } 547 } 548 } 549 550 return (0); 551 } 552 553 static void 554 usage(void) 555 { 556 557 fprintf(stderr, "usage: iscsictl -A -p portal -t target " 558 "[-u user -s secret]\n"); 559 fprintf(stderr, " iscsictl -A -d discovery-host " 560 "[-u user -s secret]\n"); 561 fprintf(stderr, " iscsictl -A -a [-c path]\n"); 562 fprintf(stderr, " iscsictl -A -n nickname [-c path]\n"); 563 fprintf(stderr, " iscsictl -M -i session-id [-p portal] " 564 "[-t target] [-u user] [-s secret]\n"); 565 fprintf(stderr, " iscsictl -M -i session-id -n nickname " 566 "[-c path]\n"); 567 fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n"); 568 fprintf(stderr, " iscsictl -R -a\n"); 569 fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); 570 fprintf(stderr, " iscsictl -L [-v]\n"); 571 exit(1); 572 } 573 574 char * 575 checked_strdup(const char *s) 576 { 577 char *c; 578 579 c = strdup(s); 580 if (c == NULL) 581 err(1, "strdup"); 582 return (c); 583 } 584 585 int 586 main(int argc, char **argv) 587 { 588 int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0; 589 const char *conf_path = DEFAULT_CONFIG_PATH; 590 char *nickname = NULL, *discovery_host = NULL, *portal = NULL, 591 *target = NULL, *user = NULL, *secret = NULL; 592 long long session_id = -1; 593 char *end; 594 int ch, error, iscsi_fd, retval, saved_errno; 595 int failed = 0; 596 struct conf *conf; 597 struct target *targ; 598 599 while ((ch = getopt(argc, argv, "AMRLac:d:i:n:p:t:u:s:v")) != -1) { 600 switch (ch) { 601 case 'A': 602 Aflag = 1; 603 break; 604 case 'M': 605 Mflag = 1; 606 break; 607 case 'R': 608 Rflag = 1; 609 break; 610 case 'L': 611 Lflag = 1; 612 break; 613 case 'a': 614 aflag = 1; 615 break; 616 case 'c': 617 conf_path = optarg; 618 break; 619 case 'd': 620 discovery_host = optarg; 621 break; 622 case 'i': 623 session_id = strtol(optarg, &end, 10); 624 if ((size_t)(end - optarg) != strlen(optarg)) 625 errx(1, "trailing characters after session-id"); 626 if (session_id < 0) 627 errx(1, "session-id cannot be negative"); 628 if (session_id > UINT_MAX) 629 errx(1, "session-id cannot be greater than %u", 630 UINT_MAX); 631 break; 632 case 'n': 633 nickname = optarg; 634 break; 635 case 'p': 636 portal = optarg; 637 break; 638 case 't': 639 target = optarg; 640 break; 641 case 'u': 642 user = optarg; 643 break; 644 case 's': 645 secret = optarg; 646 break; 647 case 'v': 648 vflag = 1; 649 break; 650 case '?': 651 default: 652 usage(); 653 } 654 } 655 argc -= optind; 656 if (argc != 0) 657 usage(); 658 659 if (Aflag + Mflag + Rflag + Lflag == 0) 660 Lflag = 1; 661 if (Aflag + Mflag + Rflag + Lflag > 1) 662 errx(1, "at most one of -A, -M, -R, or -L may be specified"); 663 664 /* 665 * Note that we ignore unneccessary/inapplicable "-c" flag; so that 666 * people can do something like "alias ISCSICTL="iscsictl -c path" 667 * in shell scripts. 668 */ 669 if (Aflag != 0) { 670 if (aflag != 0) { 671 if (portal != NULL) 672 errx(1, "-a and -p and mutually exclusive"); 673 if (target != NULL) 674 errx(1, "-a and -t and mutually exclusive"); 675 if (user != NULL) 676 errx(1, "-a and -u and mutually exclusive"); 677 if (secret != NULL) 678 errx(1, "-a and -s and mutually exclusive"); 679 if (nickname != NULL) 680 errx(1, "-a and -n and mutually exclusive"); 681 if (discovery_host != NULL) 682 errx(1, "-a and -d and mutually exclusive"); 683 } else if (nickname != NULL) { 684 if (portal != NULL) 685 errx(1, "-n and -p and mutually exclusive"); 686 if (target != NULL) 687 errx(1, "-n and -t and mutually exclusive"); 688 if (user != NULL) 689 errx(1, "-n and -u and mutually exclusive"); 690 if (secret != NULL) 691 errx(1, "-n and -s and mutually exclusive"); 692 if (discovery_host != NULL) 693 errx(1, "-n and -d and mutually exclusive"); 694 } else if (discovery_host != NULL) { 695 if (portal != NULL) 696 errx(1, "-d and -p and mutually exclusive"); 697 if (target != NULL) 698 errx(1, "-d and -t and mutually exclusive"); 699 } else { 700 if (target == NULL && portal == NULL) 701 errx(1, "must specify -a, -n or -t/-p"); 702 703 if (target != NULL && portal == NULL) 704 errx(1, "-t must always be used with -p"); 705 if (portal != NULL && target == NULL) 706 errx(1, "-p must always be used with -t"); 707 } 708 709 if (user != NULL && secret == NULL) 710 errx(1, "-u must always be used with -s"); 711 if (secret != NULL && user == NULL) 712 errx(1, "-s must always be used with -u"); 713 714 if (session_id != -1) 715 errx(1, "-i cannot be used with -A"); 716 if (vflag != 0) 717 errx(1, "-v cannot be used with -A"); 718 719 } else if (Mflag != 0) { 720 if (session_id == -1) 721 errx(1, "-M requires -i"); 722 723 if (discovery_host != NULL) 724 errx(1, "-M and -d are mutually exclusive"); 725 if (aflag != 0) 726 errx(1, "-M and -a are mutually exclusive"); 727 if (nickname != NULL) { 728 if (portal != NULL) 729 errx(1, "-n and -p and mutually exclusive"); 730 if (target != NULL) 731 errx(1, "-n and -t and mutually exclusive"); 732 if (user != NULL) 733 errx(1, "-n and -u and mutually exclusive"); 734 if (secret != NULL) 735 errx(1, "-n and -s and mutually exclusive"); 736 } 737 738 if (vflag != 0) 739 errx(1, "-v cannot be used with -M"); 740 741 } else if (Rflag != 0) { 742 if (user != NULL) 743 errx(1, "-R and -u are mutually exclusive"); 744 if (secret != NULL) 745 errx(1, "-R and -s are mutually exclusive"); 746 if (discovery_host != NULL) 747 errx(1, "-R and -d are mutually exclusive"); 748 749 if (aflag != 0) { 750 if (portal != NULL) 751 errx(1, "-a and -p and mutually exclusive"); 752 if (target != NULL) 753 errx(1, "-a and -t and mutually exclusive"); 754 if (nickname != NULL) 755 errx(1, "-a and -n and mutually exclusive"); 756 } else if (nickname != NULL) { 757 if (portal != NULL) 758 errx(1, "-n and -p and mutually exclusive"); 759 if (target != NULL) 760 errx(1, "-n and -t and mutually exclusive"); 761 } else if (target == NULL && portal == NULL) { 762 errx(1, "must specify either -a, -n, -t, or -p"); 763 } 764 765 if (session_id != -1) 766 errx(1, "-i cannot be used with -R"); 767 if (vflag != 0) 768 errx(1, "-v cannot be used with -R"); 769 770 } else { 771 assert(Lflag != 0); 772 773 if (portal != NULL) 774 errx(1, "-L and -p and mutually exclusive"); 775 if (target != NULL) 776 errx(1, "-L and -t and mutually exclusive"); 777 if (user != NULL) 778 errx(1, "-L and -u and mutually exclusive"); 779 if (secret != NULL) 780 errx(1, "-L and -s and mutually exclusive"); 781 if (nickname != NULL) 782 errx(1, "-L and -n and mutually exclusive"); 783 if (discovery_host != NULL) 784 errx(1, "-L and -d and mutually exclusive"); 785 786 if (session_id != -1) 787 errx(1, "-i cannot be used with -L"); 788 } 789 790 iscsi_fd = open(ISCSI_PATH, O_RDWR); 791 if (iscsi_fd < 0 && errno == ENOENT) { 792 saved_errno = errno; 793 retval = kldload("iscsi"); 794 if (retval != -1) 795 iscsi_fd = open(ISCSI_PATH, O_RDWR); 796 else 797 errno = saved_errno; 798 } 799 if (iscsi_fd < 0) 800 err(1, "failed to open %s", ISCSI_PATH); 801 802 if (Aflag != 0 && aflag != 0) { 803 conf = conf_new_from_file(conf_path); 804 805 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) 806 failed += kernel_add(iscsi_fd, targ); 807 } else if (nickname != NULL) { 808 conf = conf_new_from_file(conf_path); 809 targ = target_find(conf, nickname); 810 if (targ == NULL) 811 errx(1, "target %s not found in %s", 812 nickname, conf_path); 813 814 if (Aflag != 0) 815 failed += kernel_add(iscsi_fd, targ); 816 else if (Mflag != 0) 817 failed += kernel_modify(iscsi_fd, session_id, targ); 818 else if (Rflag != 0) 819 failed += kernel_remove(iscsi_fd, targ); 820 else 821 failed += kernel_list(iscsi_fd, targ, vflag); 822 } else if (Mflag != 0) { 823 kernel_modify_some(iscsi_fd, session_id, target, portal, 824 user, secret); 825 } else { 826 if (Aflag != 0 && target != NULL) { 827 if (valid_iscsi_name(target) == false) 828 errx(1, "invalid target name \"%s\"", target); 829 } 830 conf = conf_new(); 831 targ = target_new(conf); 832 targ->t_initiator_name = default_initiator_name(); 833 targ->t_header_digest = DIGEST_NONE; 834 targ->t_data_digest = DIGEST_NONE; 835 targ->t_name = target; 836 if (discovery_host != NULL) { 837 targ->t_session_type = SESSION_TYPE_DISCOVERY; 838 targ->t_address = discovery_host; 839 } else { 840 targ->t_session_type = SESSION_TYPE_NORMAL; 841 targ->t_address = portal; 842 } 843 targ->t_user = user; 844 targ->t_secret = secret; 845 846 if (Aflag != 0) 847 failed += kernel_add(iscsi_fd, targ); 848 else if (Rflag != 0) 849 failed += kernel_remove(iscsi_fd, targ); 850 else 851 failed += kernel_list(iscsi_fd, targ, vflag); 852 } 853 854 error = close(iscsi_fd); 855 if (error != 0) 856 err(1, "close"); 857 858 if (failed > 0) 859 return (1); 860 return (0); 861 } 862