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