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