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