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