1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2012 The FreeBSD Foundation 5 * 6 * This software was developed by Edward Tomasz Napierala under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 */ 31 32 #include <sys/cdefs.h> 33 #include <sys/ioctl.h> 34 #include <sys/param.h> 35 #include <sys/linker.h> 36 #include <assert.h> 37 #include <ctype.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 #include <libiscsiutil.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 targ->t_dscp = -1; 89 targ->t_pcp = -1; 90 targ->t_pingtimeout = -1; 91 targ->t_logintimeout = -1; 92 TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); 93 94 return (targ); 95 } 96 97 void 98 target_delete(struct target *targ) 99 { 100 101 TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); 102 free(targ); 103 } 104 105 static char * 106 default_initiator_name(void) 107 { 108 char *name; 109 size_t namelen; 110 int error; 111 112 namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN); 113 114 name = calloc(1, namelen + 1); 115 if (name == NULL) 116 xo_err(1, "calloc"); 117 strcpy(name, DEFAULT_IQN); 118 error = gethostname(name + strlen(DEFAULT_IQN), 119 namelen - strlen(DEFAULT_IQN)); 120 if (error != 0) 121 xo_err(1, "gethostname"); 122 123 return (name); 124 } 125 126 int 127 parse_enable(const char *enable) 128 { 129 if (enable == NULL) 130 return (ENABLE_UNSPECIFIED); 131 132 if (strcasecmp(enable, "on") == 0 || 133 strcasecmp(enable, "yes") == 0) 134 return (ENABLE_ON); 135 136 if (strcasecmp(enable, "off") == 0 || 137 strcasecmp(enable, "no") == 0) 138 return (ENABLE_OFF); 139 140 return (ENABLE_UNSPECIFIED); 141 } 142 143 void 144 conf_verify(struct conf *conf) 145 { 146 struct target *targ; 147 148 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { 149 assert(targ->t_nickname != NULL); 150 if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED) 151 targ->t_session_type = SESSION_TYPE_NORMAL; 152 if (targ->t_session_type == SESSION_TYPE_NORMAL && 153 targ->t_name == NULL) 154 xo_errx(1, "missing TargetName for target \"%s\"", 155 targ->t_nickname); 156 if (targ->t_session_type == SESSION_TYPE_DISCOVERY && 157 targ->t_name != NULL) 158 xo_errx(1, "cannot specify TargetName for discovery " 159 "sessions for target \"%s\"", targ->t_nickname); 160 if (targ->t_name != NULL) { 161 if (valid_iscsi_name(targ->t_name, xo_warnx) == false) 162 xo_errx(1, "invalid target name \"%s\"", 163 targ->t_name); 164 } 165 if (targ->t_protocol == PROTOCOL_UNSPECIFIED) 166 targ->t_protocol = PROTOCOL_ISCSI; 167 if (targ->t_address == NULL) 168 xo_errx(1, "missing TargetAddress for target \"%s\"", 169 targ->t_nickname); 170 if (targ->t_initiator_name == NULL) 171 targ->t_initiator_name = default_initiator_name(); 172 if (valid_iscsi_name(targ->t_initiator_name, xo_warnx) == false) 173 xo_errx(1, "invalid initiator name \"%s\"", 174 targ->t_initiator_name); 175 if (targ->t_header_digest == DIGEST_UNSPECIFIED) 176 targ->t_header_digest = DIGEST_NONE; 177 if (targ->t_data_digest == DIGEST_UNSPECIFIED) 178 targ->t_data_digest = DIGEST_NONE; 179 if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) { 180 if (targ->t_user != NULL || targ->t_secret != NULL || 181 targ->t_mutual_user != NULL || 182 targ->t_mutual_secret != NULL) 183 targ->t_auth_method = 184 AUTH_METHOD_CHAP; 185 else 186 targ->t_auth_method = 187 AUTH_METHOD_NONE; 188 } 189 if (targ->t_auth_method == AUTH_METHOD_CHAP) { 190 if (targ->t_user == NULL) { 191 xo_errx(1, "missing chapIName for target \"%s\"", 192 targ->t_nickname); 193 } 194 if (targ->t_secret == NULL) 195 xo_errx(1, "missing chapSecret for target \"%s\"", 196 targ->t_nickname); 197 if (targ->t_mutual_user != NULL || 198 targ->t_mutual_secret != NULL) { 199 if (targ->t_mutual_user == NULL) 200 xo_errx(1, "missing tgtChapName for " 201 "target \"%s\"", targ->t_nickname); 202 if (targ->t_mutual_secret == NULL) 203 xo_errx(1, "missing tgtChapSecret for " 204 "target \"%s\"", targ->t_nickname); 205 } 206 } 207 } 208 } 209 210 static void 211 conf_from_target(struct iscsi_session_conf *conf, 212 const struct target *targ) 213 { 214 memset(conf, 0, sizeof(*conf)); 215 216 /* 217 * XXX: Check bounds and return error instead of silently truncating. 218 */ 219 if (targ->t_initiator_name != NULL) 220 strlcpy(conf->isc_initiator, targ->t_initiator_name, 221 sizeof(conf->isc_initiator)); 222 if (targ->t_initiator_address != NULL) 223 strlcpy(conf->isc_initiator_addr, targ->t_initiator_address, 224 sizeof(conf->isc_initiator_addr)); 225 if (targ->t_initiator_alias != NULL) 226 strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias, 227 sizeof(conf->isc_initiator_alias)); 228 if (targ->t_name != NULL) 229 strlcpy(conf->isc_target, targ->t_name, 230 sizeof(conf->isc_target)); 231 if (targ->t_address != NULL) 232 strlcpy(conf->isc_target_addr, targ->t_address, 233 sizeof(conf->isc_target_addr)); 234 if (targ->t_user != NULL) 235 strlcpy(conf->isc_user, targ->t_user, 236 sizeof(conf->isc_user)); 237 if (targ->t_secret != NULL) 238 strlcpy(conf->isc_secret, targ->t_secret, 239 sizeof(conf->isc_secret)); 240 if (targ->t_mutual_user != NULL) 241 strlcpy(conf->isc_mutual_user, targ->t_mutual_user, 242 sizeof(conf->isc_mutual_user)); 243 if (targ->t_mutual_secret != NULL) 244 strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret, 245 sizeof(conf->isc_mutual_secret)); 246 if (targ->t_session_type == SESSION_TYPE_DISCOVERY) 247 conf->isc_discovery = 1; 248 if (targ->t_enable != ENABLE_OFF) 249 conf->isc_enable = 1; 250 if (targ->t_protocol == PROTOCOL_ISER) 251 conf->isc_iser = 1; 252 if (targ->t_offload != NULL) 253 strlcpy(conf->isc_offload, targ->t_offload, 254 sizeof(conf->isc_offload)); 255 if (targ->t_header_digest == DIGEST_CRC32C) 256 conf->isc_header_digest = ISCSI_DIGEST_CRC32C; 257 else 258 conf->isc_header_digest = ISCSI_DIGEST_NONE; 259 if (targ->t_data_digest == DIGEST_CRC32C) 260 conf->isc_data_digest = ISCSI_DIGEST_CRC32C; 261 else 262 conf->isc_data_digest = ISCSI_DIGEST_NONE; 263 conf->isc_dscp = targ->t_dscp; 264 conf->isc_pcp = targ->t_pcp; 265 conf->isc_ping_timeout = targ->t_pingtimeout; 266 conf->isc_login_timeout = targ->t_logintimeout; 267 } 268 269 static int 270 kernel_add(int iscsi_fd, const struct target *targ) 271 { 272 struct iscsi_session_add isa; 273 int error; 274 275 memset(&isa, 0, sizeof(isa)); 276 conf_from_target(&isa.isa_conf, targ); 277 error = ioctl(iscsi_fd, ISCSISADD, &isa); 278 if (error != 0) 279 xo_warn("ISCSISADD"); 280 return (error); 281 } 282 283 static int 284 kernel_modify(int iscsi_fd, unsigned int session_id, const struct target *targ) 285 { 286 struct iscsi_session_modify ism; 287 int error; 288 289 memset(&ism, 0, sizeof(ism)); 290 ism.ism_session_id = session_id; 291 conf_from_target(&ism.ism_conf, targ); 292 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); 293 if (error != 0) 294 xo_warn("ISCSISMODIFY"); 295 return (error); 296 } 297 298 static void 299 kernel_modify_some(int iscsi_fd, unsigned int session_id, const char *target, 300 const char *target_addr, const char *user, const char *secret, int enable) 301 { 302 struct iscsi_session_state *states = NULL; 303 struct iscsi_session_state *state; 304 struct iscsi_session_conf *conf; 305 struct iscsi_session_list isl; 306 struct iscsi_session_modify ism; 307 unsigned int i, nentries = 1; 308 int error; 309 310 for (;;) { 311 states = realloc(states, 312 nentries * sizeof(struct iscsi_session_state)); 313 if (states == NULL) 314 xo_err(1, "realloc"); 315 316 memset(&isl, 0, sizeof(isl)); 317 isl.isl_nentries = nentries; 318 isl.isl_pstates = states; 319 320 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 321 if (error != 0 && errno == EMSGSIZE) { 322 nentries *= 4; 323 continue; 324 } 325 break; 326 } 327 if (error != 0) 328 xo_errx(1, "ISCSISLIST"); 329 330 for (i = 0; i < isl.isl_nentries; i++) { 331 state = &states[i]; 332 333 if (state->iss_id == session_id) 334 break; 335 } 336 if (i == isl.isl_nentries) 337 xo_errx(1, "session-id %u not found", session_id); 338 339 conf = &state->iss_conf; 340 341 if (target != NULL) 342 strlcpy(conf->isc_target, target, sizeof(conf->isc_target)); 343 if (target_addr != NULL) 344 strlcpy(conf->isc_target_addr, target_addr, 345 sizeof(conf->isc_target_addr)); 346 if (user != NULL) 347 strlcpy(conf->isc_user, user, sizeof(conf->isc_user)); 348 if (secret != NULL) 349 strlcpy(conf->isc_secret, secret, sizeof(conf->isc_secret)); 350 if (enable == ENABLE_ON) 351 conf->isc_enable = 1; 352 else if (enable == ENABLE_OFF) 353 conf->isc_enable = 0; 354 355 memset(&ism, 0, sizeof(ism)); 356 ism.ism_session_id = session_id; 357 memcpy(&ism.ism_conf, conf, sizeof(ism.ism_conf)); 358 error = ioctl(iscsi_fd, ISCSISMODIFY, &ism); 359 if (error != 0) 360 xo_warn("ISCSISMODIFY"); 361 } 362 363 static int 364 kernel_remove(int iscsi_fd, const struct target *targ) 365 { 366 struct iscsi_session_remove isr; 367 int error; 368 369 memset(&isr, 0, sizeof(isr)); 370 conf_from_target(&isr.isr_conf, targ); 371 error = ioctl(iscsi_fd, ISCSISREMOVE, &isr); 372 if (error != 0) 373 xo_warn("ISCSISREMOVE"); 374 return (error); 375 } 376 377 /* 378 * XXX: Add filtering. 379 */ 380 static int 381 kernel_list(int iscsi_fd, const struct target *targ __unused, 382 int verbose) 383 { 384 struct iscsi_session_state *states = NULL; 385 const struct iscsi_session_state *state; 386 const struct iscsi_session_conf *conf; 387 struct iscsi_session_list isl; 388 unsigned int i, nentries = 1; 389 int error; 390 391 for (;;) { 392 states = realloc(states, 393 nentries * sizeof(struct iscsi_session_state)); 394 if (states == NULL) 395 xo_err(1, "realloc"); 396 397 memset(&isl, 0, sizeof(isl)); 398 isl.isl_nentries = nentries; 399 isl.isl_pstates = states; 400 401 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 402 if (error != 0 && errno == EMSGSIZE) { 403 nentries *= 4; 404 continue; 405 } 406 break; 407 } 408 if (error != 0) { 409 xo_warn("ISCSISLIST"); 410 return (error); 411 } 412 413 if (verbose != 0) { 414 xo_open_list("session"); 415 for (i = 0; i < isl.isl_nentries; i++) { 416 state = &states[i]; 417 conf = &state->iss_conf; 418 419 xo_open_instance("session"); 420 421 /* 422 * Display-only modifier as this information 423 * is also present within the 'session' container 424 */ 425 xo_emit("{L:/%-26s}{V:sessionId/%u}\n", 426 "Session ID:", state->iss_id); 427 428 xo_open_container("initiator"); 429 xo_emit("{L:/%-26s}{V:name/%s}\n", 430 "Initiator name:", conf->isc_initiator); 431 xo_emit("{L:/%-26s}{V:portal/%s}\n", 432 "Initiator portal:", conf->isc_initiator_addr); 433 xo_emit("{L:/%-26s}{V:alias/%s}\n", 434 "Initiator alias:", conf->isc_initiator_alias); 435 xo_close_container("initiator"); 436 437 xo_open_container("target"); 438 xo_emit("{L:/%-26s}{V:name/%s}\n", 439 "Target name:", conf->isc_target); 440 xo_emit("{L:/%-26s}{V:portal/%s}\n", 441 "Target portal:", conf->isc_target_addr); 442 xo_emit("{L:/%-26s}{V:alias/%s}\n", 443 "Target alias:", state->iss_target_alias); 444 if (conf->isc_dscp != -1) 445 xo_emit("{L:/%-26s}{V:dscp/0x%02x}\n", 446 "Target DSCP:", conf->isc_dscp); 447 if (conf->isc_pcp != -1) 448 xo_emit("{L:/%-26s}{V:pcp/0x%02x}\n", 449 "Target PCP:", conf->isc_pcp); 450 if (conf->isc_ping_timeout != -1) 451 xo_emit("{L:/%-26s}{V:PingTimeout/%d}\n", 452 "Target PingTimeout:", 453 conf->isc_ping_timeout); 454 if (conf->isc_login_timeout != -1) 455 xo_emit("{L:/%-26s}{V:LoginTimeout/%d}\n", 456 "Target LoginTimeout:", 457 conf->isc_login_timeout); 458 xo_close_container("target"); 459 460 xo_open_container("auth"); 461 xo_emit("{L:/%-26s}{V:user/%s}\n", 462 "User:", conf->isc_user); 463 xo_emit("{L:/%-26s}{V:secret/%s}\n", 464 "Secret:", conf->isc_secret); 465 xo_emit("{L:/%-26s}{V:mutualUser/%s}\n", 466 "Mutual user:", conf->isc_mutual_user); 467 xo_emit("{L:/%-26s}{V:mutualSecret/%s}\n", 468 "Mutual secret:", conf->isc_mutual_secret); 469 xo_close_container("auth"); 470 471 xo_emit("{L:/%-26s}{V:type/%s}\n", 472 "Session type:", 473 conf->isc_discovery ? "Discovery" : "Normal"); 474 xo_emit("{L:/%-26s}{V:enable/%s}\n", 475 "Enable:", 476 conf->isc_enable ? "Yes" : "No"); 477 xo_emit("{L:/%-26s}{V:state/%s}\n", 478 "Session state:", 479 state->iss_connected ? "Connected" : "Disconnected"); 480 xo_emit("{L:/%-26s}{V:failureReason/%s}\n", 481 "Failure reason:", state->iss_reason); 482 xo_emit("{L:/%-26s}{V:headerDigest/%s}\n", 483 "Header digest:", 484 state->iss_header_digest == ISCSI_DIGEST_CRC32C ? 485 "CRC32C" : "None"); 486 xo_emit("{L:/%-26s}{V:dataDigest/%s}\n", 487 "Data digest:", 488 state->iss_data_digest == ISCSI_DIGEST_CRC32C ? 489 "CRC32C" : "None"); 490 xo_emit("{L:/%-26s}{V:recvDataSegmentLen/%d}\n", 491 "MaxRecvDataSegmentLength:", 492 state->iss_max_recv_data_segment_length); 493 xo_emit("{L:/%-26s}{V:sendDataSegmentLen/%d}\n", 494 "MaxSendDataSegmentLength:", 495 state->iss_max_send_data_segment_length); 496 xo_emit("{L:/%-26s}{V:maxBurstLen/%d}\n", 497 "MaxBurstLen:", state->iss_max_burst_length); 498 xo_emit("{L:/%-26s}{V:firstBurstLen/%d}\n", 499 "FirstBurstLen:", state->iss_first_burst_length); 500 xo_emit("{L:/%-26s}{V:immediateData/%s}\n", 501 "ImmediateData:", state->iss_immediate_data ? "Yes" : "No"); 502 xo_emit("{L:/%-26s}{V:iSER/%s}\n", 503 "iSER (RDMA):", conf->isc_iser ? "Yes" : "No"); 504 xo_emit("{L:/%-26s}{V:offloadDriver/%s}\n", 505 "Offload driver:", state->iss_offload); 506 xo_emit("{L:/%-26s}", 507 "Device nodes:"); 508 print_periphs(state->iss_id); 509 xo_emit("\n\n"); 510 xo_close_instance("session"); 511 } 512 xo_close_list("session"); 513 } else { 514 xo_emit("{T:/%-36s} {T:/%-16s} {T:/%s}\n", 515 "Target name", "Target portal", "State"); 516 517 if (isl.isl_nentries != 0) 518 xo_open_list("session"); 519 for (i = 0; i < isl.isl_nentries; i++) { 520 521 state = &states[i]; 522 conf = &state->iss_conf; 523 524 xo_open_instance("session"); 525 xo_emit("{V:name/%-36s/%s} {V:portal/%-16s/%s} ", 526 conf->isc_target, conf->isc_target_addr); 527 528 if (state->iss_reason[0] != '\0' && 529 conf->isc_enable != 0) { 530 xo_emit("{V:state/%s}\n", state->iss_reason); 531 } else { 532 if (conf->isc_discovery) { 533 xo_emit("{V:state}\n", "Discovery"); 534 } else if (conf->isc_enable == 0) { 535 xo_emit("{V:state}\n", "Disabled"); 536 } else if (state->iss_connected) { 537 xo_emit("{V:state}: ", "Connected"); 538 print_periphs(state->iss_id); 539 xo_emit("\n"); 540 } else { 541 xo_emit("{V:state}\n", "Disconnected"); 542 } 543 } 544 xo_close_instance("session"); 545 } 546 if (isl.isl_nentries != 0) 547 xo_close_list("session"); 548 } 549 550 return (0); 551 } 552 553 static int 554 kernel_wait(int iscsi_fd, int timeout) 555 { 556 struct iscsi_session_state *states = NULL; 557 const struct iscsi_session_state *state; 558 struct iscsi_session_list isl; 559 unsigned int i, nentries = 1; 560 bool all_connected; 561 int error; 562 563 for (;;) { 564 for (;;) { 565 states = realloc(states, 566 nentries * sizeof(struct iscsi_session_state)); 567 if (states == NULL) 568 xo_err(1, "realloc"); 569 570 memset(&isl, 0, sizeof(isl)); 571 isl.isl_nentries = nentries; 572 isl.isl_pstates = states; 573 574 error = ioctl(iscsi_fd, ISCSISLIST, &isl); 575 if (error != 0 && errno == EMSGSIZE) { 576 nentries *= 4; 577 continue; 578 } 579 break; 580 } 581 if (error != 0) { 582 xo_warn("ISCSISLIST"); 583 return (error); 584 } 585 586 all_connected = true; 587 for (i = 0; i < isl.isl_nentries; i++) { 588 state = &states[i]; 589 590 if (!state->iss_connected) { 591 all_connected = false; 592 break; 593 } 594 } 595 596 if (all_connected) 597 return (0); 598 599 sleep(1); 600 601 if (timeout > 0) { 602 timeout--; 603 if (timeout == 0) 604 return (1); 605 } 606 } 607 } 608 609 static void 610 usage(void) 611 { 612 613 xo_error("usage: iscsictl -A -p portal -t target " 614 "[-u user -s secret] [-w timeout] [-e on | off]\n"); 615 xo_error(" iscsictl -A -d discovery-host " 616 "[-u user -s secret] [-e on | off]\n"); 617 xo_error(" iscsictl -A -a [-c path]\n"); 618 xo_error(" iscsictl -A -n nickname [-c path]\n"); 619 xo_error(" iscsictl -M -i session-id [-p portal] " 620 "[-t target] [-u user] [-s secret] [-e on | off]\n"); 621 xo_error(" iscsictl -M -i session-id -n nickname " 622 "[-c path]\n"); 623 xo_error(" iscsictl -R [-p portal] [-t target]\n"); 624 xo_error(" iscsictl -R -a\n"); 625 xo_error(" iscsictl -R -n nickname [-c path]\n"); 626 xo_error(" iscsictl -L [-v] [-w timeout]\n"); 627 exit(1); 628 } 629 630 int 631 main(int argc, char **argv) 632 { 633 int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, 634 rflag = 0, vflag = 0; 635 const char *conf_path = DEFAULT_CONFIG_PATH; 636 char *nickname = NULL, *discovery_host = NULL, *portal = NULL, 637 *target = NULL, *user = NULL, *secret = NULL; 638 int timeout = -1, enable = ENABLE_UNSPECIFIED; 639 long long session_id = -1; 640 char *end; 641 int ch, error, iscsi_fd, retval, saved_errno; 642 int failed = 0; 643 struct conf *conf; 644 struct target *targ; 645 646 argc = xo_parse_args(argc, argv); 647 if (argc < 0) 648 exit(1); 649 650 xo_set_version(ISCSICTL_XO_VERSION); 651 xo_open_container("iscsictl"); 652 653 while ((ch = getopt(argc, argv, "AMRLac:d:e:i:n:p:rt:u:s:vw:")) != -1) { 654 switch (ch) { 655 case 'A': 656 Aflag = 1; 657 break; 658 case 'M': 659 Mflag = 1; 660 break; 661 case 'R': 662 Rflag = 1; 663 break; 664 case 'L': 665 Lflag = 1; 666 break; 667 case 'a': 668 aflag = 1; 669 break; 670 case 'c': 671 conf_path = optarg; 672 break; 673 case 'd': 674 discovery_host = optarg; 675 break; 676 case 'e': 677 enable = parse_enable(optarg); 678 if (enable == ENABLE_UNSPECIFIED) { 679 xo_errx(1, "invalid argument to -e, " 680 "must be either \"on\" or \"off\""); 681 } 682 break; 683 case 'i': 684 session_id = strtol(optarg, &end, 10); 685 if ((size_t)(end - optarg) != strlen(optarg)) 686 xo_errx(1, "trailing characters after session-id"); 687 if (session_id < 0) 688 xo_errx(1, "session-id cannot be negative"); 689 if (session_id > UINT_MAX) 690 xo_errx(1, "session-id cannot be greater than %u", 691 UINT_MAX); 692 break; 693 case 'n': 694 nickname = optarg; 695 break; 696 case 'p': 697 portal = optarg; 698 break; 699 case 'r': 700 rflag = 1; 701 break; 702 case 't': 703 target = optarg; 704 break; 705 case 'u': 706 user = optarg; 707 break; 708 case 's': 709 secret = optarg; 710 break; 711 case 'v': 712 vflag = 1; 713 break; 714 case 'w': 715 timeout = strtol(optarg, &end, 10); 716 if ((size_t)(end - optarg) != strlen(optarg)) 717 xo_errx(1, "trailing characters after timeout"); 718 if (timeout < 0) 719 xo_errx(1, "timeout cannot be negative"); 720 break; 721 case '?': 722 default: 723 usage(); 724 } 725 } 726 argc -= optind; 727 if (argc != 0) 728 usage(); 729 730 if (Aflag + Mflag + Rflag + Lflag == 0) 731 Lflag = 1; 732 if (Aflag + Mflag + Rflag + Lflag > 1) 733 xo_errx(1, "at most one of -A, -M, -R, or -L may be specified"); 734 735 /* 736 * Note that we ignore unnecessary/inapplicable "-c" flag; so that 737 * people can do something like "alias ISCSICTL="iscsictl -c path" 738 * in shell scripts. 739 */ 740 if (Aflag != 0) { 741 if (aflag != 0) { 742 if (enable != ENABLE_UNSPECIFIED) 743 xo_errx(1, "-a and -e are mutually exclusive"); 744 if (portal != NULL) 745 xo_errx(1, "-a and -p are mutually exclusive"); 746 if (target != NULL) 747 xo_errx(1, "-a and -t are mutually exclusive"); 748 if (user != NULL) 749 xo_errx(1, "-a and -u are mutually exclusive"); 750 if (secret != NULL) 751 xo_errx(1, "-a and -s are mutually exclusive"); 752 if (nickname != NULL) 753 xo_errx(1, "-a and -n are mutually exclusive"); 754 if (discovery_host != NULL) 755 xo_errx(1, "-a and -d are mutually exclusive"); 756 if (rflag != 0) 757 xo_errx(1, "-a and -r are mutually exclusive"); 758 } else if (nickname != NULL) { 759 if (enable != ENABLE_UNSPECIFIED) 760 xo_errx(1, "-n and -e are mutually exclusive"); 761 if (portal != NULL) 762 xo_errx(1, "-n and -p are mutually exclusive"); 763 if (target != NULL) 764 xo_errx(1, "-n and -t are mutually exclusive"); 765 if (user != NULL) 766 xo_errx(1, "-n and -u are mutually exclusive"); 767 if (secret != NULL) 768 xo_errx(1, "-n and -s are mutually exclusive"); 769 if (discovery_host != NULL) 770 xo_errx(1, "-n and -d are mutually exclusive"); 771 if (rflag != 0) 772 xo_errx(1, "-n and -r are mutually exclusive"); 773 } else if (discovery_host != NULL) { 774 if (portal != NULL) 775 xo_errx(1, "-d and -p are mutually exclusive"); 776 if (target != NULL) 777 xo_errx(1, "-d and -t are mutually exclusive"); 778 } else { 779 if (target == NULL && portal == NULL) 780 xo_errx(1, "must specify -a, -n or -t/-p"); 781 782 if (target != NULL && portal == NULL) 783 xo_errx(1, "-t must always be used with -p"); 784 if (portal != NULL && target == NULL) 785 xo_errx(1, "-p must always be used with -t"); 786 } 787 788 if (user != NULL && secret == NULL) 789 xo_errx(1, "-u must always be used with -s"); 790 if (secret != NULL && user == NULL) 791 xo_errx(1, "-s must always be used with -u"); 792 793 if (session_id != -1) 794 xo_errx(1, "-i cannot be used with -A"); 795 if (vflag != 0) 796 xo_errx(1, "-v cannot be used with -A"); 797 798 } else if (Mflag != 0) { 799 if (session_id == -1) 800 xo_errx(1, "-M requires -i"); 801 802 if (nickname != NULL) { 803 if (enable != ENABLE_UNSPECIFIED) 804 xo_errx(1, "-n and -e are mutually exclusive"); 805 if (portal != NULL) 806 xo_errx(1, "-n and -p are mutually exclusive"); 807 if (target != NULL) 808 xo_errx(1, "-n and -t are mutually exclusive"); 809 if (user != NULL) 810 xo_errx(1, "-n and -u are mutually exclusive"); 811 if (secret != NULL) 812 xo_errx(1, "-n and -s are mutually exclusive"); 813 } 814 815 if (aflag != 0) 816 xo_errx(1, "-a cannot be used with -M"); 817 if (discovery_host != NULL) 818 xo_errx(1, "-d cannot be used with -M"); 819 if (rflag != 0) 820 xo_errx(1, "-r cannot be used with -M"); 821 if (vflag != 0) 822 xo_errx(1, "-v cannot be used with -M"); 823 if (timeout != -1) 824 xo_errx(1, "-w cannot be used with -M"); 825 826 } else if (Rflag != 0) { 827 if (aflag != 0) { 828 if (portal != NULL) 829 xo_errx(1, "-a and -p are mutually exclusive"); 830 if (target != NULL) 831 xo_errx(1, "-a and -t are mutually exclusive"); 832 if (nickname != NULL) 833 xo_errx(1, "-a and -n are mutually exclusive"); 834 } else if (nickname != NULL) { 835 if (portal != NULL) 836 xo_errx(1, "-n and -p are mutually exclusive"); 837 if (target != NULL) 838 xo_errx(1, "-n and -t are mutually exclusive"); 839 } else if (target == NULL && portal == NULL) { 840 xo_errx(1, "must specify either -a, -n, -t, or -p"); 841 } 842 843 if (discovery_host != NULL) 844 xo_errx(1, "-d cannot be used with -R"); 845 if (enable != ENABLE_UNSPECIFIED) 846 xo_errx(1, "-e cannot be used with -R"); 847 if (session_id != -1) 848 xo_errx(1, "-i cannot be used with -R"); 849 if (rflag != 0) 850 xo_errx(1, "-r cannot be used with -R"); 851 if (user != NULL) 852 xo_errx(1, "-u cannot be used with -R"); 853 if (secret != NULL) 854 xo_errx(1, "-s cannot be used with -R"); 855 if (vflag != 0) 856 xo_errx(1, "-v cannot be used with -R"); 857 if (timeout != -1) 858 xo_errx(1, "-w cannot be used with -R"); 859 860 } else { 861 assert(Lflag != 0); 862 863 if (discovery_host != NULL) 864 xo_errx(1, "-d cannot be used with -L"); 865 if (session_id != -1) 866 xo_errx(1, "-i cannot be used with -L"); 867 if (nickname != NULL) 868 xo_errx(1, "-n cannot be used with -L"); 869 if (portal != NULL) 870 xo_errx(1, "-p cannot be used with -L"); 871 if (rflag != 0) 872 xo_errx(1, "-r cannot be used with -L"); 873 if (target != NULL) 874 xo_errx(1, "-t cannot be used with -L"); 875 if (user != NULL) 876 xo_errx(1, "-u cannot be used with -L"); 877 if (secret != NULL) 878 xo_errx(1, "-s cannot be used with -L"); 879 } 880 881 iscsi_fd = open(ISCSI_PATH, O_RDWR); 882 if (iscsi_fd < 0 && errno == ENOENT) { 883 saved_errno = errno; 884 retval = kldload("iscsi"); 885 if (retval != -1) 886 iscsi_fd = open(ISCSI_PATH, O_RDWR); 887 else 888 errno = saved_errno; 889 } 890 if (iscsi_fd < 0) 891 xo_err(1, "failed to open %s", ISCSI_PATH); 892 893 if (Aflag != 0 && aflag != 0) { 894 conf = conf_new_from_file(conf_path); 895 896 TAILQ_FOREACH(targ, &conf->conf_targets, t_next) 897 failed += kernel_add(iscsi_fd, targ); 898 } else if (nickname != NULL) { 899 conf = conf_new_from_file(conf_path); 900 targ = target_find(conf, nickname); 901 if (targ == NULL) 902 xo_errx(1, "target %s not found in %s", 903 nickname, conf_path); 904 905 if (Aflag != 0) 906 failed += kernel_add(iscsi_fd, targ); 907 else if (Mflag != 0) 908 failed += kernel_modify(iscsi_fd, session_id, targ); 909 else if (Rflag != 0) 910 failed += kernel_remove(iscsi_fd, targ); 911 else 912 failed += kernel_list(iscsi_fd, targ, vflag); 913 } else if (Mflag != 0) { 914 kernel_modify_some(iscsi_fd, session_id, target, portal, 915 user, secret, enable); 916 } else { 917 if (Aflag != 0 && target != NULL) { 918 if (valid_iscsi_name(target, xo_warnx) == false) 919 xo_errx(1, "invalid target name \"%s\"", target); 920 } 921 conf = conf_new(); 922 targ = target_new(conf); 923 targ->t_initiator_name = default_initiator_name(); 924 targ->t_header_digest = DIGEST_NONE; 925 targ->t_data_digest = DIGEST_NONE; 926 targ->t_name = target; 927 if (discovery_host != NULL) { 928 targ->t_session_type = SESSION_TYPE_DISCOVERY; 929 targ->t_address = discovery_host; 930 } else { 931 targ->t_session_type = SESSION_TYPE_NORMAL; 932 targ->t_address = portal; 933 } 934 targ->t_enable = enable; 935 if (rflag != 0) 936 targ->t_protocol = PROTOCOL_ISER; 937 targ->t_user = user; 938 targ->t_secret = secret; 939 940 if (Aflag != 0) 941 failed += kernel_add(iscsi_fd, targ); 942 else if (Rflag != 0) 943 failed += kernel_remove(iscsi_fd, targ); 944 else 945 failed += kernel_list(iscsi_fd, targ, vflag); 946 } 947 948 if (timeout != -1) 949 failed += kernel_wait(iscsi_fd, timeout); 950 951 error = close(iscsi_fd); 952 if (error != 0) 953 xo_err(1, "close"); 954 955 xo_close_container("iscsictl"); 956 if (xo_finish() < 0) 957 xo_err(1, "stdout"); 958 959 if (failed != 0) 960 exit(1); 961 962 exit(0); 963 } 964