|
|
@ -1,4 +1,5 @@ |
|
|
|
/* Copyright 2011 Codership Oy <http://www.codership.com>
|
|
|
|
Copyright 2014 SkySQL Ab. |
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify |
|
|
|
it under the terms of the GNU General Public License as published by |
|
|
@ -13,367 +14,89 @@ |
|
|
|
along with this program; if not, write to the Free Software |
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
|
|
|
|
|
|
|
//#include <mysqld.h>
|
|
|
|
#include <sql_class.h>
|
|
|
|
//#include <sql_plugin.h>
|
|
|
|
//#include <set_var.h>
|
|
|
|
#include "mysqld.h"
|
|
|
|
#include "sys_vars_shared.h"
|
|
|
|
#include "wsrep.h"
|
|
|
|
#include "wsrep_sst.h"
|
|
|
|
//#include <sql_class.h>
|
|
|
|
//#include "wsrep_mysqld.h"
|
|
|
|
|
|
|
|
#include "wsrep_mysqld.h"
|
|
|
|
extern char *my_bind_addr_str; |
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
/* This file is about checking for correctness of mysql configuration options */ |
|
|
|
|
|
|
|
struct opt |
|
|
|
{ |
|
|
|
const char* const name; |
|
|
|
const char* value; |
|
|
|
}; |
|
|
|
|
|
|
|
/* A list of options to check.
|
|
|
|
* At first we assume default values and then see if they are changed on CLI or |
|
|
|
* in my.cnf */ |
|
|
|
static struct opt opts[] = |
|
|
|
{ |
|
|
|
{ "wsrep_slave_threads", "1" }, // mysqld.cc
|
|
|
|
{ "bind_address", "0.0.0.0" }, // mysqld.cc
|
|
|
|
{ "wsrep_sst_method", "rsync" }, // mysqld.cc
|
|
|
|
{ "wsrep_sst_receive_address","AUTO"}, // mysqld.cc
|
|
|
|
{ "binlog_format", "ROW" }, // mysqld.cc
|
|
|
|
{ "wsrep_provider", "none" }, // mysqld.cc
|
|
|
|
{ "query_cache_type", "0" }, // mysqld.cc
|
|
|
|
{ "query_cache_size", "0" }, // mysqld.cc
|
|
|
|
{ "locked_in_memory", "0" }, // mysqld.cc
|
|
|
|
{ "wsrep_cluster_address", "0" }, // mysqld.cc
|
|
|
|
{ "locks_unsafe_for_binlog", "0" }, // ha_innodb.cc
|
|
|
|
{ "autoinc_lock_mode", "1" }, // ha_innodb.cc
|
|
|
|
{ 0, 0 } |
|
|
|
}; |
|
|
|
|
|
|
|
enum |
|
|
|
{ |
|
|
|
WSREP_SLAVE_THREADS, |
|
|
|
BIND_ADDRESS, |
|
|
|
WSREP_SST_METHOD, |
|
|
|
WSREP_SST_RECEIVE_ADDRESS, |
|
|
|
BINLOG_FORMAT, |
|
|
|
WSREP_PROVIDER, |
|
|
|
QUERY_CACHE_TYPE, |
|
|
|
QUERY_CACHE_SIZE, |
|
|
|
LOCKED_IN_MEMORY, |
|
|
|
WSREP_CLUSTER_ADDRESS, |
|
|
|
LOCKS_UNSAFE_FOR_BINLOG, |
|
|
|
AUTOINC_LOCK_MODE |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/* A class to make a copy of argv[] vector */ |
|
|
|
struct argv_copy |
|
|
|
{ |
|
|
|
int const argc_; |
|
|
|
char** argv_; |
|
|
|
|
|
|
|
argv_copy (int const argc, const char* const argv[]) : |
|
|
|
argc_ (argc), |
|
|
|
argv_ (reinterpret_cast<char**>(calloc(argc_, sizeof(char*)))) |
|
|
|
{ |
|
|
|
if (argv_) |
|
|
|
{ |
|
|
|
for (int i = 0; i < argc_; ++i) |
|
|
|
{ |
|
|
|
argv_[i] = strdup(argv[i]); |
|
|
|
|
|
|
|
if (!argv_[i]) |
|
|
|
{ |
|
|
|
argv_free (); // free whatever bee allocated
|
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
~argv_copy () { argv_free (); } |
|
|
|
|
|
|
|
private: |
|
|
|
argv_copy (const argv_copy&); |
|
|
|
argv_copy& operator= (const argv_copy&); |
|
|
|
|
|
|
|
void argv_free() |
|
|
|
{ |
|
|
|
if (argv_) |
|
|
|
{ |
|
|
|
for (int i = 0; (i < argc_) && argv_[i] ; ++i) free (argv_[i]); |
|
|
|
free (argv_); |
|
|
|
argv_ = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/* a short corresponding to '--' byte sequence */ |
|
|
|
static short const long_opt_prefix ('-' + ('-' << 8)); |
|
|
|
|
|
|
|
/* Normalizes long options to have '_' instead of '-' */ |
|
|
|
static int |
|
|
|
normalize_opts (argv_copy& a) |
|
|
|
int wsrep_check_opts() |
|
|
|
{ |
|
|
|
if (a.argv_) |
|
|
|
if (wsrep_slave_threads > 1) |
|
|
|
{ |
|
|
|
sys_var *autoinc_lock_mode= |
|
|
|
intern_find_sys_var(STRING_WITH_LEN("innodb_autoinc_lock_mode")); |
|
|
|
bool is_null; |
|
|
|
if (autoinc_lock_mode && |
|
|
|
autoinc_lock_mode->val_int(&is_null, 0, OPT_GLOBAL, 0) != 2) |
|
|
|
{ |
|
|
|
for (int i = 0; i < a.argc_; ++i) |
|
|
|
{ |
|
|
|
char* ptr = a.argv_[i]; |
|
|
|
if (long_opt_prefix == *(short*)ptr) // long option
|
|
|
|
{ |
|
|
|
ptr += 2; |
|
|
|
const char* end = strchr(ptr, '='); |
|
|
|
|
|
|
|
if (!end) end = ptr + strlen(ptr); |
|
|
|
|
|
|
|
for (; ptr != end; ++ptr) if ('-' == *ptr) *ptr = '_'; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
WSREP_ERROR("Parallel applying (wsrep_slave_threads > 1) requires" |
|
|
|
" innodb_autoinc_lock_mode = 2."); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
return EINVAL; |
|
|
|
} |
|
|
|
|
|
|
|
/* Find required options in the argument list and change their values */ |
|
|
|
static int |
|
|
|
find_opts (argv_copy& a, struct opt* const opts) |
|
|
|
{ |
|
|
|
for (int i = 0; i < a.argc_; ++i) |
|
|
|
{ |
|
|
|
char* ptr = a.argv_[i] + 2; // we're interested only in long options
|
|
|
|
|
|
|
|
struct opt* opt = opts; |
|
|
|
for (; 0 != opt->name; ++opt) |
|
|
|
{ |
|
|
|
if (!strstr(ptr, opt->name)) continue; // try next option
|
|
|
|
|
|
|
|
/* 1. try to find value after the '=' */ |
|
|
|
opt->value = strchr(ptr, '=') + 1; |
|
|
|
|
|
|
|
/* 2. if no '=', try next element in the argument vector */ |
|
|
|
if (reinterpret_cast<void*>(1) == opt->value) |
|
|
|
{ |
|
|
|
/* also check that the next element is not an option itself */ |
|
|
|
if (i + 1 < a.argc_ && *(a.argv_[i + 1]) != '-') |
|
|
|
{ |
|
|
|
++i; |
|
|
|
opt->value = a.argv_[i]; |
|
|
|
} |
|
|
|
else opt->value = ""; // no value supplied (like boolean opt)
|
|
|
|
} |
|
|
|
|
|
|
|
break; // option found, break inner loop
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* Parses string for an integer. Returns 0 on success. */ |
|
|
|
int get_long_long (const struct opt& opt, long long* const val, int const base) |
|
|
|
{ |
|
|
|
const char* const str = opt.value; |
|
|
|
|
|
|
|
if ('\0' != *str) |
|
|
|
{ |
|
|
|
char* endptr; |
|
|
|
|
|
|
|
*val = strtoll (str, &endptr, base); |
|
|
|
|
|
|
|
if ('k' == *endptr || 'K' == *endptr) |
|
|
|
{ |
|
|
|
*val *= 1024L; |
|
|
|
endptr++; |
|
|
|
} |
|
|
|
else if ('m' == *endptr || 'M' == *endptr) |
|
|
|
{ |
|
|
|
*val *= 1024L * 1024L; |
|
|
|
endptr++; |
|
|
|
} |
|
|
|
else if ('g' == *endptr || 'G' == *endptr) |
|
|
|
{ |
|
|
|
*val *= 1024L * 1024L * 1024L; |
|
|
|
endptr++; |
|
|
|
} |
|
|
|
|
|
|
|
if ('\0' == *endptr) return 0; // the whole string was a valid integer
|
|
|
|
} |
|
|
|
|
|
|
|
WSREP_ERROR ("Bad value for *%s: '%s'. Should be integer.", |
|
|
|
opt.name, opt.value); |
|
|
|
|
|
|
|
return EINVAL; |
|
|
|
} |
|
|
|
|
|
|
|
/* This is flimzy coz hell knows how mysql interprets boolean strings...
|
|
|
|
* and, no, I'm not going to become versed in how mysql handles options - |
|
|
|
* I'd rather sing. |
|
|
|
|
|
|
|
Aha, http://dev.mysql.com/doc/refman/5.1/en/dynamic-system-variables.html:
|
|
|
|
Variables that have a type of “boolean” can be set to 0, 1, ON or OFF. (If you |
|
|
|
set them on the command line or in an option file, use the numeric values.) |
|
|
|
|
|
|
|
So it is '0' for FALSE, '1' or empty string for TRUE |
|
|
|
|
|
|
|
*/ |
|
|
|
int get_bool (const struct opt& opt, bool* const val) |
|
|
|
{ |
|
|
|
const char* str = opt.value; |
|
|
|
|
|
|
|
while (isspace(*str)) ++str; // skip initial whitespaces
|
|
|
|
|
|
|
|
ssize_t str_len = strlen(str); |
|
|
|
switch (str_len) |
|
|
|
{ |
|
|
|
case 0: |
|
|
|
*val = true; |
|
|
|
return 0; |
|
|
|
case 1: |
|
|
|
if ('0' == *str || '1' == *str) |
|
|
|
{ |
|
|
|
*val = ('1' == *str); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
WSREP_ERROR ("Bad value for *%s: '%s'. Should be '0', '1' or empty string.", |
|
|
|
opt.name, opt.value); |
|
|
|
|
|
|
|
return EINVAL; |
|
|
|
} |
|
|
|
|
|
|
|
static int |
|
|
|
check_opts (int const argc, const char* const argv[], struct opt opts[]) |
|
|
|
{ |
|
|
|
/* First, make a copy of argv to be able to manipulate it */ |
|
|
|
argv_copy a(argc, argv); |
|
|
|
|
|
|
|
if (!a.argv_) |
|
|
|
{ |
|
|
|
WSREP_ERROR ("Could not copy argv vector: not enough memory."); |
|
|
|
return ENOMEM; |
|
|
|
} |
|
|
|
|
|
|
|
int err = normalize_opts (a); |
|
|
|
if (err) |
|
|
|
{ |
|
|
|
WSREP_ERROR ("Failed to normalize options."); |
|
|
|
return err; |
|
|
|
} |
|
|
|
|
|
|
|
err = find_opts (a, opts); |
|
|
|
if (err) |
|
|
|
{ |
|
|
|
WSREP_ERROR ("Failed to parse options."); |
|
|
|
return err; |
|
|
|
} |
|
|
|
|
|
|
|
/* At this point we have updated default values in our option list to
|
|
|
|
what has been specified on the command line / my.cnf */ |
|
|
|
|
|
|
|
long long slave_threads; |
|
|
|
err = get_long_long (opts[WSREP_SLAVE_THREADS], &slave_threads, 10); |
|
|
|
if (err) return err; |
|
|
|
|
|
|
|
int rcode = 0; |
|
|
|
|
|
|
|
if (slave_threads > 1) |
|
|
|
/* Need to check AUTOINC_LOCK_MODE and LOCKS_UNSAFE_FOR_BINLOG */ |
|
|
|
{ |
|
|
|
long long autoinc_lock_mode; |
|
|
|
err = get_long_long (opts[AUTOINC_LOCK_MODE], &autoinc_lock_mode, 10); |
|
|
|
if (err) return err; |
|
|
|
|
|
|
|
bool locks_unsafe_for_binlog; |
|
|
|
err = get_bool (opts[LOCKS_UNSAFE_FOR_BINLOG],&locks_unsafe_for_binlog); |
|
|
|
if (err) return err; |
|
|
|
|
|
|
|
if (autoinc_lock_mode != 2) |
|
|
|
{ |
|
|
|
WSREP_ERROR ("Parallel applying (wsrep_slave_threads > 1) requires" |
|
|
|
" innodb_autoinc_lock_mode = 2."); |
|
|
|
rcode = EINVAL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
bool locked_in_memory; |
|
|
|
err = get_bool (opts[LOCKED_IN_MEMORY], &locked_in_memory); |
|
|
|
if (err) { WSREP_ERROR("get_bool error: %s", strerror(err)); return err; } |
|
|
|
if (locked_in_memory) |
|
|
|
{ |
|
|
|
WSREP_ERROR ("Memory locking is not supported (locked_in_memory=%s)", |
|
|
|
locked_in_memory ? "ON" : "OFF"); |
|
|
|
rcode = EINVAL; |
|
|
|
WSREP_ERROR("Memory locking is not supported (locked_in_memory=ON)"); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
if (!strcasecmp(opts[WSREP_SST_METHOD].value,"mysqldump")) |
|
|
|
if (!strcasecmp(wsrep_sst_method, "mysqldump")) |
|
|
|
{ |
|
|
|
if (!strcasecmp(opts[BIND_ADDRESS].value, "127.0.0.1") || |
|
|
|
!strcasecmp(opts[BIND_ADDRESS].value, "localhost")) |
|
|
|
{ |
|
|
|
WSREP_ERROR ("wsrep_sst_method is set to 'mysqldump' yet " |
|
|
|
"mysqld bind_address is set to '%s', which makes it " |
|
|
|
"impossible to receive state transfer from another " |
|
|
|
"node, since mysqld won't accept such connections. " |
|
|
|
"If you wish to use mysqldump state transfer method, " |
|
|
|
"set bind_address to allow mysql client connections " |
|
|
|
"from other cluster members (e.g. 0.0.0.0).", |
|
|
|
opts[BIND_ADDRESS].value); |
|
|
|
rcode = EINVAL; |
|
|
|
} |
|
|
|
if (!strcasecmp(my_bind_addr_str, "127.0.0.1") || |
|
|
|
!strcasecmp(my_bind_addr_str, "localhost")) |
|
|
|
{ |
|
|
|
WSREP_ERROR("wsrep_sst_method is set to 'mysqldump' yet " |
|
|
|
"mysqld bind_address is set to '%s', which makes it " |
|
|
|
"impossible to receive state transfer from another " |
|
|
|
"node, since mysqld won't accept such connections. " |
|
|
|
"If you wish to use mysqldump state transfer method, " |
|
|
|
"set bind_address to allow mysql client connections " |
|
|
|
"from other cluster members (e.g. 0.0.0.0).", |
|
|
|
my_bind_addr_str); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
// non-mysqldump SST requires wsrep_cluster_address on startup
|
|
|
|
if (strlen(opts[WSREP_CLUSTER_ADDRESS].value) == 0) |
|
|
|
{ |
|
|
|
WSREP_ERROR ("%s SST method requires wsrep_cluster_address to be " |
|
|
|
"configured on startup.",opts[WSREP_SST_METHOD].value); |
|
|
|
rcode = EINVAL; |
|
|
|
} |
|
|
|
// non-mysqldump SST requires wsrep_cluster_address on startup
|
|
|
|
if (!wsrep_cluster_address || !wsrep_cluster_address[0]) |
|
|
|
{ |
|
|
|
WSREP_ERROR ("%s SST method requires wsrep_cluster_address to be " |
|
|
|
"configured on startup.", wsrep_sst_method); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (strcasecmp(opts[WSREP_SST_RECEIVE_ADDRESS].value, "AUTO")) |
|
|
|
if (strcasecmp(wsrep_sst_receive_address, "AUTO")) |
|
|
|
{ |
|
|
|
if (!strncasecmp(opts[WSREP_SST_RECEIVE_ADDRESS].value, |
|
|
|
"127.0.0.1", strlen("127.0.0.1")) || |
|
|
|
!strncasecmp(opts[WSREP_SST_RECEIVE_ADDRESS].value, |
|
|
|
"localhost", strlen("localhost"))) |
|
|
|
{ |
|
|
|
WSREP_WARN ("wsrep_sst_receive_address is set to '%s' which " |
|
|
|
"makes it impossible for another host to reach this " |
|
|
|
"one. Please set it to the address which this node " |
|
|
|
"can be connected at by other cluster members.", |
|
|
|
opts[WSREP_SST_RECEIVE_ADDRESS].value); |
|
|
|
// rcode = EINVAL;
|
|
|
|
} |
|
|
|
if (!strncasecmp(wsrep_sst_receive_address, STRING_WITH_LEN("127.0.0.1")) || |
|
|
|
!strncasecmp(wsrep_sst_receive_address, STRING_WITH_LEN("localhost"))) |
|
|
|
{ |
|
|
|
WSREP_WARN("wsrep_sst_receive_address is set to '%s' which " |
|
|
|
"makes it impossible for another host to reach this " |
|
|
|
"one. Please set it to the address which this node " |
|
|
|
"can be connected at by other cluster members.", |
|
|
|
wsrep_sst_receive_address); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (strcasecmp(opts[WSREP_PROVIDER].value, "none")) |
|
|
|
if (strcasecmp(wsrep_provider, "NONE")) |
|
|
|
{ |
|
|
|
if (strcasecmp(opts[BINLOG_FORMAT].value, "ROW")) |
|
|
|
{ |
|
|
|
WSREP_ERROR ("Only binlog_format = 'ROW' is currently supported. " |
|
|
|
"Configured value: '%s'. Please adjust your " |
|
|
|
"configuration.", opts[BINLOG_FORMAT].value); |
|
|
|
|
|
|
|
rcode = EINVAL; |
|
|
|
} |
|
|
|
if (global_system_variables.binlog_format != BINLOG_FORMAT_ROW) |
|
|
|
{ |
|
|
|
WSREP_ERROR("Only binlog_format = 'ROW' is currently supported. " |
|
|
|
"Configured value: '%s'. Please adjust your " |
|
|
|
"configuration.", |
|
|
|
binlog_format_names[global_system_variables.binlog_format]); |
|
|
|
|
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return rcode; |
|
|
|
} |
|
|
|
|
|
|
|
int |
|
|
|
wsrep_check_opts (int const argc, char* const* const argv) |
|
|
|
{ |
|
|
|
return check_opts (argc, argv, opts); |
|
|
|
} |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|