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