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