blob: eaec5f9b5906ef36f614e4c71963c4ec207e9c7f [file] [log] [blame]
#include "android/cmdline-option.h"
#include "android/constants.h"
#include "android/utils/debug.h"
#include "android/utils/misc.h"
#include "android/utils/system.h"
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define _VERBOSE_TAG(x,y) { #x, VERBOSE_##x, y },
static const struct { const char* name; int flag; const char* text; }
debug_tags[] = {
VERBOSE_TAG_LIST
{ 0, 0, 0 }
};
void parse_env_debug_tags( void );
enum {
OPTION_IS_FLAG = 0,
OPTION_IS_PARAM,
OPTION_IS_LIST,
};
typedef struct {
const char* name;
int var_offset;
int var_type;
int var_is_config;
} OptionInfo;
#define OPTION(_name,_type,_config) \
{ #_name, offsetof(AndroidOptions,_name), _type, _config },
static const OptionInfo option_keys[] = {
#define OPT_FLAG(_name,_descr) OPTION(_name,OPTION_IS_FLAG,0)
#define OPT_PARAM(_name,_template,_descr) OPTION(_name,OPTION_IS_PARAM,0)
#define OPT_LIST(_name,_template,_descr) OPTION(_name,OPTION_IS_LIST,0)
#define CFG_FLAG(_name,_descr) OPTION(_name,OPTION_IS_FLAG,1)
#define CFG_PARAM(_name,_template,_descr) OPTION(_name,OPTION_IS_PARAM,1)
#include "android/cmdline-options.h"
{ NULL, 0, 0, 0 }
};
int
android_parse_options( int *pargc, char** *pargv, AndroidOptions* opt )
{
int nargs = *pargc-1;
char** aread = *pargv+1;
char** awrite = aread;
memset( opt, 0, sizeof *opt );
while (nargs > 0) {
char* arg;
char arg2_tab[64], *arg2 = arg2_tab;
/* process @<name> as a special exception meaning
* '-avd <name>'
*/
if (aread[0][0] == '@') {
opt->avd = aread[0] + 1;
nargs--;
aread++;
continue;
}
/* anything that isn't an option past this points
* exits the loop
*/
if (aread[0][0] != '-') {
break;
}
arg = aread[0]+1;
/* an option cannot contain an underscore */
if (strchr(arg, '_') != NULL) {
break;
}
nargs--;
aread++;
/* for backwards compatibility with previous versions */
if (!strcmp(arg, "verbose")) {
arg = "debug-init";
}
/* special handing for -debug <tags> and -debug-<tags> */
if (!strcmp(arg, "debug")) {
if (nargs == 0) {
derror( "-debug must be followed by tags (see -help-verbose)\n");
return -1;
}
nargs--;
android_parse_debug_tags_option(*aread++, /*parse_as_suffix*/false);
continue;
} else if (!strncmp(arg, "debug-", sizeof("debug-") - 1)) {
// For a "-debug-<tag>" option we only allow a single tag,
// otherwise it may look strange to someone.
android_parse_debug_tags_option(arg + sizeof("debug-") - 1, /*parse_as_suffix*/true);
continue;
}
/* NOTE: variable tables map option names to values
* (e.g. field offsets into the AndroidOptions structure).
*
* however, the names stored in the table used underscores
* instead of dashes. this means that the command-line option
* '-foo-bar' will be associated to the name 'foo_bar' in
* this table, and will point to the field 'foo_bar' or
* AndroidOptions.
*
* as such, before comparing the current option to the
* content of the table, we're going to translate dashes
* into underscores.
*/
arg2 = arg2_tab;
buffer_translate_char( arg2_tab, sizeof(arg2_tab),
arg, '-', '_');
/* look into our table of options
*
*/
{
const OptionInfo* oo = option_keys;
for ( ; oo->name; oo++ ) {
if ( !strcmp( oo->name, arg2 ) ) {
void* field = (char*)opt + oo->var_offset;
if (oo->var_type != OPTION_IS_FLAG) {
/* parameter/list option */
if (nargs == 0) {
derror( "-%s must be followed by parameter (see -help-%s)",
arg, arg );
exit(1);
}
nargs--;
if (oo->var_type == OPTION_IS_PARAM)
{
((char**)field)[0] = strdup(*aread++);
}
else if (oo->var_type == OPTION_IS_LIST)
{
ParamList** head = (ParamList**)field;
ParamList* pl;
ANEW0(pl);
/* note: store list items in reverse order here
* the list is reversed later in this function.
*/
pl->param = strdup(*aread++);
pl->next = *head;
*head = pl;
}
} else {
/* flag option */
((int*)field)[0] = 1;
}
break;
}
}
if (oo->name == NULL) { /* unknown option ? */
nargs++;
aread--;
break;
}
}
}
/* copy remaining parameters, if any, to command line */
*pargc = nargs + 1;
while (nargs > 0) {
awrite[0] = aread[0];
awrite ++;
aread ++;
nargs --;
}
awrite[0] = NULL;
/* reverse any parameter list before exit.
*/
{
const OptionInfo* oo = option_keys;
for ( ; oo->name; oo++ ) {
if ( oo->var_type == OPTION_IS_LIST ) {
ParamList** head = (ParamList**)((char*)opt + oo->var_offset);
ParamList* prev = NULL;
ParamList* cur = *head;
while (cur != NULL) {
ParamList* next = cur->next;
cur->next = prev;
prev = cur;
cur = next;
}
*head = prev;
}
}
}
return 0;
}
/* special handling of -debug option and tags */
#define ENV_DEBUG "ANDROID_DEBUG"
// Checks if a character range [tagStart, tagEnd) represents a debug tag |to|.
// |tagStart| to |tagEnd| is assumed to be a substring of a zero-terminated C
// string, with no \0 characters in the middle.
static bool is_tag_equal(const char* tagStart, const char* tagEnd,
const char* to) {
const size_t tagLen = tagEnd - tagStart;
if (strncmp(to, tagStart, tagLen)) {
return false;
}
// Can't do this before the strncmp() call as |to| might be not long enough.
if (to[tagLen] != 0) {
return false;
}
return true;
}
bool android_parse_debug_tags_option(const char* opt, bool parse_as_suffix) {
if (opt == NULL)
return false;
bool result = false;
const char* x = opt;
while (*x) {
const char* y = parse_as_suffix ? (x + strlen(x)) : strchr(x, ',');
if (y == NULL)
y = x + strlen(x);
if (y > x+1) {
int remove = 0;
const char* x0 = x; // in case we need to print it out
if (!parse_as_suffix && x[0] == '-') {
remove = 1;
x += 1;
} else if (!strncmp(x, "no-", 3)) {
remove = 1;
x += 3;
}
if (is_tag_equal(x, y, "all")) {
result = true;
remove
? base_disable_verbose_logs()
: base_enable_verbose_logs();
} else {
char temp[32];
buffer_translate_char_with_len(temp, sizeof temp,
x, y - x, '-', '_');
int nn;
uint64_t mask = 0;
for (nn = 0; debug_tags[nn].name != NULL; nn++) {
if (is_tag_equal(temp, temp + (y - x), debug_tags[nn].name)) {
mask |= (1ULL << debug_tags[nn].flag);
break;
}
}
if (mask == 0) {
dprint("ignoring unknown debug item '%.*s'",
(int)(y - x0), x0);
} else {
result = true;
if (remove)
android_verbose &= ~mask;
else
android_verbose |= mask;
}
}
}
// Skip the comma, but don't jump over the terminating zero.
x = *y ? y + 1 : y;
}
return result;
}
void
parse_env_debug_tags( void )
{
const char* env = getenv( ENV_DEBUG );
android_parse_debug_tags_option(env, /*parse_as_suffix*/false);
}
bool android_validate_ports(int console_port, int adb_port) {
bool result = true;
int lower_bound = ANDROID_CONSOLE_BASEPORT + 1;
int upper_bound = lower_bound + (MAX_ANDROID_EMULATORS - 1) * 2 + 1;
if (adb_port < lower_bound || adb_port > upper_bound) {
dwarning("Requested adb port (%d) is outside the recommended range "
"[%d,%d]. ADB may not function properly for the emulator. See "
"-help-port for details.",
adb_port, lower_bound, upper_bound);
result = false;
} else if (adb_port % 2 == 0) {
dwarning("Requested adb port (%d) is an even number. ADB may not "
"function properly for the emulator. See -help-port for "
"details.", adb_port);
result = false;
}
return result;
}
bool android_parse_port_option(const char* port_string,
int* console_port,
int* adb_port) {
if (port_string == NULL) {
return false;
}
char* end;
errno = 0;
int port = strtol(port_string, &end, 0);
if (end == NULL || *end || errno || port < 1 || port > UINT16_MAX) {
derror("option -port must be followed by an integer. "
"'%s' is not a valid input.", port_string);
return false;
}
*console_port = port;
*adb_port = *console_port + 1;
dprint("Requested console port %d: Inferring adb port %d.",
*console_port, *adb_port);
return true;
}
bool android_parse_ports_option(const char* ports_string,
int* console_port,
int* adb_port) {
if (ports_string == NULL) {
return false;
}
char* comma_location;
char* end;
int first_port = strtol(ports_string, &comma_location, 0);
if (comma_location == NULL || *comma_location != ',' ||
first_port < 1 || first_port > UINT16_MAX) {
derror("Failed to parse option: |%s| (Could not parse first port). "
"See -help-ports.", ports_string);
return false;
}
int second_port = strtol(comma_location+1, &end, 0);
if (end == NULL || *end || second_port < 1 || second_port > UINT16_MAX) {
derror("Failed to parse option: |%s|. (Could not parse second port). "
"See -help-ports.", ports_string);
return false;
}
if (first_port == second_port) {
derror("Failed to parse option: |%s|. (Both ports are identical). "
"See -help-ports.", ports_string);
return false;
}
*console_port = first_port;
*adb_port = second_port;
return true;
}