1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * nwamcfg is a lex/yacc based command interpreter used to manage network
28 * configurations.  The lexer (see nwamcfg_lex.l) builds up tokens, which
29 * the grammar (see nwamcfg_grammar.y) builds up into commands, some of
30 * which takes resources and/or properties as arguments.
31 */
32
33#include <arpa/inet.h>
34#include <assert.h>
35#include <ctype.h>
36#include <errno.h>
37#include <libnwam.h>
38#include <libtecla.h>
39#include <locale.h>
40#include <stdarg.h>
41#include <stdio.h>
42#include <string.h>
43#include <sys/stat.h>
44#include <sys/sysmacros.h>
45#include <sys/types.h>
46#include <unistd.h>
47
48#include "nwamcfg.h"
49
50#if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
51#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
52#endif
53
54struct help {
55	uint_t		cmd_num;
56	const char	*cmd_name;
57	const char	*cmd_usage;
58};
59
60extern int yyparse(void);
61extern int lex_lineno;
62
63#define	MAX_LINE_LEN	1024
64#define	MAX_CMD_HIST	1024
65
66/* usage of commands */
67#define	SHELP_CANCEL	"cancel"
68#define	SHELP_CLEAR	"clear <prop-name>"
69#define	SHELP_COMMIT	"commit"
70#define	SHELP_CREATE	"create [-t <template>] <object-type> [<class>] " \
71			"<object-name>"
72#define	SHELP_DESTROY	"destroy {-a | <object-type> [<class>] <object-name>}"
73#define	SHELP_END	"end"
74#define	SHELP_EXIT	"exit"
75#define	SHELP_EXPORT	"export [-d] [-f <output-file>] " \
76			"[<object-type> [<class>] <object-name>]"
77#define	SHELP_GET	"get [-V] <prop-name>"
78#define	SHELP_HELP	"help [command-name]"
79#define	SHELP_LIST	"list [-a] [<object-type> [<class>] <object-name>]"
80#define	SHELP_REVERT	"revert"
81#define	SHELP_SELECT	"select <object-type> [<class>] <object-name>"
82#define	SHELP_SET	"set <prop-name>=<value1>[,<value2>...]"
83#define	SHELP_VERIFY	"verify"
84#define	SHELP_WALK	"walkprop [-a]"
85
86/*
87 * Scope Definitions:
88 * Locations, ENMs, NCPs and Known WLANs are one scope level below global (GBL).
89 * NCUs are one more level beneath the NCP scope.
90 * Because the commands in Locations/ENM/Known WLAN and NCP level are different,
91 * the scope are divided accordingly.
92 *     GBL->LOC, GBL->ENM, GBL->WLAN or GBL->NCP->NCU
93 */
94#define	NWAM_SCOPE_GBL	0
95#define	NWAM_SCOPE_LOC	1
96#define	NWAM_SCOPE_ENM	2
97#define	NWAM_SCOPE_WLAN	3
98#define	NWAM_SCOPE_NCP	4
99#define	NWAM_SCOPE_NCU	5
100
101/* delimiter used for list of values */
102#define	NWAM_VALUE_DELIMITER_CHAR	','
103#define	NWAM_VALUE_DELIMITER_STR	","
104
105/* the max number of values for an enum used by some properties in libnwam */
106
107/*
108 * All arrays/tables are null-terminated, rather than defining the length of
109 * the array.  When looping, check for NULL rather than using the size.
110 */
111
112static struct help helptab[] = {
113	{ CMD_CANCEL,	"cancel",	SHELP_CANCEL	},
114	{ CMD_CLEAR,	"clear",	SHELP_CLEAR	},
115	{ CMD_COMMIT,	"commit",	SHELP_COMMIT	},
116	{ CMD_CREATE,	"create",	SHELP_CREATE	},
117	{ CMD_DESTROY,	"destroy",	SHELP_DESTROY	},
118	{ CMD_END,	"end",		SHELP_END	},
119	{ CMD_EXIT,	"exit",		SHELP_EXIT	},
120	{ CMD_EXPORT,	"export",	SHELP_EXPORT	},
121	{ CMD_GET,	"get",		SHELP_GET	},
122	{ CMD_HELP,	"help",		SHELP_HELP	},
123	{ CMD_LIST,	"list",		SHELP_LIST	},
124	{ CMD_REVERT,	"revert",	SHELP_REVERT	},
125	{ CMD_SELECT,	"select",	SHELP_SELECT	},
126	{ CMD_SET,	"set",		SHELP_SET	},
127	{ CMD_VERIFY,	"verify",	SHELP_VERIFY	},
128	{ CMD_WALKPROP,	"walkprop",	SHELP_WALK	},
129	{ 0, NULL, NULL }
130};
131
132/* These *must* match the order of the RT1_ define's from nwamcfg.h */
133static char *res1_types[] = {
134	"unknown",
135	"loc",
136	"ncp",
137	"enm",
138	"wlan",
139	NULL
140};
141
142/* These *must* match the order of the RT2_ define's from nwamcfg.h */
143static char *res2_types[] = {
144	"unknown",
145	"ncu",
146	NULL
147};
148
149/*
150 * No array for NCU_CLASS_.  The #define's in nwamcfg.h matches the
151 * enum nwam_ncu_class_t in libnwam and thus uses libnwam functions to
152 * retrieve the string representation.
153 */
154
155/* These *MUST* match the order of the PT_ define's from nwamcfg.h */
156static char *pt_types[] = {
157	"unknown",
158	NWAM_NCU_PROP_ACTIVATION_MODE,
159	NWAM_NCU_PROP_ENABLED,
160	NWAM_NCU_PROP_TYPE,
161	NWAM_NCU_PROP_CLASS,
162	NWAM_NCU_PROP_PARENT_NCP,
163	NWAM_NCU_PROP_PRIORITY_GROUP,
164	NWAM_NCU_PROP_PRIORITY_MODE,
165	NWAM_NCU_PROP_LINK_MAC_ADDR,
166	NWAM_NCU_PROP_LINK_AUTOPUSH,
167	NWAM_NCU_PROP_LINK_MTU,
168	NWAM_NCU_PROP_IP_VERSION,
169	NWAM_NCU_PROP_IPV4_ADDRSRC,
170	NWAM_NCU_PROP_IPV4_ADDR,
171	NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE,
172	NWAM_NCU_PROP_IPV6_ADDRSRC,
173	NWAM_NCU_PROP_IPV6_ADDR,
174	NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE,
175	NWAM_LOC_PROP_CONDITIONS,
176	NWAM_ENM_PROP_FMRI,
177	NWAM_ENM_PROP_START,
178	NWAM_ENM_PROP_STOP,
179	NWAM_LOC_PROP_NAMESERVICES,
180	NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE,
181	NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
182	NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN,
183	NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS,
184	NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH,
185	NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
186	NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS,
187	NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
188	NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS,
189	NWAM_LOC_PROP_DEFAULT_DOMAIN,
190	NWAM_LOC_PROP_NFSV4_DOMAIN,
191	NWAM_LOC_PROP_IPFILTER_CONFIG_FILE,
192	NWAM_LOC_PROP_IPFILTER_V6_CONFIG_FILE,
193	NWAM_LOC_PROP_IPNAT_CONFIG_FILE,
194	NWAM_LOC_PROP_IPPOOL_CONFIG_FILE,
195	NWAM_LOC_PROP_IKE_CONFIG_FILE,
196	NWAM_LOC_PROP_IPSECPOLICY_CONFIG_FILE,
197	NWAM_KNOWN_WLAN_PROP_BSSIDS,
198	NWAM_KNOWN_WLAN_PROP_PRIORITY,
199	NWAM_KNOWN_WLAN_PROP_KEYNAME,
200	NWAM_KNOWN_WLAN_PROP_KEYSLOT,
201	NWAM_KNOWN_WLAN_PROP_SECURITY_MODE
202};
203
204/* properties table: maps PT_* constants to property names */
205typedef struct prop_table_entry {
206	int			pte_type;
207	const char		*pte_name;
208} prop_table_entry_t;
209
210/* NCU properties table */
211static prop_table_entry_t ncu_prop_table[] = {
212	{ PT_TYPE, 			NWAM_NCU_PROP_TYPE },
213	{ PT_CLASS, 			NWAM_NCU_PROP_CLASS },
214	{ PT_PARENT, 			NWAM_NCU_PROP_PARENT_NCP },
215	{ PT_ACTIVATION_MODE,		NWAM_NCU_PROP_ACTIVATION_MODE },
216	{ PT_ENABLED, 			NWAM_NCU_PROP_ENABLED },
217	{ PT_PRIORITY_GROUP, 		NWAM_NCU_PROP_PRIORITY_GROUP },
218	{ PT_PRIORITY_MODE,		NWAM_NCU_PROP_PRIORITY_MODE },
219	{ PT_LINK_MACADDR, 		NWAM_NCU_PROP_LINK_MAC_ADDR },
220	{ PT_LINK_AUTOPUSH, 		NWAM_NCU_PROP_LINK_AUTOPUSH },
221	{ PT_LINK_MTU, 			NWAM_NCU_PROP_LINK_MTU },
222	{ PT_IP_VERSION, 		NWAM_NCU_PROP_IP_VERSION },
223	{ PT_IPV4_ADDRSRC, 		NWAM_NCU_PROP_IPV4_ADDRSRC },
224	{ PT_IPV4_ADDR, 		NWAM_NCU_PROP_IPV4_ADDR },
225	{ PT_IPV4_DEFAULT_ROUTE,	NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE },
226	{ PT_IPV6_ADDRSRC, 		NWAM_NCU_PROP_IPV6_ADDRSRC },
227	{ PT_IPV6_ADDR, 		NWAM_NCU_PROP_IPV6_ADDR },
228	{ PT_IPV6_DEFAULT_ROUTE,	NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE },
229	{ 0, NULL }
230};
231
232/* ENM properties table */
233static prop_table_entry_t enm_prop_table[] = {
234	{ PT_ENM_FMRI, 		NWAM_ENM_PROP_FMRI },
235	{ PT_ENM_START, 	NWAM_ENM_PROP_START },
236	{ PT_ENM_STOP, 		NWAM_ENM_PROP_STOP },
237	{ PT_ACTIVATION_MODE, 	NWAM_ENM_PROP_ACTIVATION_MODE },
238	{ PT_CONDITIONS, 	NWAM_ENM_PROP_CONDITIONS },
239	{ PT_ENABLED, 		NWAM_ENM_PROP_ENABLED },
240	{ 0, NULL }
241};
242
243/* LOCation properties table */
244static prop_table_entry_t loc_prop_table[] = {
245	{ PT_ACTIVATION_MODE, 	NWAM_LOC_PROP_ACTIVATION_MODE },
246	{ PT_CONDITIONS, 	NWAM_LOC_PROP_CONDITIONS },
247	{ PT_ENABLED, 		NWAM_LOC_PROP_ENABLED },
248	{ PT_LOC_NAMESERVICES, 	NWAM_LOC_PROP_NAMESERVICES },
249	{ PT_LOC_NAMESERVICES_CONFIG, NWAM_LOC_PROP_NAMESERVICES_CONFIG_FILE },
250	{ PT_LOC_DNS_CONFIGSRC, NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC },
251	{ PT_LOC_DNS_DOMAIN, 	NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN },
252	{ PT_LOC_DNS_SERVERS, 	NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS },
253	{ PT_LOC_DNS_SEARCH, 	NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH },
254	{ PT_LOC_NIS_CONFIGSRC, NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC },
255	{ PT_LOC_NIS_SERVERS, 	NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS },
256	{ PT_LOC_LDAP_CONFIGSRC, NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC },
257	{ PT_LOC_LDAP_SERVERS,	NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS },
258	{ PT_LOC_DEFAULT_DOMAIN, NWAM_LOC_PROP_DEFAULT_DOMAIN },
259	{ PT_LOC_NFSV4_DOMAIN, 	NWAM_LOC_PROP_NFSV4_DOMAIN },
260	{ PT_LOC_IPF_CONFIG, 	NWAM_LOC_PROP_IPFILTER_CONFIG_FILE },
261	{ PT_LOC_IPF_V6_CONFIG, NWAM_LOC_PROP_IPFILTER_V6_CONFIG_FILE },
262	{ PT_LOC_IPNAT_CONFIG, 	NWAM_LOC_PROP_IPNAT_CONFIG_FILE },
263	{ PT_LOC_IPPOOL_CONFIG, NWAM_LOC_PROP_IPPOOL_CONFIG_FILE },
264	{ PT_LOC_IKE_CONFIG, 	NWAM_LOC_PROP_IKE_CONFIG_FILE },
265	{ PT_LOC_IPSECPOL_CONFIG, NWAM_LOC_PROP_IPSECPOLICY_CONFIG_FILE },
266	{ 0, NULL }
267};
268
269/* Known WLAN properties table */
270static prop_table_entry_t wlan_prop_table[] = {
271	{ PT_WLAN_BSSIDS, 	NWAM_KNOWN_WLAN_PROP_BSSIDS },
272	{ PT_WLAN_PRIORITY, 	NWAM_KNOWN_WLAN_PROP_PRIORITY },
273	{ PT_WLAN_KEYNAME, 	NWAM_KNOWN_WLAN_PROP_KEYNAME },
274	{ PT_WLAN_KEYSLOT, 	NWAM_KNOWN_WLAN_PROP_KEYSLOT },
275	{ PT_WLAN_SECURITY_MODE, NWAM_KNOWN_WLAN_PROP_SECURITY_MODE },
276	{ 0, NULL }
277};
278
279/* Returns the appropriate properties table for the given object type */
280static prop_table_entry_t *
281get_prop_table(nwam_object_type_t object_type)
282{
283	switch (object_type) {
284	case NWAM_OBJECT_TYPE_NCU:
285		return (ncu_prop_table);
286	case NWAM_OBJECT_TYPE_LOC:
287		return (loc_prop_table);
288	case NWAM_OBJECT_TYPE_ENM:
289		return (enm_prop_table);
290	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
291		return (wlan_prop_table);
292	}
293	return (NULL);
294}
295
296/* Global variables */
297
298/* set early in main(), never modified thereafter, used all over the place */
299static char *execname;
300
301/* set in modifying functions, checked in read_input() */
302boolean_t saw_error = B_FALSE;
303
304/* set in yacc parser, checked in read_input() */
305boolean_t newline_terminated;
306
307/* set in main(), checked in lex error handler */
308boolean_t cmd_file_mode = B_FALSE;
309
310/* set in exit_func(), checked in read_input() */
311static boolean_t time_to_exit = B_FALSE;
312
313/* used in nerr() and nwamerr() */
314static char *cmd_file_name = NULL;
315
316/* used with cmd_file to destroy all configurations */
317static boolean_t remove_all_configurations = B_FALSE;
318
319/* checked in read_input() and other places */
320static boolean_t ok_to_prompt = B_FALSE;
321
322/* initialized in do_interactive(), checked in initialize() */
323static boolean_t interactive_mode;
324
325static boolean_t need_to_commit = B_FALSE;
326
327/* The gl_get_line() resource object */
328static GetLine *gl;
329
330/* set when create or read objects, used by other func */
331static nwam_loc_handle_t loc_h = NULL;
332static nwam_enm_handle_t enm_h = NULL;
333static nwam_known_wlan_handle_t wlan_h = NULL;
334static nwam_ncu_handle_t ncu_h = NULL;
335static nwam_ncp_handle_t ncp_h = NULL;
336
337static int current_scope = NWAM_SCOPE_GBL;
338
339/* obj1_* are used in NWAM_SCOPE_{NCP,LOC,ENM,WLAN} */
340static int obj1_type;
341static char obj1_name[NWAM_MAX_NAME_LEN + 1];
342
343/* obj2_* are used in NWAM_SCOPE_NCU only */
344static int obj2_type;
345static char obj2_name[NWAM_MAX_NAME_LEN + 1];
346
347/* arrays for tab-completion */
348/* commands at NWAM_SCOPE_GBL */
349static const char *global_scope_cmds[] = {
350	"create ",
351	"destroy ",
352	"end ",
353	"exit ",
354	"export ",
355	"help ",
356	"list ",
357	"select ",
358	NULL
359};
360
361static const char *global_create_cmds[] = {
362	"create loc ",
363	"create enm ",
364	"create ncp ",
365	"create wlan ",
366	"create -t ",		/* template */
367	NULL
368};
369
370static const char *global_destroy_cmds[] = {
371	"destroy -a ",
372	"destroy loc ",
373	"destroy enm ",
374	"destroy ncp ",
375	"destroy wlan ",
376	NULL
377};
378
379static const char *global_export_cmds[] = {
380	"export ",
381	"export -d ",		/* add destroy -a */
382	"export -f ",		/* to file */
383	"export -d -f ",	/* add destroy -a to file */
384	"export loc ",
385	"export enm ",
386	"export ncp ",
387	"export wlan ",
388	NULL
389};
390
391static const char *global_list_cmds[] = {
392	"list ",
393	"list loc ",
394	"list enm ",
395	"list ncp ",
396	"list wlan ",
397	"list -a loc ",
398	"list -a enm ",
399	"list -a wlan ",
400	NULL
401};
402
403static const char *global_select_cmds[] = {
404	"select loc ",
405	"select enm ",
406	"select ncp ",
407	"select wlan ",
408	NULL
409};
410
411/* commands at NWAM_SCOPE_LOC, _ENM, _WLAN and _NCU */
412static const char *non_ncp_scope_cmds[] = {
413	"cancel ",
414	"clear ",
415	"commit ",
416	"end ",
417	"exit ",
418	"export ",
419	"export -f ",
420	"get ",
421	"get -V ",	/* value only */
422	"help ",
423	"list ",
424	"list -a ",	/* all properties */
425	"revert ",
426	"set ",
427	"verify ",
428	"walkprop ",
429	"walkprop -a ",	/* all properties */
430	NULL
431};
432
433/* commands at NWAM_SCOPE_NCP */
434static const char *ncp_scope_cmds[] = {
435	"cancel ",
436	"create ",
437	"destroy ",
438	"end ",
439	"exit ",
440	"export ",
441	"help ",
442	"list ",
443	"select ",
444	NULL
445};
446
447static const char *ncp_create_cmds[] = {
448	"create ncu ip ",
449	"create ncu phys ",
450	"create -t ",		/* template */
451	NULL
452};
453
454static const char *ncp_destroy_cmds[] = {
455	"destroy ncu ",
456	"destroy ncu ip ",
457	"destroy ncu phys ",
458	NULL
459};
460
461static const char *ncp_export_cmds[] = {
462	"export ",
463	"export -f ",		/* to file */
464	"export ncu ",
465	"export ncu ip ",
466	"export ncu phys ",
467	NULL
468};
469
470static const char *ncp_list_cmds[] = {
471	"list ",
472	"list ncu ",
473	"list ncu ip ",
474	"list ncu phys ",
475	"list -a ncu ",
476	"list -a ncu ip ",
477	"list -a ncu phys ",
478	NULL
479};
480
481static const char *ncp_select_cmds[] = {
482	"select ncu ",
483	"select ncu ip ",
484	"select ncu phys ",
485	NULL
486};
487
488/* Functions begin here */
489
490cmd_t *
491alloc_cmd(void)
492{
493	cmd_t *cmd = calloc(1, sizeof (cmd_t));
494	if (cmd == NULL) {
495		nerr("Out of memory");
496		return (NULL);
497	}
498	cmd->cmd_argc = 0;
499	cmd->cmd_argv[0] = NULL;
500
501	return (cmd);
502}
503
504void
505free_cmd(cmd_t *cmd)
506{
507	int i;
508
509	for (i = 0; i < cmd->cmd_argc; i++)
510		free(cmd->cmd_argv[i]);
511	free(cmd);
512}
513
514void
515array_free(void **array, int nelem)
516{
517	int i;
518	for (i = 0; i < nelem; i++)
519		free(array[i]);
520	free(array);
521}
522
523static boolean_t
524initial_match(const char *line1, const char *line2, int word_end)
525{
526	if (word_end <= 0)
527		return (B_TRUE);
528	return (strncmp(line1, line2, word_end) == 0);
529}
530
531static int
532add_stuff(WordCompletion *cpl, const char *line1, const char **list,
533    int word_end)
534{
535	int i, err;
536
537	for (i = 0; list[i] != NULL; i++) {
538		if (initial_match(line1, list[i], word_end)) {
539			err = cpl_add_completion(cpl, line1, 0, word_end,
540			    list[i] + word_end, "", "");
541			if (err != 0)
542				return (err);
543		}
544	}
545	return (0);
546}
547
548/*
549 * To fill in the rest of a string when user types the tab key.
550 * First digital number is the length of the string, the second digital number
551 * is the min number of chars that is needed to uniquely identify a string.
552 */
553#define	MINI_STR(l, s, m, n) strncmp(l, s, MAX(MIN(sizeof (s) - 1, m), n))
554
555/* ARGSUSED */
556static
557CPL_MATCH_FN(cmd_cpl_fn)
558{
559	/* tab-complete according to the current scope */
560	switch (current_scope) {
561	case NWAM_SCOPE_GBL:
562		if (MINI_STR(line, "create ", word_end, 2) == 0)
563			return (add_stuff(cpl, line, global_create_cmds,
564			    word_end));
565		if (MINI_STR(line, "destroy ", word_end, 1) == 0)
566			return (add_stuff(cpl, line, global_destroy_cmds,
567			    word_end));
568		if (MINI_STR(line, "export ", word_end, 3) == 0)
569			return (add_stuff(cpl, line, global_export_cmds,
570			    word_end));
571		if (MINI_STR(line, "list ", word_end, 1) == 0)
572			return (add_stuff(cpl, line, global_list_cmds,
573			    word_end));
574		if (MINI_STR(line, "select ", word_end, 1) == 0)
575			return (add_stuff(cpl, line, global_select_cmds,
576			    word_end));
577		return (add_stuff(cpl, line, global_scope_cmds, word_end));
578	case NWAM_SCOPE_LOC:
579	case NWAM_SCOPE_ENM:
580	case NWAM_SCOPE_WLAN:
581	case NWAM_SCOPE_NCU:
582		return (add_stuff(cpl, line, non_ncp_scope_cmds, word_end));
583	case NWAM_SCOPE_NCP:
584		if (MINI_STR(line, "create ", word_end, 2) == 0)
585			return (add_stuff(cpl, line, ncp_create_cmds,
586			    word_end));
587		if (MINI_STR(line, "destroy ", word_end, 1) == 0)
588			return (add_stuff(cpl, line, ncp_destroy_cmds,
589			    word_end));
590		if (MINI_STR(line, "export ", word_end, 3) == 0)
591			return (add_stuff(cpl, line, ncp_export_cmds,
592			    word_end));
593		if (MINI_STR(line, "list ", word_end, 1) == 0)
594			return (add_stuff(cpl, line, ncp_list_cmds, word_end));
595		if (MINI_STR(line, "select ", word_end, 1) == 0)
596			return (add_stuff(cpl, line, ncp_select_cmds,
597			    word_end));
598		return (add_stuff(cpl, line, ncp_scope_cmds, word_end));
599	}
600	/* should never get here */
601	return (NULL);
602}
603
604const char *
605cmd_to_str(int cmd_num)
606{
607	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
608	return (helptab[cmd_num].cmd_name);
609}
610
611/* Returns "loc", "enm", "wlan" or "ncp" as string */
612static const char *
613rt1_to_str(int res_type)
614{
615	assert(res_type >= RT1_MIN && res_type <= RT1_MAX);
616	return (res1_types[res_type]);
617}
618
619/* Returns "ncu" as string */
620static const char *
621rt2_to_str(int res_type)
622{
623	assert(res_type >= RT2_MIN && res_type <= RT2_MAX);
624	return (res2_types[res_type]);
625}
626
627/* Returns "ncp, "ncu", "loc", "enm", or "wlan" according to the scope */
628static const char *
629scope_to_str(int scope) {
630	switch (scope) {
631	case NWAM_SCOPE_GBL:
632		return ("global");
633	case NWAM_SCOPE_NCP:
634		return ("ncp");
635	case NWAM_SCOPE_NCU:
636		return ("ncu");
637	case NWAM_SCOPE_LOC:
638		return ("loc");
639	case NWAM_SCOPE_ENM:
640		return ("enm");
641	case NWAM_SCOPE_WLAN:
642		return ("wlan");
643	default:
644		return ("invalid");
645	}
646}
647
648/* Given an enm property and value, returns it as a string */
649static const char *
650propval_to_str(const char *propname, uint64_t value)
651{
652	const char *str;
653
654	if (nwam_uint64_get_value_string(propname, value, &str) == NWAM_SUCCESS)
655		return (str);
656	return (NULL);
657}
658
659/* Given an int for a prop, returns it as string */
660static const char *
661pt_to_str(int prop_type)
662{
663	assert(prop_type >= PT_MIN && prop_type <= PT_MAX);
664	return (pt_types[prop_type]);
665}
666
667/* Return B_TRUE if string starts with "t" or is 1, B_FALSE otherwise */
668static boolean_t
669str_to_boolean(const char *str)
670{
671	if (strncasecmp(str, "t", 1) == 0 || atoi(str) == 1)
672		return (B_TRUE);
673	else
674		return (B_FALSE);
675}
676
677/*
678 * This is a separate function rather than a set of define's because of the
679 * gettext() wrapping.
680 */
681
682/*
683 * TRANSLATION_NOTE
684 * Each string below should have \t follow \n whenever needed; the
685 * initial \t and the terminal \n will be provided by the calling function.
686 */
687
688static const char *
689long_help(int cmd_num)
690{
691	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
692	switch (cmd_num) {
693		case CMD_CANCEL:
694			return (gettext("Cancels the current configuration "
695			    "changes."));
696		case CMD_CLEAR:
697			return (gettext("Clears the value for the specified "
698			    "property."));
699		case CMD_COMMIT:
700			return (gettext("Commits the current configuration."));
701		case CMD_CREATE:
702			return (gettext("Creates a new profile or resource."));
703		case CMD_DESTROY:
704			return (gettext("Destroys the specified profile or "
705			    "resource."));
706		case CMD_END:
707			return (gettext("Ends specification of a resource."));
708		case CMD_EXIT:
709			return (gettext("Exits the program."));
710		case CMD_EXPORT:
711			return (gettext("Exports the configuration."));
712		case CMD_GET:
713			return (gettext("Gets the value of the specified "
714			    "property."));
715		case CMD_HELP:
716			return (gettext("Prints help message."));
717		case CMD_LIST:
718			return (gettext("Lists existing objects."));
719		case CMD_REVERT:
720			return (gettext("Reverts to the previous "
721			    "configuration."));
722		case CMD_SELECT:
723			return (gettext("Selects a resource to modify."));
724		case CMD_SET:
725			return (gettext("Sets the value of the specified "
726			    "property."));
727		case CMD_VERIFY:
728			return (gettext("Verifies an object."));
729		case CMD_WALKPROP:
730			return (gettext("Iterates over properties."));
731		default:
732			return (gettext("Unknown command."));
733	}
734}
735
736void
737command_usage(int command)
738{
739	if (command < CMD_MIN || command > CMD_MAX) {
740		nerr("Unknown command");
741	} else {
742		nerr("%s: %s: %s", gettext("Error"), gettext("usage"),
743		    helptab[command].cmd_usage);
744	}
745}
746
747static void
748long_usage(uint_t cmd_num)
749{
750	(void) printf("%s: %s\n", gettext("usage"),
751	    helptab[cmd_num].cmd_usage);
752	(void) printf("\t%s\n", long_help(cmd_num));
753}
754
755/* Prints usage for command line options */
756static void
757cmd_line_usage()
758{
759	(void) printf("%s:\t%s\t\t\t\t(%s)\n", gettext("usage"), execname,
760	    gettext("interactive-mode"));
761	(void) printf("\t%s <%s> [%s...]\n", execname, gettext("command"),
762	    gettext("options"));
763	(void) printf("\t%s [-d] -f <%s>\n", execname, gettext("command-file"));
764	(void) printf("\t%s %s [<%s>]\n", execname, cmd_to_str(CMD_HELP),
765	    gettext("command"));
766}
767
768/* Prints the line number of the current command if in command-file mode */
769static void
770print_lineno()
771{
772	static int last_lineno;
773
774	/* lex_lineno has already been incremented in the lexer; compensate */
775	if (cmd_file_mode && lex_lineno > last_lineno) {
776		if (strcmp(cmd_file_name, "-") == 0)
777			(void) fprintf(stderr, gettext("On line %d:\n"),
778			    lex_lineno - 1);
779		else
780			(void) fprintf(stderr, gettext("On line %d of %s:\n"),
781			    lex_lineno - 1, cmd_file_name);
782		last_lineno = lex_lineno;
783	}
784}
785
786/* PRINTFLIKE1 */
787void
788nerr(const char *format, ...)
789{
790	va_list	alist;
791
792	print_lineno();
793
794	format = gettext(format);
795	va_start(alist, format);
796	(void) vfprintf(stderr, format, alist);
797	va_end(alist);
798	(void) fprintf(stderr, "\n");
799
800	saw_error = B_TRUE;
801}
802
803/* PRINTFLIKE2 */
804static void
805nwamerr(nwam_error_t err, const char *format, ...)
806{
807	va_list	alist;
808
809	print_lineno();
810
811	format = gettext(format);
812	va_start(alist, format);
813	(void) vfprintf(stderr, format, alist);
814	va_end(alist);
815	(void) fprintf(stderr, ": %s\n", nwam_strerror(err));
816
817	saw_error = B_TRUE;
818}
819
820void
821properr(const char *prop)
822{
823	nerr("Invalid property: '%s'", prop);
824}
825
826/*
827 * If free_ncu_only == B_TRUE, only ncu handle is freed, ncp handle remains the
828 * same.  Since nwam_ncp_free() takes care of its ncus, no need to explicitly
829 * call nwam_ncu_free() afterwards.
830 */
831static void
832free_handle(boolean_t free_ncu_only)
833{
834	if (ncp_h != NULL) {
835		if (!free_ncu_only) {
836			nwam_ncp_free(ncp_h);
837			ncp_h = NULL;
838			ncu_h = NULL;
839		} else if (ncu_h != NULL) {
840			nwam_ncu_free(ncu_h);
841			ncu_h = NULL;
842		}
843	}
844
845	if (enm_h != NULL) {
846		nwam_enm_free(enm_h);
847		enm_h = NULL;
848	}
849
850	if (loc_h != NULL) {
851		nwam_loc_free(loc_h);
852		loc_h = NULL;
853	}
854
855	if (wlan_h != NULL) {
856		nwam_known_wlan_free(wlan_h);
857		wlan_h = NULL;
858	}
859}
860
861/*
862 * On input, TRUE => yes, FALSE => no.
863 * On return, TRUE => 1, FALSE => no, could not ask => -1.
864 */
865static int
866ask_yesno(boolean_t default_answer, const char *question)
867{
868	char line[64];  /* should be enough to answer yes or no */
869
870	if (!ok_to_prompt) {
871		saw_error = B_TRUE;
872		return (-1);
873	}
874	for (;;) {
875		if (printf("%s (%s)? ", gettext(question),
876		    default_answer ? "[y]/n" : "y/[n]") < 0)
877			return (-1);
878		if (fgets(line, sizeof (line), stdin) == NULL)
879			return (-1);
880
881		if (line[0] == '\n')
882			return (default_answer ? 1 : 0);
883		if (tolower(line[0]) == 'y')
884			return (1);
885		if (tolower(line[0]) == 'n')
886			return (0);
887	}
888}
889
890/* This is the back-end helper function for read_input() below. */
891static int
892cleanup()
893{
894	int answer;
895
896	if (!interactive_mode && !cmd_file_mode) {
897		/*
898		 * If we're not in interactive mode, and we're not in command
899		 * file mode, then we must be in commands-from-the-command-line
900		 * mode.  As such, we can't loop back and ask for more input.
901		 * It was OK to prompt for such things as whether or not to
902		 * really delete something in the command handler called from
903		 * yyparse() above, but "really quit?" makes no sense in this
904		 * context.  So disable prompting.
905		 */
906		ok_to_prompt = B_FALSE;
907	}
908	if (need_to_commit) {
909		answer = ask_yesno(B_FALSE,
910		    "Configuration not saved; really quit");
911		switch (answer) {
912		case -1:
913			/* issue error here */
914			return (NWAM_ERR);
915		case 1:
916			/*
917			 * don't want to save, just exit. handles are freed at
918			 * end_func() or exit_func().
919			 */
920			return (NWAM_OK);
921		default:
922			/* loop back to read input */
923			time_to_exit = B_FALSE;
924			yyin = stdin;
925			return (NWAM_REPEAT);
926		}
927	}
928	return (saw_error ? NWAM_ERR : NWAM_OK);
929}
930
931static int
932string_to_yyin(char *string)
933{
934	if ((yyin = tmpfile()) == NULL)
935		goto error;
936	if (fwrite(string, strlen(string), 1, yyin) != 1)
937		goto error;
938	if (fseek(yyin, 0, SEEK_SET) != 0)
939		goto error;
940
941	return (NWAM_OK);
942
943error:
944	nerr("problem creating temporary file");
945	return (NWAM_ERR);
946}
947
948/*
949 * read_input() is the driver of this program.  It is a wrapper around
950 * yyparse(), printing appropriate prompts when needed, checking for
951 * exit conditions and reacting appropriately.  This function is
952 * called when in interactive mode or command-file mode.
953 */
954static int
955read_input(void)
956{
957	boolean_t yyin_is_a_tty = isatty(fileno(yyin));
958	/*
959	 * The prompt is "e> " or "e:t1:o1> " or "e:t1:o1:t2:o2> " where e is
960	 * execname, t is resource type, o is object name.
961	 */
962	char prompt[MAXPATHLEN + (2 * (NWAM_MAX_TYPE_LEN + NWAM_MAX_NAME_LEN))
963	    + sizeof ("::::> ")];
964	char *line;
965
966	/* yyin should have been set to the appropriate (FILE *) if not stdin */
967	newline_terminated = B_TRUE;
968	for (;;) {
969		if (yyin_is_a_tty) {
970			if (newline_terminated) {
971				switch (current_scope) {
972				case NWAM_SCOPE_GBL:
973					(void) snprintf(prompt, sizeof (prompt),
974					    "%s> ", execname);
975					break;
976				case NWAM_SCOPE_LOC:
977				case NWAM_SCOPE_ENM:
978				case NWAM_SCOPE_WLAN:
979				case NWAM_SCOPE_NCP:
980					(void) snprintf(prompt, sizeof (prompt),
981					    "%s:%s:%s> ", execname,
982					    rt1_to_str(obj1_type), obj1_name);
983
984					break;
985				case NWAM_SCOPE_NCU:
986					(void) snprintf(prompt, sizeof (prompt),
987					    "%s:%s:%s:%s:%s> ", execname,
988					    rt1_to_str(obj1_type), obj1_name,
989					    rt2_to_str(obj2_type), obj2_name);
990				}
991			}
992			/*
993			 * If the user hits ^C then we want to catch it and
994			 * start over.  If the user hits EOF then we want to
995			 * bail out.
996			 */
997			line = gl_get_line(gl, prompt, NULL, -1);
998			if (gl_return_status(gl) == GLR_SIGNAL) {
999				gl_abandon_line(gl);
1000				continue;
1001			}
1002			if (line == NULL)
1003				break;
1004			if (string_to_yyin(line) != NWAM_OK)
1005				break;
1006			while (!feof(yyin)) {
1007				yyparse();
1008
1009				/*
1010				 * If any command on a list of commands
1011				 * give an error, don't continue with the
1012				 * remaining commands.
1013				 */
1014				if (saw_error || time_to_exit)
1015					break;
1016			}
1017		} else {
1018			yyparse();
1019		}
1020
1021		/* Bail out on an error in command-file mode. */
1022		if (saw_error && cmd_file_mode && !interactive_mode)
1023			time_to_exit = B_TRUE;
1024		if (time_to_exit || (!yyin_is_a_tty && feof(yyin)))
1025			break;
1026	}
1027	return (cleanup());
1028}
1029
1030/*
1031 * This function is used in the interactive-mode scenario: it just calls
1032 * read_input() until we are done.
1033 */
1034static int
1035do_interactive(void)
1036{
1037	int err;
1038
1039	interactive_mode = B_TRUE;
1040	do {
1041		err = read_input();
1042	} while (err == NWAM_REPEAT);
1043	return (err);
1044}
1045
1046/* Calls the help_func() to print the usage of all commands */
1047void
1048help_wrap()
1049{
1050	cmd_t *help_cmd;
1051
1052	if ((help_cmd = alloc_cmd()) == NULL)
1053		exit(NWAM_ERR);
1054	help_func(help_cmd);
1055	free_cmd(help_cmd);
1056}
1057
1058/* Check if the given command is allowed in the current scope */
1059boolean_t
1060check_scope(int cmd)
1061{
1062	/* allowed in all scopes */
1063	switch (cmd) {
1064	case CMD_END:
1065	case CMD_EXIT:
1066	case CMD_HELP:
1067	case CMD_LIST:
1068	case CMD_EXPORT:
1069		return (B_TRUE);
1070	}
1071	/* scope-specific */
1072	switch (current_scope) {
1073	case NWAM_SCOPE_GBL:
1074		switch (cmd) {
1075		case CMD_CREATE:
1076		case CMD_DESTROY:
1077		case CMD_SELECT:
1078			return (B_TRUE);
1079		}
1080		break;
1081	case NWAM_SCOPE_LOC:
1082	case NWAM_SCOPE_ENM:
1083	case NWAM_SCOPE_WLAN:
1084	case NWAM_SCOPE_NCU:
1085		switch (cmd) {
1086		case CMD_CANCEL:
1087		case CMD_CLEAR:
1088		case CMD_COMMIT:
1089		case CMD_GET:
1090		case CMD_REVERT:
1091		case CMD_SET:
1092		case CMD_VERIFY:
1093		case CMD_WALKPROP:
1094			return (B_TRUE);
1095		}
1096		break;
1097	case NWAM_SCOPE_NCP:
1098		switch (cmd) {
1099		case CMD_CANCEL:
1100		case CMD_CREATE:
1101		case CMD_DESTROY:
1102		case CMD_SELECT:
1103			return (B_TRUE);
1104		}
1105		break;
1106	default:
1107		nerr("Invalid scope");
1108	}
1109	nerr("'%s' is not allowed at this scope", cmd_to_str(cmd));
1110	return (B_FALSE);
1111}
1112
1113/* Returns the active object type depending on which handle is not NULL */
1114static nwam_object_type_t
1115active_object_type()
1116{
1117	/* Check ncu_h before ncp_h, ncp_h must be loaded before ncu_h */
1118	if (ncu_h != NULL)
1119		return (NWAM_OBJECT_TYPE_NCU);
1120	else if (ncp_h != NULL)
1121		return (NWAM_OBJECT_TYPE_NCP);
1122	else if (loc_h != NULL)
1123		return (NWAM_OBJECT_TYPE_LOC);
1124	else if (enm_h != NULL)
1125		return (NWAM_OBJECT_TYPE_ENM);
1126	else if (wlan_h != NULL)
1127		return (NWAM_OBJECT_TYPE_KNOWN_WLAN);
1128	else
1129		return (NWAM_OBJECT_TYPE_UNKNOWN);
1130}
1131
1132/* Retrive the name of the object from its handle */
1133static nwam_error_t
1134object_name_from_handle(nwam_object_type_t object_type, void *handle,
1135    char **namep)
1136{
1137	switch (object_type) {
1138	case NWAM_OBJECT_TYPE_NCP:
1139		return (nwam_ncp_get_name(handle, namep));
1140	case NWAM_OBJECT_TYPE_NCU:
1141		return (nwam_ncu_get_name(handle, namep));
1142	case NWAM_OBJECT_TYPE_LOC:
1143		return (nwam_loc_get_name(handle, namep));
1144	case NWAM_OBJECT_TYPE_ENM:
1145		return (nwam_enm_get_name(handle, namep));
1146	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
1147		return (nwam_known_wlan_get_name(handle, namep));
1148	}
1149	return (NWAM_INVALID_ARG);
1150}
1151
1152static void
1153do_commit()
1154{
1155	nwam_error_t	ret = NWAM_SUCCESS;
1156	const char	*errprop;
1157
1158	if (!need_to_commit)
1159		return;
1160
1161	switch (active_object_type()) {
1162	case NWAM_OBJECT_TYPE_NCU:
1163		ret = nwam_ncu_commit(ncu_h, 0);
1164		break;
1165	case NWAM_OBJECT_TYPE_ENM:
1166		ret = nwam_enm_commit(enm_h, 0);
1167		break;
1168	case NWAM_OBJECT_TYPE_LOC:
1169		ret = nwam_loc_commit(loc_h, 0);
1170		break;
1171	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
1172		ret = nwam_known_wlan_commit(wlan_h, 0);
1173		break;
1174	}
1175
1176	if (ret == NWAM_SUCCESS) {
1177		need_to_commit = B_FALSE;
1178		if (interactive_mode)
1179			(void) printf(gettext("Committed changes\n"));
1180	} else {
1181		nwam_error_t verr;
1182
1183		/* Find property that caused failure */
1184		switch (active_object_type()) {
1185		case NWAM_OBJECT_TYPE_NCU:
1186			verr = nwam_ncu_validate(ncu_h, &errprop);
1187			break;
1188		case NWAM_OBJECT_TYPE_ENM:
1189			verr = nwam_enm_validate(enm_h, &errprop);
1190			break;
1191		case NWAM_OBJECT_TYPE_LOC:
1192			verr = nwam_loc_validate(loc_h, &errprop);
1193			break;
1194		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
1195			verr = nwam_known_wlan_validate(wlan_h, &errprop);
1196			break;
1197		}
1198
1199		if (verr != NWAM_SUCCESS)
1200			nwamerr(ret, "Commit error on property '%s'", errprop);
1201		else
1202			nwamerr(ret, "Commit error");
1203	}
1204}
1205
1206/*
1207 * Saves the current configuration to persistent storage.
1208 */
1209/* ARGSUSED */
1210void
1211commit_func(cmd_t *cmd)
1212{
1213	if (!need_to_commit) {
1214		if (interactive_mode)
1215			(void) printf(gettext("Nothing to commit\n"));
1216	} else {
1217		do_commit();
1218	}
1219}
1220
1221static void
1222do_cancel()
1223{
1224	switch (current_scope) {
1225	case NWAM_SCOPE_NCU:
1226		current_scope = NWAM_SCOPE_NCP;
1227		obj2_type = 0;
1228		free_handle(B_TRUE);
1229		break;
1230	case NWAM_SCOPE_NCP:
1231	case NWAM_SCOPE_ENM:
1232	case NWAM_SCOPE_WLAN:
1233	case NWAM_SCOPE_LOC:
1234		current_scope = NWAM_SCOPE_GBL;
1235		obj1_type = 0;
1236		free_handle(B_FALSE);
1237		break;
1238	case NWAM_SCOPE_GBL:
1239		free_handle(B_FALSE);
1240		break;
1241	default:
1242		nerr("Invalid scope");
1243		return;
1244	}
1245	need_to_commit = B_FALSE;
1246}
1247
1248/*
1249 * End operation on current scope and go up one scope.
1250 * Changes are not saved, no prompt either.
1251 */
1252/* ARGSUSED */
1253void
1254cancel_func(cmd_t *cmd)
1255{
1256	do_cancel();
1257}
1258
1259/*
1260 * Removes leading and trailing quotes from a string.
1261 * Caller must free returned string.
1262 */
1263static char *
1264trim_quotes(const char *quoted_str)
1265{
1266	char *str;
1267	int end;
1268
1269	/* export_func() and list_func() can pass NULL here */
1270	if (quoted_str == NULL)
1271		return (NULL);
1272
1273	/* remove leading quote */
1274	if (quoted_str[0] == '"')
1275		str = strdup(quoted_str + 1);
1276	else
1277		str = strdup(quoted_str);
1278	if (str == NULL)
1279		return (NULL);
1280
1281	/* remove trailing quote and newline */
1282	end = strlen(str) - 1;
1283	while (end >= 0 && (str[end] == '"' || str[end] == '\n'))
1284		end--;
1285	str[end+1] = 0;
1286
1287	return (str);
1288}
1289
1290/*
1291 * Creates a new resource and enters the scope of that resource.
1292 * The new resource can also be a copy of an existing resource (-t option).
1293 * If in interactive mode, then after creation call walkprop_func()
1294 * to do walk the properties for the new object.
1295 */
1296void
1297create_func(cmd_t *cmd)
1298{
1299	nwam_error_t	ret = NWAM_SUCCESS;
1300	int		c;
1301	boolean_t	template = B_FALSE;
1302	char		*newname = NULL, *oldname = NULL;
1303	cmd_t		*walkprop_cmd;
1304
1305	/* make sure right command at the right scope */
1306	if (current_scope == NWAM_SCOPE_GBL &&
1307	    cmd->cmd_res2_type == RT2_NCU) {
1308		nerr("cannot create ncu at global scope");
1309		return;
1310	}
1311	if (current_scope == NWAM_SCOPE_NCP &&
1312	    cmd->cmd_res2_type != RT2_NCU) {
1313		nerr("Cannot create given object at this scope");
1314		return;
1315	}
1316
1317	assert(cmd->cmd_argc > 0);
1318	optind = 0;
1319	while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "t:")) != EOF) {
1320		switch (c) {
1321		case 't':
1322			template = B_TRUE;
1323			break;
1324		default:
1325			command_usage(CMD_CREATE);
1326			return;
1327		}
1328	}
1329
1330	if (!template) {
1331		/* no template given */
1332		/* argv[0] is name */
1333		newname = trim_quotes(cmd->cmd_argv[0]);
1334		if (cmd->cmd_res1_type == RT1_ENM) {
1335			ret = nwam_enm_create(newname, NULL, &enm_h);
1336		} else if (cmd->cmd_res1_type == RT1_LOC) {
1337			ret = nwam_loc_create(newname, &loc_h);
1338		} else if (cmd->cmd_res1_type == RT1_WLAN) {
1339			ret = nwam_known_wlan_create(newname, &wlan_h);
1340		} else if (cmd->cmd_res1_type == RT1_NCP &&
1341		    current_scope == NWAM_SCOPE_GBL) {
1342			ret = nwam_ncp_create(newname, 0, &ncp_h);
1343		} else if (cmd->cmd_res2_type == RT2_NCU) {
1344			nwam_ncu_type_t		ncu_type;
1345			nwam_ncu_class_t	ncu_class;
1346
1347			/* ncp must already be read */
1348			if (ncp_h == NULL) {
1349				nerr("Create error: NCP has not been read");
1350				goto done;
1351			}
1352
1353			ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
1354			ncu_type = nwam_ncu_class_to_type(ncu_class);
1355			ret = nwam_ncu_create(ncp_h, newname, ncu_type,
1356			    ncu_class, &ncu_h);
1357		}
1358
1359		if (ret != NWAM_SUCCESS) {
1360			nwamerr(ret, "Create error");
1361			goto done;
1362		}
1363
1364	} else {
1365		/* template given */
1366		/* argv[0] is -t, argv[1] is old name, argv[2] is new name */
1367		oldname = trim_quotes(cmd->cmd_argv[1]);
1368		newname = trim_quotes(cmd->cmd_argv[2]);
1369		if (cmd->cmd_res1_type == RT1_ENM) {
1370			nwam_enm_handle_t oldenm_h;
1371
1372			ret = nwam_enm_read(oldname, 0, &oldenm_h);
1373			if (ret != NWAM_SUCCESS)
1374				goto read_error;
1375			ret = nwam_enm_copy(oldenm_h, newname, &enm_h);
1376			nwam_enm_free(oldenm_h);
1377		} else if (cmd->cmd_res1_type == RT1_LOC) {
1378			nwam_loc_handle_t oldloc_h;
1379
1380			ret = nwam_loc_read(oldname, 0, &oldloc_h);
1381			if (ret != NWAM_SUCCESS)
1382				goto read_error;
1383			ret = nwam_loc_copy(oldloc_h, newname, &loc_h);
1384			nwam_loc_free(oldloc_h);
1385		} else if (cmd->cmd_res1_type == RT1_WLAN) {
1386			nwam_known_wlan_handle_t oldwlan_h;
1387
1388			ret = nwam_known_wlan_read(oldname, 0, &oldwlan_h);
1389			if (ret != NWAM_SUCCESS)
1390				goto read_error;
1391			ret = nwam_known_wlan_copy(oldwlan_h, newname, &wlan_h);
1392			nwam_known_wlan_free(oldwlan_h);
1393		} else if (cmd->cmd_res1_type == RT1_NCP &&
1394		    current_scope == NWAM_SCOPE_GBL) {
1395			nwam_ncp_handle_t oldncp_h;
1396
1397			ret = nwam_ncp_read(oldname, 0, &oldncp_h);
1398			if (ret != NWAM_SUCCESS)
1399				goto read_error;
1400			ret = nwam_ncp_copy(oldncp_h, newname, &ncp_h);
1401			nwam_ncp_free(oldncp_h);
1402		} else if (cmd->cmd_res2_type == RT2_NCU) {
1403			nwam_ncu_handle_t	oldncu_h;
1404			nwam_ncu_type_t		ncu_type;
1405			nwam_ncu_class_t	ncu_class;
1406
1407			/* ncp must already be read */
1408			if (ncp_h == NULL) {
1409				nerr("Copy error: NCP has not been read");
1410				goto done;
1411			}
1412			ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
1413			ncu_type = nwam_ncu_class_to_type(ncu_class);
1414			ret = nwam_ncu_read(ncp_h, oldname, ncu_type, 0,
1415			    &oldncu_h);
1416			if (ret != NWAM_SUCCESS)
1417				goto read_error;
1418			ret = nwam_ncu_copy(oldncu_h, newname, &ncu_h);
1419			nwam_ncu_free(oldncu_h);
1420		}
1421
1422		if (ret != NWAM_SUCCESS) {
1423			nwamerr(ret, "Copy error");
1424			goto done;
1425		}
1426	}
1427
1428	if (current_scope == NWAM_SCOPE_GBL) {
1429		(void) strlcpy(obj1_name, newname, sizeof (obj1_name));
1430		obj1_type = cmd->cmd_res1_type;
1431		if (obj1_type == RT1_ENM)
1432			current_scope = NWAM_SCOPE_ENM;
1433		else if (obj1_type == RT1_LOC)
1434			current_scope = NWAM_SCOPE_LOC;
1435		else if (obj1_type == RT1_WLAN)
1436			current_scope = NWAM_SCOPE_WLAN;
1437		else if (obj1_type == RT1_NCP)
1438			current_scope = NWAM_SCOPE_NCP;
1439	} else {
1440		(void) strlcpy(obj2_name, newname, sizeof (obj2_name));
1441		current_scope = NWAM_SCOPE_NCU;
1442		obj2_type = cmd->cmd_res2_type;
1443	}
1444	if (current_scope != NWAM_SCOPE_NCP)
1445		need_to_commit = B_TRUE;
1446
1447	/* do a walk of the properties if in interactive mode */
1448	if (interactive_mode && current_scope != NWAM_SCOPE_NCP) {
1449		(void) printf(gettext("Created %s '%s'.  "
1450		    "Walking properties ...\n"),
1451		    scope_to_str(current_scope), newname);
1452		if ((walkprop_cmd = alloc_cmd()) == NULL)
1453			goto done;
1454		walkprop_func(walkprop_cmd);
1455		free(walkprop_cmd);
1456	}
1457
1458read_error:
1459	if (ret != NWAM_SUCCESS)
1460		nwamerr(ret, "Copy error reading '%s'", oldname);
1461
1462done:
1463	free(oldname);
1464	free(newname);
1465}
1466
1467/* Processing of return value for destroy_*_callback() */
1468static int
1469destroy_ret(nwam_object_type_t object_type, nwam_error_t ret, void *handle)
1470{
1471	if (ret == NWAM_ENTITY_NOT_DESTROYABLE) {
1472		/* log a message to stderr, but don't consider it an error */
1473		char *name;
1474		if (object_name_from_handle(object_type, handle, &name)
1475		    == NWAM_SUCCESS) {
1476			(void) fprintf(stderr,
1477			    gettext("%s '%s' cannot be removed\n"),
1478			    nwam_object_type_to_string(object_type), name);
1479			free(name);
1480		}
1481		return (0);
1482	}
1483
1484	if (ret == NWAM_SUCCESS || ret == NWAM_ENTITY_IN_USE)
1485		return (0);
1486
1487	return (1);
1488}
1489
1490/*
1491 * NWAM_FLAG_DO_NOT_FREE is passed to nwam_*_destory() so that it does not
1492 * free the handle.  The calling nwam_walk_*() function frees this handle
1493 * as it is the function that created the handle.
1494 *
1495 * Objects that are not destroyable or are active cannot be destroyed.
1496 * Don't return error in these situations so the walk can continue.
1497 */
1498/* ARGSUSED */
1499static int
1500destroy_ncp_callback(nwam_ncp_handle_t ncp, void *arg)
1501{
1502	/* The file is deleted, so NCUs are also removed */
1503	nwam_error_t ret = nwam_ncp_destroy(ncp, NWAM_FLAG_DO_NOT_FREE);
1504	return (destroy_ret(NWAM_OBJECT_TYPE_NCP, ret, ncp));
1505}
1506
1507/* ARGSUSED */
1508static int
1509destroy_loc_callback(nwam_loc_handle_t loc, void *arg)
1510{
1511	nwam_error_t ret = nwam_loc_destroy(loc, NWAM_FLAG_DO_NOT_FREE);
1512	return (destroy_ret(NWAM_OBJECT_TYPE_LOC, ret, loc));
1513}
1514
1515/* ARGSUSED */
1516static int
1517destroy_enm_callback(nwam_enm_handle_t enm, void *arg)
1518{
1519	nwam_error_t ret = nwam_enm_destroy(enm, NWAM_FLAG_DO_NOT_FREE);
1520	return (destroy_ret(NWAM_OBJECT_TYPE_ENM, ret, enm));
1521}
1522
1523/* ARGSUSED */
1524static int
1525destroy_wlan_callback(nwam_known_wlan_handle_t wlan, void *arg)
1526{
1527	nwam_error_t ret = nwam_known_wlan_destroy(wlan, NWAM_FLAG_DO_NOT_FREE);
1528	return (destroy_ret(NWAM_OBJECT_TYPE_KNOWN_WLAN, ret, wlan));
1529}
1530
1531/*
1532 * Remove all existing configuration that are not read-only.
1533 * walk through all ncps, locs, enms, wlans and destroy each one.
1534 */
1535static nwam_error_t
1536destroy_all(void)
1537{
1538	nwam_error_t	ret;
1539
1540	assert(remove_all_configurations);
1541
1542	ret = nwam_walk_ncps(destroy_ncp_callback, NULL, 0, NULL);
1543	if (ret != NWAM_SUCCESS)
1544		goto done;
1545
1546	ret = nwam_walk_enms(destroy_enm_callback, NULL,
1547	    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
1548	if (ret != NWAM_SUCCESS)
1549		goto done;
1550
1551	ret = nwam_walk_locs(destroy_loc_callback, NULL,
1552	    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
1553	if (ret != NWAM_SUCCESS)
1554		goto done;
1555
1556	ret = nwam_walk_known_wlans(destroy_wlan_callback, NULL, 0, NULL);
1557	if (ret != NWAM_SUCCESS)
1558		goto done;
1559
1560	if (interactive_mode)
1561		(void) printf(gettext("All user-defined entities destroyed\n"));
1562	remove_all_configurations = B_FALSE;
1563
1564done:
1565	if (ret != NWAM_SUCCESS) {
1566		nwamerr(ret, "Destroy error: "
1567		    "could not destroy all configurations");
1568	}
1569	return (ret);
1570}
1571
1572/*
1573 * Destroys an instance in persistent repository, and is permanent.
1574 * If interactive mode, it is allowed at global scope only
1575 * option -a destroys everything.
1576 */
1577void
1578destroy_func(cmd_t *cmd)
1579{
1580	nwam_error_t	ret;
1581	char		*name, *realname = NULL;
1582
1583	if (current_scope == NWAM_SCOPE_NCP &&
1584	    (cmd->cmd_res1_type == RT1_ENM || cmd->cmd_res1_type == RT1_LOC ||
1585	    cmd->cmd_res1_type == RT1_WLAN)) {
1586		nerr("Destroy error: only NCUs can be destroyed in NCP scope");
1587		return;
1588	}
1589
1590	assert(cmd->cmd_argc > 0);
1591
1592	/* res1_type is -1 if -a flag is used */
1593	if (cmd->cmd_res1_type == -1) {
1594		int c;
1595
1596		if (current_scope != NWAM_SCOPE_GBL) {
1597			nerr("Cannot destroy all configurations in a "
1598			    "non-global scope");
1599			return;
1600		}
1601
1602		optind = 0;
1603		while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "a")) != EOF) {
1604			switch (c) {
1605			case 'a':
1606				remove_all_configurations = B_TRUE;
1607				break;
1608			default:
1609				command_usage(CMD_DESTROY);
1610				return;
1611			}
1612		}
1613		if (remove_all_configurations) {
1614			(void) destroy_all();
1615			return;
1616		}
1617	}
1618
1619	/* argv[0] is name */
1620	name = trim_quotes(cmd->cmd_argv[0]);
1621	if (cmd->cmd_res2_type == RT2_NCU) {
1622		nwam_ncu_type_t		ncu_type;
1623		nwam_ncu_class_t	ncu_class;
1624
1625		/* ncp must already be read */
1626		if (ncp_h == NULL) {
1627			nerr("Destroy ncu error: NCP has not been read");
1628			return;
1629		}
1630		ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
1631		ncu_type = nwam_ncu_class_to_type(ncu_class);
1632		ret = nwam_ncu_read(ncp_h, name, ncu_type, 0, &ncu_h);
1633		if (ret != NWAM_SUCCESS)
1634			goto done;
1635		(void) object_name_from_handle(NWAM_OBJECT_TYPE_NCU, ncu_h,
1636		    &realname);
1637		ret = nwam_ncu_destroy(ncu_h, 0);
1638		ncu_h = NULL;
1639	} else if (cmd->cmd_res1_type == RT1_ENM) {
1640		if ((ret = nwam_enm_read(name, 0, &enm_h)) != NWAM_SUCCESS)
1641			goto done;
1642		(void) object_name_from_handle(NWAM_OBJECT_TYPE_ENM, enm_h,
1643		    &realname);
1644		ret = nwam_enm_destroy(enm_h, 0);
1645		enm_h = NULL;
1646	} else if (cmd->cmd_res1_type == RT1_LOC) {
1647		if ((ret = nwam_loc_read(name, 0, &loc_h)) != NWAM_SUCCESS)
1648			goto done;
1649		(void) object_name_from_handle(NWAM_OBJECT_TYPE_LOC, loc_h,
1650		    &realname);
1651		ret = nwam_loc_destroy(loc_h, 0);
1652		loc_h = NULL;
1653	} else if (cmd->cmd_res1_type == RT1_WLAN) {
1654		if ((ret = nwam_known_wlan_read(name, 0, &wlan_h))
1655		    != NWAM_SUCCESS)
1656			goto done;
1657		(void) object_name_from_handle(NWAM_OBJECT_TYPE_KNOWN_WLAN,
1658		    wlan_h, &realname);
1659		ret = nwam_known_wlan_destroy(wlan_h, 0);
1660		wlan_h = NULL;
1661	} else if (cmd->cmd_res1_type == RT1_NCP) {
1662		if ((ret = nwam_ncp_read(name, 0, &ncp_h)) != NWAM_SUCCESS)
1663			goto done;
1664		(void) object_name_from_handle(NWAM_OBJECT_TYPE_NCP, ncp_h,
1665		    &realname);
1666		ret = nwam_ncp_destroy(ncp_h, 0);
1667		ncp_h = NULL;
1668	} else {
1669		nerr("Destroy error: unknown object-type");
1670	}
1671
1672done:
1673	if (ret == NWAM_ENTITY_IN_USE)  {
1674		nerr("Destroy error: active entity cannot be destroyed");
1675	} else if (ret != NWAM_SUCCESS) {
1676		nwamerr(ret, "Destroy error");
1677	} else if (interactive_mode) {
1678		(void) printf(gettext("Destroyed %s '%s'\n"),
1679		    (cmd->cmd_res2_type == RT2_NCU ?
1680		    rt2_to_str(cmd->cmd_res2_type) :
1681		    rt1_to_str(cmd->cmd_res1_type)),
1682		    realname != NULL ? realname : name);
1683	}
1684	free(name);
1685	free(realname);
1686}
1687
1688/*
1689 * End operation on current scope and go up one scope.
1690 * Changes are saved.
1691 */
1692/* ARGSUSED */
1693void
1694end_func(cmd_t *cmd)
1695{
1696	/* if need_to_commit is set, commit changes */
1697	if (need_to_commit)
1698		do_commit();
1699
1700	/*
1701	 * Call do_cancel() to go up one scope.  If commit fails,
1702	 * need_to_commit is not reset and users are asked if they want to end.
1703	 */
1704	if (!need_to_commit ||
1705	    (need_to_commit && (ask_yesno(B_FALSE,
1706	    "Configuration not saved; really end")) == 1)) {
1707		/* set time_to_exit if in global scope */
1708		if (current_scope == NWAM_SCOPE_GBL)
1709			time_to_exit = B_TRUE;
1710		/* call do_cancel() to go up one scope */
1711		do_cancel();
1712	}
1713}
1714
1715/*
1716 * Exit immediately.  Configuration changes are saved by calling end_func().
1717 */
1718/* ARGSUSED */
1719void
1720exit_func(cmd_t *cmd)
1721{
1722	cmd_t *end_cmd;
1723
1724	if (need_to_commit) {
1725		if ((end_cmd = alloc_cmd()) == NULL) {
1726			nerr("Exit error");
1727			return;
1728		}
1729		end_func(end_cmd);
1730		free_cmd(end_cmd);
1731	}
1732
1733	/*
1734	 * If need_to_commit is still set, then the commit failed.
1735	 * Otherwise, exit.
1736	 */
1737	if (!need_to_commit)
1738		time_to_exit = B_TRUE;
1739}
1740
1741void
1742help_func(cmd_t *cmd)
1743{
1744	int i;
1745
1746	if (cmd->cmd_argc == 0) {
1747		(void) printf(gettext("commands:\n"));
1748		for (i = CMD_MIN; i <= CMD_MAX; i++)
1749			(void) printf("\t%s\n", helptab[i].cmd_usage);
1750		return;
1751	}
1752
1753	for (i = CMD_MIN; i <= CMD_MAX; i++) {
1754		if (strcmp(cmd->cmd_argv[0], cmd_to_str(i)) == 0) {
1755			long_usage(i);
1756			return;
1757		}
1758	}
1759	(void) fprintf(stderr, gettext("Unknown command: '%s'\n"),
1760	    cmd->cmd_argv[0]);
1761	help_wrap();
1762}
1763
1764/*
1765 * Revert configuration of an instance to latest previous version.
1766 * Free the handle and read again.
1767 */
1768/* ARGSUSED */
1769void
1770revert_func(cmd_t *cmd)
1771{
1772	nwam_error_t		ret;
1773	char			*name = NULL;
1774	nwam_ncu_type_t		ncu_type;
1775	nwam_object_type_t	object_type = active_object_type();
1776
1777	switch (object_type) {
1778	case NWAM_OBJECT_TYPE_NCU:
1779		/* retrieve name and type to use later */
1780		if ((ret = nwam_ncu_get_ncu_type(ncu_h, &ncu_type))
1781		    != NWAM_SUCCESS) {
1782			nwamerr(ret, "Revert error: Get ncu type error");
1783			return;
1784		}
1785		if ((ret = nwam_ncu_get_name(ncu_h, &name)) != NWAM_SUCCESS)
1786			goto name_error;
1787		nwam_ncu_free(ncu_h);
1788		ncu_h = NULL;
1789		ret = nwam_ncu_read(ncp_h, name, ncu_type, 0, &ncu_h);
1790		break;
1791	case NWAM_OBJECT_TYPE_ENM:
1792		if ((ret = nwam_enm_get_name(enm_h, &name)) != NWAM_SUCCESS)
1793			goto name_error;
1794		nwam_enm_free(enm_h);
1795		enm_h = NULL;
1796		ret = nwam_enm_read(name, 0, &enm_h);
1797		break;
1798	case NWAM_OBJECT_TYPE_LOC:
1799		if ((ret = nwam_loc_get_name(loc_h, &name)) != NWAM_SUCCESS)
1800			goto name_error;
1801		nwam_loc_free(loc_h);
1802		loc_h = NULL;
1803		ret = nwam_loc_read(name, 0, &loc_h);
1804		break;
1805	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
1806		if ((ret = nwam_known_wlan_get_name(wlan_h, &name))
1807		    != NWAM_SUCCESS)
1808			goto name_error;
1809		nwam_known_wlan_free(wlan_h);
1810		wlan_h = NULL;
1811		ret = nwam_known_wlan_read(name, 0, &wlan_h);
1812		break;
1813	}
1814
1815	/* Exit this scope because handle already freed (call do_cancel()) */
1816	need_to_commit = B_FALSE;
1817
1818	if (ret != NWAM_SUCCESS) {
1819		if (ret == NWAM_ENTITY_NOT_FOUND) {
1820			nerr("%s '%s' does not exist to revert to, removing it",
1821			    nwam_object_type_to_string(object_type), name);
1822		} else {
1823			nwamerr(ret, "Revert error");
1824		}
1825		do_cancel();
1826	}
1827	free(name);
1828	return;
1829
1830name_error:
1831	if (ret != NWAM_SUCCESS)
1832		nwamerr(ret, "Revert error: get name error");
1833}
1834
1835/*
1836 * Load a resource from persistent repository and enter the scope
1837 * of that resource.
1838 */
1839void
1840select_func(cmd_t *cmd)
1841{
1842	nwam_error_t	ret;
1843	char		*name, *realname = NULL;
1844
1845	assert(cmd->cmd_argc > 0);
1846	if (current_scope == NWAM_SCOPE_NCP && cmd->cmd_res2_type != RT2_NCU) {
1847		nerr("cannot select '%s' at this scope",
1848		    rt1_to_str(cmd->cmd_res1_type));
1849		return;
1850	}
1851
1852	/* argv[0] is name */
1853	name = trim_quotes(cmd->cmd_argv[0]);
1854	switch (cmd->cmd_res1_type) {
1855	case RT1_LOC:
1856		ret = nwam_loc_read(name, 0, &loc_h);
1857		if (ret == NWAM_SUCCESS) {
1858			current_scope = NWAM_SCOPE_LOC;
1859			(void) object_name_from_handle(NWAM_OBJECT_TYPE_LOC,
1860			    loc_h, &realname);
1861		}
1862		break;
1863	case RT1_ENM:
1864		ret = nwam_enm_read(name, 0, &enm_h);
1865		if (ret == NWAM_SUCCESS) {
1866			current_scope = NWAM_SCOPE_ENM;
1867			(void) object_name_from_handle(NWAM_OBJECT_TYPE_ENM,
1868			    enm_h, &realname);
1869		}
1870		break;
1871	case RT1_WLAN:
1872		ret = nwam_known_wlan_read(name, 0, &wlan_h);
1873		if (ret == NWAM_SUCCESS) {
1874			current_scope = NWAM_SCOPE_WLAN;
1875			(void) object_name_from_handle
1876			    (NWAM_OBJECT_TYPE_KNOWN_WLAN, wlan_h, &realname);
1877		}
1878		break;
1879	case RT1_NCP:
1880		if (cmd->cmd_res2_type == RT2_NCU) {
1881			nwam_ncu_type_t		ncu_type;
1882			nwam_ncu_class_t	ncu_class;
1883
1884			/* ncp must already be read */
1885			if (ncp_h == NULL) {
1886				nerr("Select error: NCP has not been read");
1887				free(name);
1888				return;
1889			}
1890			ncu_class = (nwam_ncu_class_t)cmd->cmd_ncu_class_type;
1891			ncu_type = nwam_ncu_class_to_type(ncu_class);
1892			ret = nwam_ncu_read(ncp_h, name, ncu_type, 0, &ncu_h);
1893			if (ret == NWAM_SUCCESS) {
1894				current_scope = NWAM_SCOPE_NCU;
1895				(void) object_name_from_handle
1896				    (NWAM_OBJECT_TYPE_NCU, ncu_h, &realname);
1897			}
1898		} else {
1899			ret = nwam_ncp_read(name, 0, &ncp_h);
1900			if (ret == NWAM_SUCCESS) {
1901				current_scope = NWAM_SCOPE_NCP;
1902				(void) object_name_from_handle
1903				    (NWAM_OBJECT_TYPE_NCP, ncp_h, &realname);
1904			}
1905		}
1906		break;
1907	default:
1908		nerr("Select error: unknown object-type");
1909		free(name);
1910		return;
1911	}
1912
1913	if (ret != NWAM_SUCCESS) {
1914		nwamerr(ret, "Select error");
1915	} else {
1916		/* set the obj*_name or obj*_type depending on current scope */
1917		if (current_scope == NWAM_SCOPE_NCU) {
1918			obj2_type = RT2_NCU;
1919			(void) strlcpy(obj2_name,
1920			    realname != NULL ? realname : name,
1921			    sizeof (obj2_name));
1922		} else {
1923			(void) strlcpy(obj1_name,
1924			    realname != NULL ? realname : name,
1925			    sizeof (obj1_name));
1926			obj1_type = cmd->cmd_res1_type;
1927		}
1928	}
1929	free(name);
1930	free(realname);
1931}
1932
1933/* Given an int for prop, returns it as string */
1934static const char *
1935pt_to_prop_name(nwam_object_type_t object_type, int pt_type)
1936{
1937	int i;
1938	prop_table_entry_t *prop_table = get_prop_table(object_type);
1939
1940	for (i = 0; prop_table[i].pte_name != NULL; i++) {
1941		if (pt_type == prop_table[i].pte_type)
1942			return (prop_table[i].pte_name);
1943	}
1944	return (NULL);
1945}
1946
1947/* Given a prop as a string, returns it as an int */
1948static int
1949prop_to_pt(nwam_object_type_t object_type, const char *prop)
1950{
1951	int i;
1952	prop_table_entry_t *prop_table = get_prop_table(object_type);
1953
1954	for (i = 0; prop_table[i].pte_name != NULL; i++) {
1955		if (strcmp(prop, prop_table[i].pte_name) == 0)
1956			return (prop_table[i].pte_type);
1957	}
1958	return (-1);
1959}
1960
1961/* Given a prop as an int, returns its type (nwam_value_type_t) */
1962static nwam_value_type_t
1963prop_value_type(nwam_object_type_t object_type, const char *prop)
1964{
1965	nwam_error_t		ret;
1966	nwam_value_type_t	value_type;
1967
1968	switch (object_type) {
1969	case NWAM_OBJECT_TYPE_NCU:
1970		ret = nwam_ncu_get_prop_type(prop, &value_type);
1971		break;
1972	case NWAM_OBJECT_TYPE_LOC:
1973		ret = nwam_loc_get_prop_type(prop, &value_type);
1974		break;
1975	case NWAM_OBJECT_TYPE_ENM:
1976		ret = nwam_enm_get_prop_type(prop, &value_type);
1977		break;
1978	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
1979		ret = nwam_known_wlan_get_prop_type(prop, &value_type);
1980		break;
1981	}
1982
1983	if (ret != NWAM_SUCCESS)
1984		value_type = NWAM_VALUE_TYPE_UNKNOWN;
1985
1986	return (value_type);
1987}
1988
1989/*
1990 * Converts input_str to an array nwam_value.
1991 * If is_list_prop, break input_str into array of strings first.
1992 */
1993static nwam_value_t
1994str_to_nwam_value(nwam_object_type_t object_type, char *input_str, int pt_type,
1995    boolean_t is_list_prop)
1996{
1997	int		i, n = 0, ret;
1998	nwam_value_t	data;
1999	char		**val;
2000	int		max_str_num;
2001
2002	nwam_value_type_t	value_type;
2003	int64_t			*int_vals;
2004	uint64_t		*uint_vals;
2005	boolean_t		*boolean_vals;
2006
2007	/*
2008	 * Worst case is that each char separated by DELIMITER, so the
2009	 * max number of sub strings is half of string length + 1.
2010	 */
2011	max_str_num = strlen(input_str) / 2 + 1;
2012
2013	val = calloc(max_str_num, sizeof (char *));
2014	if (val == NULL) {
2015		nerr("Out of memory");
2016		return (NULL);
2017	}
2018
2019	if (is_list_prop) {
2020		char *tmp, *next;
2021		/*
2022		 * Break down input_str and save as array of sub strings.
2023		 * Set num as the number of the sub strings.
2024		 * Use nwam_tokenize_by_unescaped_delim() rather than strtok()
2025		 * because DELIMITER may be escaped
2026		 */
2027		tmp = (char *)input_str;
2028		while ((tmp = nwam_tokenize_by_unescaped_delim(tmp,
2029		    NWAM_VALUE_DELIMITER_CHAR, &next)) != NULL) {
2030			val[n++] = trim_quotes(tmp);
2031			tmp = next;
2032		}
2033	} else {
2034		val[n++] = trim_quotes(input_str);
2035	}
2036
2037	/* initialize int_vals or booleans_vals depending on pt_type */
2038	value_type = prop_value_type(object_type,
2039	    pt_to_prop_name(object_type, pt_type));
2040	if (value_type == NWAM_VALUE_TYPE_INT64) {
2041		int_vals = calloc(n, sizeof (int64_t));
2042		if (int_vals == NULL) {
2043			nerr("Out of memory");
2044			array_free((void **)val, max_str_num);
2045			return (NULL);
2046		}
2047	} else if (value_type == NWAM_VALUE_TYPE_UINT64) {
2048		uint_vals = calloc(n, sizeof (uint64_t));
2049		if (uint_vals == NULL) {
2050			nerr("Out of memory");
2051			array_free((void **)val, max_str_num);
2052			return (NULL);
2053		}
2054	} else if (value_type == NWAM_VALUE_TYPE_BOOLEAN) {
2055		boolean_vals = calloc(n, sizeof (boolean_t));
2056		if (boolean_vals == NULL) {
2057			nerr("Out of memory");
2058			array_free((void **)val, max_str_num);
2059			return (NULL);
2060		}
2061	}
2062	/* set the appropriate array */
2063	for (i = 0; i < n; i++) {
2064		switch (value_type) {
2065		case NWAM_VALUE_TYPE_STRING:
2066			/* nothing to do - val already has the char** array */
2067			break;
2068		case NWAM_VALUE_TYPE_INT64:
2069		{
2070			int_vals[i] = (int64_t)atoi(val[i]);
2071			break;
2072		}
2073		case NWAM_VALUE_TYPE_UINT64:
2074		{
2075			uint64_t str_as_enum;
2076			char *endptr;
2077
2078			ret = nwam_value_string_get_uint64(
2079			    pt_to_prop_name(object_type, pt_type),
2080			    val[i], &str_as_enum);
2081			/*
2082			 * Returns _SUCCESS if value for enum is valid.
2083			 * Returns _INVALID_ARG if property is not an enum.
2084			 */
2085			if (ret == NWAM_SUCCESS) {
2086				uint_vals[i] = str_as_enum;
2087			} else if (ret == NWAM_INVALID_ARG) {
2088				uint_vals[i] = strtoul(val[i], &endptr, 10);
2089				/* verify conversion is valid */
2090				if (endptr == val[i]) {
2091					free(uint_vals);
2092					array_free((void **)val, max_str_num);
2093					return (NULL);
2094				}
2095			} else {
2096				free(uint_vals);
2097				array_free((void **)val, max_str_num);
2098				return (NULL);
2099			}
2100			break;
2101		}
2102		case NWAM_VALUE_TYPE_BOOLEAN:
2103			boolean_vals[i] = str_to_boolean(val[i]);
2104			break;
2105		default:
2106			array_free((void **)val, max_str_num);
2107			return (NULL);
2108		}
2109	}
2110
2111	/* create nwam_value_t */
2112	if (value_type == NWAM_VALUE_TYPE_STRING) {
2113		ret = nwam_value_create_string_array(val, n, &data);
2114	} else if (value_type == NWAM_VALUE_TYPE_INT64) {
2115		ret = nwam_value_create_int64_array(int_vals, n, &data);
2116		free(int_vals);
2117	} else if (value_type == NWAM_VALUE_TYPE_UINT64) {
2118		ret = nwam_value_create_uint64_array(uint_vals, n, &data);
2119		free(uint_vals);
2120	} else if (value_type == NWAM_VALUE_TYPE_BOOLEAN) {
2121		ret = nwam_value_create_boolean_array(boolean_vals, n, &data);
2122		free(boolean_vals);
2123	}
2124	array_free((void **)val, max_str_num);
2125
2126	if (ret != NWAM_SUCCESS) {
2127		nwamerr(ret, "Failed creating nwam_value");
2128		return (NULL);
2129	}
2130
2131	return (data);
2132}
2133
2134/*
2135 * Displaying/Skipping of properties
2136 * ---------------------------------
2137 *
2138 * This table shows if a specific property should be shown if some
2139 * other property has a specific value.  This table is used by
2140 * show_prop_test(), which is called by set_func() and walkprop_func().
2141 *
2142 * An entry in the table looks like:
2143 *	{ property1, property2, { val1, val2, -1 } }
2144 * This is read as:
2145 *	"show property1 only if property2 has value val1 or val2"
2146 *
2147 * NB: If a property does not appear in this table, then that implies
2148 * that the property is always shown.
2149 *
2150 * A property can have more than one rule.  In such a case, the property is
2151 * displayed only any of the rules is satisfied.  This checking, however,
2152 * is recursive.  If a rule says that a property can be displayed, then the
2153 * property that's checked should also satisfy its rules.  In the above
2154 * example, if property1 is to be displayed, then property2 should also
2155 * satisfy its rules and be displayable.  This recursion is necessary as
2156 * properties that are not displayed (because rules are not satisfied) are
2157 * not deleted.
2158 */
2159
2160/* The most number of values in pde_checkvals below */
2161#define	NWAM_CHECKVALS_MAX	5
2162
2163typedef struct prop_display_entry {
2164	const char	*pde_name;		/* property to show */
2165	const char	*pde_checkname;		/* property to check */
2166	int64_t	pde_checkvals[NWAM_CHECKVALS_MAX]; /* show prop for these */
2167} prop_display_entry_t;
2168
2169/* Rules for showing properties: commented for clarity */
2170
2171/*
2172 * Rules for NCUs
2173 * NB: There is no need to have an entry if a property is for IP only.
2174 *     This is taken care of in libnwam_ncp.c
2175 */
2176static prop_display_entry_t ncu_prop_display_entry_table[] = {
2177	/* show priority-{group,mode} if activation == prioritized */
2178	{ NWAM_NCU_PROP_PRIORITY_GROUP, NWAM_NCU_PROP_ACTIVATION_MODE,
2179	    { NWAM_ACTIVATION_MODE_PRIORITIZED, -1 } },
2180	{ NWAM_NCU_PROP_PRIORITY_MODE, NWAM_NCU_PROP_ACTIVATION_MODE,
2181	    { NWAM_ACTIVATION_MODE_PRIORITIZED, -1 } },
2182	/* show ipv4-addrsrc if ip-version == ipv4 */
2183	{ NWAM_NCU_PROP_IPV4_ADDRSRC, NWAM_NCU_PROP_IP_VERSION,
2184	    { IPV4_VERSION, -1 } },
2185	/* show ipv4-addr if ipv4-addrsrc == static */
2186	{ NWAM_NCU_PROP_IPV4_ADDR, NWAM_NCU_PROP_IPV4_ADDRSRC,
2187	    { NWAM_ADDRSRC_STATIC, -1 } },
2188	/* show ipv4-default-route if ip-version == ipv4 */
2189	{ NWAM_NCU_PROP_IPV4_DEFAULT_ROUTE, NWAM_NCU_PROP_IP_VERSION,
2190	    { IPV4_VERSION, -1 } },
2191	/* show ipv6-addrsrc if ip-version == ipv6 */
2192	{ NWAM_NCU_PROP_IPV6_ADDRSRC, NWAM_NCU_PROP_IP_VERSION,
2193	    { IPV6_VERSION, -1 } },
2194	/* show ipv6-addr if ipv6-addrsrc == static */
2195	{ NWAM_NCU_PROP_IPV6_ADDR, NWAM_NCU_PROP_IPV6_ADDRSRC,
2196	    { NWAM_ADDRSRC_STATIC, -1 } },
2197	/* show ipv6-default-route if ip-version == ipv6 */
2198	{ NWAM_NCU_PROP_IPV6_DEFAULT_ROUTE, NWAM_NCU_PROP_IP_VERSION,
2199	    { IPV6_VERSION, -1 } },
2200	{ NULL, NULL, { -1 } }
2201};
2202
2203/* Rules for ENMs */
2204static prop_display_entry_t enm_prop_display_entry_table[] = {
2205	/* show conditions if activation-mode == conditional-{all,any} */
2206	{ NWAM_ENM_PROP_CONDITIONS, NWAM_ENM_PROP_ACTIVATION_MODE,
2207	    { NWAM_ACTIVATION_MODE_CONDITIONAL_ALL,
2208	    NWAM_ACTIVATION_MODE_CONDITIONAL_ANY, -1 } },
2209	{ NULL, NULL, { -1 } }
2210};
2211
2212/* Rules for LOCations */
2213static prop_display_entry_t loc_prop_display_entry_table[] = {
2214	/* show conditions if activation-mode == conditional-{all,any} */
2215	{ NWAM_LOC_PROP_CONDITIONS, NWAM_LOC_PROP_ACTIVATION_MODE,
2216	    { NWAM_ACTIVATION_MODE_CONDITIONAL_ALL,
2217	    NWAM_ACTIVATION_MODE_CONDITIONAL_ANY, -1 } },
2218	/* show dns-nameservice-configsrc if nameservices == dns */
2219	{ NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC, NWAM_LOC_PROP_NAMESERVICES,
2220	    { NWAM_NAMESERVICES_DNS, -1 } },
2221	/* show other DNS options if dns-nameservices-configsrc == manual */
2222	{ NWAM_LOC_PROP_DNS_NAMESERVICE_DOMAIN,
2223	    NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
2224	    { NWAM_CONFIGSRC_MANUAL, -1 } },
2225	{ NWAM_LOC_PROP_DNS_NAMESERVICE_SERVERS,
2226	    NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
2227	    { NWAM_CONFIGSRC_MANUAL, -1 } },
2228	{ NWAM_LOC_PROP_DNS_NAMESERVICE_SEARCH,
2229	    NWAM_LOC_PROP_DNS_NAMESERVICE_CONFIGSRC,
2230	    { NWAM_CONFIGSRC_MANUAL, -1 } },
2231	/* show nis-nameservice-configsrc if nameservices == nis */
2232	{ NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC, NWAM_LOC_PROP_NAMESERVICES,
2233	    { NWAM_NAMESERVICES_NIS, -1 } },
2234	/* show nis-nameservice-servers if nis-nameservice-configsrc = manual */
2235	{ NWAM_LOC_PROP_NIS_NAMESERVICE_SERVERS,
2236	    NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
2237	    { NWAM_CONFIGSRC_MANUAL, -1 } },
2238	/* show ldap-nameservice-configsrc if nameservices == ldap */
2239	{ NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC, NWAM_LOC_PROP_NAMESERVICES,
2240	    { NWAM_NAMESERVICES_LDAP, -1 } },
2241	/* show ldap-nameservice-servers if ldap-nameservice-configsrc=manual */
2242	{ NWAM_LOC_PROP_LDAP_NAMESERVICE_SERVERS,
2243	    NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
2244	    { NWAM_CONFIGSRC_MANUAL, -1 } },
2245	/* show default-domain if {nis,ldap}-nameservice-configsrc == manual */
2246	{ NWAM_LOC_PROP_DEFAULT_DOMAIN, NWAM_LOC_PROP_NIS_NAMESERVICE_CONFIGSRC,
2247	    { NWAM_CONFIGSRC_MANUAL, -1 } },
2248	{ NWAM_LOC_PROP_DEFAULT_DOMAIN,
2249	    NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
2250	    { NWAM_CONFIGSRC_MANUAL, -1 } },
2251	{ NULL, NULL, { -1 } }
2252};
2253
2254/* Rules for Known WLANs */
2255static prop_display_entry_t wlan_prop_display_entry_table[] = {
2256	/* no rules for WLANs */
2257	{ NULL, NULL, { -1 } }
2258};
2259
2260/* Returns the appropriate rules table for the given object type */
2261static prop_display_entry_t *
2262get_prop_display_table(nwam_object_type_t object_type)
2263{
2264	switch (object_type) {
2265	case NWAM_OBJECT_TYPE_NCU:
2266		return (ncu_prop_display_entry_table);
2267	case NWAM_OBJECT_TYPE_LOC:
2268		return (loc_prop_display_entry_table);
2269	case NWAM_OBJECT_TYPE_ENM:
2270		return (enm_prop_display_entry_table);
2271	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
2272		return (wlan_prop_display_entry_table);
2273	}
2274	return (NULL);
2275}
2276
2277/*
2278 * Tests whether prop must be shown during a walk depending on the
2279 * value of a different property.
2280 *
2281 * This function is also used by set_func() to determine whether the
2282 * property being set should be allowed or not.  If the property
2283 * would not be displayed in a walk, then it should not be set.
2284 *
2285 * The checked_props and num_checked arguments are used to avoid circular
2286 * dependencies between properties.  When this function recursively calls
2287 * itself, it adds the property that it just checked to the checked_props
2288 * list.
2289 */
2290static boolean_t
2291show_prop_test(nwam_object_type_t object_type, const char *prop,
2292    prop_display_entry_t *display_list, char **checked_props, int num_checked)
2293{
2294	nwam_error_t		ret;
2295	nwam_value_t		prop_val;
2296	nwam_value_type_t	prop_type;
2297	int			i, j, k;
2298	boolean_t		prop_found = B_FALSE, show_prop = B_FALSE;
2299
2300	/*
2301	 * Check if this property has already been checked previously in
2302	 * the recursion.  If so, return B_FALSE so that the initial prop
2303	 * is not displayed.
2304	 */
2305	for (i = 0; i < num_checked; i++) {
2306		if (strcmp(prop, checked_props[i]) == 0) {
2307			free(checked_props);
2308			return (B_FALSE);
2309		}
2310	}
2311
2312	for (i = 0; display_list[i].pde_name != NULL; i++) {
2313		if (strcmp(prop, display_list[i].pde_name) != 0)
2314			continue;
2315		prop_found = B_TRUE;
2316
2317		/* get the value(s) of the (other) property to check */
2318		switch (object_type) {
2319		case NWAM_OBJECT_TYPE_NCU:
2320			ret = nwam_ncu_get_prop_value(ncu_h,
2321			    display_list[i].pde_checkname, &prop_val);
2322			break;
2323		case NWAM_OBJECT_TYPE_LOC:
2324			ret = nwam_loc_get_prop_value(loc_h,
2325			    display_list[i].pde_checkname, &prop_val);
2326			break;
2327		case NWAM_OBJECT_TYPE_ENM:
2328			ret = nwam_enm_get_prop_value(enm_h,
2329			    display_list[i].pde_checkname, &prop_val);
2330			break;
2331		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
2332			return (B_TRUE);
2333		}
2334		if (ret != NWAM_SUCCESS)
2335			continue;
2336
2337		/* prop_val may contain a uint64 array or a boolean */
2338		if (nwam_value_get_type(prop_val, &prop_type) != NWAM_SUCCESS)
2339			continue;
2340
2341		if (prop_type == NWAM_VALUE_TYPE_UINT64) {
2342			uint64_t	*prop_uvals;
2343			int64_t		*check_uvals;
2344			uint_t		numvals;
2345
2346			if (nwam_value_get_uint64_array(prop_val, &prop_uvals,
2347			    &numvals) != NWAM_SUCCESS) {
2348				nwam_value_free(prop_val);
2349				continue;
2350			}
2351
2352			/* for each value in uvals, check each value in table */
2353			for (j = 0; j < numvals; j++) {
2354				check_uvals = display_list[i].pde_checkvals;
2355				for (k = 0; check_uvals[k] != -1; k++) {
2356					/* show if uvals[j] matches */
2357					if (prop_uvals[j] ==
2358					    (uint64_t)check_uvals[k]) {
2359						show_prop = B_TRUE;
2360						goto next_rule;
2361					}
2362				}
2363			}
2364		} else if (prop_type == NWAM_VALUE_TYPE_BOOLEAN) {
2365			boolean_t bval;
2366
2367			if (nwam_value_get_boolean(prop_val, &bval) !=
2368			    NWAM_SUCCESS) {
2369				nwam_value_free(prop_val);
2370				continue;
2371			}
2372
2373			for (k = 0;
2374			    display_list[i].pde_checkvals[k] != -1;
2375			    k++) {
2376				/* show if bval matches */
2377				if (bval == (boolean_t)
2378				    display_list[i].pde_checkvals[k]) {
2379					show_prop = B_TRUE;
2380					goto next_rule;
2381				}
2382			}
2383		}
2384
2385next_rule:
2386		nwam_value_free(prop_val);
2387		/*
2388		 * If show_prop is set, then a rule is satisfied; no need to
2389		 * check other rules for this prop.  However, recursively
2390		 * check if the checked prop (pde_checkname) satisfies its
2391		 * rules.  Also, update the check_props array with this prop.
2392		 */
2393		if (show_prop) {
2394			char **newprops = realloc(checked_props,
2395			    ++num_checked * sizeof (char *));
2396			if (newprops == NULL) {
2397				free(checked_props);
2398				return (B_FALSE);
2399			}
2400			checked_props = newprops;
2401			checked_props[num_checked - 1] = (char *)prop;
2402
2403			return (show_prop_test(object_type,
2404			    display_list[i].pde_checkname, display_list,
2405			    checked_props, num_checked));
2406		}
2407	}
2408
2409	/*
2410	 * If we are here and prop_found is set, it means that no rules were
2411	 * satisfied by prop; return B_FALSE.  If prop_found is not set, then
2412	 * prop did not have a rule so it must be displayed; return B_TRUE.
2413	 */
2414	free(checked_props);
2415	if (prop_found)
2416		return (B_FALSE);
2417	else
2418		return (B_TRUE);
2419}
2420
2421/*
2422 * Returns true if the given property is read-only and cannot be modified.
2423 */
2424static boolean_t
2425is_prop_read_only(nwam_object_type_t object_type, const char *prop)
2426{
2427	boolean_t ro;
2428
2429	switch (object_type) {
2430	case NWAM_OBJECT_TYPE_NCU:
2431		if (nwam_ncu_prop_read_only(prop, &ro) == NWAM_SUCCESS && ro)
2432			return (B_TRUE);
2433		break;
2434	case NWAM_OBJECT_TYPE_ENM:
2435		if (nwam_enm_prop_read_only(prop, &ro) == NWAM_SUCCESS && ro)
2436			return (B_TRUE);
2437		break;
2438	case NWAM_OBJECT_TYPE_LOC:
2439		if (nwam_loc_prop_read_only(prop, &ro) == NWAM_SUCCESS && ro)
2440			return (B_TRUE);
2441		break;
2442	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
2443		/* no read-only properties for WLANs */
2444		return (B_FALSE);
2445	}
2446	return (B_FALSE);
2447}
2448
2449/* Returns true if the property is multi-valued */
2450static boolean_t
2451is_prop_multivalued(nwam_object_type_t object_type, const char *prop)
2452{
2453	nwam_error_t	ret;
2454	boolean_t	multi;
2455
2456	switch (object_type) {
2457	case NWAM_OBJECT_TYPE_NCU:
2458		ret = nwam_ncu_prop_multivalued(prop, &multi);
2459		break;
2460	case NWAM_OBJECT_TYPE_LOC:
2461		ret = nwam_loc_prop_multivalued(prop, &multi);
2462		break;
2463	case NWAM_OBJECT_TYPE_ENM:
2464		ret = nwam_enm_prop_multivalued(prop, &multi);
2465		break;
2466	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
2467		ret = nwam_known_wlan_prop_multivalued(prop, &multi);
2468		break;
2469	}
2470
2471	if (ret != NWAM_SUCCESS)
2472		multi = B_FALSE;
2473	return (multi);
2474}
2475
2476/*
2477 * Prints out error message specific to property that could not be set.
2478 * Property description is used to help guide user in entering correct value.
2479 */
2480static void
2481invalid_set_prop_msg(const char *prop, nwam_error_t err)
2482{
2483	const char *description;
2484
2485	if (err == NWAM_SUCCESS)
2486		return;
2487
2488	if (err != NWAM_ENTITY_INVALID_VALUE) {
2489		nwamerr(err, "Set error");
2490		return;
2491	}
2492
2493	switch (active_object_type()) {
2494	case NWAM_OBJECT_TYPE_NCU:
2495		(void) nwam_ncu_get_prop_description(prop, &description);
2496		break;
2497	case NWAM_OBJECT_TYPE_LOC:
2498		(void) nwam_loc_get_prop_description(prop, &description);
2499		break;
2500	case NWAM_OBJECT_TYPE_ENM:
2501		(void) nwam_enm_get_prop_description(prop, &description);
2502		break;
2503	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
2504		(void) nwam_known_wlan_get_prop_description(prop,
2505		    &description);
2506		break;
2507	}
2508	nerr("Set error: invalid value\n'%s' %s", prop, description);
2509}
2510
2511/*
2512 * Sets the property value.
2513 * Read-only properties and objects cannot be set.
2514 * "read-only" is a special in that it can be set on a read-only object.
2515 * The object has to be committed before other properties can be set.
2516 * Also uses show_prop_test() to test if the property being set would
2517 * be skipped during a walk (as determined by the value of some other
2518 * property).  If so, then it cannot be set.
2519 */
2520void
2521set_func(cmd_t *cmd)
2522{
2523	int			pt_type = cmd->cmd_prop_type;
2524	nwam_error_t		ret = NWAM_SUCCESS;
2525	nwam_value_t		prop_value;
2526	const char		*prop;
2527	boolean_t		is_listprop = B_FALSE;
2528	nwam_object_type_t	object_type;
2529	prop_display_entry_t	*prop_table;
2530	char			**checked = NULL;
2531
2532	assert(cmd->cmd_argc > 0);
2533
2534	object_type = active_object_type();
2535	prop_table = get_prop_display_table(object_type);
2536
2537	/* argv[0] is property value */
2538	if ((prop = pt_to_prop_name(object_type, pt_type)) == NULL) {
2539		nerr("Set error: invalid %s property: '%s'",
2540		    scope_to_str(current_scope), pt_to_str(pt_type));
2541		return;
2542	}
2543
2544	/* check if property can be set */
2545	if (is_prop_read_only(object_type, prop)) {
2546		nerr("Set error: property '%s' is read-only", prop);
2547		return;
2548	}
2549	if (!show_prop_test(object_type, prop, prop_table, checked, 0)) {
2550		if (interactive_mode) {
2551			(void) printf(gettext("setting property '%s' "
2552			    "has no effect\n"), prop);
2553		}
2554	}
2555
2556	is_listprop = is_prop_multivalued(object_type, prop);
2557	prop_value = str_to_nwam_value(object_type, cmd->cmd_argv[0], pt_type,
2558	    is_listprop);
2559	if (prop_value == NULL) {
2560		invalid_set_prop_msg(prop, NWAM_ENTITY_INVALID_VALUE);
2561		return;
2562	}
2563
2564	/* set the property value */
2565	switch (object_type) {
2566	case NWAM_OBJECT_TYPE_NCU:
2567		ret = nwam_ncu_set_prop_value(ncu_h, prop, prop_value);
2568		break;
2569	case NWAM_OBJECT_TYPE_LOC:
2570		ret = nwam_loc_set_prop_value(loc_h, prop, prop_value);
2571		break;
2572	case NWAM_OBJECT_TYPE_ENM:
2573		ret = nwam_enm_set_prop_value(enm_h, prop, prop_value);
2574		break;
2575	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
2576		ret = nwam_known_wlan_set_prop_value(wlan_h, prop, prop_value);
2577		break;
2578	}
2579	nwam_value_free(prop_value);
2580
2581	/* delete other properties if needed */
2582	if (ret == NWAM_SUCCESS)
2583		need_to_commit = B_TRUE;
2584	else
2585		invalid_set_prop_msg(prop, ret);
2586}
2587
2588static int
2589list_callback(nwam_object_type_t object_type, void *handle,
2590    boolean_t *list_msgp, const char *msg)
2591{
2592	nwam_error_t		ret;
2593	char			*name;
2594	nwam_ncu_class_t	class;
2595
2596	if (*list_msgp) {
2597		(void) printf("%s:\n", msg);
2598		*list_msgp = B_FALSE;
2599	}
2600
2601	ret = object_name_from_handle(object_type, handle, &name);
2602	if (ret != NWAM_SUCCESS) {
2603		nwamerr(ret, "List error: failed to get name");
2604		return (1);
2605	}
2606
2607	/* If NCU, get its class and print */
2608	if (object_type == NWAM_OBJECT_TYPE_NCU) {
2609		if ((ret = nwam_ncu_get_ncu_class(handle, &class))
2610		    != NWAM_SUCCESS) {
2611			nwamerr(ret, "List error: failed to get ncu class");
2612			free(name);
2613			return (1);
2614		} else {
2615			(void) printf("\t%s",
2616			    propval_to_str(NWAM_NCU_PROP_CLASS, class));
2617		}
2618	}
2619	(void) printf("\t%s\n", name);
2620
2621	free(name);
2622	return (0);
2623}
2624
2625/* Print out name, type and status */
2626static int
2627list_loc_callback(nwam_loc_handle_t loc, void *arg)
2628{
2629	return (list_callback(NWAM_OBJECT_TYPE_LOC, loc, arg, "Locations"));
2630}
2631
2632static int
2633list_enm_callback(nwam_enm_handle_t enm, void *arg)
2634{
2635	return (list_callback(NWAM_OBJECT_TYPE_ENM, enm, arg, "ENMs"));
2636}
2637
2638static int
2639list_wlan_callback(nwam_known_wlan_handle_t wlan, void *arg)
2640{
2641	return (list_callback(NWAM_OBJECT_TYPE_KNOWN_WLAN, wlan, arg, "WLANs"));
2642}
2643
2644static int
2645list_ncp_callback(nwam_ncp_handle_t ncp, void *arg)
2646{
2647	return (list_callback(NWAM_OBJECT_TYPE_NCP, ncp, arg, "NCPs"));
2648}
2649
2650static int
2651list_ncu_callback(nwam_ncu_handle_t ncu, void *arg)
2652{
2653	return (list_callback(NWAM_OBJECT_TYPE_NCU, ncu, arg, "NCUs"));
2654}
2655
2656/* functions to convert a value to a string */
2657/* ARGSUSED */
2658static const char *
2659str2str(void *s, const char *prop, char *str)
2660{
2661	(void) snprintf(str, NWAM_MAX_VALUE_LEN, "%s", s);
2662	return (str);
2663}
2664
2665/* ARGSUSED */
2666static const char *
2667str2qstr(void *s, const char *prop, char *qstr)
2668{
2669	/* quoted strings */
2670	(void) snprintf(qstr, NWAM_MAX_VALUE_LEN, "\"%s\"", s);
2671	return (qstr);
2672}
2673
2674/* ARGSUSED */
2675static const char *
2676int2str(void *in, const char *prop, char *instr)
2677{
2678	(void) snprintf(instr, NWAM_MAX_VALUE_LEN, "%lld", *((int64_t *)in));
2679	return (instr);
2680}
2681
2682static const char *
2683uint2str(void *uin, const char *prop, char *uintstr)
2684{
2685	/* returns NWAM_SUCCESS if prop is enum with string in uintstr */
2686	if (nwam_uint64_get_value_string(prop, *((uint64_t *)uin),
2687	    (const char **)&uintstr) != NWAM_SUCCESS) {
2688		(void) snprintf(uintstr, NWAM_MAX_VALUE_LEN, "%lld",
2689		    *((uint64_t *)uin));
2690	}
2691	return (uintstr);
2692}
2693
2694/* ARGSUSED */
2695static const char *
2696bool2str(void *bool, const char *prop, char *boolstr)
2697{
2698	(void) snprintf(boolstr, NWAM_MAX_VALUE_LEN, "%s",
2699	    *((boolean_t *)bool) ? "true" : "false");
2700	return (boolstr);
2701}
2702
2703/*
2704 * Print the value (enums are converted to string), use DELIMITER for
2705 * array.  If strings are to be "quoted", pass B_TRUE for quoted_strings.
2706 */
2707static void
2708output_prop_val(const char *prop_name, nwam_value_t value, FILE *wf,
2709    boolean_t quoted_strings)
2710{
2711	nwam_value_type_t	value_type;
2712	uint_t			num;
2713
2714	/* arrays for values retrieved according to the type of value */
2715	char		**svals;
2716	uint64_t	*uvals;
2717	int64_t		*ivals;
2718	boolean_t	*bvals;
2719
2720	/* pointer to function to generate string representation of value */
2721	const char	*(*tostr)(void *, const char *, char *);
2722	char		str[NWAM_MAX_VALUE_LEN]; /* to store the string */
2723	int		i;
2724
2725	if (nwam_value_get_type(value, &value_type) != NWAM_SUCCESS) {
2726		nerr("Get value type error");
2727		return;
2728	}
2729
2730	if (value_type == NWAM_VALUE_TYPE_STRING) {
2731		if (nwam_value_get_string_array(value, &svals, &num) !=
2732		    NWAM_SUCCESS) {
2733			nerr("Get string array error");
2734			return;
2735		}
2736		tostr = quoted_strings ? str2qstr : str2str;
2737	} else if (value_type == NWAM_VALUE_TYPE_INT64) {
2738		if (nwam_value_get_int64_array(value, &ivals, &num) !=
2739		    NWAM_SUCCESS) {
2740			nerr("Get int64 array error");
2741			return;
2742		}
2743		tostr = int2str;
2744	} else if (value_type == NWAM_VALUE_TYPE_UINT64) {
2745		if (nwam_value_get_uint64_array(value, &uvals, &num) !=
2746		    NWAM_SUCCESS) {
2747			nerr("Get uint64 array error");
2748			return;
2749		}
2750		tostr = uint2str;
2751	} else if (value_type == NWAM_VALUE_TYPE_BOOLEAN) {
2752		if (nwam_value_get_boolean_array(value, &bvals, &num) !=
2753		    NWAM_SUCCESS) {
2754			nerr("Get boolean array error");
2755			return;
2756		}
2757		tostr = bool2str;
2758	}
2759
2760	/* now, loop and print each value */
2761	for (i = 0; i < num; i++) {
2762		void *val;
2763
2764		/* get the pointer to the ith value to pass to func() */
2765		if (value_type == NWAM_VALUE_TYPE_STRING)
2766			val = svals[i];
2767		else if (value_type == NWAM_VALUE_TYPE_UINT64)
2768			val = &(uvals[i]);
2769		else if (value_type == NWAM_VALUE_TYPE_INT64)
2770			val = &(ivals[i]);
2771		else if (value_type == NWAM_VALUE_TYPE_BOOLEAN)
2772			val = &(bvals[i]);
2773
2774		(void) fprintf(wf, "%s%s", tostr(val, prop_name, str),
2775		    i != num-1 ? NWAM_VALUE_DELIMITER_STR : "");
2776	}
2777}
2778
2779/* Prints the property names aligned (for list/get) or "prop=" (for export) */
2780static int
2781output_propname_common(const char *prop, nwam_value_t values, void *arg,
2782    int width)
2783{
2784	FILE *of = (arg == NULL) ? stdout : arg;
2785
2786	/* arg is NULL for list/get, not NULL for export */
2787	if (arg == NULL)
2788		(void) fprintf(of, "\t%-*s\t", width, prop);
2789	else
2790		(void) fprintf(of, "%s=", prop);
2791
2792	if (values != NULL)
2793		output_prop_val(prop, values, of, B_TRUE);
2794
2795	(void) fprintf(of, "\n");
2796	return (0);
2797}
2798
2799static int
2800output_propname(const char *prop, nwam_value_t values, void *arg)
2801{
2802	return (output_propname_common(prop, values, arg, 16));
2803}
2804
2805/* For locations because of longer property names */
2806static int
2807output_loc_propname(const char *prop, nwam_value_t values, void *arg)
2808{
2809	return (output_propname_common(prop, values, arg, 25));
2810}
2811
2812/*
2813 * all_props specifies whether properties that have not been set should be
2814 * printed or not.  ncp and ncu_type are used only when the object_type is
2815 * NCU.
2816 */
2817static nwam_error_t
2818listprop(nwam_object_type_t object_type, void *handle, const char *name,
2819    boolean_t all_props, nwam_ncp_handle_t ncp, nwam_ncu_type_t ncu_type)
2820{
2821	nwam_error_t	ret;
2822	char		*lname = NULL, *realname = NULL;
2823	boolean_t	lhandle = B_FALSE;
2824	const char	**props = NULL;
2825	uint_t		prop_num;
2826	int		i;
2827	nwam_value_t	vals;
2828
2829	/*
2830	 * handle is NULL if called from a scope higher than the object's
2831	 * scope, but name must be given; so get the handle.
2832	 */
2833	if (handle == NULL) {
2834		lname = trim_quotes(name); /* name may have quotes */
2835		switch (object_type) {
2836		case NWAM_OBJECT_TYPE_NCP:
2837			if ((ret = nwam_ncp_read(lname, 0,
2838			    (nwam_ncp_handle_t *)&handle)) != NWAM_SUCCESS)
2839				goto readfail;
2840			break;
2841		case NWAM_OBJECT_TYPE_NCU:
2842			ret = nwam_ncu_read(ncp, lname, ncu_type, 0,
2843			    (nwam_ncu_handle_t *)&handle);
2844			if (ret == NWAM_ENTITY_MULTIPLE_VALUES) {
2845				/*
2846				 * Multiple NCUs with the given name exists.
2847				 * Call listprop() for each NCU type.
2848				 */
2849				if ((ret = listprop(object_type, NULL, lname,
2850				    all_props, ncp, NWAM_NCU_TYPE_LINK))
2851				    != NWAM_SUCCESS)
2852					goto done;
2853				ret = listprop(object_type, NULL, lname,
2854				    all_props, ncp, NWAM_NCU_TYPE_INTERFACE);
2855				goto done;
2856			} else if (ret != NWAM_SUCCESS) {
2857				goto readfail;
2858			}
2859			break;
2860		case NWAM_OBJECT_TYPE_LOC:
2861			if ((ret = nwam_loc_read(lname, 0,
2862			    (nwam_loc_handle_t *)&handle)) != NWAM_SUCCESS)
2863				goto readfail;
2864			break;
2865		case NWAM_OBJECT_TYPE_ENM:
2866			if ((ret = nwam_enm_read(lname, 0,
2867			    (nwam_enm_handle_t *)&handle)) != NWAM_SUCCESS)
2868				goto readfail;
2869			break;
2870		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
2871			if ((ret = nwam_known_wlan_read(lname, 0,
2872			    (nwam_known_wlan_handle_t *)&handle))
2873			    != NWAM_SUCCESS)
2874				goto readfail;
2875			break;
2876		}
2877		lhandle = B_TRUE;
2878	}
2879
2880	if ((ret = object_name_from_handle(object_type, handle, &realname))
2881	    != NWAM_SUCCESS)
2882		goto done;
2883
2884	/* get the property list */
2885	switch (object_type) {
2886	case NWAM_OBJECT_TYPE_NCP:
2887	{
2888		/* walk NCUs */
2889		boolean_t list_msg = B_TRUE;
2890		ret = nwam_ncp_walk_ncus(handle, list_ncu_callback, &list_msg,
2891		    NWAM_FLAG_NCU_TYPE_CLASS_ALL, NULL);
2892		goto done;
2893	}
2894	case NWAM_OBJECT_TYPE_NCU:
2895	{
2896		nwam_ncu_type_t		ncu_type;
2897		nwam_ncu_class_t	ncu_class;
2898
2899		if ((ret = nwam_ncu_get_ncu_type(handle, &ncu_type))
2900		    != NWAM_SUCCESS)
2901			goto done;
2902		if ((ret = nwam_ncu_get_ncu_class(handle, &ncu_class))
2903		    != NWAM_SUCCESS)
2904			goto done;
2905
2906		ret = nwam_ncu_get_default_proplist(ncu_type, ncu_class, &props,
2907		    &prop_num);
2908		break;
2909	}
2910	case NWAM_OBJECT_TYPE_LOC:
2911		ret = nwam_loc_get_default_proplist(&props, &prop_num);
2912		break;
2913	case NWAM_OBJECT_TYPE_ENM:
2914		ret = nwam_enm_get_default_proplist(&props, &prop_num);
2915		break;
2916	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
2917		ret = nwam_known_wlan_get_default_proplist(&props, &prop_num);
2918		break;
2919	}
2920	if (ret != NWAM_SUCCESS)
2921		goto done;
2922
2923	/* print object type and name */
2924	(void) printf("%s:%s\n", nwam_object_type_to_string(object_type),
2925	    realname);
2926
2927	/* Loop through the properties and print */
2928	for (i = 0; i < prop_num; i++) {
2929		/* get the existing value for this property */
2930		switch (object_type) {
2931		case NWAM_OBJECT_TYPE_NCU:
2932			ret = nwam_ncu_get_prop_value(handle, props[i], &vals);
2933			break;
2934		case NWAM_OBJECT_TYPE_LOC:
2935			ret = nwam_loc_get_prop_value(handle, props[i], &vals);
2936			break;
2937		case NWAM_OBJECT_TYPE_ENM:
2938			ret = nwam_enm_get_prop_value(handle, props[i], &vals);
2939			break;
2940		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
2941			ret = nwam_known_wlan_get_prop_value(handle, props[i],
2942			    &vals);
2943			break;
2944		}
2945		if (ret != NWAM_SUCCESS) {
2946			/* _ENTITY_NOT_FOUND is ok if listing for all props */
2947			if (!all_props)
2948				continue;
2949			else if (ret != NWAM_ENTITY_NOT_FOUND)
2950				continue;
2951		}
2952
2953		/* print property and value */
2954		if (object_type == NWAM_OBJECT_TYPE_LOC)
2955			output_loc_propname(props[i], vals, NULL);
2956		else
2957			output_propname(props[i], vals, NULL);
2958		nwam_value_free(vals);
2959	}
2960
2961done:
2962	free(lname);
2963	free(realname);
2964	if (props != NULL)
2965		free(props);
2966	if (lhandle) {
2967		switch (object_type) {
2968		case NWAM_OBJECT_TYPE_NCP:
2969			nwam_ncp_free(handle);
2970			break;
2971		case NWAM_OBJECT_TYPE_NCU:
2972			nwam_ncu_free(handle);
2973			break;
2974		case NWAM_OBJECT_TYPE_LOC:
2975			nwam_loc_free(handle);
2976			break;
2977		case NWAM_OBJECT_TYPE_ENM:
2978			nwam_enm_free(handle);
2979			break;
2980		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
2981			nwam_known_wlan_free(handle);
2982			break;
2983		}
2984	}
2985	/* don't treat _ENTITY_NOT_FOUND as an error */
2986	if (ret == NWAM_ENTITY_NOT_FOUND)
2987		ret = NWAM_SUCCESS;
2988	return (ret);
2989
2990readfail:
2991	/* When nwam_*_read() fails */
2992	free(lname);
2993	return (ret);
2994}
2995
2996/*
2997 * List profiles or property and its values.
2998 * If the -a option is specified, all properties are listed.
2999 */
3000void
3001list_func(cmd_t *cmd)
3002{
3003	nwam_error_t	ret = NWAM_SUCCESS;
3004	boolean_t	list_msg = B_TRUE;
3005
3006	boolean_t	list_loc = B_FALSE, list_enm = B_FALSE;
3007	boolean_t	list_ncp = B_FALSE, list_ncu = B_FALSE;
3008	boolean_t	list_wlan = B_FALSE;
3009
3010	/* whether all properties should be listed, given by the -a option */
3011	boolean_t	all_props = B_FALSE;
3012
3013	/*
3014	 * list_props says whether the properties should be listed.
3015	 * Note that, here NCUs are treated as properties of NCPs.
3016	 */
3017	boolean_t	list_props = B_FALSE;
3018
3019	/* determine which properties to list, also validity tests */
3020	if (current_scope == NWAM_SCOPE_GBL) {
3021		/* res1_type is -1 if only "list -a" is used */
3022		if (cmd->cmd_res1_type == -1) {
3023			nerr("'list' requires an object to be specified with "
3024			    "the -a option in the global scope");
3025			return;
3026		}
3027		if (cmd->cmd_res1_type == RT1_LOC) {
3028			list_props = B_TRUE;
3029			list_loc = B_TRUE;
3030		} else if (cmd->cmd_res1_type == RT1_ENM) {
3031			list_props = B_TRUE;
3032			list_enm = B_TRUE;
3033		} else if (cmd->cmd_res1_type == RT1_WLAN) {
3034			list_props = B_TRUE;
3035			list_wlan = B_TRUE;
3036		} else if (cmd->cmd_res1_type == RT1_NCP) {
3037			list_ncp = B_TRUE;
3038			list_props = B_TRUE;
3039		} else {
3040			list_loc = B_TRUE;
3041			list_enm = B_TRUE;
3042			list_wlan = B_TRUE;
3043			list_ncp = B_TRUE;
3044		}
3045	}
3046	if ((current_scope == NWAM_SCOPE_LOC ||
3047	    current_scope == NWAM_SCOPE_ENM ||
3048	    current_scope == NWAM_SCOPE_WLAN ||
3049	    current_scope == NWAM_SCOPE_NCU) &&
3050	    (cmd->cmd_argc >= 1 && cmd->cmd_res1_type != -1)) {
3051		nerr("Additional options are not allowed with the -a option "
3052		    "at this scope");
3053		return;
3054	}
3055	if (current_scope == NWAM_SCOPE_LOC) {
3056		list_loc = B_TRUE;
3057		list_props = B_TRUE;
3058	}
3059	if (current_scope == NWAM_SCOPE_ENM) {
3060		list_enm = B_TRUE;
3061		list_props = B_TRUE;
3062	}
3063	if (current_scope == NWAM_SCOPE_WLAN) {
3064		list_wlan = B_TRUE;
3065		list_props = B_TRUE;
3066	}
3067	if (current_scope == NWAM_SCOPE_NCP) {
3068		if (cmd->cmd_res1_type == RT1_ENM ||
3069		    cmd->cmd_res1_type == RT1_LOC ||
3070		    cmd->cmd_res1_type == RT1_WLAN) {
3071			nerr("only ncu can be listed at this scope");
3072			return;
3073		}
3074		if (cmd->cmd_res2_type == RT2_NCU) {
3075			list_ncu = B_TRUE;
3076			list_props = B_TRUE;
3077		} else {
3078			list_ncp = B_TRUE;
3079			list_props = B_TRUE;
3080		}
3081	}
3082	if (current_scope == NWAM_SCOPE_NCU) {
3083		list_ncu = B_TRUE;
3084		list_props = B_TRUE;
3085	}
3086
3087	/* Check if the -a option is specified to list all properties */
3088	if (cmd->cmd_res1_type == -1 || cmd->cmd_argc == 2) {
3089		int c, argc = 1;
3090		char **argv;
3091		optind = 0;
3092
3093		/* if res1_type is -1, option is in argv[0], else in argv[1] */
3094		if (cmd->cmd_res1_type == -1)
3095			argv = cmd->cmd_argv;
3096		else
3097			argv = &(cmd->cmd_argv[1]);
3098		while ((c = getopt(argc, argv, "a")) != EOF) {
3099			switch (c) {
3100			case 'a':
3101				all_props = B_TRUE;
3102				break;
3103			default:
3104				command_usage(CMD_LIST);
3105				return;
3106			}
3107		}
3108		if (cmd->cmd_res1_type == -1)
3109			cmd->cmd_argv[0] = NULL;
3110	}
3111
3112	/*
3113	 * Now, print objects and/or according to the flags set.
3114	 * name, if requested, is in argv[0].
3115	 */
3116	if (list_ncp) {
3117		list_msg = B_TRUE;
3118		if (list_props) {
3119			ret = listprop(NWAM_OBJECT_TYPE_NCP, ncp_h,
3120			    cmd->cmd_argv[0], all_props, NULL, -1);
3121		} else {
3122			ret = nwam_walk_ncps(list_ncp_callback, &list_msg, 0,
3123			    NULL);
3124		}
3125		if (ret != NWAM_SUCCESS)
3126			goto done;
3127	}
3128
3129	if (list_ncu) {
3130		list_msg = B_TRUE;
3131		if (ncp_h == NULL) {
3132			nerr("NCP has not been read");
3133			return;
3134		}
3135		if (list_props) {
3136			nwam_ncu_class_t	ncu_class;
3137			nwam_ncu_type_t		ncu_type;
3138
3139			/* determine the NCU type first */
3140			if (ncu_h == NULL) {
3141				ncu_class = (nwam_ncu_class_t)
3142				    cmd->cmd_ncu_class_type;
3143				ncu_type = nwam_ncu_class_to_type(ncu_class);
3144			} else {
3145				if ((ret = nwam_ncu_get_ncu_type(ncu_h,
3146				    &ncu_type)) != NWAM_SUCCESS)
3147					goto done;
3148			}
3149			ret = listprop(NWAM_OBJECT_TYPE_NCU, ncu_h,
3150			    cmd->cmd_argv[0], all_props, ncp_h, ncu_type);
3151			if (ret != NWAM_SUCCESS)
3152				goto done;
3153		}
3154	}
3155
3156	if (list_loc) {
3157		list_msg = B_TRUE;
3158		if (list_props) {
3159			ret = listprop(NWAM_OBJECT_TYPE_LOC, loc_h,
3160			    cmd->cmd_argv[0], all_props, NULL, -1);
3161		} else {
3162			ret = nwam_walk_locs(list_loc_callback, &list_msg,
3163			    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
3164		}
3165		if (ret != NWAM_SUCCESS)
3166			goto done;
3167	}
3168
3169	if (list_enm) {
3170		list_msg = B_TRUE;
3171		if (list_props) {
3172			ret = listprop(NWAM_OBJECT_TYPE_ENM, enm_h,
3173			    cmd->cmd_argv[0], all_props, NULL, -1);
3174		} else {
3175			ret = nwam_walk_enms(list_enm_callback, &list_msg,
3176			    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
3177		}
3178		if (ret != NWAM_SUCCESS)
3179			goto done;
3180	}
3181
3182	if (list_wlan) {
3183		list_msg = B_TRUE;
3184		if (list_props) {
3185			ret = listprop(NWAM_OBJECT_TYPE_KNOWN_WLAN, wlan_h,
3186			    cmd->cmd_argv[0], all_props, NULL, -1);
3187		} else {
3188			ret = nwam_walk_known_wlans(list_wlan_callback,
3189			    &list_msg, NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER,
3190			    NULL);
3191		}
3192		if (ret != NWAM_SUCCESS)
3193			goto done;
3194	}
3195
3196done:
3197	if (ret != NWAM_SUCCESS)
3198		nwamerr(ret, "List error");
3199}
3200
3201static int
3202write_export_command(nwam_object_type_t object_type, const char *prop,
3203    nwam_value_t values, FILE *of)
3204{
3205	/* exclude read-only properties */
3206	if (is_prop_read_only(object_type, prop))
3207		return (0);
3208
3209	(void) fprintf(of, "set ");
3210	output_propname(prop, values, of);
3211	return (0);
3212}
3213
3214static int
3215export_ncu_callback(nwam_ncu_handle_t ncu, void *arg)
3216{
3217	char		*name;
3218	const char	**props;
3219	nwam_ncu_type_t type;
3220	nwam_ncu_class_t class;
3221	nwam_value_t	vals;
3222	nwam_error_t	ret;
3223	uint_t		num;
3224	int		i;
3225	FILE		*of = arg;
3226
3227	assert(of != NULL);
3228
3229	/* get the NCU's type and class */
3230	if ((ret = nwam_ncu_get_ncu_type(ncu, &type)) != NWAM_SUCCESS)
3231		return (ret);
3232	if ((ret = nwam_ncu_get_ncu_class(ncu, &class)) != NWAM_SUCCESS)
3233		return (ret);
3234
3235	if ((ret = nwam_ncu_get_name(ncu, &name)) != NWAM_SUCCESS)
3236		return (ret);
3237
3238	(void) fprintf(of, "create ncu %s \"%s\"\n",
3239	    propval_to_str(NWAM_NCU_PROP_CLASS, class), name);
3240	free(name);
3241	/*
3242	 * Because of dependencies between properties, they have to be
3243	 * exported in the same order as when they are walked.
3244	 */
3245	if ((ret = nwam_ncu_get_default_proplist(type, class, &props, &num))
3246	    != NWAM_SUCCESS)
3247		return (ret);
3248	for (i = 0; i < num; i++) {
3249		ret = nwam_ncu_get_prop_value(ncu, props[i], &vals);
3250		if (ret == NWAM_SUCCESS) {
3251			write_export_command(NWAM_OBJECT_TYPE_NCU, props[i],
3252			    vals, of);
3253			nwam_value_free(vals);
3254		}
3255	}
3256	(void) fprintf(of, "end\n");
3257
3258	free(props);
3259	return (0);
3260}
3261
3262static int
3263export_ncp_callback(nwam_ncp_handle_t ncp, void *arg)
3264{
3265	char		*name;
3266	nwam_error_t	ret;
3267	FILE		*of = arg;
3268
3269	assert(of != NULL);
3270
3271	if ((ret = nwam_ncp_get_name(ncp, &name)) != NWAM_SUCCESS)
3272		return (ret);
3273
3274	/* Do not export "automatic" NCP */
3275	if (NWAM_NCP_AUTOMATIC(name)) {
3276		free(name);
3277		return (0);
3278	}
3279
3280	(void) fprintf(of, "create ncp \"%s\"\n", name);
3281	free(name);
3282
3283	/* now walk NCUs for this ncp */
3284	ret = nwam_ncp_walk_ncus(ncp, export_ncu_callback, of,
3285	    NWAM_FLAG_NCU_TYPE_CLASS_ALL, NULL);
3286	if (ret != NWAM_SUCCESS) {
3287		nwamerr(ret, "Export ncp error: failed to walk ncus");
3288		return (ret);
3289	}
3290	(void) fprintf(of, "end\n");
3291	return (0);
3292}
3293
3294static int
3295export_enm_callback(nwam_enm_handle_t enm, void *arg)
3296{
3297	char		*name;
3298	const char	**props;
3299	nwam_value_t	vals;
3300	nwam_error_t	ret;
3301	uint_t		num;
3302	int		i;
3303	FILE		*of = arg;
3304
3305	assert(of != NULL);
3306
3307	if ((ret = nwam_enm_get_name(enm, &name)) != NWAM_SUCCESS)
3308		return (ret);
3309
3310	(void) fprintf(of, "create enm \"%s\"\n", name);
3311	free(name);
3312	/*
3313	 * Because of dependencies between properties, they have to be
3314	 * exported in the same order as when they are walked.
3315	 */
3316	if ((ret = nwam_enm_get_default_proplist(&props, &num)) != NWAM_SUCCESS)
3317		return (ret);
3318	for (i = 0; i < num; i++) {
3319		ret = nwam_enm_get_prop_value(enm, props[i], &vals);
3320		if (ret == NWAM_SUCCESS) {
3321			write_export_command(NWAM_OBJECT_TYPE_ENM, props[i],
3322			    vals, of);
3323			nwam_value_free(vals);
3324		}
3325	}
3326	(void) fprintf(of, "end\n");
3327
3328	free(props);
3329	return (0);
3330}
3331
3332static int
3333export_loc_callback(nwam_loc_handle_t loc, void *arg)
3334{
3335	char		*name;
3336	const char	**props;
3337	nwam_value_t	vals;
3338	nwam_error_t	ret;
3339	uint_t		num;
3340	int		i;
3341	FILE		*of = arg;
3342
3343	assert(of != NULL);
3344
3345	if ((ret = nwam_loc_get_name(loc, &name)) != NWAM_SUCCESS)
3346		return (ret);
3347
3348	/* Do not export Automatic, NoNet or Legacy locations */
3349	if (NWAM_LOC_NAME_PRE_DEFINED(name)) {
3350		free(name);
3351		return (0);
3352	}
3353
3354	(void) fprintf(of, "create loc \"%s\"\n", name);
3355	free(name);
3356	/*
3357	 * Because of dependencies between properties, they have to be
3358	 * exported in the same order as when they are walked.
3359	 */
3360	if ((ret = nwam_loc_get_default_proplist(&props, &num)) != NWAM_SUCCESS)
3361		return (ret);
3362	for (i = 0; i < num; i++) {
3363		ret = nwam_loc_get_prop_value(loc, props[i], &vals);
3364		if (ret == NWAM_SUCCESS) {
3365			write_export_command(NWAM_OBJECT_TYPE_LOC, props[i],
3366			    vals, of);
3367			nwam_value_free(vals);
3368		}
3369	}
3370	(void) fprintf(of, "end\n");
3371
3372	free(props);
3373	return (0);
3374}
3375
3376static int
3377export_wlan_callback(nwam_known_wlan_handle_t wlan, void *arg)
3378{
3379	char		*name;
3380	const char	**props;
3381	nwam_value_t	vals;
3382	nwam_error_t	ret;
3383	uint_t		num;
3384	int		i;
3385	FILE		*of = arg;
3386
3387	assert(of != NULL);
3388
3389	if ((ret = nwam_known_wlan_get_name(wlan, &name)) != NWAM_SUCCESS)
3390		return (ret);
3391
3392	(void) fprintf(of, "create wlan \"%s\"\n", name);
3393	free(name);
3394	/*
3395	 * Because of dependencies between properties, they have to be
3396	 * exported in the same order as when they are walked.
3397	 */
3398	if ((ret = nwam_known_wlan_get_default_proplist(&props, &num))
3399	    != NWAM_SUCCESS)
3400		return (ret);
3401	for (i = 0; i < num; i++) {
3402		ret = nwam_known_wlan_get_prop_value(wlan, props[i], &vals);
3403		if (ret == NWAM_SUCCESS) {
3404			write_export_command(NWAM_OBJECT_TYPE_KNOWN_WLAN,
3405			    props[i], vals, of);
3406			nwam_value_free(vals);
3407		}
3408	}
3409	(void) fprintf(of, "end\n");
3410
3411	free(props);
3412	return (0);
3413}
3414
3415/*
3416 * Writes configuration to screen or file (with -f option).
3417 * Writes a "destroy -a" if option -d is given.
3418 */
3419void
3420export_func(cmd_t *cmd)
3421{
3422	int		c;
3423	boolean_t	need_to_close = B_FALSE, write_to_file = B_FALSE;
3424	boolean_t	add_destroy = B_FALSE, lhandle = B_FALSE;
3425	char		filepath[MAXPATHLEN];
3426	nwam_error_t	ret = NWAM_SUCCESS;
3427	FILE		*of = NULL; /* either filename or stdout */
3428
3429	/* what to export */
3430	boolean_t export_ncp = B_FALSE, export_ncu = B_FALSE;
3431	boolean_t export_loc = B_FALSE, export_enm = B_FALSE;
3432	boolean_t export_wlan = B_FALSE;
3433	char *name = NULL;
3434
3435	/* check for -d and -f flags */
3436	filepath[0] = '\0';
3437	optind = 0;
3438	while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "df:")) != EOF) {
3439		switch (c) {
3440		case 'f':
3441			write_to_file = B_TRUE;
3442			break;
3443		case 'd':
3444			add_destroy = B_TRUE;
3445			break;
3446		default:
3447			command_usage(CMD_EXPORT);
3448			return;
3449		}
3450	}
3451
3452	/* determine where to export */
3453	if (!write_to_file) {
3454		of = stdout;
3455	} else {
3456		/*
3457		 * If -d was specified with -f, then argv[2] is filename,
3458		 * otherwise, argv[1] is filename.
3459		 */
3460		(void) strlcpy(filepath,
3461		    (add_destroy ? cmd->cmd_argv[2] : cmd->cmd_argv[1]),
3462		    sizeof (filepath));
3463		if ((of = fopen(filepath, "w")) == NULL) {
3464			nerr(gettext("opening file '%s': %s"), filepath,
3465			    strerror(errno));
3466			goto done;
3467		}
3468		setbuf(of, NULL);
3469		need_to_close = B_TRUE;
3470	}
3471
3472	if (add_destroy) {
3473		/* only possible in global scope */
3474		if (current_scope == NWAM_SCOPE_GBL) {
3475			(void) fprintf(of, "destroy -a\n");
3476		} else {
3477			nerr("Option -d is not allowed in non-global scope");
3478			goto done;
3479		}
3480	}
3481
3482	/* In the following scopes, only the -f argument is valid */
3483	if (((current_scope == NWAM_SCOPE_LOC ||
3484	    current_scope == NWAM_SCOPE_ENM ||
3485	    current_scope == NWAM_SCOPE_WLAN ||
3486	    current_scope == NWAM_SCOPE_NCU) &&
3487	    cmd->cmd_argc != 0 && !write_to_file)) {
3488		nerr("'export' does not take arguments at this scope");
3489		goto done;
3490	}
3491	if (current_scope == NWAM_SCOPE_NCP) {
3492		if (cmd->cmd_res1_type == RT1_ENM ||
3493		    cmd->cmd_res1_type == RT1_LOC ||
3494		    cmd->cmd_res1_type == RT1_WLAN) {
3495			nerr("only ncu can be exported at this scope");
3496			goto done;
3497		}
3498	}
3499
3500	/*
3501	 * Determine what objects to export depending on scope and command
3502	 * arguments.  If -f is specified, then the object name is argv[2].
3503	 * Otherwise, argv[0] is name, unless exporting all in global
3504	 * scope in which case name is set back to NULL.
3505	 */
3506	switch (current_scope) {
3507	case NWAM_SCOPE_GBL:
3508		name = (write_to_file ? trim_quotes(cmd->cmd_argv[2]) :
3509		    trim_quotes(cmd->cmd_argv[0]));
3510		switch (cmd->cmd_res1_type) {
3511		case RT1_LOC:
3512			export_loc = B_TRUE;
3513			break;
3514		case RT1_ENM:
3515			export_enm = B_TRUE;
3516			break;
3517		case RT1_WLAN:
3518			export_wlan = B_TRUE;
3519			break;
3520		case RT1_NCP:
3521			export_ncp = B_TRUE;
3522			if (cmd->cmd_res2_type == RT2_NCU) {
3523				nerr("cannot export ncu at from global scope");
3524				goto done;
3525			}
3526			break;
3527		default:
3528			/* export everything */
3529			export_loc = B_TRUE;
3530			export_enm = B_TRUE;
3531			export_wlan = B_TRUE;
3532			export_ncp = B_TRUE; /* NCP will export the NCUs */
3533			free(name);
3534			name = NULL; /* exporting all, undo name */
3535			break;
3536		}
3537		break;
3538	case NWAM_SCOPE_LOC:
3539		export_loc = B_TRUE;
3540		ret = nwam_loc_get_name(loc_h, &name);
3541		if (ret != NWAM_SUCCESS)
3542			goto fail;
3543		break;
3544	case NWAM_SCOPE_ENM:
3545		export_enm = B_TRUE;
3546		ret = nwam_enm_get_name(enm_h, &name);
3547		if (ret != NWAM_SUCCESS)
3548			goto fail;
3549		break;
3550	case NWAM_SCOPE_WLAN:
3551		export_wlan = B_TRUE;
3552		ret = nwam_known_wlan_get_name(wlan_h, &name);
3553		if (ret != NWAM_SUCCESS)
3554			goto fail;
3555		break;
3556	case NWAM_SCOPE_NCP:
3557		if (cmd->cmd_res2_type == RT2_NCU) {
3558			export_ncu = B_TRUE;
3559			name = (write_to_file ? trim_quotes(cmd->cmd_argv[2]) :
3560			    trim_quotes(cmd->cmd_argv[0]));
3561		} else {
3562			export_ncp = B_TRUE;
3563			ret = nwam_ncp_get_name(ncp_h, &name);
3564			if (ret != NWAM_SUCCESS)
3565				goto fail;
3566		}
3567		break;
3568	case NWAM_SCOPE_NCU:
3569		export_ncu = B_TRUE;
3570		ret = nwam_ncu_get_name(ncu_h, &name);
3571		if (ret != NWAM_SUCCESS)
3572			goto fail;
3573		break;
3574	default:
3575		nerr("Invalid scope");
3576		goto done;
3577	}
3578
3579	/* Now, export objects according to the flags set */
3580	if (export_ncp) {
3581		lhandle = B_FALSE;
3582		if (name == NULL) {
3583			/* export all NCPs */
3584			ret = nwam_walk_ncps(export_ncp_callback, of, 0, NULL);
3585		} else if (NWAM_NCP_AUTOMATIC(name)) {
3586			nerr("'%s' ncp cannot be exported", name);
3587			goto fail;
3588		} else {
3589			if (ncp_h == NULL) {
3590				ret = nwam_ncp_read(name, 0, &ncp_h);
3591				if (ret != NWAM_SUCCESS)
3592					goto fail;
3593				lhandle = B_TRUE;
3594			}
3595			/* will export NCUs also */
3596			ret = export_ncp_callback(ncp_h, of);
3597			if (lhandle) {
3598				nwam_ncp_free(ncp_h);
3599				ncp_h = NULL;
3600			}
3601		}
3602		if (ret != NWAM_SUCCESS)
3603			goto fail;
3604	}
3605
3606	if (export_ncu) {
3607		if (name == NULL) {
3608			/* export all NCUs */
3609			ret = nwam_ncp_walk_ncus(ncp_h, export_ncu_callback, of,
3610			    NWAM_FLAG_NCU_TYPE_CLASS_ALL, NULL);
3611		} else {
3612			if (ncu_h == NULL) {
3613				/* no NCU handle -> called from NCP scope */
3614				nwam_ncu_type_t		ncu_type;
3615				nwam_ncu_class_t	ncu_class;
3616
3617				ncu_class = (nwam_ncu_class_t)
3618				    cmd->cmd_ncu_class_type;
3619				ncu_type = nwam_ncu_class_to_type(ncu_class);
3620				ret = nwam_ncu_read(ncp_h, name,
3621				    ncu_type, 0, &ncu_h);
3622				if (ret == NWAM_SUCCESS) {
3623					/* one NCU with given name */
3624					ret = export_ncu_callback(ncu_h, of);
3625					nwam_ncu_free(ncu_h);
3626					ncu_h = NULL;
3627				} else if (ret == NWAM_ENTITY_MULTIPLE_VALUES) {
3628					/* multiple NCUs with given name */
3629					ret = nwam_ncu_read(ncp_h, name,
3630					    NWAM_NCU_TYPE_LINK, 0, &ncu_h);
3631					if (ret != NWAM_SUCCESS)
3632						goto fail;
3633					ret = export_ncu_callback(ncu_h, of);
3634					nwam_ncu_free(ncu_h);
3635					ncu_h = NULL;
3636
3637					ret = nwam_ncu_read(ncp_h, name,
3638					    NWAM_NCU_TYPE_INTERFACE, 0, &ncu_h);
3639					if (ret != NWAM_SUCCESS)
3640						goto fail;
3641					ret = export_ncu_callback(ncu_h, of);
3642					nwam_ncu_free(ncu_h);
3643					ncu_h = NULL;
3644				} else {
3645					goto fail;
3646				}
3647			} else {
3648				/* NCU handle exists */
3649				ret = export_ncu_callback(ncu_h, of);
3650			}
3651		}
3652		if (ret != NWAM_SUCCESS)
3653			goto fail;
3654	}
3655
3656	if (export_loc) {
3657		lhandle = B_FALSE;
3658		if (name == NULL) {
3659			/* export all locations */
3660			ret = nwam_walk_locs(export_loc_callback, of,
3661			    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
3662		} else if (NWAM_LOC_NAME_PRE_DEFINED(name)) {
3663			nerr("'%s' loc cannot be exported", name);
3664			goto fail;
3665		} else {
3666			if (loc_h == NULL) {
3667				ret = nwam_loc_read(name, 0, &loc_h);
3668				if (ret != NWAM_SUCCESS)
3669					goto fail;
3670				lhandle = B_TRUE;
3671			}
3672			ret = export_loc_callback(loc_h, of);
3673			if (lhandle) {
3674				nwam_loc_free(loc_h);
3675				loc_h = NULL;
3676			}
3677		}
3678		if (ret != NWAM_SUCCESS)
3679			goto fail;
3680	}
3681
3682	if (export_enm) {
3683		lhandle = B_FALSE;
3684		if (name == NULL) {
3685			/* export all ENMs */
3686			ret = nwam_walk_enms(export_enm_callback, of,
3687			    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
3688		} else {
3689			if (enm_h == NULL) {
3690				ret = nwam_enm_read(name, 0, &enm_h);
3691				if (ret != NWAM_SUCCESS)
3692					goto fail;
3693				lhandle = B_TRUE;
3694			}
3695			ret = export_enm_callback(enm_h, of);
3696			if (lhandle) {
3697				nwam_enm_free(enm_h);
3698				enm_h = NULL;
3699			}
3700		}
3701		if (ret != NWAM_SUCCESS)
3702			goto fail;
3703	}
3704
3705	if (export_wlan) {
3706		lhandle = B_FALSE;
3707		if (name == NULL) {
3708			/* export all WLANs */
3709			ret = nwam_walk_known_wlans(export_wlan_callback, of,
3710			    NWAM_FLAG_KNOWN_WLAN_WALK_PRIORITY_ORDER, NULL);
3711		} else {
3712			if (wlan_h == NULL) {
3713				ret = nwam_known_wlan_read(name, 0,
3714				    &wlan_h);
3715				if (ret != NWAM_SUCCESS)
3716					goto fail;
3717				lhandle = B_TRUE;
3718			}
3719			ret = export_wlan_callback(wlan_h, of);
3720			if (lhandle) {
3721				nwam_known_wlan_free(wlan_h);
3722				wlan_h = NULL;
3723			}
3724		}
3725		if (ret != NWAM_SUCCESS)
3726			goto fail;
3727	}
3728
3729fail:
3730	free(name);
3731	if (ret != NWAM_SUCCESS)
3732		nwamerr(ret, "Export error");
3733
3734done:
3735	if (need_to_close)
3736		(void) fclose(of);
3737}
3738
3739/*
3740 * Get property value.  If the -V option is specified, only the value is
3741 * printed without the property name.
3742 */
3743void
3744get_func(cmd_t *cmd)
3745{
3746	nwam_error_t		ret = NWAM_SUCCESS;
3747	nwam_value_t		prop_value;
3748	const char		*prop;
3749	boolean_t		value_only = B_FALSE;
3750	nwam_object_type_t	object_type = active_object_type();
3751
3752	/* check if option is -V to print value only */
3753	if (cmd->cmd_argc == 1) {
3754		int c;
3755
3756		optind = 0;
3757		while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "V")) != EOF) {
3758			switch (c) {
3759			case 'V':
3760				value_only = B_TRUE;
3761				break;
3762			default:
3763				command_usage(CMD_GET);
3764				return;
3765			}
3766		}
3767	}
3768
3769	/* property to get is in cmd->cmd_prop_type */
3770	if ((prop = pt_to_prop_name(object_type, cmd->cmd_prop_type)) == NULL) {
3771		nerr("Get error: invalid %s property: '%s'",
3772		    scope_to_str(current_scope), pt_to_str(cmd->cmd_prop_type));
3773		return;
3774	}
3775
3776	switch (object_type) {
3777	case NWAM_OBJECT_TYPE_NCU:
3778		ret = nwam_ncu_get_prop_value(ncu_h, prop, &prop_value);
3779		break;
3780	case NWAM_OBJECT_TYPE_LOC:
3781		ret = nwam_loc_get_prop_value(loc_h, prop, &prop_value);
3782		break;
3783	case NWAM_OBJECT_TYPE_ENM:
3784		ret = nwam_enm_get_prop_value(enm_h, prop, &prop_value);
3785		break;
3786	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
3787		ret = nwam_known_wlan_get_prop_value(wlan_h, prop, &prop_value);
3788		break;
3789	}
3790
3791	if (ret != NWAM_SUCCESS) {
3792		if (ret == NWAM_ENTITY_NOT_FOUND)
3793			nerr("Get error: property '%s' has not been set", prop);
3794		else
3795			nwamerr(ret, "Get error");
3796		return;
3797	}
3798
3799	if (value_only) {
3800		output_prop_val(prop, prop_value, stdout, B_FALSE);
3801		(void) printf("\n");
3802	} else {
3803		output_propname(prop, prop_value, NULL);
3804	}
3805	nwam_value_free(prop_value);
3806}
3807
3808/*
3809 * Clears value of a property.
3810 * Read-only properties cannot be cleared.
3811 * If clearing a property invalidates the object, then that property
3812 * cannot be cleared.
3813 */
3814void
3815clear_func(cmd_t *cmd)
3816{
3817	nwam_error_t		ret;
3818	const char		*prop;
3819	nwam_object_type_t	object_type = active_object_type();
3820
3821	/* property to clear is in cmd->cmd_prop_type */
3822	if ((prop = pt_to_prop_name(object_type, cmd->cmd_prop_type)) == NULL) {
3823		nerr("Clear error: invalid %s property: '%s'",
3824		    scope_to_str(current_scope), pt_to_str(cmd->cmd_prop_type));
3825		return;
3826	}
3827	if (is_prop_read_only(object_type, prop)) {
3828		nerr("Clear error: property '%s' is read-only", prop);
3829		return;
3830	}
3831
3832	switch (object_type) {
3833	case NWAM_OBJECT_TYPE_NCU:
3834		ret = nwam_ncu_delete_prop(ncu_h, prop);
3835		break;
3836	case NWAM_OBJECT_TYPE_LOC:
3837		ret = nwam_loc_delete_prop(loc_h, prop);
3838		break;
3839	case NWAM_OBJECT_TYPE_ENM:
3840		ret = nwam_enm_delete_prop(enm_h, prop);
3841		break;
3842	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
3843		ret = nwam_known_wlan_delete_prop(wlan_h, prop);
3844		break;
3845	}
3846
3847	if (ret != NWAM_SUCCESS) {
3848		if (ret == NWAM_INVALID_ARG || ret == NWAM_ENTITY_NOT_FOUND) {
3849			nerr("Clear error: property '%s' has not been set",
3850			    prop);
3851		} else {
3852			nwamerr(ret, "Clear error");
3853		}
3854		return;
3855	}
3856
3857	need_to_commit = B_TRUE;
3858}
3859
3860/*
3861 * Prints all the choices available for an enum property [c1|c2|c3].
3862 * Prints [true|false] for a boolean property.
3863 */
3864static void
3865print_all_prop_choices(nwam_object_type_t object_type, const char *prop)
3866{
3867	uint64_t		i = 0;
3868	const char		*str;
3869	boolean_t		choices = B_FALSE;
3870	nwam_value_type_t	value_type;
3871	nwam_error_t		ret;
3872
3873	/* Special case: print object-specific options for activation-mode */
3874	if (strcmp(prop, NWAM_NCU_PROP_ACTIVATION_MODE) == 0) {
3875		/* "manual" for all objects */
3876		(void) printf(" [%s|",
3877		    propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
3878		    NWAM_ACTIVATION_MODE_MANUAL));
3879		if (object_type == NWAM_OBJECT_TYPE_NCU) {
3880			(void) printf("%s]",
3881			    propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
3882			    NWAM_ACTIVATION_MODE_PRIORITIZED));
3883		} else {
3884			(void) printf("%s|%s]",
3885			    propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
3886			    NWAM_ACTIVATION_MODE_CONDITIONAL_ANY),
3887			    propval_to_str(NWAM_NCU_PROP_ACTIVATION_MODE,
3888			    NWAM_ACTIVATION_MODE_CONDITIONAL_ALL));
3889		}
3890		return;
3891	}
3892
3893	/* Special case: only "manual" configsrc is allowed for LDAP */
3894	if (strcmp(prop, NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC) == 0) {
3895		(void) printf(" [%s]",
3896		    propval_to_str(NWAM_LOC_PROP_LDAP_NAMESERVICE_CONFIGSRC,
3897		    NWAM_CONFIGSRC_MANUAL));
3898		return;
3899	}
3900
3901	value_type = prop_value_type(object_type, prop);
3902	switch (value_type) {
3903	case NWAM_VALUE_TYPE_UINT64:
3904		/* uint64 may be an enum, will print nothing if not an enum */
3905		while ((ret = nwam_uint64_get_value_string(prop, i++, &str))
3906		    == NWAM_SUCCESS || ret == NWAM_ENTITY_INVALID_VALUE) {
3907			/* No string representation for i, continue. */
3908			if (ret == NWAM_ENTITY_INVALID_VALUE)
3909				continue;
3910
3911			if (!choices)
3912				(void) printf("%s", " [");
3913			(void) printf("%s%s", choices ? "|" : "", str);
3914			choices = B_TRUE;
3915		}
3916		if (choices)
3917			(void) putchar(']');
3918		break;
3919	case NWAM_VALUE_TYPE_BOOLEAN:
3920		(void) printf(" [%s|%s]", "true", "false");
3921		break;
3922	case NWAM_VALUE_TYPE_STRING:
3923		break;
3924	}
3925}
3926
3927/*
3928 * Walk through object properties.
3929 * For newly-created object, the property name with no value is displayed, and
3930 * the user can input a value for each property.
3931 * For existing object, the current value is displayed and user input overwrites
3932 * the existing one. If no input is given, the existing value remains.
3933 * Read-only properties are not displayed.
3934 * Read-only objects cannot be walked.
3935 * If the -a option is specified, no properties are skipped.
3936 */
3937void
3938walkprop_func(cmd_t *cmd)
3939{
3940	nwam_error_t	ret = NWAM_SUCCESS;
3941	nwam_value_t	vals = NULL; /* freed in _wait_input() */
3942	int		i;
3943	uint_t		prop_num;
3944	const char	**props;
3945	boolean_t	read_only = B_FALSE, all_props = B_FALSE;
3946
3947	nwam_object_type_t object_type;
3948	prop_display_entry_t *prop_table;
3949
3950	if (!interactive_mode) {
3951		nerr("'walkprop' is only allowed in interactive mode");
3952		return;
3953	}
3954
3955	/* check if option -a is specified to show all properties */
3956	if (cmd->cmd_argc == 1) {
3957		int c;
3958		optind = 0;
3959		while ((c = getopt(cmd->cmd_argc, cmd->cmd_argv, "a")) != EOF) {
3960			switch (c) {
3961			case 'a':
3962				all_props = B_TRUE;
3963				break;
3964			default:
3965				command_usage(CMD_WALKPROP);
3966				return;
3967			}
3968		}
3969	}
3970
3971	/* read-only objects cannot be walked */
3972	if (obj1_type == RT1_NCP) {
3973		/* must be in NCU scope, NCP scope doesn't get here */
3974		(void) nwam_ncu_get_read_only(ncu_h, &read_only);
3975	}
3976	if (read_only) {
3977		nerr("'walkprop' cannot be used in read-only objects");
3978		return;
3979	}
3980
3981	/* get the current object type and the prop_display_table */
3982	object_type = active_object_type();
3983	prop_table = get_prop_display_table(object_type);
3984
3985	/* get the property list depending on the object type */
3986	switch (object_type) {
3987	case NWAM_OBJECT_TYPE_NCU:
3988	{
3989		nwam_ncu_type_t		ncu_type;
3990		nwam_ncu_class_t	ncu_class;
3991
3992		if ((ret = nwam_ncu_get_ncu_type(ncu_h, &ncu_type))
3993		    != NWAM_SUCCESS)
3994			break;
3995		if ((ret = nwam_ncu_get_ncu_class(ncu_h, &ncu_class))
3996		    != NWAM_SUCCESS)
3997			break;
3998
3999		ret = nwam_ncu_get_default_proplist(ncu_type, ncu_class, &props,
4000		    &prop_num);
4001		break;
4002	}
4003	case NWAM_OBJECT_TYPE_LOC:
4004		ret = nwam_loc_get_default_proplist(&props, &prop_num);
4005		break;
4006	case NWAM_OBJECT_TYPE_ENM:
4007		ret = nwam_enm_get_default_proplist(&props, &prop_num);
4008		break;
4009	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
4010		ret = nwam_known_wlan_get_default_proplist(&props, &prop_num);
4011		break;
4012	}
4013	if (ret != NWAM_SUCCESS) {
4014		nwamerr(ret, "Walkprop error: could not get property list");
4015		return;
4016	}
4017
4018	/* Loop through the properties */
4019	if (all_props)
4020		(void) printf(gettext("Walking all properties ...\n"));
4021	for (i = 0; i < prop_num; i++) {
4022		char line[NWAM_MAX_VALUE_LEN];
4023		char **checked = NULL;
4024
4025		/* check if this property should be displayed */
4026		if (is_prop_read_only(object_type, props[i]))
4027			continue;
4028		if (!all_props &&
4029		    !show_prop_test(object_type, props[i], prop_table,
4030		    checked, 0))
4031			continue;
4032
4033		/* get the existing value for this property */
4034		switch (object_type) {
4035		case NWAM_OBJECT_TYPE_NCU:
4036			ret = nwam_ncu_get_prop_value(ncu_h, props[i], &vals);
4037			break;
4038		case NWAM_OBJECT_TYPE_LOC:
4039			ret = nwam_loc_get_prop_value(loc_h, props[i], &vals);
4040			break;
4041		case NWAM_OBJECT_TYPE_ENM:
4042			ret = nwam_enm_get_prop_value(enm_h, props[i], &vals);
4043			break;
4044		case NWAM_OBJECT_TYPE_KNOWN_WLAN:
4045			ret = nwam_known_wlan_get_prop_value(wlan_h, props[i],
4046			    &vals);
4047			break;
4048		}
4049		/* returns NWAM_ENTITY_NOT_FOUND if no existing value */
4050		if (ret != NWAM_SUCCESS && ret != NWAM_ENTITY_NOT_FOUND)
4051			continue;
4052
4053		/* print property */
4054		(void) printf("%s", props[i]);
4055		/* print the existing value(s) if they exist */
4056		if (ret == NWAM_SUCCESS) {
4057			(void) printf(" (");
4058			output_prop_val(props[i], vals, stdout, B_TRUE);
4059			(void) putchar(')');
4060			nwam_value_free(vals);
4061		}
4062		/* print choices, won't print anything if there aren't any */
4063		print_all_prop_choices(object_type, props[i]);
4064		(void) printf("> ");
4065
4066		/* wait for user input */
4067		if (fgets(line, sizeof (line), stdin) == NULL)
4068			continue;
4069
4070		/* if user input new value, existing value is overrode */
4071		if (line[0] != '\n') {
4072			boolean_t is_listprop;
4073			int pt_type = prop_to_pt(object_type, props[i]);
4074
4075			is_listprop = is_prop_multivalued(object_type,
4076			    props[i]);
4077			vals = str_to_nwam_value(object_type, line, pt_type,
4078			    is_listprop);
4079			if (vals == NULL) {
4080				ret = NWAM_ENTITY_INVALID_VALUE;
4081				goto repeat;
4082			}
4083
4084			/* set the new value for the property */
4085			switch (object_type) {
4086			case NWAM_OBJECT_TYPE_NCU:
4087				ret = nwam_ncu_set_prop_value(ncu_h, props[i],
4088				    vals);
4089				break;
4090			case NWAM_OBJECT_TYPE_LOC:
4091				ret = nwam_loc_set_prop_value(loc_h, props[i],
4092				    vals);
4093				break;
4094			case NWAM_OBJECT_TYPE_ENM:
4095				ret = nwam_enm_set_prop_value(enm_h, props[i],
4096				    vals);
4097				break;
4098			case NWAM_OBJECT_TYPE_KNOWN_WLAN:
4099				ret = nwam_known_wlan_set_prop_value(wlan_h,
4100				    props[i], vals);
4101				break;
4102			}
4103			nwam_value_free(vals);
4104
4105			if (ret != NWAM_SUCCESS)
4106				goto repeat;
4107
4108			need_to_commit = B_TRUE;
4109			continue;
4110
4111repeat:
4112			invalid_set_prop_msg(props[i], ret);
4113			i--; /* decrement i to repeat */
4114		}
4115	}
4116
4117	free(props);
4118}
4119
4120/*
4121 * Verify whether all properties of a resource are valid.
4122 */
4123/* ARGSUSED */
4124void
4125verify_func(cmd_t *cmd)
4126{
4127	nwam_error_t	ret;
4128	const char	*errprop;
4129
4130	switch (active_object_type()) {
4131	case NWAM_OBJECT_TYPE_NCU:
4132		ret = nwam_ncu_validate(ncu_h, &errprop);
4133		break;
4134	case NWAM_OBJECT_TYPE_LOC:
4135		ret = nwam_loc_validate(loc_h, &errprop);
4136		break;
4137	case NWAM_OBJECT_TYPE_ENM:
4138		ret = nwam_enm_validate(enm_h, &errprop);
4139		break;
4140	case NWAM_OBJECT_TYPE_KNOWN_WLAN:
4141		ret = nwam_known_wlan_validate(wlan_h, &errprop);
4142		break;
4143	}
4144	if (ret != NWAM_SUCCESS)
4145		nwamerr(ret, "Verify error on property '%s'", errprop);
4146	else if (interactive_mode)
4147		(void) printf(gettext("All properties verified\n"));
4148}
4149
4150/*
4151 * command-line mode (# nwamcfg list or # nwamcfg "select loc test; list")
4152 */
4153static int
4154one_command_at_a_time(int argc, char *argv[])
4155{
4156	char *command;
4157	size_t len = 2; /* terminal \n\0 */
4158	int i, err;
4159
4160	for (i = 0; i < argc; i++)
4161		len += strlen(argv[i]) + 1;
4162	if ((command = malloc(len)) == NULL) {
4163		nerr("Out of memory");
4164		return (NWAM_ERR);
4165	}
4166	(void) strlcpy(command, argv[0], len);
4167	for (i = 1; i < argc; i++) {
4168		(void) strlcat(command, " ", len);
4169		(void) strlcat(command, argv[i], len);
4170	}
4171	(void) strlcat(command, "\n", len);
4172	err = string_to_yyin(command);
4173	free(command);
4174	if (err != NWAM_OK)
4175		return (err);
4176	while (!feof(yyin)) {
4177		yyparse();
4178
4179		/*
4180		 * If any command on a list of commands give an error,
4181		 * don't continue with the remaining commands.
4182		 */
4183		if (saw_error || time_to_exit)
4184			return (cleanup());
4185	}
4186
4187	/* if there are changes to commit, commit it */
4188	if (need_to_commit) {
4189		do_commit();
4190		/* if need_to_commit is not set, then there was a error */
4191		if (need_to_commit)
4192			return (NWAM_ERR);
4193	}
4194
4195	if (!interactive_mode)
4196		return (cleanup());
4197	else {
4198		yyin = stdin;
4199		return (read_input());
4200	}
4201}
4202
4203/*
4204 * cmd_file is slightly more complicated, as it has to open the command file
4205 * and set yyin appropriately.  Once that is done, though, it just calls
4206 * read_input(), and only once, since prompting is not possible.
4207 */
4208static int
4209cmd_file(char *file)
4210{
4211	FILE *infile;
4212	int err;
4213	struct stat statbuf;
4214	boolean_t using_real_file = (strcmp(file, "-") != 0);
4215
4216	if (using_real_file) {
4217		/*
4218		 * nerr() prints a line number in cmd_file_mode, which we do
4219		 * not want here, so temporarily unset it.
4220		 */
4221		cmd_file_mode = B_FALSE;
4222		if ((infile = fopen(file, "r")) == NULL) {
4223			nerr(gettext("could not open file '%s': %s"),
4224			    file, strerror(errno));
4225			return (1);
4226		}
4227		if ((err = fstat(fileno(infile), &statbuf)) != 0) {
4228			nerr(gettext("could not stat file '%s': %s"),
4229			    file, strerror(errno));
4230			err = 1;
4231			goto done;
4232		}
4233		if (!S_ISREG(statbuf.st_mode)) {
4234			nerr(gettext("'%s' is not a regular file."), file);
4235			err = 1;
4236			goto done;
4237		}
4238
4239		/*
4240		 * If -d was passed on the command-line, we need to
4241		 * start by removing any existing configuration.
4242		 * Alternatively, the file may begin with 'destroy -a';
4243		 * but in that case, the line will go through the lexer
4244		 * and be processed as it's encountered in the file.
4245		 */
4246		if (remove_all_configurations && destroy_all() != NWAM_SUCCESS)
4247			goto done;
4248
4249		/* set up for lexer */
4250		yyin = infile;
4251		cmd_file_mode = B_TRUE;
4252		ok_to_prompt = B_FALSE;
4253	} else {
4254		/*
4255		 * "-f -" is essentially the same as interactive mode,
4256		 * so treat it that way.
4257		 */
4258		interactive_mode = B_TRUE;
4259	}
4260	/* NWAM_REPEAT is for interactive mode; treat it like NWAM_ERR here. */
4261	if ((err = read_input()) == NWAM_REPEAT)
4262		err = NWAM_ERR;
4263	if (err == NWAM_OK)
4264		(void) printf(gettext("Configuration read.\n"));
4265
4266done:
4267	if (using_real_file)
4268		(void) fclose(infile);
4269	return (err);
4270}
4271
4272int
4273main(int argc, char *argv[])
4274{
4275	int	err;
4276	char	c;
4277
4278	/* This must be before anything goes to stdout. */
4279	setbuf(stdout, NULL);
4280
4281	if ((execname = strrchr(argv[0], '/')) == NULL)
4282		execname = argv[0];
4283	else
4284		execname++;
4285
4286	(void) setlocale(LC_ALL, "");
4287	(void) textdomain(TEXT_DOMAIN);
4288
4289	while ((c = getopt(argc, argv, "?hf:d")) != EOF) {
4290		switch (c) {
4291		case 'f':
4292			cmd_file_name = optarg;
4293			cmd_file_mode = B_TRUE;
4294			break;
4295		case '?':
4296		case 'h':
4297			cmd_line_usage();
4298			return (NWAM_OK);
4299		case 'd':
4300			remove_all_configurations = B_TRUE;
4301			break;
4302		default:
4303			cmd_line_usage();
4304			return (NWAM_ERR);
4305		}
4306	}
4307	/* -d can only be used with -f */
4308	if (remove_all_configurations && !cmd_file_mode) {
4309		nerr("Option -d can only be used with -f");
4310		return (NWAM_ERR);
4311	}
4312
4313	/*
4314	 * This may get set back to FALSE again in cmd_file() if cmd_file_name
4315	 * is a "real" file as opposed to "-" (i.e. meaning use stdin).
4316	 */
4317	if (isatty(STDIN_FILENO))
4318		ok_to_prompt = B_TRUE;
4319	if ((gl = new_GetLine(MAX_LINE_LEN, MAX_CMD_HIST)) == NULL)
4320		exit(NWAM_ERR);
4321	if (gl_customize_completion(gl, NULL, cmd_cpl_fn) != 0)
4322		exit(NWAM_ERR);
4323	(void) sigset(SIGINT, SIG_IGN);
4324
4325	if (optind == argc) {
4326		/* interactive or command-file mode */
4327		if (!cmd_file_mode)
4328			err = do_interactive();
4329		else
4330			err = cmd_file(cmd_file_name);
4331	} else {
4332		/* command-line mode */
4333		err = one_command_at_a_time(argc - optind, &(argv[optind]));
4334	}
4335	(void) del_GetLine(gl);
4336
4337	return (err);
4338}
4339