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