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) 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#include <dirent.h>
45
46#include <libstmf.h>
47#include <libsrpt.h>
48
49/* SMF service info */
50#define	STMF_SVC	"svc:/system/stmf:default"
51
52#define	STMF_STALE(ret) {\
53	if (ret == STMF_ERROR_PROV_DATA_STALE) {\
54		(void) fprintf(stderr, "%s\n",\
55		    gettext("Configuration changed during processing.  "\
56		    "Check the configuration, then retry this command "\
57		    "if appropriate."));\
58	}\
59}
60
61#define	SRPTADM_CHKAUTH(sec) {\
62	if (!chkauthattr(sec, srptadm_uname)) {\
63		(void) fprintf(stderr,\
64		    gettext("Error, operation requires authorization %s"),\
65		    sec);\
66		(void) fprintf(stderr, "\n");\
67		return (1);\
68	}\
69}
70
71#define	PROPS_FORMAT	"    %-20s: "
72
73static struct option srptadm_long[] = {
74	{"enable",		no_argument,		NULL, 'e'},
75	{"disable",		no_argument,		NULL, 'd'},
76	{"reset",		no_argument,		NULL, 'r'},
77	{"help",		no_argument,		NULL, '?'},
78	{"help",		no_argument,		NULL, 'h'},
79	{NULL, 0, NULL, 0}
80};
81
82static char m_def[] = "srptadm modify-defaults [-e] [-d]";
83static char l_def[] = "srptadm list-defaults";
84static char s_tgt[] = "srptadm modify-target [-e] [-d] [-r] <hca>";
85static char l_tgt[] = "srptadm list-target [<hca>]";
86
87/* keep the order of this enum in the same order as the 'subcmds' struct */
88typedef enum {
89	MODIFY_DEFAULT,
90	LIST_DEFAULT,
91	MODIFY_TARGET,
92	LIST_TARGET,
93	NULL_SUBCMD	/* must always be last! */
94} srptadm_sub_t;
95
96typedef struct {
97	char		*name;
98	char		*shortopts;
99	char		*usemsg;
100} srptadm_subcmds_t;
101
102static srptadm_subcmds_t	subcmds[] = {
103	{"modify-defaults", "edh?", m_def},
104	{"list-defaults", "h?", l_def},
105	{"modify-target", "edrh?", s_tgt},
106	{"list-target", "h?", l_tgt},
107	{NULL, ":h?", NULL},
108};
109
110/* used for checking if user is authorized */
111static char *srptadm_uname = NULL;
112
113/* prototypes */
114static int get_local_hcas(char **hcaArray, int count);
115static int print_target_props(char *hca);
116static int list_target(char *hca);
117static int disable_target(char *hca);
118static int reset_target(char *hca);
119static int list_defaults(void);
120static int enable_target(char *hca);
121static int set_default_state(boolean_t enabled);
122
123int
124main(int argc, char *argv[])
125{
126	int		ret = 0;
127	int		idx = NULL_SUBCMD;
128	char		c;
129	int		newargc = argc;
130	char		**newargv = NULL;
131	char		*objp;
132	int		srptind = 0;
133	struct passwd	*pwd = NULL;
134	char		*smfstate = NULL;
135	boolean_t	reset = B_FALSE;
136	int		dflag = 0;
137	int		eflag = 0;
138
139	(void) setlocale(LC_ALL, "");
140	(void) textdomain(TEXT_DOMAIN);
141
142	if (argc < 2) {
143		ret = 1;
144		goto usage_error;
145	}
146
147	for (idx = 0; subcmds[idx].name != NULL; idx++) {
148		if (strcmp(argv[1], subcmds[idx].name) == 0) {
149			break;
150		}
151	}
152
153	/* get the caller's user name for subsequent chkauthattr() calls */
154	pwd = getpwuid(getuid());
155	if (pwd == NULL) {
156		(void) fprintf(stderr, "%s\n",
157		    gettext("Could not determine callers user name."));
158		return (1);
159	}
160
161	srptadm_uname = strdup(pwd->pw_name);
162
163	/* increment past command & subcommand */
164	newargc--;
165	newargv = &(argv[1]);
166
167	while ((ret == 0) && (newargv)) {
168		c = getopt_long(newargc, newargv, subcmds[idx].shortopts,
169		    srptadm_long, &srptind);
170		if (c == -1) {
171			break;
172		}
173
174		switch (c) {
175			case 0:
176				/* flag set by getopt */
177				break;
178			case 'd':
179				dflag++;
180				break;
181			case 'e':
182				eflag++;
183				break;
184			case 'r':
185				reset = B_TRUE;
186				break;
187			case '?':
188				/*
189				 * '?' is returned for both unrecognized
190				 * options and if explicitly provided on
191				 * the command line.  The latter should
192				 * be handled the same as -h.
193				 */
194				if (strcmp(newargv[optind-1], "-?") != 0) {
195					(void) fprintf(stderr,
196					    gettext("Unrecognized option %s"),
197					    newargv[optind-1]);
198					(void) fprintf(stderr, "\n");
199					ret = 1;
200				}
201				goto usage_error;
202			case 'h':
203				goto usage_error;
204			case ':':
205				(void) fprintf(stderr,
206				    gettext("Option %s requires an operand."),
207				    newargv[optind-1]);
208				(void) fprintf(stderr, "\n");
209
210				/* FALLTHROUGH */
211			default:
212				ret = 1;
213				break;
214		}
215	}
216
217	if (ret != 0) {
218		goto usage_error;
219	}
220
221	/* after getopt() to allow handling of -h option */
222	if ((srptadm_sub_t)idx == NULL_SUBCMD) {
223		(void) fprintf(stderr, "%s\n",
224		    gettext("Error, no subcommand specified"));
225		ret = 1;
226		goto usage_error;
227	}
228
229	newargc -= optind;
230	if (newargc == 0) {
231		newargv = NULL;
232		objp = NULL;
233	} else {
234		newargv = &(newargv[optind]);
235		objp = newargv[0];
236	}
237
238	if (objp == NULL) {
239		switch ((srptadm_sub_t)idx) {
240		case MODIFY_TARGET:
241			/* These subcommands need operands */
242			ret = 1;
243			goto usage_error;
244		default:
245			break;
246		}
247	}
248
249	if (newargc > 1) {
250		switch ((srptadm_sub_t)idx) {
251		case MODIFY_TARGET:
252		case LIST_TARGET:
253			/* These subcommands should have at most one operand */
254			ret = 1;
255			goto usage_error;
256
257		default:
258			break;
259		}
260	}
261
262
263	/*
264	 * Make sure STMF service is enabled before proceeding.
265	 */
266	smfstate = smf_get_state(STMF_SVC);
267	if (!smfstate ||
268	    (strcmp(smfstate, SCF_STATE_STRING_ONLINE) != 0)) {
269		(void) fprintf(stderr, "%s\n",
270		    gettext("The STMF service must be online "
271		    "before running this command."));
272		(void) fprintf(stderr,
273		    gettext("Use 'svcadm enable -r %s'"), STMF_SVC);
274		(void) fprintf(stderr, "\n");
275		(void) fprintf(stderr, "%s\n",
276		    gettext("to enable the service and its prerequisite "
277		    "services and/or"));
278		(void) fprintf(stderr,
279		    gettext("'svcs -x %s' to determine why it is not online."),
280		    STMF_SVC);
281		(void) fprintf(stderr, "\n");
282
283		return (1);
284	}
285
286	switch ((srptadm_sub_t)idx) {
287		case MODIFY_DEFAULT:
288			if (eflag) {
289				ret = set_default_state(B_TRUE);
290			} else if (dflag) {
291				ret = set_default_state(B_FALSE);
292			} else {
293				ret = 1;
294				goto usage_error;
295			}
296			break;
297		case LIST_DEFAULT:
298			ret = list_defaults();
299			break;
300		case MODIFY_TARGET:
301			if (reset) {
302				ret = reset_target(objp);
303			} else if (eflag) {
304				ret = enable_target(objp);
305			} else if (dflag) {
306				ret = disable_target(objp);
307			} else {
308				ret = 1;
309				goto usage_error;
310			}
311			break;
312		case LIST_TARGET:
313			ret = list_target(objp);
314			break;
315		default:
316			ret = 1;
317			goto usage_error;
318	}
319
320	if (ret != 0) {
321		(void) fprintf(stderr,
322		    gettext("srptadm %s failed with error %d"),
323		    subcmds[idx].name, ret);
324		(void) fprintf(stderr, "\n");
325	}
326	return (ret);
327
328usage_error:
329	if (subcmds[idx].name) {
330		(void) printf("%s\n", gettext(subcmds[idx].usemsg));
331	} else {
332		/* overall usage */
333		(void) printf("%s\n\n", gettext("srptadm usage:"));
334		for (idx = 0; subcmds[idx].name != NULL; idx++) {
335			if (!subcmds[idx].usemsg) {
336				continue;
337			}
338			(void) printf("\t%s\n", gettext(subcmds[idx].usemsg));
339		}
340	}
341
342	return (ret);
343}
344
345static int
346set_default_state(boolean_t enabled)
347{
348	int		ret;
349	char		*sec = "solaris.smf.modify.stmf";
350
351	SRPTADM_CHKAUTH(sec);
352
353	ret = srpt_SetDefaultState(enabled);
354
355	return (ret);
356}
357
358static int
359enable_target(char *hca)
360{
361	int		ret;
362	char		*sec = "solaris.smf.modify.stmf";
363
364	SRPTADM_CHKAUTH(sec);
365
366	ret = srpt_SetTargetState(hca, B_TRUE);
367
368	return (ret);
369}
370
371static int
372disable_target(char *hca)
373{
374	int		ret;
375	char		*sec = "solaris.smf.modify.stmf";
376
377	SRPTADM_CHKAUTH(sec);
378
379	ret = srpt_SetTargetState(hca, B_FALSE);
380
381	return (ret);
382}
383
384static int
385reset_target(char *hca)
386{
387	int		ret;
388	char		*sec = "solaris.smf.modify.stmf";
389
390	SRPTADM_CHKAUTH(sec);
391
392	ret = srpt_ResetTarget(hca);
393
394	return (ret);
395}
396
397static int
398list_defaults(void)
399{
400	int		ret;
401	char		*sec = "solaris.smf.read.stmf";
402	boolean_t	enabled;
403
404	SRPTADM_CHKAUTH(sec);
405
406	/* only state set as default for now */
407	ret = srpt_GetDefaultState(&enabled);
408
409	if (ret == 0) {
410		(void) printf("%s:\n\n",
411		    gettext("SRP Target Service Default Properties"));
412
413		(void) printf("    %s:\t",
414		    gettext("Target creation enabled by default"));
415
416		if (enabled) {
417			(void) printf("%s\n", gettext("true"));
418		} else {
419			(void) printf("%s\n", gettext("false"));
420		}
421	}
422
423	return (ret);
424}
425
426static int
427list_target(char *hca)
428{
429	int		ret;
430	char		*sec = "solaris.smf.read.stmf";
431	char		*hcaArr[1024];	/* way bigger than we'll ever see */
432	int		i;
433
434	SRPTADM_CHKAUTH(sec);
435
436	if (hca != NULL) {
437		ret = print_target_props(hca);
438		return (ret);
439	}
440
441	/* get list of HCAs configured on this system, from /dev/cfg */
442	(void) memset(&hcaArr, 0, 1024 * sizeof (char *));
443
444	ret = get_local_hcas(hcaArr, sizeof (hcaArr));
445	if (ret == ETOOMANYREFS) {
446		(void) fprintf(stderr, "Internal error:  too many HCAs\n");
447		goto done;
448	} else if (ret != 0) {
449		(void) fprintf(stderr, "Error getting list of HCAs: %d\n", ret);
450		goto done;
451	}
452
453	for (i = 0; i < 1024; i++) {
454		if (hcaArr[i] == NULL) {
455			break;
456		}
457		ret = print_target_props(hcaArr[i]);
458	}
459
460done:
461	for (i = 0; i < 1024; i++) {
462		if (hcaArr[i] == NULL) {
463			break;
464		}
465		free(hcaArr[i]);
466	}
467
468	return (ret);
469}
470
471static int
472print_target_props(char *hca)
473{
474	int		ret;
475	boolean_t	enabled;
476	char		buf[32];
477	char		euibuf[64];
478	uint64_t	hcaguid;
479	stmfDevid	devid;
480	stmfTargetProperties	props;
481	char		*state;
482
483	ret = srpt_NormalizeGuid(hca, buf, sizeof (buf), &hcaguid);
484	if (ret != 0) {
485		(void) fprintf(stderr, "Invalid target HCA: %s\n",
486		    hca);
487		return (ret);
488	}
489
490	/* only property set is enabled */
491	ret = srpt_GetTargetState(buf, &enabled);
492	if (ret != 0) {
493		(void) fprintf(stderr,
494		    "Could not get enabled state for %s: %d\n",
495		    buf, ret);
496		return (ret);
497	}
498
499	(void) printf("Target HCA %s:\n", buf);
500
501	(void) printf(PROPS_FORMAT, gettext("Enabled"));
502
503	if (enabled) {
504		(void) printf("%s\n", gettext("true"));
505	} else {
506		(void) printf("%s\n", gettext("false"));
507	}
508
509	state = "-";
510
511	(void) snprintf(euibuf, sizeof (euibuf), "eui.%016llX", hcaguid);
512
513	ret = stmfDevidFromIscsiName(euibuf, &devid);
514	if (ret == STMF_STATUS_SUCCESS) {
515		ret = stmfGetTargetProperties(&devid, &props);
516		if (ret == STMF_STATUS_SUCCESS) {
517			if (props.status == STMF_TARGET_PORT_ONLINE) {
518				state = "online";
519			} else {
520				state = "offline";
521			}
522		}
523	}
524
525	(void) printf(PROPS_FORMAT, gettext("SRP Target Name"));
526	(void) printf("%s\n", euibuf);
527	(void) printf(PROPS_FORMAT, gettext("Operational Status"));
528	(void) printf("%s\n", state);
529
530	(void) printf("\n");
531
532	return (0);
533}
534
535
536static int
537get_local_hcas(char **hcaArray, int count)
538{
539	int		ret = 0;
540	char		*cfgdir = "/dev/cfg";
541	DIR		*dirp = NULL;
542	struct dirent	*entry;
543	int		idx = 0;
544	char		*bufp;
545
546	if ((hcaArray == NULL) || (count == 0)) {
547		return (EINVAL);
548	}
549
550	dirp = opendir(cfgdir);
551
552	if (dirp == NULL) {
553		ret = errno;
554		(void) fprintf(stderr, "Could not open %s: errno %d\n",
555		    cfgdir, ret);
556		return (ret);
557	}
558
559	while ((entry = readdir(dirp)) != NULL) {
560		bufp = &entry->d_name[0];
561
562		if (strncmp(bufp, "hca:", 4) != 0) {
563			continue;
564		}
565
566		bufp += 4;
567
568		hcaArray[idx] = strdup(bufp);
569		if (hcaArray[idx] == NULL) {
570			ret = ENOMEM;
571			break;
572		}
573		idx++;
574
575		if (idx >= count) {
576			ret = ETOOMANYREFS;
577			break;
578		}
579	}
580
581	return (ret);
582}
583