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