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