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