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