#include "utils/eloop.h"
#include "utils/edit.h"
#include "common/version.h"
+#include "common/cli.h"
#ifndef CONFIG_NO_CTRL_IFACE
"hostapd_cli v" VERSION_STR "\n"
"Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> and contributors";
-
-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 *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"
-"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 *const 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 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)
" -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 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;
+ }
}
if (ctrl_conn == NULL)
return;
+ unregister_event_handler(ctrl_conn);
if (hostapd_cli_attached) {
wpa_ctrl_detach(ctrl_conn);
hostapd_cli_attached = 0;
}
-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[])
{
}
+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[])
{
}
+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_TAXONOMY
+static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char buf[64];
+
+ if (argc != 1) {
+ printf("Invalid 'signature' command - exactly one argument, STA address, is required.\n");
+ return -1;
+ }
+ os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
+ return wpa_ctrl_command(ctrl, buf);
+}
+#endif /* CONFIG_TAXONOMY */
+
+
#ifdef CONFIG_IEEE80211W
static int hostapd_cli_cmd_sa_query(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;
}
+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 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;
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");
}
+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];
"= 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,
+ { "deauthenticate", hostapd_cli_cmd_deauthenticate,
+ hostapd_complete_deauthenticate,
"<addr> = deauthenticate a station" },
- { "disassociate", hostapd_cli_cmd_disassociate, NULL,
+ { "disassociate", hostapd_cli_cmd_disassociate,
+ hostapd_complete_disassociate,
"<addr> = disassociate a station" },
+#ifdef CONFIG_TAXONOMY
+ { "signature", hostapd_cli_cmd_signature, NULL,
+ "<addr> = get taxonomy signature for a station" },
+#endif /* CONFIG_TAXONOMY */
#ifdef CONFIG_IEEE80211W
{ "sa_query", hostapd_cli_cmd_sa_query, NULL,
"<addr> = send SA Query to a station" },
{ "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, NULL,
+ { "help", hostapd_cli_cmd_help, hostapd_cli_complete_help,
"= show this usage help" },
- { "interface", hostapd_cli_cmd_interface, NULL,
+ { "interface", hostapd_cli_cmd_interface, hostapd_complete_interface,
"[ifname] = show interfaces/select interface" },
#ifdef CONFIG_FST
{ "fst", hostapd_cli_cmd_fst, 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[])
{
const struct hostapd_cli_cmd *cmd, *match = NULL;
}
+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)
{
if (action_monitor)
hostapd_cli_action_process(buf, len);
else {
+ cli_event(buf);
if (in_read && first)
printf("\n");
first = 0;
}
-#define max_args 10
-
-static int tokenize_cmd(char *cmd, char *argv[])
+static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
- 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';
- }
-
- return argc;
+ hostapd_cli_recv_pending(ctrl_conn, 0, 0);
}
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");
eloop_run();
+ cli_txt_list_flush(&stations);
edit_deinit(NULL, NULL);
eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
}
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())
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)
else
wpa_request(ctrl_conn, argc - optind, &argv[optind]);
+ unregister_event_handler(ctrl_conn);
os_free(ctrl_ifname);
eloop_destroy();
hostapd_cli_cleanup();