view usr/src/cmd/cmd-inet/usr.sbin/nwamcfg/nwamcfg.c @ 13046:b615ff9cec13

6971008 nwamcfg revert emits confusing message
author Anurag S. Maskey <Anurag.Maskey@Oracle.COM>
date Fri, 06 Aug 2010 14:58:14 -0400
parents 8f30d0e611c6
children
line wrap: on
line source

/*
 * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * nwamcfg is a lex/yacc based command interpreter used to manage network
 * configurations.  The lexer (see nwamcfg_lex.l) builds up tokens, which
 * the grammar (see nwamcfg_grammar.y) builds up into commands, some of
 * which takes resources and/or properties as arguments.
 */

#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <libnwam.h>
#include <libtecla.h>
#include <locale.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>

#include "nwamcfg.h"

#if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
#endif

struct help {
	uint_t		cmd_num;
	const char	*cmd_name;
	const char	*cmd_usage;
};

extern int yyparse(void);
extern int lex_lineno;

#define	MAX_LINE_LEN	1024
#define	MAX_CMD_HIST	1024

/* usage of commands */
#define	SHELP_CANCEL	"cancel"
#define	SHELP_CLEAR	"clear <prop-name>"
#define	SHELP_COMMIT	"commit"
#define	SHELP_CREATE	"create [-t <template>] <object-type> [<class>] " \
			"<object-name>"
#define	SHELP_DESTROY	"destroy {-a | <object-type> [<class>] <object-name>}"
#define	SHELP_END	"end"
#define	SHELP_EXIT	"exit"
#define	SHELP_EXPORT	"export [-d] [-f <output-file>] " \
			"[<object-type> [<class>] <object-name>]"
#define	SHELP_GET	"get [-V] <prop-name>"
#define	SHELP_HELP	"help [command-name]"
#define	SHELP_LIST	"list [-a] [<object-type> [<class>] <object-name>]"
#define	SHELP_REVERT	"revert"
#define	SHELP_SELECT	"select <object-type> [<class>] <object-name>"
#define	SHELP_SET	"set <prop-name>=<value1>[,<value2>...]"
#define	SHELP_VERIFY	"verify"
#define	SHELP_WALK	"walkprop [-a]"

/*
 * Scope Definitions:
 * Locations, ENMs, NCPs and Known WLANs are one scope level below global (GBL).
 * NCUs are one more level beneath the NCP scope.
 * Because the commands in Locations/ENM/Known WLAN and NCP level are different,
 * the scope are divided accordingly.
 *     GBL->LOC, GBL->ENM, GBL->WLAN or GBL->NCP->NCU
 */
#define	NWAM_SCOPE_GBL	0
#define	NWAM_SCOPE_LOC	1
#define	NWAM_SCOPE_ENM	2
#define	NWAM_SCOPE_WLAN	3
#define	NWAM_SCOPE_NCP	4
#define	NWAM_SCOPE_NCU	5

/* delimiter used for list of values */
#define	NWAM_VALUE_DELIMITER_CHAR	','
#define	NWAM_VALUE_DELIMITER_STR	","

/* the max number of values for an enum used by some properties in libnwam */

/*
 * All arrays/tables are null-terminated, rather than defining the length of
 * the array.  When looping, check for NULL rather than using the size.
 */

static struct help helptab[] = {
	{ CMD_CANCEL,	"cancel",	SHELP_CANCEL	},
	{ CMD_CLEAR,	"clear",	SHELP_CLEAR	},
	{ CMD_COMMIT,	"commit",	SHELP_COMMIT	},
	{ CMD_CREATE,	"create",	SHELP_CREATE	},
	{ CMD_DESTROY,	"destroy",	SHELP_DESTROY	},
	{ CMD_END,	"end",		SHELP_END	},
	{ CMD_EXIT,	"exit",		SHELP_EXIT	},
	{ CMD_EXPORT,	"export",	SHELP_EXPORT	},
	{ CMD_GET,	"get",		SHELP_GET	},
	{ CMD_HELP,	"help",		SHELP_HELP	},
	{ CMD_LIST,	"list",		SHELP_LIST	},
	{ CMD_REVERT,	"revert",	SHELP_REVERT	},
	{ CMD_SELECT,	"select",	SHELP_SELECT	},
	{ CMD_SET,	"set",		SHELP_SET	},
	{ CMD_VERIFY,	"verify",	SHELP_VERIFY	},
	{ CMD_WALKPROP,	"walkprop",	SHELP_WALK	},
	{ 0, NULL, NULL }
};

/* These *must* match the order of the RT1_ define's from nwamcfg.h */
static char *res1_types[] = {
	"unknown",
	"loc",
	"ncp",
	"enm",
	"wlan",
	NULL
};

/* These *must* match the order of the RT2_ define's from nwamcfg.h */
static char *res2_types[] = {
	"unknown",
	"ncu",
	NULL
};

/*
 * No array for NCU_CLASS_.  The #define's in nwamcfg.h matches the
 * enum nwam_ncu_class_t in libnwam and thus uses libnwam functions to
 * retrieve the string representation.
 */

/* These *MUST* match the order of the PT_ define's from nwamcfg.h */
static char *pt_types[] = {
	"unknown",
	NWAM_NCU_PROP_ACTIVATION_MODE,
	NWAM_NCU_PROP_ENABLED,
	NWAM_NCU_PROP_TYPE,
	NWAM_NCU_PROP_CLASS,
	NWAM_NCU_PROP_PARENT_NCP,
	NWAM_NCU_PROP_PRIORITY_GROUP,
	NWAM_NCU_PROP_PRIORITY_MODE,
	NWAM_NCU_PROP_LINK_MAC_ADDR,
	NWAM_NCU_PROP_LINK_AUTOPUSH,
	NWAM_NCU_PROP_LINK_MTU,
	NWAM_NCU_PROP_IP_VERSION,
	NWAM_NCU_PROP_IPV4_ADDRSRC,
	NWAM_NCU_PROP_IPV4_ADDR,
	NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE,
	NWAM_NCU_PROP_IPV6_ADDRSRC,
	NWAM_NCU_PROP_IPV6_ADDR,
	NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE,
	NWAM_LOC_PROP_CONDITIONS,
	NWAM_ENM_PROP_FMRI,
	NWAM_ENM_PROP_START,
	NWAM_ENM_PROP_STOP,
	NWAM_LOC_PROP_NAMESERVICES,
	NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE,
	NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
	NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN,
	NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS,
	NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH,
	NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
	NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS,
	NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
	NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS,
	NWAM_LOC_PROP_DEFAULT_DOMAIN,
	NWAM_LOC_PROP_NFSV4_DOMAIN,
	NWAM_LOC_PROP_IPFILTER_CONFIG_FILE,
	NWAM_LOC_PROP_IPFILTER_V6_CONFIG_FILE,
	NWAM_LOC_PROP_IPNAT_CONFIG_FILE,
	NWAM_LOC_PROP_IPPOOL_CONFIG_FILE,
	NWAM_LOC_PROP_IKE_CONFIG_FILE,
	NWAM_LOC_PROP_IPSECPOLICY_CONFIG_FILE,
	NWAM_KNOWN_WLAN_PROP_BSSIDS,
	NWAM_KNOWN_WLAN_PROP_PRIORITY,
	NWAM_KNOWN_WLAN_PROP_KEYNAME,
	NWAM_KNOWN_WLAN_PROP_KEYSLOT,
	NWAM_KNOWN_WLAN_PROP_SECURITY_MODE
};

/* properties table: maps PT_* constants to property names */
typedef struct prop_table_entry {
	int			pte_type;
	const char		*pte_name;
} prop_table_entry_t;

/* NCU properties table */
static prop_table_entry_t ncu_prop_table[] = {
	{ PT_TYPE, 			NWAM_NCU_PROP_TYPE },
	{ PT_CLASS, 			NWAM_NCU_PROP_CLASS },
	{ PT_PARENT, 			NWAM_NCU_PROP_PARENT_NCP },
	{ PT_ACTIVATION_MODE,		NWAM_NCU_PROP_ACTIVATION_MODE },
	{ PT_ENABLED, 			NWAM_NCU_PROP_ENABLED },
	{ PT_PRIORITY_GROUP, 		NWAM_NCU_PROP_PRIORITY_GROUP },
	{ PT_PRIORITY_MODE,		NWAM_NCU_PROP_PRIORITY_MODE },
	{ PT_LINK_MACADDR, 		NWAM_NCU_PROP_LINK_MAC_ADDR },
	{ PT_LINK_AUTOPUSH, 		NWAM_NCU_PROP_LINK_AUTOPUSH },
	{ PT_LINK_MTU, 			NWAM_NCU_PROP_LINK_MTU },
	{ PT_IP_VERSION, 		NWAM_NCU_PROP_IP_VERSION },
	{ PT_IPV4_ADDRSRC, 		NWAM_NCU_PROP_IPV4_ADDRSRC },
	{ PT_IPV4_ADDR, 		NWAM_NCU_PROP_IPV4_ADDR },
	{ PT_IPV4_DEFAULT_ROUTE,	NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE },
	{ PT_IPV6_ADDRSRC, 		NWAM_NCU_PROP_IPV6_ADDRSRC },
	{ PT_IPV6_ADDR, 		NWAM_NCU_PROP_IPV6_ADDR },
	{ PT_IPV6_DEFAULT_ROUTE,	NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE },
	{ 0, NULL }
};

/* ENM properties table */
static prop_table_entry_t enm_prop_table[] = {
	{ PT_ENM_FMRI, 		NWAM_ENM_PROP_FMRI },
	{ PT_ENM_START, 	NWAM_ENM_PROP_START },
	{ PT_ENM_STOP, 		NWAM_ENM_PROP_STOP },
	{ PT_ACTIVATION_MODE, 	NWAM_ENM_PROP_ACTIVATION_MODE },
	{ PT_CONDITIONS, 	NWAM_ENM_PROP_CONDITIONS },
	{ PT_ENABLED, 		NWAM_ENM_PROP_ENABLED },
	{ 0, NULL }
};

/* LOCation properties table */
static prop_table_entry_t loc_prop_table[] = {
	{ PT_ACTIVATION_MODE, 	NWAM_LOC_PROP_ACTIVATION_MODE },
	{ PT_CONDITIONS, 	NWAM_LOC_PROP_CONDITIONS },
	{ PT_ENABLED, 		NWAM_LOC_PROP_ENABLED },
	{ PT_LOC_NAMESERVICES, 	NWAM_LOC_PROP_NAMESERVICES },
	{ PT_LOC_NAMESERVICES_CONFIG, NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE },
	{ PT_LOC_DNS_CONFIGSRC, NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC },
	{ PT_LOC_DNS_DOMAIN, 	NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN },
	{ PT_LOC_DNS_SERVERS, 	NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS },
	{ PT_LOC_DNS_SEARCH, 	NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH },
	{ PT_LOC_NIS_CONFIGSRC, NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC },
	{ PT_LOC_NIS_SERVERS, 	NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS },
	{ PT_LOC_LDAP_CONFIGSRC, NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC },
	{ PT_LOC_LDAP_SERVERS,	NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS },
	{ PT_LOC_DEFAULT_DOMAIN, NWAM_LOC_PROP_DEFAULT_DOMAIN },
	{ PT_LOC_NFSV4_DOMAIN, 	NWAM_LOC_PROP_NFSV4_DOMAIN },
	{ PT_LOC_IPF_CONFIG, 	NWAM_LOC_PROP_IPFILTER_CONFIG_FILE },
	{ PT_LOC_IPF_V6_CONFIG, NWAM_LOC_PROP_IPFILTER_V6_CONFIG_FILE },
	{ PT_LOC_IPNAT_CONFIG, 	NWAM_LOC_PROP_IPNAT_CONFIG_FILE },
	{ PT_LOC_IPPOOL_CONFIG, NWAM_LOC_PROP_IPPOOL_CONFIG_FILE },
	{ PT_LOC_IKE_CONFIG, 	NWAM_LOC_PROP_IKE_CONFIG_FILE },
	{ PT_LOC_IPSECPOL_CONFIG, NWAM_LOC_PROP_IPSECPOLICY_CONFIG_FILE },
	{ 0, NULL }
};

/* Known WLAN properties table */
static prop_table_entry_t wlan_prop_table[] = {
	{ PT_WLAN_BSSIDS, 	NWAM_KNOWN_WLAN_PROP_BSSIDS },
	{ PT_WLAN_PRIORITY, 	NWAM_KNOWN_WLAN_PROP_PRIORITY },
	{ PT_WLAN_KEYNAME, 	NWAM_KNOWN_WLAN_PROP_KEYNAME },
	{ PT_WLAN_KEYSLOT, 	NWAM_KNOWN_WLAN_PROP_KEYSLOT },
	{ PT_WLAN_SECURITY_MODE, NWAM_KNOWN_WLAN_PROP_SECURITY_MODE },
	{ 0, NULL }
};

/* Returns the appropriate properties table for the given object type */
static prop_table_entry_t *
get_prop_table(nwam_object_type_t object_type)
{
	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCU:
		return (ncu_prop_table);
	case NWAM_OBJECT_TYPE_LOC:
		return (loc_prop_table);
	case NWAM_OBJECT_TYPE_ENM:
		return (enm_prop_table);
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		return (wlan_prop_table);
	}
	return (NULL);
}

/* Global variables */

/* set early in main(), never modified thereafter, used all over the place */
static char *execname;

/* set in modifying functions, checked in read_input() */
boolean_t saw_error = B_FALSE;

/* set in yacc parser, checked in read_input() */
boolean_t newline_terminated;

/* set in main(), checked in lex error handler */
boolean_t cmd_file_mode = B_FALSE;

/* set in exit_func(), checked in read_input() */
static boolean_t time_to_exit = B_FALSE;

/* used in nerr() and nwamerr() */
static char *cmd_file_name = NULL;

/* used with cmd_file to destroy all configurations */
static boolean_t remove_all_configurations = B_FALSE;

/* checked in read_input() and other places */
static boolean_t ok_to_prompt = B_FALSE;

/* initialized in do_interactive(), checked in initialize() */
static boolean_t interactive_mode;

static boolean_t need_to_commit = B_FALSE;

/* The gl_get_line() resource object */
static GetLine *gl;

/* set when create or read objects, used by other func */
static nwam_loc_handle_t loc_h = NULL;
static nwam_enm_handle_t enm_h = NULL;
static nwam_known_wlan_handle_t wlan_h = NULL;
static nwam_ncu_handle_t ncu_h = NULL;
static nwam_ncp_handle_t ncp_h = NULL;

static int current_scope = NWAM_SCOPE_GBL;

/* obj1_* are used in NWAM_SCOPE_{NCP,LOC,ENM,WLAN} */
static int obj1_type;
static char obj1_name[NWAM_MAX_NAME_LEN + 1];

/* obj2_* are used in NWAM_SCOPE_NCU only */
static int obj2_type;
static char obj2_name[NWAM_MAX_NAME_LEN + 1];

/* arrays for tab-completion */
/* commands at NWAM_SCOPE_GBL */
static const char *global_scope_cmds[] = {
	"create ",
	"destroy ",
	"end ",
	"exit ",
	"export ",
	"help ",
	"list ",
	"select ",
	NULL
};

static const char *global_create_cmds[] = {
	"create loc ",
	"create enm ",
	"create ncp ",
	"create wlan ",
	"create -t ",		/* template */
	NULL
};

static const char *global_destroy_cmds[] = {
	"destroy -a ",
	"destroy loc ",
	"destroy enm ",
	"destroy ncp ",
	"destroy wlan ",
	NULL
};

static const char *global_export_cmds[] = {
	"export ",
	"export -d ",		/* add destroy -a */
	"export -f ",		/* to file */
	"export -d -f ",	/* add destroy -a to file */
	"export loc ",
	"export enm ",
	"export ncp ",
	"export wlan ",
	NULL
};

static const char *global_list_cmds[] = {
	"list ",
	"list loc ",
	"list enm ",
	"list ncp ",
	"list wlan ",
	"list -a loc ",
	"list -a enm ",
	"list -a wlan ",
	NULL
};

static const char *global_select_cmds[] = {
	"select loc ",
	"select enm ",
	"select ncp ",
	"select wlan ",
	NULL
};

/* commands at NWAM_SCOPE_LOC, _ENM, _WLAN and _NCU */
static const char *non_ncp_scope_cmds[] = {
	"cancel ",
	"clear ",
	"commit ",
	"end ",
	"exit ",
	"export ",
	"export -f ",
	"get ",
	"get -V ",	/* value only */
	"help ",
	"list ",
	"list -a ",	/* all properties */
	"revert ",
	"set ",
	"verify ",
	"walkprop ",
	"walkprop -a ",	/* all properties */
	NULL
};

/* commands at NWAM_SCOPE_NCP */
static const char *ncp_scope_cmds[] = {
	"cancel ",
	"create ",
	"destroy ",
	"end ",
	"exit ",
	"export ",
	"help ",
	"list ",
	"select ",
	NULL
};

static const char *ncp_create_cmds[] = {
	"create ncu ip ",
	"create ncu phys ",
	"create -t ",		/* template */
	NULL
};

static const char *ncp_destroy_cmds[] = {
	"destroy ncu ",
	"destroy ncu ip ",
	"destroy ncu phys ",
	NULL
};

static const char *ncp_export_cmds[] = {
	"export ",
	"export -f ",		/* to file */
	"export ncu ",
	"export ncu ip ",
	"export ncu phys ",
	NULL
};

static const char *ncp_list_cmds[] = {
	"list ",
	"list ncu ",
	"list ncu ip ",
	"list ncu phys ",
	"list -a ncu ",
	"list -a ncu ip ",
	"list -a ncu phys ",
	NULL
};

static const char *ncp_select_cmds[] = {
	"select ncu ",
	"select ncu ip ",
	"select ncu phys ",
	NULL
};

/* Functions begin here */

cmd_t *
alloc_cmd(void)
{
	cmd_t *cmd = calloc(1, sizeof (cmd_t));
	if (cmd == NULL) {
		nerr("Out of memory");
		return (NULL);
	}
	cmd->cmd_argc = 0;
	cmd->cmd_argv[0] = NULL;

	return (cmd);
}

void
free_cmd(cmd_t *cmd)
{
	int i;

	for (i = 0; i < cmd->cmd_argc; i++)
		free(cmd->cmd_argv[i]);
	free(cmd);
}

void
array_free(void **array, int nelem)
{
	int i;
	for (i = 0; i < nelem; i++)
		free(array[i]);
	free(array);
}

static boolean_t
initial_match(const char *line1, const char *line2, int word_end)
{
	if (word_end <= 0)
		return (B_TRUE);
	return (strncmp(line1, line2, word_end) == 0);
}

static int
add_stuff(WordCompletion *cpl, const char *line1, const char **list,
    int word_end)
{
	int i, err;

	for (i = 0; list[i] != NULL; i++) {
		if (initial_match(line1, list[i], word_end)) {
			err = cpl_add_completion(cpl, line1, 0, word_end,
			    list[i] + word_end, "", "");
			if (err != 0)
				return (err);
		}
	}
	return (0);
}

/*
 * To fill in the rest of a string when user types the tab key.
 * First digital number is the length of the string, the second digital number
 * is the min number of chars that is needed to uniquely identify a string.
 */
#define	MINI_STR(l, s, m, n) strncmp(l, s, MAX(MIN(sizeof (s) - 1, m), n))

/* ARGSUSED */
static
CPL_MATCH_FN(cmd_cpl_fn)
{
	/* tab-complete according to the current scope */
	switch (current_scope) {
	case NWAM_SCOPE_GBL:
		if (MINI_STR(line, "create ", word_end, 2) == 0)
			return (add_stuff(cpl, line, global_create_cmds,
			    word_end));
		if (MINI_STR(line, "destroy ", word_end, 1) == 0)
			return (add_stuff(cpl, line, global_destroy_cmds,
			    word_end));
		if (MINI_STR(line, "export ", word_end, 3) == 0)
			return (add_stuff(cpl, line, global_export_cmds,
			    word_end));
		if (MINI_STR(line, "list ", word_end, 1) == 0)
			return (add_stuff(cpl, line, global_list_cmds,
			    word_end));
		if (MINI_STR(line, "select ", word_end, 1) == 0)
			return (add_stuff(cpl, line, global_select_cmds,
			    word_end));
		return (add_stuff(cpl, line, global_scope_cmds, word_end));
	case NWAM_SCOPE_LOC:
	case NWAM_SCOPE_ENM:
	case NWAM_SCOPE_WLAN:
	case NWAM_SCOPE_NCU:
		return (add_stuff(cpl, line, non_ncp_scope_cmds, word_end));
	case NWAM_SCOPE_NCP:
		if (MINI_STR(line, "create ", word_end, 2) == 0)
			return (add_stuff(cpl, line, ncp_create_cmds,
			    word_end));
		if (MINI_STR(line, "destroy ", word_end, 1) == 0)
			return (add_stuff(cpl, line, ncp_destroy_cmds,
			    word_end));
		if (MINI_STR(line, "export ", word_end, 3) == 0)
			return (add_stuff(cpl, line, ncp_export_cmds,
			    word_end));
		if (MINI_STR(line, "list ", word_end, 1) == 0)
			return (add_stuff(cpl, line, ncp_list_cmds, word_end));
		if (MINI_STR(line, "select ", word_end, 1) == 0)
			return (add_stuff(cpl, line, ncp_select_cmds,
			    word_end));
		return (add_stuff(cpl, line, ncp_scope_cmds, word_end));
	}
	/* should never get here */
	return (NULL);
}

const char *
cmd_to_str(int cmd_num)
{
	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
	return (helptab[cmd_num].cmd_name);
}

/* Returns "loc", "enm", "wlan" or "ncp" as string */
static const char *
rt1_to_str(int res_type)
{
	assert(res_type >= RT1_MIN && res_type <= RT1_MAX);
	return (res1_types[res_type]);
}

/* Returns "ncu" as string */
static const char *
rt2_to_str(int res_type)
{
	assert(res_type >= RT2_MIN && res_type <= RT2_MAX);
	return (res2_types[res_type]);
}

/* Returns "ncp, "ncu", "loc", "enm", or "wlan" according to the scope */
static const char *
scope_to_str(int scope) {
	switch (scope) {
	case NWAM_SCOPE_GBL:
		return ("global");
	case NWAM_SCOPE_NCP:
		return ("ncp");
	case NWAM_SCOPE_NCU:
		return ("ncu");
	case NWAM_SCOPE_LOC:
		return ("loc");
	case NWAM_SCOPE_ENM:
		return ("enm");
	case NWAM_SCOPE_WLAN:
		return ("wlan");
	default:
		return ("invalid");
	}
}

/* Given an enm property and value, returns it as a string */
static const char *
propval_to_str(const char *propname, uint64_t value)
{
	const char *str;

	if (nwam_uint64_get_value_string(propname, value, &str) == NWAM_SUCCESS)
		return (str);
	return (NULL);
}

/* Given an int for a prop, returns it as string */
static const char *
pt_to_str(int prop_type)
{
	assert(prop_type >= PT_MIN && prop_type <= PT_MAX);
	return (pt_types[prop_type]);
}

/* Return B_TRUE if string starts with "t" or is 1, B_FALSE otherwise */
static boolean_t
str_to_boolean(const char *str)
{
	if (strncasecmp(str, "t", 1) == 0 || atoi(str) == 1)
		return (B_TRUE);
	else
		return (B_FALSE);
}

/*
 * This is a separate function rather than a set of define's because of the
 * gettext() wrapping.
 */

/*
 * TRANSLATION_NOTE
 * Each string below should have \t follow \n whenever needed; the
 * initial \t and the terminal \n will be provided by the calling function.
 */

static const char *
long_help(int cmd_num)
{
	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
	switch (cmd_num) {
		case CMD_CANCEL:
			return (gettext("Cancels the current configuration "
			    "changes."));
		case CMD_CLEAR:
			return (gettext("Clears the value for the specified "
			    "property."));
		case CMD_COMMIT:
			return (gettext("Commits the current configuration."));
		case CMD_CREATE:
			return (gettext("Creates a new profile or resource."));
		case CMD_DESTROY:
			return (gettext("Destroys the specified profile or "
			    "resource."));
		case CMD_END:
			return (gettext("Ends specification of a resource."));
		case CMD_EXIT:
			return (gettext("Exits the program."));
		case CMD_EXPORT:
			return (gettext("Exports the configuration."));
		case CMD_GET:
			return (gettext("Gets the value of the specified "
			    "property."));
		case CMD_HELP:
			return (gettext("Prints help message."));
		case CMD_LIST:
			return (gettext("Lists existing objects."));
		case CMD_REVERT:
			return (gettext("Reverts to the previous "
			    "configuration."));
		case CMD_SELECT:
			return (gettext("Selects a resource to modify."));
		case CMD_SET:
			return (gettext("Sets the value of the specified "
			    "property."));
		case CMD_VERIFY:
			return (gettext("Verifies an object."));
		case CMD_WALKPROP:
			return (gettext("Iterates over properties."));
		default:
			return (gettext("Unknown command."));
	}
}

void
command_usage(int command)
{
	if (command < CMD_MIN || command > CMD_MAX) {
		nerr("Unknown command");
	} else {
		nerr("%s: %s: %s", gettext("Error"), gettext("usage"),
		    helptab[command].cmd_usage);
	}
}

static void
long_usage(uint_t cmd_num)
{
	(void) printf("%s: %s\n", gettext("usage"),
	    helptab[cmd_num].cmd_usage);
	(void) printf("\t%s\n", long_help(cmd_num));
}

/* Prints usage for command line options */
static void
cmd_line_usage()
{
	(void) printf("%s:\t%s\t\t\t\t(%s)\n", gettext("usage"), execname,
	    gettext("interactive-mode"));
	(void) printf("\t%s <%s> [%s...]\n", execname, gettext("command"),
	    gettext("options"));
	(void) printf("\t%s [-d] -f <%s>\n", execname, gettext("command-file"));
	(void) printf("\t%s %s [<%s>]\n", execname, cmd_to_str(CMD_HELP),
	    gettext("command"));
}

/* Prints the line number of the current command if in command-file mode */
static void
print_lineno()
{
	static int last_lineno;

	/* lex_lineno has already been incremented in the lexer; compensate */
	if (cmd_file_mode && lex_lineno > last_lineno) {
		if (strcmp(cmd_file_name, "-") == 0)
			(void) fprintf(stderr, gettext("On line %d:\n"),
			    lex_lineno - 1);
		else
			(void) fprintf(stderr, gettext("On line %d of %s:\n"),
			    lex_lineno - 1, cmd_file_name);
		last_lineno = lex_lineno;
	}
}

/* PRINTFLIKE1 */
void
nerr(const char *format, ...)
{
	va_list	alist;

	print_lineno();

	format = gettext(format);
	va_start(alist, format);
	(void) vfprintf(stderr, format, alist);
	va_end(alist);
	(void) fprintf(stderr, "\n");

	saw_error = B_TRUE;
}

/* PRINTFLIKE2 */
static void
nwamerr(nwam_error_t err, const char *format, ...)
{
	va_list	alist;

	print_lineno();

	format = gettext(format);
	va_start(alist, format);
	(void) vfprintf(stderr, format, alist);
	va_end(alist);
	(void) fprintf(stderr, ": %s\n", nwam_strerror(err));

	saw_error = B_TRUE;
}

void
properr(const char *prop)
{
	nerr("Invalid property: '%s'", prop);
}

/*
 * If free_ncu_only == B_TRUE, only ncu handle is freed, ncp handle remains the
 * same.  Since nwam_ncp_free() takes care of its ncus, no need to explicitly
 * call nwam_ncu_free() afterwards.
 */
static void
free_handle(boolean_t free_ncu_only)
{
	if (ncp_h != NULL) {
		if (!free_ncu_only) {
			nwam_ncp_free(ncp_h);
			ncp_h = NULL;
			ncu_h = NULL;
		} else if (ncu_h != NULL) {
			nwam_ncu_free(ncu_h);
			ncu_h = NULL;
		}
	}

	if (enm_h != NULL) {
		nwam_enm_free(enm_h);
		enm_h = NULL;
	}

	if (loc_h != NULL) {
		nwam_loc_free(loc_h);
		loc_h = NULL;
	}

	if (wlan_h != NULL) {
		nwam_known_wlan_free(wlan_h);
		wlan_h = NULL;
	}
}

/*
 * On input, TRUE => yes, FALSE => no.
 * On return, TRUE => 1, FALSE => no, could not ask => -1.
 */
static int
ask_yesno(boolean_t default_answer, const char *question)
{
	char line[64];  /* should be enough to answer yes or no */

	if (!ok_to_prompt) {
		saw_error = B_TRUE;
		return (-1);
	}
	for (;;) {
		if (printf("%s (%s)? ", gettext(question),
		    default_answer ? "[y]/n" : "y/[n]") < 0)
			return (-1);
		if (fgets(line, sizeof (line), stdin) == NULL)
			return (-1);

		if (line[0] == '\n')
			return (default_answer ? 1 : 0);
		if (tolower(line[0]) == 'y')
			return (1);
		if (tolower(line[0]) == 'n')
			return (0);
	}
}

/* This is the back-end helper function for read_input() below. */
static int
cleanup()
{
	int answer;

	if (!interactive_mode && !cmd_file_mode) {
		/*
		 * If we're not in interactive mode, and we're not in command
		 * file mode, then we must be in commands-from-the-command-line
		 * mode.  As such, we can't loop back and ask for more input.
		 * It was OK to prompt for such things as whether or not to
		 * really delete something in the command handler called from
		 * yyparse() above, but "really quit?" makes no sense in this
		 * context.  So disable prompting.
		 */
		ok_to_prompt = B_FALSE;
	}
	if (need_to_commit) {
		answer = ask_yesno(B_FALSE,
		    "Configuration not saved; really quit");
		switch (answer) {
		case -1:
			/* issue error here */
			return (NWAM_ERR);
		case 1:
			/*
			 * don't want to save, just exit. handles are freed at
			 * end_func() or exit_func().
			 */
			return (NWAM_OK);
		default:
			/* loop back to read input */
			time_to_exit = B_FALSE;
			yyin = stdin;
			return (NWAM_REPEAT);
		}
	}
	return (saw_error ? NWAM_ERR : NWAM_OK);
}

static int
string_to_yyin(char *string)
{
	if ((yyin = tmpfile()) == NULL)
		goto error;
	if (fwrite(string, strlen(string), 1, yyin) != 1)
		goto error;
	if (fseek(yyin, 0, SEEK_SET) != 0)
		goto error;

	return (NWAM_OK);

error:
	nerr("problem creating temporary file");
	return (NWAM_ERR);
}

/*
 * read_input() is the driver of this program.  It is a wrapper around
 * yyparse(), printing appropriate prompts when needed, checking for
 * exit conditions and reacting appropriately.  This function is
 * called when in interactive mode or command-file mode.
 */
static int
read_input(void)
{
	boolean_t yyin_is_a_tty = isatty(fileno(yyin));
	/*
	 * The prompt is "e> " or "e:t1:o1> " or "e:t1:o1:t2:o2> " where e is
	 * execname, t is resource type, o is object name.
	 */
	char prompt[MAXPATHLEN + (2 * (NWAM_MAX_TYPE_LEN + NWAM_MAX_NAME_LEN))
	    + sizeof ("::::> ")];
	char *line;

	/* yyin should have been set to the appropriate (FILE *) if not stdin */
	newline_terminated = B_TRUE;
	for (;;) {
		if (yyin_is_a_tty) {
			if (newline_terminated) {
				switch (current_scope) {
				case NWAM_SCOPE_GBL:
					(void) snprintf(prompt, sizeof (prompt),
					    "%s> ", execname);
					break;
				case NWAM_SCOPE_LOC:
				case NWAM_SCOPE_ENM:
				case NWAM_SCOPE_WLAN:
				case NWAM_SCOPE_NCP:
					(void) snprintf(prompt, sizeof (prompt),
					    "%s:%s:%s> ", execname,
					    rt1_to_str(obj1_type), obj1_name);

					break;
				case NWAM_SCOPE_NCU:
					(void) snprintf(prompt, sizeof (prompt),
					    "%s:%s:%s:%s:%s> ", execname,
					    rt1_to_str(obj1_type), obj1_name,
					    rt2_to_str(obj2_type), obj2_name);
				}
			}
			/*
			 * If the user hits ^C then we want to catch it and
			 * start over.  If the user hits EOF then we want to
			 * bail out.
			 */
			line = gl_get_line(gl, prompt, NULL, -1);
			if (gl_return_status(gl) == GLR_SIGNAL) {
				gl_abandon_line(gl);
				continue;
			}
			if (line == NULL)
				break;
			if (string_to_yyin(line) != NWAM_OK)
				break;
			while (!feof(yyin)) {
				yyparse();

				/*
				 * If any command on a list of commands
				 * give an error, don't continue with the
				 * remaining commands.
				 */
				if (saw_error || time_to_exit)
					break;
			}
		} else {
			yyparse();
		}

		/* Bail out on an error in command-file mode. */
		if (saw_error && cmd_file_mode && !interactive_mode)
			time_to_exit = B_TRUE;
		if (time_to_exit || (!yyin_is_a_tty && feof(yyin)))
			break;
	}
	return (cleanup());
}

/*
 * This function is used in the interactive-mode scenario: it just calls
 * read_input() until we are done.
 */
static int
do_interactive(void)
{
	int err;

	interactive_mode = B_TRUE;
	do {
		err = read_input();
	} while (err == NWAM_REPEAT);
	return (err);
}

/* Calls the help_func() to print the usage of all commands */
void
help_wrap()
{
	cmd_t *help_cmd;

	if ((help_cmd = alloc_cmd()) == NULL)
		exit(NWAM_ERR);
	help_func(help_cmd);
	free_cmd(help_cmd);
}

/* Check if the given command is allowed in the current scope */
boolean_t
check_scope(int cmd)
{
	/* allowed in all scopes */
	switch (cmd) {
	case CMD_END:
	case CMD_EXIT:
	case CMD_HELP:
	case CMD_LIST:
	case CMD_EXPORT:
		return (B_TRUE);
	}
	/* scope-specific */
	switch (current_scope) {
	case NWAM_SCOPE_GBL:
		switch (cmd) {
		case CMD_CREATE:
		case CMD_DESTROY:
		case CMD_SELECT:
			return (B_TRUE);
		}
		break;
	case NWAM_SCOPE_LOC:
	case NWAM_SCOPE_ENM:
	case NWAM_SCOPE_WLAN:
	case NWAM_SCOPE_NCU:
		switch (cmd) {
		case CMD_CANCEL:
		case CMD_CLEAR:
		case CMD_COMMIT:
		case CMD_GET:
		case CMD_REVERT:
		case CMD_SET:
		case CMD_VERIFY:
		case CMD_WALKPROP:
			return (B_TRUE);
		}
		break;
	case NWAM_SCOPE_NCP:
		switch (cmd) {
		case CMD_CANCEL:
		case CMD_CREATE:
		case CMD_DESTROY:
		case CMD_SELECT:
			return (B_TRUE);
		}
		break;
	default:
		nerr("Invalid scope");
	}
	nerr("'%s' is not allowed at this scope", cmd_to_str(cmd));
	return (B_FALSE);
}

/* Returns the active object type depending on which handle is not NULL */
static nwam_object_type_t
active_object_type()
{
	/* Check ncu_h before ncp_h, ncp_h must be loaded before ncu_h */
	if (ncu_h != NULL)
		return (NWAM_OBJECT_TYPE_NCU);
	else if (ncp_h != NULL)
		return (NWAM_OBJECT_TYPE_NCP);
	else if (loc_h != NULL)
		return (NWAM_OBJECT_TYPE_LOC);
	else if (enm_h != NULL)
		return (NWAM_OBJECT_TYPE_ENM);
	else if (wlan_h != NULL)
		return (NWAM_OBJECT_TYPE_KNOWN_WLAN);
	else
		return (NWAM_OBJECT_TYPE_UNKNOWN);
}

/* Retrive the name of the object from its handle */
static nwam_error_t
object_name_from_handle(nwam_object_type_t object_type, void *handle,
    char **namep)
{
	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCP:
		return (nwam_ncp_get_name(handle, namep));
	case NWAM_OBJECT_TYPE_NCU:
		return (nwam_ncu_get_name(handle, namep));
	case NWAM_OBJECT_TYPE_LOC:
		return (nwam_loc_get_name(handle, namep));
	case NWAM_OBJECT_TYPE_ENM:
		return (nwam_enm_get_name(handle, namep));
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		return (nwam_known_wlan_get_name(handle, namep));
	}
	return (NWAM_INVALID_ARG);
}

static void
do_commit()
{
	nwam_error_t	ret = NWAM_SUCCESS;
	const char	*errprop;

	if (!need_to_commit)
		return;

	switch (active_object_type()) {
	case NWAM_OBJECT_TYPE_NCU:
		ret = nwam_ncu_commit(ncu_h, 0);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		ret = nwam_enm_commit(enm_h, 0);
		break;
	case NWAM_OBJECT_TYPE_LOC:
		ret = nwam_loc_commit(loc_h, 0);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		ret = nwam_known_wlan_commit(wlan_h, 0);
		break;
	}

	if (ret == NWAM_SUCCESS) {
		need_to_commit = B_FALSE;
		if (interactive_mode)
			(void) printf(gettext("Committed changes\n"));
	} else {
		nwam_error_t verr;

		/* Find property that caused failure */
		switch (active_object_type()) {
		case NWAM_OBJECT_TYPE_NCU:
			verr = nwam_ncu_validate(ncu_h, &errprop);
			break;
		case NWAM_OBJECT_TYPE_ENM:
			verr = nwam_enm_validate(enm_h, &errprop);
			break;
		case NWAM_OBJECT_TYPE_LOC:
			verr = nwam_loc_validate(loc_h, &errprop);
			break;
		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
			verr = nwam_known_wlan_validate(wlan_h, &errprop);
			break;
		}

		if (verr != NWAM_SUCCESS)
			nwamerr(ret, "Commit error on property '%s'", errprop);
		else
			nwamerr(ret, "Commit error");
	}
}

/*
 * Saves the current configuration to persistent storage.
 */
/* ARGSUSED */
void
commit_func(cmd_t *cmd)
{
	if (!need_to_commit) {
		if (interactive_mode)
			(void) printf(gettext("Nothing to commit\n"));
	} else {
		do_commit();
	}
}

static void
do_cancel()
{
	switch (current_scope) {
	case NWAM_SCOPE_NCU:
		current_scope = NWAM_SCOPE_NCP;
		obj2_type = 0;
		free_handle(B_TRUE);
		break;
	case NWAM_SCOPE_NCP:
	case NWAM_SCOPE_ENM:
	case NWAM_SCOPE_WLAN:
	case NWAM_SCOPE_LOC:
		current_scope = NWAM_SCOPE_GBL;
		obj1_type = 0;
		free_handle(B_FALSE);
		break;
	case NWAM_SCOPE_GBL:
		free_handle(B_FALSE);
		break;
	default:
		nerr("Invalid scope");
		return;
	}
	need_to_commit = B_FALSE;
}

/*
 * End operation on current scope and go up one scope.
 * Changes are not saved, no prompt either.
 */
/* ARGSUSED */
void
cancel_func(cmd_t *cmd)
{
	do_cancel();
}

/*
 * Removes leading and trailing quotes from a string.
 * Caller must free returned string.
 */
static char *
trim_quotes(const char *quoted_str)
{
	char *str;
	int end;

	/* export_func() and list_func() can pass NULL here */
	if (quoted_str == NULL)
		return (NULL);

	/* remove leading quote */
	if (quoted_str[0] == '"')
		str = strdup(quoted_str + 1);
	else
		str = strdup(quoted_str);
	if (str == NULL)
		return (NULL);

	/* remove trailing quote and newline */
	end = strlen(str) - 1;
	while (end >= 0 && (str[end] == '"' || str[end] == '\n'))
		end--;
	str[end+1] = 0;

	return (str);
}

/*
 * Creates a new resource and enters the scope of that resource.
 * The new resource can also be a copy of an existing resource (-t option).
 * If in interactive mode, then after creation call walkprop_func()
 * to do walk the properties for the new object.
 */
void
create_func(cmd_t *cmd)
{
	nwam_error_t	ret = NWAM_SUCCESS;
	int		c;
	boolean_t	template = B_FALSE;
	char		*newname = NULL, *oldname = NULL;
	cmd_t		*walkprop_cmd;

	/* make sure right command at the right scope */
	if (current_scope == NWAM_SCOPE_GBL &&
	    cmd->cmd_res2_type == RT2_NCU) {
		nerr("cannot create ncu at global scope");
		return;
	}
	if (current_scope == NWAM_SCOPE_NCP &&
	    cmd->cmd_res2_type != RT2_NCU) {
		nerr("Cannot create given object at this scope");
		return;
	}

	assert(cmd->cmd_argc > 0);
	optind = 0;
	while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "t:")) != EOF) {
		switch (c) {
		case 't':
			template = B_TRUE;
			break;
		default:
			command_usage(CMD_CREATE);
			return;
		}
	}

	if (!template) {
		/* no template given */
		/* argv[0] is name */
		newname = trim_quotes(cmd->cmd_argv[0]);
		if (cmd->cmd_res1_type == RT1_ENM) {
			ret = nwam_enm_create(newname, NULL, &enm_h);
		} else if (cmd->cmd_res1_type == RT1_LOC) {
			ret = nwam_loc_create(newname, &loc_h);
		} else if (cmd->cmd_res1_type == RT1_WLAN) {
			ret = nwam_known_wlan_create(newname, &wlan_h);
		} else if (cmd->cmd_res1_type == RT1_NCP &&
		    current_scope == NWAM_SCOPE_GBL) {
			ret = nwam_ncp_create(newname, 0, &ncp_h);
		} else if (cmd->cmd_res2_type == RT2_NCU) {
			nwam_ncu_type_t		ncu_type;
			nwam_ncu_class_t	ncu_class;

			/* ncp must already be read */
			if (ncp_h == NULL) {
				nerr("Create error: NCP has not been read");
				goto done;
			}

			ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
			ncu_type = nwam_ncu_class_to_type(ncu_class);
			ret = nwam_ncu_create(ncp_h, newname, ncu_type,
			    ncu_class, &ncu_h);
		}

		if (ret != NWAM_SUCCESS) {
			nwamerr(ret, "Create error");
			goto done;
		}

	} else {
		/* template given */
		/* argv[0] is -t, argv[1] is old name, argv[2] is new name */
		oldname = trim_quotes(cmd->cmd_argv[1]);
		newname = trim_quotes(cmd->cmd_argv[2]);
		if (cmd->cmd_res1_type == RT1_ENM) {
			nwam_enm_handle_t oldenm_h;

			ret = nwam_enm_read(oldname, 0, &oldenm_h);
			if (ret != NWAM_SUCCESS)
				goto read_error;
			ret = nwam_enm_copy(oldenm_h, newname, &enm_h);
			nwam_enm_free(oldenm_h);
		} else if (cmd->cmd_res1_type == RT1_LOC) {
			nwam_loc_handle_t oldloc_h;

			ret = nwam_loc_read(oldname, 0, &oldloc_h);
			if (ret != NWAM_SUCCESS)
				goto read_error;
			ret = nwam_loc_copy(oldloc_h, newname, &loc_h);
			nwam_loc_free(oldloc_h);
		} else if (cmd->cmd_res1_type == RT1_WLAN) {
			nwam_known_wlan_handle_t oldwlan_h;

			ret = nwam_known_wlan_read(oldname, 0, &oldwlan_h);
			if (ret != NWAM_SUCCESS)
				goto read_error;
			ret = nwam_known_wlan_copy(oldwlan_h, newname, &wlan_h);
			nwam_known_wlan_free(oldwlan_h);
		} else if (cmd->cmd_res1_type == RT1_NCP &&
		    current_scope == NWAM_SCOPE_GBL) {
			nwam_ncp_handle_t oldncp_h;

			ret = nwam_ncp_read(oldname, 0, &oldncp_h);
			if (ret != NWAM_SUCCESS)
				goto read_error;
			ret = nwam_ncp_copy(oldncp_h, newname, &ncp_h);
			nwam_ncp_free(oldncp_h);
		} else if (cmd->cmd_res2_type == RT2_NCU) {
			nwam_ncu_handle_t	oldncu_h;
			nwam_ncu_type_t		ncu_type;
			nwam_ncu_class_t	ncu_class;

			/* ncp must already be read */
			if (ncp_h == NULL) {
				nerr("Copy error: NCP has not been read");
				goto done;
			}
			ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
			ncu_type = nwam_ncu_class_to_type(ncu_class);
			ret = nwam_ncu_read(ncp_h, oldname, ncu_type, 0,
			    &oldncu_h);
			if (ret != NWAM_SUCCESS)
				goto read_error;
			ret = nwam_ncu_copy(oldncu_h, newname, &ncu_h);
			nwam_ncu_free(oldncu_h);
		}

		if (ret != NWAM_SUCCESS) {
			nwamerr(ret, "Copy error");
			goto done;
		}
	}

	if (current_scope == NWAM_SCOPE_GBL) {
		(void) strlcpy(obj1_name, newname, sizeof (obj1_name));
		obj1_type = cmd->cmd_res1_type;
		if (obj1_type == RT1_ENM)
			current_scope = NWAM_SCOPE_ENM;
		else if (obj1_type == RT1_LOC)
			current_scope = NWAM_SCOPE_LOC;
		else if (obj1_type == RT1_WLAN)
			current_scope = NWAM_SCOPE_WLAN;
		else if (obj1_type == RT1_NCP)
			current_scope = NWAM_SCOPE_NCP;
	} else {
		(void) strlcpy(obj2_name, newname, sizeof (obj2_name));
		current_scope = NWAM_SCOPE_NCU;
		obj2_type = cmd->cmd_res2_type;
	}
	if (current_scope != NWAM_SCOPE_NCP)
		need_to_commit = B_TRUE;

	/* do a walk of the properties if in interactive mode */
	if (interactive_mode && current_scope != NWAM_SCOPE_NCP) {
		(void) printf(gettext("Created %s '%s'.  "
		    "Walking properties ...\n"),
		    scope_to_str(current_scope), newname);
		if ((walkprop_cmd = alloc_cmd()) == NULL)
			goto done;
		walkprop_func(walkprop_cmd);
		free(walkprop_cmd);
	}

read_error:
	if (ret != NWAM_SUCCESS)
		nwamerr(ret, "Copy error reading '%s'", oldname);

done:
	free(oldname);
	free(newname);
}

/* Processing of return value for destroy_*_callback() */
static int
destroy_ret(nwam_object_type_t object_type, nwam_error_t ret, void *handle)
{
	if (ret == NWAM_ENTITY_NOT_DESTROYABLE) {
		/* log a message to stderr, but don't consider it an error */
		char *name;
		if (object_name_from_handle(object_type, handle, &name)
		    == NWAM_SUCCESS) {
			(void) fprintf(stderr,
			    gettext("%s '%s' cannot be removed\n"),
			    nwam_object_type_to_string(object_type), name);
			free(name);
		}
		return (0);
	}

	if (ret == NWAM_SUCCESS || ret == NWAM_ENTITY_IN_USE)
		return (0);

	return (1);
}

/*
 * NWAM_FLAG_DO_NOT_FREE is passed to nwam_*_destory() so that it does not
 * free the handle.  The calling nwam_walk_*() function frees this handle
 * as it is the function that created the handle.
 *
 * Objects that are not destroyable or are active cannot be destroyed.
 * Don't return error in these situations so the walk can continue.
 */
/* ARGSUSED */
static int
destroy_ncp_callback(nwam_ncp_handle_t ncp, void *arg)
{
	/* The file is deleted, so NCUs are also removed */
	nwam_error_t ret = nwam_ncp_destroy(ncp, NWAM_FLAG_DO_NOT_FREE);
	return (destroy_ret(NWAM_OBJECT_TYPE_NCP, ret, ncp));
}

/* ARGSUSED */
static int
destroy_loc_callback(nwam_loc_handle_t loc, void *arg)
{
	nwam_error_t ret = nwam_loc_destroy(loc, NWAM_FLAG_DO_NOT_FREE);
	return (destroy_ret(NWAM_OBJECT_TYPE_LOC, ret, loc));
}

/* ARGSUSED */
static int
destroy_enm_callback(nwam_enm_handle_t enm, void *arg)
{
	nwam_error_t ret = nwam_enm_destroy(enm, NWAM_FLAG_DO_NOT_FREE);
	return (destroy_ret(NWAM_OBJECT_TYPE_ENM, ret, enm));
}

/* ARGSUSED */
static int
destroy_wlan_callback(nwam_known_wlan_handle_t wlan, void *arg)
{
	nwam_error_t ret = nwam_known_wlan_destroy(wlan, NWAM_FLAG_DO_NOT_FREE);
	return (destroy_ret(NWAM_OBJECT_TYPE_KNOWN_WLAN, ret, wlan));
}

/*
 * Remove all existing configuration that are not read-only.
 * walk through all ncps, locs, enms, wlans and destroy each one.
 */
static nwam_error_t
destroy_all(void)
{
	nwam_error_t	ret;

	assert(remove_all_configurations);

	ret = nwam_walk_ncps(destroy_ncp_callback, NULL, 0, NULL);
	if (ret != NWAM_SUCCESS)
		goto done;

	ret = nwam_walk_enms(destroy_enm_callback, NULL,
	    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
	if (ret != NWAM_SUCCESS)
		goto done;

	ret = nwam_walk_locs(destroy_loc_callback, NULL,
	    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
	if (ret != NWAM_SUCCESS)
		goto done;

	ret = nwam_walk_known_wlans(destroy_wlan_callback, NULL, 0, NULL);
	if (ret != NWAM_SUCCESS)
		goto done;

	if (interactive_mode)
		(void) printf(gettext("All user-defined entities destroyed\n"));
	remove_all_configurations = B_FALSE;

done:
	if (ret != NWAM_SUCCESS) {
		nwamerr(ret, "Destroy error: "
		    "could not destroy all configurations");
	}
	return (ret);
}

/*
 * Destroys an instance in persistent repository, and is permanent.
 * If interactive mode, it is allowed at global scope only
 * option -a destroys everything.
 */
void
destroy_func(cmd_t *cmd)
{
	nwam_error_t	ret;
	char		*name, *realname = NULL;

	if (current_scope == NWAM_SCOPE_NCP &&
	    (cmd->cmd_res1_type == RT1_ENM || cmd->cmd_res1_type == RT1_LOC ||
	    cmd->cmd_res1_type == RT1_WLAN)) {
		nerr("Destroy error: only NCUs can be destroyed in NCP scope");
		return;
	}

	assert(cmd->cmd_argc > 0);

	/* res1_type is -1 if -a flag is used */
	if (cmd->cmd_res1_type == -1) {
		int c;

		if (current_scope != NWAM_SCOPE_GBL) {
			nerr("Cannot destroy all configurations in a "
			    "non-global scope");
			return;
		}

		optind = 0;
		while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "a")) != EOF) {
			switch (c) {
			case 'a':
				remove_all_configurations = B_TRUE;
				break;
			default:
				command_usage(CMD_DESTROY);
				return;
			}
		}
		if (remove_all_configurations) {
			(void) destroy_all();
			return;
		}
	}

	/* argv[0] is name */
	name = trim_quotes(cmd->cmd_argv[0]);
	if (cmd->cmd_res2_type == RT2_NCU) {
		nwam_ncu_type_t		ncu_type;
		nwam_ncu_class_t	ncu_class;

		/* ncp must already be read */
		if (ncp_h == NULL) {
			nerr("Destroy ncu error: NCP has not been read");
			return;
		}
		ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
		ncu_type = nwam_ncu_class_to_type(ncu_class);
		ret = nwam_ncu_read(ncp_h, name, ncu_type, 0, &ncu_h);
		if (ret != NWAM_SUCCESS)
			goto done;
		(void) object_name_from_handle(NWAM_OBJECT_TYPE_NCU, ncu_h,
		    &realname);
		ret = nwam_ncu_destroy(ncu_h, 0);
		ncu_h = NULL;
	} else if (cmd->cmd_res1_type == RT1_ENM) {
		if ((ret = nwam_enm_read(name, 0, &enm_h)) != NWAM_SUCCESS)
			goto done;
		(void) object_name_from_handle(NWAM_OBJECT_TYPE_ENM, enm_h,
		    &realname);
		ret = nwam_enm_destroy(enm_h, 0);
		enm_h = NULL;
	} else if (cmd->cmd_res1_type == RT1_LOC) {
		if ((ret = nwam_loc_read(name, 0, &loc_h)) != NWAM_SUCCESS)
			goto done;
		(void) object_name_from_handle(NWAM_OBJECT_TYPE_LOC, loc_h,
		    &realname);
		ret = nwam_loc_destroy(loc_h, 0);
		loc_h = NULL;
	} else if (cmd->cmd_res1_type == RT1_WLAN) {
		if ((ret = nwam_known_wlan_read(name, 0, &wlan_h))
		    != NWAM_SUCCESS)
			goto done;
		(void) object_name_from_handle(NWAM_OBJECT_TYPE_KNOWN_WLAN,
		    wlan_h, &realname);
		ret = nwam_known_wlan_destroy(wlan_h, 0);
		wlan_h = NULL;
	} else if (cmd->cmd_res1_type == RT1_NCP) {
		if ((ret = nwam_ncp_read(name, 0, &ncp_h)) != NWAM_SUCCESS)
			goto done;
		(void) object_name_from_handle(NWAM_OBJECT_TYPE_NCP, ncp_h,
		    &realname);
		ret = nwam_ncp_destroy(ncp_h, 0);
		ncp_h = NULL;
	} else {
		nerr("Destroy error: unknown object-type");
	}

done:
	if (ret == NWAM_ENTITY_IN_USE)  {
		nerr("Destroy error: active entity cannot be destroyed");
	} else if (ret != NWAM_SUCCESS) {
		nwamerr(ret, "Destroy error");
	} else if (interactive_mode) {
		(void) printf(gettext("Destroyed %s '%s'\n"),
		    (cmd->cmd_res2_type == RT2_NCU ?
		    rt2_to_str(cmd->cmd_res2_type) :
		    rt1_to_str(cmd->cmd_res1_type)),
		    realname != NULL ? realname : name);
	}
	free(name);
	free(realname);
}

/*
 * End operation on current scope and go up one scope.
 * Changes are saved.
 */
/* ARGSUSED */
void
end_func(cmd_t *cmd)
{
	/* if need_to_commit is set, commit changes */
	if (need_to_commit)
		do_commit();

	/*
	 * Call do_cancel() to go up one scope.  If commit fails,
	 * need_to_commit is not reset and users are asked if they want to end.
	 */
	if (!need_to_commit ||
	    (need_to_commit && (ask_yesno(B_FALSE,
	    "Configuration not saved; really end")) == 1)) {
		/* set time_to_exit if in global scope */
		if (current_scope == NWAM_SCOPE_GBL)
			time_to_exit = B_TRUE;
		/* call do_cancel() to go up one scope */
		do_cancel();
	}
}

/*
 * Exit immediately.  Configuration changes are saved by calling end_func().
 */
/* ARGSUSED */
void
exit_func(cmd_t *cmd)
{
	cmd_t *end_cmd;

	if (need_to_commit) {
		if ((end_cmd = alloc_cmd()) == NULL) {
			nerr("Exit error");
			return;
		}
		end_func(end_cmd);
		free_cmd(end_cmd);
	}

	/*
	 * If need_to_commit is still set, then the commit failed.
	 * Otherwise, exit.
	 */
	if (!need_to_commit)
		time_to_exit = B_TRUE;
}

void
help_func(cmd_t *cmd)
{
	int i;

	if (cmd->cmd_argc == 0) {
		(void) printf(gettext("commands:\n"));
		for (i = CMD_MIN; i <= CMD_MAX; i++)
			(void) printf("\t%s\n", helptab[i].cmd_usage);
		return;
	}

	for (i = CMD_MIN; i <= CMD_MAX; i++) {
		if (strcmp(cmd->cmd_argv[0], cmd_to_str(i)) == 0) {
			long_usage(i);
			return;
		}
	}
	(void) fprintf(stderr, gettext("Unknown command: '%s'\n"),
	    cmd->cmd_argv[0]);
	help_wrap();
}

/*
 * Revert configuration of an instance to latest previous version.
 * Free the handle and read again.
 */
/* ARGSUSED */
void
revert_func(cmd_t *cmd)
{
	nwam_error_t		ret;
	char			*name = NULL;
	nwam_ncu_type_t		ncu_type;
	nwam_object_type_t	object_type = active_object_type();

	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCU:
		/* retrieve name and type to use later */
		if ((ret = nwam_ncu_get_ncu_type(ncu_h, &ncu_type))
		    != NWAM_SUCCESS) {
			nwamerr(ret, "Revert error: Get ncu type error");
			return;
		}
		if ((ret = nwam_ncu_get_name(ncu_h, &name)) != NWAM_SUCCESS)
			goto name_error;
		nwam_ncu_free(ncu_h);
		ncu_h = NULL;
		ret = nwam_ncu_read(ncp_h, name, ncu_type, 0, &ncu_h);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		if ((ret = nwam_enm_get_name(enm_h, &name)) != NWAM_SUCCESS)
			goto name_error;
		nwam_enm_free(enm_h);
		enm_h = NULL;
		ret = nwam_enm_read(name, 0, &enm_h);
		break;
	case NWAM_OBJECT_TYPE_LOC:
		if ((ret = nwam_loc_get_name(loc_h, &name)) != NWAM_SUCCESS)
			goto name_error;
		nwam_loc_free(loc_h);
		loc_h = NULL;
		ret = nwam_loc_read(name, 0, &loc_h);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		if ((ret = nwam_known_wlan_get_name(wlan_h, &name))
		    != NWAM_SUCCESS)
			goto name_error;
		nwam_known_wlan_free(wlan_h);
		wlan_h = NULL;
		ret = nwam_known_wlan_read(name, 0, &wlan_h);
		break;
	}

	/* Exit this scope because handle already freed (call do_cancel()) */
	need_to_commit = B_FALSE;

	if (ret != NWAM_SUCCESS) {
		if (ret == NWAM_ENTITY_NOT_FOUND) {
			nerr("%s '%s' does not exist to revert to, removing it",
			    nwam_object_type_to_string(object_type), name);
		} else {
			nwamerr(ret, "Revert error");
		}
		do_cancel();
	}
	free(name);
	return;

name_error:
	if (ret != NWAM_SUCCESS)
		nwamerr(ret, "Revert error: get name error");
}

/*
 * Load a resource from persistent repository and enter the scope
 * of that resource.
 */
void
select_func(cmd_t *cmd)
{
	nwam_error_t	ret;
	char		*name, *realname = NULL;

	assert(cmd->cmd_argc > 0);
	if (current_scope == NWAM_SCOPE_NCP && cmd->cmd_res2_type != RT2_NCU) {
		nerr("cannot select '%s' at this scope",
		    rt1_to_str(cmd->cmd_res1_type));
		return;
	}

	/* argv[0] is name */
	name = trim_quotes(cmd->cmd_argv[0]);
	switch (cmd->cmd_res1_type) {
	case RT1_LOC:
		ret = nwam_loc_read(name, 0, &loc_h);
		if (ret == NWAM_SUCCESS) {
			current_scope = NWAM_SCOPE_LOC;
			(void) object_name_from_handle(NWAM_OBJECT_TYPE_LOC,
			    loc_h, &realname);
		}
		break;
	case RT1_ENM:
		ret = nwam_enm_read(name, 0, &enm_h);
		if (ret == NWAM_SUCCESS) {
			current_scope = NWAM_SCOPE_ENM;
			(void) object_name_from_handle(NWAM_OBJECT_TYPE_ENM,
			    enm_h, &realname);
		}
		break;
	case RT1_WLAN:
		ret = nwam_known_wlan_read(name, 0, &wlan_h);
		if (ret == NWAM_SUCCESS) {
			current_scope = NWAM_SCOPE_WLAN;
			(void) object_name_from_handle
			    (NWAM_OBJECT_TYPE_KNOWN_WLAN, wlan_h, &realname);
		}
		break;
	case RT1_NCP:
		if (cmd->cmd_res2_type == RT2_NCU) {
			nwam_ncu_type_t		ncu_type;
			nwam_ncu_class_t	ncu_class;

			/* ncp must already be read */
			if (ncp_h == NULL) {
				nerr("Select error: NCP has not been read");
				free(name);
				return;
			}
			ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
			ncu_type = nwam_ncu_class_to_type(ncu_class);
			ret = nwam_ncu_read(ncp_h, name, ncu_type, 0, &ncu_h);
			if (ret == NWAM_SUCCESS) {
				current_scope = NWAM_SCOPE_NCU;
				(void) object_name_from_handle
				    (NWAM_OBJECT_TYPE_NCU, ncu_h, &realname);
			}
		} else {
			ret = nwam_ncp_read(name, 0, &ncp_h);
			if (ret == NWAM_SUCCESS) {
				current_scope = NWAM_SCOPE_NCP;
				(void) object_name_from_handle
				    (NWAM_OBJECT_TYPE_NCP, ncp_h, &realname);
			}
		}
		break;
	default:
		nerr("Select error: unknown object-type");
		free(name);
		return;
	}

	if (ret != NWAM_SUCCESS) {
		nwamerr(ret, "Select error");
	} else {
		/* set the obj*_name or obj*_type depending on current scope */
		if (current_scope == NWAM_SCOPE_NCU) {
			obj2_type = RT2_NCU;
			(void) strlcpy(obj2_name,
			    realname != NULL ? realname : name,
			    sizeof (obj2_name));
		} else {
			(void) strlcpy(obj1_name,
			    realname != NULL ? realname : name,
			    sizeof (obj1_name));
			obj1_type = cmd->cmd_res1_type;
		}
	}
	free(name);
	free(realname);
}

/* Given an int for prop, returns it as string */
static const char *
pt_to_prop_name(nwam_object_type_t object_type, int pt_type)
{
	int i;
	prop_table_entry_t *prop_table = get_prop_table(object_type);

	for (i = 0; prop_table[i].pte_name != NULL; i++) {
		if (pt_type == prop_table[i].pte_type)
			return (prop_table[i].pte_name);
	}
	return (NULL);
}

/* Given a prop as a string, returns it as an int */
static int
prop_to_pt(nwam_object_type_t object_type, const char *prop)
{
	int i;
	prop_table_entry_t *prop_table = get_prop_table(object_type);

	for (i = 0; prop_table[i].pte_name != NULL; i++) {
		if (strcmp(prop, prop_table[i].pte_name) == 0)
			return (prop_table[i].pte_type);
	}
	return (-1);
}

/* Given a prop as an int, returns its type (nwam_value_type_t) */
static nwam_value_type_t
prop_value_type(nwam_object_type_t object_type, const char *prop)
{
	nwam_error_t		ret;
	nwam_value_type_t	value_type;

	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCU:
		ret = nwam_ncu_get_prop_type(prop, &value_type);
		break;
	case NWAM_OBJECT_TYPE_LOC:
		ret = nwam_loc_get_prop_type(prop, &value_type);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		ret = nwam_enm_get_prop_type(prop, &value_type);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		ret = nwam_known_wlan_get_prop_type(prop, &value_type);
		break;
	}

	if (ret != NWAM_SUCCESS)
		value_type = NWAM_VALUE_TYPE_UNKNOWN;

	return (value_type);
}

/*
 * Converts input_str to an array nwam_value.
 * If is_list_prop, break input_str into array of strings first.
 */
static nwam_value_t
str_to_nwam_value(nwam_object_type_t object_type, char *input_str, int pt_type,
    boolean_t is_list_prop)
{
	int		i, n = 0, ret;
	nwam_value_t	data;
	char		**val;
	int		max_str_num;

	nwam_value_type_t	value_type;
	int64_t			*int_vals;
	uint64_t		*uint_vals;
	boolean_t		*boolean_vals;

	/*
	 * Worst case is that each char separated by DELIMITER, so the
	 * max number of sub strings is half of string length + 1.
	 */
	max_str_num = strlen(input_str) / 2 + 1;

	val = calloc(max_str_num, sizeof (char *));
	if (val == NULL) {
		nerr("Out of memory");
		return (NULL);
	}

	if (is_list_prop) {
		char *tmp, *next;
		/*
		 * Break down input_str and save as array of sub strings.
		 * Set num as the number of the sub strings.
		 * Use nwam_tokenize_by_unescaped_delim() rather than strtok()
		 * because DELIMITER may be escaped
		 */
		tmp = (char *)input_str;
		while ((tmp = nwam_tokenize_by_unescaped_delim(tmp,
		    NWAM_VALUE_DELIMITER_CHAR, &next)) != NULL) {
			val[n++] = trim_quotes(tmp);
			tmp = next;
		}
	} else {
		val[n++] = trim_quotes(input_str);
	}

	/* initialize int_vals or booleans_vals depending on pt_type */
	value_type = prop_value_type(object_type,
	    pt_to_prop_name(object_type, pt_type));
	if (value_type == NWAM_VALUE_TYPE_INT64) {
		int_vals = calloc(n, sizeof (int64_t));
		if (int_vals == NULL) {
			nerr("Out of memory");
			array_free((void **)val, max_str_num);
			return (NULL);
		}
	} else if (value_type == NWAM_VALUE_TYPE_UINT64) {
		uint_vals = calloc(n, sizeof (uint64_t));
		if (uint_vals == NULL) {
			nerr("Out of memory");
			array_free((void **)val, max_str_num);
			return (NULL);
		}
	} else if (value_type == NWAM_VALUE_TYPE_BOOLEAN) {
		boolean_vals = calloc(n, sizeof (boolean_t));
		if (boolean_vals == NULL) {
			nerr("Out of memory");
			array_free((void **)val, max_str_num);
			return (NULL);
		}
	}
	/* set the appropriate array */
	for (i = 0; i < n; i++) {
		switch (value_type) {
		case NWAM_VALUE_TYPE_STRING:
			/* nothing to do - val already has the char** array */
			break;
		case NWAM_VALUE_TYPE_INT64:
		{
			int_vals[i] = (int64_t)atoi(val[i]);
			break;
		}
		case NWAM_VALUE_TYPE_UINT64:
		{
			uint64_t str_as_enum;
			char *endptr;

			ret = nwam_value_string_get_uint64(
			    pt_to_prop_name(object_type, pt_type),
			    val[i], &str_as_enum);
			/*
			 * Returns _SUCCESS if value for enum is valid.
			 * Returns _INVALID_ARG if property is not an enum.
			 */
			if (ret == NWAM_SUCCESS) {
				uint_vals[i] = str_as_enum;
			} else if (ret == NWAM_INVALID_ARG) {
				uint_vals[i] = strtoul(val[i], &endptr, 10);
				/* verify conversion is valid */
				if (endptr == val[i]) {
					free(uint_vals);
					array_free((void **)val, max_str_num);
					return (NULL);
				}
			} else {
				free(uint_vals);
				array_free((void **)val, max_str_num);
				return (NULL);
			}
			break;
		}
		case NWAM_VALUE_TYPE_BOOLEAN:
			boolean_vals[i] = str_to_boolean(val[i]);
			break;
		default:
			array_free((void **)val, max_str_num);
			return (NULL);
		}
	}

	/* create nwam_value_t */
	if (value_type == NWAM_VALUE_TYPE_STRING) {
		ret = nwam_value_create_string_array(val, n, &data);
	} else if (value_type == NWAM_VALUE_TYPE_INT64) {
		ret = nwam_value_create_int64_array(int_vals, n, &data);
		free(int_vals);
	} else if (value_type == NWAM_VALUE_TYPE_UINT64) {
		ret = nwam_value_create_uint64_array(uint_vals, n, &data);
		free(uint_vals);
	} else if (value_type == NWAM_VALUE_TYPE_BOOLEAN) {
		ret = nwam_value_create_boolean_array(boolean_vals, n, &data);
		free(boolean_vals);
	}
	array_free((void **)val, max_str_num);

	if (ret != NWAM_SUCCESS) {
		nwamerr(ret, "Failed creating nwam_value");
		return (NULL);
	}

	return (data);
}

/*
 * Displaying/Skipping of properties
 * ---------------------------------
 *
 * This table shows if a specific property should be shown if some
 * other property has a specific value.  This table is used by
 * show_prop_test(), which is called by set_func() and walkprop_func().
 *
 * An entry in the table looks like:
 *	{ property1, property2, { val1, val2, -1 } }
 * This is read as:
 *	"show property1 only if property2 has value val1 or val2"
 *
 * NB: If a property does not appear in this table, then that implies
 * that the property is always shown.
 *
 * A property can have more than one rule.  In such a case, the property is
 * displayed only any of the rules is satisfied.  This checking, however,
 * is recursive.  If a rule says that a property can be displayed, then the
 * property that's checked should also satisfy its rules.  In the above
 * example, if property1 is to be displayed, then property2 should also
 * satisfy its rules and be displayable.  This recursion is necessary as
 * properties that are not displayed (because rules are not satisfied) are
 * not deleted.
 */

/* The most number of values in pde_checkvals below */
#define	NWAM_CHECKVALS_MAX	5

typedef struct prop_display_entry {
	const char	*pde_name;		/* property to show */
	const char	*pde_checkname;		/* property to check */
	int64_t	pde_checkvals[NWAM_CHECKVALS_MAX]; /* show prop for these */
} prop_display_entry_t;

/* Rules for showing properties: commented for clarity */

/*
 * Rules for NCUs
 * NB: There is no need to have an entry if a property is for IP only.
 *     This is taken care of in libnwam_ncp.c
 */
static prop_display_entry_t ncu_prop_display_entry_table[] = {
	/* show priority-{group,mode} if activation == prioritized */
	{ NWAM_NCU_PROP_PRIORITY_GROUP, NWAM_NCU_PROP_ACTIVATION_MODE,
	    { NWAM_ACTIVATION_MODE_PRIORITIZED, -1 } },
	{ NWAM_NCU_PROP_PRIORITY_MODE, NWAM_NCU_PROP_ACTIVATION_MODE,
	    { NWAM_ACTIVATION_MODE_PRIORITIZED, -1 } },
	/* show ipv4-addrsrc if ip-version == ipv4 */
	{ NWAM_NCU_PROP_IPV4_ADDRSRC, NWAM_NCU_PROP_IP_VERSION,
	    { IPV4_VERSION, -1 } },
	/* show ipv4-addr if ipv4-addrsrc == static */
	{ NWAM_NCU_PROP_IPV4_ADDR, NWAM_NCU_PROP_IPV4_ADDRSRC,
	    { NWAM_ADDRSRC_STATIC, -1 } },
	/* show ipv4-default-route if ip-version == ipv4 */
	{ NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE, NWAM_NCU_PROP_IP_VERSION,
	    { IPV4_VERSION, -1 } },
	/* show ipv6-addrsrc if ip-version == ipv6 */
	{ NWAM_NCU_PROP_IPV6_ADDRSRC, NWAM_NCU_PROP_IP_VERSION,
	    { IPV6_VERSION, -1 } },
	/* show ipv6-addr if ipv6-addrsrc == static */
	{ NWAM_NCU_PROP_IPV6_ADDR, NWAM_NCU_PROP_IPV6_ADDRSRC,
	    { NWAM_ADDRSRC_STATIC, -1 } },
	/* show ipv6-default-route if ip-version == ipv6 */
	{ NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, NWAM_NCU_PROP_IP_VERSION,
	    { IPV6_VERSION, -1 } },
	{ NULL, NULL, { -1 } }
};

/* Rules for ENMs */
static prop_display_entry_t enm_prop_display_entry_table[] = {
	/* show conditions if activation-mode == conditional-{all,any} */
	{ NWAM_ENM_PROP_CONDITIONS, NWAM_ENM_PROP_ACTIVATION_MODE,
	    { NWAM_ACTIVATION_MODE_CONDITIONAL_ALL,
	    NWAM_ACTIVATION_MODE_CONDITIONAL_ANY, -1 } },
	{ NULL, NULL, { -1 } }
};

/* Rules for LOCations */
static prop_display_entry_t loc_prop_display_entry_table[] = {
	/* show conditions if activation-mode == conditional-{all,any} */
	{ NWAM_LOC_PROP_CONDITIONS, NWAM_LOC_PROP_ACTIVATION_MODE,
	    { NWAM_ACTIVATION_MODE_CONDITIONAL_ALL,
	    NWAM_ACTIVATION_MODE_CONDITIONAL_ANY, -1 } },
	/* show dns-nameservice-configsrc if nameservices == dns */
	{ NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC, NWAM_LOC_PROP_NAMESERVICES,
	    { NWAM_NAMESERVICES_DNS, -1 } },
	/* show other DNS options if dns-nameservices-configsrc == manual */
	{ NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN,
	    NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
	    { NWAM_CONFIGSRC_MANUAL, -1 } },
	{ NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS,
	    NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
	    { NWAM_CONFIGSRC_MANUAL, -1 } },
	{ NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH,
	    NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
	    { NWAM_CONFIGSRC_MANUAL, -1 } },
	/* show nis-nameservice-configsrc if nameservices == nis */
	{ NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC, NWAM_LOC_PROP_NAMESERVICES,
	    { NWAM_NAMESERVICES_NIS, -1 } },
	/* show nis-nameservice-servers if nis-nameservice-configsrc = manual */
	{ NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS,
	    NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
	    { NWAM_CONFIGSRC_MANUAL, -1 } },
	/* show ldap-nameservice-configsrc if nameservices == ldap */
	{ NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC, NWAM_LOC_PROP_NAMESERVICES,
	    { NWAM_NAMESERVICES_LDAP, -1 } },
	/* show ldap-nameservice-servers if ldap-nameservice-configsrc=manual */
	{ NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS,
	    NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
	    { NWAM_CONFIGSRC_MANUAL, -1 } },
	/* show default-domain if {nis,ldap}-nameservice-configsrc == manual */
	{ NWAM_LOC_PROP_DEFAULT_DOMAIN, NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
	    { NWAM_CONFIGSRC_MANUAL, -1 } },
	{ NWAM_LOC_PROP_DEFAULT_DOMAIN,
	    NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
	    { NWAM_CONFIGSRC_MANUAL, -1 } },
	{ NULL, NULL, { -1 } }
};

/* Rules for Known WLANs */
static prop_display_entry_t wlan_prop_display_entry_table[] = {
	/* no rules for WLANs */
	{ NULL, NULL, { -1 } }
};

/* Returns the appropriate rules table for the given object type */
static prop_display_entry_t *
get_prop_display_table(nwam_object_type_t object_type)
{
	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCU:
		return (ncu_prop_display_entry_table);
	case NWAM_OBJECT_TYPE_LOC:
		return (loc_prop_display_entry_table);
	case NWAM_OBJECT_TYPE_ENM:
		return (enm_prop_display_entry_table);
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		return (wlan_prop_display_entry_table);
	}
	return (NULL);
}

/*
 * Tests whether prop must be shown during a walk depending on the
 * value of a different property.
 *
 * This function is also used by set_func() to determine whether the
 * property being set should be allowed or not.  If the property
 * would not be displayed in a walk, then it should not be set.
 *
 * The checked_props and num_checked arguments are used to avoid circular
 * dependencies between properties.  When this function recursively calls
 * itself, it adds the property that it just checked to the checked_props
 * list.
 */
static boolean_t
show_prop_test(nwam_object_type_t object_type, const char *prop,
    prop_display_entry_t *display_list, char **checked_props, int num_checked)
{
	nwam_error_t		ret;
	nwam_value_t		prop_val;
	nwam_value_type_t	prop_type;
	int			i, j, k;
	boolean_t		prop_found = B_FALSE, show_prop = B_FALSE;

	/*
	 * Check if this property has already been checked previously in
	 * the recursion.  If so, return B_FALSE so that the initial prop
	 * is not displayed.
	 */
	for (i = 0; i < num_checked; i++) {
		if (strcmp(prop, checked_props[i]) == 0) {
			free(checked_props);
			return (B_FALSE);
		}
	}

	for (i = 0; display_list[i].pde_name != NULL; i++) {
		if (strcmp(prop, display_list[i].pde_name) != 0)
			continue;
		prop_found = B_TRUE;

		/* get the value(s) of the (other) property to check */
		switch (object_type) {
		case NWAM_OBJECT_TYPE_NCU:
			ret = nwam_ncu_get_prop_value(ncu_h,
			    display_list[i].pde_checkname, &prop_val);
			break;
		case NWAM_OBJECT_TYPE_LOC:
			ret = nwam_loc_get_prop_value(loc_h,
			    display_list[i].pde_checkname, &prop_val);
			break;
		case NWAM_OBJECT_TYPE_ENM:
			ret = nwam_enm_get_prop_value(enm_h,
			    display_list[i].pde_checkname, &prop_val);
			break;
		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
			return (B_TRUE);
		}
		if (ret != NWAM_SUCCESS)
			continue;

		/* prop_val may contain a uint64 array or a boolean */
		if (nwam_value_get_type(prop_val, &prop_type) != NWAM_SUCCESS)
			continue;

		if (prop_type == NWAM_VALUE_TYPE_UINT64) {
			uint64_t	*prop_uvals;
			int64_t		*check_uvals;
			uint_t		numvals;

			if (nwam_value_get_uint64_array(prop_val, &prop_uvals,
			    &numvals) != NWAM_SUCCESS) {
				nwam_value_free(prop_val);
				continue;
			}

			/* for each value in uvals, check each value in table */
			for (j = 0; j < numvals; j++) {
				check_uvals = display_list[i].pde_checkvals;
				for (k = 0; check_uvals[k] != -1; k++) {
					/* show if uvals[j] matches */
					if (prop_uvals[j] ==
					    (uint64_t)check_uvals[k]) {
						show_prop = B_TRUE;
						goto next_rule;
					}
				}
			}
		} else if (prop_type == NWAM_VALUE_TYPE_BOOLEAN) {
			boolean_t bval;

			if (nwam_value_get_boolean(prop_val, &bval) !=
			    NWAM_SUCCESS) {
				nwam_value_free(prop_val);
				continue;
			}

			for (k = 0;
			    display_list[i].pde_checkvals[k] != -1;
			    k++) {
				/* show if bval matches */
				if (bval == (boolean_t)
				    display_list[i].pde_checkvals[k]) {
					show_prop = B_TRUE;
					goto next_rule;
				}
			}
		}

next_rule:
		nwam_value_free(prop_val);
		/*
		 * If show_prop is set, then a rule is satisfied; no need to
		 * check other rules for this prop.  However, recursively
		 * check if the checked prop (pde_checkname) satisfies its
		 * rules.  Also, update the check_props array with this prop.
		 */
		if (show_prop) {
			char **newprops = realloc(checked_props,
			    ++num_checked * sizeof (char *));
			if (newprops == NULL) {
				free(checked_props);
				return (B_FALSE);
			}
			checked_props = newprops;
			checked_props[num_checked - 1] = (char *)prop;

			return (show_prop_test(object_type,
			    display_list[i].pde_checkname, display_list,
			    checked_props, num_checked));
		}
	}

	/*
	 * If we are here and prop_found is set, it means that no rules were
	 * satisfied by prop; return B_FALSE.  If prop_found is not set, then
	 * prop did not have a rule so it must be displayed; return B_TRUE.
	 */
	free(checked_props);
	if (prop_found)
		return (B_FALSE);
	else
		return (B_TRUE);
}

/*
 * Returns true if the given property is read-only and cannot be modified.
 */
static boolean_t
is_prop_read_only(nwam_object_type_t object_type, const char *prop)
{
	boolean_t ro;

	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCU:
		if (nwam_ncu_prop_read_only(prop, &ro) == NWAM_SUCCESS && ro)
			return (B_TRUE);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		if (nwam_enm_prop_read_only(prop, &ro) == NWAM_SUCCESS && ro)
			return (B_TRUE);
		break;
	case NWAM_OBJECT_TYPE_LOC:
		if (nwam_loc_prop_read_only(prop, &ro) == NWAM_SUCCESS && ro)
			return (B_TRUE);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		/* no read-only properties for WLANs */
		return (B_FALSE);
	}
	return (B_FALSE);
}

/* Returns true if the property is multi-valued */
static boolean_t
is_prop_multivalued(nwam_object_type_t object_type, const char *prop)
{
	nwam_error_t	ret;
	boolean_t	multi;

	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCU:
		ret = nwam_ncu_prop_multivalued(prop, &multi);
		break;
	case NWAM_OBJECT_TYPE_LOC:
		ret = nwam_loc_prop_multivalued(prop, &multi);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		ret = nwam_enm_prop_multivalued(prop, &multi);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		ret = nwam_known_wlan_prop_multivalued(prop, &multi);
		break;
	}

	if (ret != NWAM_SUCCESS)
		multi = B_FALSE;
	return (multi);
}

/*
 * Prints out error message specific to property that could not be set.
 * Property description is used to help guide user in entering correct value.
 */
static void
invalid_set_prop_msg(const char *prop, nwam_error_t err)
{
	const char *description;

	if (err == NWAM_SUCCESS)
		return;

	if (err != NWAM_ENTITY_INVALID_VALUE) {
		nwamerr(err, "Set error");
		return;
	}

	switch (active_object_type()) {
	case NWAM_OBJECT_TYPE_NCU:
		(void) nwam_ncu_get_prop_description(prop, &description);
		break;
	case NWAM_OBJECT_TYPE_LOC:
		(void) nwam_loc_get_prop_description(prop, &description);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		(void) nwam_enm_get_prop_description(prop, &description);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		(void) nwam_known_wlan_get_prop_description(prop,
		    &description);
		break;
	}
	nerr("Set error: invalid value\n'%s' %s", prop, description);
}

/*
 * Sets the property value.
 * Read-only properties and objects cannot be set.
 * "read-only" is a special in that it can be set on a read-only object.
 * The object has to be committed before other properties can be set.
 * Also uses show_prop_test() to test if the property being set would
 * be skipped during a walk (as determined by the value of some other
 * property).  If so, then it cannot be set.
 */
void
set_func(cmd_t *cmd)
{
	int			pt_type = cmd->cmd_prop_type;
	nwam_error_t		ret = NWAM_SUCCESS;
	nwam_value_t		prop_value;
	const char		*prop;
	boolean_t		is_listprop = B_FALSE;
	nwam_object_type_t	object_type;
	prop_display_entry_t	*prop_table;
	char			**checked = NULL;

	assert(cmd->cmd_argc > 0);

	object_type = active_object_type();
	prop_table = get_prop_display_table(object_type);

	/* argv[0] is property value */
	if ((prop = pt_to_prop_name(object_type, pt_type)) == NULL) {
		nerr("Set error: invalid %s property: '%s'",
		    scope_to_str(current_scope), pt_to_str(pt_type));
		return;
	}

	/* check if property can be set */
	if (is_prop_read_only(object_type, prop)) {
		nerr("Set error: property '%s' is read-only", prop);
		return;
	}
	if (!show_prop_test(object_type, prop, prop_table, checked, 0)) {
		if (interactive_mode) {
			(void) printf(gettext("setting property '%s' "
			    "has no effect\n"), prop);
		}
	}

	is_listprop = is_prop_multivalued(object_type, prop);
	prop_value = str_to_nwam_value(object_type, cmd->cmd_argv[0], pt_type,
	    is_listprop);
	if (prop_value == NULL) {
		invalid_set_prop_msg(prop, NWAM_ENTITY_INVALID_VALUE);
		return;
	}

	/* set the property value */
	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCU:
		ret = nwam_ncu_set_prop_value(ncu_h, prop, prop_value);
		break;
	case NWAM_OBJECT_TYPE_LOC:
		ret = nwam_loc_set_prop_value(loc_h, prop, prop_value);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		ret = nwam_enm_set_prop_value(enm_h, prop, prop_value);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		ret = nwam_known_wlan_set_prop_value(wlan_h, prop, prop_value);
		break;
	}
	nwam_value_free(prop_value);

	/* delete other properties if needed */
	if (ret == NWAM_SUCCESS)
		need_to_commit = B_TRUE;
	else
		invalid_set_prop_msg(prop, ret);
}

static int
list_callback(nwam_object_type_t object_type, void *handle,
    boolean_t *list_msgp, const char *msg)
{
	nwam_error_t		ret;
	char			*name;
	nwam_ncu_class_t	class;

	if (*list_msgp) {
		(void) printf("%s:\n", msg);
		*list_msgp = B_FALSE;
	}

	ret = object_name_from_handle(object_type, handle, &name);
	if (ret != NWAM_SUCCESS) {
		nwamerr(ret, "List error: failed to get name");
		return (1);
	}

	/* If NCU, get its class and print */
	if (object_type == NWAM_OBJECT_TYPE_NCU) {
		if ((ret = nwam_ncu_get_ncu_class(handle, &class))
		    != NWAM_SUCCESS) {
			nwamerr(ret, "List error: failed to get ncu class");
			free(name);
			return (1);
		} else {
			(void) printf("\t%s",
			    propval_to_str(NWAM_NCU_PROP_CLASS, class));
		}
	}
	(void) printf("\t%s\n", name);

	free(name);
	return (0);
}

/* Print out name, type and status */
static int
list_loc_callback(nwam_loc_handle_t loc, void *arg)
{
	return (list_callback(NWAM_OBJECT_TYPE_LOC, loc, arg, "Locations"));
}

static int
list_enm_callback(nwam_enm_handle_t enm, void *arg)
{
	return (list_callback(NWAM_OBJECT_TYPE_ENM, enm, arg, "ENMs"));
}

static int
list_wlan_callback(nwam_known_wlan_handle_t wlan, void *arg)
{
	return (list_callback(NWAM_OBJECT_TYPE_KNOWN_WLAN, wlan, arg, "WLANs"));
}

static int
list_ncp_callback(nwam_ncp_handle_t ncp, void *arg)
{
	return (list_callback(NWAM_OBJECT_TYPE_NCP, ncp, arg, "NCPs"));
}

static int
list_ncu_callback(nwam_ncu_handle_t ncu, void *arg)
{
	return (list_callback(NWAM_OBJECT_TYPE_NCU, ncu, arg, "NCUs"));
}

/* functions to convert a value to a string */
/* ARGSUSED */
static const char *
str2str(void *s, const char *prop, char *str)
{
	(void) snprintf(str, NWAM_MAX_VALUE_LEN, "%s", s);
	return (str);
}

/* ARGSUSED */
static const char *
str2qstr(void *s, const char *prop, char *qstr)
{
	/* quoted strings */
	(void) snprintf(qstr, NWAM_MAX_VALUE_LEN, "\"%s\"", s);
	return (qstr);
}

/* ARGSUSED */
static const char *
int2str(void *in, const char *prop, char *instr)
{
	(void) snprintf(instr, NWAM_MAX_VALUE_LEN, "%lld", *((int64_t *)in));
	return (instr);
}

static const char *
uint2str(void *uin, const char *prop, char *uintstr)
{
	/* returns NWAM_SUCCESS if prop is enum with string in uintstr */
	if (nwam_uint64_get_value_string(prop, *((uint64_t *)uin),
	    (const char **)&uintstr) != NWAM_SUCCESS) {
		(void) snprintf(uintstr, NWAM_MAX_VALUE_LEN, "%lld",
		    *((uint64_t *)uin));
	}
	return (uintstr);
}

/* ARGSUSED */
static const char *
bool2str(void *bool, const char *prop, char *boolstr)
{
	(void) snprintf(boolstr, NWAM_MAX_VALUE_LEN, "%s",
	    *((boolean_t *)bool) ? "true" : "false");
	return (boolstr);
}

/*
 * Print the value (enums are converted to string), use DELIMITER for
 * array.  If strings are to be "quoted", pass B_TRUE for quoted_strings.
 */
static void
output_prop_val(const char *prop_name, nwam_value_t value, FILE *wf,
    boolean_t quoted_strings)
{
	nwam_value_type_t	value_type;
	uint_t			num;

	/* arrays for values retrieved according to the type of value */
	char		**svals;
	uint64_t	*uvals;
	int64_t		*ivals;
	boolean_t	*bvals;

	/* pointer to function to generate string representation of value */
	const char	*(*tostr)(void *, const char *, char *);
	char		str[NWAM_MAX_VALUE_LEN]; /* to store the string */
	int		i;

	if (nwam_value_get_type(value, &value_type) != NWAM_SUCCESS) {
		nerr("Get value type error");
		return;
	}

	if (value_type == NWAM_VALUE_TYPE_STRING) {
		if (nwam_value_get_string_array(value, &svals, &num) !=
		    NWAM_SUCCESS) {
			nerr("Get string array error");
			return;
		}
		tostr = quoted_strings ? str2qstr : str2str;
	} else if (value_type == NWAM_VALUE_TYPE_INT64) {
		if (nwam_value_get_int64_array(value, &ivals, &num) !=
		    NWAM_SUCCESS) {
			nerr("Get int64 array error");
			return;
		}
		tostr = int2str;
	} else if (value_type == NWAM_VALUE_TYPE_UINT64) {
		if (nwam_value_get_uint64_array(value, &uvals, &num) !=
		    NWAM_SUCCESS) {
			nerr("Get uint64 array error");
			return;
		}
		tostr = uint2str;
	} else if (value_type == NWAM_VALUE_TYPE_BOOLEAN) {
		if (nwam_value_get_boolean_array(value, &bvals, &num) !=
		    NWAM_SUCCESS) {
			nerr("Get boolean array error");
			return;
		}
		tostr = bool2str;
	}

	/* now, loop and print each value */
	for (i = 0; i < num; i++) {
		void *val;

		/* get the pointer to the ith value to pass to func() */
		if (value_type == NWAM_VALUE_TYPE_STRING)
			val = svals[i];
		else if (value_type == NWAM_VALUE_TYPE_UINT64)
			val = &(uvals[i]);
		else if (value_type == NWAM_VALUE_TYPE_INT64)
			val = &(ivals[i]);
		else if (value_type == NWAM_VALUE_TYPE_BOOLEAN)
			val = &(bvals[i]);

		(void) fprintf(wf, "%s%s", tostr(val, prop_name, str),
		    i != num-1 ? NWAM_VALUE_DELIMITER_STR : "");
	}
}

/* Prints the property names aligned (for list/get) or "prop=" (for export) */
static int
output_propname_common(const char *prop, nwam_value_t values, void *arg,
    int width)
{
	FILE *of = (arg == NULL) ? stdout : arg;

	/* arg is NULL for list/get, not NULL for export */
	if (arg == NULL)
		(void) fprintf(of, "\t%-*s\t", width, prop);
	else
		(void) fprintf(of, "%s=", prop);

	if (values != NULL)
		output_prop_val(prop, values, of, B_TRUE);

	(void) fprintf(of, "\n");
	return (0);
}

static int
output_propname(const char *prop, nwam_value_t values, void *arg)
{
	return (output_propname_common(prop, values, arg, 16));
}

/* For locations because of longer property names */
static int
output_loc_propname(const char *prop, nwam_value_t values, void *arg)
{
	return (output_propname_common(prop, values, arg, 25));
}

/*
 * all_props specifies whether properties that have not been set should be
 * printed or not.  ncp and ncu_type are used only when the object_type is
 * NCU.
 */
static nwam_error_t
listprop(nwam_object_type_t object_type, void *handle, const char *name,
    boolean_t all_props, nwam_ncp_handle_t ncp, nwam_ncu_type_t ncu_type)
{
	nwam_error_t	ret;
	char		*lname = NULL, *realname = NULL;
	boolean_t	lhandle = B_FALSE;
	const char	**props = NULL;
	uint_t		prop_num;
	int		i;
	nwam_value_t	vals;

	/*
	 * handle is NULL if called from a scope higher than the object's
	 * scope, but name must be given; so get the handle.
	 */
	if (handle == NULL) {
		lname = trim_quotes(name); /* name may have quotes */
		switch (object_type) {
		case NWAM_OBJECT_TYPE_NCP:
			if ((ret = nwam_ncp_read(lname, 0,
			    (nwam_ncp_handle_t *)&handle)) != NWAM_SUCCESS)
				goto readfail;
			break;
		case NWAM_OBJECT_TYPE_NCU:
			ret = nwam_ncu_read(ncp, lname, ncu_type, 0,
			    (nwam_ncu_handle_t *)&handle);
			if (ret == NWAM_ENTITY_MULTIPLE_VALUES) {
				/*
				 * Multiple NCUs with the given name exists.
				 * Call listprop() for each NCU type.
				 */
				if ((ret = listprop(object_type, NULL, lname,
				    all_props, ncp, NWAM_NCU_TYPE_LINK))
				    != NWAM_SUCCESS)
					goto done;
				ret = listprop(object_type, NULL, lname,
				    all_props, ncp, NWAM_NCU_TYPE_INTERFACE);
				goto done;
			} else if (ret != NWAM_SUCCESS) {
				goto readfail;
			}
			break;
		case NWAM_OBJECT_TYPE_LOC:
			if ((ret = nwam_loc_read(lname, 0,
			    (nwam_loc_handle_t *)&handle)) != NWAM_SUCCESS)
				goto readfail;
			break;
		case NWAM_OBJECT_TYPE_ENM:
			if ((ret = nwam_enm_read(lname, 0,
			    (nwam_enm_handle_t *)&handle)) != NWAM_SUCCESS)
				goto readfail;
			break;
		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
			if ((ret = nwam_known_wlan_read(lname, 0,
			    (nwam_known_wlan_handle_t *)&handle))
			    != NWAM_SUCCESS)
				goto readfail;
			break;
		}
		lhandle = B_TRUE;
	}

	if ((ret = object_name_from_handle(object_type, handle, &realname))
	    != NWAM_SUCCESS)
		goto done;

	/* get the property list */
	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCP:
	{
		/* walk NCUs */
		boolean_t list_msg = B_TRUE;
		ret = nwam_ncp_walk_ncus(handle, list_ncu_callback, &list_msg,
		    NWAM_FLAG_NCU_TYPE_CLASS_ALL, NULL);
		goto done;
	}
	case NWAM_OBJECT_TYPE_NCU:
	{
		nwam_ncu_type_t		ncu_type;
		nwam_ncu_class_t	ncu_class;

		if ((ret = nwam_ncu_get_ncu_type(handle, &ncu_type))
		    != NWAM_SUCCESS)
			goto done;
		if ((ret = nwam_ncu_get_ncu_class(handle, &ncu_class))
		    != NWAM_SUCCESS)
			goto done;

		ret = nwam_ncu_get_default_proplist(ncu_type, ncu_class, &props,
		    &prop_num);
		break;
	}
	case NWAM_OBJECT_TYPE_LOC:
		ret = nwam_loc_get_default_proplist(&props, &prop_num);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		ret = nwam_enm_get_default_proplist(&props, &prop_num);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		ret = nwam_known_wlan_get_default_proplist(&props, &prop_num);
		break;
	}
	if (ret != NWAM_SUCCESS)
		goto done;

	/* print object type and name */
	(void) printf("%s:%s\n", nwam_object_type_to_string(object_type),
	    realname);

	/* Loop through the properties and print */
	for (i = 0; i < prop_num; i++) {
		/* get the existing value for this property */
		switch (object_type) {
		case NWAM_OBJECT_TYPE_NCU:
			ret = nwam_ncu_get_prop_value(handle, props[i], &vals);
			break;
		case NWAM_OBJECT_TYPE_LOC:
			ret = nwam_loc_get_prop_value(handle, props[i], &vals);
			break;
		case NWAM_OBJECT_TYPE_ENM:
			ret = nwam_enm_get_prop_value(handle, props[i], &vals);
			break;
		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
			ret = nwam_known_wlan_get_prop_value(handle, props[i],
			    &vals);
			break;
		}
		if (ret != NWAM_SUCCESS) {
			/* _ENTITY_NOT_FOUND is ok if listing for all props */
			if (!all_props)
				continue;
			else if (ret != NWAM_ENTITY_NOT_FOUND)
				continue;
		}

		/* print property and value */
		if (object_type == NWAM_OBJECT_TYPE_LOC)
			output_loc_propname(props[i], vals, NULL);
		else
			output_propname(props[i], vals, NULL);
		nwam_value_free(vals);
	}

done:
	free(lname);
	free(realname);
	if (props != NULL)
		free(props);
	if (lhandle) {
		switch (object_type) {
		case NWAM_OBJECT_TYPE_NCP:
			nwam_ncp_free(handle);
			break;
		case NWAM_OBJECT_TYPE_NCU:
			nwam_ncu_free(handle);
			break;
		case NWAM_OBJECT_TYPE_LOC:
			nwam_loc_free(handle);
			break;
		case NWAM_OBJECT_TYPE_ENM:
			nwam_enm_free(handle);
			break;
		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
			nwam_known_wlan_free(handle);
			break;
		}
	}
	/* don't treat _ENTITY_NOT_FOUND as an error */
	if (ret == NWAM_ENTITY_NOT_FOUND)
		ret = NWAM_SUCCESS;
	return (ret);

readfail:
	/* When nwam_*_read() fails */
	free(lname);
	return (ret);
}

/*
 * List profiles or property and its values.
 * If the -a option is specified, all properties are listed.
 */
void
list_func(cmd_t *cmd)
{
	nwam_error_t	ret = NWAM_SUCCESS;
	boolean_t	list_msg = B_TRUE;

	boolean_t	list_loc = B_FALSE, list_enm = B_FALSE;
	boolean_t	list_ncp = B_FALSE, list_ncu = B_FALSE;
	boolean_t	list_wlan = B_FALSE;

	/* whether all properties should be listed, given by the -a option */
	boolean_t	all_props = B_FALSE;

	/*
	 * list_props says whether the properties should be listed.
	 * Note that, here NCUs are treated as properties of NCPs.
	 */
	boolean_t	list_props = B_FALSE;

	/* determine which properties to list, also validity tests */
	if (current_scope == NWAM_SCOPE_GBL) {
		/* res1_type is -1 if only "list -a" is used */
		if (cmd->cmd_res1_type == -1) {
			nerr("'list' requires an object to be specified with "
			    "the -a option in the global scope");
			return;
		}
		if (cmd->cmd_res1_type == RT1_LOC) {
			list_props = B_TRUE;
			list_loc = B_TRUE;
		} else if (cmd->cmd_res1_type == RT1_ENM) {
			list_props = B_TRUE;
			list_enm = B_TRUE;
		} else if (cmd->cmd_res1_type == RT1_WLAN) {
			list_props = B_TRUE;
			list_wlan = B_TRUE;
		} else if (cmd->cmd_res1_type == RT1_NCP) {
			list_ncp = B_TRUE;
			list_props = B_TRUE;
		} else {
			list_loc = B_TRUE;
			list_enm = B_TRUE;
			list_wlan = B_TRUE;
			list_ncp = B_TRUE;
		}
	}
	if ((current_scope == NWAM_SCOPE_LOC ||
	    current_scope == NWAM_SCOPE_ENM ||
	    current_scope == NWAM_SCOPE_WLAN ||
	    current_scope == NWAM_SCOPE_NCU) &&
	    (cmd->cmd_argc >= 1 && cmd->cmd_res1_type != -1)) {
		nerr("Additional options are not allowed with the -a option "
		    "at this scope");
		return;
	}
	if (current_scope == NWAM_SCOPE_LOC) {
		list_loc = B_TRUE;
		list_props = B_TRUE;
	}
	if (current_scope == NWAM_SCOPE_ENM) {
		list_enm = B_TRUE;
		list_props = B_TRUE;
	}
	if (current_scope == NWAM_SCOPE_WLAN) {
		list_wlan = B_TRUE;
		list_props = B_TRUE;
	}
	if (current_scope == NWAM_SCOPE_NCP) {
		if (cmd->cmd_res1_type == RT1_ENM ||
		    cmd->cmd_res1_type == RT1_LOC ||
		    cmd->cmd_res1_type == RT1_WLAN) {
			nerr("only ncu can be listed at this scope");
			return;
		}
		if (cmd->cmd_res2_type == RT2_NCU) {
			list_ncu = B_TRUE;
			list_props = B_TRUE;
		} else {
			list_ncp = B_TRUE;
			list_props = B_TRUE;
		}
	}
	if (current_scope == NWAM_SCOPE_NCU) {
		list_ncu = B_TRUE;
		list_props = B_TRUE;
	}

	/* Check if the -a option is specified to list all properties */
	if (cmd->cmd_res1_type == -1 || cmd->cmd_argc == 2) {
		int c, argc = 1;
		char **argv;
		optind = 0;

		/* if res1_type is -1, option is in argv[0], else in argv[1] */
		if (cmd->cmd_res1_type == -1)
			argv = cmd->cmd_argv;
		else
			argv = &(cmd->cmd_argv[1]);
		while ((c = getopt(argc, argv, "a")) != EOF) {
			switch (c) {
			case 'a':
				all_props = B_TRUE;
				break;
			default:
				command_usage(CMD_LIST);
				return;
			}
		}
		if (cmd->cmd_res1_type == -1)
			cmd->cmd_argv[0] = NULL;
	}

	/*
	 * Now, print objects and/or according to the flags set.
	 * name, if requested, is in argv[0].
	 */
	if (list_ncp) {
		list_msg = B_TRUE;
		if (list_props) {
			ret = listprop(NWAM_OBJECT_TYPE_NCP, ncp_h,
			    cmd->cmd_argv[0], all_props, NULL, -1);
		} else {
			ret = nwam_walk_ncps(list_ncp_callback, &list_msg, 0,
			    NULL);
		}
		if (ret != NWAM_SUCCESS)
			goto done;
	}

	if (list_ncu) {
		list_msg = B_TRUE;
		if (ncp_h == NULL) {
			nerr("NCP has not been read");
			return;
		}
		if (list_props) {
			nwam_ncu_class_t	ncu_class;
			nwam_ncu_type_t		ncu_type;

			/* determine the NCU type first */
			if (ncu_h == NULL) {
				ncu_class = (nwam_ncu_class_t)
				    cmd->cmd_ncu_class_type;
				ncu_type = nwam_ncu_class_to_type(ncu_class);
			} else {
				if ((ret = nwam_ncu_get_ncu_type(ncu_h,
				    &ncu_type)) != NWAM_SUCCESS)
					goto done;
			}
			ret = listprop(NWAM_OBJECT_TYPE_NCU, ncu_h,
			    cmd->cmd_argv[0], all_props, ncp_h, ncu_type);
			if (ret != NWAM_SUCCESS)
				goto done;
		}
	}

	if (list_loc) {
		list_msg = B_TRUE;
		if (list_props) {
			ret = listprop(NWAM_OBJECT_TYPE_LOC, loc_h,
			    cmd->cmd_argv[0], all_props, NULL, -1);
		} else {
			ret = nwam_walk_locs(list_loc_callback, &list_msg,
			    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
		}
		if (ret != NWAM_SUCCESS)
			goto done;
	}

	if (list_enm) {
		list_msg = B_TRUE;
		if (list_props) {
			ret = listprop(NWAM_OBJECT_TYPE_ENM, enm_h,
			    cmd->cmd_argv[0], all_props, NULL, -1);
		} else {
			ret = nwam_walk_enms(list_enm_callback, &list_msg,
			    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
		}
		if (ret != NWAM_SUCCESS)
			goto done;
	}

	if (list_wlan) {
		list_msg = B_TRUE;
		if (list_props) {
			ret = listprop(NWAM_OBJECT_TYPE_KNOWN_WLAN, wlan_h,
			    cmd->cmd_argv[0], all_props, NULL, -1);
		} else {
			ret = nwam_walk_known_wlans(list_wlan_callback,
			    &list_msg, NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER,
			    NULL);
		}
		if (ret != NWAM_SUCCESS)
			goto done;
	}

done:
	if (ret != NWAM_SUCCESS)
		nwamerr(ret, "List error");
}

static int
write_export_command(nwam_object_type_t object_type, const char *prop,
    nwam_value_t values, FILE *of)
{
	/* exclude read-only properties */
	if (is_prop_read_only(object_type, prop))
		return (0);

	(void) fprintf(of, "set ");
	output_propname(prop, values, of);
	return (0);
}

static int
export_ncu_callback(nwam_ncu_handle_t ncu, void *arg)
{
	char		*name;
	const char	**props;
	nwam_ncu_type_t type;
	nwam_ncu_class_t class;
	nwam_value_t	vals;
	nwam_error_t	ret;
	uint_t		num;
	int		i;
	FILE		*of = arg;

	assert(of != NULL);

	/* get the NCU's type and class */
	if ((ret = nwam_ncu_get_ncu_type(ncu, &type)) != NWAM_SUCCESS)
		return (ret);
	if ((ret = nwam_ncu_get_ncu_class(ncu, &class)) != NWAM_SUCCESS)
		return (ret);

	if ((ret = nwam_ncu_get_name(ncu, &name)) != NWAM_SUCCESS)
		return (ret);

	(void) fprintf(of, "create ncu %s \"%s\"\n",
	    propval_to_str(NWAM_NCU_PROP_CLASS, class), name);
	free(name);
	/*
	 * Because of dependencies between properties, they have to be
	 * exported in the same order as when they are walked.
	 */
	if ((ret = nwam_ncu_get_default_proplist(type, class, &props, &num))
	    != NWAM_SUCCESS)
		return (ret);
	for (i = 0; i < num; i++) {
		ret = nwam_ncu_get_prop_value(ncu, props[i], &vals);
		if (ret == NWAM_SUCCESS) {
			write_export_command(NWAM_OBJECT_TYPE_NCU, props[i],
			    vals, of);
			nwam_value_free(vals);
		}
	}
	(void) fprintf(of, "end\n");

	free(props);
	return (0);
}

static int
export_ncp_callback(nwam_ncp_handle_t ncp, void *arg)
{
	char		*name;
	nwam_error_t	ret;
	FILE		*of = arg;

	assert(of != NULL);

	if ((ret = nwam_ncp_get_name(ncp, &name)) != NWAM_SUCCESS)
		return (ret);

	/* Do not export "automatic" NCP */
	if (NWAM_NCP_AUTOMATIC(name)) {
		free(name);
		return (0);
	}

	(void) fprintf(of, "create ncp \"%s\"\n", name);
	free(name);

	/* now walk NCUs for this ncp */
	ret = nwam_ncp_walk_ncus(ncp, export_ncu_callback, of,
	    NWAM_FLAG_NCU_TYPE_CLASS_ALL, NULL);
	if (ret != NWAM_SUCCESS) {
		nwamerr(ret, "Export ncp error: failed to walk ncus");
		return (ret);
	}
	(void) fprintf(of, "end\n");
	return (0);
}

static int
export_enm_callback(nwam_enm_handle_t enm, void *arg)
{
	char		*name;
	const char	**props;
	nwam_value_t	vals;
	nwam_error_t	ret;
	uint_t		num;
	int		i;
	FILE		*of = arg;

	assert(of != NULL);

	if ((ret = nwam_enm_get_name(enm, &name)) != NWAM_SUCCESS)
		return (ret);

	(void) fprintf(of, "create enm \"%s\"\n", name);
	free(name);
	/*
	 * Because of dependencies between properties, they have to be
	 * exported in the same order as when they are walked.
	 */
	if ((ret = nwam_enm_get_default_proplist(&props, &num)) != NWAM_SUCCESS)
		return (ret);
	for (i = 0; i < num; i++) {
		ret = nwam_enm_get_prop_value(enm, props[i], &vals);
		if (ret == NWAM_SUCCESS) {
			write_export_command(NWAM_OBJECT_TYPE_ENM, props[i],
			    vals, of);
			nwam_value_free(vals);
		}
	}
	(void) fprintf(of, "end\n");

	free(props);
	return (0);
}

static int
export_loc_callback(nwam_loc_handle_t loc, void *arg)
{
	char		*name;
	const char	**props;
	nwam_value_t	vals;
	nwam_error_t	ret;
	uint_t		num;
	int		i;
	FILE		*of = arg;

	assert(of != NULL);

	if ((ret = nwam_loc_get_name(loc, &name)) != NWAM_SUCCESS)
		return (ret);

	/* Do not export Automatic, NoNet or Legacy locations */
	if (NWAM_LOC_NAME_PRE_DEFINED(name)) {
		free(name);
		return (0);
	}

	(void) fprintf(of, "create loc \"%s\"\n", name);
	free(name);
	/*
	 * Because of dependencies between properties, they have to be
	 * exported in the same order as when they are walked.
	 */
	if ((ret = nwam_loc_get_default_proplist(&props, &num)) != NWAM_SUCCESS)
		return (ret);
	for (i = 0; i < num; i++) {
		ret = nwam_loc_get_prop_value(loc, props[i], &vals);
		if (ret == NWAM_SUCCESS) {
			write_export_command(NWAM_OBJECT_TYPE_LOC, props[i],
			    vals, of);
			nwam_value_free(vals);
		}
	}
	(void) fprintf(of, "end\n");

	free(props);
	return (0);
}

static int
export_wlan_callback(nwam_known_wlan_handle_t wlan, void *arg)
{
	char		*name;
	const char	**props;
	nwam_value_t	vals;
	nwam_error_t	ret;
	uint_t		num;
	int		i;
	FILE		*of = arg;

	assert(of != NULL);

	if ((ret = nwam_known_wlan_get_name(wlan, &name)) != NWAM_SUCCESS)
		return (ret);

	(void) fprintf(of, "create wlan \"%s\"\n", name);
	free(name);
	/*
	 * Because of dependencies between properties, they have to be
	 * exported in the same order as when they are walked.
	 */
	if ((ret = nwam_known_wlan_get_default_proplist(&props, &num))
	    != NWAM_SUCCESS)
		return (ret);
	for (i = 0; i < num; i++) {
		ret = nwam_known_wlan_get_prop_value(wlan, props[i], &vals);
		if (ret == NWAM_SUCCESS) {
			write_export_command(NWAM_OBJECT_TYPE_KNOWN_WLAN,
			    props[i], vals, of);
			nwam_value_free(vals);
		}
	}
	(void) fprintf(of, "end\n");

	free(props);
	return (0);
}

/*
 * Writes configuration to screen or file (with -f option).
 * Writes a "destroy -a" if option -d is given.
 */
void
export_func(cmd_t *cmd)
{
	int		c;
	boolean_t	need_to_close = B_FALSE, write_to_file = B_FALSE;
	boolean_t	add_destroy = B_FALSE, lhandle = B_FALSE;
	char		filepath[MAXPATHLEN];
	nwam_error_t	ret = NWAM_SUCCESS;
	FILE		*of = NULL; /* either filename or stdout */

	/* what to export */
	boolean_t export_ncp = B_FALSE, export_ncu = B_FALSE;
	boolean_t export_loc = B_FALSE, export_enm = B_FALSE;
	boolean_t export_wlan = B_FALSE;
	char *name = NULL;

	/* check for -d and -f flags */
	filepath[0] = '\0';
	optind = 0;
	while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "df:")) != EOF) {
		switch (c) {
		case 'f':
			write_to_file = B_TRUE;
			break;
		case 'd':
			add_destroy = B_TRUE;
			break;
		default:
			command_usage(CMD_EXPORT);
			return;
		}
	}

	/* determine where to export */
	if (!write_to_file) {
		of = stdout;
	} else {
		/*
		 * If -d was specified with -f, then argv[2] is filename,
		 * otherwise, argv[1] is filename.
		 */
		(void) strlcpy(filepath,
		    (add_destroy ? cmd->cmd_argv[2] : cmd->cmd_argv[1]),
		    sizeof (filepath));
		if ((of = fopen(filepath, "w")) == NULL) {
			nerr(gettext("opening file '%s': %s"), filepath,
			    strerror(errno));
			goto done;
		}
		setbuf(of, NULL);
		need_to_close = B_TRUE;
	}

	if (add_destroy) {
		/* only possible in global scope */
		if (current_scope == NWAM_SCOPE_GBL) {
			(void) fprintf(of, "destroy -a\n");
		} else {
			nerr("Option -d is not allowed in non-global scope");
			goto done;
		}
	}

	/* In the following scopes, only the -f argument is valid */
	if (((current_scope == NWAM_SCOPE_LOC ||
	    current_scope == NWAM_SCOPE_ENM ||
	    current_scope == NWAM_SCOPE_WLAN ||
	    current_scope == NWAM_SCOPE_NCU) &&
	    cmd->cmd_argc != 0 && !write_to_file)) {
		nerr("'export' does not take arguments at this scope");
		goto done;
	}
	if (current_scope == NWAM_SCOPE_NCP) {
		if (cmd->cmd_res1_type == RT1_ENM ||
		    cmd->cmd_res1_type == RT1_LOC ||
		    cmd->cmd_res1_type == RT1_WLAN) {
			nerr("only ncu can be exported at this scope");
			goto done;
		}
	}

	/*
	 * Determine what objects to export depending on scope and command
	 * arguments.  If -f is specified, then the object name is argv[2].
	 * Otherwise, argv[0] is name, unless exporting all in global
	 * scope in which case name is set back to NULL.
	 */
	switch (current_scope) {
	case NWAM_SCOPE_GBL:
		name = (write_to_file ? trim_quotes(cmd->cmd_argv[2]) :
		    trim_quotes(cmd->cmd_argv[0]));
		switch (cmd->cmd_res1_type) {
		case RT1_LOC:
			export_loc = B_TRUE;
			break;
		case RT1_ENM:
			export_enm = B_TRUE;
			break;
		case RT1_WLAN:
			export_wlan = B_TRUE;
			break;
		case RT1_NCP:
			export_ncp = B_TRUE;
			if (cmd->cmd_res2_type == RT2_NCU) {
				nerr("cannot export ncu at from global scope");
				goto done;
			}
			break;
		default:
			/* export everything */
			export_loc = B_TRUE;
			export_enm = B_TRUE;
			export_wlan = B_TRUE;
			export_ncp = B_TRUE; /* NCP will export the NCUs */
			free(name);
			name = NULL; /* exporting all, undo name */
			break;
		}
		break;
	case NWAM_SCOPE_LOC:
		export_loc = B_TRUE;
		ret = nwam_loc_get_name(loc_h, &name);
		if (ret != NWAM_SUCCESS)
			goto fail;
		break;
	case NWAM_SCOPE_ENM:
		export_enm = B_TRUE;
		ret = nwam_enm_get_name(enm_h, &name);
		if (ret != NWAM_SUCCESS)
			goto fail;
		break;
	case NWAM_SCOPE_WLAN:
		export_wlan = B_TRUE;
		ret = nwam_known_wlan_get_name(wlan_h, &name);
		if (ret != NWAM_SUCCESS)
			goto fail;
		break;
	case NWAM_SCOPE_NCP:
		if (cmd->cmd_res2_type == RT2_NCU) {
			export_ncu = B_TRUE;
			name = (write_to_file ? trim_quotes(cmd->cmd_argv[2]) :
			    trim_quotes(cmd->cmd_argv[0]));
		} else {
			export_ncp = B_TRUE;
			ret = nwam_ncp_get_name(ncp_h, &name);
			if (ret != NWAM_SUCCESS)
				goto fail;
		}
		break;
	case NWAM_SCOPE_NCU:
		export_ncu = B_TRUE;
		ret = nwam_ncu_get_name(ncu_h, &name);
		if (ret != NWAM_SUCCESS)
			goto fail;
		break;
	default:
		nerr("Invalid scope");
		goto done;
	}

	/* Now, export objects according to the flags set */
	if (export_ncp) {
		lhandle = B_FALSE;
		if (name == NULL) {
			/* export all NCPs */
			ret = nwam_walk_ncps(export_ncp_callback, of, 0, NULL);
		} else if (NWAM_NCP_AUTOMATIC(name)) {
			nerr("'%s' ncp cannot be exported", name);
			goto fail;
		} else {
			if (ncp_h == NULL) {
				ret = nwam_ncp_read(name, 0, &ncp_h);
				if (ret != NWAM_SUCCESS)
					goto fail;
				lhandle = B_TRUE;
			}
			/* will export NCUs also */
			ret = export_ncp_callback(ncp_h, of);
			if (lhandle) {
				nwam_ncp_free(ncp_h);
				ncp_h = NULL;
			}
		}
		if (ret != NWAM_SUCCESS)
			goto fail;
	}

	if (export_ncu) {
		if (name == NULL) {
			/* export all NCUs */
			ret = nwam_ncp_walk_ncus(ncp_h, export_ncu_callback, of,
			    NWAM_FLAG_NCU_TYPE_CLASS_ALL, NULL);
		} else {
			if (ncu_h == NULL) {
				/* no NCU handle -> called from NCP scope */
				nwam_ncu_type_t		ncu_type;
				nwam_ncu_class_t	ncu_class;

				ncu_class = (nwam_ncu_class_t)
				    cmd->cmd_ncu_class_type;
				ncu_type = nwam_ncu_class_to_type(ncu_class);
				ret = nwam_ncu_read(ncp_h, name,
				    ncu_type, 0, &ncu_h);
				if (ret == NWAM_SUCCESS) {
					/* one NCU with given name */
					ret = export_ncu_callback(ncu_h, of);
					nwam_ncu_free(ncu_h);
					ncu_h = NULL;
				} else if (ret == NWAM_ENTITY_MULTIPLE_VALUES) {
					/* multiple NCUs with given name */
					ret = nwam_ncu_read(ncp_h, name,
					    NWAM_NCU_TYPE_LINK, 0, &ncu_h);
					if (ret != NWAM_SUCCESS)
						goto fail;
					ret = export_ncu_callback(ncu_h, of);
					nwam_ncu_free(ncu_h);
					ncu_h = NULL;

					ret = nwam_ncu_read(ncp_h, name,
					    NWAM_NCU_TYPE_INTERFACE, 0, &ncu_h);
					if (ret != NWAM_SUCCESS)
						goto fail;
					ret = export_ncu_callback(ncu_h, of);
					nwam_ncu_free(ncu_h);
					ncu_h = NULL;
				} else {
					goto fail;
				}
			} else {
				/* NCU handle exists */
				ret = export_ncu_callback(ncu_h, of);
			}
		}
		if (ret != NWAM_SUCCESS)
			goto fail;
	}

	if (export_loc) {
		lhandle = B_FALSE;
		if (name == NULL) {
			/* export all locations */
			ret = nwam_walk_locs(export_loc_callback, of,
			    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
		} else if (NWAM_LOC_NAME_PRE_DEFINED(name)) {
			nerr("'%s' loc cannot be exported", name);
			goto fail;
		} else {
			if (loc_h == NULL) {
				ret = nwam_loc_read(name, 0, &loc_h);
				if (ret != NWAM_SUCCESS)
					goto fail;
				lhandle = B_TRUE;
			}
			ret = export_loc_callback(loc_h, of);
			if (lhandle) {
				nwam_loc_free(loc_h);
				loc_h = NULL;
			}
		}
		if (ret != NWAM_SUCCESS)
			goto fail;
	}

	if (export_enm) {
		lhandle = B_FALSE;
		if (name == NULL) {
			/* export all ENMs */
			ret = nwam_walk_enms(export_enm_callback, of,
			    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
		} else {
			if (enm_h == NULL) {
				ret = nwam_enm_read(name, 0, &enm_h);
				if (ret != NWAM_SUCCESS)
					goto fail;
				lhandle = B_TRUE;
			}
			ret = export_enm_callback(enm_h, of);
			if (lhandle) {
				nwam_enm_free(enm_h);
				enm_h = NULL;
			}
		}
		if (ret != NWAM_SUCCESS)
			goto fail;
	}

	if (export_wlan) {
		lhandle = B_FALSE;
		if (name == NULL) {
			/* export all WLANs */
			ret = nwam_walk_known_wlans(export_wlan_callback, of,
			    NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, NULL);
		} else {
			if (wlan_h == NULL) {
				ret = nwam_known_wlan_read(name, 0,
				    &wlan_h);
				if (ret != NWAM_SUCCESS)
					goto fail;
				lhandle = B_TRUE;
			}
			ret = export_wlan_callback(wlan_h, of);
			if (lhandle) {
				nwam_known_wlan_free(wlan_h);
				wlan_h = NULL;
			}
		}
		if (ret != NWAM_SUCCESS)
			goto fail;
	}

fail:
	free(name);
	if (ret != NWAM_SUCCESS)
		nwamerr(ret, "Export error");

done:
	if (need_to_close)
		(void) fclose(of);
}

/*
 * Get property value.  If the -V option is specified, only the value is
 * printed without the property name.
 */
void
get_func(cmd_t *cmd)
{
	nwam_error_t		ret = NWAM_SUCCESS;
	nwam_value_t		prop_value;
	const char		*prop;
	boolean_t		value_only = B_FALSE;
	nwam_object_type_t	object_type = active_object_type();

	/* check if option is -V to print value only */
	if (cmd->cmd_argc == 1) {
		int c;

		optind = 0;
		while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "V")) != EOF) {
			switch (c) {
			case 'V':
				value_only = B_TRUE;
				break;
			default:
				command_usage(CMD_GET);
				return;
			}
		}
	}

	/* property to get is in cmd->cmd_prop_type */
	if ((prop = pt_to_prop_name(object_type, cmd->cmd_prop_type)) == NULL) {
		nerr("Get error: invalid %s property: '%s'",
		    scope_to_str(current_scope), pt_to_str(cmd->cmd_prop_type));
		return;
	}

	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCU:
		ret = nwam_ncu_get_prop_value(ncu_h, prop, &prop_value);
		break;
	case NWAM_OBJECT_TYPE_LOC:
		ret = nwam_loc_get_prop_value(loc_h, prop, &prop_value);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		ret = nwam_enm_get_prop_value(enm_h, prop, &prop_value);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		ret = nwam_known_wlan_get_prop_value(wlan_h, prop, &prop_value);
		break;
	}

	if (ret != NWAM_SUCCESS) {
		if (ret == NWAM_ENTITY_NOT_FOUND)
			nerr("Get error: property '%s' has not been set", prop);
		else
			nwamerr(ret, "Get error");
		return;
	}

	if (value_only) {
		output_prop_val(prop, prop_value, stdout, B_FALSE);
		(void) printf("\n");
	} else {
		output_propname(prop, prop_value, NULL);
	}
	nwam_value_free(prop_value);
}

/*
 * Clears value of a property.
 * Read-only properties cannot be cleared.
 * If clearing a property invalidates the object, then that property
 * cannot be cleared.
 */
void
clear_func(cmd_t *cmd)
{
	nwam_error_t		ret;
	const char		*prop;
	nwam_object_type_t	object_type = active_object_type();

	/* property to clear is in cmd->cmd_prop_type */
	if ((prop = pt_to_prop_name(object_type, cmd->cmd_prop_type)) == NULL) {
		nerr("Clear error: invalid %s property: '%s'",
		    scope_to_str(current_scope), pt_to_str(cmd->cmd_prop_type));
		return;
	}
	if (is_prop_read_only(object_type, prop)) {
		nerr("Clear error: property '%s' is read-only", prop);
		return;
	}

	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCU:
		ret = nwam_ncu_delete_prop(ncu_h, prop);
		break;
	case NWAM_OBJECT_TYPE_LOC:
		ret = nwam_loc_delete_prop(loc_h, prop);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		ret = nwam_enm_delete_prop(enm_h, prop);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		ret = nwam_known_wlan_delete_prop(wlan_h, prop);
		break;
	}

	if (ret != NWAM_SUCCESS) {
		if (ret == NWAM_INVALID_ARG || ret == NWAM_ENTITY_NOT_FOUND) {
			nerr("Clear error: property '%s' has not been set",
			    prop);
		} else {
			nwamerr(ret, "Clear error");
		}
		return;
	}

	need_to_commit = B_TRUE;
}

/*
 * Prints all the choices available for an enum property [c1|c2|c3].
 * Prints [true|false] for a boolean property.
 */
static void
print_all_prop_choices(nwam_object_type_t object_type, const char *prop)
{
	uint64_t		i = 0;
	const char		*str;
	boolean_t		choices = B_FALSE;
	nwam_value_type_t	value_type;
	nwam_error_t		ret;

	/* Special case: print object-specific options for activation-mode */
	if (strcmp(prop, NWAM_NCU_PROP_ACTIVATION_MODE) == 0) {
		/* "manual" for all objects */
		(void) printf(" [%s|",
		    propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
		    NWAM_ACTIVATION_MODE_MANUAL));
		if (object_type == NWAM_OBJECT_TYPE_NCU) {
			(void) printf("%s]",
			    propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
			    NWAM_ACTIVATION_MODE_PRIORITIZED));
		} else {
			(void) printf("%s|%s]",
			    propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
			    NWAM_ACTIVATION_MODE_CONDITIONAL_ANY),
			    propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
			    NWAM_ACTIVATION_MODE_CONDITIONAL_ALL));
		}
		return;
	}

	/* Special case: only "manual" configsrc is allowed for LDAP */
	if (strcmp(prop, NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC) == 0) {
		(void) printf(" [%s]",
		    propval_to_str(NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
		    NWAM_CONFIGSRC_MANUAL));
		return;
	}

	value_type = prop_value_type(object_type, prop);
	switch (value_type) {
	case NWAM_VALUE_TYPE_UINT64:
		/* uint64 may be an enum, will print nothing if not an enum */
		while ((ret = nwam_uint64_get_value_string(prop, i++, &str))
		    == NWAM_SUCCESS || ret == NWAM_ENTITY_INVALID_VALUE) {
			/* No string representation for i, continue. */
			if (ret == NWAM_ENTITY_INVALID_VALUE)
				continue;

			if (!choices)
				(void) printf("%s", " [");
			(void) printf("%s%s", choices ? "|" : "", str);
			choices = B_TRUE;
		}
		if (choices)
			(void) putchar(']');
		break;
	case NWAM_VALUE_TYPE_BOOLEAN:
		(void) printf(" [%s|%s]", "true", "false");
		break;
	case NWAM_VALUE_TYPE_STRING:
		break;
	}
}

/*
 * Walk through object properties.
 * For newly-created object, the property name with no value is displayed, and
 * the user can input a value for each property.
 * For existing object, the current value is displayed and user input overwrites
 * the existing one. If no input is given, the existing value remains.
 * Read-only properties are not displayed.
 * Read-only objects cannot be walked.
 * If the -a option is specified, no properties are skipped.
 */
void
walkprop_func(cmd_t *cmd)
{
	nwam_error_t	ret = NWAM_SUCCESS;
	nwam_value_t	vals = NULL; /* freed in _wait_input() */
	int		i;
	uint_t		prop_num;
	const char	**props;
	boolean_t	read_only = B_FALSE, all_props = B_FALSE;

	nwam_object_type_t object_type;
	prop_display_entry_t *prop_table;

	if (!interactive_mode) {
		nerr("'walkprop' is only allowed in interactive mode");
		return;
	}

	/* check if option -a is specified to show all properties */
	if (cmd->cmd_argc == 1) {
		int c;
		optind = 0;
		while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "a")) != EOF) {
			switch (c) {
			case 'a':
				all_props = B_TRUE;
				break;
			default:
				command_usage(CMD_WALKPROP);
				return;
			}
		}
	}

	/* read-only objects cannot be walked */
	if (obj1_type == RT1_NCP) {
		/* must be in NCU scope, NCP scope doesn't get here */
		(void) nwam_ncu_get_read_only(ncu_h, &read_only);
	}
	if (read_only) {
		nerr("'walkprop' cannot be used in read-only objects");
		return;
	}

	/* get the current object type and the prop_display_table */
	object_type = active_object_type();
	prop_table = get_prop_display_table(object_type);

	/* get the property list depending on the object type */
	switch (object_type) {
	case NWAM_OBJECT_TYPE_NCU:
	{
		nwam_ncu_type_t		ncu_type;
		nwam_ncu_class_t	ncu_class;

		if ((ret = nwam_ncu_get_ncu_type(ncu_h, &ncu_type))
		    != NWAM_SUCCESS)
			break;
		if ((ret = nwam_ncu_get_ncu_class(ncu_h, &ncu_class))
		    != NWAM_SUCCESS)
			break;

		ret = nwam_ncu_get_default_proplist(ncu_type, ncu_class, &props,
		    &prop_num);
		break;
	}
	case NWAM_OBJECT_TYPE_LOC:
		ret = nwam_loc_get_default_proplist(&props, &prop_num);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		ret = nwam_enm_get_default_proplist(&props, &prop_num);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		ret = nwam_known_wlan_get_default_proplist(&props, &prop_num);
		break;
	}
	if (ret != NWAM_SUCCESS) {
		nwamerr(ret, "Walkprop error: could not get property list");
		return;
	}

	/* Loop through the properties */
	if (all_props)
		(void) printf(gettext("Walking all properties ...\n"));
	for (i = 0; i < prop_num; i++) {
		char line[NWAM_MAX_VALUE_LEN];
		char **checked = NULL;

		/* check if this property should be displayed */
		if (is_prop_read_only(object_type, props[i]))
			continue;
		if (!all_props &&
		    !show_prop_test(object_type, props[i], prop_table,
		    checked, 0))
			continue;

		/* get the existing value for this property */
		switch (object_type) {
		case NWAM_OBJECT_TYPE_NCU:
			ret = nwam_ncu_get_prop_value(ncu_h, props[i], &vals);
			break;
		case NWAM_OBJECT_TYPE_LOC:
			ret = nwam_loc_get_prop_value(loc_h, props[i], &vals);
			break;
		case NWAM_OBJECT_TYPE_ENM:
			ret = nwam_enm_get_prop_value(enm_h, props[i], &vals);
			break;
		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
			ret = nwam_known_wlan_get_prop_value(wlan_h, props[i],
			    &vals);
			break;
		}
		/* returns NWAM_ENTITY_NOT_FOUND if no existing value */
		if (ret != NWAM_SUCCESS && ret != NWAM_ENTITY_NOT_FOUND)
			continue;

		/* print property */
		(void) printf("%s", props[i]);
		/* print the existing value(s) if they exist */
		if (ret == NWAM_SUCCESS) {
			(void) printf(" (");
			output_prop_val(props[i], vals, stdout, B_TRUE);
			(void) putchar(')');
			nwam_value_free(vals);
		}
		/* print choices, won't print anything if there aren't any */
		print_all_prop_choices(object_type, props[i]);
		(void) printf("> ");

		/* wait for user input */
		if (fgets(line, sizeof (line), stdin) == NULL)
			continue;

		/* if user input new value, existing value is overrode */
		if (line[0] != '\n') {
			boolean_t is_listprop;
			int pt_type = prop_to_pt(object_type, props[i]);

			is_listprop = is_prop_multivalued(object_type,
			    props[i]);
			vals = str_to_nwam_value(object_type, line, pt_type,
			    is_listprop);
			if (vals == NULL) {
				ret = NWAM_ENTITY_INVALID_VALUE;
				goto repeat;
			}

			/* set the new value for the property */
			switch (object_type) {
			case NWAM_OBJECT_TYPE_NCU:
				ret = nwam_ncu_set_prop_value(ncu_h, props[i],
				    vals);
				break;
			case NWAM_OBJECT_TYPE_LOC:
				ret = nwam_loc_set_prop_value(loc_h, props[i],
				    vals);
				break;
			case NWAM_OBJECT_TYPE_ENM:
				ret = nwam_enm_set_prop_value(enm_h, props[i],
				    vals);
				break;
			case NWAM_OBJECT_TYPE_KNOWN_WLAN:
				ret = nwam_known_wlan_set_prop_value(wlan_h,
				    props[i], vals);
				break;
			}
			nwam_value_free(vals);

			if (ret != NWAM_SUCCESS)
				goto repeat;

			need_to_commit = B_TRUE;
			continue;

repeat:
			invalid_set_prop_msg(props[i], ret);
			i--; /* decrement i to repeat */
		}
	}

	free(props);
}

/*
 * Verify whether all properties of a resource are valid.
 */
/* ARGSUSED */
void
verify_func(cmd_t *cmd)
{
	nwam_error_t	ret;
	const char	*errprop;

	switch (active_object_type()) {
	case NWAM_OBJECT_TYPE_NCU:
		ret = nwam_ncu_validate(ncu_h, &errprop);
		break;
	case NWAM_OBJECT_TYPE_LOC:
		ret = nwam_loc_validate(loc_h, &errprop);
		break;
	case NWAM_OBJECT_TYPE_ENM:
		ret = nwam_enm_validate(enm_h, &errprop);
		break;
	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
		ret = nwam_known_wlan_validate(wlan_h, &errprop);
		break;
	}
	if (ret != NWAM_SUCCESS)
		nwamerr(ret, "Verify error on property '%s'", errprop);
	else if (interactive_mode)
		(void) printf(gettext("All properties verified\n"));
}

/*
 * command-line mode (# nwamcfg list or # nwamcfg "select loc test; list")
 */
static int
one_command_at_a_time(int argc, char *argv[])
{
	char *command;
	size_t len = 2; /* terminal \n\0 */
	int i, err;

	for (i = 0; i < argc; i++)
		len += strlen(argv[i]) + 1;
	if ((command = malloc(len)) == NULL) {
		nerr("Out of memory");
		return (NWAM_ERR);
	}
	(void) strlcpy(command, argv[0], len);
	for (i = 1; i < argc; i++) {
		(void) strlcat(command, " ", len);
		(void) strlcat(command, argv[i], len);
	}
	(void) strlcat(command, "\n", len);
	err = string_to_yyin(command);
	free(command);
	if (err != NWAM_OK)
		return (err);
	while (!feof(yyin)) {
		yyparse();

		/*
		 * If any command on a list of commands give an error,
		 * don't continue with the remaining commands.
		 */
		if (saw_error || time_to_exit)
			return (cleanup());
	}

	/* if there are changes to commit, commit it */
	if (need_to_commit) {
		do_commit();
		/* if need_to_commit is not set, then there was a error */
		if (need_to_commit)
			return (NWAM_ERR);
	}

	if (!interactive_mode)
		return (cleanup());
	else {
		yyin = stdin;
		return (read_input());
	}
}

/*
 * cmd_file is slightly more complicated, as it has to open the command file
 * and set yyin appropriately.  Once that is done, though, it just calls
 * read_input(), and only once, since prompting is not possible.
 */
static int
cmd_file(char *file)
{
	FILE *infile;
	int err;
	struct stat statbuf;
	boolean_t using_real_file = (strcmp(file, "-") != 0);

	if (using_real_file) {
		/*
		 * nerr() prints a line number in cmd_file_mode, which we do
		 * not want here, so temporarily unset it.
		 */
		cmd_file_mode = B_FALSE;
		if ((infile = fopen(file, "r")) == NULL) {
			nerr(gettext("could not open file '%s': %s"),
			    file, strerror(errno));
			return (1);
		}
		if ((err = fstat(fileno(infile), &statbuf)) != 0) {
			nerr(gettext("could not stat file '%s': %s"),
			    file, strerror(errno));
			err = 1;
			goto done;
		}
		if (!S_ISREG(statbuf.st_mode)) {
			nerr(gettext("'%s' is not a regular file."), file);
			err = 1;
			goto done;
		}

		/*
		 * If -d was passed on the command-line, we need to
		 * start by removing any existing configuration.
		 * Alternatively, the file may begin with 'destroy -a';
		 * but in that case, the line will go through the lexer
		 * and be processed as it's encountered in the file.
		 */
		if (remove_all_configurations && destroy_all() != NWAM_SUCCESS)
			goto done;

		/* set up for lexer */
		yyin = infile;
		cmd_file_mode = B_TRUE;
		ok_to_prompt = B_FALSE;
	} else {
		/*
		 * "-f -" is essentially the same as interactive mode,
		 * so treat it that way.
		 */
		interactive_mode = B_TRUE;
	}
	/* NWAM_REPEAT is for interactive mode; treat it like NWAM_ERR here. */
	if ((err = read_input()) == NWAM_REPEAT)
		err = NWAM_ERR;
	if (err == NWAM_OK)
		(void) printf(gettext("Configuration read.\n"));

done:
	if (using_real_file)
		(void) fclose(infile);
	return (err);
}

int
main(int argc, char *argv[])
{
	int	err;
	char	c;

	/* This must be before anything goes to stdout. */
	setbuf(stdout, NULL);

	if ((execname = strrchr(argv[0], '/')) == NULL)
		execname = argv[0];
	else
		execname++;

	(void) setlocale(LC_ALL, "");
	(void) textdomain(TEXT_DOMAIN);

	while ((c = getopt(argc, argv, "?hf:d")) != EOF) {
		switch (c) {
		case 'f':
			cmd_file_name = optarg;
			cmd_file_mode = B_TRUE;
			break;
		case '?':
		case 'h':
			cmd_line_usage();
			return (NWAM_OK);
		case 'd':
			remove_all_configurations = B_TRUE;
			break;
		default:
			cmd_line_usage();
			return (NWAM_ERR);
		}
	}
	/* -d can only be used with -f */
	if (remove_all_configurations && !cmd_file_mode) {
		nerr("Option -d can only be used with -f");
		return (NWAM_ERR);
	}

	/*
	 * This may get set back to FALSE again in cmd_file() if cmd_file_name
	 * is a "real" file as opposed to "-" (i.e. meaning use stdin).
	 */
	if (isatty(STDIN_FILENO))
		ok_to_prompt = B_TRUE;
	if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL)
		exit(NWAM_ERR);
	if (gl_customize_completion(gl, NULL, cmd_cpl_fn) != 0)
		exit(NWAM_ERR);
	(void) sigset(SIGINT, SIG_IGN);

	if (optind == argc) {
		/* interactive or command-file mode */
		if (!cmd_file_mode)
			err = do_interactive();
		else
			err = cmd_file(cmd_file_name);
	} else {
		/* command-line mode */
		err = one_command_at_a_time(argc - optind, &(argv[optind]));
	}
	(void) del_GetLine(gl);

	return (err);
}