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 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 operands */ 461 ret = 1; 462 goto usage_error; 463 default: 464 break; 465 } 466 } 467 468 if (newargc > 1) { 469 switch ((itadm_sub_t)idx) { 470 case MODIFY_TGT: 471 case DELETE_TGT: 472 case LIST_TGT: 473 case DELETE_TPG: 474 case LIST_TPG: 475 case CREATE_INI: 476 case MODIFY_INI: 477 case LIST_INI: 478 case DELETE_INI: 479 /* These subcommands should have at most one operand */ 480 ret = 1; 481 goto usage_error; 482 483 default: 484 break; 485 } 486 } 487 488 /* 489 * XXX - this should probably get pushed down to the library 490 * depending on the decision to allow/disallow configuratoin 491 * without the service running. 492 */ 493 /* 494 * Make sure iSCSI target service is enabled before 495 * proceeding. 496 */ 497 smfstate = smf_get_state(ISCSIT_SVC); 498 if (!smfstate || 499 (strcmp(smfstate, SCF_STATE_STRING_ONLINE) != 0)) { 500 (void) fprintf(stderr, "%s\n", 501 gettext("The iSCSI target service must be online " 502 "before running this command.")); 503 (void) fprintf(stderr, 504 gettext("Use 'svcadm enable -r %s'"), ISCSIT_SVC); 505 (void) fprintf(stderr, "\n"); 506 (void) fprintf(stderr, "%s\n", 507 gettext("to enable the service and its prerequisite " 508 "services and/or")); 509 (void) fprintf(stderr, 510 gettext("'svcs -x %s' to determine why it is not online."), 511 ISCSIT_SVC); 512 (void) fprintf(stderr, "\n"); 513 514 return (1); 515 } 516 517 switch ((itadm_sub_t)idx) { 518 case CREATE_TGT: 519 if (targetname) { 520 ret = create_target(targetname, proplist); 521 } else { 522 /* 523 * OK for objp to be NULL here. If the 524 * user did not specify a target name, 525 * one will be generated. 526 */ 527 ret = create_target(objp, proplist); 528 } 529 break; 530 case MODIFY_TGT: 531 ret = modify_target(objp, targetname, proplist); 532 break; 533 case DELETE_TGT: 534 ret = delete_target(objp, force); 535 break; 536 case LIST_TGT: 537 ret = list_target(objp, verbose, scripting); 538 break; 539 case CREATE_TPG: 540 ret = create_tpg(objp, newargc - 1, &(newargv[1])); 541 break; 542 case DELETE_TPG: 543 ret = delete_tpg(objp, force); 544 break; 545 case LIST_TPG: 546 ret = list_tpg(objp, verbose, scripting); 547 break; 548 case CREATE_INI: 549 ret = modify_initiator(objp, proplist, B_TRUE); 550 break; 551 case MODIFY_INI: 552 ret = modify_initiator(objp, proplist, B_FALSE); 553 break; 554 case LIST_INI: 555 ret = list_initiator(objp, verbose, scripting); 556 break; 557 case DELETE_INI: 558 ret = delete_initiator(objp); 559 break; 560 case MODIFY_DEF: 561 ret = modify_defaults(proplist); 562 break; 563 case LIST_DEF: 564 ret = list_defaults(scripting); 565 break; 566 default: 567 ret = 1; 568 goto usage_error; 569 } 570 571 if (ret != 0) { 572 (void) fprintf(stderr, 573 gettext("itadm %s failed with error %d"), 574 subcmds[idx].name, ret); 575 (void) fprintf(stderr, "\n"); 576 } 577 return (ret); 578 579 usage_error: 580 if (subcmds[idx].name) { 581 (void) printf("%s\n", gettext(subcmds[idx].usemsg)); 582 } else { 583 /* overall usage */ 584 (void) printf("%s\n\n", gettext("itadm usage:")); 585 for (idx = 0; subcmds[idx].name != NULL; idx++) { 586 if (!subcmds[idx].usemsg) { 587 continue; 588 } 589 (void) printf("\t%s\n", gettext(subcmds[idx].usemsg)); 590 } 591 } 592 593 return (ret); 594 } 595 596 static int 597 create_target(char *tgt, nvlist_t *proplist) 598 { 599 int ret; 600 it_config_t *cfg = NULL; 601 it_tgt_t *tgtp; 602 char **tags = NULL; 603 uint32_t count = 0; 604 nvlist_t *errlist = NULL; 605 int i; 606 it_tpg_t *tpg = NULL; 607 uint16_t tagid = 0; 608 it_tpgt_t *tpgt; 609 char *sec = "solaris.smf.modify.stmf"; 610 boolean_t did_it_config_load = B_FALSE; 611 612 ITADM_CHKAUTH(sec); 613 614 if (tgt) { 615 /* 616 * Validate target name. 617 */ 618 if (!IS_IQN_NAME(tgt) && !IS_EUI_NAME(tgt)) { 619 (void) fprintf(stderr, gettext("Invalid name %s"), 620 tgt); 621 (void) fprintf(stderr, "\n"); 622 return (EINVAL); 623 } 624 } 625 626 ret = it_config_load(&cfg); 627 if (ret != 0) { 628 (void) fprintf(stderr, 629 gettext("Error retrieving iSCSI target configuration: %d"), 630 ret); 631 (void) fprintf(stderr, "\n"); 632 goto done; 633 } 634 635 did_it_config_load = B_TRUE; 636 637 ret = it_tgt_create(cfg, &tgtp, tgt); 638 if (ret != 0) { 639 if (ret == EFAULT) { 640 (void) fprintf(stderr, 641 gettext("Invalid iSCSI name %s"), tgt); 642 } else if (ret == EEXIST) { 643 (void) fprintf(stderr, 644 gettext("iSCSI target %s already configured"), 645 tgt); 646 } else if (ret == E2BIG) { 647 (void) fprintf(stderr, 648 gettext("Maximum of %d iSCSI targets"), 649 MAX_TARGETS); 650 } else { 651 (void) fprintf(stderr, 652 gettext("Error creating target: %d"), ret); 653 } 654 (void) fprintf(stderr, "\n"); 655 goto done; 656 } 657 658 /* set the target portal group tags */ 659 ret = nvlist_lookup_string_array(proplist, "tpg-tag", &tags, 660 &count); 661 662 if (ret == ENOENT) { 663 /* none specified. is this ok? */ 664 ret = 0; 665 } else if (ret != 0) { 666 (void) fprintf(stderr, 667 gettext("internal error: %d"), ret); 668 (void) fprintf(stderr, "\n"); 669 goto done; 670 } 671 672 /* special case, don't set any TPGs */ 673 if (tags && (strcmp("default", tags[0]) == 0)) { 674 count = 0; 675 } 676 677 for (i = 0; i < count; i++) { 678 if (!tags[i]) { 679 continue; 680 } 681 682 /* see that all referenced groups are already defined */ 683 tpg = cfg->config_tpg_list; 684 while (tpg != NULL) { 685 if (strcmp(tags[i], tpg->tpg_name) == 0) { 686 break; 687 } 688 689 tpg = tpg->tpg_next; 690 } 691 if (tpg == NULL) { 692 (void) fprintf(stderr, 693 gettext("Invalid tpg-tag %s, tag not defined"), 694 tags[i]); 695 (void) fprintf(stderr, "\n"); 696 ret = 1; 697 goto done; 698 } 699 700 /* generate the tag number to use */ 701 tag_name_to_num(tags[i], &tagid); 702 703 ret = it_tpgt_create(cfg, tgtp, &tpgt, tags[i], tagid); 704 if (ret != 0) { 705 (void) fprintf(stderr, 706 gettext("Could not add target portal group" 707 "tag %s, error %d"), tags[i], ret); 708 (void) fprintf(stderr, "\n"); 709 goto done; 710 } 711 tagid++; 712 } 713 714 /* remove the tags from the proplist before continuing */ 715 if (tags) { 716 (void) nvlist_remove_all(proplist, "tpg-tag"); 717 } 718 719 ret = it_tgt_setprop(cfg, tgtp, proplist, &errlist); 720 if (ret != 0) { 721 (void) fprintf(stderr, 722 gettext("Error setting target properties, %d"), ret); 723 (void) fprintf(stderr, "\n"); 724 if (errlist) { 725 nvpair_t *nvp = NULL; 726 char *nn; 727 char *nv; 728 729 while ((nvp = nvlist_next_nvpair(errlist, nvp)) 730 != NULL) { 731 nv = NULL; 732 733 nn = nvpair_name(nvp); 734 (void) nvpair_value_string(nvp, &nv); 735 736 if (nv != NULL) { 737 (void) fprintf(stderr, "\t%s: %s\n", 738 nn, nv); 739 } 740 } 741 742 nvlist_free(errlist); 743 } 744 goto done; 745 } 746 747 if (ret == 0) { 748 ret = it_config_commit(cfg); 749 STMF_STALE(ret); 750 } 751 752 done: 753 if (ret == 0) { 754 (void) printf(gettext("Target %s successfully created"), 755 tgtp->tgt_name); 756 (void) printf("\n"); 757 } 758 759 if (did_it_config_load) 760 it_config_free(cfg); 761 762 return (ret); 763 } 764 765 int 766 list_target(char *tgt, boolean_t verbose, boolean_t script) 767 { 768 int ret; 769 it_config_t *cfg; 770 it_tgt_t *ptr; 771 boolean_t found = B_FALSE; 772 boolean_t first = B_TRUE; 773 boolean_t first_tag = B_TRUE; 774 char *gauth = "none"; 775 char *galias = "-"; 776 char *auth; 777 char *alias; 778 char *chapu; 779 char *chaps; 780 it_tpgt_t *tagp; 781 char *sec = "solaris.smf.read.stmf"; 782 stmfDevid devid; 783 stmfSessionList *sess = NULL; 784 stmfTargetProperties props; 785 char *state; 786 int num_sessions; 787 788 ITADM_CHKAUTH(sec); 789 790 ret = it_config_load(&cfg); 791 if (ret != 0) { 792 (void) fprintf(stderr, 793 gettext("Error retrieving iSCSI target configuration: %d"), 794 ret); 795 (void) fprintf(stderr, "\n"); 796 return (ret); 797 } 798 799 ptr = cfg->config_tgt_list; 800 801 /* grab global defaults for auth, alias */ 802 if (cfg->config_global_properties) { 803 (void) nvlist_lookup_string(cfg->config_global_properties, 804 "alias", &galias); 805 (void) nvlist_lookup_string(cfg->config_global_properties, 806 "auth", &gauth); 807 } 808 809 for (; ptr != NULL; ptr = ptr->tgt_next) { 810 if (found) { 811 break; 812 } 813 814 if (tgt) { 815 /* 816 * We do a case-insensitive match in case 817 * a non-lower case value got stored. 818 */ 819 if (strcasecmp(tgt, ptr->tgt_name) != 0) { 820 continue; 821 } else { 822 found = B_TRUE; 823 } 824 } 825 826 state = "-"; 827 num_sessions = 0; 828 sess = NULL; 829 830 /* 831 * make a best effort to retrieve target status and 832 * number of active sessions from STMF. 833 */ 834 ret = stmfDevidFromIscsiName(ptr->tgt_name, &devid); 835 if (ret == STMF_STATUS_SUCCESS) { 836 ret = stmfGetTargetProperties(&devid, &props); 837 if (ret == STMF_STATUS_SUCCESS) { 838 if (props.status == STMF_TARGET_PORT_ONLINE) { 839 state = "online"; 840 } else { 841 state = "offline"; 842 } 843 } 844 } 845 if (ret == STMF_STATUS_SUCCESS) { 846 ret = stmfGetSessionList(&devid, &sess); 847 if (ret == STMF_STATUS_SUCCESS) { 848 num_sessions = sess->cnt; 849 free(sess); 850 } 851 } 852 853 /* reset ret so we don't return an error */ 854 ret = 0; 855 856 if (!script && first) { 857 (void) printf("%-61s%-9s%-9s\n", "TARGET NAME", 858 "STATE", "SESSIONS"); 859 first = B_FALSE; 860 } 861 862 if (!script) { 863 /* 864 * try not to let columns run into each other. 865 * Stick a tab after too-long fields. 866 * Lengths chosen are for the 'common' cases. 867 */ 868 (void) printf("%-61s", ptr->tgt_name); 869 if (strlen(ptr->tgt_name) > 60) { 870 (void) printf("\t"); 871 } 872 (void) printf("%-9s%-9d", state, num_sessions); 873 } else { 874 (void) printf("%s\t%s\t%d", ptr->tgt_name, 875 state, num_sessions); 876 } 877 878 if (!verbose) { 879 (void) printf("\n"); 880 continue; 881 } 882 883 auth = gauth; 884 alias = galias; 885 chapu = "-"; 886 chaps = "unset"; 887 888 if (ptr->tgt_properties) { 889 (void) nvlist_lookup_string(ptr->tgt_properties, 890 "auth", &auth); 891 (void) nvlist_lookup_string(ptr->tgt_properties, 892 "alias", &alias); 893 if (nvlist_exists(ptr->tgt_properties, 894 "targetchapsecret")) { 895 chaps = "set"; 896 } 897 (void) nvlist_lookup_string(ptr->tgt_properties, 898 "targetchapuser", &chapu); 899 } 900 901 if (!script) { 902 (void) printf("\n\t%-20s\t%s\n\t%-20s\t%s %s\n" 903 "\t%-20s\t%s\n\t%-20s\t%s\n\t%-20s\t", 904 "alias:", alias, "auth:", auth, 905 ((auth == gauth) ? "(defaults)" : ""), 906 "targetchapuser:", 907 chapu, "targetchapsecret:", chaps, "tpg-tags:"); 908 } else { 909 (void) printf("\t%s\t%s %s\t%s\t%s\t", 910 alias, auth, 911 ((auth == gauth) ? "(defaults)" : ""), 912 chapu, chaps); 913 } 914 915 first_tag = B_TRUE; 916 tagp = ptr->tgt_tpgt_list; 917 for (; tagp != NULL; tagp = tagp->tpgt_next) { 918 if (!first_tag) { 919 (void) printf(","); 920 } else { 921 first_tag = B_FALSE; 922 } 923 (void) printf("%s = %d", 924 tagp->tpgt_tpg_name, tagp->tpgt_tag); 925 } 926 927 if (first_tag) { 928 /* didn't find any */ 929 (void) printf("default"); 930 } 931 932 (void) printf("\n"); 933 } 934 935 if (tgt && (!found)) { 936 (void) fprintf(stderr, 937 gettext("Target %s not found!\n"), tgt); 938 (void) fprintf(stderr, "\n"); 939 ret = 1; 940 } 941 942 it_config_free(cfg); 943 944 return (ret); 945 } 946 947 int 948 delete_target(char *tgt, boolean_t force) 949 { 950 int ret; 951 it_config_t *cfg; 952 it_tgt_t *ptr; 953 char *sec = "solaris.smf.modify.stmf"; 954 955 ITADM_CHKAUTH(sec); 956 957 if (!tgt) { 958 (void) fprintf(stderr, "%s\n", 959 gettext("Error, no target specified")); 960 return (EINVAL); 961 } 962 963 ret = it_config_load(&cfg); 964 if (ret != 0) { 965 (void) fprintf(stderr, 966 gettext("Error retrieving iSCSI target configuration: %d"), 967 ret); 968 (void) fprintf(stderr, "\n"); 969 return (ret); 970 } 971 972 ptr = cfg->config_tgt_list; 973 while (ptr) { 974 /* 975 * We do a case-insensitive match in case 976 * a non-lower case value got stored. 977 */ 978 if (strcasecmp(ptr->tgt_name, tgt) == 0) { 979 break; 980 } 981 982 ptr = ptr->tgt_next; 983 } 984 985 if (ptr) { 986 ret = it_tgt_delete(cfg, ptr, force); 987 988 if (ret != 0) { 989 if (ret == EBUSY) { 990 (void) fprintf(stderr, 991 gettext("The target is online or busy. " 992 "Use the -f (force) option, or " 993 "'stmfadm offline-target %s'"), tgt); 994 (void) fprintf(stderr, "\n"); 995 } 996 } 997 998 if (ret == 0) { 999 ret = it_config_commit(cfg); 1000 STMF_STALE(ret); 1001 } 1002 } else { 1003 (void) fprintf(stderr, 1004 gettext("Target %s not found"), tgt); 1005 (void) fprintf(stderr, "\n"); 1006 ret = 1; 1007 } 1008 1009 it_config_free(cfg); 1010 1011 return (ret); 1012 } 1013 1014 static int 1015 modify_target(char *tgt, char *newname, nvlist_t *proplist) 1016 { 1017 int ret; 1018 it_config_t *cfg = NULL; 1019 it_tgt_t *ptr = NULL; 1020 it_tgt_t *tgtp = NULL; 1021 char **tags = NULL; 1022 uint32_t count = 0; 1023 nvlist_t *errlist = NULL; 1024 int i; 1025 it_tpg_t *tpg = NULL; 1026 uint16_t tagid; 1027 it_tpgt_t *tpgt = NULL; 1028 char *sec = "solaris.smf.modify.stmf"; 1029 boolean_t did_it_config_load = B_FALSE; 1030 1031 ITADM_CHKAUTH(sec); 1032 1033 /* XXX: Do we need to offline anything here too? */ 1034 1035 if (!tgt) { 1036 (void) fprintf(stderr, "%s\n", 1037 gettext("Error, no target specified")); 1038 ret = EINVAL; 1039 goto done; 1040 } 1041 1042 ret = it_config_load(&cfg); 1043 if (ret != 0) { 1044 (void) fprintf(stderr, 1045 gettext("Error retrieving iSCSI target configuration: %d"), 1046 ret); 1047 (void) fprintf(stderr, "\n"); 1048 goto done; 1049 } 1050 1051 did_it_config_load = B_TRUE; 1052 1053 /* 1054 * If newname is specified, ensure it is a valid name. 1055 */ 1056 if (newname) { 1057 if (!validate_iscsi_name(newname)) { 1058 (void) fprintf(stderr, 1059 gettext("Invalid iSCSI name %s"), newname); 1060 (void) fprintf(stderr, "\n"); 1061 ret = 1; 1062 goto done; 1063 } 1064 } 1065 1066 /* 1067 * Loop through to verify that the target to be modified truly 1068 * exists. If this target is to be renamed, ensure the new 1069 * name is not already in use. 1070 */ 1071 ptr = cfg->config_tgt_list; 1072 while (ptr) { 1073 /* 1074 * Does a target with the new name already exist? 1075 */ 1076 if (newname && 1077 (strcasecmp(newname, ptr->tgt_name) == 0)) { 1078 (void) fprintf(stderr, 1079 gettext("A target with name %s already exists"), 1080 newname); 1081 (void) fprintf(stderr, "\n"); 1082 ret = 1; 1083 goto done; 1084 } 1085 1086 if (strcasecmp(ptr->tgt_name, tgt) == 0) { 1087 tgtp = ptr; 1088 } 1089 1090 ptr = ptr ->tgt_next; 1091 } 1092 1093 if (!tgtp) { 1094 (void) fprintf(stderr, 1095 gettext("Target %s not found"), tgt); 1096 (void) fprintf(stderr, "\n"); 1097 ret = EINVAL; 1098 goto done; 1099 } 1100 1101 /* set the target portal group tags */ 1102 ret = nvlist_lookup_string_array(proplist, "tpg-tag", &tags, 1103 &count); 1104 1105 if (ret == ENOENT) { 1106 /* none specified. is this ok? */ 1107 ret = 0; 1108 } else if (ret != 0) { 1109 (void) fprintf(stderr, 1110 gettext("internal error: %d"), ret); 1111 (void) fprintf(stderr, "\n"); 1112 goto done; 1113 } 1114 1115 /* special case, remove all explicit TPGs, and don't add any */ 1116 if (tags && (strcmp("default", tags[0]) == 0)) { 1117 count = 0; 1118 } 1119 1120 for (i = 0; i < count; i++) { 1121 if (!tags || !tags[i]) { 1122 continue; 1123 } 1124 1125 /* see that all referenced groups are already defined */ 1126 tpg = cfg->config_tpg_list; 1127 while (tpg != NULL) { 1128 if (strcmp(tags[i], tpg->tpg_name) == 0) { 1129 break; 1130 } 1131 tpg = tpg->tpg_next; 1132 } 1133 if (tpg == NULL) { 1134 (void) fprintf(stderr, 1135 gettext("Invalid tpg-name %s: not defined"), 1136 tags[i]); 1137 (void) fprintf(stderr, "\n"); 1138 ret = 1; 1139 goto done; 1140 } 1141 } 1142 1143 /* 1144 * don't recreate tags that are already associated, 1145 * remove tags not requested. 1146 */ 1147 if (tags) { 1148 tpgt = tgtp->tgt_tpgt_list; 1149 while (tpgt) { 1150 for (i = 0; i < count; i++) { 1151 if (!tags[i]) { 1152 continue; 1153 } 1154 1155 if (strcmp(tpgt->tpgt_tpg_name, tags[i]) 1156 == 0) { 1157 /* non-null tags will be created */ 1158 tags[i] = NULL; 1159 break; 1160 } 1161 } 1162 if (i == count) { 1163 /* one to remove */ 1164 it_tpgt_t *ptr = tpgt; 1165 1166 tpgt = ptr->tpgt_next; 1167 it_tpgt_delete(cfg, tgtp, ptr); 1168 } else { 1169 tpgt = tpgt->tpgt_next; 1170 } 1171 } 1172 } 1173 1174 /* see if there are any left to add */ 1175 for (i = 0; i < count; i++) { 1176 if (!tags || !tags[i]) { 1177 continue; 1178 } 1179 1180 /* generate the tag number to use */ 1181 tag_name_to_num(tags[i], &tagid); 1182 1183 ret = it_tpgt_create(cfg, tgtp, &tpgt, tags[i], tagid); 1184 if (ret != 0) { 1185 if (ret == E2BIG) { 1186 (void) fprintf(stderr, "%s\n", 1187 gettext("Error, no portal tag available")); 1188 } else { 1189 (void) fprintf(stderr, gettext( 1190 "Could not add target portal group" 1191 " tag %s, error %d"), tags[i], ret); 1192 (void) fprintf(stderr, "\n"); 1193 } 1194 goto done; 1195 } 1196 } 1197 1198 /* remove the tags from the proplist before continuing */ 1199 (void) nvlist_remove_all(proplist, "tpg-tag"); 1200 1201 /* 1202 * Rename this target, if requested. Save the old name in 1203 * the property list, so the kernel knows this is a renamed 1204 * target, and not a new one. 1205 */ 1206 if (newname && (strlen(newname) > 0)) { 1207 ret = nvlist_add_string(proplist, "oldtargetname", 1208 tgtp->tgt_name); 1209 if (ret != 0) { 1210 (void) fprintf(stderr, "%s\n", 1211 gettext("Error renaming target.")); 1212 goto done; 1213 } 1214 (void) strlcpy(tgtp->tgt_name, newname, 1215 sizeof (tgtp->tgt_name)); 1216 } 1217 1218 ret = it_tgt_setprop(cfg, tgtp, proplist, &errlist); 1219 if (ret != 0) { 1220 (void) fprintf(stderr, 1221 gettext("Error setting target properties: %d"), ret); 1222 (void) fprintf(stderr, "\n"); 1223 if (errlist) { 1224 nvpair_t *nvp = NULL; 1225 char *nn; 1226 char *nv; 1227 1228 while ((nvp = nvlist_next_nvpair(errlist, nvp)) 1229 != NULL) { 1230 nv = NULL; 1231 1232 nn = nvpair_name(nvp); 1233 (void) nvpair_value_string(nvp, &nv); 1234 1235 if (nv != NULL) { 1236 (void) fprintf(stderr, "\t%s: %s\n", 1237 nn, nv); 1238 } 1239 } 1240 1241 nvlist_free(errlist); 1242 } 1243 goto done; 1244 } 1245 1246 if (ret == 0) { 1247 ret = it_config_commit(cfg); 1248 STMF_STALE(ret); 1249 } 1250 1251 done: 1252 if (ret == 0) { 1253 (void) printf(gettext("Target %s successfully modified"), 1254 tgtp->tgt_name); 1255 (void) printf("\n"); 1256 } 1257 1258 if (did_it_config_load) 1259 it_config_free(cfg); 1260 1261 return (ret); 1262 } 1263 1264 int 1265 create_tpg(char *tpg, int addrc, char **addrs) 1266 { 1267 int ret; 1268 it_config_t *cfg; 1269 it_tpg_t *tpgp; 1270 int count = 0; 1271 it_portal_t *ptl; 1272 char *sec = "solaris.smf.modify.stmf"; 1273 int i = 0; 1274 1275 ITADM_CHKAUTH(sec); 1276 1277 if (!tpg) { 1278 (void) fprintf(stderr, "%s\n", 1279 gettext("Error, no target portal group specified")); 1280 return (EINVAL); 1281 } 1282 1283 if (strlen(tpg) > (MAX_TPG_NAMELEN - 1)) { 1284 (void) fprintf(stderr, 1285 gettext("Target Portal Group name must be no longer " 1286 "than %d characters."), (MAX_TPG_NAMELEN - 1)); 1287 (void) fprintf(stderr, "\n"); 1288 return (EINVAL); 1289 } 1290 1291 if (!addrs || (addrc <= 0)) { 1292 (void) fprintf(stderr, "%s\n", 1293 gettext("Error, no portal addresses specified")); 1294 return (EINVAL); 1295 } 1296 1297 ret = it_config_load(&cfg); 1298 if (ret != 0) { 1299 (void) fprintf(stderr, 1300 gettext("Error retrieving iSCSI target configuration: %d"), 1301 ret); 1302 (void) fprintf(stderr, "\n"); 1303 return (ret); 1304 } 1305 1306 tpgp = cfg->config_tpg_list; 1307 while (tpgp != NULL) { 1308 if (strcmp(tpgp->tpg_name, tpg) == 0) { 1309 (void) fprintf(stderr, 1310 gettext("Target Portal Group %s already exists"), 1311 tpg); 1312 (void) fprintf(stderr, "\n"); 1313 it_config_free(cfg); 1314 return (1); 1315 } 1316 tpgp = tpgp->tpg_next; 1317 } 1318 1319 /* 1320 * Ensure that the addrs don't contain commas. 1321 */ 1322 for (i = 0; i < addrc; i++) { 1323 if (strchr(addrs[i], ',')) { 1324 (void) fprintf(stderr, 1325 gettext("Bad portal name %s"), 1326 addrs[i]); 1327 (void) fprintf(stderr, "\n"); 1328 1329 it_config_free(cfg); 1330 return (EINVAL); 1331 } 1332 } 1333 1334 /* 1335 * Create the portal group and first portal 1336 */ 1337 ret = it_tpg_create(cfg, &tpgp, tpg, addrs[count]); 1338 if (ret != 0) { 1339 if (ret == EEXIST) { 1340 (void) fprintf(stderr, 1341 gettext("Portal %s already in use"), 1342 addrs[count]); 1343 (void) fprintf(stderr, "\n"); 1344 } 1345 it_config_free(cfg); 1346 return (ret); 1347 } 1348 1349 /* 1350 * Add the remaining portals 1351 */ 1352 for (count = 1; count < addrc; count++) { 1353 if (!addrs[count]) { 1354 continue; 1355 } 1356 1357 ret = it_portal_create(cfg, tpgp, &ptl, addrs[count]); 1358 if (ret != 0) { 1359 if (ret == EEXIST) { 1360 (void) fprintf(stderr, 1361 gettext("Portal %s already in use"), 1362 addrs[count]); 1363 (void) fprintf(stderr, "\n"); 1364 } else { 1365 (void) fprintf(stderr, 1366 gettext("Error adding portal %s: %d"), 1367 addrs[count], ret); 1368 (void) fprintf(stderr, "\n"); 1369 break; 1370 } 1371 } 1372 } 1373 1374 if (ret == 0) { 1375 ret = it_config_commit(cfg); 1376 STMF_STALE(ret); 1377 } 1378 1379 it_config_free(cfg); 1380 1381 return (ret); 1382 } 1383 1384 static int 1385 list_tpg(char *tpg, boolean_t verbose, boolean_t script) 1386 { 1387 int ret; 1388 it_config_t *cfg; 1389 it_tpg_t *ptr; 1390 boolean_t found = B_FALSE; 1391 it_portal_t *portal; 1392 boolean_t first = B_TRUE; 1393 boolean_t first_portal; 1394 char *pstr; 1395 char *sec = "solaris.smf.read.stmf"; 1396 1397 ITADM_CHKAUTH(sec); 1398 1399 ret = it_config_load(&cfg); 1400 if (ret != 0) { 1401 (void) fprintf(stderr, 1402 gettext("Error retrieving iSCSI target configuration: %d"), 1403 ret); 1404 (void) fprintf(stderr, "\n"); 1405 return (ret); 1406 } 1407 1408 ptr = cfg->config_tpg_list; 1409 1410 for (; ptr != NULL; ptr = ptr->tpg_next) { 1411 if (found) { 1412 break; 1413 } 1414 1415 if (tpg) { 1416 if (strcmp(tpg, ptr->tpg_name) != 0) { 1417 continue; 1418 } else { 1419 found = B_TRUE; 1420 } 1421 } 1422 1423 if (!script && first) { 1424 (void) printf("%-30s%-9s\n", "TARGET PORTAL GROUP", 1425 "PORTAL COUNT"); 1426 first = B_FALSE; 1427 } 1428 1429 if (!script) { 1430 (void) printf("%-30s", ptr->tpg_name); 1431 if (strlen(ptr->tpg_name) > 30) { 1432 (void) printf("\t"); 1433 } 1434 (void) printf("%-9d", ptr->tpg_portal_count); 1435 } else { 1436 (void) printf("%s\t%d", ptr->tpg_name, 1437 ptr->tpg_portal_count); 1438 } 1439 1440 if (!verbose) { 1441 (void) printf("\n"); 1442 continue; 1443 } 1444 1445 if (!script) { 1446 (void) printf("\n portals:"); 1447 } 1448 1449 first_portal = B_TRUE; 1450 1451 portal = ptr->tpg_portal_list; 1452 for (; portal != NULL; portal = portal->next) { 1453 ret = sockaddr_to_str(&(portal->portal_addr), &pstr); 1454 if (ret != 0) { 1455 /* invalid addr? */ 1456 continue; 1457 } 1458 if (!first_portal) { 1459 (void) printf(","); 1460 } else { 1461 (void) printf("\t"); 1462 first_portal = B_FALSE; 1463 } 1464 1465 (void) printf("%s", pstr); 1466 free(pstr); 1467 } 1468 1469 if (first_portal) { 1470 /* none found */ 1471 (void) printf("\t<none>"); 1472 } 1473 1474 (void) printf("\n"); 1475 } 1476 1477 if (tpg && (!found)) { 1478 (void) fprintf(stderr, 1479 gettext("Target Portal Group %s not found!\n"), tpg); 1480 (void) fprintf(stderr, "\n"); 1481 ret = 1; 1482 } 1483 1484 it_config_free(cfg); 1485 1486 return (ret); 1487 } 1488 1489 static int 1490 delete_tpg(char *tpg, boolean_t force) 1491 { 1492 int ret; 1493 it_config_t *cfg; 1494 it_tpg_t *ptpg = NULL; 1495 char *sec = "solaris.smf.modify.stmf"; 1496 1497 ITADM_CHKAUTH(sec); 1498 1499 if (!tpg) { 1500 (void) fprintf(stderr, "%s\n", 1501 gettext("Error, no target portal group specified")); 1502 return (EINVAL); 1503 } 1504 1505 ret = it_config_load(&cfg); 1506 if (ret != 0) { 1507 (void) fprintf(stderr, 1508 gettext("Error retrieving iSCSI target configuration: %d"), 1509 ret); 1510 (void) fprintf(stderr, "\n"); 1511 return (ret); 1512 } 1513 1514 ptpg = cfg->config_tpg_list; 1515 for (; ptpg != NULL; ptpg = ptpg->tpg_next) { 1516 if (strcmp(tpg, ptpg->tpg_name) == 0) { 1517 break; 1518 } 1519 } 1520 1521 if (!ptpg) { 1522 (void) fprintf(stderr, 1523 gettext("Target portal group %s does not exist."), 1524 tpg); 1525 (void) fprintf(stderr, "\n"); 1526 ret = 1; 1527 } else { 1528 ret = it_tpg_delete(cfg, ptpg, force); 1529 if (ret == EBUSY) { 1530 (void) fprintf(stderr, "%s\n", 1531 gettext( 1532 "Target portal group associated with one or more " 1533 "targets. Cannot delete.")); 1534 } 1535 1536 if (ret == 0) { 1537 ret = it_config_commit(cfg); 1538 STMF_STALE(ret); 1539 } 1540 } 1541 1542 it_config_free(cfg); 1543 1544 return (ret); 1545 } 1546 1547 static int 1548 modify_initiator(char *ini, nvlist_t *proplist, boolean_t create) 1549 { 1550 int ret; 1551 it_config_t *cfg; 1552 it_ini_t *inip; 1553 nvlist_t *errlist = NULL; 1554 nvpair_t *nvp = NULL; 1555 char *sec = "solaris.smf.modify.stmf"; 1556 boolean_t changed = B_TRUE; 1557 1558 ITADM_CHKAUTH(sec); 1559 1560 if (!ini) { 1561 (void) fprintf(stderr, "%s\n", 1562 gettext("Error, no initiator specified")); 1563 return (EINVAL); 1564 } else if (create) { 1565 /* 1566 * validate input name - what are the rules for EUI 1567 * and IQN values? 1568 */ 1569 if (!IS_IQN_NAME(ini) && !IS_EUI_NAME(ini)) { 1570 (void) fprintf(stderr, gettext("Invalid name %s"), 1571 ini); 1572 (void) fprintf(stderr, "\n"); 1573 return (EINVAL); 1574 } 1575 } 1576 1577 /* 1578 * See if any properties were actually specified. 1579 */ 1580 if (proplist) { 1581 nvp = nvlist_next_nvpair(proplist, nvp); 1582 } 1583 1584 if ((nvp == NULL) && !create) { 1585 changed = B_FALSE; 1586 } 1587 1588 /* 1589 * If no properties, and this is really a modify op, verify 1590 * that the requested initiator exists, but then don't do anything. 1591 * Modifying non-existent is an error; doing nothing to a defined 1592 * initiator is not. 1593 */ 1594 1595 ret = it_config_load(&cfg); 1596 if (ret != 0) { 1597 (void) fprintf(stderr, 1598 gettext("Error retrieving iSCSI target configuration: %d"), 1599 ret); 1600 (void) fprintf(stderr, "\n"); 1601 return (ret); 1602 } 1603 1604 inip = cfg->config_ini_list; 1605 while (inip) { 1606 if (strcmp(inip->ini_name, ini) == 0) { 1607 break; 1608 } 1609 1610 inip = inip->ini_next; 1611 } 1612 1613 if (create) { 1614 if (inip) { 1615 (void) fprintf(stderr, 1616 gettext("Initiator %s already exists"), 1617 inip->ini_name); 1618 (void) fprintf(stderr, "\n"); 1619 ret = EINVAL; 1620 } else { 1621 ret = it_ini_create(cfg, &inip, ini); 1622 if (ret != 0) { 1623 if (ret == EFAULT) { 1624 (void) fprintf(stderr, 1625 gettext("Invalid iSCSI name %s"), 1626 ini); 1627 } else { 1628 (void) fprintf(stderr, 1629 gettext( 1630 "Error creating initiator: %d"), 1631 ret); 1632 } 1633 (void) fprintf(stderr, "\n"); 1634 } 1635 } 1636 } else if (!inip) { 1637 ret = ENOENT; 1638 (void) fprintf(stderr, 1639 gettext("Error, initiator %s not found."), 1640 ini); 1641 (void) fprintf(stderr, "\n"); 1642 } 1643 1644 if ((ret == 0) && nvp) { 1645 ret = it_ini_setprop(inip, proplist, &errlist); 1646 1647 if (ret != 0) { 1648 (void) fprintf(stderr, 1649 gettext("Error setting initiator properties: %d"), 1650 ret); 1651 (void) fprintf(stderr, "\n"); 1652 if (errlist) { 1653 nvpair_t *nvp = NULL; 1654 char *nn; 1655 char *nv; 1656 1657 while ((nvp = nvlist_next_nvpair(errlist, nvp)) 1658 != NULL) { 1659 nv = NULL; 1660 1661 nn = nvpair_name(nvp); 1662 (void) nvpair_value_string(nvp, &nv); 1663 1664 if (nv != NULL) { 1665 (void) fprintf(stderr, 1666 "\t%s: %s\n", nn, nv); 1667 } 1668 } 1669 1670 nvlist_free(errlist); 1671 } 1672 } 1673 } 1674 1675 if ((ret == 0) && changed) { 1676 ret = it_config_commit(cfg); 1677 STMF_STALE(ret); 1678 } 1679 1680 it_config_free(cfg); 1681 1682 return (ret); 1683 } 1684 1685 static int 1686 list_initiator(char *ini, boolean_t verbose, boolean_t script) /* ARGSUSED */ 1687 { 1688 int ret; 1689 it_config_t *cfg; 1690 it_ini_t *ptr; 1691 boolean_t found = B_FALSE; 1692 boolean_t first = B_TRUE; 1693 char *isecret; 1694 char *iuser; 1695 char *sec = "solaris.smf.read.stmf"; 1696 1697 ITADM_CHKAUTH(sec); 1698 1699 ret = it_config_load(&cfg); 1700 if (ret != 0) { 1701 (void) fprintf(stderr, 1702 gettext("Error retrieving iSCSI target configuration: %d"), 1703 ret); 1704 (void) fprintf(stderr, "\n"); 1705 return (ret); 1706 } 1707 1708 ptr = cfg->config_ini_list; 1709 1710 for (; ptr != NULL; ptr = ptr->ini_next) { 1711 isecret = "unset"; 1712 iuser = "<none>"; 1713 1714 if (found) { 1715 break; 1716 } 1717 1718 if (ini) { 1719 if (strcmp(ini, ptr->ini_name) != 0) { 1720 continue; 1721 } else { 1722 found = B_TRUE; 1723 } 1724 } 1725 1726 if (ptr->ini_properties) { 1727 if (nvlist_exists(ptr->ini_properties, "chapsecret")) { 1728 isecret = "set"; 1729 } 1730 (void) nvlist_lookup_string(ptr->ini_properties, 1731 "chapuser", &iuser); 1732 1733 } 1734 1735 /* there's nothing to print for verbose yet */ 1736 if (!script && first) { 1737 (void) printf("%-61s%-10s%-7s\n", "INITIATOR NAME", 1738 "CHAPUSER", "SECRET"); 1739 first = B_FALSE; 1740 } 1741 1742 if (!script) { 1743 /* 1744 * try not to let columns run into each other. 1745 * Stick a tab after too-long fields. 1746 * Lengths chosen are for the 'common' cases. 1747 */ 1748 (void) printf("%-61s", ptr->ini_name); 1749 1750 if (strlen(ptr->ini_name) > 60) { 1751 (void) printf("\t"); 1752 } 1753 1754 (void) printf("%-15s", iuser); 1755 if (strlen(iuser) >= 15) { 1756 (void) printf("\t"); 1757 } 1758 1759 (void) printf("%-4s", isecret); 1760 } else { 1761 (void) printf("%s\t%s\t%s", ptr->ini_name, 1762 iuser, isecret); 1763 } 1764 1765 (void) printf("\n"); 1766 } 1767 1768 if (ini && (!found)) { 1769 (void) fprintf(stderr, 1770 gettext("Initiator %s not found!"), ini); 1771 (void) fprintf(stderr, "\n"); 1772 ret = 1; 1773 } 1774 1775 it_config_free(cfg); 1776 1777 return (ret); 1778 } 1779 1780 int 1781 delete_initiator(char *ini) 1782 { 1783 int ret; 1784 it_config_t *cfg; 1785 it_ini_t *ptr; 1786 char *sec = "solaris.smf.modify.stmf"; 1787 1788 ITADM_CHKAUTH(sec); 1789 1790 if (!ini) { 1791 (void) fprintf(stderr, "%s\n", 1792 gettext("Error, no initiator specified")); 1793 return (EINVAL); 1794 } 1795 1796 ret = it_config_load(&cfg); 1797 if (ret != 0) { 1798 (void) fprintf(stderr, 1799 gettext("Error retrieving iSCSI target configuration: %d"), 1800 ret); 1801 (void) fprintf(stderr, "\n"); 1802 return (ret); 1803 } 1804 1805 ptr = cfg->config_ini_list; 1806 while (ptr) { 1807 if (strcmp(ptr->ini_name, ini) == 0) { 1808 break; 1809 } 1810 1811 ptr = ptr->ini_next; 1812 } 1813 1814 if (ptr) { 1815 it_ini_delete(cfg, ptr); 1816 1817 ret = it_config_commit(cfg); 1818 STMF_STALE(ret); 1819 } else { 1820 (void) fprintf(stderr, 1821 gettext("Initiator %s not found"), ini); 1822 (void) fprintf(stderr, "\n"); 1823 ret = 1; 1824 } 1825 1826 return (ret); 1827 } 1828 1829 static int 1830 modify_defaults(nvlist_t *proplist) 1831 { 1832 int ret; 1833 it_config_t *cfg; 1834 nvlist_t *errlist = NULL; 1835 nvpair_t *nvp = NULL; 1836 char *sec = "solaris.smf.modify.stmf"; 1837 1838 ITADM_CHKAUTH(sec); 1839 1840 if (proplist) { 1841 /* make sure at least one property is specified */ 1842 nvp = nvlist_next_nvpair(proplist, nvp); 1843 } 1844 1845 if (nvp == NULL) { 1846 /* empty list */ 1847 (void) fprintf(stderr, "%s\n", 1848 gettext("Error, no properties specified")); 1849 return (EINVAL); 1850 } 1851 1852 ret = it_config_load(&cfg); 1853 if (ret != 0) { 1854 (void) fprintf(stderr, 1855 gettext("Error retrieving iSCSI target configuration: %d"), 1856 ret); 1857 (void) fprintf(stderr, "\n"); 1858 return (ret); 1859 } 1860 1861 ret = it_config_setprop(cfg, proplist, &errlist); 1862 if (ret != 0) { 1863 (void) fprintf(stderr, 1864 gettext("Error setting global properties: %d"), 1865 ret); 1866 (void) fprintf(stderr, "\n"); 1867 if (errlist) { 1868 nvpair_t *nvp = NULL; 1869 char *nn; 1870 char *nv; 1871 1872 while ((nvp = nvlist_next_nvpair(errlist, nvp)) 1873 != NULL) { 1874 nv = NULL; 1875 1876 nn = nvpair_name(nvp); 1877 (void) nvpair_value_string(nvp, &nv); 1878 1879 if (nv != NULL) { 1880 (void) fprintf(stderr, "\t%s: %s\n", 1881 nn, nv); 1882 } 1883 } 1884 1885 nvlist_free(errlist); 1886 } 1887 } 1888 1889 if (ret == 0) { 1890 ret = it_config_commit(cfg); 1891 STMF_STALE(ret); 1892 } 1893 1894 it_config_free(cfg); 1895 1896 return (ret); 1897 } 1898 1899 static int 1900 list_defaults(boolean_t script) 1901 { 1902 int ret; 1903 it_config_t *cfg; 1904 nvlist_t *nvl; 1905 char *alias = "<none>"; 1906 char *auth = "<none>"; 1907 char *isns = "disabled"; 1908 char **isvrs = NULL; 1909 uint32_t scount = 0; 1910 char *rsvr = "<none>"; 1911 char *rsecret = "unset"; 1912 boolean_t val = B_FALSE; 1913 int i; 1914 char *sec = "solaris.smf.read.stmf"; 1915 1916 ITADM_CHKAUTH(sec); 1917 1918 ret = it_config_load(&cfg); 1919 if (ret != 0) { 1920 (void) fprintf(stderr, 1921 gettext("Error retrieving iSCSI target configuration: %d"), 1922 ret); 1923 (void) fprintf(stderr, "\n"); 1924 return (ret); 1925 } 1926 1927 nvl = cfg->config_global_properties; 1928 1929 /* look up all possible options */ 1930 (void) nvlist_lookup_string(nvl, "alias", &alias); 1931 (void) nvlist_lookup_string(nvl, "auth", &auth); 1932 (void) nvlist_lookup_boolean_value(nvl, "isns", &val); 1933 if (val == B_TRUE) { 1934 isns = "enabled"; 1935 } 1936 (void) nvlist_lookup_string_array(nvl, "isnsserver", &isvrs, 1937 &scount); 1938 (void) nvlist_lookup_string(nvl, "radiusserver", &rsvr); 1939 if (nvlist_exists(nvl, "radiussecret")) { 1940 rsecret = "set"; 1941 } 1942 1943 if (!script) { 1944 (void) printf("%s:\n\n", 1945 gettext("iSCSI Target Default Properties")); 1946 } 1947 1948 if (script) { 1949 (void) printf("%s\t%s\t%s\t%s\t%s\t", 1950 alias, auth, rsvr, rsecret, isns); 1951 } else { 1952 (void) printf("%-15s\t%s\n%-15s\t%s\n%-15s\t%s\n%-15s\t%s\n" 1953 "%-15s\t%s\n%-15s\t", 1954 "alias:", alias, "auth:", auth, "radiusserver:", 1955 rsvr, "radiussecret:", rsecret, "isns:", isns, 1956 "isnsserver:"); 1957 } 1958 1959 for (i = 0; i < scount; i++) { 1960 if (!isvrs || !isvrs[i]) { 1961 break; 1962 } 1963 if (i > 0) { 1964 (void) printf(","); 1965 } 1966 (void) printf("%s", isvrs[i]); 1967 } 1968 1969 if (i == 0) { 1970 (void) printf("%s", "<none>"); 1971 } 1972 1973 (void) printf("\n"); 1974 1975 it_config_free(cfg); 1976 1977 return (0); 1978 } 1979 1980 static int 1981 itadm_get_password(nvlist_t *nvl, char *key, char *passfile, 1982 char *phrase) 1983 { 1984 int ret = 0; 1985 char *pass; 1986 char buf[1024]; 1987 int fd; 1988 struct stat64 sbuf; 1989 size_t rd; 1990 1991 if (!nvl || !key) { 1992 return (EINVAL); 1993 } 1994 1995 if (passfile) { 1996 ret = stat64(passfile, &sbuf); 1997 if ((ret != 0) || (!S_ISREG(sbuf.st_mode))) { 1998 (void) fprintf(stderr, 1999 gettext("Invalid secret file %s"), 2000 passfile); 2001 (void) fprintf(stderr, "\n"); 2002 return (EBADF); 2003 } 2004 2005 fd = open64(passfile, O_RDONLY); 2006 if (fd == -1) { 2007 ret = errno; 2008 (void) fprintf(stderr, 2009 gettext("Could not open secret file %s, %d"), 2010 passfile, ret); 2011 (void) fprintf(stderr, "\n"); 2012 return (ret); 2013 } 2014 2015 rd = read(fd, buf, sbuf.st_size); 2016 (void) close(fd); 2017 2018 if (rd != sbuf.st_size) { 2019 ret = EIO; 2020 (void) fprintf(stderr, 2021 gettext("Could not read secret file %s, %d"), 2022 passfile, ret); 2023 (void) fprintf(stderr, "\n"); 2024 return (ret); 2025 } 2026 2027 /* ensure buf is properly terminated */ 2028 buf[rd] = '\0'; 2029 2030 /* if last char is a newline, strip it off */ 2031 if (buf[rd - 1] == '\n') { 2032 buf[rd - 1] = '\0'; 2033 } 2034 2035 /* validate length */ 2036 if ((strlen(buf) > 255) || (strlen(buf) < 12)) { 2037 (void) fprintf(stderr, "%s\n", 2038 gettext( 2039 "Secret must be between 12 and 255 characters")); 2040 return (EINVAL); 2041 } 2042 } else { 2043 /* prompt for secret */ 2044 if (!phrase) { 2045 return (EINVAL); 2046 } 2047 2048 pass = getpassphrase(phrase); 2049 if (!pass) { 2050 ret = errno; 2051 (void) fprintf(stderr, 2052 gettext("Could not read secret, %d"), 2053 ret); 2054 (void) fprintf(stderr, "\n"); 2055 return (ret); 2056 } 2057 2058 /* validate length */ 2059 if ((strlen(pass) > 255) || (strlen(pass) < 12)) { 2060 (void) fprintf(stderr, "%s\n", 2061 gettext( 2062 "Secret must be between 12 and 255 characters")); 2063 return (EINVAL); 2064 } 2065 2066 (void) strlcpy(buf, pass, sizeof (buf)); 2067 2068 /* confirm entered secret */ 2069 pass = getpassphrase(gettext("Re-enter secret: ")); 2070 if (!pass) { 2071 ret = errno; 2072 (void) fprintf(stderr, 2073 gettext("Could not read secret, %d"), 2074 ret); 2075 (void) fprintf(stderr, "\n"); 2076 return (ret); 2077 } 2078 2079 if (strcmp(buf, pass) != 0) { 2080 ret = EINVAL; 2081 (void) fprintf(stderr, "%s\n", 2082 gettext("Secret validation failed")); 2083 return (ret); 2084 } 2085 2086 } 2087 2088 ret = nvlist_add_string(nvl, key, buf); 2089 2090 return (ret); 2091 } 2092 2093 static int 2094 itadm_opt_to_arr(nvlist_t *nvl, char *key, char *opt, uint32_t *num) 2095 { 2096 int count; 2097 char *bufp; 2098 char **arr; 2099 2100 if (!opt || !key || !nvl) { 2101 return (EINVAL); 2102 } 2103 2104 bufp = opt; 2105 count = 1; 2106 2107 for (;;) { 2108 bufp = strchr(bufp, ','); 2109 if (!bufp) { 2110 break; 2111 } 2112 bufp++; 2113 count++; 2114 } 2115 2116 arr = calloc(count, sizeof (char *)); 2117 if (!arr) { 2118 return (ENOMEM); 2119 } 2120 2121 bufp = opt; 2122 /* set delimiter to comma */ 2123 (void) bufsplit(",", 0, NULL); 2124 2125 /* split up that buf! */ 2126 (void) bufsplit(bufp, count, arr); 2127 2128 /* if requested, return the number of array members found */ 2129 if (num) { 2130 *num = count; 2131 } 2132 2133 return (nvlist_add_string_array(nvl, key, arr, count)); 2134 } 2135 2136 static void 2137 tag_name_to_num(char *tagname, uint16_t *tagnum) 2138 { 2139 ulong_t id; 2140 char *ptr = NULL; 2141 2142 if (!tagname || !tagnum) { 2143 return; 2144 } 2145 2146 *tagnum = 0; 2147 2148 id = strtoul(tagname, &ptr, 10); 2149 2150 /* Must be entirely numeric and in-range */ 2151 if (ptr && (*ptr != '\0')) { 2152 return; 2153 } 2154 2155 if ((id <= UINT16_MAX) && (id > 1)) { 2156 *tagnum = (uint16_t)id; 2157 } 2158 } 2159