X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=hostapd%2Fhostapd_cli.c;h=56f5fe25105d6d1f2e1f52b29cd389df4efdac21;hb=fcc84b48b2827abbb84394a0acee6ac0ea74071f;hp=a48d773259e6c5692569269afb19a9c62d2884e7;hpb=9e0749737c39312da005391c13eb715b2e5d5b1c;p=mech_eap.git diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index a48d773..56f5fe2 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1,118 +1,50 @@ /* * hostapd - command line interface for hostapd daemon - * Copyright (c) 2004-2011, Jouni Malinen + * Copyright (c) 2004-2016, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #include #include "common/wpa_ctrl.h" -#include "common.h" +#include "common/ieee802_11_defs.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/edit.h" #include "common/version.h" +#include "common/cli.h" +#ifndef CONFIG_NO_CTRL_IFACE -static const char *hostapd_cli_version = +static const char *const hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2011, Jouni Malinen and contributors"; - - -static const char *hostapd_cli_license = -"This program is free software. You can distribute it and/or modify it\n" -"under the terms of the GNU General Public License version 2.\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license. See README and COPYING for more details.\n"; - -static const char *hostapd_cli_full_license = -"This program is free software; you can redistribute it and/or modify\n" -"it under the terms of the GNU General Public License version 2 as\n" -"published by the Free Software Foundation.\n" -"\n" -"This program is distributed in the hope that it will be useful,\n" -"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" -"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" -"GNU General Public License for more details.\n" -"\n" -"You should have received a copy of the GNU General Public License\n" -"along with this program; if not, write to the Free Software\n" -"Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n" -"\n" -"Alternatively, this software may be distributed under the terms of the\n" -"BSD license.\n" -"\n" -"Redistribution and use in source and binary forms, with or without\n" -"modification, are permitted provided that the following conditions are\n" -"met:\n" -"\n" -"1. Redistributions of source code must retain the above copyright\n" -" notice, this list of conditions and the following disclaimer.\n" -"\n" -"2. Redistributions in binary form must reproduce the above copyright\n" -" notice, this list of conditions and the following disclaimer in the\n" -" documentation and/or other materials provided with the distribution.\n" -"\n" -"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n" -" names of its contributors may be used to endorse or promote products\n" -" derived from this software without specific prior written permission.\n" -"\n" -"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" -"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" -"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" -"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n" -"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n" -"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n" -"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" -"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" -"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" -"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" -"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" -"\n"; - -static const char *commands_help = -"Commands:\n" -" mib get MIB variables (dot1x, dot11, radius)\n" -" sta get MIB variables for one station\n" -" all_sta get MIB variables for all stations\n" -" new_sta add a new station\n" -" deauthenticate deauthenticate a station\n" -" disassociate disassociate a station\n" -#ifdef CONFIG_IEEE80211W -" sa_query send SA Query to a station\n" -#endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_WPS -" wps_pin [timeout] [addr] add WPS Enrollee PIN\n" -" wps_check_pin verify PIN checksum\n" -" wps_pbc indicate button pushed to initiate PBC\n" -#ifdef CONFIG_WPS_OOB -" wps_oob use WPS with out-of-band (UFD)\n" -#endif /* CONFIG_WPS_OOB */ -" wps_ap_pin [params..] enable/disable AP PIN\n" -" wps_config configure AP\n" -#endif /* CONFIG_WPS */ -" get_config show current configuration\n" -" help show this usage help\n" -" interface [ifname] show interfaces/select interface\n" -" level change debug level\n" -" license show full hostapd_cli license\n" -" quit exit hostapd_cli\n"; +"Copyright (c) 2004-2016, Jouni Malinen and contributors"; static struct wpa_ctrl *ctrl_conn; static int hostapd_cli_quit = 0; static int hostapd_cli_attached = 0; -static const char *ctrl_iface_dir = "/var/run/hostapd"; + +#ifndef CONFIG_CTRL_IFACE_DIR +#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd" +#endif /* CONFIG_CTRL_IFACE_DIR */ +static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; +static const char *client_socket_dir = NULL; + static char *ctrl_ifname = NULL; static const char *pid_file = NULL; static const char *action_file = NULL; static int ping_interval = 5; +static int interactive = 0; +static int event_handler_registered = 0; + +static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */ + +static void print_help(FILE *stream, const char *cmd); +static char ** list_cmd_list(void); +static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx); static void usage(void) @@ -122,42 +54,81 @@ static void usage(void) "\n" "usage: hostapd_cli [-p] [-i] [-hvB] " "[-a] \\\n" - " [-G] [command..]\n" + " [-P] [-G] [command..]\n" "\n" "Options:\n" " -h help (show this usage text)\n" " -v shown version information\n" " -p path to find control sockets (default: " "/var/run/hostapd)\n" + " -s dir path to open client sockets (default: " + CONFIG_CTRL_IFACE_DIR ")\n" " -a run in daemon mode executing the action file " "based on events\n" " from hostapd\n" " -B run a daemon in the background\n" " -i Interface to listen on (default: first " "interface found in the\n" - " socket path)\n\n" - "%s", - commands_help); + " socket path)\n\n"); + print_help(stderr, NULL); +} + + +static void register_event_handler(struct wpa_ctrl *ctrl) +{ + if (!ctrl_conn) + return; + if (interactive) { + event_handler_registered = + !eloop_register_read_sock(wpa_ctrl_get_fd(ctrl), + hostapd_cli_receive, + NULL, NULL); + } +} + + +static void unregister_event_handler(struct wpa_ctrl *ctrl) +{ + if (!ctrl_conn) + return; + if (interactive && event_handler_registered) { + eloop_unregister_read_sock(wpa_ctrl_get_fd(ctrl)); + event_handler_registered = 0; + } } static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) { +#ifndef CONFIG_CTRL_IFACE_UDP char *cfile; int flen; +#endif /* !CONFIG_CTRL_IFACE_UDP */ if (ifname == NULL) return NULL; +#ifdef CONFIG_CTRL_IFACE_UDP + ctrl_conn = wpa_ctrl_open(ifname); + return ctrl_conn; +#else /* CONFIG_CTRL_IFACE_UDP */ flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2; cfile = malloc(flen); if (cfile == NULL) return NULL; snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); - ctrl_conn = wpa_ctrl_open(cfile); + if (client_socket_dir && client_socket_dir[0] && + access(client_socket_dir, F_OK) < 0) { + perror(client_socket_dir); + free(cfile); + return NULL; + } + + ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir); free(cfile); return ctrl_conn; +#endif /* CONFIG_CTRL_IFACE_UDP */ } @@ -166,6 +137,7 @@ static void hostapd_cli_close_connection(void) if (ctrl_conn == NULL) return; + unregister_event_handler(ctrl_conn); if (hostapd_cli_attached) { wpa_ctrl_detach(ctrl_conn); hostapd_cli_attached = 0; @@ -215,6 +187,52 @@ static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) } +static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, + char *argv[]) +{ + int i, res; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + res = os_snprintf(pos, end - pos, "%s", cmd); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + + for (i = 0; i < argc; i++) { + res = os_snprintf(pos, end - pos, " %s", argv[i]); + if (os_snprintf_error(end - pos, res)) + goto fail; + pos += res; + } + + buf[buflen - 1] = '\0'; + return 0; + +fail: + printf("Too long command\n"); + return -1; +} + + +static int hostapd_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, + int min_args, int argc, char *argv[]) +{ + char buf[4096]; + + if (argc < min_args) { + printf("Invalid %s command - at least %d argument%s required.\n", + cmd, min_args, min_args > 1 ? "s are" : " is"); + return -1; + } + if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "PING"); @@ -227,8 +245,21 @@ static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc > 0 && os_strcmp(argv[0], "driver") == 0) + return wpa_ctrl_command(ctrl, "STATUS-DRIVER"); + return wpa_ctrl_command(ctrl, "STATUS"); +} + + static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) { + if (argc > 0) { + char buf[100]; + os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); + } return wpa_ctrl_command(ctrl, "MIB"); } @@ -236,28 +267,19 @@ static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) static int hostapd_cli_exec(const char *program, const char *arg1, const char *arg2) { - char *cmd; + char *arg; size_t len; int res; - int ret = 0; - len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3; - cmd = os_malloc(len); - if (cmd == NULL) - return -1; - res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2); - if (res < 0 || (size_t) res >= len) { - os_free(cmd); + len = os_strlen(arg1) + os_strlen(arg2) + 2; + arg = os_malloc(len); + if (arg == NULL) return -1; - } - cmd[len - 1] = '\0'; -#ifndef _WIN32_WCE - if (system(cmd) < 0) - ret = -1; -#endif /* _WIN32_WCE */ - os_free(cmd); + os_snprintf(arg, len, "%s %s", arg1, arg2); + res = os_exec(program, arg, 1); + os_free(arg); - return ret; + return res; } @@ -281,12 +303,15 @@ static void hostapd_cli_action_process(char *msg, size_t len) static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char buf[64]; - if (argc != 1) { - printf("Invalid 'sta' command - exactly one argument, STA " + if (argc < 1) { + printf("Invalid 'sta' command - at least one argument, STA " "address, is required.\n"); return -1; } - snprintf(buf, sizeof(buf), "STA %s", argv[0]); + if (argc > 1) + snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]); + else + snprintf(buf, sizeof(buf), "STA %s", argv[0]); return wpa_ctrl_command(ctrl, buf); } @@ -323,6 +348,21 @@ static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, } +static char ** hostapd_complete_deauthenticate(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&stations); + break; + } + + return res; +} + + static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -341,6 +381,21 @@ static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, } +static char ** hostapd_complete_disassociate(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&stations); + break; + } + + return res; +} + + #ifdef CONFIG_IEEE80211W static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -397,7 +452,7 @@ static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc, else res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long WPS_CHECK_PIN command.\n"); return -1; } @@ -412,38 +467,105 @@ static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, } -#ifdef CONFIG_WPS_OOB -static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, - char *argv[]) +static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { - char cmd[256]; + return wpa_ctrl_command(ctrl, "WPS_CANCEL"); +} + + +#ifdef CONFIG_WPS_NFC +static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + int ret; + char *buf; + size_t buflen; + + if (argc != 1) { + printf("Invalid 'wps_nfc_tag_read' command - one argument " + "is required.\n"); + return -1; + } + + buflen = 18 + os_strlen(argv[0]); + buf = os_malloc(buflen); + if (buf == NULL) + return -1; + os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]); + + ret = wpa_ctrl_command(ctrl, buf); + os_free(buf); + + return ret; +} + + +static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; int res; - if (argc != 3 && argc != 4) { - printf("Invalid WPS_OOB command: need three or four " - "arguments:\n" - "- DEV_TYPE: use 'ufd' or 'nfc'\n" - "- PATH: path of OOB device like '/mnt'\n" - "- METHOD: OOB method 'pin-e' or 'pin-r', " - "'cred'\n" - "- DEV_NAME: (only for NFC) device name like " - "'pn531'\n"); + if (argc != 1) { + printf("Invalid 'wps_nfc_config_token' command - one argument " + "is required.\n"); return -1; } - if (argc == 3) - res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s", - argv[0], argv[1], argv[2]); - else - res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s", - argv[0], argv[1], argv[2], argv[3]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { - printf("Too long WPS_OOB command.\n"); + res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s", + argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long WPS_NFC_CONFIG_TOKEN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 1) { + printf("Invalid 'wps_nfc_token' command - one argument is " + "required.\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long WPS_NFC_TOKEN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 2) { + printf("Invalid 'nfc_get_handover_sel' command - two arguments " + "are required.\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long NFC_GET_HANDOVER_SEL command.\n"); return -1; } return wpa_ctrl_command(ctrl, cmd); } -#endif /* CONFIG_WPS_OOB */ + +#endif /* CONFIG_WPS_NFC */ static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, @@ -467,11 +589,18 @@ static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, } +static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_GET_STATUS"); +} + + static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char buf[256]; - char ssid_hex[2 * 32 + 1]; + char ssid_hex[2 * SSID_MAX_LEN + 1]; char key_hex[2 * 64 + 1]; int i; @@ -482,7 +611,7 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, } ssid_hex[0] = '\0'; - for (i = 0; i < 32; i++) { + for (i = 0; i < SSID_MAX_LEN; i++) { if (argv[0][i] == '\0') break; os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]); @@ -512,6 +641,73 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, #endif /* CONFIG_WPS */ +static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 2) { + printf("Invalid 'disassoc_imminent' command - two arguments " + "(STA addr and Disassociation Timer) are needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 3) { + printf("Invalid 'ess_disassoc' command - three arguments (STA " + "addr, disassoc timer, and URL) are needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s", + argv[0], argv[1], argv[2]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[2000], *tmp; + int res, i, total; + + if (argc < 1) { + printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + + total = res; + for (i = 1; i < argc; i++) { + tmp = &buf[total]; + res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]); + if (os_snprintf_error(sizeof(buf) - total, res)) + return -1; + total += res; + } + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -572,22 +768,123 @@ static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - printf("%s", commands_help); + print_help(stdout, argc > 0 ? argv[0] : NULL); return 0; } +static char ** hostapd_cli_complete_help(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = list_cmd_list(); + break; + } + + return res; +} + + static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license); + printf("%s\n\n%s\n", hostapd_cli_version, cli_full_license); return 0; } +static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[200]; + int res; + + if (argc != 1) { + printf("Invalid 'set_qos_map_set' command - " + "one argument (comma delimited QoS map set) " + "is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[50]; + int res; + + if (argc != 1) { + printf("Invalid 'send_qos_map_conf' command - " + "one argument (STA addr) is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 2) { + printf("Invalid 'hs20_wnm_notif' command - two arguments (STA " + "addr and URL) are needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 3) { + printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n"); + return -1; + } + + if (argc > 3) + res = os_snprintf(buf, sizeof(buf), + "HS20_DEAUTH_REQ %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else + res = os_snprintf(buf, sizeof(buf), + "HS20_DEAUTH_REQ %s %s %s", + argv[0], argv[1], argv[2]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) { hostapd_cli_quit = 1; + if (interactive) + eloop_terminate(); return 0; } @@ -605,6 +902,28 @@ static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl, + struct dl_list *interfaces) +{ + struct dirent *dent; + DIR *dir; + + if (!ctrl || !interfaces) + return; + dir = opendir(ctrl_iface_dir); + if (dir == NULL) + return; + + while ((dent = readdir(dir))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + cli_txt_list_add(interfaces, dent->d_name); + } + closedir(dir); +} + + static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl) { struct dirent *dent; @@ -637,13 +956,16 @@ static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, } hostapd_cli_close_connection(); - free(ctrl_ifname); - ctrl_ifname = strdup(argv[0]); + os_free(ctrl_ifname); + ctrl_ifname = os_strdup(argv[0]); + if (ctrl_ifname == NULL) + return -1; if (hostapd_cli_open_connection(ctrl_ifname)) { printf("Connected to interface '%s.\n", ctrl_ifname); if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; + register_event_handler(ctrl_conn); } else { printf("Warning: Failed to attach to " "hostapd.\n"); @@ -656,7 +978,25 @@ static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, } -static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) +static char ** hostapd_complete_interface(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + DEFINE_DL_LIST(interfaces); + + switch (arg) { + case 1: + hostapd_cli_get_interfaces(ctrl_conn, &interfaces); + res = cli_txt_list_array(&interfaces); + cli_txt_list_flush(&interfaces); + break; + } + + return res; +} + + +static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; int res; @@ -668,7 +1008,7 @@ static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) } res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long SET command.\n"); return -1; } @@ -688,7 +1028,7 @@ static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) } res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long GET command.\n"); return -1; } @@ -696,48 +1036,380 @@ static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +#ifdef CONFIG_FST +static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + int i; + int total; + + if (argc <= 0) { + printf("FST command: parameters are required.\n"); + return -1; + } + + total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER"); + + for (i = 0; i < argc; i++) { + res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s", + argv[i]); + if (os_snprintf_error(sizeof(cmd) - total, res)) { + printf("Too long fst command.\n"); + return -1; + } + total += res; + } + return wpa_ctrl_command(ctrl, cmd); +} +#endif /* CONFIG_FST */ + + +static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[256]; + int res; + int i; + char *tmp; + int total; + + if (argc < 2) { + printf("Invalid chan_switch command: needs at least two " + "arguments (count and freq)\n" + "usage: [sec_channel_offset=] " + "[center_freq1=] [center_freq2=] [bandwidth=] " + "[blocktx] [ht|vht]\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long CHAN_SWITCH command.\n"); + return -1; + } + + total = res; + for (i = 2; i < argc; i++) { + tmp = cmd + total; + res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]); + if (os_snprintf_error(sizeof(cmd) - total, res)) { + printf("Too long CHAN_SWITCH command.\n"); + return -1; + } + total += res; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ENABLE"); +} + + +static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOAD"); +} + + +static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DISABLE"); +} + + +static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc < 2 || argc > 3) { + printf("Invalid vendor command\n" + "usage: []\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1], + argc == 3 ? argv[2] : ""); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long VENDOR command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ERP_FLUSH"); +} + + +static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s", + argc >= 1 ? " " : "", + argc >= 1 ? argv[0] : "", + argc == 2 ? " " : "", + argc == 2 ? argv[1] : ""); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long option\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc == 0) + return -1; + return hostapd_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]); +} + + +static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA"); +} + + +static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA_FLUSH"); +} + + +static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[2048]; + int res; + + if (argc < 3 || argc > 5) { + printf("Invalid set_neighbor command: needs 3-5 arguments\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s", + argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "", + argc == 5 ? argv[4] : ""); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long SET_NEIGHBOR command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[400]; + int res; + + if (argc != 2) { + printf("Invalid remove_neighbor command: needs 2 arguments\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NEIGHBOR %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long REMOVE_NEIGHBOR command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_req_lci(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid req_lci command - requires destination address\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REQ_LCI %s", argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long REQ_LCI command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_req_range(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc < 4) { + printf("Invalid req_range command: needs at least 4 arguments - dest address, randomization interval, min AP count, and 1 to 16 AP addresses\n"); + return -1; + } + + return hostapd_cli_cmd(ctrl, "REQ_RANGE", 4, argc, argv); +} + + +static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DRIVER_FLAGS"); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); + char ** (*completion)(const char *str, int pos); + const char *usage; }; -static struct hostapd_cli_cmd hostapd_cli_commands[] = { - { "ping", hostapd_cli_cmd_ping }, - { "mib", hostapd_cli_cmd_mib }, - { "relog", hostapd_cli_cmd_relog }, - { "sta", hostapd_cli_cmd_sta }, - { "all_sta", hostapd_cli_cmd_all_sta }, - { "new_sta", hostapd_cli_cmd_new_sta }, - { "deauthenticate", hostapd_cli_cmd_deauthenticate }, - { "disassociate", hostapd_cli_cmd_disassociate }, +static const struct hostapd_cli_cmd hostapd_cli_commands[] = { + { "ping", hostapd_cli_cmd_ping, NULL, + "= pings hostapd" }, + { "mib", hostapd_cli_cmd_mib, NULL, + "= get MIB variables (dot1x, dot11, radius)" }, + { "relog", hostapd_cli_cmd_relog, NULL, NULL }, + { "status", hostapd_cli_cmd_status, NULL, NULL }, + { "sta", hostapd_cli_cmd_sta, NULL, + " = get MIB variables for one station" }, + { "all_sta", hostapd_cli_cmd_all_sta, NULL, + "= get MIB variables for all stations" }, + { "new_sta", hostapd_cli_cmd_new_sta, NULL, + " = add a new station" }, + { "deauthenticate", hostapd_cli_cmd_deauthenticate, + hostapd_complete_deauthenticate, + " = deauthenticate a station" }, + { "disassociate", hostapd_cli_cmd_disassociate, + hostapd_complete_disassociate, + " = disassociate a station" }, #ifdef CONFIG_IEEE80211W - { "sa_query", hostapd_cli_cmd_sa_query }, + { "sa_query", hostapd_cli_cmd_sa_query, NULL, + " = send SA Query to a station" }, #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS - { "wps_pin", hostapd_cli_cmd_wps_pin }, - { "wps_check_pin", hostapd_cli_cmd_wps_check_pin }, - { "wps_pbc", hostapd_cli_cmd_wps_pbc }, -#ifdef CONFIG_WPS_OOB - { "wps_oob", hostapd_cli_cmd_wps_oob }, -#endif /* CONFIG_WPS_OOB */ - { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin }, - { "wps_config", hostapd_cli_cmd_wps_config }, + { "wps_pin", hostapd_cli_cmd_wps_pin, NULL, + " [timeout] [addr] = add WPS Enrollee PIN" }, + { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL, + " = verify PIN checksum" }, + { "wps_pbc", hostapd_cli_cmd_wps_pbc, NULL, + "= indicate button pushed to initiate PBC" }, + { "wps_cancel", hostapd_cli_cmd_wps_cancel, NULL, + "= cancel the pending WPS operation" }, +#ifdef CONFIG_WPS_NFC + { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read, NULL, + " = report read NFC tag with WPS data" }, + { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token, NULL, + " = build NFC configuration token" }, + { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token, NULL, + " = manager NFC password token" }, + { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel, NULL, + NULL }, +#endif /* CONFIG_WPS_NFC */ + { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin, NULL, + " [params..] = enable/disable AP PIN" }, + { "wps_config", hostapd_cli_cmd_wps_config, NULL, + " = configure AP" }, + { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL, + "= show current WPS status" }, #endif /* CONFIG_WPS */ - { "get_config", hostapd_cli_cmd_get_config }, - { "help", hostapd_cli_cmd_help }, - { "interface", hostapd_cli_cmd_interface }, - { "level", hostapd_cli_cmd_level }, - { "license", hostapd_cli_cmd_license }, - { "quit", hostapd_cli_cmd_quit }, - { "set", hostapd_cli_cmd_set }, - { "get", hostapd_cli_cmd_get }, - { NULL, NULL } + { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL, NULL }, + { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL, NULL }, + { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL, NULL }, + { "get_config", hostapd_cli_cmd_get_config, NULL, + "= show current configuration" }, + { "help", hostapd_cli_cmd_help, hostapd_cli_complete_help, + "= show this usage help" }, + { "interface", hostapd_cli_cmd_interface, hostapd_complete_interface, + "[ifname] = show interfaces/select interface" }, +#ifdef CONFIG_FST + { "fst", hostapd_cli_cmd_fst, NULL, NULL }, +#endif /* CONFIG_FST */ + { "raw", hostapd_cli_cmd_raw, NULL, NULL }, + { "level", hostapd_cli_cmd_level, NULL, + " = change debug level" }, + { "license", hostapd_cli_cmd_license, NULL, + "= show full hostapd_cli license" }, + { "quit", hostapd_cli_cmd_quit, NULL, + "= exit hostapd_cli" }, + { "set", hostapd_cli_cmd_set, NULL, NULL }, + { "get", hostapd_cli_cmd_get, NULL, NULL }, + { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL, NULL }, + { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf, NULL, NULL }, + { "chan_switch", hostapd_cli_cmd_chan_switch, NULL, NULL }, + { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, NULL }, + { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL, NULL }, + { "vendor", hostapd_cli_cmd_vendor, NULL, NULL }, + { "enable", hostapd_cli_cmd_enable, NULL, NULL }, + { "reload", hostapd_cli_cmd_reload, NULL, NULL }, + { "disable", hostapd_cli_cmd_disable, NULL, NULL }, + { "erp_flush", hostapd_cli_cmd_erp_flush, NULL, NULL }, + { "log_level", hostapd_cli_cmd_log_level, NULL, NULL }, + { "pmksa", hostapd_cli_cmd_pmksa, NULL, NULL }, + { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL, NULL }, + { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL, NULL }, + { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL, NULL }, + { "req_lci", hostapd_cli_cmd_req_lci, NULL, NULL }, + { "req_range", hostapd_cli_cmd_req_range, NULL, NULL }, + { "driver_flags", hostapd_cli_cmd_driver_flags, NULL, NULL }, + { NULL, NULL, NULL, NULL } }; +/* + * Prints command usage, lines are padded with the specified string. + */ +static void print_cmd_help(FILE *stream, const struct hostapd_cli_cmd *cmd, + const char *pad) +{ + char c; + size_t n; + + if (cmd->usage == NULL) + return; + fprintf(stream, "%s%s ", pad, cmd->cmd); + for (n = 0; (c = cmd->usage[n]); n++) { + fprintf(stream, "%c", c); + if (c == '\n') + fprintf(stream, "%s", pad); + } + fprintf(stream, "\n"); +} + + +static void print_help(FILE *stream, const char *cmd) +{ + int n; + + fprintf(stream, "commands:\n"); + for (n = 0; hostapd_cli_commands[n].cmd; n++) { + if (cmd == NULL || str_starts(hostapd_cli_commands[n].cmd, cmd)) + print_cmd_help(stream, &hostapd_cli_commands[n], " "); + } +} + + static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - struct hostapd_cli_cmd *cmd, *match = NULL; + const struct hostapd_cli_cmd *cmd, *match = NULL; int count; count = 0; @@ -774,6 +1446,34 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static void cli_event(const char *str) +{ + const char *start, *s; + + start = os_strchr(str, '>'); + if (start == NULL) + return; + + start++; + + if (str_starts(start, AP_STA_CONNECTED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + cli_txt_list_add(&stations, s + 1); + return; + } + + if (str_starts(start, AP_STA_DISCONNECTED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + cli_txt_list_del_addr(&stations, s + 1); + return; + } +} + + static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, int action_monitor) { @@ -788,6 +1488,7 @@ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, if (action_monitor) hostapd_cli_action_process(buf, len); else { + cli_event(buf); if (in_read && first) printf("\n"); first = 0; @@ -801,70 +1502,45 @@ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, } -static void hostapd_cli_interactive(void) +static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx) { - const int max_args = 10; - char cmd[256], *res, *argv[max_args], *pos; - int argc; - - printf("\nInteractive mode\n\n"); - - do { - hostapd_cli_recv_pending(ctrl_conn, 0, 0); - printf("> "); - alarm(ping_interval); - res = fgets(cmd, sizeof(cmd), stdin); - alarm(0); - if (res == NULL) - break; - pos = cmd; - while (*pos != '\0') { - if (*pos == '\n') { - *pos = '\0'; - break; - } - pos++; - } - argc = 0; - pos = cmd; - for (;;) { - while (*pos == ' ') - pos++; - if (*pos == '\0') - break; - argv[argc] = pos; - argc++; - if (argc == max_args) - break; - while (*pos != '\0' && *pos != ' ') - pos++; - if (*pos == ' ') - *pos++ = '\0'; - } - if (argc) - wpa_request(ctrl_conn, argc, argv); - } while (!hostapd_cli_quit); + hostapd_cli_recv_pending(ctrl_conn, 0, 0); } -static void hostapd_cli_cleanup(void) -{ - hostapd_cli_close_connection(); - if (pid_file) - os_daemonize_terminate(pid_file); +#define max_args 10 - os_program_deinit(); -} +static int tokenize_cmd(char *cmd, char *argv[]) +{ + char *pos; + int argc = 0; + pos = cmd; + for (;;) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[argc] = pos; + argc++; + if (argc == max_args) + break; + if (*pos == '"') { + char *pos2 = os_strrchr(pos, '"'); + if (pos2) + pos = pos2 + 1; + } + while (*pos != '\0' && *pos != ' ') + pos++; + if (*pos == ' ') + *pos++ = '\0'; + } -static void hostapd_cli_terminate(int sig) -{ - hostapd_cli_cleanup(); - exit(0); + return argc; } -static void hostapd_cli_alarm(int sig) +static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx) { if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { printf("Connection to hostapd lost - trying to reconnect\n"); @@ -876,6 +1552,7 @@ static void hostapd_cli_alarm(int sig) printf("Connection to hostapd re-established\n"); if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; + register_event_handler(ctrl_conn); } else { printf("Warning: Failed to attach to " "hostapd.\n"); @@ -884,7 +1561,120 @@ static void hostapd_cli_alarm(int sig) } if (ctrl_conn) hostapd_cli_recv_pending(ctrl_conn, 1, 0); - alarm(ping_interval); + eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); +} + + +static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx) +{ + eloop_terminate(); +} + + +static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd) +{ + char *argv[max_args]; + int argc; + argc = tokenize_cmd(cmd, argv); + if (argc) + wpa_request(ctrl_conn, argc, argv); +} + + +static void hostapd_cli_edit_eof_cb(void *ctx) +{ + eloop_terminate(); +} + + +static char ** list_cmd_list(void) +{ + char **res; + int i, count; + + count = ARRAY_SIZE(hostapd_cli_commands); + res = os_calloc(count + 1, sizeof(char *)); + if (res == NULL) + return NULL; + + for (i = 0; hostapd_cli_commands[i].cmd; i++) { + res[i] = os_strdup(hostapd_cli_commands[i].cmd); + if (res[i] == NULL) + break; + } + + return res; +} + + +static char ** hostapd_cli_cmd_completion(const char *cmd, const char *str, + int pos) +{ + int i; + + for (i = 0; hostapd_cli_commands[i].cmd; i++) { + if (os_strcasecmp(hostapd_cli_commands[i].cmd, cmd) != 0) + continue; + if (hostapd_cli_commands[i].completion) + return hostapd_cli_commands[i].completion(str, pos); + if (!hostapd_cli_commands[i].usage) + return NULL; + edit_clear_line(); + printf("\r%s\n", hostapd_cli_commands[i].usage); + edit_redraw(); + break; + } + + return NULL; +} + + +static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str, + int pos) +{ + char **res; + const char *end; + char *cmd; + + end = os_strchr(str, ' '); + if (end == NULL || str + pos < end) + return list_cmd_list(); + + cmd = os_malloc(pos + 1); + if (cmd == NULL) + return NULL; + os_memcpy(cmd, str, pos); + cmd[end - str] = '\0'; + res = hostapd_cli_cmd_completion(cmd, str, pos); + os_free(cmd); + return res; +} + + +static void hostapd_cli_interactive(void) +{ + printf("\nInteractive mode\n\n"); + + eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); + edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, + hostapd_cli_edit_completion_cb, NULL, NULL, NULL); + eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); + + eloop_run(); + + cli_txt_list_flush(&stations); + edit_deinit(NULL, NULL); + eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL); +} + + +static void hostapd_cli_cleanup(void) +{ + hostapd_cli_close_connection(); + if (pid_file) + os_daemonize_terminate(pid_file); + + os_program_deinit(); } @@ -927,7 +1717,6 @@ static void hostapd_cli_action(struct wpa_ctrl *ctrl) int main(int argc, char *argv[]) { - int interactive; int warning_displayed = 0; int c; int daemonize = 0; @@ -936,7 +1725,7 @@ int main(int argc, char *argv[]) return -1; for (;;) { - c = getopt(argc, argv, "a:BhG:i:p:v"); + c = getopt(argc, argv, "a:BhG:i:p:P:s:v"); if (c < 0) break; switch (c) { @@ -962,6 +1751,12 @@ int main(int argc, char *argv[]) case 'p': ctrl_iface_dir = optarg; break; + case 'P': + pid_file = optarg; + break; + case 's': + client_socket_dir = optarg; + break; default: usage(); return -1; @@ -971,10 +1766,12 @@ int main(int argc, char *argv[]) interactive = (argc == optind) && (action_file == NULL); if (interactive) { - printf("%s\n\n%s\n\n", hostapd_cli_version, - hostapd_cli_license); + printf("%s\n\n%s\n\n", hostapd_cli_version, cli_license); } + if (eloop_init()) + return -1; + for (;;) { if (ctrl_ifname == NULL) { struct dirent *dent; @@ -1014,13 +1811,10 @@ int main(int argc, char *argv[]) continue; } - signal(SIGINT, hostapd_cli_terminate); - signal(SIGTERM, hostapd_cli_terminate); - signal(SIGALRM, hostapd_cli_alarm); - if (interactive || action_file) { if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; + register_event_handler(ctrl_conn); } else { printf("Warning: Failed to attach to hostapd.\n"); if (action_file) @@ -1028,7 +1822,7 @@ int main(int argc, char *argv[]) } } - if (daemonize && os_daemonize(pid_file)) + if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) return -1; if (interactive) @@ -1038,7 +1832,18 @@ int main(int argc, char *argv[]) else wpa_request(ctrl_conn, argc - optind, &argv[optind]); + unregister_event_handler(ctrl_conn); os_free(ctrl_ifname); + eloop_destroy(); hostapd_cli_cleanup(); return 0; } + +#else /* CONFIG_NO_CTRL_IFACE */ + +int main(int argc, char *argv[]) +{ + return -1; +} + +#endif /* CONFIG_NO_CTRL_IFACE */