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