Move parts of wpa_cli to a new common file
[mech_eap.git] / hostapd / hostapd_cli.c
index 8caca4f..c0e27de 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd - command line interface for hostapd daemon
 /*
  * hostapd - command line interface for hostapd daemon
- * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include <dirent.h>
 
 #include "common/wpa_ctrl.h"
 #include <dirent.h>
 
 #include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/edit.h"
 #include "common/version.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"
 "hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> and contributors";
 
 
 
 
-static const char *hostapd_cli_license =
+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";
 
 "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 =
+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"
 "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"
@@ -56,38 +59,6 @@ static const char *hostapd_cli_full_license =
 "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
 "\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 <addr>           get MIB variables for one station\n"
-"   all_sta              get MIB variables for all stations\n"
-"   new_sta <addr>       add a new station\n"
-"   deauthenticate <addr>  deauthenticate a station\n"
-"   disassociate <addr>  disassociate a station\n"
-#ifdef CONFIG_IEEE80211W
-"   sa_query <addr>      send SA Query to a station\n"
-#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WPS
-"   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"
-"   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"
-"   interface [ifname]   show interfaces/select interface\n"
-"   level <debug level>  change debug level\n"
-"   license              show full hostapd_cli license\n"
-"   quit                 exit hostapd_cli\n";
-
 static struct wpa_ctrl *ctrl_conn;
 static int hostapd_cli_quit = 0;
 static int hostapd_cli_attached = 0;
 static struct wpa_ctrl *ctrl_conn;
 static int hostapd_cli_quit = 0;
 static int hostapd_cli_attached = 0;
@@ -96,6 +67,7 @@ static int hostapd_cli_attached = 0;
 #define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
 #endif /* CONFIG_CTRL_IFACE_DIR */
 static const char *ctrl_iface_dir = 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 char *ctrl_ifname = NULL;
 static const char *pid_file = NULL;
@@ -103,6 +75,9 @@ static const char *action_file = NULL;
 static int ping_interval = 5;
 static int interactive = 0;
 
 static int ping_interval = 5;
 static int interactive = 0;
 
+static void print_help(FILE *stream, const char *cmd);
+static char ** list_cmd_list(void);
+
 
 static void usage(void)
 {
 
 static void usage(void)
 {
@@ -111,42 +86,81 @@ static void usage(void)
                "\n"
                "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
                "[-a<path>] \\\n"
                "\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"
                "\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"
                "   -B           run a daemon in the background\n"
                "   -i<ifname>   Interface to listen on (default: first "
                "interface found in the\n"
                "   -a<file>     run in daemon mode executing the action file "
                "based on events\n"
                "                from hostapd\n"
                "   -B           run a daemon in the background\n"
                "   -i<ifname>   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 int get_cmd_arg_num(const char *str, int pos)
+{
+       int arg = 0, i;
+
+       for (i = 0; i <= pos; i++) {
+               if (str[i] != ' ') {
+                       arg++;
+                       while (i <= pos && str[i] != ' ')
+                               i++;
+               }
+       }
+
+       if (arg > 0)
+               arg--;
+       return arg;
+}
+
+
+static int str_starts(const char *src, const char *match)
+{
+       return os_strncmp(src, match, os_strlen(match)) == 0;
 }
 
 
 static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
 {
 }
 
 
 static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
 {
+#ifndef CONFIG_CTRL_IFACE_UDP
        char *cfile;
        int flen;
        char *cfile;
        int flen;
+#endif /* !CONFIG_CTRL_IFACE_UDP */
 
        if (ifname == NULL)
                return NULL;
 
 
        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);
 
        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;
        free(cfile);
        return ctrl_conn;
+#endif /* CONFIG_CTRL_IFACE_UDP */
 }
 
 
 }
 
 
@@ -204,6 +218,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");
 static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        return wpa_ctrl_command(ctrl, "PING");
@@ -238,28 +298,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)
 {
 static int hostapd_cli_exec(const char *program, const char *arg1,
                            const char *arg2)
 {
-       char *cmd;
+       char *arg;
        size_t len;
        int res;
        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;
                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;
 }
 
 
 }
 
 
@@ -402,7 +453,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]);
        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;
        }
                printf("Too long WPS_CHECK_PIN command.\n");
                return -1;
        }
@@ -465,7 +516,7 @@ static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
 
        res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
                          argv[0]);
 
        res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
                          argv[0]);
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
                return -1;
        }
                printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
                return -1;
        }
@@ -486,7 +537,7 @@ static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
        }
 
        res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
        }
 
        res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long WPS_NFC_TOKEN command.\n");
                return -1;
        }
                printf("Too long WPS_NFC_TOKEN command.\n");
                return -1;
        }
@@ -508,7 +559,7 @@ static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
 
        res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
                          argv[0], argv[1]);
 
        res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %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 NFC_GET_HANDOVER_SEL command.\n");
                return -1;
        }
                printf("Too long NFC_GET_HANDOVER_SEL command.\n");
                return -1;
        }
@@ -550,7 +601,7 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
                                      char *argv[])
 {
        char buf[256];
                                      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;
 
        char key_hex[2 * 64 + 1];
        int i;
 
@@ -561,7 +612,7 @@ static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
        }
 
        ssid_hex[0] = '\0';
        }
 
        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]);
                if (argv[0][i] == '\0')
                        break;
                os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
@@ -605,7 +656,7 @@ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
 
        res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
                          argv[0], argv[1]);
 
        res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
                          argv[0], argv[1]);
-       if (res < 0 || res >= (int) sizeof(buf))
+       if (os_snprintf_error(sizeof(buf), res))
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
@@ -625,12 +676,39 @@ static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
 
        res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
                          argv[0], argv[1], argv[2]);
 
        res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
                          argv[0], argv[1], argv[2]);
-       if (res < 0 || res >= (int) sizeof(buf))
+       if (os_snprintf_error(sizeof(buf), res))
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
 
 
                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[])
 {
 static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
                                      char *argv[])
 {
@@ -691,11 +769,26 @@ 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[])
 {
 
 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;
 }
 
 
        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[])
 {
 static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
                                   char *argv[])
 {
@@ -718,7 +811,7 @@ static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
        }
 
        res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
        }
 
        res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
-       if (res < 0 || res >= (int) sizeof(buf))
+       if (os_snprintf_error(sizeof(buf), res))
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
@@ -737,7 +830,7 @@ static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
        }
 
        res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
        }
 
        res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
-       if (res < 0 || res >= (int) sizeof(buf))
+       if (os_snprintf_error(sizeof(buf), res))
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
@@ -757,7 +850,7 @@ static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
 
        res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
                          argv[0], argv[1]);
 
        res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
                          argv[0], argv[1]);
-       if (res < 0 || res >= (int) sizeof(buf))
+       if (os_snprintf_error(sizeof(buf), res))
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
@@ -782,7 +875,7 @@ static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
                res = os_snprintf(buf, sizeof(buf),
                                  "HS20_DEAUTH_REQ %s %s %s",
                                  argv[0], argv[1], argv[2]);
                res = os_snprintf(buf, sizeof(buf),
                                  "HS20_DEAUTH_REQ %s %s %s",
                                  argv[0], argv[1], argv[2]);
-       if (res < 0 || res >= (int) sizeof(buf))
+       if (os_snprintf_error(sizeof(buf), res))
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
@@ -842,8 +935,10 @@ static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
        }
 
        hostapd_cli_close_connection();
        }
 
        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 (hostapd_cli_open_connection(ctrl_ifname)) {
                printf("Connected to interface '%s.\n", ctrl_ifname);
@@ -873,7 +968,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]);
        }
 
        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;
        }
                printf("Too long SET command.\n");
                return -1;
        }
@@ -893,7 +988,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]);
        }
 
        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;
        }
                printf("Too long GET command.\n");
                return -1;
        }
@@ -901,6 +996,35 @@ 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[])
 {
 static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
                                       int argc, char *argv[])
 {
@@ -921,7 +1045,7 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
 
        res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
                          argv[0], argv[1]);
 
        res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %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 CHAN_SWITCH command.\n");
                return -1;
        }
                printf("Too long CHAN_SWITCH command.\n");
                return -1;
        }
@@ -930,7 +1054,7 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
        for (i = 2; i < argc; i++) {
                tmp = cmd + total;
                res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
        for (i = 2; i < argc; i++) {
                tmp = cmd + total;
                res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
-               if (res < 0 || (size_t) res >= sizeof(cmd) - total - 1) {
+               if (os_snprintf_error(sizeof(cmd) - total, res)) {
                        printf("Too long CHAN_SWITCH command.\n");
                        return -1;
                }
                        printf("Too long CHAN_SWITCH command.\n");
                        return -1;
                }
@@ -940,61 +1064,310 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
 }
 
 
 }
 
 
+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);
+}
+
+
+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[]);
 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 },
-       { "status", hostapd_cli_cmd_status },
-       { "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,
+         "<addr> = 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,
+         "<addr> = add a new station" },
+       { "deauthenticate", hostapd_cli_cmd_deauthenticate, NULL,
+         "<addr> = deauthenticate a station" },
+       { "disassociate", hostapd_cli_cmd_disassociate, NULL,
+         "<addr> = disassociate a station" },
 #ifdef CONFIG_IEEE80211W
 #ifdef CONFIG_IEEE80211W
-       { "sa_query", hostapd_cli_cmd_sa_query },
+       { "sa_query", hostapd_cli_cmd_sa_query, NULL,
+         "<addr> = send SA Query to a station" },
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WPS
 #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 },
-       { "wps_cancel", hostapd_cli_cmd_wps_cancel },
+       { "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
+         "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
+       { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
+         "<PIN> = 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
 #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 },
+       { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read, NULL,
+         "<hexdump> = report read NFC tag with WPS data" },
+       { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token, NULL,
+         "<WPS/NDEF> = build NFC configuration token" },
+       { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token, NULL,
+         "<WPS/NDEF/enable/disable> = manager NFC password token" },
+       { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel, NULL,
+         NULL },
 #endif /* CONFIG_WPS_NFC */
 #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 },
+       { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin, NULL,
+         "<cmd> [params..] = enable/disable AP PIN" },
+       { "wps_config", hostapd_cli_cmd_wps_config, NULL,
+         "<SSID> <auth> <encr> <key> = configure AP" },
+       { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
+         "= show current WPS status" },
 #endif /* CONFIG_WPS */
 #endif /* CONFIG_WPS */
-       { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
-       { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
-       { "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 },
-       { "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 },
-       { 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, NULL,
+         "[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,
+         "<debug level> = 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[])
 {
 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;
        int count;
 
        count = 0;
@@ -1136,13 +1509,77 @@ static void hostapd_cli_edit_eof_cb(void *ctx)
 }
 
 
 }
 
 
+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,
 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);
+                 hostapd_cli_edit_completion_cb, NULL, NULL, NULL);
        eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
 
        eloop_run();
        eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
 
        eloop_run();
@@ -1209,7 +1646,7 @@ int main(int argc, char *argv[])
                return -1;
 
        for (;;) {
                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) {
                if (c < 0)
                        break;
                switch (c) {
@@ -1235,6 +1672,12 @@ int main(int argc, char *argv[])
                case 'p':
                        ctrl_iface_dir = optarg;
                        break;
                case 'p':
                        ctrl_iface_dir = optarg;
                        break;
+               case 'P':
+                       pid_file = optarg;
+                       break;
+               case 's':
+                       client_socket_dir = optarg;
+                       break;
                default:
                        usage();
                        return -1;
                default:
                        usage();
                        return -1;
@@ -1300,7 +1743,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)
                return -1;
 
        if (interactive)
@@ -1315,3 +1758,12 @@ int main(int argc, char *argv[])
        hostapd_cli_cleanup();
        return 0;
 }
        hostapd_cli_cleanup();
        return 0;
 }
+
+#else /* CONFIG_NO_CTRL_IFACE */
+
+int main(int argc, char *argv[])
+{
+       return -1;
+}
+
+#endif /* CONFIG_NO_CTRL_IFACE */