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