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 <errno.h> 40 #include <fcntl.h> 41 #include <limits.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 #include <libxo/xo.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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_errx(1, "missing chapIName for target \"%s\"", 272 targ->t_nickname); 273 } 274 if (targ->t_secret == NULL) 275 xo_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 xo_errx(1, "missing tgtChapName for " 281 "target \"%s\"", targ->t_nickname); 282 if (targ->t_mutual_secret == NULL) 283 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_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 xo_warn("ISCSISLIST"); 480 return (error); 481 } 482 483 if (verbose != 0) { 484 xo_open_list("session"); 485 for (i = 0; i < isl.isl_nentries; i++) { 486 state = &states[i]; 487 conf = &state->iss_conf; 488 489 xo_open_instance("session"); 490 491 /* 492 * Display-only modifier as this information 493 * is also present within the 'session' container 494 */ 495 xo_emit("{L:/%-18s}{V:sessionId/%u}\n", 496 "Session ID:", state->iss_id); 497 498 xo_open_container("initiator"); 499 xo_emit("{L:/%-18s}{V:name/%s}\n", 500 "Initiator name:", conf->isc_initiator); 501 xo_emit("{L:/%-18s}{V:portal/%s}\n", 502 "Initiator portal:", conf->isc_initiator_addr); 503 xo_emit("{L:/%-18s}{V:alias/%s}\n", 504 "Initiator alias:", conf->isc_initiator_alias); 505 xo_close_container("initiator"); 506 507 xo_open_container("target"); 508 xo_emit("{L:/%-18s}{V:name/%s}\n", 509 "Target name:", conf->isc_target); 510 xo_emit("{L:/%-18s}{V:portal/%s}\n", 511 "Target portal:", conf->isc_target_addr); 512 xo_emit("{L:/%-18s}{V:alias/%s}\n", 513 "Target alias:", state->iss_target_alias); 514 xo_close_container("target"); 515 516 xo_open_container("auth"); 517 xo_emit("{L:/%-18s}{V:user/%s}\n", 518 "User:", conf->isc_user); 519 xo_emit("{L:/%-18s}{V:secret/%s}\n", 520 "Secret:", conf->isc_secret); 521 xo_emit("{L:/%-18s}{V:mutualUser/%s}\n", 522 "Mutual user:", conf->isc_mutual_user); 523 xo_emit("{L:/%-18s}{V:mutualSecret/%s}\n", 524 "Mutual secret:", conf->isc_mutual_secret); 525 xo_close_container("auth"); 526 527 xo_emit("{L:/%-18s}{V:type/%s}\n", 528 "Session type:", 529 conf->isc_discovery ? "Discovery" : "Normal"); 530 xo_emit("{L:/%-18s}{V:state/%s}\n", 531 "Session state:", 532 state->iss_connected ? "Connected" : "Disconnected"); 533 xo_emit("{L:/%-18s}{V:failureReason/%s}\n", 534 "Failure reason:", state->iss_reason); 535 xo_emit("{L:/%-18s}{V:headerDigest/%s}\n", 536 "Header digest:", 537 state->iss_header_digest == ISCSI_DIGEST_CRC32C ? 538 "CRC32C" : "None"); 539 xo_emit("{L:/%-18s}{V:dataDigest/%s}\n", 540 "Data digest:", 541 state->iss_data_digest == ISCSI_DIGEST_CRC32C ? 542 "CRC32C" : "None"); 543 xo_emit("{L:/%-18s}{V:dataSegmentLen/%d}\n", 544 "DataSegmentLen:", state->iss_max_data_segment_length); 545 xo_emit("{L:/%-18s}{V:immediateData/%s}\n", 546 "ImmediateData:", state->iss_immediate_data ? "Yes" : "No"); 547 xo_emit("{L:/%-18s}{V:iSER/%s}\n", 548 "iSER (RDMA):", conf->isc_iser ? "Yes" : "No"); 549 xo_emit("{L:/%-18s}{V:offloadDriver/%s}\n", 550 "Offload driver:", state->iss_offload); 551 xo_emit("{L:/%-18s}", 552 "Device nodes:"); 553 print_periphs(state->iss_id); 554 xo_emit("\n\n"); 555 xo_close_instance("session"); 556 } 557 xo_close_list("session"); 558 } else { 559 xo_emit("{T:/%-36s} {T:/%-16s} {T:/%s}\n", 560 "Target name", "Target portal", "State"); 561 562 if (isl.isl_nentries != 0) 563 xo_open_list("session"); 564 for (i = 0; i < isl.isl_nentries; i++) { 565 566 state = &states[i]; 567 conf = &state->iss_conf; 568 569 xo_open_instance("session"); 570 xo_emit("{V:name/%-36s/%s} {V:portal/%-16s/%s} ", 571 conf->isc_target, conf->isc_target_addr); 572 573 if (state->iss_reason[0] != '\0') { 574 xo_emit("{V:state/%s}\n", state->iss_reason); 575 } else { 576 if (conf->isc_discovery) { 577 xo_emit("{V:state}\n", "Discovery"); 578 } else if (state->iss_connected) { 579 xo_emit("{V:state}: ", "Connected"); 580 print_periphs(state->iss_id); 581 xo_emit("\n"); 582 } else { 583 xo_emit("{V:state}\n", "Disconnected"); 584 } 585 } 586 xo_close_instance("session"); 587 } 588 if (isl.isl_nentries != 0) 589 xo_close_list("session"); 590 } 591 592 return (0); 593 } 594 595 static void 596 usage(void) 597 { 598 599 fprintf(stderr, "usage: iscsictl -A -p portal -t target " 600 "[-u user -s secret]\n"); 601 fprintf(stderr, " iscsictl -A -d discovery-host " 602 "[-u user -s secret]\n"); 603 fprintf(stderr, " iscsictl -A -a [-c path]\n"); 604 fprintf(stderr, " iscsictl -A -n nickname [-c path]\n"); 605 fprintf(stderr, " iscsictl -M -i session-id [-p portal] " 606 "[-t target] [-u user] [-s secret]\n"); 607 fprintf(stderr, " iscsictl -M -i session-id -n nickname " 608 "[-c path]\n"); 609 fprintf(stderr, " iscsictl -R [-p portal] [-t target]\n"); 610 fprintf(stderr, " iscsictl -R -a\n"); 611 fprintf(stderr, " iscsictl -R -n nickname [-c path]\n"); 612 fprintf(stderr, " iscsictl -L [-v]\n"); 613 exit(1); 614 } 615 616 char * 617 checked_strdup(const char *s) 618 { 619 char *c; 620 621 c = strdup(s); 622 if (c == NULL) 623 xo_err(1, "strdup"); 624 return (c); 625 } 626 627 int 628 main(int argc, char **argv) 629 { 630 int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0; 631 const char *conf_path = DEFAULT_CONFIG_PATH; 632 char *nickname = NULL, *discovery_host = NULL, *portal = NULL, 633 *target = NULL, *user = NULL, *secret = NULL; 634 long long session_id = -1; 635 char *end; 636 int ch, error, iscsi_fd, retval, saved_errno; 637 int failed = 0; 638 struct conf *conf; 639 struct target *targ; 640 641 argc = xo_parse_args(argc, argv); 642 xo_open_container("iscsictl"); 643 644 while ((ch = getopt(argc, argv, "AMRLac:d:i:n:p:t:u:s:v")) != -1) { 645 switch (ch) { 646 case 'A': 647 Aflag = 1; 648 break; 649 case 'M': 650 Mflag = 1; 651 break; 652 case 'R': 653 Rflag = 1; 654 break; 655 case 'L': 656 Lflag = 1; 657 break; 658 case 'a': 659 aflag = 1; 660 break; 661 case 'c': 662 conf_path = optarg; 663 break; 664 case 'd': 665 discovery_host = optarg; 666 break; 667 case 'i': 668 session_id = strtol(optarg, &end, 10); 669 if ((size_t)(end - optarg) != strlen(optarg)) 670 xo_errx(1, "trailing characters after session-id"); 671 if (session_id < 0) 672 xo_errx(1, "session-id cannot be negative"); 673 if (session_id > UINT_MAX) 674 xo_errx(1, "session-id cannot be greater than %u", 675 UINT_MAX); 676 break; 677 case 'n': 678 nickname = optarg; 679 break; 680 case 'p': 681 portal = optarg; 682 break; 683 case 't': 684 target = optarg; 685 break; 686 case 'u': 687 user = optarg; 688 break; 689 case 's': 690 secret = optarg; 691 break; 692 case 'v': 693 vflag = 1; 694 break; 695 case '?': 696 default: 697 usage(); 698 } 699 } 700 argc -= optind; 701 if (argc != 0) 702 usage(); 703 704 if (Aflag + Mflag + Rflag + Lflag == 0) 705 Lflag = 1; 706 if (Aflag + Mflag + Rflag + Lflag > 1) 707 xo_errx(1, "at most one of -A, -M, -R, or -L may be specified"); 708 709 /* 710 * Note that we ignore unneccessary/inapplicable "-c" flag; so that 711 * people can do something like "alias ISCSICTL="iscsictl -c path" 712 * in shell scripts. 713 */ 714 if (Aflag != 0) { 715 if (aflag != 0) { 716 if (portal != NULL) 717 xo_errx(1, "-a and -p and mutually exclusive"); 718 if (target != NULL) 719 xo_errx(1, "-a and -t and mutually exclusive"); 720 if (user != NULL) 721 xo_errx(1, "-a and -u and mutually exclusive"); 722 if (secret != NULL) 723 xo_errx(1, "-a and -s and mutually exclusive"); 724 if (nickname != NULL) 725 xo_errx(1, "-a and -n and mutually exclusive"); 726 if (discovery_host != NULL) 727 xo_errx(1, "-a and -d and mutually exclusive"); 728 } else if (nickname != NULL) { 729 if (portal != NULL) 730 xo_errx(1, "-n and -p and mutually exclusive"); 731 if (target != NULL) 732 xo_errx(1, "-n and -t and mutually exclusive"); 733 if (user != NULL) 734 xo_errx(1, "-n and -u and mutually exclusive"); 735 if (secret != NULL) 736 xo_errx(1, "-n and -s and mutually exclusive"); 737 if (discovery_host != NULL) 738 xo_errx(1, "-n and -d and mutually exclusive"); 739 } else if (discovery_host != NULL) { 740 if (portal != NULL) 741 xo_errx(1, "-d and -p and mutually exclusive"); 742 if (target != NULL) 743 xo_errx(1, "-d and -t and mutually exclusive"); 744 } else { 745 if (target == NULL && portal == NULL) 746 xo_errx(1, "must specify -a, -n or -t/-p"); 747 748 if (target != NULL && portal == NULL) 749 xo_errx(1, "-t must always be used with -p"); 750 if (portal != NULL && target == NULL) 751 xo_errx(1, "-p must always be used with -t"); 752 } 753 754 if (user != NULL && secret == NULL) 755 xo_errx(1, "-u must always be used with -s"); 756 if (secret != NULL && user == NULL) 757 xo_errx(1, "-s must always be used with -u"); 758 759 if (session_id != -1) 760 xo_errx(1, "-i cannot be used with -A"); 761 if (vflag != 0) 762 xo_errx(1, "-v cannot be used with -A"); 763 764 } else if (Mflag != 0) { 765 if (session_id == -1) 766 xo_errx(1, "-M requires -i"); 767 768 if (discovery_host != NULL) 769 xo_errx(1, "-M and -d are mutually exclusive"); 770 if (aflag != 0) 771 xo_errx(1, "-M and -a are mutually exclusive"); 772 if (nickname != NULL) { 773 if (portal != NULL) 774 xo_errx(1, "-n and -p and mutually exclusive"); 775 if (target != NULL) 776 xo_errx(1, "-n and -t and mutually exclusive"); 777 if (user != NULL) 778 xo_errx(1, "-n and -u and mutually exclusive"); 779 if (secret != NULL) 780 xo_errx(1, "-n and -s and mutually exclusive"); 781 } 782 783 if (vflag != 0) 784 xo_errx(1, "-v cannot be used with -M"); 785 786 } else if (Rflag != 0) { 787 if (user != NULL) 788 xo_errx(1, "-R and -u are mutually exclusive"); 789 if (secret != NULL) 790 xo_errx(1, "-R and -s are mutually exclusive"); 791 if (discovery_host != NULL) 792 xo_errx(1, "-R and -d are mutually exclusive"); 793 794 if (aflag != 0) { 795 if (portal != NULL) 796 xo_errx(1, "-a and -p and mutually exclusive"); 797 if (target != NULL) 798 xo_errx(1, "-a and -t and mutually exclusive"); 799 if (nickname != NULL) 800 xo_errx(1, "-a and -n and mutually exclusive"); 801 } else if (nickname != NULL) { 802 if (portal != NULL) 803 xo_errx(1, "-n and -p and mutually exclusive"); 804 if (target != NULL) 805 xo_errx(1, "-n and -t and mutually exclusive"); 806 } else if (target == NULL && portal == NULL) { 807 xo_errx(1, "must specify either -a, -n, -t, or -p"); 808 } 809 810 if (session_id != -1) 811 xo_errx(1, "-i cannot be used with -R"); 812 if (vflag != 0) 813 xo_errx(1, "-v cannot be used with -R"); 814 815 } else { 816 assert(Lflag != 0); 817 818 if (portal != NULL) 819 xo_errx(1, "-L and -p and mutually exclusive"); 820 if (target != NULL) 821 xo_errx(1, "-L and -t and mutually exclusive"); 822 if (user != NULL) 823 xo_errx(1, "-L and -u and mutually exclusive"); 824 if (secret != NULL) 825 xo_errx(1, "-L and -s and mutually exclusive"); 826 if (nickname != NULL) 827 xo_errx(1, "-L and -n and mutually exclusive"); 828 if (discovery_host != NULL) 829 xo_errx(1, "-L and -d and mutually exclusive"); 830 831 if (session_id != -1) 832 xo_errx(1, "-i cannot be used with -L"); 833 } 834 835 iscsi_fd = open(ISCSI_PATH, O_RDWR); 836 if (iscsi_fd < 0 && errno == ENOENT) { 837 saved_errno = errno; 838 retval = kldload("iscsi"); 839 if (retval != -1) 840 iscsi_fd = open(ISCSI_PATH, O_RDWR); 841 else 842 errno = saved_errno; 843 } 844 if (iscsi_fd < 0) 845 xo_err(1, "failed to open %s", ISCSI_PATH); 846 847 if (Aflag != 0 && aflag != 0) { 848 conf = conf_new_from_file(conf_path); 849 850 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) 851 failed += kernel_add(iscsi_fd, targ); 852 } else if (nickname != NULL) { 853 conf = conf_new_from_file(conf_path); 854 targ = target_find(conf, nickname); 855 if (targ == NULL) 856 xo_errx(1, "target %s not found in %s", 857 nickname, conf_path); 858 859 if (Aflag != 0) 860 failed += kernel_add(iscsi_fd, targ); 861 else if (Mflag != 0) 862 failed += kernel_modify(iscsi_fd, session_id, targ); 863 else if (Rflag != 0) 864 failed += kernel_remove(iscsi_fd, targ); 865 else 866 failed += kernel_list(iscsi_fd, targ, vflag); 867 } else if (Mflag != 0) { 868 kernel_modify_some(iscsi_fd, session_id, target, portal, 869 user, secret); 870 } else { 871 if (Aflag != 0 && target != NULL) { 872 if (valid_iscsi_name(target) == false) 873 xo_errx(1, "invalid target name \"%s\"", target); 874 } 875 conf = conf_new(); 876 targ = target_new(conf); 877 targ->t_initiator_name = default_initiator_name(); 878 targ->t_header_digest = DIGEST_NONE; 879 targ->t_data_digest = DIGEST_NONE; 880 targ->t_name = target; 881 if (discovery_host != NULL) { 882 targ->t_session_type = SESSION_TYPE_DISCOVERY; 883 targ->t_address = discovery_host; 884 } else { 885 targ->t_session_type = SESSION_TYPE_NORMAL; 886 targ->t_address = portal; 887 } 888 targ->t_user = user; 889 targ->t_secret = secret; 890 891 if (Aflag != 0) 892 failed += kernel_add(iscsi_fd, targ); 893 else if (Rflag != 0) 894 failed += kernel_remove(iscsi_fd, targ); 895 else 896 failed += kernel_list(iscsi_fd, targ, vflag); 897 } 898 899 error = close(iscsi_fd); 900 if (error != 0) 901 xo_err(1, "close"); 902 903 if (failed > 0) 904 return (1); 905 906 xo_close_container("iscsictl"); 907 xo_finish(); 908 return (0); 909 } 910