blob: ede1b7a454691b5620e3da7bd35d0abac0c2a1d1 [file] [log] [blame]
/* Android console command implementation.
*
* Copyright (c) 2014 Linaro Limited
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "android-console.h"
#include "monitor/monitor.h"
#include "qemu/sockets.h"
#include "net/slirp.h"
#include "slirp/libslirp.h"
#include "qmp-commands.h"
#include "hw/misc/goldfish_battery.h"
#include "hw/input/goldfish_events.h"
typedef struct {
int is_udp;
int host_port;
int guest_port;
} RedirRec;
GList *redir_list;
void android_monitor_print_error(Monitor *mon, const char *fmt, ...)
{
/* Print an error (typically a syntax error from the parser), with
* the required "KO: " prefix.
*/
va_list ap;
monitor_printf(mon, "KO: ");
va_start(ap, fmt);
monitor_vprintf(mon, fmt, ap);
va_end(ap);
}
void android_console_kill(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "OK: killing emulator, bye bye\n");
monitor_suspend(mon);
qmp_quit(NULL);
}
void android_console_quit(Monitor *mon, const QDict *qdict)
{
/* Don't print an OK response for success, just close the connection */
if (monitor_disconnect(mon)) {
monitor_printf(mon, "KO: this connection doesn't support quitting\n");
}
}
#ifdef CONFIG_SLIRP
void android_console_redir_list(Monitor *mon, const QDict *qdict)
{
if (!redir_list) {
monitor_printf(mon, "no active redirections\n");
} else {
GList *l;
for (l = redir_list; l; l = l->next) {
RedirRec *r = l->data;
monitor_printf(mon, "%s:%-5d => %-5d\n", r->is_udp ? "udp" : "tcp",
r->host_port, r->guest_port);
}
}
monitor_printf(mon, "OK\n");
}
static int parse_proto(const char *s)
{
if (!strcmp(s, "tcp")) {
return 0;
} else if (!strcmp(s, "udp")) {
return 1;
} else {
return -1;
}
}
static int parse_port(const char *s)
{
char *end;
int port;
port = strtol(s, &end, 10);
if (*end != 0 || port < 1 || port > 65535) {
return 0;
}
return port;
}
void android_console_redir_add(Monitor *mon, const QDict *qdict)
{
const char *arg = qdict_get_str(qdict, "arg");
char **tokens;
int is_udp, host_port, guest_port;
Slirp *slirp;
Error *err = NULL;
struct in_addr host_addr = { .s_addr = htonl(INADDR_LOOPBACK) };
struct in_addr guest_addr = { .s_addr = 0 };
RedirRec *redir;
slirp = net_slirp_lookup(NULL, NULL, &err);
if (err) {
monitor_printf(mon, "KO: %s\n", error_get_pretty(err));
error_free(err);
return;
}
/* Argument syntax: "proto:hostport:guestport" */
tokens = g_strsplit(arg, ":", 0);
if (g_strv_length(tokens) != 3) {
goto fail_syntax;
}
is_udp = parse_proto(tokens[0]);
host_port = parse_port(tokens[1]);
guest_port = parse_port(tokens[2]);
if (is_udp < 0 || host_port == 0 || guest_port == 0) {
goto fail_syntax;
}
g_strfreev(tokens);
if (slirp_add_hostfwd(slirp, is_udp, host_addr, host_port,
guest_addr, guest_port) < 0) {
monitor_printf(mon, "KO: can't setup redirection, "
"port probably used by another program on host\n");
return;
}
redir = g_new0(RedirRec, 1);
redir->is_udp = is_udp;
redir->host_port = host_port;
redir->guest_port = guest_port;
redir_list = g_list_append(redir_list, redir);
monitor_printf(mon, "OK\n");
return;
fail_syntax:
monitor_printf(mon, "KO: bad redirection format, try "
"(tcp|udp):hostport:guestport\n");
g_strfreev(tokens);
}
static gint redir_cmp(gconstpointer a, gconstpointer b)
{
const RedirRec *ra = a;
const RedirRec *rb = b;
/* For purposes of list deletion, only protocol and host port matter */
if (ra->is_udp != rb->is_udp) {
return ra->is_udp - rb->is_udp;
}
return ra->host_port - rb->host_port;
}
void android_console_redir_del(Monitor *mon, const QDict *qdict)
{
const char *arg = qdict_get_str(qdict, "arg");
char **tokens;
int is_udp, host_port;
Slirp *slirp;
Error *err = NULL;
struct in_addr host_addr = { .s_addr = INADDR_ANY };
RedirRec rr;
GList *entry;
slirp = net_slirp_lookup(NULL, NULL, &err);
if (err) {
monitor_printf(mon, "KO: %s\n", error_get_pretty(err));
error_free(err);
return;
}
/* Argument syntax: "proto:hostport" */
tokens = g_strsplit(arg, ":", 0);
if (g_strv_length(tokens) != 2) {
goto fail_syntax;
}
is_udp = parse_proto(tokens[0]);
host_port = parse_port(tokens[1]);
if (is_udp < 0 || host_port == 0) {
goto fail_syntax;
}
g_strfreev(tokens);
rr.is_udp = is_udp;
rr.host_port = host_port;
entry = g_list_find_custom(redir_list, &rr, redir_cmp);
if (!entry || slirp_remove_hostfwd(slirp, is_udp, host_addr, host_port)) {
monitor_printf(mon, "KO: can't remove unknown redirection (%s:%d)\n",
is_udp ? "udp" : "tcp", host_port);
return;
}
g_free(entry->data);
redir_list = g_list_delete_link(redir_list, entry);
monitor_printf(mon, "OK\n");
return;
fail_syntax:
monitor_printf(mon, "KO: bad redirection format, try (tcp|udp):hostport\n");
g_strfreev(tokens);
}
#else /* not CONFIG_SLIRP */
void android_console_redir_list(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "KO: emulator not built with CONFIG_SLIRP\n");
}
void android_console_redir_add(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "KO: emulator not built with CONFIG_SLIRP\n");
}
void android_console_redir_remove(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "KO: emulator not built with CONFIG_SLIRP\n");
}
#endif
enum {
CMD_REDIR,
CMD_REDIR_LIST,
CMD_REDIR_ADD,
CMD_REDIR_DEL,
};
static const char *redir_help[] = {
/* CMD_REDIR */
"allows you to add, list and remove UDP and/or PORT redirection "
"from the host to the device\n"
"as an example, 'redir tcp:5000:6000' will route any packet sent "
"to the host's TCP port 5000\n"
"to TCP port 6000 of the emulated device\n"
"\n"
"available sub-commands:\n"
" list list current redirections\n"
" add add new redirection\n"
" del remove existing redirection\n",
/* CMD_REDIR_LIST */
"list current port redirections. use 'redir add' and 'redir del' to add "
"and remove them",
/* CMD_REDIR_ADD */
"add a new port redirection, arguments must be:\n"
"\n"
" redir add <protocol>:<host-port>:<guest-port>\n"
"\n"
"where: <protocol> is either 'tcp' or 'udp'\n"
" <host-port> a number indicating which "
"port on the host to open\n"
" <guest-port> a number indicating which "
"port to route to on the device\n"
"\n"
"as an example, 'redir tcp:5000:6000' will allow any packets sent to\n"
"the host's TCP port 5000 to be routed to TCP port 6000 of the "
"emulated device",
/* CMD_REDIR_DEL */
"remove a port redirecion that was created with 'redir add', "
"arguments must be:\n"
" redir del <protocol>:<host-port>\n\n"
"see the 'help redir add' for the meaning of <protocol> and <host-port>",
};
void android_console_redir(Monitor *mon, const QDict *qdict)
{
/* This only gets called for bad subcommands and help requests */
const char *helptext = qdict_get_try_str(qdict, "helptext");
/* Default to the first entry which is the parent help message */
int cmd = CMD_REDIR;
if (helptext) {
if (strstr(helptext, "add")) {
cmd = CMD_REDIR_ADD;
} else if (strstr(helptext, "del")) {
cmd = CMD_REDIR_DEL;
} else if (strstr(helptext, "list")) {
cmd = CMD_REDIR_LIST;
}
}
/* If this is not a help request then we are here with a bad sub-command */
monitor_printf(mon, "%s\n%s\n", redir_help[cmd],
helptext ? "OK" : "KO: missing sub-command");
}
void android_console_power_display(Monitor *mon, const QDict *qdict)
{
goldfish_battery_display(mon);
monitor_printf(mon, "OK\n");
}
void android_console_power_ac(Monitor *mon, const QDict *qdict)
{
const char *arg = qdict_get_try_str(qdict, "arg");
if (arg) {
if (strcasecmp(arg, "on") == 0) {
goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 1);
monitor_printf(mon, "OK\n");
return;
}
if (strcasecmp(arg, "off") == 0) {
goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 0);
monitor_printf(mon, "OK\n");
return;
}
}
monitor_printf(mon, "KO: Usage: \"ac on\" or \"ac off\"\n");
}
void android_console_power_status(Monitor *mon, const QDict *qdict)
{
const char *arg = qdict_get_try_str(qdict, "arg");
if (arg) {
if (strcasecmp(arg, "unknown") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_STATUS_UNKNOWN);
monitor_printf(mon, "OK\n");
return;
} else if (strcasecmp(arg, "charging") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_STATUS_CHARGING);
monitor_printf(mon, "OK\n");
return;
} else if (strcasecmp(arg, "discharging") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_STATUS_DISCHARGING);
monitor_printf(mon, "OK\n");
return;
} else if (strcasecmp(arg, "not-charging") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_STATUS_NOT_CHARGING);
monitor_printf(mon, "OK\n");
return;
} else if (strcasecmp(arg, "full") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_STATUS_FULL);
monitor_printf(mon, "OK\n");
return;
}
}
monitor_printf(mon, "KO: Usage: \"status unknown|charging|"
"discharging|not-charging|full\"\n");
}
void android_console_power_present(Monitor *mon, const QDict *qdict)
{
const char *arg = qdict_get_try_str(qdict, "arg");
if (arg) {
if (strcasecmp(arg, "true") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 1);
monitor_printf(mon, "OK\n");
return;
}
if (strcasecmp(arg, "false") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 0);
monitor_printf(mon, "OK\n");
return;
}
}
monitor_printf(mon, "KO: Usage: \"present true\" or \"present false\"\n");
}
void android_console_power_health(Monitor *mon, const QDict *qdict)
{
const char *arg = qdict_get_try_str(qdict, "arg");
if (arg) {
if (strcasecmp(arg, "unknown") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_HEALTH_UNKNOWN);
monitor_printf(mon, "OK\n");
return;
}
if (strcasecmp(arg, "good") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_HEALTH_GOOD);
monitor_printf(mon, "OK\n");
return;
}
if (strcasecmp(arg, "overheat") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_HEALTH_OVERHEAT);
monitor_printf(mon, "OK\n");
return;
}
if (strcasecmp(arg, "dead") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_HEALTH_DEAD);
monitor_printf(mon, "OK\n");
return;
}
if (strcasecmp(arg, "overvoltage") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_HEALTH_OVERVOLTAGE);
monitor_printf(mon, "OK\n");
return;
}
if (strcasecmp(arg, "failure") == 0) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_HEALTH_UNSPEC_FAILURE);
monitor_printf(mon, "OK\n");
return;
}
}
monitor_printf(mon, "KO: Usage: \"health unknown|good|overheat|"
"dead|overvoltage|failure\"\n");
}
void android_console_power_capacity(Monitor *mon, const QDict *qdict)
{
const char *arg = qdict_get_try_str(qdict, "arg");
if (arg) {
int capacity;
if (sscanf(arg, "%d", &capacity) == 1 &&
capacity >= 0 && capacity <= 100) {
goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_CAPACITY, capacity);
monitor_printf(mon, "OK\n");
return;
}
}
monitor_printf(mon, "KO: Usage: \"capacity <percentage>\"\n");
}
enum {
CMD_POWER,
CMD_POWER_DISPLAY,
CMD_POWER_AC,
CMD_POWER_STATUS,
CMD_POWER_PRESENT,
CMD_POWER_HEALTH,
CMD_POWER_CAPACITY,
};
static const char *power_help[] = {
/* CMD_POWER */
"allows to change battery and AC power status\n"
"\n"
"available sub-commands:\n"
" power display display battery and charger state\n"
" power ac set AC charging state\n"
" power status set battery status\n"
" power present set battery present state\n"
" power health set battery health state\n"
" power capacity set battery capacity state\n",
/* CMD_POWER_DISPLAY */
"display battery and charger state",
/* CMD_POWER_AC */
"'ac on|off' allows you to set the AC charging state to on or off",
/* CMD_POWER_STATUS */
"'status unknown|charging|discharging|not-charging|full' allows you "
"to set battery status",
/* CMD_POWER_PRESENT */
"'present true|false' allows you to set battery present state to true "
"or false",
/* CMD_POWER_HEALTH */
"'health unknown|good|overheat|dead|overvoltage|failure' allows you "
"to set battery health state",
/* CMD_POWER_CAPACITY */
"'capacity <percentage>' allows you to set battery capacity to a "
"value 0 - 100",
};
void android_console_power(Monitor *mon, const QDict *qdict)
{
/* This only gets called for bad subcommands and help requests */
const char *helptext = qdict_get_try_str(qdict, "helptext");
/* Default to the first entry which is the parent help message */
int cmd = CMD_POWER;
/* In the below command name parsing, "capacity" has to precede "ac"
* otherwise we will hit on "ac" first.
*/
if (helptext) {
if (strstr(helptext, "display")) {
cmd = CMD_POWER_DISPLAY;
} else if (strstr(helptext, "capacity")) {
cmd = CMD_POWER_CAPACITY;
} else if (strstr(helptext, "ac")) {
cmd = CMD_POWER_AC;
} else if (strstr(helptext, "status")) {
cmd = CMD_POWER_STATUS;
} else if (strstr(helptext, "present")) {
cmd = CMD_POWER_PRESENT;
} else if (strstr(helptext, "health")) {
cmd = CMD_POWER_HEALTH;
}
}
/* If this is not a help request then we are here with a bad sub-command */
monitor_printf(mon, "%s\n%s\n", power_help[cmd],
helptext ? "OK" : "KO: missing sub-command");
}
enum {
CMD_EVENT,
CMD_EVENT_TYPES,
CMD_EVENT_CODES,
};
static const char *event_help[] = {
/* CMD_EVENT */
"allows you to send fake hardware events to the kernel\n"
"\n"
"available sub-commands:\n"
" event send send a series of events to the kernel\n"
" event types list all <type> aliases\n"
" event codes list all <code> aliases for a given <type>\n"
" event text simulate keystrokes from a given text\n",
/* CMD_EVENT_TYPES */
"'event types' list all <type> string aliases supported by the "
"'event' subcommands",
/* CMD_EVENT_CODES */
"'event codes <type>' lists all <code> string aliases for a given "
"event <type>",
};
void android_console_event_types(Monitor *mon, const QDict *qdict)
{
int count = gf_get_event_type_count();
int nn;
monitor_printf(mon, "event <type> can be an integer or one of the"
"following aliases\n");
/* Loop through and print out each type found along with the count of alias
* codes if any.
*/
for (nn = 0; nn < count; nn++) {
char tmp[16];
char *p = tmp;
int code_count;
gf_get_event_type_name(nn, p);
code_count = gf_get_event_code_count(p);
monitor_printf(mon, " %-8s", p);
if (code_count > 0) {
monitor_printf(mon, " (%d code aliases)", code_count);
}
monitor_printf(mon, "\n");
}
monitor_printf(mon, "OK\n");
}
void android_console_event_codes(Monitor *mon, const QDict *qdict)
{
const char *arg = qdict_get_try_str(qdict, "arg");
int count, nn;
if (!arg) {
monitor_printf(mon, "KO: argument missing, try 'event codes <type>'\n");
return;
}
count = gf_get_event_code_count(arg);
/* If the type is invalid then bail */
if (count < 0) {
monitor_printf(mon,
"KO: bad argument, see 'event types' for valid values\n");
return;
}
if (count == 0) {
monitor_printf(mon, "no code aliases defined for this type\n");
} else {
monitor_printf(mon, "type '%s' accepts the following <code> aliases:\n",
arg);
for (nn = 0; nn < count; nn++) {
char temp[20], *p = temp;
gf_get_event_code_name(arg, nn, p);
monitor_printf(mon, " %-12s\r\n", p);
}
}
monitor_printf(mon, "OK\n");
}
void android_console_event(Monitor *mon, const QDict *qdict)
{
/* This only gets called for bad subcommands and help requests */
const char *helptext = qdict_get_try_str(qdict, "helptext");
/* Default to the first entry which is the parent help message */
int cmd = CMD_EVENT;
if (helptext) {
if (strstr(helptext, "types")) {
cmd = CMD_EVENT_TYPES;
} else if (strstr(helptext, "codes")) {
cmd = CMD_EVENT_CODES;
}
}
/* If this is not a help request then we are here with a bad sub-command */
monitor_printf(mon, "%s\n%s\n", event_help[cmd],
helptext ? "OK" : "KO: missing sub-command");
}