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