xref: /titanic_50/usr/src/cmd/cmd-inet/usr.sbin/nwamadm/nwamadm.c (revision 6d6fcbbb256ba9a8fe3e6076ae8a1f493b1f1a99)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * nwamadm is a command interpreter to administer NWAM profiles.  It
28  * is all in C (i.e., no lex/yacc), and all the argument passing is
29  * argc/argv based.  main() calls the command's handler function,
30  * which first calls parse_argv() to parse the input arguments and set
31  * approriate variables for each command.  The rest of the program is
32  * helper functions for the handler functions.
33  */
34 
35 #include <arpa/inet.h>
36 #include <assert.h>
37 #include <errno.h>
38 #include <libdlwlan.h>
39 #include <libinetutil.h>
40 #include <libnwam.h>
41 #include <libscf.h>
42 #include <locale.h>
43 #include <netinet/in.h>
44 #include <ofmt.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <strings.h>
50 #include <sys/socket.h>
51 #include <sys/types.h>
52 #include <unistd.h>
53 
54 #if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
55 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
56 #endif
57 
58 typedef void (cmd_func_t)(int, char **);
59 
60 struct cmd {
61 	uint_t		cmd_num;		/* command number */
62 	const char	*cmd_name;		/* command name */
63 	cmd_func_t	*cmd_handler;		/* function to call */
64 	const char	*cmd_usage;		/* short form help */
65 	const char	*cmd_desc;		/* command description */
66 	boolean_t	cmd_needs_nwamd;	/* nwam needs to run */
67 };
68 
69 /* constants for commands */
70 #define	CMD_HELP	0
71 #define	CMD_ENABLE	1
72 #define	CMD_DISABLE	2
73 #define	CMD_LIST	3
74 #define	CMD_SHOW_EVENTS	4
75 #define	CMD_SCAN_WIFI	5
76 #define	CMD_SELECT_WIFI	6
77 
78 #define	CMD_MIN		CMD_HELP
79 #define	CMD_MAX		CMD_SELECT_WIFI
80 
81 /* functions to call */
82 static cmd_func_t help_func, enable_func, disable_func, list_func;
83 static cmd_func_t show_events_func, scan_wifi_func, select_wifi_func;
84 static ofmt_cb_t print_list_cb;
85 
86 /* table of commands and usage */
87 static struct cmd cmdtab[] = {
88 	{ CMD_HELP,		"help",		help_func,
89 	    "help",
90 	    "Print this usage message.",		B_FALSE		},
91 	{ CMD_ENABLE,		"enable",	enable_func,
92 	    "enable [-p <profile-type>] [-c <ncu-class>] <object-name>",
93 	    "Enable the specified profile.",		B_FALSE		},
94 	{ CMD_DISABLE,		"disable",	disable_func,
95 	    "disable [-p <profile-type>] [-c <ncu-class>] <object-name>",
96 	    "Disable the specified profile.",		B_FALSE		},
97 	{ CMD_LIST,		"list",		list_func,
98 	    "list [-x] [-p <profile-type>] [-c <ncu-class>] [<object-name>]",
99 	    "List profiles and their current states.",	B_TRUE		},
100 	{ CMD_SHOW_EVENTS,	"show-events",	show_events_func,
101 	    "show-events",
102 	    "Display all events.",			B_TRUE		},
103 	{ CMD_SCAN_WIFI,	"scan-wifi",	scan_wifi_func,
104 	    "scan-wifi <link-name>",
105 	    "Request a WiFi scan for the selected link.", B_TRUE	},
106 	{ CMD_SELECT_WIFI,	"select-wifi",	select_wifi_func,
107 	    "select-wifi <link-name>",
108 	    "Make a WLAN selection from the last WiFi scan.", B_TRUE	}
109 };
110 
111 /* Structure for "nwamadm list" output */
112 
113 typedef struct profile_entry {
114 	nwam_object_type_t	p_type;
115 	nwam_ncu_class_t	p_ncu_class;
116 	char			p_name[NWAM_MAX_NAME_LEN];
117 	nwam_state_t		p_state;
118 	nwam_aux_state_t	p_aux_state;
119 } profile_entry_t;
120 
121 /* widths of colums for printing */
122 #define	TYPE_WIDTH		12	/* width of TYPE column */
123 #define	PROFILE_WIDTH		15	/* width of PROFILE column */
124 #define	STATE_WIDTH		15	/* width of STATE column */
125 #define	AUXSTATE_WIDTH		36	/* width of AUXILIARY STATE column */
126 
127 #define	EVENT_WIDTH		15	/* width of EVENT column */
128 #define	DESCRIPTION_WIDTH	64	/* width of DESCRIPTION column */
129 
130 /* id for columns of "nwamadm list" */
131 typedef enum {
132 	LIST_TYPE,
133 	LIST_PROFILE,
134 	LIST_STATE,
135 	LIST_AUXSTATE
136 } list_field_id_t;
137 
138 static const ofmt_field_t list_fields[] = {
139 	/* header,		width,		id,		callback */
140 	{ "TYPE",		TYPE_WIDTH,	LIST_TYPE,	print_list_cb },
141 	{ "PROFILE",		PROFILE_WIDTH,	LIST_PROFILE,	print_list_cb },
142 	{ "STATE",		STATE_WIDTH,	LIST_STATE,	print_list_cb },
143 	{ "AUXILIARY STATE",	AUXSTATE_WIDTH,	LIST_AUXSTATE,	print_list_cb },
144 	{ NULL,			0,		0,		NULL }
145 };
146 
147 /* Global variables */
148 
149 /* set early in main(), never modified thereafter, used all over the place */
150 static char *execname;
151 
152 /* whether the auxilary states are to be printed or not */
153 static boolean_t extended_list = B_FALSE;
154 
155 /* Functions */
156 
157 static const char *
158 cmd_to_str(int cmd_num)
159 {
160 	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
161 	return (cmdtab[cmd_num].cmd_name);
162 }
163 
164 /* returns description of given command */
165 static const char *
166 long_help(int cmd_num)
167 {
168 	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
169 	return (gettext(cmdtab[cmd_num].cmd_desc));
170 }
171 
172 /*
173  * Called with explicit B_TRUE when help is explicitly required,
174  * B_FALSE for errors
175  */
176 static void
177 usage(boolean_t explicit)
178 {
179 	int	i;
180 	FILE	*fd = explicit ? stdout : stderr;
181 
182 	(void) fprintf(fd, gettext("usage: <subcommand> <args> ...\n"));
183 	for (i = CMD_MIN; i <= CMD_MAX; i++) {
184 		(void) fprintf(fd, "\t%s\n", cmdtab[i].cmd_usage);
185 		if (explicit)
186 			(void) fprintf(fd, "\t\t%s\n\n", long_help(i));
187 	}
188 }
189 
190 /* PRINTFLIKE1 */
191 static void
192 die(const char *format, ...)
193 {
194 	va_list alist;
195 
196 	format = gettext(format);
197 	(void) fprintf(stderr, "%s: ", execname);
198 
199 	va_start(alist, format);
200 	(void) vfprintf(stderr, format, alist);
201 	va_end(alist);
202 	(void) fprintf(stderr, "\n");
203 
204 	exit(EXIT_FAILURE);
205 }
206 
207 /* PRINTFLIKE2 */
208 static void
209 die_nwamerr(nwam_error_t err, const char *format, ...)
210 {
211 	va_list alist;
212 
213 	format = gettext(format);
214 	(void) fprintf(stderr, "%s: ", execname);
215 
216 	va_start(alist, format);
217 	(void) vfprintf(stderr, format, alist);
218 	va_end(alist);
219 	(void) fprintf(stderr, ": %s\n", nwam_strerror(err));
220 
221 	exit(EXIT_FAILURE);
222 }
223 
224 /* prints the usage for cmd_num and exits */
225 static void
226 die_usage(int cmd_num)
227 {
228 	assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX);
229 
230 	(void) fprintf(stderr, "%s: %s\n", gettext("usage"),
231 	    cmdtab[cmd_num].cmd_usage);
232 	(void) fprintf(stderr, "\t%s\n", long_help(cmd_num));
233 
234 	exit(EXIT_FAILURE);
235 }
236 
237 /*
238  * Prints the usage and description of all commands
239  */
240 /* ARGSUSED */
241 static void
242 help_func(int argc, char *argv[])
243 {
244 	usage(B_TRUE);
245 }
246 
247 /* determines if the NCP is active or not.  If so, sets arg and halts walk. */
248 static int
249 active_ncp_callback(nwam_ncp_handle_t ncph, void *arg)
250 {
251 	char			**namep = arg;
252 	nwam_state_t		state = NWAM_STATE_UNINITIALIZED;
253 	nwam_aux_state_t	aux;
254 
255 	(void) nwam_ncp_get_state(ncph, &state, &aux);
256 	if (state == NWAM_STATE_ONLINE) {
257 		if (nwam_ncp_get_name(ncph, namep) != NWAM_SUCCESS)
258 			*namep = NULL;
259 		return (1);
260 	}
261 
262 	return (0);
263 }
264 
265 /* find the currently active NCP and returns its handle */
266 static nwam_ncp_handle_t
267 determine_active_ncp()
268 {
269 	char *active_ncp;
270 	nwam_ncp_handle_t ncph;
271 	nwam_error_t ret;
272 
273 	if (nwam_walk_ncps(active_ncp_callback, &active_ncp, 0, NULL)
274 	    == NWAM_WALK_HALTED) {
275 		if (active_ncp == NULL)
276 			return (NULL);
277 
278 		/* retrieve the NCP handle */
279 		ret = nwam_ncp_read(active_ncp, 0, &ncph);
280 		free(active_ncp);
281 		if (ret == NWAM_SUCCESS)
282 			return (ncph);
283 	}
284 
285 	return (NULL);
286 }
287 
288 /* check if the given name is a valid loc, test by reading the given loc */
289 static boolean_t
290 valid_loc(const char *name)
291 {
292 	nwam_loc_handle_t loch;
293 
294 	if (nwam_loc_read(name, 0, &loch) != NWAM_SUCCESS)
295 		return (B_FALSE);
296 	nwam_loc_free(loch);
297 	return (B_TRUE);
298 }
299 
300 static boolean_t
301 valid_enm(const char *name)
302 {
303 	nwam_enm_handle_t enmh;
304 
305 	if (nwam_enm_read(name, 0, &enmh) != NWAM_SUCCESS)
306 		return (B_FALSE);
307 	nwam_enm_free(enmh);
308 	return (B_TRUE);
309 }
310 
311 static boolean_t
312 valid_ncp(const char *name)
313 {
314 	nwam_ncp_handle_t ncph;
315 
316 	if (nwam_ncp_read(name, 0, &ncph) != NWAM_SUCCESS)
317 		return (B_FALSE);
318 	nwam_ncp_free(ncph);
319 	return (B_TRUE);
320 }
321 
322 static boolean_t
323 valid_ncu(const char *name)
324 {
325 	nwam_ncp_handle_t ncph;
326 	nwam_ncu_handle_t ncuh;
327 	nwam_error_t	ret;
328 
329 	if ((ncph = determine_active_ncp()) == NULL)
330 		return (B_FALSE);
331 
332 	ret = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_ANY, 0, &ncuh);
333 	nwam_ncp_free(ncph);
334 	if (ret != NWAM_SUCCESS && ret != NWAM_ENTITY_MULTIPLE_VALUES)
335 		return (B_FALSE);
336 	nwam_ncu_free(ncuh);
337 	return (B_TRUE);
338 }
339 
340 /*
341  * Given a name, returns object type (loc, enm, ncp, or ncu) and how many
342  * objects matched that name.
343  */
344 static nwam_object_type_t
345 determine_object_type(const char *name, int *num)
346 {
347 	nwam_object_type_t type;
348 	int n = 0;
349 
350 	/* see if a valid loc, enm, ncp and/or ncu exists with given name */
351 	if (valid_loc(name)) {
352 		n++;
353 		type = NWAM_OBJECT_TYPE_LOC;
354 	}
355 	if (valid_enm(name)) {
356 		n++;
357 		type = NWAM_OBJECT_TYPE_ENM;
358 	}
359 	if (valid_ncp(name)) {
360 		n++;
361 		type = NWAM_OBJECT_TYPE_NCP;
362 	}
363 	if (valid_ncu(name)) {
364 		n++;
365 		type = NWAM_OBJECT_TYPE_NCU;
366 	}
367 
368 	/* if n > 1, then it means *type was set multiple times, undo it */
369 	if (n != 1)
370 		type = NWAM_OBJECT_TYPE_UNKNOWN;
371 
372 	*num = n;
373 	return (type);
374 }
375 
376 /*
377  * Parses argv array and populates object_type and name.
378  * Program exits on failure.
379  */
380 static void
381 parse_argv(int argc, char *argv[], int cmd_num, nwam_object_type_t *object_type,
382     nwam_ncu_type_t *ncu_type, nwam_ncu_class_t *ncu_class, const char **name)
383 {
384 	int			arg;
385 	nwam_object_type_t	type = NWAM_OBJECT_TYPE_UNKNOWN;
386 	uint64_t		ncu = NWAM_NCU_TYPE_ANY;
387 	uint64_t		class = NWAM_NCU_CLASS_ANY;
388 
389 	/* check argv for option */
390 	optind = 0;
391 	while ((arg = getopt(argc, argv, "?p:c:x")) != EOF) {
392 		switch (arg) {
393 		case 'p':
394 			type = nwam_string_to_object_type(optarg);
395 			if (type == NWAM_OBJECT_TYPE_UNKNOWN)
396 				die("Invalid profile-type: %s", optarg);
397 			break;
398 		case 'c':
399 			if (nwam_value_string_get_uint64(NWAM_NCU_PROP_CLASS,
400 			    optarg, &class) != NWAM_SUCCESS) {
401 				die("Invalid ncu-class: %s", optarg);
402 			}
403 			ncu = nwam_ncu_class_to_type(class);
404 			if (ncu == NWAM_NCU_TYPE_ANY ||
405 			    ncu == NWAM_NCU_TYPE_UNKNOWN)
406 				die("Invalid ncu-class: %s", optarg);
407 			break;
408 		case 'x':
409 			/* -x is only for list */
410 			if (cmd_num != CMD_LIST)
411 				die("-x can only be used with 'list'");
412 			extended_list = B_TRUE;
413 			break;
414 		case '?':
415 		default:
416 			die_usage(cmd_num);
417 		}
418 	}
419 
420 	if (ncu != NWAM_NCU_TYPE_ANY) {
421 		/* If -c is given, -p must be NCU. If unspecified, assume NCU */
422 		if (type != NWAM_OBJECT_TYPE_UNKNOWN &&
423 		    type != NWAM_OBJECT_TYPE_NCU)
424 			die("'-c <ncu-class>' can only be used for ncu");
425 
426 		type = NWAM_OBJECT_TYPE_NCU;
427 	}
428 
429 	/* name is mandatory for enable and disable, but not for list */
430 	if (optind == (argc-1))
431 		*name = argv[optind];
432 	else if (argc != optind)
433 		die("too many profile names given");
434 	else if (cmd_num != CMD_LIST)
435 		die("no profile name given");
436 
437 	/*
438 	 * No need to determine type for list.
439 	 * If -p is not given for enable or disable, then determine type.
440 	 */
441 	if (cmd_num != CMD_LIST && type == NWAM_OBJECT_TYPE_UNKNOWN) {
442 		int num = 0;
443 
444 		type = determine_object_type(*name, &num);
445 		if (num == 0) {
446 			die("no profile matched '%s'", *name);
447 		} else if (num > 1) {
448 			die("more than one profile matched '%s' - use "
449 			    "'-p <profile-type>' to specify a profile type.",
450 			    *name);
451 		}
452 	}
453 
454 	*object_type = type;
455 	*ncu_type = ncu;
456 	*ncu_class = class;
457 }
458 
459 /* Enables/Disables profiles depending on boolean */
460 static nwam_error_t
461 loc_action(const char *name, boolean_t enable, char **realnamep)
462 {
463 	nwam_loc_handle_t loch;
464 	nwam_error_t ret;
465 
466 	if ((ret = nwam_loc_read(name, 0, &loch)) != NWAM_SUCCESS)
467 		return (ret);
468 
469 	if (enable)
470 		ret = nwam_loc_enable(loch);
471 	else
472 		ret = nwam_loc_disable(loch);
473 
474 	(void) nwam_loc_get_name(loch, realnamep);
475 	nwam_loc_free(loch);
476 	return (ret);
477 }
478 
479 static nwam_error_t
480 enm_action(const char *name, boolean_t enable, char **realnamep)
481 {
482 	nwam_enm_handle_t enmh;
483 	nwam_error_t ret;
484 
485 	if ((ret = nwam_enm_read(name, 0, &enmh)) != NWAM_SUCCESS)
486 		return (ret);
487 
488 	if (enable)
489 		ret = nwam_enm_enable(enmh);
490 	else
491 		ret = nwam_enm_disable(enmh);
492 
493 	(void) nwam_enm_get_name(enmh, realnamep);
494 	nwam_enm_free(enmh);
495 	return (ret);
496 }
497 
498 static nwam_error_t
499 ncu_action(const char *name, nwam_ncp_handle_t ncph, nwam_ncu_type_t type,
500     boolean_t enable, char **realnamep)
501 {
502 	nwam_ncu_handle_t ncuh;
503 	nwam_error_t ret;
504 	boolean_t retrieved_ncph = B_FALSE;
505 
506 	if (ncph == NULL) {
507 		if ((ncph = determine_active_ncp()) == NULL)
508 			return (NWAM_ENTITY_NOT_FOUND);
509 		retrieved_ncph = B_TRUE;
510 	}
511 
512 	ret = nwam_ncu_read(ncph, name, type, 0, &ncuh);
513 	switch (ret) {
514 	case NWAM_SUCCESS:
515 		if (enable)
516 			ret = nwam_ncu_enable(ncuh);
517 		else
518 			ret = nwam_ncu_disable(ncuh);
519 		(void) nwam_ncu_get_name(ncuh, realnamep);
520 		nwam_ncu_free(ncuh);
521 		break;
522 	case NWAM_ENTITY_MULTIPLE_VALUES:
523 		/* Call ncu_action() for link and interface types */
524 		ret = ncu_action(name, ncph, NWAM_NCU_TYPE_LINK, enable,
525 		    realnamep);
526 		if (ret != NWAM_SUCCESS)
527 			break;
528 
529 		ret = ncu_action(name, ncph, NWAM_NCU_TYPE_INTERFACE, enable,
530 		    realnamep);
531 		break;
532 	}
533 	if (retrieved_ncph)
534 		nwam_ncp_free(ncph);
535 
536 	return (ret);
537 }
538 
539 /*
540  * If more than one type of profile with the same name, return error.
541  * In such situations, the -p option must be used.
542  * If a location is enabled when a different one is already enabled, then
543  * that location is disabled automatically by nwamd.
544  */
545 static void
546 enable_func(int argc, char *argv[])
547 {
548 	nwam_error_t		ret;
549 	nwam_object_type_t	type = NWAM_OBJECT_TYPE_UNKNOWN;
550 	nwam_ncu_type_t		ncu_type = NWAM_NCU_TYPE_ANY;
551 	nwam_ncu_class_t	ncu_class = NWAM_NCU_CLASS_ANY;
552 	const char		*name;
553 	char			*realname = NULL;
554 
555 	/* parse_argv() returns only on success */
556 	parse_argv(argc, argv, CMD_ENABLE, &type, &ncu_type, &ncu_class, &name);
557 
558 	/*
559 	 * NCPs and Locations don't need to disable the currently active
560 	 * profile - nwamd automatically switches to the new active profile.
561 	 * and will disable it if necessary.
562 	 */
563 
564 	/* activate given profile */
565 	switch (type) {
566 	case NWAM_OBJECT_TYPE_LOC:
567 		ret = loc_action(name, B_TRUE, &realname);
568 		break;
569 	case NWAM_OBJECT_TYPE_ENM:
570 		ret = enm_action(name, B_TRUE, &realname);
571 		break;
572 	case NWAM_OBJECT_TYPE_NCP:
573 	{
574 		nwam_ncp_handle_t ncph;
575 
576 		if ((ret = nwam_ncp_read(name, 0, &ncph)) != NWAM_SUCCESS)
577 			break;
578 
579 		ret = nwam_ncp_enable(ncph);
580 		(void) nwam_ncp_get_name(ncph, &realname);
581 		nwam_ncp_free(ncph);
582 		break;
583 	}
584 	case NWAM_OBJECT_TYPE_NCU:
585 		ret = ncu_action(name, NULL, ncu_type, B_TRUE, &realname);
586 		break;
587 	}
588 
589 	switch (ret) {
590 	case NWAM_SUCCESS:
591 		(void) printf(gettext("Enabling %s '%s'\n"),
592 		    nwam_object_type_to_string(type),
593 		    realname != NULL ? realname : name);
594 		break;
595 	case NWAM_ENTITY_NOT_MANUAL:
596 		die("Only profiles with manual activation-mode can be enabled");
597 		break;
598 	default:
599 		die_nwamerr(ret, "Could not enable %s '%s'",
600 		    nwam_object_type_to_string(type),
601 		    realname != NULL ? realname : name);
602 	}
603 	free(realname);
604 }
605 
606 /*
607  * Disables a given profile.  Similar to enable, the -p option must be used
608  * if more than one type of profile is matched by the given name.
609  */
610 static void
611 disable_func(int argc, char *argv[])
612 {
613 	nwam_error_t		ret;
614 	nwam_object_type_t	type = NWAM_OBJECT_TYPE_UNKNOWN;
615 	nwam_ncu_type_t		ncu_type = NWAM_NCU_TYPE_ANY;
616 	nwam_ncu_class_t	ncu_class = NWAM_NCU_CLASS_ANY;
617 	const char		*name;
618 	char			*realname = NULL;
619 
620 	/* parse_argv() returns only on success */
621 	parse_argv(argc, argv, CMD_DISABLE, &type, &ncu_type, &ncu_class,
622 	    &name);
623 
624 	/* deactivate the given profile */
625 	switch (type) {
626 	case NWAM_OBJECT_TYPE_LOC:
627 		ret = loc_action(name, B_FALSE, &realname);
628 		break;
629 	case NWAM_OBJECT_TYPE_ENM:
630 		ret = enm_action(name, B_FALSE, &realname);
631 		break;
632 	case NWAM_OBJECT_TYPE_NCU:
633 		ret = ncu_action(name, NULL, ncu_type, B_FALSE, &realname);
634 		break;
635 	case NWAM_OBJECT_TYPE_NCP:
636 		die("ncp's cannot be disabled.  Enable a different ncp to "
637 		    "switch to that ncp");
638 	}
639 
640 	switch (ret) {
641 	case NWAM_SUCCESS:
642 		(void) printf(gettext("Disabling %s '%s'\n"),
643 		    nwam_object_type_to_string(type),
644 		    realname != NULL ? realname : name);
645 		break;
646 	case NWAM_ENTITY_NOT_MANUAL:
647 		die("Only profiles with manual activation-mode can be "
648 		    "disabled");
649 		break;
650 	default:
651 		die_nwamerr(ret, "Could not disable %s '%s'",
652 		    nwam_object_type_to_string(type),
653 		    realname != NULL ? realname : name);
654 	}
655 	free(realname);
656 }
657 
658 /* prints each column */
659 static boolean_t
660 print_list_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
661 {
662 	profile_entry_t *pent = ofarg->ofmt_cbarg;
663 
664 	switch (ofarg->ofmt_id) {
665 	case LIST_TYPE:
666 		/* ncu:ip or ncu:phys for NCUs; ncp, loc, enm for others */
667 		if (pent->p_type == NWAM_OBJECT_TYPE_NCU) {
668 			const char *class;
669 			if (nwam_uint64_get_value_string(NWAM_NCU_PROP_CLASS,
670 			    pent->p_ncu_class, &class) != NWAM_SUCCESS)
671 				class = ""; /* empty */
672 			(void) snprintf(buf, bufsize, "%s:%s",
673 			    nwam_object_type_to_string(pent->p_type), class);
674 		} else {
675 			(void) strlcpy(buf,
676 			    nwam_object_type_to_string(pent->p_type), bufsize);
677 		}
678 		break;
679 	case LIST_PROFILE:
680 		(void) strlcpy(buf, pent->p_name, bufsize);
681 		break;
682 	case LIST_STATE:
683 		(void) strlcpy(buf, nwam_state_to_string(pent->p_state),
684 		    bufsize);
685 		break;
686 	case LIST_AUXSTATE:
687 		(void) strlcpy(buf,
688 		    nwam_aux_state_to_string(pent->p_aux_state), bufsize);
689 		break;
690 	default:
691 		die("invalid print_list_cb() input: %d", ofarg->ofmt_id);
692 		break;
693 	}
694 	return (B_TRUE);
695 }
696 
697 /* returns the state and auxilliary state of the object */
698 static nwam_state_t
699 determine_object_state(nwam_object_type_t type, void *handle,
700     nwam_aux_state_t *aux_statep)
701 {
702 	nwam_state_t state;
703 	nwam_aux_state_t astate;
704 	nwam_error_t ret;
705 
706 	switch (type) {
707 	case NWAM_OBJECT_TYPE_ENM:
708 		ret = nwam_enm_get_state(handle, &state, &astate);
709 		break;
710 	case NWAM_OBJECT_TYPE_LOC:
711 		ret = nwam_loc_get_state(handle, &state, &astate);
712 		break;
713 	case NWAM_OBJECT_TYPE_NCP:
714 		ret = nwam_ncp_get_state(handle, &state, &astate);
715 		break;
716 	case NWAM_OBJECT_TYPE_NCU:
717 		ret = nwam_ncu_get_state(handle, &state, &astate);
718 		break;
719 	default:
720 		/* NOTREACHED */
721 		break;
722 	}
723 
724 	if (ret == NWAM_PERMISSION_DENIED) {
725 		die_nwamerr(ret, "could not get object state");
726 	} else if (ret != NWAM_SUCCESS) {
727 		state = NWAM_STATE_UNINITIALIZED;
728 		astate = NWAM_AUX_STATE_UNINITIALIZED;
729 	}
730 
731 	if (aux_statep != NULL)
732 		*aux_statep = astate;
733 	return (state);
734 }
735 
736 /* populate profile_entry_t with values for object with given handle */
737 static int
738 add_to_profile_entry(nwam_object_type_t type, void *handle,
739     profile_entry_t *pent)
740 {
741 	char		*name;
742 	nwam_error_t	ret;
743 
744 	pent->p_type = type;
745 	if (type == NWAM_OBJECT_TYPE_NCU) {
746 		nwam_ncu_class_t class;
747 		if ((ret = nwam_ncu_get_ncu_class(handle, &class))
748 		    != NWAM_SUCCESS)
749 			return (ret);
750 		pent->p_ncu_class = class;
751 	} else {
752 		pent->p_ncu_class = -1;
753 	}
754 
755 	switch (type) {
756 	case NWAM_OBJECT_TYPE_ENM:
757 		ret = nwam_enm_get_name(handle, &name);
758 		break;
759 	case NWAM_OBJECT_TYPE_LOC:
760 		ret = nwam_loc_get_name(handle, &name);
761 		break;
762 	case NWAM_OBJECT_TYPE_NCP:
763 		ret = nwam_ncp_get_name(handle, &name);
764 		break;
765 	case NWAM_OBJECT_TYPE_NCU:
766 		ret = nwam_ncu_get_name(handle, &name);
767 		break;
768 	default:
769 		/* NOTREACHED */
770 		break;
771 	}
772 	if (ret != NWAM_SUCCESS) {
773 		return (ret);
774 	}
775 	(void) strlcpy(pent->p_name, name, sizeof (pent->p_name));
776 	free(name);
777 
778 	pent->p_state = determine_object_state(type, handle,
779 	    &pent->p_aux_state);
780 
781 	return (NWAM_SUCCESS);
782 }
783 
784 /* callback functions used by walk */
785 
786 static int
787 list_ncu_cb(nwam_ncu_handle_t ncuh, void *arg)
788 {
789 	ofmt_handle_t	ofmt = arg;
790 	profile_entry_t pent;
791 	nwam_error_t	ret;
792 
793 	bzero(&pent, sizeof (profile_entry_t));
794 	ret = add_to_profile_entry(NWAM_OBJECT_TYPE_NCU, ncuh, &pent);
795 	if (ret != NWAM_SUCCESS)
796 		die_nwamerr(ret, "could not add ncu to list");
797 	ofmt_print(ofmt, &pent);
798 	return (0);
799 }
800 
801 static int
802 list_ncp_cb(nwam_ncp_handle_t ncph, void *arg)
803 {
804 	ofmt_handle_t	ofmt = arg;
805 	profile_entry_t pent;
806 	nwam_error_t	ret;
807 	nwam_state_t	state;
808 
809 	bzero(&pent, sizeof (profile_entry_t));
810 	ret = add_to_profile_entry(NWAM_OBJECT_TYPE_NCP, ncph, &pent);
811 	if (ret != NWAM_SUCCESS)
812 		die_nwamerr(ret, "could not add ncp to list");
813 	ofmt_print(ofmt, &pent);
814 
815 	state = determine_object_state(NWAM_OBJECT_TYPE_NCP, ncph, NULL);
816 	if (state == NWAM_STATE_ONLINE) {
817 		(void) nwam_ncp_walk_ncus(ncph, list_ncu_cb, ofmt,
818 		    NWAM_FLAG_NCU_TYPE_ALL, NULL);
819 	}
820 	return (0);
821 }
822 
823 static int
824 list_loc_cb(nwam_loc_handle_t loch, void *arg)
825 {
826 	ofmt_handle_t	ofmt = arg;
827 	profile_entry_t pent;
828 	nwam_error_t	ret;
829 
830 	bzero(&pent, sizeof (profile_entry_t));
831 	ret = add_to_profile_entry(NWAM_OBJECT_TYPE_LOC, loch, &pent);
832 	if (ret != NWAM_SUCCESS)
833 		die_nwamerr(ret, "could not add loc to list");
834 	ofmt_print(ofmt, &pent);
835 	return (0);
836 }
837 
838 static int
839 list_enm_cb(nwam_enm_handle_t enmh, void *arg)
840 {
841 	ofmt_handle_t	ofmt = arg;
842 	profile_entry_t pent;
843 	nwam_error_t	ret;
844 
845 	bzero(&pent, sizeof (profile_entry_t));
846 	ret = add_to_profile_entry(NWAM_OBJECT_TYPE_ENM, enmh, &pent);
847 	if (ret != NWAM_SUCCESS)
848 		die_nwamerr(ret, "could not add enm to list");
849 	ofmt_print(ofmt, &pent);
850 	return (0);
851 }
852 
853 /*
854  * lists all profiles and their state
855  */
856 static void
857 list_func(int argc, char *argv[])
858 {
859 	nwam_error_t		ret = NWAM_SUCCESS;
860 	nwam_object_type_t	type = NWAM_OBJECT_TYPE_UNKNOWN;
861 	nwam_ncu_type_t		ncu_type = NWAM_NCU_TYPE_ANY;
862 	nwam_ncu_class_t	ncu_class = NWAM_NCU_CLASS_ANY;
863 	char			*name = NULL;
864 
865 	ofmt_handle_t	ofmt;
866 	ofmt_status_t	oferr;
867 	char		*default_fields = "type,profile,state";
868 	char		*extended_fields = "type,profile,state,auxiliary state";
869 	char		*fields = NULL;
870 
871 	/* parse_argv() returns only on success */
872 	parse_argv(argc, argv, CMD_LIST, &type, &ncu_type, &ncu_class,
873 	    (const char **)&name);
874 
875 	if (extended_list)
876 		fields = extended_fields;
877 	else
878 		fields = default_fields;
879 	oferr = ofmt_open(fields, list_fields, 0, 0, &ofmt);
880 	if (oferr != OFMT_SUCCESS) {
881 		char buf[OFMT_BUFSIZE];
882 		(void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
883 		die("ofmt_open() failed: %s", buf);
884 	}
885 
886 	/* object-name given in command-line */
887 	if (name != NULL) {
888 		boolean_t found = B_FALSE;
889 
890 		/*
891 		 * If objects with different types have the same name
892 		 * (type = UNKNOWN), then try to open handle for each object
893 		 * and print if successful.
894 		 */
895 		if (type == NWAM_OBJECT_TYPE_NCP ||
896 		    type == NWAM_OBJECT_TYPE_UNKNOWN) {
897 			nwam_ncp_handle_t ncph;
898 			if (nwam_ncp_read(name, 0, &ncph) == NWAM_SUCCESS) {
899 				found = B_TRUE;
900 				(void) list_ncp_cb(ncph, ofmt);
901 				nwam_ncp_free(ncph);
902 			}
903 		}
904 		if (type == NWAM_OBJECT_TYPE_NCU ||
905 		    type == NWAM_OBJECT_TYPE_UNKNOWN) {
906 			nwam_ncp_handle_t ncph;
907 			nwam_ncu_handle_t ncuh;
908 
909 			if ((ncph = determine_active_ncp()) != NULL) {
910 				ret = nwam_ncu_read(ncph, name, ncu_type, 0,
911 				    &ncuh);
912 				if (ret == NWAM_ENTITY_MULTIPLE_VALUES) {
913 					found = B_TRUE;
914 					if (nwam_ncu_read(ncph, name,
915 					    NWAM_NCU_TYPE_LINK, 0, &ncuh)
916 					    == NWAM_SUCCESS) {
917 						(void) list_ncu_cb(ncuh, ofmt);
918 						nwam_ncu_free(ncuh);
919 					}
920 					if (nwam_ncu_read(ncph, name,
921 					    NWAM_NCU_TYPE_INTERFACE, 0, &ncuh)
922 					    == NWAM_SUCCESS) {
923 						(void) list_ncu_cb(ncuh, ofmt);
924 						nwam_ncu_free(ncuh);
925 					}
926 				} else if (ret == NWAM_SUCCESS) {
927 					found = B_TRUE;
928 					(void) list_ncu_cb(ncuh, ofmt);
929 					nwam_ncu_free(ncuh);
930 				}
931 				nwam_ncp_free(ncph);
932 			}
933 		}
934 		if (type == NWAM_OBJECT_TYPE_LOC ||
935 		    type == NWAM_OBJECT_TYPE_UNKNOWN) {
936 			nwam_loc_handle_t loch;
937 			if (nwam_loc_read(name, 0, &loch) == NWAM_SUCCESS) {
938 				found = B_TRUE;
939 				(void) list_loc_cb(loch, ofmt);
940 				nwam_loc_free(loch);
941 			}
942 		}
943 		if (type == NWAM_OBJECT_TYPE_ENM ||
944 		    type == NWAM_OBJECT_TYPE_UNKNOWN) {
945 			nwam_enm_handle_t enmh;
946 			if (nwam_enm_read(name, 0, &enmh) == NWAM_SUCCESS) {
947 				found = B_TRUE;
948 				(void) list_enm_cb(enmh, ofmt);
949 				nwam_enm_free(enmh);
950 			}
951 		}
952 		/* If at least object is found, don't return error */
953 		if (found)
954 			ret = NWAM_SUCCESS;
955 		else
956 			ret = NWAM_ENTITY_NOT_FOUND;
957 	}
958 
959 	/* object-name not given in command-line */
960 	if (name == NULL) {
961 		/*
962 		 * If type given (type != UNKNOWN), just walk objects in that
963 		 * type.  Otherwise, walk all ncp, ncu, loc and enm.
964 		 */
965 		if (type == NWAM_OBJECT_TYPE_NCP ||
966 		    type == NWAM_OBJECT_TYPE_UNKNOWN) {
967 			ret = nwam_walk_ncps(list_ncp_cb, ofmt, 0, NULL);
968 			if (ret != NWAM_SUCCESS)
969 				goto done;
970 		}
971 		/* no UNKNOWN for NCUs.  They walked with active NCP above */
972 		if (type == NWAM_OBJECT_TYPE_NCU) {
973 			nwam_ncp_handle_t ncph;
974 			if ((ncph = determine_active_ncp()) != NULL) {
975 				ret = nwam_ncp_walk_ncus(ncph, list_ncu_cb,
976 				    ofmt, nwam_ncu_class_to_flag(ncu_class),
977 				    NULL);
978 				nwam_ncp_free(ncph);
979 				if (ret != NWAM_SUCCESS)
980 					goto done;
981 			}
982 		}
983 		if (type == NWAM_OBJECT_TYPE_LOC ||
984 		    type == NWAM_OBJECT_TYPE_UNKNOWN) {
985 			ret = nwam_walk_locs(list_loc_cb, ofmt,
986 			    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
987 			if (ret != NWAM_SUCCESS)
988 				goto done;
989 		}
990 		if (type == NWAM_OBJECT_TYPE_ENM ||
991 		    type == NWAM_OBJECT_TYPE_UNKNOWN) {
992 			ret = nwam_walk_enms(list_enm_cb, ofmt,
993 			    NWAM_FLAG_ACTIVATION_MODE_ALL, NULL);
994 			if (ret != NWAM_SUCCESS)
995 				goto done;
996 		}
997 	}
998 
999 done:
1000 	ofmt_close(ofmt);
1001 	if (ret == NWAM_ENTITY_NOT_FOUND && name != NULL)
1002 		die("no profile matched '%s'", name);
1003 	else if (ret != NWAM_SUCCESS)
1004 		die_nwamerr(ret, "list failed during walk");
1005 }
1006 
1007 /*
1008  * Print NWAM events.
1009  */
1010 static void
1011 eventhandler(nwam_event_t event)
1012 {
1013 	char description[DESCRIPTION_WIDTH];
1014 	char statestr[DESCRIPTION_WIDTH];
1015 	char objstr[DESCRIPTION_WIDTH];
1016 	char *object = NULL;
1017 	const char *action = NULL;
1018 	char *state = NULL;
1019 	boolean_t display = B_TRUE;
1020 	int i;
1021 	nwam_wlan_t *wlans;
1022 
1023 	(void) strlcpy(description, "-", sizeof (description));
1024 
1025 	switch (event->nwe_type) {
1026 	case NWAM_EVENT_TYPE_OBJECT_ACTION:
1027 		action = nwam_action_to_string
1028 		    (event->nwe_data.nwe_object_action.nwe_action);
1029 		(void) snprintf(objstr, sizeof (objstr), "%s %s",
1030 		    nwam_object_type_to_string
1031 		    (event->nwe_data.nwe_object_action.nwe_object_type),
1032 		    event->nwe_data.nwe_object_action.nwe_name);
1033 		object = objstr;
1034 		break;
1035 
1036 	case NWAM_EVENT_TYPE_OBJECT_STATE:
1037 		(void) snprintf(statestr, sizeof (statestr), "%s, %s",
1038 		    nwam_state_to_string
1039 		    (event->nwe_data.nwe_object_state.nwe_state),
1040 		    nwam_aux_state_to_string
1041 		    (event->nwe_data.nwe_object_state.nwe_aux_state));
1042 		state = statestr;
1043 
1044 		(void) snprintf(objstr, sizeof (objstr), "%s %s",
1045 		    nwam_object_type_to_string
1046 		    (event->nwe_data.nwe_object_state.nwe_object_type),
1047 		    event->nwe_data.nwe_object_state.nwe_name);
1048 		object = objstr;
1049 		break;
1050 
1051 	case NWAM_EVENT_TYPE_PRIORITY_GROUP:
1052 		(void) snprintf(description, DESCRIPTION_WIDTH,
1053 		    "priority-group: %d",
1054 		    event->nwe_data.nwe_priority_group_info.nwe_priority);
1055 		break;
1056 
1057 	case NWAM_EVENT_TYPE_WLAN_SCAN_REPORT:
1058 		(void) printf("%-*s \n", EVENT_WIDTH,
1059 		    nwam_event_type_to_string(event->nwe_type));
1060 		wlans = event->nwe_data.nwe_wlan_info.nwe_wlans;
1061 		for (i = 0;
1062 		    i < event->nwe_data.nwe_wlan_info.nwe_num_wlans;
1063 		    i++) {
1064 			(void) snprintf(description, DESCRIPTION_WIDTH,
1065 			    "%d: %c%c ESSID %s BSSID %s", i + 1,
1066 			    wlans[i].nww_selected ? 'S' : '-',
1067 			    wlans[i].nww_connected ? 'C' : '-',
1068 			    wlans[i].nww_essid, wlans[i].nww_bssid);
1069 			(void) printf("%-*s %-*s\n", EVENT_WIDTH, "-",
1070 			    DESCRIPTION_WIDTH, description);
1071 		}
1072 		display = B_FALSE;
1073 		break;
1074 
1075 	case NWAM_EVENT_TYPE_WLAN_NEED_CHOICE:
1076 		(void) printf("%-*s \n", EVENT_WIDTH,
1077 		    nwam_event_type_to_string(event->nwe_type));
1078 		display = B_FALSE;
1079 		break;
1080 
1081 	case NWAM_EVENT_TYPE_WLAN_NEED_KEY:
1082 		(void) printf("%-*s \n", EVENT_WIDTH,
1083 		    nwam_event_type_to_string(event->nwe_type));
1084 		display = B_FALSE;
1085 		break;
1086 
1087 	case NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT:
1088 		(void) snprintf(description, DESCRIPTION_WIDTH,
1089 		    gettext("connect to WLAN ESSID %s, BSSID %s %s"),
1090 		    event->nwe_data.nwe_wlan_info.nwe_wlans[0].nww_essid,
1091 		    event->nwe_data.nwe_wlan_info.nwe_wlans[0].nww_bssid,
1092 		    event->nwe_data.nwe_wlan_info.nwe_connected ?
1093 		    "succeeded" : "failed");
1094 		break;
1095 
1096 	case NWAM_EVENT_TYPE_INFO:
1097 		(void) snprintf(description, sizeof (description),
1098 		    "%s", event->nwe_data.nwe_info.nwe_message);
1099 		break;
1100 
1101 	case NWAM_EVENT_TYPE_IF_ACTION:
1102 		action = nwam_action_to_string
1103 		    (event->nwe_data.nwe_if_action.nwe_action);
1104 		object = event->nwe_data.nwe_if_action.nwe_name;
1105 		break;
1106 
1107 	case NWAM_EVENT_TYPE_IF_STATE:
1108 		object = event->nwe_data.nwe_if_state.nwe_name;
1109 		if (event->nwe_data.nwe_if_state.nwe_addr_valid) {
1110 			struct sockaddr_storage *address =
1111 			    &(event->nwe_data.nwe_if_state.nwe_addr);
1112 			struct sockaddr_storage *netmask =
1113 			    &(event->nwe_data.nwe_if_state.nwe_netmask);
1114 			struct sockaddr_in *v4addr;
1115 			struct sockaddr_in6 *v6addr;
1116 			char addrstr[NWAM_MAX_VALUE_LEN];
1117 			int plen = mask2plen((struct sockaddr *)netmask);
1118 
1119 			switch (address->ss_family) {
1120 			case AF_INET:
1121 				v4addr = (struct sockaddr_in *)address;
1122 				(void) inet_ntop(AF_INET, &v4addr->sin_addr,
1123 				    addrstr, sizeof (addrstr));
1124 				break;
1125 			case AF_INET6:
1126 				v6addr = (struct sockaddr_in6 *)address;
1127 				(void) inet_ntop(AF_INET6, &v6addr->sin6_addr,
1128 				    addrstr, sizeof (addrstr));
1129 				break;
1130 			}
1131 			(void) snprintf(statestr, sizeof (statestr),
1132 			    "flags %x addr %s/%d",
1133 			    event->nwe_data.nwe_if_state.nwe_flags,
1134 			    addrstr, plen);
1135 		} else {
1136 			(void) snprintf(statestr, sizeof (statestr),
1137 			    "flags %x", event->nwe_data.nwe_if_state.nwe_flags);
1138 		}
1139 		state = statestr;
1140 		break;
1141 
1142 	case NWAM_EVENT_TYPE_LINK_ACTION:
1143 		action = nwam_action_to_string
1144 		    (event->nwe_data.nwe_link_action.nwe_action);
1145 		object = event->nwe_data.nwe_link_action.nwe_name;
1146 		break;
1147 
1148 	case NWAM_EVENT_TYPE_LINK_STATE:
1149 		state = event->nwe_data.nwe_link_state.nwe_link_up ?
1150 		    "up" : "down";
1151 		object = event->nwe_data.nwe_link_state.nwe_name;
1152 		break;
1153 	}
1154 
1155 	if (object != NULL && action != NULL) {
1156 		(void) snprintf(description, sizeof (description),
1157 		    "%s -> action %s", object, action);
1158 	} else if (object != NULL && state != NULL) {
1159 		(void) snprintf(description, sizeof (description),
1160 		    "%s -> state %s", object, state);
1161 	}
1162 
1163 	if (display) {
1164 		(void) printf("%-*s %-*s\n", EVENT_WIDTH,
1165 		    nwam_event_type_to_string(event->nwe_type),
1166 		    DESCRIPTION_WIDTH,
1167 		    description);
1168 	}
1169 }
1170 
1171 /*
1172  * listens for events and displays them via the eventhandler() function above.
1173  */
1174 /* ARGSUSED */
1175 static void
1176 show_events_func(int argc, char *argv[])
1177 {
1178 	nwam_error_t err;
1179 	nwam_event_t event;
1180 
1181 	err = nwam_events_init();
1182 
1183 	if (err != NWAM_SUCCESS)
1184 		die_nwamerr(err, "could not bind to receive events");
1185 
1186 	/* print header */
1187 	(void) printf("%-*s %-*s\n", EVENT_WIDTH, "EVENT",
1188 	    DESCRIPTION_WIDTH, "DESCRIPTION");
1189 
1190 	do {
1191 		/*
1192 		 * Needed for stdout redirection to ensure event output is
1193 		 * regularly flushed to file.
1194 		 */
1195 		(void) fflush(stdout);
1196 		err = nwam_event_wait(&event);
1197 		if (err == NWAM_SUCCESS) {
1198 			eventhandler(event);
1199 			nwam_event_free(event);
1200 		}
1201 	} while (err == NWAM_SUCCESS);
1202 	die_nwamerr(err, "event handling stopped");
1203 }
1204 
1205 /* May need to convert case-insensitive link name match to case-sensitive one */
1206 static nwam_error_t
1207 name_to_linkname(char *name, char **linknamep)
1208 {
1209 	nwam_error_t err;
1210 	nwam_ncp_handle_t ncph = NULL;
1211 	nwam_ncu_handle_t ncuh = NULL;
1212 
1213 	if ((ncph = determine_active_ncp()) == NULL)
1214 		return (NWAM_ENTITY_NOT_FOUND);
1215 
1216 	err = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_LINK, 0, &ncuh);
1217 	if (err == NWAM_SUCCESS)
1218 		err = nwam_ncu_get_name(ncuh, linknamep);
1219 
1220 	nwam_ncp_free(ncph);
1221 	nwam_ncu_free(ncuh);
1222 	return (err);
1223 }
1224 
1225 static void
1226 scan_wifi_func(int argc, char *argv[])
1227 {
1228 	nwam_error_t err;
1229 	char *linkname = NULL;
1230 
1231 	if (argc != 1)
1232 		die_usage(CMD_SCAN_WIFI);
1233 
1234 	if ((err = name_to_linkname(argv[0], &linkname)) != NWAM_SUCCESS)
1235 		die_nwamerr(err, "scan request failed for %s", argv[0]);
1236 
1237 	err = nwam_wlan_scan(linkname);
1238 
1239 	if (err != NWAM_SUCCESS)
1240 		die_nwamerr(err, "scan request failed for %s", linkname);
1241 
1242 	free(linkname);
1243 }
1244 
1245 static void
1246 select_wifi_func(int argc, char *argv[])
1247 {
1248 	nwam_error_t err;
1249 	char *linkname = NULL;
1250 	uint_t i, choice, num_wlans = 0;
1251 	uint32_t security_mode;
1252 	boolean_t have_key = B_FALSE;
1253 	nwam_wlan_t *wlans = NULL;
1254 	char choicestr[NWAM_MAX_VALUE_LEN];
1255 	char modestr[NWAM_MAX_VALUE_LEN];
1256 	char essid[NWAM_MAX_VALUE_LEN];
1257 	char bssid[NWAM_MAX_VALUE_LEN];
1258 
1259 	if (argc != 1)
1260 		die_usage(CMD_SELECT_WIFI);
1261 
1262 	if ((err = name_to_linkname(argv[0], &linkname)) != NWAM_SUCCESS) {
1263 		die_nwamerr(err, "could not retrieve scan results for %s",
1264 		    argv[0]);
1265 	}
1266 	err = nwam_wlan_get_scan_results(linkname, &num_wlans, &wlans);
1267 
1268 	if (err != NWAM_SUCCESS) {
1269 		die_nwamerr(err, "could not retrieve scan results for %s",
1270 		    linkname);
1271 	}
1272 	bssid[0] = '\0';
1273 
1274 	/* Loop until valid selection made */
1275 	for (;;) {
1276 		(void) printf("\n");
1277 		/* Display WLAN choices for user to select from */
1278 		for (i = 0; i < num_wlans; i++) {
1279 			(void) printf("%d: ESSID %s BSSID %s\n",
1280 			    i + 1, wlans[i].nww_essid, wlans[i].nww_bssid);
1281 		}
1282 		(void) printf(gettext("%d: Other\n"), i + 1);
1283 
1284 		(void) printf(gettext("\nChoose WLAN to connect to [1-%d]: "),
1285 		    i + 1);
1286 
1287 		if (fgets(choicestr, sizeof (choicestr), stdin) != NULL &&
1288 		    (choice = atoi(choicestr)) >= 1 && choice <= (i + 1))
1289 			break;
1290 	}
1291 
1292 	if (choice == i + 1 || wlans[choice - 1].nww_essid[0] == '\0') {
1293 		nwam_known_wlan_handle_t kwh = NULL;
1294 		nwam_value_t keynameval = NULL;
1295 
1296 		/* If "Other" or a hidden WLAN is selected, ask for ESSID */
1297 		do {
1298 			(void) printf(gettext("\nEnter WLAN name: "));
1299 			while (fgets(essid, sizeof (essid), stdin) == NULL) {}
1300 			essid[strlen(essid) - 1] = '\0';
1301 		} while (strspn(essid, " \t") == strlen(essid));
1302 
1303 		/* If "Other" was selected, secmode must be specified. */
1304 		if (choice == i + 1) {
1305 			for (;;) {
1306 				(void) printf(gettext("1: None\n"));
1307 				(void) printf(gettext("2: WEP\n"));
1308 				(void) printf(gettext("3: WPA\n"));
1309 				(void) printf(gettext("Enter security mode: "));
1310 				if (fgets(modestr, sizeof (choicestr), stdin)
1311 				    != NULL &&
1312 				    (security_mode = atoi(modestr)) >= 1 &&
1313 				    security_mode <= 3)
1314 					break;
1315 			}
1316 		} else {
1317 			security_mode = wlans[choice - 1].nww_security_mode;
1318 			have_key = wlans[choice - 1].nww_have_key;
1319 		}
1320 
1321 		/*
1322 		 * We have to determine if we have a key for this ESSID from
1323 		 * the known WLAN list, since we cannot determine this from
1324 		 * the scan results.
1325 		 */
1326 		if (nwam_known_wlan_read(essid, 0, &kwh) == NWAM_SUCCESS &&
1327 		    nwam_known_wlan_get_prop_value(kwh,
1328 		    NWAM_KNOWN_WLAN_PROP_KEYNAME, &keynameval) == NWAM_SUCCESS)
1329 			have_key = B_TRUE;
1330 		else
1331 			have_key = B_FALSE;
1332 
1333 		nwam_value_free(keynameval);
1334 		nwam_known_wlan_free(kwh);
1335 	} else {
1336 		(void) strlcpy(essid, wlans[choice - 1].nww_essid,
1337 		    sizeof (essid));
1338 		(void) strlcpy(bssid, wlans[choice - 1].nww_bssid,
1339 		    sizeof (bssid));
1340 		security_mode = wlans[choice - 1].nww_security_mode;
1341 		have_key = wlans[choice - 1].nww_have_key;
1342 	}
1343 
1344 	if (security_mode != DLADM_WLAN_SECMODE_NONE && !have_key) {
1345 		uint_t keyslot = 1;
1346 		char key[NWAM_MAX_VALUE_LEN];
1347 		char slotstr[NWAM_MAX_VALUE_LEN];
1348 
1349 		do {
1350 			(void) printf(gettext("\nEnter WLAN key for "
1351 			    "ESSID %s: "), essid);
1352 			while (fgets(key, sizeof (key), stdin) == NULL) {}
1353 			key[strlen(key) - 1] = '\0';
1354 		} while (strspn(key, " \t") == strlen(key));
1355 
1356 		if (security_mode == DLADM_WLAN_SECMODE_WEP) {
1357 			for (;;) {
1358 				(void) printf(
1359 				    gettext("\nEnter key slot [1-4]: "));
1360 				if (fgets(slotstr, sizeof (slotstr), stdin)
1361 				    != NULL && (keyslot = atoi(slotstr)) >= 1 &&
1362 				    keyslot <= 4)
1363 					break;
1364 			}
1365 		}
1366 
1367 		err = nwam_wlan_set_key(linkname, essid, bssid, security_mode,
1368 		    keyslot, key);
1369 		if (err != NWAM_SUCCESS)
1370 			die_nwamerr(err, "could not set WiFi key");
1371 	}
1372 	err = nwam_wlan_select(linkname, essid, bssid[0] != '\0' ? bssid : NULL,
1373 	    security_mode, B_TRUE);
1374 	if (err != NWAM_SUCCESS)
1375 		die_nwamerr(err, "could not select WLAN %s", essid);
1376 	free(wlans);
1377 	free(linkname);
1378 }
1379 
1380 int
1381 main(int argc, char *argv[])
1382 {
1383 	int i;
1384 	char *state;
1385 
1386 	(void) setlocale(LC_ALL, "");
1387 	(void) textdomain(TEXT_DOMAIN);
1388 
1389 	if ((execname = strrchr(argv[0], '/')) == NULL)
1390 		execname = argv[0];
1391 	else
1392 		execname++;
1393 
1394 	if (argc < 2) {
1395 		usage(B_FALSE);
1396 		exit(EXIT_FAILURE);
1397 	}
1398 
1399 	for (i = CMD_MIN; i <= CMD_MAX; i++) {
1400 		if (strcmp(argv[1], cmd_to_str(i)) == 0) {
1401 			if (cmdtab[i].cmd_needs_nwamd) {
1402 				state = smf_get_state(NWAM_FMRI);
1403 				if (state == NULL || strcmp(state,
1404 				    SCF_STATE_STRING_ONLINE) != 0) {
1405 					free(state);
1406 					die("enable '%s' to use '%s %s'",
1407 					    NWAM_FMRI, execname,
1408 					    cmd_to_str(cmdtab[i].cmd_num));
1409 				}
1410 				free(state);
1411 			}
1412 
1413 			cmdtab[i].cmd_handler(argc - 2, &(argv[2]));
1414 
1415 			exit(EXIT_SUCCESS);
1416 		}
1417 	}
1418 
1419 	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
1420 	    execname, argv[1]);
1421 	usage(B_FALSE);
1422 
1423 	return (1);
1424 }
1425