1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 #include <stdlib.h> 26 #include <stdio.h> 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <fcntl.h> 30 #include <unistd.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <getopt.h> 34 #include <strings.h> 35 #include <ctype.h> 36 #include <libnvpair.h> 37 #include <libintl.h> 38 #include <libgen.h> 39 #include <pwd.h> 40 #include <auth_attr.h> 41 #include <secdb.h> 42 #include <libscf.h> 43 #include <limits.h> 44 #include <locale.h> 45 46 #include <libstmf.h> 47 #include <libiscsit.h> 48 49 /* what's this used for?? */ 50 #define ITADM_VERSION "1.0" 51 52 /* SMF service info */ 53 #define ISCSIT_SVC "svc:/network/iscsi/target:default" 54 55 #define STMF_STALE(ret) {\ 56 if (ret == STMF_ERROR_PROV_DATA_STALE) {\ 57 (void) fprintf(stderr, "%s\n",\ 58 gettext("Configuration changed during processing. "\ 59 "Check the configuration, then retry this command "\ 60 "if appropriate."));\ 61 }\ 62 } 63 64 #define ITADM_CHKAUTH(sec) {\ 65 if (!chkauthattr(sec, itadm_uname)) {\ 66 (void) fprintf(stderr,\ 67 gettext("Error, operation requires authorization %s"),\ 68 sec);\ 69 (void) fprintf(stderr, "\n");\ 70 return (1);\ 71 }\ 72 } 73 74 75 static struct option itadm_long[] = { 76 {"alias", required_argument, NULL, 'l'}, 77 {"auth-method", required_argument, NULL, 'a'}, 78 {"chap-secret", no_argument, NULL, 's'}, 79 {"chap-secret-file", required_argument, NULL, 'S'}, 80 {"chap-user", required_argument, NULL, 'u'}, 81 {"force", no_argument, NULL, 'f'}, 82 {"help", no_argument, NULL, 'h'}, 83 {"help", no_argument, NULL, '?'}, 84 {"isns", required_argument, NULL, 'i'}, 85 {"isns-server", required_argument, NULL, 'I'}, 86 {"node-name", required_argument, NULL, 'n'}, 87 {"radius-secret", no_argument, NULL, 'd'}, 88 {"radius-secret-file", required_argument, NULL, 'D'}, 89 {"radius-server", required_argument, NULL, 'r'}, 90 {"tpg-tag", required_argument, NULL, 't'}, 91 {"verbose", no_argument, NULL, 'v'}, 92 {"version", no_argument, NULL, 'V'}, 93 {NULL, 0, NULL, 0} 94 }; 95 96 char c_tgt[] = "itadm create-target [-a radius|chap|none|default] [-s] \ 97 [-S chap-secret-path] [-u chap-username] [-n target-node-name] \ 98 [-l alias] [-t tpg-name[,tpg-name,...]]"; 99 100 static char m_tgt[] = "itadm modify-target [-a radius|chap|none|default] [-s] \ 101 [-S chap-secret-path] [-u chap-username] [-n new-target-node-name] \ 102 [-l alias] [-t tpg-name[,tpg-name,...]] target-node-name"; 103 104 static char d_tgt[] = "itadm delete-target [-f] target-node-name"; 105 106 static char l_tgt[] = "itadm list-target [-v] [target-node-name]"; 107 108 static char c_tpg[] = "itadm create-tpg tpg-name IP-address[:port] \ 109 [IP-address[:port]] [...]"; 110 111 static char l_tpg[] = "itadm list-tpg [-v] [tpg-name]"; 112 113 static char d_tpg[] = "itadm delete-tpg [-f] tpg-name"; 114 115 static char c_ini[] = "itadm create-initiator [-s] [-S chap-secret-path] \ 116 [-u chap-username] initiator-node-name"; 117 118 static char m_ini[] = "itadm modify-initiator [-s] [-S chap-secret-path] \ 119 [-u chap-username] initiator-node-name"; 120 121 static char l_ini[] = "itadm list-initiator [-v] initiator-node-name"; 122 123 static char d_ini[] = "itadm delete-inititator initiator-node-name"; 124 125 static char m_def[] = "itadm modify-defaults [-a radius|chap|none] \ 126 [-r IP-address[:port]] [-d] [-D radius-secret-path] [-i enable|disable] \ 127 [-I IP-address[:port][,IP-adddress[:port]]]"; 128 129 static char l_def[] = "itadm list-defaults"; 130 131 132 /* keep the order of this enum in the same order as the 'subcmds' struct */ 133 typedef enum { 134 CREATE_TGT, 135 MODIFY_TGT, 136 DELETE_TGT, 137 LIST_TGT, 138 CREATE_TPG, 139 DELETE_TPG, 140 LIST_TPG, 141 CREATE_INI, 142 MODIFY_INI, 143 LIST_INI, 144 DELETE_INI, 145 MODIFY_DEF, 146 LIST_DEF, 147 NULL_SUBCMD /* must always be last! */ 148 } itadm_sub_t; 149 150 typedef struct { 151 char *name; 152 char *shortopts; 153 char *usemsg; 154 } itadm_subcmds_t; 155 156 static itadm_subcmds_t subcmds[] = { 157 {"create-target", ":a:sS:u:n:l:t:h?", c_tgt}, 158 {"modify-target", ":a:sS:u:n:l:t:h?", m_tgt}, 159 {"delete-target", ":fh?", d_tgt}, 160 {"list-target", ":vh?", l_tgt}, 161 {"create-tpg", ":h?", c_tpg}, 162 {"delete-tpg", ":fh?", d_tpg}, 163 {"list-tpg", ":vh?", l_tpg}, 164 {"create-initiator", ":sS:u:h?", c_ini}, 165 {"modify-initiator", ":sS:u:h?", m_ini}, 166 {"list-initiator", ":vh?", l_ini}, 167 {"delete-initiator", ":h?", d_ini}, 168 {"modify-defaults", ":a:r:dD:i:I:h?", m_def}, 169 {"list-defaults", ":h?", l_def}, 170 {NULL, ":h?", NULL}, 171 }; 172 173 /* used for checking if user is authorized */ 174 static char *itadm_uname = NULL; 175 176 /* prototypes */ 177 static int 178 itadm_get_password(nvlist_t *nvl, char *key, char *passfile, 179 char *phrase); 180 181 static int 182 itadm_opt_to_arr(nvlist_t *nvl, char *key, char *opt, uint32_t *num); 183 184 static int 185 create_target(char *tgt, nvlist_t *proplist); 186 187 static int 188 modify_target(char *tgt, char *new, nvlist_t *proplist); 189 190 static int 191 delete_target(char *tgt, boolean_t force); 192 193 static int 194 list_target(char *tgt, boolean_t verbose, boolean_t script); 195 196 static int 197 create_tpg(char *tpg, int addrc, char **addrs); 198 199 static int 200 list_tpg(char *tpg, boolean_t verbose, boolean_t script); 201 202 static int 203 delete_tpg(char *tpg, boolean_t force); 204 205 static int 206 modify_initiator(char *ini, nvlist_t *proplist, boolean_t create); 207 208 static int 209 list_initiator(char *ini, boolean_t verbose, boolean_t script); 210 211 static int 212 delete_initiator(char *ini); 213 214 static int 215 modify_defaults(nvlist_t *proplist); 216 217 static int 218 list_defaults(boolean_t script); 219 220 static void 221 tag_name_to_num(char *tagname, uint16_t *tagnum); 222 223 /* prototype from iscsit_common.h */ 224 extern int 225 sockaddr_to_str(struct sockaddr_storage *sa, char **addr); 226 227 int 228 main(int argc, char *argv[]) 229 { 230 int ret = 0; 231 int idx = NULL_SUBCMD; 232 char c; 233 int newargc = argc; 234 char **newargv = NULL; 235 char *objp; 236 int itind = 0; 237 nvlist_t *proplist = NULL; 238 boolean_t verbose = B_FALSE; 239 boolean_t scripting = B_FALSE; 240 boolean_t tbool; 241 char *targetname = NULL; 242 char *propname; 243 boolean_t force = B_FALSE; 244 struct passwd *pwd = NULL; 245 uint32_t count = 0; 246 char *smfstate = NULL; 247 248 (void) setlocale(LC_ALL, ""); 249 (void) textdomain(TEXT_DOMAIN); 250 251 if (argc < 2) { 252 ret = 1; 253 goto usage_error; 254 } 255 256 for (idx = 0; subcmds[idx].name != NULL; idx++) { 257 if (strcmp(argv[1], subcmds[idx].name) == 0) { 258 break; 259 } 260 } 261 262 263 /* get the caller's user name for subsequent chkauthattr() calls */ 264 pwd = getpwuid(getuid()); 265 if (pwd == NULL) { 266 (void) fprintf(stderr, "%s\n", 267 gettext("Could not determine callers user name.")); 268 return (1); 269 } 270 271 itadm_uname = strdup(pwd->pw_name); 272 273 /* increment past command & subcommand */ 274 newargc--; 275 newargv = &(argv[1]); 276 277 ret = nvlist_alloc(&proplist, NV_UNIQUE_NAME, 0); 278 if (ret != 0) { 279 ret = errno; 280 (void) fprintf(stderr, 281 gettext("Could not allocate nvlist, errno = %d"), 282 ret); 283 (void) fprintf(stderr, "\n"); 284 ret = 1; 285 goto usage_error; 286 } 287 288 while ((ret == 0) && (newargv)) { 289 c = getopt_long(newargc, newargv, subcmds[idx].shortopts, 290 itadm_long, &itind); 291 if (c == -1) { 292 break; 293 } 294 295 switch (c) { 296 case 0: 297 /* flag set by getopt */ 298 break; 299 case 'a': 300 ret = nvlist_add_string(proplist, 301 "auth", optarg); 302 break; 303 case 'd': 304 ret = itadm_get_password(proplist, 305 "radiussecret", NULL, 306 gettext("Enter RADIUS secret: ")); 307 break; 308 case 'D': 309 ret = itadm_get_password(proplist, 310 "radiussecret", optarg, NULL); 311 break; 312 case 'f': 313 force = B_TRUE; 314 break; 315 case '?': 316 /* 317 * '?' is returned for both unrecognized 318 * options and if explicitly provided on 319 * the command line. The latter should 320 * be handled the same as -h. 321 */ 322 if (strcmp(newargv[optind-1], "-?") != 0) { 323 (void) fprintf(stderr, 324 gettext("Unrecognized option %s"), 325 newargv[optind-1]); 326 (void) fprintf(stderr, "\n"); 327 ret = 1; 328 } 329 goto usage_error; 330 case 'h': 331 goto usage_error; 332 case 'i': 333 if (strncmp(optarg, "enable", strlen(optarg)) 334 == 0) { 335 tbool = B_TRUE; 336 } else if (strncmp(optarg, "disable", 337 strlen(optarg)) == 0) { 338 tbool = B_FALSE; 339 } else { 340 (void) fprintf(stderr, "%s\n", 341 gettext("invalid value for -i")); 342 ret = 1; 343 break; 344 } 345 ret = nvlist_add_boolean_value(proplist, 346 "isns", tbool); 347 break; 348 case 'I': 349 /* possibly multi-valued */ 350 ret = itadm_opt_to_arr(proplist, 351 "isnsserver", optarg, &count); 352 if ((ret == 0) && (count > 8)) { 353 (void) fprintf(stderr, "%s\n", 354 gettext( 355 "Too many iSNS servers specified, " 356 "maximum of 8 allowed")); 357 ret = 1; 358 } 359 break; 360 case 'l': 361 ret = nvlist_add_string(proplist, 362 "alias", optarg); 363 break; 364 case 'n': 365 targetname = strdup(optarg); 366 if (targetname == NULL) { 367 ret = ENOMEM; 368 } 369 break; 370 case 'r': 371 ret = nvlist_add_string(proplist, 372 "radiusserver", optarg); 373 break; 374 case 's': 375 if ((idx == CREATE_TGT) || 376 (idx == MODIFY_TGT)) { 377 propname = "targetchapsecret"; 378 } else { 379 propname = "chapsecret"; 380 } 381 ret = itadm_get_password(proplist, 382 propname, NULL, 383 gettext("Enter CHAP secret: ")); 384 break; 385 case 'S': 386 if ((idx == CREATE_TGT) || 387 (idx == MODIFY_TGT)) { 388 propname = "targetchapsecret"; 389 } else { 390 propname = "chapsecret"; 391 } 392 ret = itadm_get_password(proplist, 393 propname, optarg, NULL); 394 break; 395 case 't': 396 /* possibly multi-valued */ 397 ret = itadm_opt_to_arr(proplist, 398 "tpg-tag", optarg, NULL); 399 break; 400 case 'u': 401 if ((idx == CREATE_TGT) || 402 (idx == MODIFY_TGT)) { 403 propname = "targetchapuser"; 404 } else { 405 propname = "chapuser"; 406 } 407 ret = nvlist_add_string(proplist, 408 propname, optarg); 409 break; 410 case 'v': 411 verbose = B_TRUE; 412 break; 413 case ':': 414 (void) fprintf(stderr, 415 gettext("Option %s requires an operand."), 416 newargv[optind-1]); 417 (void) fprintf(stderr, "\n"); 418 419 /* fall through to default */ 420 default: 421 ret = 1; 422 break; 423 } 424 } 425 426 if (ret != 0) { 427 goto usage_error; 428 } 429 430 /* after getopt() to allow handling of -h option */ 431 if ((itadm_sub_t)idx == NULL_SUBCMD) { 432 (void) fprintf(stderr, "%s\n", 433 gettext("Error, no subcommand specified")); 434 ret = 1; 435 goto usage_error; 436 } 437 438 /* 439 * some subcommands take multiple operands, so adjust now that 440 * getopt is complete 441 */ 442 newargc -= optind; 443 if (newargc == 0) { 444 newargv = NULL; 445 objp = NULL; 446 } else { 447 newargv = &(newargv[optind]); 448 objp = newargv[0]; 449 } 450 451 if (objp == NULL) { 452 switch ((itadm_sub_t)idx) { 453 case MODIFY_TGT: 454 case DELETE_TGT: 455 case CREATE_TPG: 456 case DELETE_TPG: 457 case CREATE_INI: 458 case MODIFY_INI: 459 case DELETE_INI: 460 /* These subcommands need at least one operand */ 461 (void) fprintf(stderr, 462 gettext("Error, %s requires an operand"), 463 subcmds[idx].name); 464 (void) fprintf(stderr, "\n"); 465 466 ret = 1; 467 goto usage_error; 468 default: 469 break; 470 } 471 } 472 473 if (newargc > 1) { 474 switch ((itadm_sub_t)idx) { 475 case MODIFY_TGT: 476 case DELETE_TGT: 477 case LIST_TGT: 478 case DELETE_TPG: 479 case LIST_TPG: 480 case CREATE_INI: 481 case MODIFY_INI: 482 case LIST_INI: 483 case DELETE_INI: 484 /* These subcommands should have at most one operand */ 485 (void) fprintf(stderr, 486 gettext("Error, %s accepts only a single operand"), 487 subcmds[idx].name); 488 (void) fprintf(stderr, "\n"); 489 490 ret = 1; 491 goto usage_error; 492 493 default: 494 break; 495 } 496 } 497 498 if (newargc > 0) { 499 switch ((itadm_sub_t)idx) { 500 case CREATE_TGT: 501 case MODIFY_DEF: 502 case LIST_DEF: 503 /* These subcommands do not support an operand */ 504 (void) fprintf(stderr, 505 gettext("Error, %s does not support any operands"), 506 subcmds[idx].name); 507 (void) fprintf(stderr, "\n"); 508 509 ret = 1; 510 goto usage_error; 511 512 default: 513 break; 514 } 515 } 516 517 /* 518 * XXX - this should probably get pushed down to the library 519 * depending on the decision to allow/disallow configuratoin 520 * without the service running. 521 */ 522 /* 523 * Make sure iSCSI target service is enabled before 524 * proceeding. 525 */ 526 smfstate = smf_get_state(ISCSIT_SVC); 527 if (!smfstate || 528 (strcmp(smfstate, SCF_STATE_STRING_ONLINE) != 0)) { 529 (void) fprintf(stderr, "%s\n", 530 gettext("The iSCSI target service must be online " 531 "before running this command.")); 532 (void) fprintf(stderr, 533 gettext("Use 'svcadm enable -r %s'"), ISCSIT_SVC); 534 (void) fprintf(stderr, "\n"); 535 (void) fprintf(stderr, "%s\n", 536 gettext("to enable the service and its prerequisite " 537 "services and/or")); 538 (void) fprintf(stderr, 539 gettext("'svcs -x %s' to determine why it is not online."), 540 ISCSIT_SVC); 541 (void) fprintf(stderr, "\n"); 542 543 return (1); 544 } 545 546 switch ((itadm_sub_t)idx) { 547 case CREATE_TGT: 548 /* 549 * OK for targetname to be NULL here. If the 550 * user did not specify a target name, 551 * one will be generated. 552 */ 553 ret = create_target(targetname, proplist); 554 break; 555 case MODIFY_TGT: 556 ret = modify_target(objp, targetname, proplist); 557 break; 558 case DELETE_TGT: 559 ret = delete_target(objp, force); 560 break; 561 case LIST_TGT: 562 ret = list_target(objp, verbose, scripting); 563 break; 564 case CREATE_TPG: 565 ret = create_tpg(objp, newargc - 1, &(newargv[1])); 566 break; 567 case DELETE_TPG: 568 ret = delete_tpg(objp, force); 569 break; 570 case LIST_TPG: 571 ret = list_tpg(objp, verbose, scripting); 572 break; 573 case CREATE_INI: 574 ret = modify_initiator(objp, proplist, B_TRUE); 575 break; 576 case MODIFY_INI: 577 ret = modify_initiator(objp, proplist, B_FALSE); 578 break; 579 case LIST_INI: 580 ret = list_initiator(objp, verbose, scripting); 581 break; 582 case DELETE_INI: 583 ret = delete_initiator(objp); 584 break; 585 case MODIFY_DEF: 586 ret = modify_defaults(proplist); 587 break; 588 case LIST_DEF: 589 ret = list_defaults(scripting); 590 break; 591 default: 592 ret = 1; 593 goto usage_error; 594 } 595 596 if (ret != 0) { 597 (void) fprintf(stderr, 598 gettext("itadm %s failed with error %d"), 599 subcmds[idx].name, ret); 600 (void) fprintf(stderr, "\n"); 601 } 602 return (ret); 603 604 usage_error: 605 if (subcmds[idx].name) { 606 (void) printf("%s\n", gettext(subcmds[idx].usemsg)); 607 } else { 608 /* overall usage */ 609 (void) printf("%s\n\n", gettext("itadm usage:")); 610 for (idx = 0; subcmds[idx].name != NULL; idx++) { 611 if (!subcmds[idx].usemsg) { 612 continue; 613 } 614 (void) printf("\t%s\n", gettext(subcmds[idx].usemsg)); 615 } 616 } 617 618 return (ret); 619 } 620 621 static int 622 create_target(char *tgt, nvlist_t *proplist) 623 { 624 int ret; 625 it_config_t *cfg = NULL; 626 it_tgt_t *tgtp; 627 char **tags = NULL; 628 uint32_t count = 0; 629 nvlist_t *errlist = NULL; 630 int i; 631 it_tpg_t *tpg = NULL; 632 uint16_t tagid = 0; 633 it_tpgt_t *tpgt; 634 char *sec = "solaris.smf.modify.stmf"; 635 boolean_t did_it_config_load = B_FALSE; 636 637 ITADM_CHKAUTH(sec); 638 639 if (tgt) { 640 /* 641 * Validate target name. 642 */ 643 if (!IS_IQN_NAME(tgt) && !IS_EUI_NAME(tgt)) { 644 (void) fprintf(stderr, gettext("Invalid name %s"), 645 tgt); 646 (void) fprintf(stderr, "\n"); 647 return (EINVAL); 648 } 649 } 650 651 ret = it_config_load(&cfg); 652 if (ret != 0) { 653 (void) fprintf(stderr, 654 gettext("Error retrieving iSCSI target configuration: %d"), 655 ret); 656 (void) fprintf(stderr, "\n"); 657 goto done; 658 } 659 660 did_it_config_load = B_TRUE; 661 662 ret = it_tgt_create(cfg, &tgtp, tgt); 663 if (ret != 0) { 664 if (ret == EFAULT) { 665 (void) fprintf(stderr, 666 gettext("Invalid iSCSI name %s"), tgt); 667 } else if (ret == EEXIST) { 668 (void) fprintf(stderr, 669 gettext("iSCSI target %s already configured"), 670 tgt); 671 } else if (ret == E2BIG) { 672 (void) fprintf(stderr, 673 gettext("Maximum of %d iSCSI targets"), 674 MAX_TARGETS); 675 } else { 676 (void) fprintf(stderr, 677 gettext("Error creating target: %d"), ret); 678 } 679 (void) fprintf(stderr, "\n"); 680 goto done; 681 } 682 683 /* set the target portal group tags */ 684 ret = nvlist_lookup_string_array(proplist, "tpg-tag", &tags, 685 &count); 686 687 if (ret == ENOENT) { 688 /* none specified. is this ok? */ 689 ret = 0; 690 } else if (ret != 0) { 691 (void) fprintf(stderr, 692 gettext("internal error: %d"), ret); 693 (void) fprintf(stderr, "\n"); 694 goto done; 695 } 696 697 /* special case, don't set any TPGs */ 698 if (tags && (count == 1) && (strcmp("default", tags[0]) == 0)) { 699 count = 0; 700 } 701 702 for (i = 0; i < count; i++) { 703 if (!tags[i]) { 704 continue; 705 } 706 707 /* see that all referenced groups are already defined */ 708 tpg = cfg->config_tpg_list; 709 while (tpg != NULL) { 710 if (strcmp(tags[i], tpg->tpg_name) == 0) { 711 break; 712 } 713 714 tpg = tpg->tpg_next; 715 } 716 if (tpg == NULL) { 717 (void) fprintf(stderr, 718 gettext("Invalid tpg-tag %s, tag not defined"), 719 tags[i]); 720 (void) fprintf(stderr, "\n"); 721 ret = 1; 722 goto done; 723 } 724 725 /* generate the tag number to use */ 726 tag_name_to_num(tags[i], &tagid); 727 728 ret = it_tpgt_create(cfg, tgtp, &tpgt, tags[i], tagid); 729 if (ret != 0) { 730 (void) fprintf(stderr, 731 gettext("Could not add target portal group" 732 "tag %s, error %d"), tags[i], ret); 733 (void) fprintf(stderr, "\n"); 734 goto done; 735 } 736 tagid++; 737 } 738 739 /* remove the tags from the proplist before continuing */ 740 if (tags) { 741 (void) nvlist_remove_all(proplist, "tpg-tag"); 742 } 743 744 ret = it_tgt_setprop(cfg, tgtp, proplist, &errlist); 745 if (ret != 0) { 746 (void) fprintf(stderr, 747 gettext("Error setting target properties, %d"), ret); 748 (void) fprintf(stderr, "\n"); 749 if (errlist) { 750 nvpair_t *nvp = NULL; 751 char *nn; 752 char *nv; 753 754 while ((nvp = nvlist_next_nvpair(errlist, nvp)) 755 != NULL) { 756 nv = NULL; 757 758 nn = nvpair_name(nvp); 759 (void) nvpair_value_string(nvp, &nv); 760 761 if (nv != NULL) { 762 (void) fprintf(stderr, "\t%s: %s\n", 763 nn, nv); 764 } 765 } 766 767 nvlist_free(errlist); 768 } 769 goto done; 770 } 771 772 if (ret == 0) { 773 ret = it_config_commit(cfg); 774 STMF_STALE(ret); 775 } 776 777 done: 778 if (ret == 0) { 779 (void) printf(gettext("Target %s successfully created"), 780 tgtp->tgt_name); 781 (void) printf("\n"); 782 } 783 784 if (did_it_config_load) 785 it_config_free(cfg); 786 787 return (ret); 788 } 789 790 int 791 list_target(char *tgt, boolean_t verbose, boolean_t script) 792 { 793 int ret; 794 it_config_t *cfg; 795 it_tgt_t *ptr; 796 boolean_t found = B_FALSE; 797 boolean_t first = B_TRUE; 798 boolean_t first_tag = B_TRUE; 799 char *gauth = "none"; 800 char *galias = "-"; 801 char *auth; 802 char *alias; 803 char *chapu; 804 char *chaps; 805 it_tpgt_t *tagp; 806 char *sec = "solaris.smf.read.stmf"; 807 stmfDevid devid; 808 stmfSessionList *sess = NULL; 809 stmfTargetProperties props; 810 char *state; 811 int num_sessions; 812 813 ITADM_CHKAUTH(sec); 814 815 ret = it_config_load(&cfg); 816 if (ret != 0) { 817 (void) fprintf(stderr, 818 gettext("Error retrieving iSCSI target configuration: %d"), 819 ret); 820 (void) fprintf(stderr, "\n"); 821 return (ret); 822 } 823 824 ptr = cfg->config_tgt_list; 825 826 /* grab global defaults for auth, alias */ 827 if (cfg->config_global_properties) { 828 (void) nvlist_lookup_string(cfg->config_global_properties, 829 "alias", &galias); 830 (void) nvlist_lookup_string(cfg->config_global_properties, 831 "auth", &gauth); 832 } 833 834 for (; ptr != NULL; ptr = ptr->tgt_next) { 835 if (found) { 836 break; 837 } 838 839 if (tgt) { 840 /* 841 * We do a case-insensitive match in case 842 * a non-lower case value got stored. 843 */ 844 if (strcasecmp(tgt, ptr->tgt_name) != 0) { 845 continue; 846 } else { 847 found = B_TRUE; 848 } 849 } 850 851 state = "-"; 852 num_sessions = 0; 853 sess = NULL; 854 855 /* 856 * make a best effort to retrieve target status and 857 * number of active sessions from STMF. 858 */ 859 ret = stmfDevidFromIscsiName(ptr->tgt_name, &devid); 860 if (ret == STMF_STATUS_SUCCESS) { 861 ret = stmfGetTargetProperties(&devid, &props); 862 if (ret == STMF_STATUS_SUCCESS) { 863 if (props.status == STMF_TARGET_PORT_ONLINE) { 864 state = "online"; 865 } else { 866 state = "offline"; 867 } 868 } 869 } 870 if (ret == STMF_STATUS_SUCCESS) { 871 ret = stmfGetSessionList(&devid, &sess); 872 if (ret == STMF_STATUS_SUCCESS) { 873 num_sessions = sess->cnt; 874 free(sess); 875 } 876 } 877 878 /* reset ret so we don't return an error */ 879 ret = 0; 880 881 if (!script && first) { 882 (void) printf("%-61s%-9s%-9s\n", "TARGET NAME", 883 "STATE", "SESSIONS"); 884 first = B_FALSE; 885 } 886 887 if (!script) { 888 /* 889 * try not to let columns run into each other. 890 * Stick a tab after too-long fields. 891 * Lengths chosen are for the 'common' cases. 892 */ 893 (void) printf("%-61s", ptr->tgt_name); 894 if (strlen(ptr->tgt_name) > 60) { 895 (void) printf("\t"); 896 } 897 (void) printf("%-9s%-9d", state, num_sessions); 898 } else { 899 (void) printf("%s\t%s\t%d", ptr->tgt_name, 900 state, num_sessions); 901 } 902 903 if (!verbose) { 904 (void) printf("\n"); 905 continue; 906 } 907 908 auth = gauth; 909 alias = galias; 910 chapu = "-"; 911 chaps = "unset"; 912 913 if (ptr->tgt_properties) { 914 (void) nvlist_lookup_string(ptr->tgt_properties, 915 "auth", &auth); 916 (void) nvlist_lookup_string(ptr->tgt_properties, 917 "alias", &alias); 918 if (nvlist_exists(ptr->tgt_properties, 919 "targetchapsecret")) { 920 chaps = "set"; 921 } 922 (void) nvlist_lookup_string(ptr->tgt_properties, 923 "targetchapuser", &chapu); 924 } 925 926 if (!script) { 927 (void) printf("\n\t%-20s\t%s\n\t%-20s\t%s %s\n" 928 "\t%-20s\t%s\n\t%-20s\t%s\n\t%-20s\t", 929 "alias:", alias, "auth:", auth, 930 ((auth == gauth) ? "(defaults)" : ""), 931 "targetchapuser:", 932 chapu, "targetchapsecret:", chaps, "tpg-tags:"); 933 } else { 934 (void) printf("\t%s\t%s %s\t%s\t%s\t", 935 alias, auth, 936 ((auth == gauth) ? "(defaults)" : ""), 937 chapu, chaps); 938 } 939 940 first_tag = B_TRUE; 941 tagp = ptr->tgt_tpgt_list; 942 for (; tagp != NULL; tagp = tagp->tpgt_next) { 943 if (!first_tag) { 944 (void) printf(","); 945 } else { 946 first_tag = B_FALSE; 947 } 948 (void) printf("%s = %d", 949 tagp->tpgt_tpg_name, tagp->tpgt_tag); 950 } 951 952 if (first_tag) { 953 /* didn't find any */ 954 (void) printf("default"); 955 } 956 957 (void) printf("\n"); 958 } 959 960 if (tgt && (!found)) { 961 (void) fprintf(stderr, 962 gettext("Target %s not found!\n"), tgt); 963 (void) fprintf(stderr, "\n"); 964 ret = 1; 965 } 966 967 it_config_free(cfg); 968 969 return (ret); 970 } 971 972 int 973 delete_target(char *tgt, boolean_t force) 974 { 975 int ret; 976 it_config_t *cfg; 977 it_tgt_t *ptr; 978 char *sec = "solaris.smf.modify.stmf"; 979 980 ITADM_CHKAUTH(sec); 981 982 if (!tgt) { 983 (void) fprintf(stderr, "%s\n", 984 gettext("Error, no target specified")); 985 return (EINVAL); 986 } 987 988 ret = it_config_load(&cfg); 989 if (ret != 0) { 990 (void) fprintf(stderr, 991 gettext("Error retrieving iSCSI target configuration: %d"), 992 ret); 993 (void) fprintf(stderr, "\n"); 994 return (ret); 995 } 996 997 ptr = cfg->config_tgt_list; 998 while (ptr) { 999 /* 1000 * We do a case-insensitive match in case 1001 * a non-lower case value got stored. 1002 */ 1003 if (strcasecmp(ptr->tgt_name, tgt) == 0) { 1004 break; 1005 } 1006 1007 ptr = ptr->tgt_next; 1008 } 1009 1010 if (ptr) { 1011 ret = it_tgt_delete(cfg, ptr, force); 1012 1013 if (ret != 0) { 1014 if (ret == EBUSY) { 1015 (void) fprintf(stderr, 1016 gettext("The target is online or busy. " 1017 "Use the -f (force) option, or " 1018 "'stmfadm offline-target %s'"), tgt); 1019 (void) fprintf(stderr, "\n"); 1020 } 1021 } 1022 1023 if (ret == 0) { 1024 ret = it_config_commit(cfg); 1025 STMF_STALE(ret); 1026 } 1027 } else { 1028 (void) fprintf(stderr, 1029 gettext("Target %s not found"), tgt); 1030 (void) fprintf(stderr, "\n"); 1031 ret = 1; 1032 } 1033 1034 it_config_free(cfg); 1035 1036 return (ret); 1037 } 1038 1039 static int 1040 modify_target(char *tgt, char *newname, nvlist_t *proplist) 1041 { 1042 int ret; 1043 it_config_t *cfg = NULL; 1044 it_tgt_t *ptr = NULL; 1045 it_tgt_t *tgtp = NULL; 1046 char **tags = NULL; 1047 uint32_t count = 0; 1048 nvlist_t *errlist = NULL; 1049 int i; 1050 it_tpg_t *tpg = NULL; 1051 uint16_t tagid; 1052 it_tpgt_t *tpgt = NULL; 1053 char *sec = "solaris.smf.modify.stmf"; 1054 boolean_t did_it_config_load = B_FALSE; 1055 1056 ITADM_CHKAUTH(sec); 1057 1058 /* XXX: Do we need to offline anything here too? */ 1059 1060 if (!tgt) { 1061 (void) fprintf(stderr, "%s\n", 1062 gettext("Error, no target specified")); 1063 ret = EINVAL; 1064 goto done; 1065 } 1066 1067 ret = it_config_load(&cfg); 1068 if (ret != 0) { 1069 (void) fprintf(stderr, 1070 gettext("Error retrieving iSCSI target configuration: %d"), 1071 ret); 1072 (void) fprintf(stderr, "\n"); 1073 goto done; 1074 } 1075 1076 did_it_config_load = B_TRUE; 1077 1078 /* 1079 * If newname is specified, ensure it is a valid name. 1080 */ 1081 if (newname) { 1082 if (!validate_iscsi_name(newname)) { 1083 (void) fprintf(stderr, 1084 gettext("Invalid iSCSI name %s"), newname); 1085 (void) fprintf(stderr, "\n"); 1086 ret = 1; 1087 goto done; 1088 } 1089 } 1090 1091 /* 1092 * Loop through to verify that the target to be modified truly 1093 * exists. If this target is to be renamed, ensure the new 1094 * name is not already in use. 1095 */ 1096 ptr = cfg->config_tgt_list; 1097 while (ptr) { 1098 /* 1099 * Does a target with the new name already exist? 1100 */ 1101 if (newname && 1102 (strcasecmp(newname, ptr->tgt_name) == 0)) { 1103 (void) fprintf(stderr, 1104 gettext("A target with name %s already exists"), 1105 newname); 1106 (void) fprintf(stderr, "\n"); 1107 ret = 1; 1108 goto done; 1109 } 1110 1111 if (strcasecmp(ptr->tgt_name, tgt) == 0) { 1112 tgtp = ptr; 1113 } 1114 1115 ptr = ptr ->tgt_next; 1116 } 1117 1118 if (!tgtp) { 1119 (void) fprintf(stderr, 1120 gettext("Target %s not found"), tgt); 1121 (void) fprintf(stderr, "\n"); 1122 ret = EINVAL; 1123 goto done; 1124 } 1125 1126 /* set the target portal group tags */ 1127 ret = nvlist_lookup_string_array(proplist, "tpg-tag", &tags, 1128 &count); 1129 1130 if (ret == ENOENT) { 1131 /* none specified. is this ok? */ 1132 ret = 0; 1133 } else if (ret != 0) { 1134 (void) fprintf(stderr, 1135 gettext("internal error: %d"), ret); 1136 (void) fprintf(stderr, "\n"); 1137 goto done; 1138 } 1139 1140 /* special case, remove all explicit TPGs, and don't add any */ 1141 if (tags && (count == 1) && (strcmp("default", tags[0]) == 0)) { 1142 count = 0; 1143 } 1144 1145 for (i = 0; i < count; i++) { 1146 if (!tags || !tags[i]) { 1147 continue; 1148 } 1149 1150 /* see that all referenced groups are already defined */ 1151 tpg = cfg->config_tpg_list; 1152 while (tpg != NULL) { 1153 if (strcmp(tags[i], tpg->tpg_name) == 0) { 1154 break; 1155 } 1156 tpg = tpg->tpg_next; 1157 } 1158 if (tpg == NULL) { 1159 (void) fprintf(stderr, 1160 gettext("Invalid tpg-name %s: not defined"), 1161 tags[i]); 1162 (void) fprintf(stderr, "\n"); 1163 ret = 1; 1164 goto done; 1165 } 1166 } 1167 1168 /* 1169 * don't recreate tags that are already associated, 1170 * remove tags not requested. 1171 */ 1172 if (tags) { 1173 tpgt = tgtp->tgt_tpgt_list; 1174 while (tpgt) { 1175 for (i = 0; i < count; i++) { 1176 if (!tags[i]) { 1177 continue; 1178 } 1179 1180 if (strcmp(tpgt->tpgt_tpg_name, tags[i]) 1181 == 0) { 1182 /* non-null tags will be created */ 1183 tags[i] = NULL; 1184 break; 1185 } 1186 } 1187 if (i == count) { 1188 /* one to remove */ 1189 it_tpgt_t *ptr = tpgt; 1190 1191 tpgt = ptr->tpgt_next; 1192 it_tpgt_delete(cfg, tgtp, ptr); 1193 } else { 1194 tpgt = tpgt->tpgt_next; 1195 } 1196 } 1197 } 1198 1199 /* see if there are any left to add */ 1200 for (i = 0; i < count; i++) { 1201 if (!tags || !tags[i]) { 1202 continue; 1203 } 1204 1205 /* generate the tag number to use */ 1206 tag_name_to_num(tags[i], &tagid); 1207 1208 ret = it_tpgt_create(cfg, tgtp, &tpgt, tags[i], tagid); 1209 if (ret != 0) { 1210 if (ret == E2BIG) { 1211 (void) fprintf(stderr, "%s\n", 1212 gettext("Error, no portal tag available")); 1213 } else { 1214 (void) fprintf(stderr, gettext( 1215 "Could not add target portal group" 1216 " tag %s, error %d"), tags[i], ret); 1217 (void) fprintf(stderr, "\n"); 1218 } 1219 goto done; 1220 } 1221 } 1222 1223 /* remove the tags from the proplist before continuing */ 1224 (void) nvlist_remove_all(proplist, "tpg-tag"); 1225 1226 /* 1227 * Rename this target, if requested. Save the old name in 1228 * the property list, so the kernel knows this is a renamed 1229 * target, and not a new one. 1230 */ 1231 if (newname && (strlen(newname) > 0)) { 1232 ret = nvlist_add_string(proplist, "oldtargetname", 1233 tgtp->tgt_name); 1234 if (ret != 0) { 1235 (void) fprintf(stderr, "%s\n", 1236 gettext("Error renaming target.")); 1237 goto done; 1238 } 1239 (void) strlcpy(tgtp->tgt_name, newname, 1240 sizeof (tgtp->tgt_name)); 1241 } 1242 1243 ret = it_tgt_setprop(cfg, tgtp, proplist, &errlist); 1244 if (ret != 0) { 1245 (void) fprintf(stderr, 1246 gettext("Error setting target properties: %d"), ret); 1247 (void) fprintf(stderr, "\n"); 1248 if (errlist) { 1249 nvpair_t *nvp = NULL; 1250 char *nn; 1251 char *nv; 1252 1253 while ((nvp = nvlist_next_nvpair(errlist, nvp)) 1254 != NULL) { 1255 nv = NULL; 1256 1257 nn = nvpair_name(nvp); 1258 (void) nvpair_value_string(nvp, &nv); 1259 1260 if (nv != NULL) { 1261 (void) fprintf(stderr, "\t%s: %s\n", 1262 nn, nv); 1263 } 1264 } 1265 1266 nvlist_free(errlist); 1267 } 1268 goto done; 1269 } 1270 1271 if (ret == 0) { 1272 ret = it_config_commit(cfg); 1273 STMF_STALE(ret); 1274 } 1275 1276 done: 1277 if (ret == 0) { 1278 (void) printf(gettext("Target %s successfully modified"), 1279 tgtp->tgt_name); 1280 (void) printf("\n"); 1281 } 1282 1283 if (did_it_config_load) 1284 it_config_free(cfg); 1285 1286 return (ret); 1287 } 1288 1289 int 1290 create_tpg(char *tpg, int addrc, char **addrs) 1291 { 1292 int ret; 1293 it_config_t *cfg; 1294 it_tpg_t *tpgp; 1295 int count = 0; 1296 it_portal_t *ptl; 1297 char *sec = "solaris.smf.modify.stmf"; 1298 int i = 0; 1299 1300 ITADM_CHKAUTH(sec); 1301 1302 if (!tpg) { 1303 (void) fprintf(stderr, "%s\n", 1304 gettext("Error, no target portal group specified")); 1305 return (EINVAL); 1306 } 1307 1308 if (strlen(tpg) > (MAX_TPG_NAMELEN - 1)) { 1309 (void) fprintf(stderr, 1310 gettext("Target Portal Group name must be no longer " 1311 "than %d characters."), (MAX_TPG_NAMELEN - 1)); 1312 (void) fprintf(stderr, "\n"); 1313 return (EINVAL); 1314 } 1315 1316 if (!addrs || (addrc <= 0)) { 1317 (void) fprintf(stderr, "%s\n", 1318 gettext("Error, no portal addresses specified")); 1319 return (EINVAL); 1320 } 1321 1322 ret = it_config_load(&cfg); 1323 if (ret != 0) { 1324 (void) fprintf(stderr, 1325 gettext("Error retrieving iSCSI target configuration: %d"), 1326 ret); 1327 (void) fprintf(stderr, "\n"); 1328 return (ret); 1329 } 1330 1331 tpgp = cfg->config_tpg_list; 1332 while (tpgp != NULL) { 1333 if (strcmp(tpgp->tpg_name, tpg) == 0) { 1334 (void) fprintf(stderr, 1335 gettext("Target Portal Group %s already exists"), 1336 tpg); 1337 (void) fprintf(stderr, "\n"); 1338 it_config_free(cfg); 1339 return (1); 1340 } 1341 tpgp = tpgp->tpg_next; 1342 } 1343 1344 /* 1345 * Ensure that the addrs don't contain commas. 1346 */ 1347 for (i = 0; i < addrc; i++) { 1348 if (strchr(addrs[i], ',')) { 1349 (void) fprintf(stderr, 1350 gettext("Bad portal name %s"), 1351 addrs[i]); 1352 (void) fprintf(stderr, "\n"); 1353 1354 it_config_free(cfg); 1355 return (EINVAL); 1356 } 1357 } 1358 1359 /* 1360 * Create the portal group and first portal 1361 */ 1362 ret = it_tpg_create(cfg, &tpgp, tpg, addrs[count]); 1363 if (ret != 0) { 1364 if (ret == EEXIST) { 1365 (void) fprintf(stderr, 1366 gettext("Portal %s already in use"), 1367 addrs[count]); 1368 (void) fprintf(stderr, "\n"); 1369 } 1370 it_config_free(cfg); 1371 return (ret); 1372 } 1373 1374 /* 1375 * Add the remaining portals 1376 */ 1377 for (count = 1; count < addrc; count++) { 1378 if (!addrs[count]) { 1379 continue; 1380 } 1381 1382 ret = it_portal_create(cfg, tpgp, &ptl, addrs[count]); 1383 if (ret != 0) { 1384 if (ret == EEXIST) { 1385 (void) fprintf(stderr, 1386 gettext("Portal %s already in use"), 1387 addrs[count]); 1388 (void) fprintf(stderr, "\n"); 1389 } else { 1390 (void) fprintf(stderr, 1391 gettext("Error adding portal %s: %d"), 1392 addrs[count], ret); 1393 (void) fprintf(stderr, "\n"); 1394 break; 1395 } 1396 } 1397 } 1398 1399 if (ret == 0) { 1400 ret = it_config_commit(cfg); 1401 STMF_STALE(ret); 1402 } 1403 1404 it_config_free(cfg); 1405 1406 return (ret); 1407 } 1408 1409 static int 1410 list_tpg(char *tpg, boolean_t verbose, boolean_t script) 1411 { 1412 int ret; 1413 it_config_t *cfg; 1414 it_tpg_t *ptr; 1415 boolean_t found = B_FALSE; 1416 it_portal_t *portal; 1417 boolean_t first = B_TRUE; 1418 boolean_t first_portal; 1419 char *pstr; 1420 char *sec = "solaris.smf.read.stmf"; 1421 1422 ITADM_CHKAUTH(sec); 1423 1424 ret = it_config_load(&cfg); 1425 if (ret != 0) { 1426 (void) fprintf(stderr, 1427 gettext("Error retrieving iSCSI target configuration: %d"), 1428 ret); 1429 (void) fprintf(stderr, "\n"); 1430 return (ret); 1431 } 1432 1433 ptr = cfg->config_tpg_list; 1434 1435 for (; ptr != NULL; ptr = ptr->tpg_next) { 1436 if (found) { 1437 break; 1438 } 1439 1440 if (tpg) { 1441 if (strcmp(tpg, ptr->tpg_name) != 0) { 1442 continue; 1443 } else { 1444 found = B_TRUE; 1445 } 1446 } 1447 1448 if (!script && first) { 1449 (void) printf("%-30s%-9s\n", "TARGET PORTAL GROUP", 1450 "PORTAL COUNT"); 1451 first = B_FALSE; 1452 } 1453 1454 if (!script) { 1455 (void) printf("%-30s", ptr->tpg_name); 1456 if (strlen(ptr->tpg_name) > 30) { 1457 (void) printf("\t"); 1458 } 1459 (void) printf("%-9d", ptr->tpg_portal_count); 1460 } else { 1461 (void) printf("%s\t%d", ptr->tpg_name, 1462 ptr->tpg_portal_count); 1463 } 1464 1465 if (!verbose) { 1466 (void) printf("\n"); 1467 continue; 1468 } 1469 1470 if (!script) { 1471 (void) printf("\n portals:"); 1472 } 1473 1474 first_portal = B_TRUE; 1475 1476 portal = ptr->tpg_portal_list; 1477 for (; portal != NULL; portal = portal->next) { 1478 ret = sockaddr_to_str(&(portal->portal_addr), &pstr); 1479 if (ret != 0) { 1480 /* invalid addr? */ 1481 continue; 1482 } 1483 if (!first_portal) { 1484 (void) printf(","); 1485 } else { 1486 (void) printf("\t"); 1487 first_portal = B_FALSE; 1488 } 1489 1490 (void) printf("%s", pstr); 1491 free(pstr); 1492 } 1493 1494 if (first_portal) { 1495 /* none found */ 1496 (void) printf("\t<none>"); 1497 } 1498 1499 (void) printf("\n"); 1500 } 1501 1502 if (tpg && (!found)) { 1503 (void) fprintf(stderr, 1504 gettext("Target Portal Group %s not found!\n"), tpg); 1505 (void) fprintf(stderr, "\n"); 1506 ret = 1; 1507 } 1508 1509 it_config_free(cfg); 1510 1511 return (ret); 1512 } 1513 1514 static int 1515 delete_tpg(char *tpg, boolean_t force) 1516 { 1517 int ret; 1518 it_config_t *cfg; 1519 it_tpg_t *ptpg = NULL; 1520 char *sec = "solaris.smf.modify.stmf"; 1521 1522 ITADM_CHKAUTH(sec); 1523 1524 if (!tpg) { 1525 (void) fprintf(stderr, "%s\n", 1526 gettext("Error, no target portal group specified")); 1527 return (EINVAL); 1528 } 1529 1530 ret = it_config_load(&cfg); 1531 if (ret != 0) { 1532 (void) fprintf(stderr, 1533 gettext("Error retrieving iSCSI target configuration: %d"), 1534 ret); 1535 (void) fprintf(stderr, "\n"); 1536 return (ret); 1537 } 1538 1539 ptpg = cfg->config_tpg_list; 1540 for (; ptpg != NULL; ptpg = ptpg->tpg_next) { 1541 if (strcmp(tpg, ptpg->tpg_name) == 0) { 1542 break; 1543 } 1544 } 1545 1546 if (!ptpg) { 1547 (void) fprintf(stderr, 1548 gettext("Target portal group %s does not exist."), 1549 tpg); 1550 (void) fprintf(stderr, "\n"); 1551 ret = 1; 1552 } else { 1553 ret = it_tpg_delete(cfg, ptpg, force); 1554 if (ret == EBUSY) { 1555 (void) fprintf(stderr, "%s\n", 1556 gettext( 1557 "Target portal group associated with one or more " 1558 "targets. Cannot delete.")); 1559 } 1560 1561 if (ret == 0) { 1562 ret = it_config_commit(cfg); 1563 STMF_STALE(ret); 1564 } 1565 } 1566 1567 it_config_free(cfg); 1568 1569 return (ret); 1570 } 1571 1572 static int 1573 modify_initiator(char *ini, nvlist_t *proplist, boolean_t create) 1574 { 1575 int ret; 1576 it_config_t *cfg; 1577 it_ini_t *inip; 1578 nvlist_t *errlist = NULL; 1579 nvpair_t *nvp = NULL; 1580 char *sec = "solaris.smf.modify.stmf"; 1581 boolean_t changed = B_TRUE; 1582 1583 ITADM_CHKAUTH(sec); 1584 1585 if (!ini) { 1586 (void) fprintf(stderr, "%s\n", 1587 gettext("Error, no initiator specified")); 1588 return (EINVAL); 1589 } else if (create) { 1590 /* 1591 * validate input name - what are the rules for EUI 1592 * and IQN values? 1593 */ 1594 if (!IS_IQN_NAME(ini) && !IS_EUI_NAME(ini)) { 1595 (void) fprintf(stderr, gettext("Invalid name %s"), 1596 ini); 1597 (void) fprintf(stderr, "\n"); 1598 return (EINVAL); 1599 } 1600 } 1601 1602 /* 1603 * See if any properties were actually specified. 1604 */ 1605 if (proplist) { 1606 nvp = nvlist_next_nvpair(proplist, nvp); 1607 } 1608 1609 if ((nvp == NULL) && !create) { 1610 changed = B_FALSE; 1611 } 1612 1613 /* 1614 * If no properties, and this is really a modify op, verify 1615 * that the requested initiator exists, but then don't do anything. 1616 * Modifying non-existent is an error; doing nothing to a defined 1617 * initiator is not. 1618 */ 1619 1620 ret = it_config_load(&cfg); 1621 if (ret != 0) { 1622 (void) fprintf(stderr, 1623 gettext("Error retrieving iSCSI target configuration: %d"), 1624 ret); 1625 (void) fprintf(stderr, "\n"); 1626 return (ret); 1627 } 1628 1629 inip = cfg->config_ini_list; 1630 while (inip) { 1631 if (strcasecmp(inip->ini_name, ini) == 0) { 1632 break; 1633 } 1634 1635 inip = inip->ini_next; 1636 } 1637 1638 if (create) { 1639 if (inip) { 1640 (void) fprintf(stderr, 1641 gettext("Initiator %s already exists"), 1642 inip->ini_name); 1643 (void) fprintf(stderr, "\n"); 1644 ret = EINVAL; 1645 } else { 1646 ret = it_ini_create(cfg, &inip, ini); 1647 if (ret != 0) { 1648 if (ret == EFAULT) { 1649 (void) fprintf(stderr, 1650 gettext("Invalid iSCSI name %s"), 1651 ini); 1652 } else { 1653 (void) fprintf(stderr, 1654 gettext( 1655 "Error creating initiator: %d"), 1656 ret); 1657 } 1658 (void) fprintf(stderr, "\n"); 1659 } 1660 } 1661 } else if (!inip) { 1662 ret = ENOENT; 1663 (void) fprintf(stderr, 1664 gettext("Error, initiator %s not found."), 1665 ini); 1666 (void) fprintf(stderr, "\n"); 1667 } 1668 1669 if ((ret == 0) && nvp) { 1670 ret = it_ini_setprop(inip, proplist, &errlist); 1671 1672 if (ret != 0) { 1673 (void) fprintf(stderr, 1674 gettext("Error setting initiator properties: %d"), 1675 ret); 1676 (void) fprintf(stderr, "\n"); 1677 if (errlist) { 1678 nvpair_t *nvp = NULL; 1679 char *nn; 1680 char *nv; 1681 1682 while ((nvp = nvlist_next_nvpair(errlist, nvp)) 1683 != NULL) { 1684 nv = NULL; 1685 1686 nn = nvpair_name(nvp); 1687 (void) nvpair_value_string(nvp, &nv); 1688 1689 if (nv != NULL) { 1690 (void) fprintf(stderr, 1691 "\t%s: %s\n", nn, nv); 1692 } 1693 } 1694 1695 nvlist_free(errlist); 1696 } 1697 } 1698 } 1699 1700 if ((ret == 0) && changed) { 1701 ret = it_config_commit(cfg); 1702 STMF_STALE(ret); 1703 } 1704 1705 it_config_free(cfg); 1706 1707 return (ret); 1708 } 1709 1710 static int 1711 list_initiator(char *ini, boolean_t verbose, boolean_t script) /* ARGSUSED */ 1712 { 1713 int ret; 1714 it_config_t *cfg; 1715 it_ini_t *ptr; 1716 boolean_t found = B_FALSE; 1717 boolean_t first = B_TRUE; 1718 char *isecret; 1719 char *iuser; 1720 char *sec = "solaris.smf.read.stmf"; 1721 1722 ITADM_CHKAUTH(sec); 1723 1724 ret = it_config_load(&cfg); 1725 if (ret != 0) { 1726 (void) fprintf(stderr, 1727 gettext("Error retrieving iSCSI target configuration: %d"), 1728 ret); 1729 (void) fprintf(stderr, "\n"); 1730 return (ret); 1731 } 1732 1733 ptr = cfg->config_ini_list; 1734 1735 for (; ptr != NULL; ptr = ptr->ini_next) { 1736 isecret = "unset"; 1737 iuser = "<none>"; 1738 1739 if (found) { 1740 break; 1741 } 1742 1743 if (ini) { 1744 if (strcasecmp(ini, ptr->ini_name) != 0) { 1745 continue; 1746 } else { 1747 found = B_TRUE; 1748 } 1749 } 1750 1751 if (ptr->ini_properties) { 1752 if (nvlist_exists(ptr->ini_properties, "chapsecret")) { 1753 isecret = "set"; 1754 } 1755 (void) nvlist_lookup_string(ptr->ini_properties, 1756 "chapuser", &iuser); 1757 1758 } 1759 1760 /* there's nothing to print for verbose yet */ 1761 if (!script && first) { 1762 (void) printf("%-61s%-10s%-7s\n", "INITIATOR NAME", 1763 "CHAPUSER", "SECRET"); 1764 first = B_FALSE; 1765 } 1766 1767 if (!script) { 1768 /* 1769 * try not to let columns run into each other. 1770 * Stick a tab after too-long fields. 1771 * Lengths chosen are for the 'common' cases. 1772 */ 1773 (void) printf("%-61s", ptr->ini_name); 1774 1775 if (strlen(ptr->ini_name) > 60) { 1776 (void) printf("\t"); 1777 } 1778 1779 (void) printf("%-15s", iuser); 1780 if (strlen(iuser) >= 15) { 1781 (void) printf("\t"); 1782 } 1783 1784 (void) printf("%-4s", isecret); 1785 } else { 1786 (void) printf("%s\t%s\t%s", ptr->ini_name, 1787 iuser, isecret); 1788 } 1789 1790 (void) printf("\n"); 1791 } 1792 1793 if (ini && (!found)) { 1794 (void) fprintf(stderr, 1795 gettext("Initiator %s not found!"), ini); 1796 (void) fprintf(stderr, "\n"); 1797 ret = 1; 1798 } 1799 1800 it_config_free(cfg); 1801 1802 return (ret); 1803 } 1804 1805 int 1806 delete_initiator(char *ini) 1807 { 1808 int ret; 1809 it_config_t *cfg; 1810 it_ini_t *ptr; 1811 char *sec = "solaris.smf.modify.stmf"; 1812 1813 ITADM_CHKAUTH(sec); 1814 1815 if (!ini) { 1816 (void) fprintf(stderr, "%s\n", 1817 gettext("Error, no initiator specified")); 1818 return (EINVAL); 1819 } 1820 1821 ret = it_config_load(&cfg); 1822 if (ret != 0) { 1823 (void) fprintf(stderr, 1824 gettext("Error retrieving iSCSI target configuration: %d"), 1825 ret); 1826 (void) fprintf(stderr, "\n"); 1827 return (ret); 1828 } 1829 1830 ptr = cfg->config_ini_list; 1831 while (ptr) { 1832 if (strcasecmp(ptr->ini_name, ini) == 0) { 1833 break; 1834 } 1835 1836 ptr = ptr->ini_next; 1837 } 1838 1839 if (ptr) { 1840 it_ini_delete(cfg, ptr); 1841 1842 ret = it_config_commit(cfg); 1843 STMF_STALE(ret); 1844 } else { 1845 (void) fprintf(stderr, 1846 gettext("Initiator %s not found"), ini); 1847 (void) fprintf(stderr, "\n"); 1848 ret = 1; 1849 } 1850 1851 return (ret); 1852 } 1853 1854 static int 1855 modify_defaults(nvlist_t *proplist) 1856 { 1857 int ret; 1858 it_config_t *cfg; 1859 nvlist_t *errlist = NULL; 1860 nvpair_t *nvp = NULL; 1861 char *sec = "solaris.smf.modify.stmf"; 1862 1863 ITADM_CHKAUTH(sec); 1864 1865 if (proplist) { 1866 /* make sure at least one property is specified */ 1867 nvp = nvlist_next_nvpair(proplist, nvp); 1868 } 1869 1870 if (nvp == NULL) { 1871 /* empty list */ 1872 (void) fprintf(stderr, "%s\n", 1873 gettext("Error, no properties specified")); 1874 return (EINVAL); 1875 } 1876 1877 ret = it_config_load(&cfg); 1878 if (ret != 0) { 1879 (void) fprintf(stderr, 1880 gettext("Error retrieving iSCSI target configuration: %d"), 1881 ret); 1882 (void) fprintf(stderr, "\n"); 1883 return (ret); 1884 } 1885 1886 ret = it_config_setprop(cfg, proplist, &errlist); 1887 if (ret != 0) { 1888 (void) fprintf(stderr, 1889 gettext("Error setting global properties: %d"), 1890 ret); 1891 (void) fprintf(stderr, "\n"); 1892 if (errlist) { 1893 nvpair_t *nvp = NULL; 1894 char *nn; 1895 char *nv; 1896 1897 while ((nvp = nvlist_next_nvpair(errlist, nvp)) 1898 != NULL) { 1899 nv = NULL; 1900 1901 nn = nvpair_name(nvp); 1902 (void) nvpair_value_string(nvp, &nv); 1903 1904 if (nv != NULL) { 1905 (void) fprintf(stderr, "\t%s: %s\n", 1906 nn, nv); 1907 } 1908 } 1909 1910 nvlist_free(errlist); 1911 } 1912 } 1913 1914 if (ret == 0) { 1915 ret = it_config_commit(cfg); 1916 STMF_STALE(ret); 1917 } 1918 1919 it_config_free(cfg); 1920 1921 return (ret); 1922 } 1923 1924 static int 1925 list_defaults(boolean_t script) 1926 { 1927 int ret; 1928 it_config_t *cfg; 1929 nvlist_t *nvl; 1930 char *alias = "<none>"; 1931 char *auth = "<none>"; 1932 char *isns = "disabled"; 1933 char **isvrs = NULL; 1934 uint32_t scount = 0; 1935 char *rsvr = "<none>"; 1936 char *rsecret = "unset"; 1937 boolean_t val = B_FALSE; 1938 int i; 1939 char *sec = "solaris.smf.read.stmf"; 1940 1941 ITADM_CHKAUTH(sec); 1942 1943 ret = it_config_load(&cfg); 1944 if (ret != 0) { 1945 (void) fprintf(stderr, 1946 gettext("Error retrieving iSCSI target configuration: %d"), 1947 ret); 1948 (void) fprintf(stderr, "\n"); 1949 return (ret); 1950 } 1951 1952 nvl = cfg->config_global_properties; 1953 1954 /* look up all possible options */ 1955 (void) nvlist_lookup_string(nvl, "alias", &alias); 1956 (void) nvlist_lookup_string(nvl, "auth", &auth); 1957 (void) nvlist_lookup_boolean_value(nvl, "isns", &val); 1958 if (val == B_TRUE) { 1959 isns = "enabled"; 1960 } 1961 (void) nvlist_lookup_string_array(nvl, "isnsserver", &isvrs, 1962 &scount); 1963 (void) nvlist_lookup_string(nvl, "radiusserver", &rsvr); 1964 if (nvlist_exists(nvl, "radiussecret")) { 1965 rsecret = "set"; 1966 } 1967 1968 if (!script) { 1969 (void) printf("%s:\n\n", 1970 gettext("iSCSI Target Default Properties")); 1971 } 1972 1973 if (script) { 1974 (void) printf("%s\t%s\t%s\t%s\t%s\t", 1975 alias, auth, rsvr, rsecret, isns); 1976 } else { 1977 (void) printf("%-15s\t%s\n%-15s\t%s\n%-15s\t%s\n%-15s\t%s\n" 1978 "%-15s\t%s\n%-15s\t", 1979 "alias:", alias, "auth:", auth, "radiusserver:", 1980 rsvr, "radiussecret:", rsecret, "isns:", isns, 1981 "isnsserver:"); 1982 } 1983 1984 for (i = 0; i < scount; i++) { 1985 if (!isvrs || !isvrs[i]) { 1986 break; 1987 } 1988 if (i > 0) { 1989 (void) printf(","); 1990 } 1991 (void) printf("%s", isvrs[i]); 1992 } 1993 1994 if (i == 0) { 1995 (void) printf("%s", "<none>"); 1996 } 1997 1998 (void) printf("\n"); 1999 2000 it_config_free(cfg); 2001 2002 return (0); 2003 } 2004 2005 static int 2006 itadm_get_password(nvlist_t *nvl, char *key, char *passfile, 2007 char *phrase) 2008 { 2009 int ret = 0; 2010 char *pass; 2011 char buf[1024]; 2012 int fd; 2013 struct stat64 sbuf; 2014 size_t rd; 2015 2016 if (!nvl || !key) { 2017 return (EINVAL); 2018 } 2019 2020 if (passfile) { 2021 ret = stat64(passfile, &sbuf); 2022 if ((ret != 0) || (!S_ISREG(sbuf.st_mode))) { 2023 (void) fprintf(stderr, 2024 gettext("Invalid secret file %s"), 2025 passfile); 2026 (void) fprintf(stderr, "\n"); 2027 return (EBADF); 2028 } 2029 2030 fd = open64(passfile, O_RDONLY); 2031 if (fd == -1) { 2032 ret = errno; 2033 (void) fprintf(stderr, 2034 gettext("Could not open secret file %s, %d"), 2035 passfile, ret); 2036 (void) fprintf(stderr, "\n"); 2037 return (ret); 2038 } 2039 2040 rd = read(fd, buf, sbuf.st_size); 2041 (void) close(fd); 2042 2043 if (rd != sbuf.st_size) { 2044 ret = EIO; 2045 (void) fprintf(stderr, 2046 gettext("Could not read secret file %s, %d"), 2047 passfile, ret); 2048 (void) fprintf(stderr, "\n"); 2049 return (ret); 2050 } 2051 2052 /* ensure buf is properly terminated */ 2053 buf[rd] = '\0'; 2054 2055 /* if last char is a newline, strip it off */ 2056 if (buf[rd - 1] == '\n') { 2057 buf[rd - 1] = '\0'; 2058 } 2059 2060 /* validate length */ 2061 if ((strlen(buf) > 255) || (strlen(buf) < 12)) { 2062 (void) fprintf(stderr, "%s\n", 2063 gettext( 2064 "Secret must be between 12 and 255 characters")); 2065 return (EINVAL); 2066 } 2067 } else { 2068 /* prompt for secret */ 2069 if (!phrase) { 2070 return (EINVAL); 2071 } 2072 2073 pass = getpassphrase(phrase); 2074 if (!pass) { 2075 ret = errno; 2076 (void) fprintf(stderr, 2077 gettext("Could not read secret, %d"), 2078 ret); 2079 (void) fprintf(stderr, "\n"); 2080 return (ret); 2081 } 2082 2083 /* validate length */ 2084 if ((strlen(pass) > 255) || (strlen(pass) < 12)) { 2085 (void) fprintf(stderr, "%s\n", 2086 gettext( 2087 "Secret must be between 12 and 255 characters")); 2088 return (EINVAL); 2089 } 2090 2091 (void) strlcpy(buf, pass, sizeof (buf)); 2092 2093 /* confirm entered secret */ 2094 pass = getpassphrase(gettext("Re-enter secret: ")); 2095 if (!pass) { 2096 ret = errno; 2097 (void) fprintf(stderr, 2098 gettext("Could not read secret, %d"), 2099 ret); 2100 (void) fprintf(stderr, "\n"); 2101 return (ret); 2102 } 2103 2104 if (strcmp(buf, pass) != 0) { 2105 ret = EINVAL; 2106 (void) fprintf(stderr, "%s\n", 2107 gettext("Secret validation failed")); 2108 return (ret); 2109 } 2110 2111 } 2112 2113 ret = nvlist_add_string(nvl, key, buf); 2114 2115 return (ret); 2116 } 2117 2118 static int 2119 itadm_opt_to_arr(nvlist_t *nvl, char *key, char *opt, uint32_t *num) 2120 { 2121 int count; 2122 char *bufp; 2123 char **arr; 2124 2125 if (!opt || !key || !nvl) { 2126 return (EINVAL); 2127 } 2128 2129 bufp = opt; 2130 count = 1; 2131 2132 for (;;) { 2133 bufp = strchr(bufp, ','); 2134 if (!bufp) { 2135 break; 2136 } 2137 bufp++; 2138 count++; 2139 } 2140 2141 arr = calloc(count, sizeof (char *)); 2142 if (!arr) { 2143 return (ENOMEM); 2144 } 2145 2146 bufp = opt; 2147 /* set delimiter to comma */ 2148 (void) bufsplit(",", 0, NULL); 2149 2150 /* split up that buf! */ 2151 (void) bufsplit(bufp, count, arr); 2152 2153 /* if requested, return the number of array members found */ 2154 if (num) { 2155 *num = count; 2156 } 2157 2158 return (nvlist_add_string_array(nvl, key, arr, count)); 2159 } 2160 2161 static void 2162 tag_name_to_num(char *tagname, uint16_t *tagnum) 2163 { 2164 ulong_t id; 2165 char *ptr = NULL; 2166 2167 if (!tagname || !tagnum) { 2168 return; 2169 } 2170 2171 *tagnum = 0; 2172 2173 id = strtoul(tagname, &ptr, 10); 2174 2175 /* Must be entirely numeric and in-range */ 2176 if (ptr && (*ptr != '\0')) { 2177 return; 2178 } 2179 2180 if ((id <= UINT16_MAX) && (id > 1)) { 2181 *tagnum = (uint16_t)id; 2182 } 2183 } 2184