xref: /illumos-gate/usr/src/cmd/itadm/itadm.c (revision 257873cf)
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\n"
872 			    "\t%-20s\t%s\n\t%-20s\t%s\n\t%-20s\t",
873 			    "alias:", alias, "auth:", auth, "targetchapuser:",
874 			    chapu, "targetchapsecret:", chaps, "tpg-tags:");
875 		} else {
876 			(void) printf("\t%s\t%s\t%s\t%s\t",
877 			    alias, auth, chapu, chaps);
878 		}
879 
880 		first_tag = B_TRUE;
881 		tagp = ptr->tgt_tpgt_list;
882 		for (; tagp != NULL; tagp = tagp->tpgt_next) {
883 			if (!first_tag) {
884 				(void) printf(",");
885 			} else {
886 				first_tag = B_FALSE;
887 			}
888 			(void) printf("%s", tagp->tpgt_tpg_name);
889 		}
890 
891 		if (first_tag) {
892 			/* didn't find any */
893 			(void) printf("default");
894 		}
895 
896 		(void) printf("\n");
897 	}
898 
899 	if (tgt && (!found)) {
900 		(void) fprintf(stderr,
901 		    gettext("Target %s not found!\n"), tgt);
902 		(void) fprintf(stderr, "\n");
903 		ret = 1;
904 	}
905 
906 	it_config_free(cfg);
907 
908 	return (ret);
909 }
910 
911 int
912 delete_target(char *tgt, boolean_t force)
913 {
914 	int		ret;
915 	it_config_t	*cfg;
916 	it_tgt_t	*ptr;
917 	char		*sec = "solaris.smf.modify.stmf";
918 
919 	ITADM_CHKAUTH(sec);
920 
921 	if (!tgt) {
922 		(void) fprintf(stderr, "%s\n",
923 		    gettext("Error, no target specified"));
924 		return (EINVAL);
925 	}
926 
927 	ret = it_config_load(&cfg);
928 	if (ret != 0) {
929 		(void) fprintf(stderr,
930 		    gettext("Error retrieving iSCSI target configuration: %d"),
931 		    ret);
932 		(void) fprintf(stderr, "\n");
933 		return (ret);
934 	}
935 
936 	ptr = cfg->config_tgt_list;
937 	while (ptr) {
938 		if (strcmp(ptr->tgt_name, tgt) == 0) {
939 			break;
940 		}
941 
942 		ptr = ptr->tgt_next;
943 	}
944 
945 	if (ptr) {
946 		ret = it_tgt_delete(cfg, ptr, force);
947 
948 		if (ret != 0) {
949 			if (ret == EBUSY) {
950 				(void) fprintf(stderr,
951 				    gettext("The target is online or busy. "
952 				    "Use the -f (force) option, or "
953 				    "'stmfadm offline-target %s'"), tgt);
954 				(void) fprintf(stderr, "\n");
955 			}
956 		}
957 
958 		if (ret == 0) {
959 			ret = it_config_commit(cfg);
960 			STMF_STALE(ret);
961 		}
962 	} else {
963 		(void) fprintf(stderr,
964 		    gettext("Target %s not found"), tgt);
965 		(void) fprintf(stderr, "\n");
966 		ret = 1;
967 	}
968 
969 	it_config_free(cfg);
970 
971 	return (ret);
972 }
973 
974 static int
975 modify_target(char *tgt, char *newname, nvlist_t *proplist)
976 {
977 	int		ret;
978 	it_config_t	*cfg = NULL;
979 	it_tgt_t	*ptr = NULL;
980 	it_tgt_t	*tgtp;
981 	char		**tags = NULL;
982 	uint32_t	count = 0;
983 	nvlist_t	*errlist = NULL;
984 	int		i;
985 	it_tpg_t	*tpg = NULL;
986 	uint16_t	tagid;
987 	it_tpgt_t	*tpgt;
988 	char		*sec = "solaris.smf.modify.stmf";
989 
990 	ITADM_CHKAUTH(sec);
991 
992 	/* XXX:  Do we need to offline anything here too? */
993 
994 	if (!tgt) {
995 		(void) fprintf(stderr, "%s\n",
996 		    gettext("Error, no target specified"));
997 		return (EINVAL);
998 	}
999 
1000 	ret = it_config_load(&cfg);
1001 	if (ret != 0) {
1002 		(void) fprintf(stderr,
1003 		    gettext("Error retrieving iSCSI target configuration: %d"),
1004 		    ret);
1005 		(void) fprintf(stderr, "\n");
1006 		return (ret);
1007 	}
1008 
1009 	/*
1010 	 * If newname is specified, ensure it is a valid name
1011 	 */
1012 	if (newname) {
1013 		if (!validate_iscsi_name(newname)) {
1014 			(void) fprintf(stderr,
1015 			    gettext("Invalid iSCSI name %s"), newname);
1016 			(void) fprintf(stderr, "\n");
1017 			return (1);
1018 		}
1019 	}
1020 
1021 	/*
1022 	 * Loop through to verify that the target to be modified truly
1023 	 * exists.  If this target is to be renamed, ensure the new
1024 	 * name is not already in use.
1025 	 */
1026 	ptr = cfg->config_tgt_list;
1027 	while (ptr) {
1028 		if (newname && (strcmp(newname, ptr->tgt_name) == 0)) {
1029 			(void) fprintf(stderr,
1030 			    gettext("A target with name %s already exists"),
1031 			    newname);
1032 			(void) fprintf(stderr, "\n");
1033 			ret = 1;
1034 			goto done;
1035 		}
1036 
1037 		if (strcmp(ptr->tgt_name, tgt) == 0) {
1038 			tgtp = ptr;
1039 		}
1040 
1041 		ptr = ptr ->tgt_next;
1042 	}
1043 
1044 	if (!tgtp) {
1045 		(void) fprintf(stderr,
1046 		    gettext("Target %s not found"), tgt);
1047 		(void) fprintf(stderr, "\n");
1048 		it_config_free(cfg);
1049 		return (EINVAL);
1050 	}
1051 
1052 	/* set the target portal group tags */
1053 	ret = nvlist_lookup_string_array(proplist, "tpg-tag", &tags,
1054 	    &count);
1055 
1056 	if (ret == ENOENT) {
1057 		/* none specified.  is this ok? */
1058 		ret = 0;
1059 	} else if (ret != 0) {
1060 		(void) fprintf(stderr,
1061 		    gettext("internal error: %d"), ret);
1062 		(void) fprintf(stderr, "\n");
1063 		goto done;
1064 	}
1065 
1066 	/* special case, remove all explicit TPGs, and don't add any */
1067 	if (tags && (strcmp("default", tags[0]) == 0)) {
1068 		count = 0;
1069 	}
1070 
1071 	for (i = 0; i < count; i++) {
1072 		if (!tags[i]) {
1073 			continue;
1074 		}
1075 
1076 		/* see that all referenced groups are already defined */
1077 		tpg = cfg->config_tpg_list;
1078 		while (tpg != NULL) {
1079 			if (strcmp(tags[i], tpg->tpg_name) == 0) {
1080 				break;
1081 			}
1082 			tpg = tpg->tpg_next;
1083 		}
1084 		if (tpg == NULL) {
1085 			(void) fprintf(stderr,
1086 			    gettext("Invalid tpg-name %s: not defined"),
1087 			    tags[i]);
1088 			(void) fprintf(stderr, "\n");
1089 			ret = 1;
1090 			goto done;
1091 		}
1092 	}
1093 
1094 	/*
1095 	 * don't recreate tags that are already associated,
1096 	 * remove tags not requested.
1097 	 */
1098 	if (tags) {
1099 		tpgt = tgtp->tgt_tpgt_list;
1100 		while (tpgt) {
1101 			for (i = 0; i < count; i++) {
1102 				if (!tags[i]) {
1103 					continue;
1104 				}
1105 
1106 				if (strcmp(tpgt->tpgt_tpg_name, tags[i])
1107 				    == 0) {
1108 					/* non-null tags will be created */
1109 					tags[i] = NULL;
1110 					break;
1111 				}
1112 			}
1113 			if (i == count) {
1114 				/* one to remove */
1115 				it_tpgt_t	*ptr = tpgt;
1116 
1117 				tpgt = ptr->tpgt_next;
1118 				it_tpgt_delete(cfg, tgtp, ptr);
1119 			} else {
1120 				tpgt = tpgt->tpgt_next;
1121 			}
1122 		}
1123 	}
1124 
1125 	/* see if there are any left to add */
1126 	for (i = 0; i < count; i++) {
1127 		if (!tags[i]) {
1128 			continue;
1129 		}
1130 
1131 		/* generate the tag number to use */
1132 		tag_name_to_num(tags[i], &tagid);
1133 
1134 		ret = it_tpgt_create(cfg, tgtp, &tpgt, tags[i], tagid);
1135 		if (ret != 0) {
1136 			if (ret == E2BIG) {
1137 				(void) fprintf(stderr, "%s\n",
1138 				    gettext("Error, no portal tag available"));
1139 			} else {
1140 				(void) fprintf(stderr, gettext(
1141 				    "Could not add target portal group"
1142 				    " tag %s, error %d"), tags[i], ret);
1143 				(void) fprintf(stderr, "\n");
1144 			}
1145 			goto done;
1146 		}
1147 	}
1148 
1149 	/* remove the tags from the proplist before continuing */
1150 	(void) nvlist_remove_all(proplist, "tpg-tag");
1151 
1152 	/*
1153 	 * Rename this target, if requested.  Save the old name in
1154 	 * the property list, so the kernel knows this is a renamed
1155 	 * target, and not a new one.
1156 	 */
1157 	if (newname && (strlen(newname) > 0)) {
1158 		ret = nvlist_add_string(proplist, "oldtargetname",
1159 		    tgtp->tgt_name);
1160 		if (ret != 0) {
1161 			(void) fprintf(stderr, "%s\n",
1162 			    gettext("Error renaming target."));
1163 			goto done;
1164 		}
1165 		(void) strlcpy(tgtp->tgt_name, newname,
1166 		    sizeof (tgtp->tgt_name));
1167 	}
1168 
1169 	ret = it_tgt_setprop(cfg, tgtp, proplist, &errlist);
1170 	if (ret != 0) {
1171 		(void) fprintf(stderr,
1172 		    gettext("Error setting target properties: %d"), ret);
1173 		(void) fprintf(stderr, "\n");
1174 		if (errlist) {
1175 			nvpair_t	*nvp = NULL;
1176 			char		*nn;
1177 			char		*nv;
1178 
1179 			while ((nvp = nvlist_next_nvpair(errlist, nvp))
1180 			    != NULL) {
1181 				nv = NULL;
1182 
1183 				nn = nvpair_name(nvp);
1184 				(void) nvpair_value_string(nvp, &nv);
1185 
1186 				if (nv != NULL) {
1187 					(void) fprintf(stderr, "\t%s: %s\n",
1188 					    nn, nv);
1189 				}
1190 			}
1191 
1192 			nvlist_free(errlist);
1193 		}
1194 		goto done;
1195 	}
1196 
1197 	if (ret == 0) {
1198 		ret = it_config_commit(cfg);
1199 		STMF_STALE(ret);
1200 	}
1201 
1202 done:
1203 	if (ret == 0) {
1204 		(void) printf(gettext("Target %s successfully modified"),
1205 		    tgtp->tgt_name);
1206 		(void) printf("\n");
1207 	}
1208 
1209 	it_config_free(cfg);
1210 
1211 	return (ret);
1212 }
1213 
1214 int
1215 create_tpg(char *tpg, int addrc, char **addrs)
1216 {
1217 	int		ret;
1218 	it_config_t	*cfg;
1219 	it_tpg_t	*tpgp;
1220 	int		count = 0;
1221 	it_portal_t	*ptl;
1222 	char		*sec = "solaris.smf.modify.stmf";
1223 
1224 	ITADM_CHKAUTH(sec);
1225 
1226 	if (!tpg) {
1227 		(void) fprintf(stderr, "%s\n",
1228 		    gettext("Error, no target portal group specified"));
1229 		return (EINVAL);
1230 	}
1231 
1232 	if (strlen(tpg) > (MAX_TPG_NAMELEN - 1)) {
1233 		(void) fprintf(stderr,
1234 		    gettext("Target Portal Group name must be no longer "
1235 		    "than %d characters."), (MAX_TPG_NAMELEN - 1));
1236 		(void) fprintf(stderr, "\n");
1237 		return (EINVAL);
1238 	}
1239 
1240 	if (!addrs || (addrc <= 0)) {
1241 		(void) fprintf(stderr, "%s\n",
1242 		    gettext("Error, no portal addresses specified"));
1243 		return (EINVAL);
1244 	}
1245 
1246 	ret = it_config_load(&cfg);
1247 	if (ret != 0) {
1248 		(void) fprintf(stderr,
1249 		    gettext("Error retrieving iSCSI target configuration: %d"),
1250 		    ret);
1251 		(void) fprintf(stderr, "\n");
1252 		return (ret);
1253 	}
1254 
1255 	tpgp = cfg->config_tpg_list;
1256 	while (tpgp != NULL) {
1257 		if (strcmp(tpgp->tpg_name, tpg) == 0) {
1258 			(void) fprintf(stderr,
1259 			    gettext("Target Portal Group %s already exists"),
1260 			    tpg);
1261 			(void) fprintf(stderr, "\n");
1262 			it_config_free(cfg);
1263 			return (1);
1264 		}
1265 		tpgp = tpgp->tpg_next;
1266 	}
1267 
1268 	/*
1269 	 * Create the portal group and first portal
1270 	 */
1271 	ret = it_tpg_create(cfg, &tpgp, tpg, addrs[count]);
1272 	if (ret != 0) {
1273 		if (ret == EEXIST) {
1274 			(void) fprintf(stderr,
1275 			    gettext("Portal %s already in use"),
1276 			    addrs[count]);
1277 			(void) fprintf(stderr, "\n");
1278 		}
1279 		it_config_free(cfg);
1280 		return (ret);
1281 	}
1282 
1283 	/*
1284 	 * Add the remaining portals
1285 	 */
1286 	for (count = 1; count < addrc; count++) {
1287 		if (!addrs[count]) {
1288 			continue;
1289 		}
1290 
1291 		ret = it_portal_create(cfg, tpgp, &ptl, addrs[count]);
1292 		if (ret != 0) {
1293 			if (ret == EEXIST) {
1294 				(void) fprintf(stderr,
1295 				    gettext("Portal %s already in use"),
1296 				    addrs[count]);
1297 				(void) fprintf(stderr, "\n");
1298 			} else {
1299 				(void) fprintf(stderr,
1300 				    gettext("Error adding portal %s: %d"),
1301 				    addrs[count], ret);
1302 				(void) fprintf(stderr, "\n");
1303 				break;
1304 			}
1305 		}
1306 	}
1307 
1308 	if (ret == 0) {
1309 		ret = it_config_commit(cfg);
1310 		STMF_STALE(ret);
1311 	}
1312 
1313 	it_config_free(cfg);
1314 
1315 	return (ret);
1316 }
1317 
1318 static int
1319 list_tpg(char *tpg, boolean_t verbose, boolean_t script)
1320 {
1321 	int		ret;
1322 	it_config_t	*cfg;
1323 	it_tpg_t	*ptr;
1324 	boolean_t	found = B_FALSE;
1325 	it_portal_t	*portal;
1326 	boolean_t	first = B_TRUE;
1327 	boolean_t	first_portal;
1328 	char		*pstr;
1329 	char		*sec = "solaris.smf.read.stmf";
1330 
1331 	ITADM_CHKAUTH(sec);
1332 
1333 	ret = it_config_load(&cfg);
1334 	if (ret != 0) {
1335 		(void) fprintf(stderr,
1336 		    gettext("Error retrieving iSCSI target configuration: %d"),
1337 		    ret);
1338 		(void) fprintf(stderr, "\n");
1339 		return (ret);
1340 	}
1341 
1342 	ptr = cfg->config_tpg_list;
1343 
1344 	for (; ptr != NULL; ptr = ptr->tpg_next) {
1345 		if (found) {
1346 			break;
1347 		}
1348 
1349 		if (tpg) {
1350 			if (strcmp(tpg, ptr->tpg_name) != 0) {
1351 				continue;
1352 			} else {
1353 				found = B_TRUE;
1354 			}
1355 		}
1356 
1357 		if (!script && first) {
1358 			(void) printf("%-30s%-9s\n", "TARGET PORTAL GROUP",
1359 			    "PORTAL COUNT");
1360 			first = B_FALSE;
1361 		}
1362 
1363 		if (!script) {
1364 			(void) printf("%-30s", ptr->tpg_name);
1365 			if (strlen(ptr->tpg_name) > 30) {
1366 				(void) printf("\t");
1367 			}
1368 			(void) printf("%-9d", ptr->tpg_portal_count);
1369 		} else {
1370 			(void) printf("%s\t%d", ptr->tpg_name,
1371 			    ptr->tpg_portal_count);
1372 		}
1373 
1374 		if (!verbose) {
1375 			(void) printf("\n");
1376 			continue;
1377 		}
1378 
1379 		if (!script) {
1380 			(void) printf("\n    portals:");
1381 		}
1382 
1383 		first_portal = B_TRUE;
1384 
1385 		portal = ptr->tpg_portal_list;
1386 		for (; portal != NULL; portal = portal->next) {
1387 			ret = sockaddr_to_str(&(portal->portal_addr), &pstr);
1388 			if (ret != 0) {
1389 				/* invalid addr? */
1390 				continue;
1391 			}
1392 			if (!first_portal) {
1393 				(void) printf(",");
1394 			} else {
1395 				(void) printf("\t");
1396 				first_portal = B_FALSE;
1397 			}
1398 
1399 			(void) printf("%s", pstr);
1400 			free(pstr);
1401 		}
1402 
1403 		if (first_portal) {
1404 			/* none found */
1405 			(void) printf("\t<none>");
1406 		}
1407 
1408 		(void) printf("\n");
1409 	}
1410 
1411 	if (tpg && (!found)) {
1412 		(void) fprintf(stderr,
1413 		    gettext("Target Portal Group %s not found!\n"), tpg);
1414 		(void) fprintf(stderr, "\n");
1415 		ret = 1;
1416 	}
1417 
1418 	it_config_free(cfg);
1419 
1420 	return (ret);
1421 }
1422 
1423 static int
1424 delete_tpg(char *tpg, boolean_t force)
1425 {
1426 	int		ret;
1427 	it_config_t	*cfg;
1428 	it_tpg_t	*ptpg = NULL;
1429 	char		*sec = "solaris.smf.modify.stmf";
1430 
1431 	ITADM_CHKAUTH(sec);
1432 
1433 	if (!tpg) {
1434 		(void) fprintf(stderr, "%s\n",
1435 		    gettext("Error, no target portal group specified"));
1436 		return (EINVAL);
1437 	}
1438 
1439 	ret = it_config_load(&cfg);
1440 	if (ret != 0) {
1441 		(void) fprintf(stderr,
1442 		    gettext("Error retrieving iSCSI target configuration: %d"),
1443 		    ret);
1444 		(void) fprintf(stderr, "\n");
1445 		return (ret);
1446 	}
1447 
1448 	ptpg = cfg->config_tpg_list;
1449 	for (; ptpg != NULL; ptpg = ptpg->tpg_next) {
1450 		if (strcmp(tpg, ptpg->tpg_name) == 0) {
1451 			break;
1452 		}
1453 	}
1454 
1455 	if (!ptpg) {
1456 		(void) fprintf(stderr,
1457 		    gettext("Target portal group %s does not exist."),
1458 		    tpg);
1459 		(void) fprintf(stderr, "\n");
1460 		ret = 1;
1461 	} else {
1462 		ret = it_tpg_delete(cfg, ptpg, force);
1463 		if (ret == EBUSY) {
1464 			(void) fprintf(stderr, "%s\n",
1465 			    gettext(
1466 			    "Target portal group associated with one or more "
1467 			    "targets.  Cannot delete."));
1468 		}
1469 
1470 		if (ret == 0) {
1471 			ret = it_config_commit(cfg);
1472 			STMF_STALE(ret);
1473 		}
1474 	}
1475 
1476 	it_config_free(cfg);
1477 
1478 	return (ret);
1479 }
1480 
1481 static int
1482 modify_initiator(char *ini, nvlist_t *proplist, boolean_t create)
1483 {
1484 	int		ret;
1485 	it_config_t	*cfg;
1486 	it_ini_t	*inip;
1487 	nvlist_t	*errlist = NULL;
1488 	nvpair_t	*nvp = NULL;
1489 	char		*sec = "solaris.smf.modify.stmf";
1490 	boolean_t	changed = B_TRUE;
1491 
1492 	ITADM_CHKAUTH(sec);
1493 
1494 	if (!ini) {
1495 		(void) fprintf(stderr, "%s\n",
1496 		    gettext("Error, no initiator specified"));
1497 		return (EINVAL);
1498 	} else if (create) {
1499 		/*
1500 		 * validate input name - what are the rules for EUI
1501 		 * and IQN values?
1502 		 */
1503 		if ((strncmp(ini, "eui.", 4) != 0) &&
1504 		    (strncmp(ini, "iqn.", 4) != 0)) {
1505 			(void) fprintf(stderr, gettext("Invalid name %s"),
1506 			    ini);
1507 			(void) fprintf(stderr, "\n");
1508 			return (EINVAL);
1509 		}
1510 	}
1511 
1512 	/*
1513 	 * See if any properties were actually specified.
1514 	 */
1515 	if (proplist) {
1516 		nvp = nvlist_next_nvpair(proplist, nvp);
1517 	}
1518 
1519 	if ((nvp == NULL) && !create) {
1520 		changed = B_FALSE;
1521 	}
1522 
1523 	/*
1524 	 * If no properties, and this is really a modify op, verify
1525 	 * that the requested initiator exists, but then don't do anything.
1526 	 * Modifying non-existent is an error; doing nothing to a defined
1527 	 * initiator is not.
1528 	 */
1529 
1530 	ret = it_config_load(&cfg);
1531 	if (ret != 0) {
1532 		(void) fprintf(stderr,
1533 		    gettext("Error retrieving iSCSI target configuration: %d"),
1534 		    ret);
1535 		(void) fprintf(stderr, "\n");
1536 		return (ret);
1537 	}
1538 
1539 	inip = cfg->config_ini_list;
1540 	while (inip) {
1541 		if (strcmp(inip->ini_name, ini) == 0) {
1542 			break;
1543 		}
1544 
1545 		inip = inip->ini_next;
1546 	}
1547 
1548 	if (create) {
1549 		if (inip) {
1550 			(void) fprintf(stderr,
1551 			    gettext("Initiator %s already exists"),
1552 			    inip->ini_name);
1553 			(void) fprintf(stderr, "\n");
1554 			ret = EINVAL;
1555 		} else {
1556 			ret = it_ini_create(cfg, &inip, ini);
1557 			if (ret != 0) {
1558 				if (ret == EFAULT) {
1559 					(void) fprintf(stderr,
1560 					    gettext("Invalid iSCSI name %s"),
1561 					    ini);
1562 				} else {
1563 					(void) fprintf(stderr,
1564 					    gettext(
1565 					    "Error creating initiator: %d"),
1566 					    ret);
1567 				}
1568 				(void) fprintf(stderr, "\n");
1569 			}
1570 		}
1571 	} else if (!inip) {
1572 		ret = ENOENT;
1573 		(void) fprintf(stderr,
1574 		    gettext("Error, initiator %s not found."),
1575 		    ini);
1576 		(void) fprintf(stderr, "\n");
1577 	}
1578 
1579 	if ((ret == 0) && nvp) {
1580 		ret = it_ini_setprop(inip, proplist, &errlist);
1581 
1582 		if (ret != 0) {
1583 			(void) fprintf(stderr,
1584 			    gettext("Error setting initiator properties: %d"),
1585 			    ret);
1586 			(void) fprintf(stderr, "\n");
1587 			if (errlist) {
1588 				nvpair_t	*nvp = NULL;
1589 				char		*nn;
1590 				char		*nv;
1591 
1592 				while ((nvp = nvlist_next_nvpair(errlist, nvp))
1593 				    != NULL) {
1594 					nv = NULL;
1595 
1596 					nn = nvpair_name(nvp);
1597 					(void) nvpair_value_string(nvp, &nv);
1598 
1599 					if (nv != NULL) {
1600 						(void) fprintf(stderr,
1601 						    "\t%s: %s\n", nn, nv);
1602 					}
1603 				}
1604 
1605 				nvlist_free(errlist);
1606 			}
1607 		}
1608 	}
1609 
1610 	if ((ret == 0) && changed) {
1611 		ret = it_config_commit(cfg);
1612 		STMF_STALE(ret);
1613 	}
1614 
1615 	it_config_free(cfg);
1616 
1617 	return (ret);
1618 }
1619 
1620 static int
1621 list_initiator(char *ini, boolean_t verbose, boolean_t script) /* ARGSUSED */
1622 {
1623 	int		ret;
1624 	it_config_t	*cfg;
1625 	it_ini_t	*ptr;
1626 	boolean_t	found = B_FALSE;
1627 	boolean_t	first = B_TRUE;
1628 	char		*isecret;
1629 	char		*iuser;
1630 	char		*sec = "solaris.smf.read.stmf";
1631 
1632 	ITADM_CHKAUTH(sec);
1633 
1634 	ret = it_config_load(&cfg);
1635 	if (ret != 0) {
1636 		(void) fprintf(stderr,
1637 		    gettext("Error retrieving iSCSI target configuration: %d"),
1638 		    ret);
1639 		(void) fprintf(stderr, "\n");
1640 		return (ret);
1641 	}
1642 
1643 	ptr = cfg->config_ini_list;
1644 
1645 	for (; ptr != NULL; ptr = ptr->ini_next) {
1646 		isecret = "unset";
1647 		iuser = "<none>";
1648 
1649 		if (found) {
1650 			break;
1651 		}
1652 
1653 		if (ini) {
1654 			if (strcmp(ini, ptr->ini_name) != 0) {
1655 				continue;
1656 			} else {
1657 				found = B_TRUE;
1658 			}
1659 		}
1660 
1661 		if (ptr->ini_properties) {
1662 			if (nvlist_exists(ptr->ini_properties, "chapsecret")) {
1663 				isecret = "set";
1664 			}
1665 			(void) nvlist_lookup_string(ptr->ini_properties,
1666 			    "chapuser", &iuser);
1667 
1668 		}
1669 
1670 		/* there's nothing to print for verbose yet */
1671 		if (!script && first) {
1672 			(void) printf("%-61s%-10s%-7s\n", "INITIATOR NAME",
1673 			    "CHAPUSER", "SECRET");
1674 			first = B_FALSE;
1675 		}
1676 
1677 		if (!script) {
1678 			/*
1679 			 * try not to let columns run into each other.
1680 			 * Stick a tab after too-long fields.
1681 			 * Lengths chosen are for the 'common' cases.
1682 			 */
1683 			(void) printf("%-61s", ptr->ini_name);
1684 
1685 			if (strlen(ptr->ini_name) > 60) {
1686 				(void) printf("\t");
1687 			}
1688 
1689 			(void) printf("%-15s", iuser);
1690 			if (strlen(iuser) >= 15) {
1691 				(void) printf("\t");
1692 			}
1693 
1694 			(void) printf("%-4s", isecret);
1695 		} else {
1696 			(void) printf("%s\t%s\t%s", ptr->ini_name,
1697 			    iuser, isecret);
1698 		}
1699 
1700 		(void) printf("\n");
1701 	}
1702 
1703 	if (ini && (!found)) {
1704 		(void) fprintf(stderr,
1705 		    gettext("Initiator %s not found!"), ini);
1706 		(void) fprintf(stderr, "\n");
1707 		ret = 1;
1708 	}
1709 
1710 	it_config_free(cfg);
1711 
1712 	return (ret);
1713 }
1714 
1715 int
1716 delete_initiator(char *ini)
1717 {
1718 	int		ret;
1719 	it_config_t	*cfg;
1720 	it_ini_t	*ptr;
1721 	char		*sec = "solaris.smf.modify.stmf";
1722 
1723 	ITADM_CHKAUTH(sec);
1724 
1725 	if (!ini) {
1726 		(void) fprintf(stderr, "%s\n",
1727 		    gettext("Error, no initiator specified"));
1728 		return (EINVAL);
1729 	}
1730 
1731 	ret = it_config_load(&cfg);
1732 	if (ret != 0) {
1733 		(void) fprintf(stderr,
1734 		    gettext("Error retrieving iSCSI target configuration: %d"),
1735 		    ret);
1736 		(void) fprintf(stderr, "\n");
1737 		return (ret);
1738 	}
1739 
1740 	ptr = cfg->config_ini_list;
1741 	while (ptr) {
1742 		if (strcmp(ptr->ini_name, ini) == 0) {
1743 			break;
1744 		}
1745 
1746 		ptr = ptr->ini_next;
1747 	}
1748 
1749 	if (ptr) {
1750 		it_ini_delete(cfg, ptr);
1751 
1752 		ret = it_config_commit(cfg);
1753 		STMF_STALE(ret);
1754 	} else {
1755 		(void) fprintf(stderr,
1756 		    gettext("Initiator %s not found"), ini);
1757 		(void) fprintf(stderr, "\n");
1758 		ret = 1;
1759 	}
1760 
1761 	return (ret);
1762 }
1763 
1764 static int
1765 modify_defaults(nvlist_t *proplist)
1766 {
1767 	int		ret;
1768 	it_config_t	*cfg;
1769 	nvlist_t	*errlist = NULL;
1770 	nvpair_t	*nvp = NULL;
1771 	char		*sec = "solaris.smf.modify.stmf";
1772 
1773 	ITADM_CHKAUTH(sec);
1774 
1775 	if (proplist) {
1776 		/* make sure at least one property is specified */
1777 		nvp = nvlist_next_nvpair(proplist, nvp);
1778 	}
1779 
1780 	if (nvp == NULL) {
1781 		/* empty list */
1782 		(void) fprintf(stderr, "%s\n",
1783 		    gettext("Error, no properties specified"));
1784 		return (EINVAL);
1785 	}
1786 
1787 	ret = it_config_load(&cfg);
1788 	if (ret != 0) {
1789 		(void) fprintf(stderr,
1790 		    gettext("Error retrieving iSCSI target configuration: %d"),
1791 		    ret);
1792 		(void) fprintf(stderr, "\n");
1793 		return (ret);
1794 	}
1795 
1796 	ret = it_config_setprop(cfg, proplist, &errlist);
1797 	if (ret != 0) {
1798 		(void) fprintf(stderr,
1799 		    gettext("Error setting global properties: %d"),
1800 		    ret);
1801 		(void) fprintf(stderr, "\n");
1802 		if (errlist) {
1803 			nvpair_t	*nvp = NULL;
1804 			char		*nn;
1805 			char		*nv;
1806 
1807 			while ((nvp = nvlist_next_nvpair(errlist, nvp))
1808 			    != NULL) {
1809 				nv = NULL;
1810 
1811 				nn = nvpair_name(nvp);
1812 				(void) nvpair_value_string(nvp, &nv);
1813 
1814 				if (nv != NULL) {
1815 					(void) fprintf(stderr, "\t%s: %s\n",
1816 					    nn, nv);
1817 				}
1818 			}
1819 
1820 			nvlist_free(errlist);
1821 		}
1822 	}
1823 
1824 	if (ret == 0) {
1825 		ret = it_config_commit(cfg);
1826 		STMF_STALE(ret);
1827 	}
1828 
1829 	it_config_free(cfg);
1830 
1831 	return (ret);
1832 }
1833 
1834 static int
1835 list_defaults(boolean_t script)
1836 {
1837 	int		ret;
1838 	it_config_t	*cfg;
1839 	nvlist_t	*nvl;
1840 	char		*alias = "<none>";
1841 	char		*auth = "<none>";
1842 	char		*isns = "disabled";
1843 	char		**isvrs = NULL;
1844 	uint32_t	scount = 0;
1845 	char		*rsvr = "<none>";
1846 	char		*rsecret = "unset";
1847 	boolean_t	val = B_FALSE;
1848 	int		i;
1849 	char		*sec = "solaris.smf.read.stmf";
1850 
1851 	ITADM_CHKAUTH(sec);
1852 
1853 	ret = it_config_load(&cfg);
1854 	if (ret != 0) {
1855 		(void) fprintf(stderr,
1856 		    gettext("Error retrieving iSCSI target configuration: %d"),
1857 		    ret);
1858 		(void) fprintf(stderr, "\n");
1859 		return (ret);
1860 	}
1861 
1862 	nvl = cfg->config_global_properties;
1863 
1864 	/* look up all possible options */
1865 	(void) nvlist_lookup_string(nvl, "alias", &alias);
1866 	(void) nvlist_lookup_string(nvl, "auth", &auth);
1867 	(void) nvlist_lookup_boolean_value(nvl, "isns", &val);
1868 	if (val == B_TRUE) {
1869 		isns = "enabled";
1870 	}
1871 	(void) nvlist_lookup_string_array(nvl, "isnsserver", &isvrs,
1872 	    &scount);
1873 	(void) nvlist_lookup_string(nvl, "radiusserver", &rsvr);
1874 	if (nvlist_exists(nvl, "radiussecret")) {
1875 		rsecret = "set";
1876 	}
1877 
1878 	if (!script) {
1879 		(void) printf("%s:\n\n",
1880 		    gettext("iSCSI Target Default Properties"));
1881 	}
1882 
1883 	if (script) {
1884 		(void) printf("%s\t%s\t%s\t%s\t%s\t",
1885 		    alias, auth, rsvr, rsecret, isns);
1886 	} else {
1887 		(void) printf("%-15s\t%s\n%-15s\t%s\n%-15s\t%s\n%-15s\t%s\n"
1888 		    "%-15s\t%s\n%-15s\t",
1889 		    "alias:", alias, "auth:", auth, "radiusserver:",
1890 		    rsvr, "radiussecret:", rsecret, "isns:", isns,
1891 		    "isnsserver:");
1892 	}
1893 
1894 	for (i = 0; i < scount; i++) {
1895 		if (!isvrs || !isvrs[i]) {
1896 			break;
1897 		}
1898 		if (i > 0) {
1899 			(void) printf(",");
1900 		}
1901 		(void) printf("%s", isvrs[i]);
1902 	}
1903 
1904 	if (i == 0) {
1905 		(void) printf("%s", "<none>");
1906 	}
1907 
1908 	(void) printf("\n");
1909 
1910 	it_config_free(cfg);
1911 
1912 	return (0);
1913 }
1914 
1915 static int
1916 itadm_get_password(nvlist_t *nvl, char *key, char *passfile,
1917     char *phrase)
1918 {
1919 	int		ret = 0;
1920 	char		*pass;
1921 	char		buf[1024];
1922 	int		fd;
1923 	struct stat64	sbuf;
1924 	size_t		rd;
1925 
1926 	if (!nvl || !key) {
1927 		return (EINVAL);
1928 	}
1929 
1930 	if (passfile) {
1931 		ret = stat64(passfile, &sbuf);
1932 		if ((ret != 0) || (!S_ISREG(sbuf.st_mode))) {
1933 			(void) fprintf(stderr,
1934 			    gettext("Invalid secret file %s"),
1935 			    passfile);
1936 			(void) fprintf(stderr, "\n");
1937 			return (EBADF);
1938 		}
1939 
1940 		fd = open64(passfile, O_RDONLY);
1941 		if (fd == -1) {
1942 			ret = errno;
1943 			(void) fprintf(stderr,
1944 			    gettext("Could not open secret file %s, %d"),
1945 			    passfile, ret);
1946 			(void) fprintf(stderr, "\n");
1947 			return (ret);
1948 		}
1949 
1950 		rd = read(fd, buf, sbuf.st_size);
1951 		(void) close(fd);
1952 
1953 		if (rd != sbuf.st_size) {
1954 			ret = EIO;
1955 			(void) fprintf(stderr,
1956 			    gettext("Could not read secret file %s, %d"),
1957 			    passfile, ret);
1958 			(void) fprintf(stderr, "\n");
1959 			return (ret);
1960 		}
1961 
1962 		/* ensure buf is properly terminated */
1963 		buf[rd] = '\0';
1964 
1965 		/* if last char is a newline, strip it off */
1966 		if (buf[rd - 1] == '\n') {
1967 			buf[rd - 1] = '\0';
1968 		}
1969 
1970 		/* validate length */
1971 		if ((strlen(buf) > 255) || (strlen(buf) < 12)) {
1972 			(void) fprintf(stderr, "%s\n",
1973 			    gettext(
1974 			    "Secret must be between 12 and 255 characters"));
1975 			return (EINVAL);
1976 		}
1977 	} else {
1978 		/* prompt for secret */
1979 		if (!phrase) {
1980 			return (EINVAL);
1981 		}
1982 
1983 		pass = getpassphrase(phrase);
1984 		if (!pass) {
1985 			ret = errno;
1986 			(void) fprintf(stderr,
1987 			    gettext("Could not read secret, %d"),
1988 			    ret);
1989 			(void) fprintf(stderr, "\n");
1990 			return (ret);
1991 		}
1992 
1993 		/* validate length */
1994 		if ((strlen(pass) > 255) || (strlen(pass) < 12)) {
1995 			(void) fprintf(stderr, "%s\n",
1996 			    gettext(
1997 			    "Secret must be between 12 and 255 characters"));
1998 			return (EINVAL);
1999 		}
2000 
2001 		(void) strlcpy(buf, pass, sizeof (buf));
2002 
2003 		/* confirm entered secret */
2004 		pass = getpassphrase(gettext("Re-enter secret: "));
2005 		if (!pass) {
2006 			ret = errno;
2007 			(void) fprintf(stderr,
2008 			    gettext("Could not read secret, %d"),
2009 			    ret);
2010 			(void) fprintf(stderr, "\n");
2011 			return (ret);
2012 		}
2013 
2014 		if (strcmp(buf, pass) != 0) {
2015 			ret = EINVAL;
2016 			(void) fprintf(stderr, "%s\n",
2017 			    gettext("Secret validation failed"));
2018 			return (ret);
2019 		}
2020 
2021 	}
2022 
2023 	ret = nvlist_add_string(nvl, key, buf);
2024 
2025 	return (ret);
2026 }
2027 
2028 static int
2029 itadm_opt_to_arr(nvlist_t *nvl, char *key, char *opt, uint32_t *num)
2030 {
2031 	int		count;
2032 	char		*bufp;
2033 	char		**arr;
2034 
2035 	if (!opt || !key || !nvl) {
2036 		return (EINVAL);
2037 	}
2038 
2039 	bufp = opt;
2040 	count = 1;
2041 
2042 	for (;;) {
2043 		bufp = strchr(bufp, ',');
2044 		if (!bufp) {
2045 			break;
2046 		}
2047 		bufp++;
2048 		count++;
2049 	}
2050 
2051 	arr = calloc(count, sizeof (char *));
2052 	if (!arr) {
2053 		return (ENOMEM);
2054 	}
2055 
2056 	bufp = opt;
2057 	/* set delimiter to comma */
2058 	(void) bufsplit(",", 0, NULL);
2059 
2060 	/* split up that buf! */
2061 	(void) bufsplit(bufp, count, arr);
2062 
2063 	/* if requested, return the number of array members found */
2064 	if (num) {
2065 		*num = count;
2066 	}
2067 
2068 	return (nvlist_add_string_array(nvl, key, arr, count));
2069 }
2070 
2071 static void
2072 tag_name_to_num(char *tagname, uint16_t *tagnum)
2073 {
2074 	ulong_t		id;
2075 	char		*ptr = NULL;
2076 
2077 	if (!tagname || !tagnum) {
2078 		return;
2079 	}
2080 
2081 	*tagnum = 0;
2082 
2083 	id = strtoul(tagname, &ptr, 10);
2084 
2085 	/* Must be entirely numeric and in-range */
2086 	if (ptr && (*ptr != '\0')) {
2087 		return;
2088 	}
2089 
2090 	if ((id <= UINT16_MAX) && (id > 1)) {
2091 		*tagnum = (uint16_t)id;
2092 	}
2093 }
2094