/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/vtoc.h>
#include <sys/stat.h>
#include <stdio.h>
#include <sys/mnttab.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

#include <locale.h>
#include <langinfo.h>
#include <libintl.h>
#include <stdarg.h>
#include <netdb.h>
#include <ctype.h>
#include <assert.h>

#include <sys/nsctl/cfg_impl.h>
#include <sys/nsctl/cfg.h>

#include <sys/unistat/spcs_s.h>
#include <sys/unistat/spcs_s_u.h>
#include <sys/unistat/spcs_errors.h>

#ifdef DEBUG
#include <sys/nsctl/dsw.h>
#endif

#define	DEFAULT_PARSER_LOC "/etc/dscfg_format"

int Cflg;
int Dflg;
int Lflg;
int aflg;
int iflg;
int lflg;
int nflg;
int pflg;
int rflg;
int sflg;
int uflg;

int verbose;
int noflags;
int errflg;
int mustcommit;
char *locname;		/* config location  from cfg_location */
char *cmdname;

#define	MAX_FILENAME	80

char output_file[MAX_FILENAME];	/* specified output file */
char altroot[MAX_FILENAME];	/* specifed root location */
char config_file[MAX_FILENAME];	/* specified configuration file */
char input_file[MAX_FILENAME];	/* specified input file */
char logical_host[MAX_FILENAME]; /* specified  cluster node */
char device_group[MAX_FILENAME]; /* specified device group name */

#define	IS_NOT_CLUSTER	1
#define	IS_CLUSTER	2

void cfg_invalidate_hsizes(int, const char *);
static int check_cluster();

void
usage(char *errmsg)
{
	if (errmsg)
		(void) fprintf(stderr, "%s: %s\n", cmdname, errmsg);
	(void) fprintf(stderr,
	    gettext("dscfg \t\t\t\tDisplay location of "
		"local configuration database\n"));
	(void) fprintf(stderr, gettext("dscfg -l -s path\t\t"
		"List contents of configuration database\n"));
	(void) fprintf(stderr, gettext(
		"\t\t\t\tlocated at path specified\n"));
	(void) fprintf(stderr, gettext("dscfg -i\t\t\t"
		"Initialize configuration database\n"));
	(void) fprintf(stderr,
	    gettext("dscfg -i -p "
#ifdef DEBUG
"[-n] "
#endif
	    "/etc/dscfg_format\tFormat configuration database\n"));
	(void) fprintf(stderr,
	    gettext("dscfg -a file\t\t\tRestore configuration "
	    "database from file\n"));
	(void) fprintf(stderr, gettext("\t\t\t\tspecified\n"));
	(void) fprintf(stderr,
		gettext("dscfg -l\t\t\tList contents of configuration database"
		"\n"));
	(void) fprintf(stderr,
		gettext("dscfg -L\t\t\tDisplay configuration database's\n"));
	(void) fprintf(stderr, gettext("\t\t\t\tlock status\n"));
	(void) fprintf(stderr, gettext("dscfg -h\t\t\tUsage message\n"));
	if (check_cluster() != IS_NOT_CLUSTER) {
	(void) fprintf(stderr, gettext("\nSun Cluster Usage\n"));
	(void) fprintf(stderr, gettext("******************\n"));
	(void) fprintf(stderr,
		gettext("dscfg -s path\t\t\tSet cluster "
		"configuration database at DID\n"));
	(void) fprintf(stderr, gettext("\t\t\t\tpath specified\n"));
	(void) fprintf(stderr, gettext("dscfg -D device_group\t\t"
		"Check status of cluster device group\n"));
	(void) fprintf(stderr, gettext("dscfg -C -\t\t\t"
		"Display location of cluster configuration\n"));
	(void) fprintf(stderr, gettext("\t\t\t\tdatabase\n"));
	(void) fprintf(stderr, gettext("dscfg -l -s DID_device\t\tList "
		"the contents of cluster configuration\n"));
	(void) fprintf(stderr, gettext("\t\t\t\tdatabase\n"));
	(void) fprintf(stderr, gettext("dscfg -C - -i\t\t\tInitialize "
		"cluster configuration database\n"));
	(void) fprintf(stderr, gettext("dscfg -C - -i -p "
		"/etc/dscfg_format Format cluster configuration database\n"));
	(void) fprintf(stderr, gettext("dscfg -C - -a file\t\t"
		"Restore cluster configuration database from\n"));
	(void) fprintf(stderr, gettext("\t\t\t\tfile specified\n"));
	(void) fprintf(stderr, gettext("dscfg -C - -l\t\t\t"
		"List contents of local configuration database\n"));
	(void) fprintf(stderr, gettext("dscfg -C device_group -l\t"
		"List configuration database by device group\n"));
	(void) fprintf(stderr, gettext("dscfg -C \"-\" -l\t\t\t"
		"List configuration database excluding\n"));
	(void) fprintf(stderr, gettext("\t\t\t\tdevice groups\n"));
	}
}

int
parse_parse_config(CFGFILE *cfg)
{
	FILE *fp;
	char	inbuf[CFG_MAX_BUF];
	char	*buff;
	int	rc;

	/*
	 * Open parser config file, use default if none specified
	 */
	buff = (input_file[0]) ? input_file : DEFAULT_PARSER_LOC;
	if ((fp = fopen(buff, "r")) == NULL) {
		(void) fprintf(stderr,
			gettext("parser config file (%s) not found\n"), buff);
		return (-1);
	}

	/*
	 * start at begining of configration database
	 */
	cfg_rewind(cfg, CFG_SEC_ALL);

	while (((buff = fgets(inbuf, (sizeof (inbuf) - 1), fp)) != NULL)) {
		if (*buff == '#' || *buff == '%')
			continue;
		/* overwrite newline */
		buff[strlen(buff) - 1] = '\0';
		rc = cfg_update_parser_config(cfg, buff, CFG_PARSE_CONF);
		if (rc < 0) {
			(void) fprintf(stderr,
			    gettext("update parser config rc %d key %s\n"),
			    rc, buff);
			(void) fclose(fp);
			return (-1);
		}
	}
	(void) fclose(fp);
	return (1);
}

void
parse_text_config(CFGFILE *cfg)
{
	FILE *fp;
	char	inbuf[CFG_MAX_BUF];
	char	*buff;
	char	*key;
	char	*p;
	int	rc;

	if ((fp = fopen(input_file, "r")) == NULL) {
		(void) fprintf(stderr,
			    gettext("Unable to open text config %s\n"),
				input_file);
		exit(2);
	}
	bzero(inbuf, sizeof (inbuf));
	cfg_rewind(cfg, CFG_SEC_CONF);
	while (((buff = fgets(inbuf, (sizeof (inbuf) - 1), fp)) != NULL)) {
		if (*buff == '#')
			continue;
		/* overwrite newline */
		buff[strlen(buff) - 1] = '\0';
		key = strtok(buff, ":");
		if (!key) {
			continue;
		}
		p = &buff[strlen(key)+2];
		while (*p && isspace(*p)) {
			++p;
		}
		if (!*p) {
			continue;
		}
		rc = cfg_put_cstring(cfg, key, p, strlen(p));
		if (rc < 0) {
			(void) fprintf(stderr,
			    gettext("update text config failed rc %d key %s"),
			    rc, buff);
			return;
		}
		bzero(inbuf, sizeof (inbuf));
	}
	(void) fclose(fp);
}
void
dump_status(CFGFILE *cfg)
{
	cfp_t *cfp = FP_SUN_CLUSTER(cfg);

	/*
	 * WARNING will robinson
	 * The following is using a non-exported internal interface
	 * to libcfg
	 * You may not use any of the following fields in MS software
	 */
	if (!locname)
		exit(2);
	if (!verbose)
		(void) printf("%s\n", locname);
	else {
#ifdef DEBUG
		(void) printf(gettext("Configuration location: %s\n"), locname);
		(void) printf(
		    gettext("Header info:\n\t\t\tmagic: %x\tstate: %x\n"),
		    cfp->cf_head->h_magic, cfp->cf_head->h_state);
		(void) printf(
		    gettext("Parser section:\t\t"
		    "Start: %x\tsize: %d offset: %d\n"),
		    cfp->cf_mapped, cfp->cf_head->h_parsesize,
		    cfp->cf_head->h_parseoff);
		(void) printf(
		    gettext("Config section:\t\t"
		    "Start: %x\tsize:%d\tacsize: %d\n"),
		    cfp->cf_head->h_cparse, cfp->cf_head->h_csize,
		    cfp->cf_head->h_acsize);
		(void) printf("\t\t\tccopy1: %s\tccopy2: %s\n",
			cfp->cf_head->h_ccopy1,
			cfp->cf_head->h_ccopy2);
		(void) printf(
		    gettext("Sequence:\t\tseq1: %d\t\tseq2: %d\n"),
			cfp->cf_head->h_seq1, cfp->cf_head->h_seq2);
#endif
	}
}

void
dump_lockstat(CFGFILE *cfg)
{
	pid_t pid;
	CFGLOCK lock;
	char	ps_str[1024];

	if (cfg_get_lock(cfg, &lock, &pid) == TRUE) {
		(void) printf("%s %ld\n",
		    lock == CFG_RDLOCK ?
			    gettext("Read locked by process id") :
			    gettext("Write locked by process id"),
		    pid);
		(void) sprintf(ps_str, "ps -p %ld", pid);
		system(ps_str);
	} else
		(void) printf("%s\n", gettext("Not locked."));
}


/*
 * dump current configuration section to stdout
 */

void
print_config(CFGFILE *cfg)
{
	time_t tloc = 0;
	int set = 0;
	char pconfig[CFG_MAX_BUF];
	char key[CFG_MAX_KEY];
	char buf[CFG_MAX_BUF];
	char *cp, pbuf[CFG_MAX_BUF];
	FILE *fp;
	int rc;
	int end;

	(void) snprintf(pconfig, sizeof (pconfig),
	    "%s%s", altroot, DEFAULT_PARSER_LOC);
	if ((fp = fopen(pconfig, "r")) == NULL) {
		(void) fprintf(stderr,
		    gettext("dscfg: unable to open "
		    "parser configuration (%s): %s\n"),
		    pconfig, strerror(errno));
		exit(1);
	}

	(void) time(&tloc);
	(void) printf(gettext("# Consolidated Dataservice Configuration\n"));
	(void) printf(gettext("# Do not edit out whitespace or dashes\n"));
	(void) printf(gettext("# File created on: %s"), ctime(&tloc));

	while (fgets(pbuf, (sizeof (pbuf) - 1), fp) != NULL) {
		if (pbuf[0] == '#') {
			/* comment */
			continue;
		}
		/* force a NULL terminator */
		pbuf[sizeof (pbuf) - 1] = '\0';

		if (pbuf[0] == '%') {
			/*
			 * descriptive text
			 * - print it (with comment leader) and move on
			 */
			(void) printf("#%s", &pbuf[1]);
			continue;
		}

		/*
		 * truncate the parser config in pbuf[] to just the tag
		 */
		cp = strchr(pbuf, '.');
		if (cp != NULL) {
			*cp = '\0';
		}

		set = 1;
		/*CONSTCOND*/
		while (1) {
			bzero(buf, CFG_MAX_BUF);
			(void) snprintf(key,
			    sizeof (key), "%s.set%d", pbuf, set);
			rc = cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF);
			if (rc < 0) {
				break;
			}
			/* trim trailing space if necessary */
			end = strlen(buf) - 1;
			if (buf[end] == ' ')
				buf[end] = '\0';

			(void) printf("%s:%s\n", pbuf, buf);
			set++;
		}
	}

	(void) fclose(fp);
}

int
make_new_config(const char *fileloc)
{
	int fd;
	int rc;
	int skip;

	char buf[CFG_MAX_BUF];
	/*CONSTCOND*/
	assert((sizeof (buf) % 512) == 0);

	bzero(buf, CFG_MAX_BUF);

	if ((fd = open(fileloc, O_RDWR | O_CREAT, 0640)) == -1) {
		return (-1);
	}

	/* if this is a device, we may have to skip the vtoc */
	if ((skip = cfg_shldskip_vtoc(fd, fileloc)) == -1) {
		(void) fprintf(stderr,
			gettext("dscfg: unable to read vtoc on (%s)\n"),
				fileloc);
		return (-1);
	} else if (skip) {
		do {
			rc = lseek(fd, CFG_VTOC_SKIP, SEEK_SET);
		} while (rc == -1 && errno == EINTR);

		if (rc == -1) {
			(void) fprintf(stderr, gettext("dscfg: seek error"));
			return (-1);
		}
	}

	do {
		rc = write(fd, buf, sizeof (buf));
	} while (rc == -1 && errno == EINTR);

	close(fd);

	return ((rc < 0) ? 0 : 1);
}

/*
 * dscfg
 * configure or return dataservice persistent configuration
 *
 * options
 *		-i initialize file for first time
 *		-l dump current configuration to stdout in ascii
 *		-a add
 *		-C node	Set resource filter
 *		-p parser config specified input file
 *		-s set partition location or filename in default location
 *		-L print configuration lock status
 *		-u upgrade
 *		-r prepend bootdir to beginning of path for cfg_open
 *	no options    status
 *
 *
 */
#ifdef lint
int
dscfg_lintmain(int argc, char *argv[])
#else
int
main(int argc, char *argv[])
#endif
{
	CFGFILE *cfg;
	extern char *optarg;
	char *loc;
	int offset = 0;
	int rc;
	char c;
	int local;
	int action_counts = 0;

	bzero(input_file, sizeof (input_file));
	(void) setlocale(LC_ALL, "");
	(void) textdomain("dscfg");
	logical_host[0] = '\0';
	cmdname = argv[0];
#ifdef DEBUG
	while ((c = getopt(argc, argv, "a:C:dD:ilLp:r:s:hvn")) != EOF) {
#else
	while ((c = getopt(argc, argv, "a:C:dD:ilLp:r:s:h")) != EOF) {
#endif
		switch (c) {
			case 'a':
				aflg++;
				strcpy(input_file, optarg);
				mustcommit++;
				action_counts++;
				break;
			case 'C':
				Cflg++;
				strcpy(logical_host, optarg);
				if (logical_host && *logical_host == '-')
				    if (argc == 3)
					action_counts++;
				break;
			case 'D':
				Dflg++;
				strcpy(device_group, optarg);
				action_counts++;
				break;
			case 'i':
				iflg++;
				mustcommit++;
				action_counts++;
				break;
			case 'l':
				lflg++;
				action_counts++;
				break;
			case 'L':
				Lflg++;
				action_counts++;
				break;
			case 'p':
				pflg++;
				strcpy(input_file, optarg);
				mustcommit++;
				break;
			case 's':
				sflg++;
				strcpy(config_file, optarg);
				action_counts++;
				break;
			case 'h':
				usage(NULL);
				exit(0);
			/*NOTREACHED*/
#ifdef DEBUG
			case 'v':
				verbose++;
				action_counts++;
				break;
#endif
#ifdef UPGRADE
			case 'u':
				uflg++;
				action_counts++;
				break;
#endif

			case 'r':
				rflg++;
				strcpy(altroot, optarg);
				break;

			case 'n':
				nflg++;
				break;

			default:
				usage(NULL);
				exit(1);
				break;
		};
	}

	switch (action_counts) {
	    case 0:
		if (argc > 1) {
		    if (pflg)
			usage(gettext(
			    "-p option must be used in conjunction with -i"));
		    else
			usage(gettext("must specify an action flag"));
		    exit(1);
		}
		break;
	    case 1:
		break;
	    case 2:
		if (lflg && sflg)
			break;
		else {
		    usage(gettext("too many action flags"));
		    exit(1);
		    break;
		}
	    default:
		usage(gettext("too many action flags"));
		exit(1);
		break;
	}

	if (argc == 1 || (argc == 2 && verbose) || (argc == 3 && (rflg|Cflg)))
		noflags++;

	if (Dflg) {
		/*
		 * Determine if the value specified is a device group
		 * that is active on this node
		 */
		char *other_node;
		if ((cfg_issuncluster() > 0) && (strlen(device_group) > 0)) {
		    local = cfg_dgname_islocal(device_group, &other_node);
		    if (local == 0)
			(void) fprintf(stderr, gettext(
			    "Device group %s active on %s\n"),
			    device_group, other_node);
		    else if (local == 1)
			(void) fprintf(stderr, gettext(
			    "Device group %s active on this node\n"),
			    device_group);
		    else
			(void) fprintf(stderr, gettext(
			    "Device group %s not found\n"), device_group);
		    return (local);
		} else {
			(void) fprintf(stderr, gettext(
			    "dscfg -D is only allowed in "
			    "Sun Cluster OE\n"));
			return (0);
		}
	}

	if (sflg && !lflg) {
		/*
		 * Only allow setting location on a non-sun cluster system
		 * if the cluster reference file is already present.
		 */
		struct stat dscfg_stat = {0};
		if (cfg_issuncluster() <= 0) {
			if (stat(CFG_CLUSTER_LOCATION, &dscfg_stat) != 0) {
				if (dscfg_stat.st_blocks == 0) {
					(void) fprintf(stderr, gettext(
						"dscfg -s is only allowed in "
						"Sun Cluster OE\n"));
					exit(1);
				}
			}
		}

		spcs_log("dscfg", NULL, gettext("dscfg -s %s"), config_file);
		locname = cfg_location(config_file, CFG_LOC_SET_CLUSTER,
		    rflg ? altroot : NULL);
		if (locname == NULL) {
			(void) fprintf(stderr, gettext("dscfg: %s\n"),
			    cfg_error(NULL));
			exit(1);
		} else
			exit(0);

	} else if (sflg && lflg) {
		/* s used with l for temporarily peeking at a dscfg database */
		loc = config_file;
	} else {
		locname = cfg_location(NULL,
			Cflg ? CFG_LOC_GET_CLUSTER : CFG_LOC_GET_LOCAL,
			rflg ? altroot : NULL);
		if (Cflg && (locname == NULL)) {
			(void) fprintf(stderr, gettext(
			    "dscfg: cluster config not set: %s\n"),
			    cfg_error(NULL));
			return (1);
		}
		loc = rflg ? locname : NULL;
	}

	/*
	 * the following hack forces the configuration file to initialize
	 */
	if (iflg && !pflg) {
		int fild;
		int c;
		char buf[CFG_MAX_BUF] = {0};
		cfp_t *cfp;

		if (!nflg) {
			(void) printf(
			    gettext("WARNING: This option will erase your "
				"Availability Suite configuration\n"));
			(void) printf(
			    gettext("Do you want to continue? (Y/N) [N] "));

			c = getchar();
			switch (c) {
			case 'y':
			case 'Y': break;
			case 'n':
			case 'N':
			case '\n':
				(void) fprintf(stderr, gettext(
				"dscfg: configuration not initialized\n"));
				exit(1);
			default:
				(void) fprintf(stderr, gettext(
				"dscfg: %d is not a valid response\n"), c);
				exit(1);
			}
		}

		spcs_log("dscfg", NULL, gettext("dscfg -i"));

		if ((cfg = cfg_open(loc)) == NULL) {
			/* this is not a good config, or non-existent so.. */
			if (!make_new_config(locname)) {
				(void) fprintf(stderr, gettext("dscfg: %s\n"),
				    cfg_error(NULL));
				exit(1);
			}
			if ((cfg = cfg_open(loc)) == NULL) {
				(void) fprintf(stderr, gettext("dscfg: %s\n"),
				    cfg_error(NULL));
				exit(1);
			}
		}

		/*
		 * Set cluster node if specified
		 */
		if (Cflg)
			cfg_resource(cfg, logical_host);

		if (cfg_is_cfg(cfg) != 1) {
			if (!make_new_config(locname)) {
				(void) fprintf(stderr, gettext("dscfg: unable "
				    " to create new config \n"));
				exit(1);
			}
		}

		if (!cfg_lock(cfg, CFG_WRLOCK)) {
			(void) fprintf(stderr, gettext("dscfg: %s\n"),
			    cfg_error(NULL));
			exit(1);
		}

		cfp = FP_SUN_CLUSTER(cfg);
		if ((fild = cfp->cf_fd) == 0) {
			(void) fprintf(stderr,
				gettext("dscfg: failure to access %s "
				"configuration database: %s\n"),
				(Cflg) ? gettext("cluster") : gettext("local"),
			cfg_error(NULL));
			exit(1);
		}

		if (cfg_shldskip_vtoc(fild, locname) > 0)
			offset += CFG_VTOC_SKIP;

		lseek(fild, offset, SEEK_SET);
		write(fild, buf, sizeof (buf));
		cfg_invalidate_hsizes(fild, locname);

		cfg_close(cfg);
		exit(0);
	}

	if (pflg && !iflg) {
		usage(gettext("-p option must be used in conjunction with -i"));
		exit(1);

	}

	if (uflg) {
		char cmd[CFG_MAX_BUF];
		if (rflg)
			(void) snprintf(cmd, sizeof (cmd),
			    "%s/usr/sbin/dscfg -r %s -l >"
			    " %s/var/tmp/.dscfg.bak", altroot,
			    altroot, altroot);
		else
			(void) snprintf(cmd, sizeof (cmd),
			    "/usr/sbin/dscfg -l >"
			    " /var/tmp/.dscfg.bak");

		if (system(cmd) != 0) {
			(void) fprintf(stderr,
			    "dscfg: unable to create backup\n");
			exit(1);
		}

		if ((cfg = cfg_open(loc)) == NULL) {
			(void) fprintf(stderr, gettext("dscfg: %s\n"),
			    cfg_error(NULL));
			exit(2);
		}

		if (!cfg_lock(cfg, CFG_UPGRADE)) {
			(void) fprintf(stderr,
			    gettext("dscfg: upgrade failed\n"));
			cfg_close(cfg);
			exit(1);
		}

		cfg_close(cfg);
		exit(0);
	}

	if ((cfg = cfg_open(loc)) == NULL) {
		(void) fprintf(stderr, gettext("dscfg: %s\n"), cfg_error(NULL));
		exit(2);
	}

	/*
	 * Set cluster node if specified
	 */
	if (Cflg)
		cfg_resource(cfg, logical_host);

	if ((!pflg) && (!noflags)) {
		if (cfg_is_cfg(cfg) != 1) {
			(void) fprintf(stderr,
				gettext("dscfg: %s\n"), cfg_error(NULL));
			cfg_close(cfg);
			exit(1);
		}
	}

	if (Lflg) {
		dump_lockstat(cfg);
		cfg_close(cfg);
		exit(0);
	}

	if (noflags) {
		dump_status(cfg);
		cfg_close(cfg);
		exit(0);
	}

	if (!cfg_lock(cfg, mustcommit? CFG_WRLOCK : CFG_RDLOCK)) {
		(void) fprintf(stderr, gettext("cfg_lock: lock failed\n"));
		cfg_close(cfg);
		exit(1);
	}

	if (lflg) {
		print_config(cfg);
		cfg_close(cfg);
		exit(0);
	}

	/*
	 * initialize configuration
	 */
	if (iflg) {
		spcs_log("dscfg", NULL, gettext("dscfg -i -p %s"), input_file);

		if (!pflg) {
			(void) fprintf(stderr,
			    gettext("dscfg: cannot init without "
			    "parser configuration file\n"));
			cfg_close(cfg);
			exit(1);
		} else if (parse_parse_config(cfg) < 0) {
			(void) fprintf(stderr, gettext("dscfg: cannot load "
				    "parser configuration file\n"));
			cfg_close(cfg);
			exit(1);
		}
	}

	/*
	 * read asci config file and write
	 */
	if (aflg) {
		spcs_log("dscfg", NULL, gettext("dscfg -a %s"), input_file);
		parse_text_config(cfg);
	}

	if (mustcommit) {
		rc = cfg_commit(cfg);
		if (rc < 0) {
			int sev = 0;
			(void) fprintf(stderr, gettext("dscfg: %s\n"),
			    cfg_error(&sev));
			if (sev == CFG_EFATAL) {
				cfg_close(cfg);
				exit(2);
			}
		}
	}

	cfg_close(cfg);
	return (0);
}

static int
check_cluster()
{
	static int is_cluster = -1;
	int rc;

	if (is_cluster != -1)
	    return (is_cluster);
	rc = cfg_iscluster();
	if (rc > 0) {
	    is_cluster = IS_CLUSTER;
	    return (is_cluster);
	} else if (rc == 0) {
	    is_cluster = IS_NOT_CLUSTER;
	    return (is_cluster);
	} else {
	    (void) fprintf(stderr,
		gettext("dscfg: unable to determin environment\n"));
	    /*NOTREACHED*/
	}

	/* gcc */
	return (is_cluster);
}