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