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