Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / hostapd / hostapd_cli.c
index c0d647f..46c2f37 100644 (file)
@@ -1,53 +1,33 @@
 /*
  * hostapd - command line interface for hostapd daemon
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
- * 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 <dirent.h>
 
 #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"
 
 
-static const char *hostapd_cli_version =
+static const char *const hostapd_cli_version =
 "hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> 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 *const hostapd_cli_license =
+"This software may be distributed under the terms of the BSD license.\n"
+"See README 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"
+static const char *const hostapd_cli_full_license =
+"This software may be distributed under the terms of the 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"
@@ -77,7 +57,7 @@ static const char *hostapd_cli_full_license =
 "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
 "\n";
 
-static const char *commands_help =
+static const char *const commands_help =
 "Commands:\n"
 "   mib                  get MIB variables (dot1x, dot11, radius)\n"
 "   sta <addr>           get MIB variables for one station\n"
@@ -92,10 +72,15 @@ static const char *commands_help =
 "   wps_pin <uuid> <pin> [timeout] [addr]  add WPS Enrollee PIN\n"
 "   wps_check_pin <PIN>  verify PIN checksum\n"
 "   wps_pbc              indicate button pushed to initiate PBC\n"
-#ifdef CONFIG_WPS_OOB
-"   wps_oob <type> <path> <method>  use WPS with out-of-band (UFD)\n"
-#endif /* CONFIG_WPS_OOB */
+"   wps_cancel           cancel the pending WPS operation\n"
+#ifdef CONFIG_WPS_NFC
+"   wps_nfc_tag_read <hexdump>  report read NFC tag with WPS data\n"
+"   wps_nfc_config_token <WPS/NDEF>  build NFC configuration token\n"
+"   wps_nfc_token <WPS/NDEF/enable/disable>  manager NFC password token\n"
+#endif /* CONFIG_WPS_NFC */
 "   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
+"   wps_config <SSID> <auth> <encr> <key>  configure AP\n"
+"   wps_get_status       show current WPS status\n"
 #endif /* CONFIG_WPS */
 "   get_config           show current configuration\n"
 "   help                 show this usage help\n"
@@ -107,11 +92,18 @@ static const char *commands_help =
 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 void usage(void)
@@ -121,13 +113,15 @@ static void usage(void)
                "\n"
                "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
                "[-a<path>] \\\n"
-               "                   [-G<ping interval>] [command..]\n"
+               "                   [-P<pid file>] [-G<ping interval>] [command..]\n"
                "\n"
                "Options:\n"
                "   -h           help (show this usage text)\n"
                "   -v           shown version information\n"
                "   -p<path>     path to find control sockets (default: "
                "/var/run/hostapd)\n"
+               "   -s<dir_path> dir path to open client sockets (default: "
+               CONFIG_CTRL_IFACE_DIR ")\n"
                "   -a<file>     run in daemon mode executing the action file "
                "based on events\n"
                "                from hostapd\n"
@@ -154,7 +148,14 @@ static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
                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;
 }
@@ -220,8 +221,27 @@ static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "RELOG");
+}
+
+
+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");
 }
 
@@ -229,28 +249,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;
 }
 
 
@@ -274,12 +285,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);
 }
 
@@ -390,7 +404,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;
        }
@@ -405,38 +419,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);
 }
-#endif /* CONFIG_WPS_OOB */
+
+
+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_NFC */
 
 
 static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
@@ -458,9 +539,127 @@ static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
                snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
        return wpa_ctrl_command(ctrl, buf);
 }
+
+
+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 * SSID_MAX_LEN + 1];
+       char key_hex[2 * 64 + 1];
+       int i;
+
+       if (argc < 1) {
+               printf("Invalid 'wps_config' command - at least two arguments "
+                      "are required.\n");
+               return -1;
+       }
+
+       ssid_hex[0] = '\0';
+       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]);
+       }
+
+       key_hex[0] = '\0';
+       if (argc > 3) {
+               for (i = 0; i < 64; i++) {
+                       if (argv[3][i] == '\0')
+                               break;
+                       os_snprintf(&key_hex[i * 2], 3, "%02x",
+                                   argv[3][i]);
+               }
+       }
+
+       if (argc > 3)
+               snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
+                        ssid_hex, argv[1], argv[2], key_hex);
+       else if (argc > 2)
+               snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
+                        ssid_hex, argv[1], argv[2]);
+       else
+               snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
+                        ssid_hex, argv[1]);
+       return wpa_ctrl_command(ctrl, buf);
+}
 #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[])
 {
@@ -534,9 +733,95 @@ static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+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;
 }
 
@@ -586,8 +871,10 @@ 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);
@@ -617,7 +904,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;
        }
@@ -625,14 +912,172 @@ static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid GET command: needs one argument (variable "
+                      "name)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
+       if (os_snprintf_error(sizeof(cmd), res)) {
+               printf("Too long GET command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+#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: <cs_count> <freq> [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: <vendor id> <command id> [<hex formatted command argument>]\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);
+}
+
+
 struct hostapd_cli_cmd {
        const char *cmd;
        int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
 };
 
-static struct hostapd_cli_cmd hostapd_cli_commands[] = {
+static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
        { "ping", hostapd_cli_cmd_ping },
        { "mib", hostapd_cli_cmd_mib },
+       { "relog", hostapd_cli_cmd_relog },
+       { "status", hostapd_cli_cmd_status },
        { "sta", hostapd_cli_cmd_sta },
        { "all_sta", hostapd_cli_cmd_all_sta },
        { "new_sta", hostapd_cli_cmd_new_sta },
@@ -645,25 +1090,49 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
        { "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_cancel", hostapd_cli_cmd_wps_cancel },
+#ifdef CONFIG_WPS_NFC
+       { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
+       { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
+       { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
+       { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
+#endif /* CONFIG_WPS_NFC */
        { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
+       { "wps_config", hostapd_cli_cmd_wps_config },
+       { "wps_get_status", hostapd_cli_cmd_wps_get_status },
 #endif /* CONFIG_WPS */
+       { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
+       { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
+       { "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
        { "get_config", hostapd_cli_cmd_get_config },
        { "help", hostapd_cli_cmd_help },
        { "interface", hostapd_cli_cmd_interface },
+#ifdef CONFIG_FST
+       { "fst", hostapd_cli_cmd_fst },
+#endif /* CONFIG_FST */
        { "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 },
+       { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
+       { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
+       { "chan_switch", hostapd_cli_cmd_chan_switch },
+       { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
+       { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
+       { "vendor", hostapd_cli_cmd_vendor },
+       { "enable", hostapd_cli_cmd_enable },
+       { "reload", hostapd_cli_cmd_reload },
+       { "disable", hostapd_cli_cmd_disable },
+       { "erp_flush", hostapd_cli_cmd_erp_flush },
+       { "log_level", hostapd_cli_cmd_log_level },
        { NULL, NULL }
 };
 
 
 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;
@@ -671,6 +1140,11 @@ static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
        while (cmd->cmd) {
                if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
                        match = cmd;
+                       if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
+                               /* we have an exact match */
+                               count = 1;
+                               break;
+                       }
                        count++;
                }
                cmd++;
@@ -722,70 +1196,39 @@ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
 }
 
 
-static void hostapd_cli_interactive(void)
-{
-       const int max_args = 10;
-       char cmd[256], *res, *argv[max_args], *pos;
-       int argc;
+#define max_args 10
 
-       printf("\nInteractive mode\n\n");
+static int tokenize_cmd(char *cmd, char *argv[])
+{
+       char *pos;
+       int argc = 0;
 
-       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 = 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;
                }
-               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);
-}
-
-
-static void hostapd_cli_cleanup(void)
-{
-       hostapd_cli_close_connection();
-       if (pid_file)
-               os_daemonize_terminate(pid_file);
-
-       os_program_deinit();
-}
-
+               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");
@@ -805,7 +1248,55 @@ 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 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,
+                 NULL, NULL, NULL, NULL);
+       eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
+
+       eloop_run();
+
+       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();
 }
 
 
@@ -848,7 +1339,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;
@@ -857,7 +1347,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) {
@@ -883,6 +1373,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;
@@ -896,6 +1392,9 @@ int main(int argc, char *argv[])
                       hostapd_cli_license);
        }
 
+       if (eloop_init())
+               return -1;
+
        for (;;) {
                if (ctrl_ifname == NULL) {
                        struct dirent *dent;
@@ -935,10 +1434,6 @@ 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;
@@ -960,6 +1455,7 @@ int main(int argc, char *argv[])
                wpa_request(ctrl_conn, argc - optind, &argv[optind]);
 
        os_free(ctrl_ifname);
+       eloop_destroy();
        hostapd_cli_cleanup();
        return 0;
 }