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 51struct conf * 52conf_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 65struct target * 66target_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 79struct target * 80target_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 93void 94target_delete(struct target *targ) 95{ 96 97 TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); 98 free(targ); 99} 100 101 102static char * 103default_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 123static bool 124valid_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 155bool 156valid_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 223void 224conf_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 290static void 291conf_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 340static int 341kernel_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 354static int 355kernel_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 369static void 370kernel_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 430static int 431kernel_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 */ 447static int 448kernel_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 553static int 554kernel_wait(int iscsi_fd, int timeout) 555{ 556 struct iscsi_session_state *states = NULL; 557 const struct iscsi_session_state *state; 558 const struct iscsi_session_conf *conf; 559 struct iscsi_session_list isl; 560 unsigned int i, nentries = 1; 561 bool all_connected; 562 int error; 563 564 for (;;) { 565 for (;;) { 566 states = realloc(states, 567 nentries * sizeof(struct iscsi_session_state)); 568 if (states == NULL) 569 err(1, "realloc"); 570 571 memset(&isl, 0, sizeof(isl)); 572 isl.isl_nentries = nentries; 573 isl.isl_pstates = states; 574 575 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 576 if (error != 0 && errno == EMSGSIZE) { 577 nentries *= 4; 578 continue; 579 } 580 break; 581 } 582 if (error != 0) { 583 warn("ISCSISLIST"); 584 return (error); 585 } 586 587 all_connected = true; 588 for (i = 0; i < isl.isl_nentries; i++) { 589 state = &states[i]; 590 conf = &state->iss_conf; 591 592 if (!state->iss_connected) { 593 all_connected = false; 594 break; 595 } 596 } 597 598 if (all_connected) 599 return (0); 600 601 sleep(1); 602 603 if (timeout > 0) { 604 timeout--; 605 if (timeout == 0) 606 return (1); 607 } 608 } 609} 610 611static void 612usage(void) 613{ 614 615 fprintf(stderr, "usage: iscsictl -A -p portal -t target " 616 "[-u user -s secret] [-w timeout]\n"); 617 fprintf(stderr, " iscsictl -A -d discovery-host " 618 "[-u user -s secret]\n"); 619 fprintf(stderr, " iscsictl -A -a [-c path]\n"); 620 fprintf(stderr, " iscsictl -A -n nickname [-c path]\n"); 621 fprintf(stderr, " iscsictl -M -i session-id [-p portal] " 622 "[-t target] [-u user] [-s secret]\n"); 623 fprintf(stderr, " iscsictl -M -i session-id -n nickname " 624 "[-c path]\n"); 625 fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n"); 626 fprintf(stderr, " iscsictl -R -a\n"); 627 fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); 628 fprintf(stderr, " iscsictl -L [-v] [-w timeout]\n"); 629 exit(1); 630} 631 632char * 633checked_strdup(const char *s) 634{ 635 char *c; 636 637 c = strdup(s); 638 if (c == NULL) 639 err(1, "strdup"); 640 return (c); 641} 642 643int 644main(int argc, char **argv) 645{ 646 int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0; 647 const char *conf_path = DEFAULT_CONFIG_PATH; 648 char *nickname = NULL, *discovery_host = NULL, *portal = NULL, 649 *target = NULL, *user = NULL, *secret = NULL; 650 int timeout = -1; 651 long long session_id = -1; 652 char *end; 653 int ch, error, iscsi_fd, retval, saved_errno; 654 int failed = 0; 655 struct conf *conf; 656 struct target *targ; 657 658 while ((ch = getopt(argc, argv, "AMRLac:d:i:n:p:t:u:s:vw:")) != -1) { 659 switch (ch) { 660 case 'A': 661 Aflag = 1; 662 break; 663 case 'M': 664 Mflag = 1; 665 break; 666 case 'R': 667 Rflag = 1; 668 break; 669 case 'L': 670 Lflag = 1; 671 break; 672 case 'a': 673 aflag = 1; 674 break; 675 case 'c': 676 conf_path = optarg; 677 break; 678 case 'd': 679 discovery_host = optarg; 680 break; 681 case 'i': 682 session_id = strtol(optarg, &end, 10); 683 if ((size_t)(end - optarg) != strlen(optarg)) 684 errx(1, "trailing characters after session-id"); 685 if (session_id < 0) 686 errx(1, "session-id cannot be negative"); 687 if (session_id > UINT_MAX) 688 errx(1, "session-id cannot be greater than %u", 689 UINT_MAX); 690 break; 691 case 'n': 692 nickname = optarg; 693 break; 694 case 'p': 695 portal = optarg; 696 break; 697 case 't': 698 target = optarg; 699 break; 700 case 'u': 701 user = optarg; 702 break; 703 case 's': 704 secret = optarg; 705 break; 706 case 'v': 707 vflag = 1; 708 break; 709 case 'w': 710 timeout = strtol(optarg, &end, 10); 711 if ((size_t)(end - optarg) != strlen(optarg)) 712 errx(1, "trailing characters after timeout"); 713 if (timeout < 0) 714 errx(1, "timeout cannot be negative"); 715 break; 716 case '?': 717 default: 718 usage(); 719 } 720 } 721 argc -= optind; 722 if (argc != 0) 723 usage(); 724 725 if (Aflag + Mflag + Rflag + Lflag == 0) 726 Lflag = 1; 727 if (Aflag + Mflag + Rflag + Lflag > 1) 728 errx(1, "at most one of -A, -M, -R, or -L may be specified"); 729 730 /* 731 * Note that we ignore unneccessary/inapplicable "-c" flag; so that 732 * people can do something like "alias ISCSICTL="iscsictl -c path" 733 * in shell scripts. 734 */ 735 if (Aflag != 0) { 736 if (aflag != 0) { 737 if (portal != NULL) 738 errx(1, "-a and -p and mutually exclusive"); 739 if (target != NULL) 740 errx(1, "-a and -t and mutually exclusive"); 741 if (user != NULL) 742 errx(1, "-a and -u and mutually exclusive"); 743 if (secret != NULL) 744 errx(1, "-a and -s and mutually exclusive"); 745 if (nickname != NULL) 746 errx(1, "-a and -n and mutually exclusive"); 747 if (discovery_host != NULL) 748 errx(1, "-a and -d and mutually exclusive"); 749 } else if (nickname != NULL) { 750 if (portal != NULL) 751 errx(1, "-n and -p and mutually exclusive"); 752 if (target != NULL) 753 errx(1, "-n and -t and mutually exclusive"); 754 if (user != NULL) 755 errx(1, "-n and -u and mutually exclusive"); 756 if (secret != NULL) 757 errx(1, "-n and -s and mutually exclusive"); 758 if (discovery_host != NULL) 759 errx(1, "-n and -d and mutually exclusive"); 760 } else if (discovery_host != NULL) { 761 if (portal != NULL) 762 errx(1, "-d and -p and mutually exclusive"); 763 if (target != NULL) 764 errx(1, "-d and -t and mutually exclusive"); 765 } else { 766 if (target == NULL && portal == NULL) 767 errx(1, "must specify -a, -n or -t/-p"); 768 769 if (target != NULL && portal == NULL) 770 errx(1, "-t must always be used with -p"); 771 if (portal != NULL && target == NULL) 772 errx(1, "-p must always be used with -t"); 773 } 774 775 if (user != NULL && secret == NULL) 776 errx(1, "-u must always be used with -s"); 777 if (secret != NULL && user == NULL) 778 errx(1, "-s must always be used with -u"); 779 780 if (session_id != -1) 781 errx(1, "-i cannot be used with -A"); 782 if (vflag != 0) 783 errx(1, "-v cannot be used with -A"); 784 785 } else if (Mflag != 0) { 786 if (session_id == -1) 787 errx(1, "-M requires -i"); 788 789 if (discovery_host != NULL) 790 errx(1, "-M and -d are mutually exclusive"); 791 if (aflag != 0) 792 errx(1, "-M and -a are mutually exclusive"); 793 if (nickname != NULL) { 794 if (portal != NULL) 795 errx(1, "-n and -p and mutually exclusive"); 796 if (target != NULL) 797 errx(1, "-n and -t and mutually exclusive"); 798 if (user != NULL) 799 errx(1, "-n and -u and mutually exclusive"); 800 if (secret != NULL) 801 errx(1, "-n and -s and mutually exclusive"); 802 } 803 804 if (vflag != 0) 805 errx(1, "-v cannot be used with -M"); 806 if (timeout != -1) 807 errx(1, "-w cannot be used with -M"); 808 809 } else if (Rflag != 0) { 810 if (user != NULL) 811 errx(1, "-R and -u are mutually exclusive"); 812 if (secret != NULL) 813 errx(1, "-R and -s are mutually exclusive"); 814 if (discovery_host != NULL) 815 errx(1, "-R and -d are mutually exclusive"); 816 817 if (aflag != 0) { 818 if (portal != NULL) 819 errx(1, "-a and -p and mutually exclusive"); 820 if (target != NULL) 821 errx(1, "-a and -t and mutually exclusive"); 822 if (nickname != NULL) 823 errx(1, "-a and -n and mutually exclusive"); 824 } else if (nickname != NULL) { 825 if (portal != NULL) 826 errx(1, "-n and -p and mutually exclusive"); 827 if (target != NULL) 828 errx(1, "-n and -t and mutually exclusive"); 829 } else if (target == NULL && portal == NULL) { 830 errx(1, "must specify either -a, -n, -t, or -p"); 831 } 832 833 if (session_id != -1) 834 errx(1, "-i cannot be used with -R"); 835 if (vflag != 0) 836 errx(1, "-v cannot be used with -R"); 837 if (timeout != -1) 838 errx(1, "-w cannot be used with -R"); 839 840 } else { 841 assert(Lflag != 0); 842 843 if (portal != NULL) 844 errx(1, "-L and -p and mutually exclusive"); 845 if (target != NULL) 846 errx(1, "-L and -t and mutually exclusive"); 847 if (user != NULL) 848 errx(1, "-L and -u and mutually exclusive"); 849 if (secret != NULL) 850 errx(1, "-L and -s and mutually exclusive"); 851 if (nickname != NULL) 852 errx(1, "-L and -n and mutually exclusive"); 853 if (discovery_host != NULL) 854 errx(1, "-L and -d and mutually exclusive"); 855 856 if (session_id != -1) 857 errx(1, "-i cannot be used with -L"); 858 } 859 860 iscsi_fd = open(ISCSI_PATH, O_RDWR); 861 if (iscsi_fd < 0 && errno == ENOENT) { 862 saved_errno = errno; 863 retval = kldload("iscsi"); 864 if (retval != -1) 865 iscsi_fd = open(ISCSI_PATH, O_RDWR); 866 else 867 errno = saved_errno; 868 } 869 if (iscsi_fd < 0) 870 err(1, "failed to open %s", ISCSI_PATH); 871 872 if (Aflag != 0 && aflag != 0) { 873 conf = conf_new_from_file(conf_path); 874 875 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) 876 failed += kernel_add(iscsi_fd, targ); 877 } else if (nickname != NULL) { 878 conf = conf_new_from_file(conf_path); 879 targ = target_find(conf, nickname); 880 if (targ == NULL) 881 errx(1, "target %s not found in %s", 882 nickname, conf_path); 883 884 if (Aflag != 0) 885 failed += kernel_add(iscsi_fd, targ); 886 else if (Mflag != 0) 887 failed += kernel_modify(iscsi_fd, session_id, targ); 888 else if (Rflag != 0) 889 failed += kernel_remove(iscsi_fd, targ); 890 else 891 failed += kernel_list(iscsi_fd, targ, vflag); 892 } else if (Mflag != 0) { 893 kernel_modify_some(iscsi_fd, session_id, target, portal, 894 user, secret); 895 } else { 896 if (Aflag != 0 && target != NULL) { 897 if (valid_iscsi_name(target) == false) 898 errx(1, "invalid target name \"%s\"", target); 899 } 900 conf = conf_new(); 901 targ = target_new(conf); 902 targ->t_initiator_name = default_initiator_name(); 903 targ->t_header_digest = DIGEST_NONE; 904 targ->t_data_digest = DIGEST_NONE; 905 targ->t_name = target; 906 if (discovery_host != NULL) { 907 targ->t_session_type = SESSION_TYPE_DISCOVERY; 908 targ->t_address = discovery_host; 909 } else { 910 targ->t_session_type = SESSION_TYPE_NORMAL; 911 targ->t_address = portal; 912 } 913 targ->t_user = user; 914 targ->t_secret = secret; 915 916 if (Aflag != 0) 917 failed += kernel_add(iscsi_fd, targ); 918 else if (Rflag != 0) 919 failed += kernel_remove(iscsi_fd, targ); 920 else 921 failed += kernel_list(iscsi_fd, targ, vflag); 922 } 923 924 if (timeout != -1) 925 failed += kernel_wait(iscsi_fd, timeout); 926 927 error = close(iscsi_fd); 928 if (error != 0) 929 err(1, "close"); 930 931 if (failed > 0) 932 return (1); 933 return (0); 934} 935