/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /* * nwamadm is a command interpreter to administer NWAM profiles. It * is all in C (i.e., no lex/yacc), and all the argument passing is * argc/argv based. main() calls the command's handler function, * which first calls parse_argv() to parse the input arguments and set * approriate variables for each command. The rest of the program is * helper functions for the handler functions. */ #include <arpa/inet.h> #include <assert.h> #include <errno.h> #include <libdlwlan.h> #include <libnwam.h> #include <libscf.h> #include <locale.h> #include <netinet/in.h> #include <ofmt.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ #endif typedef void (cmd_func_t)(int, char **); struct cmd { uint_t cmd_num; /* command number */ const char *cmd_name; /* command name */ cmd_func_t *cmd_handler; /* function to call */ const char *cmd_usage; /* short form help */ const char *cmd_desc; /* command description */ boolean_t cmd_needs_nwamd; /* nwam needs to run */ }; /* constants for commands */ #define CMD_HELP 0 #define CMD_ENABLE 1 #define CMD_DISABLE 2 #define CMD_LIST 3 #define CMD_SHOW_EVENTS 4 #define CMD_SCAN_WIFI 5 #define CMD_SELECT_WIFI 6 #define CMD_MIN CMD_HELP #define CMD_MAX CMD_SELECT_WIFI /* functions to call */ static cmd_func_t help_func, enable_func, disable_func, list_func; static cmd_func_t show_events_func, scan_wifi_func, select_wifi_func; static ofmt_cb_t print_list_cb; /* table of commands and usage */ static struct cmd cmdtab[] = { { CMD_HELP, "help", help_func, "help", "Print this usage message.", B_FALSE }, { CMD_ENABLE, "enable", enable_func, "enable [-p <profile-type>] [-c <ncu-class>] <object-name>", "Enable the specified profile.", B_FALSE }, { CMD_DISABLE, "disable", disable_func, "disable [-p <profile-type>] [-c <ncu-class>] <object-name>", "Disable the specified profile.", B_FALSE }, { CMD_LIST, "list", list_func, "list [-x] [-p <profile-type>] [-c <ncu-class>] [<object-name>]", "List profiles and their current states.", B_TRUE }, { CMD_SHOW_EVENTS, "show-events", show_events_func, "show-events", "Display all events.", B_TRUE }, { CMD_SCAN_WIFI, "scan-wifi", scan_wifi_func, "scan-wifi <link-name>", "Request a WiFi scan for the selected link.", B_TRUE }, { CMD_SELECT_WIFI, "select-wifi", select_wifi_func, "select-wifi <link-name>", "Make a WLAN selection from the last WiFi scan.", B_TRUE } }; /* Structure for "nwamadm list" output */ typedef struct profile_entry { nwam_object_type_t p_type; nwam_ncu_class_t p_ncu_class; char p_name[NWAM_MAX_NAME_LEN]; nwam_state_t p_state; nwam_aux_state_t p_aux_state; } profile_entry_t; /* widths of colums for printing */ #define TYPE_WIDTH 12 /* width of TYPE column */ #define PROFILE_WIDTH 15 /* width of PROFILE column */ #define STATE_WIDTH 15 /* width of STATE column */ #define AUXSTATE_WIDTH 36 /* width of AUXILIARY STATE column */ #define EVENT_WIDTH 22 /* width of EVENT column */ #define DESCRIPTION_WIDTH 56 /* width of DESCRIPTION column */ /* id for columns of "nwamadm list" */ typedef enum { LIST_TYPE, LIST_PROFILE, LIST_STATE, LIST_AUXSTATE } list_field_id_t; static const ofmt_field_t list_fields[] = { /* header, width, id, callback */ { "TYPE", TYPE_WIDTH, LIST_TYPE, print_list_cb }, { "PROFILE", PROFILE_WIDTH, LIST_PROFILE, print_list_cb }, { "STATE", STATE_WIDTH, LIST_STATE, print_list_cb }, { "AUXILIARY STATE", AUXSTATE_WIDTH, LIST_AUXSTATE, print_list_cb }, { NULL, 0, 0, NULL } }; /* Global variables */ /* set early in main(), never modified thereafter, used all over the place */ static char *execname; /* whether the auxilary states are to be printed or not */ static boolean_t extended_list = B_FALSE; /* Functions */ static const char * cmd_to_str(int cmd_num) { assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); return (cmdtab[cmd_num].cmd_name); } /* returns description of given command */ static const char * long_help(int cmd_num) { assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); return (gettext(cmdtab[cmd_num].cmd_desc)); } /* * Called with explicit B_TRUE when help is explicitly required, * B_FALSE for errors */ static void usage(boolean_t explicit) { int i; FILE *fd = explicit ? stdout : stderr; (void) fprintf(fd, gettext("usage: <subcommand> <args> ...\n")); for (i = CMD_MIN; i <= CMD_MAX; i++) { (void) fprintf(fd, "\t%s\n", cmdtab[i].cmd_usage); if (explicit) (void) fprintf(fd, "\t\t%s\n\n", long_help(i)); } } /* PRINTFLIKE1 */ static void die(const char *format, ...) { va_list alist; format = gettext(format); (void) fprintf(stderr, "%s: ", execname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) fprintf(stderr, "\n"); exit(EXIT_FAILURE); } /* PRINTFLIKE2 */ static void die_nwamerr(nwam_error_t err, const char *format, ...) { va_list alist; format = gettext(format); (void) fprintf(stderr, "%s: ", execname); va_start(alist, format); (void) vfprintf(stderr, format, alist); va_end(alist); (void) fprintf(stderr, ": %s\n", nwam_strerror(err)); exit(EXIT_FAILURE); } /* prints the usage for cmd_num and exits */ static void die_usage(int cmd_num) { assert(cmd_num >= CMD_MIN && cmd_num <= CMD_MAX); (void) fprintf(stderr, "%s: %s\n", gettext("usage"), cmdtab[cmd_num].cmd_usage); (void) fprintf(stderr, "\t%s\n", long_help(cmd_num)); exit(EXIT_FAILURE); } /* * Prints the usage and description of all commands */ /* ARGSUSED */ static void help_func(int argc, char *argv[]) { usage(B_TRUE); } /* determines if the NCP is active or not. If so, sets arg and halts walk. */ static int active_ncp_callback(nwam_ncp_handle_t ncph, void *arg) { char **namep = arg; nwam_state_t state = NWAM_STATE_UNINITIALIZED; nwam_aux_state_t aux; (void) nwam_ncp_get_state(ncph, &state, &aux); if (state == NWAM_STATE_ONLINE) { if (nwam_ncp_get_name(ncph, namep) != NWAM_SUCCESS) *namep = NULL; return (1); } return (0); } /* find the currently active NCP and returns its handle */ static nwam_ncp_handle_t determine_active_ncp() { char *active_ncp; nwam_ncp_handle_t ncph; nwam_error_t ret; if (nwam_walk_ncps(active_ncp_callback, &active_ncp, 0, NULL) == NWAM_WALK_HALTED) { if (active_ncp == NULL) return (NULL); /* retrieve the NCP handle */ ret = nwam_ncp_read(active_ncp, 0, &ncph); free(active_ncp); if (ret == NWAM_SUCCESS) return (ncph); } return (NULL); } /* check if the given name is a valid loc, test by reading the given loc */ static boolean_t valid_loc(const char *name) { nwam_loc_handle_t loch; if (nwam_loc_read(name, 0, &loch) != NWAM_SUCCESS) return (B_FALSE); nwam_loc_free(loch); return (B_TRUE); } static boolean_t valid_enm(const char *name) { nwam_enm_handle_t enmh; if (nwam_enm_read(name, 0, &enmh) != NWAM_SUCCESS) return (B_FALSE); nwam_enm_free(enmh); return (B_TRUE); } static boolean_t valid_ncp(const char *name) { nwam_ncp_handle_t ncph; if (nwam_ncp_read(name, 0, &ncph) != NWAM_SUCCESS) return (B_FALSE); nwam_ncp_free(ncph); return (B_TRUE); } static boolean_t valid_ncu(const char *name) { nwam_ncp_handle_t ncph; nwam_ncu_handle_t ncuh; nwam_error_t ret; if ((ncph = determine_active_ncp()) == NULL) return (B_FALSE); ret = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_ANY, 0, &ncuh); nwam_ncp_free(ncph); if (ret != NWAM_SUCCESS && ret != NWAM_ENTITY_MULTIPLE_VALUES) return (B_FALSE); nwam_ncu_free(ncuh); return (B_TRUE); } /* * Given a name, returns object type (loc, enm, ncp, or ncu) and how many * objects matched that name. */ static nwam_object_type_t determine_object_type(const char *name, int *num) { nwam_object_type_t type; int n = 0; /* see if a valid loc, enm, ncp and/or ncu exists with given name */ if (valid_loc(name)) { n++; type = NWAM_OBJECT_TYPE_LOC; } if (valid_enm(name)) { n++; type = NWAM_OBJECT_TYPE_ENM; } if (valid_ncp(name)) { n++; type = NWAM_OBJECT_TYPE_NCP; } if (valid_ncu(name)) { n++; type = NWAM_OBJECT_TYPE_NCU; } /* if n > 1, then it means *type was set multiple times, undo it */ if (n != 1) type = NWAM_OBJECT_TYPE_UNKNOWN; *num = n; return (type); } /* * Parses argv array and populates object_type and name. * Program exits on failure. */ static void parse_argv(int argc, char *argv[], int cmd_num, nwam_object_type_t *object_type, nwam_ncu_type_t *ncu_type, nwam_ncu_class_t *ncu_class, const char **name) { int arg; nwam_object_type_t type = NWAM_OBJECT_TYPE_UNKNOWN; uint64_t ncu = NWAM_NCU_TYPE_ANY; uint64_t class = NWAM_NCU_CLASS_ANY; /* check argv for option */ optind = 0; while ((arg = getopt(argc, argv, "?p:c:x")) != EOF) { switch (arg) { case 'p': type = nwam_string_to_object_type(optarg); if (type == NWAM_OBJECT_TYPE_UNKNOWN) die("Invalid profile-type: %s", optarg); break; case 'c': if (nwam_value_string_get_uint64(NWAM_NCU_PROP_CLASS, optarg, &class) != NWAM_SUCCESS) { die("Invalid ncu-class: %s", optarg); } ncu = nwam_ncu_class_to_type(class); if (ncu == NWAM_NCU_TYPE_ANY || ncu == NWAM_NCU_TYPE_UNKNOWN) die("Invalid ncu-class: %s", optarg); break; case 'x': /* -x is only for list */ if (cmd_num != CMD_LIST) die("-x can only be used with 'list'"); extended_list = B_TRUE; break; case '?': default: die_usage(cmd_num); } } if (ncu != NWAM_NCU_TYPE_ANY) { /* If -c is given, -p must be NCU. If unspecified, assume NCU */ if (type != NWAM_OBJECT_TYPE_UNKNOWN && type != NWAM_OBJECT_TYPE_NCU) die("'-c <ncu-class>' can only be used for ncu"); type = NWAM_OBJECT_TYPE_NCU; } /* name is mandatory for enable and disable, but not for list */ if (optind == (argc-1)) *name = argv[optind]; else if (argc != optind) die("too many profile names given"); else if (cmd_num != CMD_LIST) die("no profile name given"); /* * No need to determine type for list. * If -p is not given for enable or disable, then determine type. */ if (cmd_num != CMD_LIST && type == NWAM_OBJECT_TYPE_UNKNOWN) { int num = 0; type = determine_object_type(*name, &num); if (num == 0) { die("no profile matched '%s'", *name); } else if (num > 1) { die("more than one profile matched '%s' - use " "'-p <profile-type>' to specify a profile type.", *name); } } *object_type = type; *ncu_type = ncu; *ncu_class = class; } /* Enables/Disables profiles depending on boolean */ static nwam_error_t loc_action(const char *name, boolean_t enable, char **realnamep) { nwam_loc_handle_t loch; nwam_error_t ret; if ((ret = nwam_loc_read(name, 0, &loch)) != NWAM_SUCCESS) return (ret); if (enable) ret = nwam_loc_enable(loch); else ret = nwam_loc_disable(loch); (void) nwam_loc_get_name(loch, realnamep); nwam_loc_free(loch); return (ret); } static nwam_error_t enm_action(const char *name, boolean_t enable, char **realnamep) { nwam_enm_handle_t enmh; nwam_error_t ret; if ((ret = nwam_enm_read(name, 0, &enmh)) != NWAM_SUCCESS) return (ret); if (enable) ret = nwam_enm_enable(enmh); else ret = nwam_enm_disable(enmh); (void) nwam_enm_get_name(enmh, realnamep); nwam_enm_free(enmh); return (ret); } static nwam_error_t ncu_action(const char *name, nwam_ncp_handle_t ncph, nwam_ncu_type_t type, boolean_t enable, char **realnamep) { nwam_ncu_handle_t ncuh; nwam_error_t ret; boolean_t retrieved_ncph = B_FALSE; if (ncph == NULL) { if ((ncph = determine_active_ncp()) == NULL) return (NWAM_ENTITY_NOT_FOUND); retrieved_ncph = B_TRUE; } ret = nwam_ncu_read(ncph, name, type, 0, &ncuh); switch (ret) { case NWAM_SUCCESS: if (enable) ret = nwam_ncu_enable(ncuh); else ret = nwam_ncu_disable(ncuh); (void) nwam_ncu_get_name(ncuh, realnamep); nwam_ncu_free(ncuh); break; case NWAM_ENTITY_MULTIPLE_VALUES: /* Call ncu_action() for link and interface types */ ret = ncu_action(name, ncph, NWAM_NCU_TYPE_LINK, enable, realnamep); if (ret != NWAM_SUCCESS) break; ret = ncu_action(name, ncph, NWAM_NCU_TYPE_INTERFACE, enable, realnamep); break; } if (retrieved_ncph) nwam_ncp_free(ncph); return (ret); } /* * If more than one type of profile with the same name, return error. * In such situations, the -p option must be used. * If a location is enabled when a different one is already enabled, then * that location is disabled automatically by nwamd. */ static void enable_func(int argc, char *argv[]) { nwam_error_t ret; nwam_object_type_t type = NWAM_OBJECT_TYPE_UNKNOWN; nwam_ncu_type_t ncu_type = NWAM_NCU_TYPE_ANY; nwam_ncu_class_t ncu_class = NWAM_NCU_CLASS_ANY; const char *name; char *realname = NULL; /* parse_argv() returns only on success */ parse_argv(argc, argv, CMD_ENABLE, &type, &ncu_type, &ncu_class, &name); /* * NCPs and Locations don't need to disable the currently active * profile - nwamd automatically switches to the new active profile. * and will disable it if necessary. */ /* activate given profile */ switch (type) { case NWAM_OBJECT_TYPE_LOC: ret = loc_action(name, B_TRUE, &realname); break; case NWAM_OBJECT_TYPE_ENM: ret = enm_action(name, B_TRUE, &realname); break; case NWAM_OBJECT_TYPE_NCP: { nwam_ncp_handle_t ncph; if ((ret = nwam_ncp_read(name, 0, &ncph)) != NWAM_SUCCESS) break; ret = nwam_ncp_enable(ncph); (void) nwam_ncp_get_name(ncph, &realname); nwam_ncp_free(ncph); break; } case NWAM_OBJECT_TYPE_NCU: ret = ncu_action(name, NULL, ncu_type, B_TRUE, &realname); break; } switch (ret) { case NWAM_SUCCESS: (void) printf(gettext("Enabling %s '%s'\n"), nwam_object_type_to_string(type), realname != NULL ? realname : name); break; case NWAM_ENTITY_NOT_MANUAL: die("Only profiles with manual activation-mode can be enabled"); break; default: die_nwamerr(ret, "Could not enable %s '%s'", nwam_object_type_to_string(type), realname != NULL ? realname : name); } free(realname); } /* * Disables a given profile. Similar to enable, the -p option must be used * if more than one type of profile is matched by the given name. */ static void disable_func(int argc, char *argv[]) { nwam_error_t ret; nwam_object_type_t type = NWAM_OBJECT_TYPE_UNKNOWN; nwam_ncu_type_t ncu_type = NWAM_NCU_TYPE_ANY; nwam_ncu_class_t ncu_class = NWAM_NCU_CLASS_ANY; const char *name; char *realname = NULL; /* parse_argv() returns only on success */ parse_argv(argc, argv, CMD_DISABLE, &type, &ncu_type, &ncu_class, &name); /* deactivate the given profile */ switch (type) { case NWAM_OBJECT_TYPE_LOC: ret = loc_action(name, B_FALSE, &realname); break; case NWAM_OBJECT_TYPE_ENM: ret = enm_action(name, B_FALSE, &realname); break; case NWAM_OBJECT_TYPE_NCU: ret = ncu_action(name, NULL, ncu_type, B_FALSE, &realname); break; case NWAM_OBJECT_TYPE_NCP: die("ncp's cannot be disabled. Enable a different ncp to " "switch to that ncp"); } switch (ret) { case NWAM_SUCCESS: (void) printf(gettext("Disabling %s '%s'\n"), nwam_object_type_to_string(type), realname != NULL ? realname : name); break; case NWAM_ENTITY_NOT_MANUAL: die("Only profiles with manual activation-mode can be " "disabled"); break; default: die_nwamerr(ret, "Could not disable %s '%s'", nwam_object_type_to_string(type), realname != NULL ? realname : name); } free(realname); } /* prints each column */ static boolean_t print_list_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) { profile_entry_t *pent = ofarg->ofmt_cbarg; switch (ofarg->ofmt_id) { case LIST_TYPE: /* ncu:ip or ncu:phys for NCUs; ncp, loc, enm for others */ if (pent->p_type == NWAM_OBJECT_TYPE_NCU) { const char *class; if (nwam_uint64_get_value_string(NWAM_NCU_PROP_CLASS, pent->p_ncu_class, &class) != NWAM_SUCCESS) class = ""; /* empty */ (void) snprintf(buf, bufsize, "%s:%s", nwam_object_type_to_string(pent->p_type), class); } else { (void) strlcpy(buf, nwam_object_type_to_string(pent->p_type), bufsize); } break; case LIST_PROFILE: (void) strlcpy(buf, pent->p_name, bufsize); break; case LIST_STATE: (void) strlcpy(buf, nwam_state_to_string(pent->p_state), bufsize); break; case LIST_AUXSTATE: (void) strlcpy(buf, nwam_aux_state_to_string(pent->p_aux_state), bufsize); break; default: die("invalid print_list_cb() input: %d", ofarg->ofmt_id); break; } return (B_TRUE); } /* returns the state and auxilliary state of the object */ static nwam_state_t determine_object_state(nwam_object_type_t type, void *handle, nwam_aux_state_t *aux_statep) { nwam_state_t state; nwam_aux_state_t astate; nwam_error_t ret; switch (type) { case NWAM_OBJECT_TYPE_ENM: ret = nwam_enm_get_state(handle, &state, &astate); break; case NWAM_OBJECT_TYPE_LOC: ret = nwam_loc_get_state(handle, &state, &astate); break; case NWAM_OBJECT_TYPE_NCP: ret = nwam_ncp_get_state(handle, &state, &astate); break; case NWAM_OBJECT_TYPE_NCU: ret = nwam_ncu_get_state(handle, &state, &astate); break; default: /* NOTREACHED */ break; } if (ret == NWAM_PERMISSION_DENIED) { die_nwamerr(ret, "could not get object state"); } else if (ret != NWAM_SUCCESS) { state = NWAM_STATE_UNINITIALIZED; astate = NWAM_AUX_STATE_UNINITIALIZED; } if (aux_statep != NULL) *aux_statep = astate; return (state); } /* populate profile_entry_t with values for object with given handle */ static int add_to_profile_entry(nwam_object_type_t type, void *handle, profile_entry_t *pent) { char *name; nwam_error_t ret; pent->p_type = type; if (type == NWAM_OBJECT_TYPE_NCU) { nwam_ncu_class_t class; if ((ret = nwam_ncu_get_ncu_class(handle, &class)) != NWAM_SUCCESS) return (ret); pent->p_ncu_class = class; } else { pent->p_ncu_class = -1; } switch (type) { case NWAM_OBJECT_TYPE_ENM: ret = nwam_enm_get_name(handle, &name); break; case NWAM_OBJECT_TYPE_LOC: ret = nwam_loc_get_name(handle, &name); break; case NWAM_OBJECT_TYPE_NCP: ret = nwam_ncp_get_name(handle, &name); break; case NWAM_OBJECT_TYPE_NCU: ret = nwam_ncu_get_name(handle, &name); break; default: /* NOTREACHED */ break; } if (ret != NWAM_SUCCESS) { return (ret); } (void) strlcpy(pent->p_name, name, sizeof (pent->p_name)); free(name); pent->p_state = determine_object_state(type, handle, &pent->p_aux_state); return (NWAM_SUCCESS); } /* callback functions used by walk */ static int list_ncu_cb(nwam_ncu_handle_t ncuh, void *arg) { ofmt_handle_t ofmt = arg; profile_entry_t pent; nwam_error_t ret; bzero(&pent, sizeof (profile_entry_t)); ret = add_to_profile_entry(NWAM_OBJECT_TYPE_NCU, ncuh, &pent); if (ret != NWAM_SUCCESS) die_nwamerr(ret, "could not add ncu to list"); ofmt_print(ofmt, &pent); return (0); } static int list_ncp_cb(nwam_ncp_handle_t ncph, void *arg) { ofmt_handle_t ofmt = arg; profile_entry_t pent; nwam_error_t ret; nwam_state_t state; bzero(&pent, sizeof (profile_entry_t)); ret = add_to_profile_entry(NWAM_OBJECT_TYPE_NCP, ncph, &pent); if (ret != NWAM_SUCCESS) die_nwamerr(ret, "could not add ncp to list"); ofmt_print(ofmt, &pent); state = determine_object_state(NWAM_OBJECT_TYPE_NCP, ncph, NULL); if (state == NWAM_STATE_ONLINE) { (void) nwam_ncp_walk_ncus(ncph, list_ncu_cb, ofmt, NWAM_FLAG_NCU_TYPE_ALL, NULL); } return (0); } static int list_loc_cb(nwam_loc_handle_t loch, void *arg) { ofmt_handle_t ofmt = arg; profile_entry_t pent; nwam_error_t ret; bzero(&pent, sizeof (profile_entry_t)); ret = add_to_profile_entry(NWAM_OBJECT_TYPE_LOC, loch, &pent); if (ret != NWAM_SUCCESS) die_nwamerr(ret, "could not add loc to list"); ofmt_print(ofmt, &pent); return (0); } static int list_enm_cb(nwam_enm_handle_t enmh, void *arg) { ofmt_handle_t ofmt = arg; profile_entry_t pent; nwam_error_t ret; bzero(&pent, sizeof (profile_entry_t)); ret = add_to_profile_entry(NWAM_OBJECT_TYPE_ENM, enmh, &pent); if (ret != NWAM_SUCCESS) die_nwamerr(ret, "could not add enm to list"); ofmt_print(ofmt, &pent); return (0); } /* * lists all profiles and their state */ static void list_func(int argc, char *argv[]) { nwam_error_t ret = NWAM_SUCCESS; nwam_object_type_t type = NWAM_OBJECT_TYPE_UNKNOWN; nwam_ncu_type_t ncu_type = NWAM_NCU_TYPE_ANY; nwam_ncu_class_t ncu_class = NWAM_NCU_CLASS_ANY; char *name = NULL; ofmt_handle_t ofmt; ofmt_status_t oferr; char *default_fields = "type,profile,state"; char *extended_fields = "type,profile,state,auxiliary state"; char *fields = NULL; /* parse_argv() returns only on success */ parse_argv(argc, argv, CMD_LIST, &type, &ncu_type, &ncu_class, (const char **)&name); if (extended_list) fields = extended_fields; else fields = default_fields; oferr = ofmt_open(fields, list_fields, 0, 0, &ofmt); if (oferr != OFMT_SUCCESS) { char buf[OFMT_BUFSIZE]; (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf)); die("ofmt_open() failed: %s", buf); } /* object-name given in command-line */ if (name != NULL) { boolean_t found = B_FALSE; /* * If objects with different types have the same name * (type = UNKNOWN), then try to open handle for each object * and print if successful. */ if (type == NWAM_OBJECT_TYPE_NCP || type == NWAM_OBJECT_TYPE_UNKNOWN) { nwam_ncp_handle_t ncph; if (nwam_ncp_read(name, 0, &ncph) == NWAM_SUCCESS) { found = B_TRUE; (void) list_ncp_cb(ncph, ofmt); nwam_ncp_free(ncph); } } if (type == NWAM_OBJECT_TYPE_NCU || type == NWAM_OBJECT_TYPE_UNKNOWN) { nwam_ncp_handle_t ncph; nwam_ncu_handle_t ncuh; if ((ncph = determine_active_ncp()) != NULL) { ret = nwam_ncu_read(ncph, name, ncu_type, 0, &ncuh); if (ret == NWAM_ENTITY_MULTIPLE_VALUES) { found = B_TRUE; if (nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_LINK, 0, &ncuh) == NWAM_SUCCESS) { (void) list_ncu_cb(ncuh, ofmt); nwam_ncu_free(ncuh); } if (nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_INTERFACE, 0, &ncuh) == NWAM_SUCCESS) { (void) list_ncu_cb(ncuh, ofmt); nwam_ncu_free(ncuh); } } else if (ret == NWAM_SUCCESS) { found = B_TRUE; (void) list_ncu_cb(ncuh, ofmt); nwam_ncu_free(ncuh); } nwam_ncp_free(ncph); } } if (type == NWAM_OBJECT_TYPE_LOC || type == NWAM_OBJECT_TYPE_UNKNOWN) { nwam_loc_handle_t loch; if (nwam_loc_read(name, 0, &loch) == NWAM_SUCCESS) { found = B_TRUE; (void) list_loc_cb(loch, ofmt); nwam_loc_free(loch); } } if (type == NWAM_OBJECT_TYPE_ENM || type == NWAM_OBJECT_TYPE_UNKNOWN) { nwam_enm_handle_t enmh; if (nwam_enm_read(name, 0, &enmh) == NWAM_SUCCESS) { found = B_TRUE; (void) list_enm_cb(enmh, ofmt); nwam_enm_free(enmh); } } /* If at least object is found, don't return error */ if (found) ret = NWAM_SUCCESS; else ret = NWAM_ENTITY_NOT_FOUND; } /* object-name not given in command-line */ if (name == NULL) { /* * If type given (type != UNKNOWN), just walk objects in that * type. Otherwise, walk all ncp, ncu, loc and enm. */ if (type == NWAM_OBJECT_TYPE_NCP || type == NWAM_OBJECT_TYPE_UNKNOWN) { ret = nwam_walk_ncps(list_ncp_cb, ofmt, 0, NULL); if (ret != NWAM_SUCCESS) goto done; } /* no UNKNOWN for NCUs. They walked with active NCP above */ if (type == NWAM_OBJECT_TYPE_NCU) { nwam_ncp_handle_t ncph; if ((ncph = determine_active_ncp()) != NULL) { ret = nwam_ncp_walk_ncus(ncph, list_ncu_cb, ofmt, nwam_ncu_class_to_flag(ncu_class), NULL); nwam_ncp_free(ncph); if (ret != NWAM_SUCCESS) goto done; } } if (type == NWAM_OBJECT_TYPE_LOC || type == NWAM_OBJECT_TYPE_UNKNOWN) { ret = nwam_walk_locs(list_loc_cb, ofmt, NWAM_FLAG_ACTIVATION_MODE_ALL, NULL); if (ret != NWAM_SUCCESS) goto done; } if (type == NWAM_OBJECT_TYPE_ENM || type == NWAM_OBJECT_TYPE_UNKNOWN) { ret = nwam_walk_enms(list_enm_cb, ofmt, NWAM_FLAG_ACTIVATION_MODE_ALL, NULL); if (ret != NWAM_SUCCESS) goto done; } } done: ofmt_close(ofmt); if (ret == NWAM_ENTITY_NOT_FOUND && name != NULL) die("no profile matched '%s'", name); else if (ret != NWAM_SUCCESS) die_nwamerr(ret, "list failed during walk"); } /* * Print NWAM events. */ static void eventhandler(nwam_event_t event) { char description[DESCRIPTION_WIDTH]; char statestr[DESCRIPTION_WIDTH]; char objstr[DESCRIPTION_WIDTH]; char *object = NULL; const char *action = NULL; char *state = NULL; boolean_t display = B_TRUE; int i; nwam_wlan_t *wlans; (void) strlcpy(description, "-", sizeof (description)); switch (event->nwe_type) { case NWAM_EVENT_TYPE_OBJECT_ACTION: action = nwam_action_to_string (event->nwe_data.nwe_object_action.nwe_action); (void) snprintf(objstr, sizeof (objstr), "%s %s", nwam_object_type_to_string (event->nwe_data.nwe_object_action.nwe_object_type), event->nwe_data.nwe_object_action.nwe_name); object = objstr; break; case NWAM_EVENT_TYPE_OBJECT_STATE: (void) snprintf(statestr, sizeof (statestr), "%s, %s", nwam_state_to_string (event->nwe_data.nwe_object_state.nwe_state), nwam_aux_state_to_string (event->nwe_data.nwe_object_state.nwe_aux_state)); state = statestr; (void) snprintf(objstr, sizeof (objstr), "%s %s", nwam_object_type_to_string (event->nwe_data.nwe_object_state.nwe_object_type), event->nwe_data.nwe_object_state.nwe_name); object = objstr; break; case NWAM_EVENT_TYPE_PRIORITY_GROUP: (void) snprintf(description, DESCRIPTION_WIDTH, "priority-group: %d", event->nwe_data.nwe_priority_group_info.nwe_priority); break; case NWAM_EVENT_TYPE_WLAN_SCAN_REPORT: (void) printf("%-*s \n", EVENT_WIDTH, nwam_event_type_to_string(event->nwe_type)); wlans = event->nwe_data.nwe_wlan_info.nwe_wlans; for (i = 0; i < event->nwe_data.nwe_wlan_info.nwe_num_wlans; i++) { (void) snprintf(description, DESCRIPTION_WIDTH, "%d: %c%c ESSID %s BSSID %s", i + 1, wlans[i].nww_selected ? 'S' : '-', wlans[i].nww_connected ? 'C' : '-', wlans[i].nww_essid, wlans[i].nww_bssid); (void) printf("%-*s %-*s\n", EVENT_WIDTH, "-", DESCRIPTION_WIDTH, description); } display = B_FALSE; break; case NWAM_EVENT_TYPE_WLAN_NEED_CHOICE: (void) printf("%-*s \n", EVENT_WIDTH, nwam_event_type_to_string(event->nwe_type)); display = B_FALSE; break; case NWAM_EVENT_TYPE_WLAN_NEED_KEY: (void) printf("%-*s \n", EVENT_WIDTH, nwam_event_type_to_string(event->nwe_type)); display = B_FALSE; break; case NWAM_EVENT_TYPE_WLAN_CONNECTION_REPORT: (void) snprintf(description, DESCRIPTION_WIDTH, gettext("connect to WLAN ESSID %s, BSSID %s %s"), event->nwe_data.nwe_wlan_info.nwe_wlans[0].nww_essid, event->nwe_data.nwe_wlan_info.nwe_wlans[0].nww_bssid, event->nwe_data.nwe_wlan_info.nwe_connected ? "succeeded" : "failed"); break; case NWAM_EVENT_TYPE_INFO: (void) snprintf(description, sizeof (description), "%s", event->nwe_data.nwe_info.nwe_message); break; case NWAM_EVENT_TYPE_IF_ACTION: action = nwam_action_to_string (event->nwe_data.nwe_if_action.nwe_action); object = event->nwe_data.nwe_if_action.nwe_name; break; case NWAM_EVENT_TYPE_IF_STATE: object = event->nwe_data.nwe_if_state.nwe_name; if (event->nwe_data.nwe_if_state.nwe_addr_valid) { struct sockaddr_storage *address = &(event->nwe_data.nwe_if_state.nwe_addr); struct sockaddr_in *v4addr; struct sockaddr_in6 *v6addr; char addrstr[NWAM_MAX_VALUE_LEN]; switch (address->ss_family) { case AF_INET: v4addr = (struct sockaddr_in *)address; (void) inet_ntop(AF_INET, &v4addr->sin_addr, addrstr, sizeof (addrstr)); break; case AF_INET6: v6addr = (struct sockaddr_in6 *)address; (void) inet_ntop(AF_INET6, &v6addr->sin6_addr, addrstr, sizeof (addrstr)); break; } (void) snprintf(statestr, sizeof (statestr), "index %d flags 0x%x address %s", event->nwe_data.nwe_if_state.nwe_index, event->nwe_data.nwe_if_state.nwe_flags, addrstr); } else { (void) snprintf(statestr, sizeof (statestr), "(%d) flags %x", event->nwe_data.nwe_if_state.nwe_index, event->nwe_data.nwe_if_state.nwe_flags); } state = statestr; break; case NWAM_EVENT_TYPE_LINK_ACTION: action = nwam_action_to_string (event->nwe_data.nwe_link_action.nwe_action); object = event->nwe_data.nwe_link_action.nwe_name; break; case NWAM_EVENT_TYPE_LINK_STATE: state = event->nwe_data.nwe_link_state.nwe_link_up ? "up" : "down"; object = event->nwe_data.nwe_link_state.nwe_name; break; } if (object != NULL && action != NULL) { (void) snprintf(description, sizeof (description), "%s -> action %s", object, action); } else if (object != NULL && state != NULL) { (void) snprintf(description, sizeof (description), "%s -> state %s", object, state); } if (display) { (void) printf("%-*s %-*s\n", EVENT_WIDTH, nwam_event_type_to_string(event->nwe_type), DESCRIPTION_WIDTH, description); } } /* * listens for events and displays them via the eventhandler() function above. */ /* ARGSUSED */ static void show_events_func(int argc, char *argv[]) { nwam_error_t err; nwam_event_t event; err = nwam_events_init(); if (err != NWAM_SUCCESS) die_nwamerr(err, "could not bind to receive events"); /* print header */ (void) printf("%-*s %-*s\n", EVENT_WIDTH, "EVENT", DESCRIPTION_WIDTH, "DESCRIPTION"); do { /* * Needed for stdout redirection to ensure event output is * regularly flushed to file. */ (void) fflush(stdout); err = nwam_event_wait(&event); if (err == NWAM_SUCCESS) { eventhandler(event); nwam_event_free(event); } } while (err == NWAM_SUCCESS); die_nwamerr(err, "event handling stopped"); } /* May need to convert case-insensitive link name match to case-sensitive one */ static nwam_error_t name_to_linkname(char *name, char **linknamep) { nwam_error_t err; nwam_ncp_handle_t ncph = NULL; nwam_ncu_handle_t ncuh = NULL; if ((ncph = determine_active_ncp()) == NULL) return (NWAM_ENTITY_NOT_FOUND); err = nwam_ncu_read(ncph, name, NWAM_NCU_TYPE_LINK, 0, &ncuh); if (err == NWAM_SUCCESS) err = nwam_ncu_get_name(ncuh, linknamep); nwam_ncp_free(ncph); nwam_ncu_free(ncuh); return (err); } static void scan_wifi_func(int argc, char *argv[]) { nwam_error_t err; char *linkname = NULL; if (argc != 1) die_usage(CMD_SCAN_WIFI); if ((err = name_to_linkname(argv[0], &linkname)) != NWAM_SUCCESS) die_nwamerr(err, "scan request failed for %s", argv[0]); err = nwam_wlan_scan(linkname); if (err != NWAM_SUCCESS) die_nwamerr(err, "scan request failed for %s", linkname); free(linkname); } static void select_wifi_func(int argc, char *argv[]) { nwam_error_t err; char *linkname = NULL; uint_t i, choice, num_wlans = 0; uint32_t security_mode; boolean_t have_key = B_FALSE; nwam_wlan_t *wlans = NULL; char choicestr[NWAM_MAX_VALUE_LEN]; char modestr[NWAM_MAX_VALUE_LEN]; char essid[NWAM_MAX_VALUE_LEN]; char bssid[NWAM_MAX_VALUE_LEN]; if (argc != 1) die_usage(CMD_SELECT_WIFI); if ((err = name_to_linkname(argv[0], &linkname)) != NWAM_SUCCESS) { die_nwamerr(err, "could not retrieve scan results for %s", argv[0]); } err = nwam_wlan_get_scan_results(linkname, &num_wlans, &wlans); if (err != NWAM_SUCCESS) { die_nwamerr(err, "could not retrieve scan results for %s", linkname); } bssid[0] = '\0'; /* Loop until valid selection made */ for (;;) { (void) printf("\n"); /* Display WLAN choices for user to select from */ for (i = 0; i < num_wlans; i++) { (void) printf("%d: ESSID %s BSSID %s\n", i + 1, wlans[i].nww_essid, wlans[i].nww_bssid); } (void) printf(gettext("%d: Other\n"), i + 1); (void) printf(gettext("\nChoose WLAN to connect to [1-%d]: "), i + 1); if (fgets(choicestr, sizeof (choicestr), stdin) != NULL && (choice = atoi(choicestr)) >= 1 && choice <= (i + 1)) break; } if (choice == i + 1 || wlans[choice - 1].nww_essid[0] == '\0') { nwam_known_wlan_handle_t kwh = NULL; nwam_value_t keynameval = NULL; /* If "Other" or a hidden WLAN is selected, ask for ESSID */ do { (void) printf(gettext("\nEnter WLAN name: ")); while (fgets(essid, sizeof (essid), stdin) == NULL) {} essid[strlen(essid) - 1] = '\0'; } while (strspn(essid, " \t") == strlen(essid)); /* If "Other" was selected, secmode must be specified. */ if (choice == i + 1) { for (;;) { (void) printf(gettext("1: None\n")); (void) printf(gettext("2: WEP\n")); (void) printf(gettext("3: WPA\n")); (void) printf(gettext("Enter security mode: ")); if (fgets(modestr, sizeof (choicestr), stdin) != NULL && (security_mode = atoi(modestr)) >= 1 && security_mode <= 3) break; } } else { security_mode = wlans[choice - 1].nww_security_mode; have_key = wlans[choice - 1].nww_have_key; } /* * We have to determine if we have a key for this ESSID from * the known WLAN list, since we cannot determine this from * the scan results. */ if (nwam_known_wlan_read(essid, 0, &kwh) == NWAM_SUCCESS && nwam_known_wlan_get_prop_value(kwh, NWAM_KNOWN_WLAN_PROP_KEYNAME, &keynameval) == NWAM_SUCCESS) have_key = B_TRUE; else have_key = B_FALSE; nwam_value_free(keynameval); nwam_known_wlan_free(kwh); } else { (void) strlcpy(essid, wlans[choice - 1].nww_essid, sizeof (essid)); (void) strlcpy(bssid, wlans[choice - 1].nww_bssid, sizeof (bssid)); security_mode = wlans[choice - 1].nww_security_mode; have_key = wlans[choice - 1].nww_have_key; } if (security_mode != DLADM_WLAN_SECMODE_NONE && !have_key) { uint_t keyslot = 1; char key[NWAM_MAX_VALUE_LEN]; char slotstr[NWAM_MAX_VALUE_LEN]; do { (void) printf(gettext("\nEnter WLAN key for " "ESSID %s: "), essid); while (fgets(key, sizeof (key), stdin) == NULL) {} key[strlen(key) - 1] = '\0'; } while (strspn(key, " \t") == strlen(key)); if (security_mode == DLADM_WLAN_SECMODE_WEP) { for (;;) { (void) printf( gettext("\nEnter key slot [1-4]: ")); if (fgets(slotstr, sizeof (slotstr), stdin) != NULL && (keyslot = atoi(slotstr)) >= 1 && keyslot <= 4) break; } } err = nwam_wlan_set_key(linkname, essid, NULL, security_mode, keyslot, key); if (err != NWAM_SUCCESS) die_nwamerr(err, "could not set WiFi key"); } err = nwam_wlan_select(linkname, essid, bssid[0] != '\0' ? bssid : NULL, security_mode, B_TRUE); if (err != NWAM_SUCCESS) die_nwamerr(err, "could not select WLAN %s", essid); free(wlans); free(linkname); } int main(int argc, char *argv[]) { int i; char *state; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if ((execname = strrchr(argv[0], '/')) == NULL) execname = argv[0]; else execname++; if (argc < 2) { usage(B_FALSE); exit(EXIT_FAILURE); } for (i = CMD_MIN; i <= CMD_MAX; i++) { if (strcmp(argv[1], cmd_to_str(i)) == 0) { if (cmdtab[i].cmd_needs_nwamd) { state = smf_get_state(NWAM_FMRI); if (state == NULL || strcmp(state, SCF_STATE_STRING_ONLINE) != 0) { free(state); die("enable '%s' to use '%s %s'", NWAM_FMRI, execname, cmd_to_str(cmdtab[i].cmd_num)); } free(state); } cmdtab[i].cmd_handler(argc - 2, &(argv[2])); exit(EXIT_SUCCESS); } } (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"), execname, argv[1]); usage(B_FALSE); return (1); }