diff --git a/CMakeLists.txt b/CMakeLists.txt index 649e700a..6b2959c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,6 +139,7 @@ SET(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DDBUG_OFF") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DDBUG_OFF") IF(CMAKE_COMPILER_IS_GNUCC) + ADD_DEFINITIONS(-D_GNU_SOURCE=1) INCLUDE(CheckCCompilerFlag) SET(GCC_FLAGS -Wunused -Wno-uninitialized -Wall -Wextra -Wformat-security -Wno-init-self -Wwrite-strings -Wshift-count-overflow -Wdeclaration-after-statement) FOREACH(GCC_FLAG ${GCC_FLAGS}) @@ -325,7 +326,7 @@ CONFIGURE_FILE(${CC_SOURCE_DIR}/include/mariadb_version.h.in INCLUDE_DIRECTORIES(${CC_BINARY_DIR}/include) IF(WIN32) - SET(SYSTEM_LIBS ws2_32 advapi32 kernel32) + SET(SYSTEM_LIBS ws2_32 advapi32 kernel32 shlwapi) ELSE() SET(SYSTEM_LIBS ${LIBPTHREAD} ${LIBDL} ${LIBM}) IF(ICONV_EXTERNAL) diff --git a/include/ma_config.h.in b/include/ma_config.h.in index 66d7cfa1..4c28a172 100644 --- a/include/ma_config.h.in +++ b/include/ma_config.h.in @@ -278,4 +278,4 @@ #cmakedefine HAVE_THREADS 1 #cmakedefine SHAREDIR "@SHAREDIR@" #cmakedefine DEFAULT_CHARSET_HOME "@DEFAULT_CHARSET_HOME@" - +#cmakedefine DEFAULT_SYSCONFDIR "@DEFAULT_SYSCONFDIR@" diff --git a/include/ma_global.h b/include/ma_global.h index 576a318b..6eb9aeb8 100644 --- a/include/ma_global.h +++ b/include/ma_global.h @@ -420,7 +420,7 @@ typedef SOCKET_SIZE_TYPE size_socket; #define FN_DEVCHAR ':' #ifndef FN_LIBCHAR -#ifdef __EMX__ +#ifdef _WIN32 #define FN_LIBCHAR '\\' #define FN_ROOTDIR "\\" #else diff --git a/libmariadb/ma_default.c b/libmariadb/ma_default.c index 48e49c6d..84c790e9 100644 --- a/libmariadb/ma_default.c +++ b/libmariadb/ma_default.c @@ -27,71 +27,138 @@ #ifdef _WIN32 #include +#include "Shlwapi.h" + static const char *ini_exts[]= {"ini", "cnf", 0}; -static const char *ini_dirs[]= {"C:", ".", 0}; -static const char *ini_env_dirs[]= {"WINDOWS", "HOMEPATH", 0}; -#define ENV_HOME_DIR "HOMEPATH" #define R_OK 4 #else #include static const char *ini_exts[]= {"cnf", 0}; -static const char *ini_dirs[]= {"/etc", "/etc/mysql", ".", 0}; -static const char *ini_env_dirs[]= {"HOME", "SYSCONFDIR", 0}; -#define ENV_HOME_DIR "HOME" #endif -extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value); +char **configuration_dirs= NULL; +#define MAX_CONFIG_DIRS 6 -char *_mariadb_get_default_file(char *filename, size_t length) +static int add_cfg_dir(char **cfg_dirs, const char *directory) { - int dirs; int exts; + int i; + + for (i=0; i < MAX_CONFIG_DIRS && cfg_dirs[i]; i++); + + if (i < MAX_CONFIG_DIRS) { + cfg_dirs[i]= strdup(directory); + return 0; + } + return 1; +} + +void release_configuration_dirs() +{ + if (configuration_dirs) + { + int i= 0; + while (configuration_dirs[i]) + free(configuration_dirs[i++]); + free(configuration_dirs); + } +} + +char **get_default_configuration_dirs() +{ +#ifdef _WIN32 + char dirname[FN_REFLEN]; +#endif char *env; - for (dirs= 0; ini_dirs[dirs]; dirs++) - { - for (exts= 0; ini_exts[exts]; exts++) - { - snprintf(filename, length, - "%s%cmy.%s", ini_dirs[dirs], FN_LIBCHAR, ini_exts[exts]); - if (!access(filename, R_OK)) - return filename; - } - } - for (dirs= 0; ini_env_dirs[dirs]; dirs++) - { - for (exts= 0; ini_exts[exts]; exts++) - { - env= getenv(ini_env_dirs[dirs]); - snprintf(filename, length, - "%s%cmy.%s", env, FN_LIBCHAR, ini_exts[exts]); - if (!access(filename, R_OK)) - return filename; - } - } + configuration_dirs= (char **)calloc(1, (MAX_CONFIG_DIRS + 1) * sizeof(char *)); + if (!configuration_dirs) + goto end; - /* check for .my file in home directoy */ - env= getenv(ENV_HOME_DIR); - for (exts= 0; ini_exts[exts]; exts++) +#ifdef _WIN32 + /* On Windows operating systems configuration files are stored in + 1. System directory + 2. Windows directory + 3. C:\ + */ + if (!GetSystemDirectory(dirname, FN_REFLEN) || + add_cfg_dir(configuration_dirs, dirname)) + goto error; + + if (!GetWindowsDirectory(dirname, FN_REFLEN) || + add_cfg_dir(configuration_dirs, dirname)) + goto error; + + if (add_cfg_dir(configuration_dirs, "C:")) + goto error; + + if (GetModuleFileName(NULL, dirname, FN_REFLEN)) { - snprintf(filename, length, - "%s%c.my.%s", env, FN_LIBCHAR, ini_exts[exts]); - if (!access(filename, R_OK)) - return filename; + PathRemoveFileSpec(dirname); + if (add_cfg_dir(configuration_dirs, dirname)) + goto error; } +#else + /* on *nix platforms configuration files are stored in + 1. SYSCONFDIR (if build happens inside server package, or + -DDEFAULT_SYSCONFDIR was specified + 2. /etc + 3. /etc/mysql + */ +#ifdef DEFAULT_SYSCONFDIR + if (add_cfg_dir(configuration_dirs, DEFAULT_SYSCONFDIR)) + goto error; +#else + if (add_cfg_dir(configuration_dirs, "/etc")) + goto error; + if (add_cfg_dir(configuration_dirs, "/etc/mysql")) + goto error; +#endif +#endif +/* This differs from https://mariadb.com/kb/en/mariadb/configuring-mariadb-with-mycnf/ where MYSQL_HOME is not specified for Windows */ + if ((env= getenv("MYSQL_HOME")) && + add_cfg_dir(configuration_dirs, env)) + goto error; +#ifndef _WIN32 + if ((env= getenv("HOME")) && + add_cfg_dir(configuration_dirs, env)) + goto error; +#endif +end: + return configuration_dirs; +error: return NULL; } -my_bool _mariadb_read_options(MYSQL *mysql, const char *config_file, - const char *group) +extern my_bool _mariadb_set_conf_option(MYSQL *mysql, const char *config_option, const char *config_value); + +static my_bool is_group(char *ptr, const char **groups) { + while (*groups) + { + if (!strcmp(ptr, *groups)) + return 1; + groups++; + } + return 0; +} + +static my_bool _mariadb_read_options_from_file(MYSQL *mysql, + const char *config_file, + const char *group) +{ + uint line=0; + my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0; char buff[4096],*ptr,*end,*value, *key= 0, *optval; MA_FILE *file= NULL; - char *filename; - uint line=0; my_bool rc= 1; - my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0; + const char *groups[5]= {"client", + "client-server", + "client-mariadb", + group, + NULL}; my_bool (*set_option)(MYSQL *mysql, const char *config_option, const char *config_value); + /* if a plugin registered a hook we will call this hook, otherwise * default (_mariadb_set_conf_option) will be called */ if (mysql->options.extension && mysql->options.extension->set_option) @@ -99,18 +166,7 @@ my_bool _mariadb_read_options(MYSQL *mysql, const char *config_file, else set_option= _mariadb_set_conf_option; - if (config_file) - filename= strdup(config_file); - else - { - filename= (char *)malloc(FN_REFLEN + 10); - if (!_mariadb_get_default_file(filename, FN_REFLEN + 10)) - { - goto err; - } - } - - if (!(file = ma_open(filename, "r", NULL))) + if (!(file = ma_open(config_file, "r", NULL))) goto err; while (ma_gets(buff,sizeof(buff)-1,file)) @@ -137,7 +193,7 @@ my_bool _mariadb_read_options(MYSQL *mysql, const char *config_file, } for ( ; isspace(end[-1]) ; end--) ; /* Remove end space */ end[0]=0; - read_values= test(strcmp(ptr, group) == 0); + read_values= is_group(ptr, groups); continue; } if (!found_group) @@ -224,13 +280,50 @@ my_bool _mariadb_read_options(MYSQL *mysql, const char *config_file, key= optval= 0; } } + if (file) + ma_close(file); rc= 0; err: - free(filename); - if (file) - ma_close(file); return rc; } +my_bool _mariadb_read_options(MYSQL *mysql, + const char *config_file, + const char *group) +{ + int i= 0, + exts, + errors= 0; + char filename[FN_REFLEN + 1]; + char *env; + + if (config_file) + return _mariadb_read_options_from_file(mysql, config_file, group); + + for (i=0; i < MAX_CONFIG_DIRS && configuration_dirs[i]; i++) + { + for (exts= 0; exts < 2; exts++) + { + snprintf(filename, FN_REFLEN, + "%s%cmy.%s", configuration_dirs[i], FN_LIBCHAR, ini_exts[exts]); + if (!access(filename, R_OK)) + errors+= _mariadb_read_options_from_file(mysql, filename, group); + } + } +#ifndef _WIN32 + /* special case: .my.cnf in Home directory */ + if ((env= getenv("HOME"))) + { + for (exts= 0; exts < 2; exts++) + { + snprintf(filename, FN_REFLEN, + "%s%c.my.%s", env, FN_LIBCHAR, ini_exts[exts]); + if (!access(filename, R_OK)) + errors+= _mariadb_read_options_from_file(mysql, filename, group); + } + } +#endif + return errors; +} diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index e52560cb..4e8401ba 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -68,6 +68,9 @@ #include #endif #include +#ifdef _WIN32 +#include "Shlwapi.h" +#endif #define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) #define MA_RPL_VERSION_HACK "5.5.5-" @@ -80,6 +83,8 @@ extern ulong net_buffer_length; /* net.c */ static MYSQL_PARAMETERS mariadb_internal_parameters= {&max_allowed_packet, &net_buffer_length, 0}; static my_bool mysql_client_init=0; static void mysql_close_options(MYSQL *mysql); +extern void release_configuration_dirs(); +extern char **get_default_configuration_dirs(); extern my_bool ma_init_done; extern my_bool mysql_ps_subsystem_initialized; extern my_bool mysql_handle_local_infile(MYSQL *mysql, const char *filename); @@ -1309,7 +1314,7 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, { char *hdr = mysql->options.extension->proxy_header; size_t len = mysql->options.extension->proxy_header_len; - if (ma_pvio_write(pvio, hdr, len) <= 0) + if (ma_pvio_write(pvio, (unsigned char *)hdr, len) <= 0) { ma_pvio_close(pvio); goto error; @@ -2655,6 +2660,26 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) OPT_SET_VALUE_STR(&mysql->options, my_cnf_file, (char *)arg1); break; case MYSQL_READ_DEFAULT_GROUP: + if (!arg1 || !((char *)arg1)[0]) + { +#if defined(__APPLE__) || defined(__FreeBSD__) + const char * appname = getprogname(); +#elif defined(_GNU_SOURCE) + const char * appname = program_invocation_short_name; +#elif defined(WIN32) + char appname[FN_REFLEN]= ""; + + if (GetModuleFileName(NULL, appname, FN_REFLEN)) + { + PathStripPath(appname); + PathRemoveExtension(appname); + } +#else + const char * appname = ""; +#endif + OPT_SET_VALUE_STR(&mysql->options, my_cnf_group, appname); + break; + } OPT_SET_VALUE_STR(&mysql->options, my_cnf_group, (char *)arg1); break; case MYSQL_SET_CHARSET_DIR: @@ -3452,6 +3477,7 @@ static void mysql_once_init() { ma_init(); /* Will init threads */ init_client_errs(); + get_default_configuration_dirs(); if (mysql_client_plugin_init()) { #ifdef _WIN32 @@ -3485,6 +3511,9 @@ static void mysql_once_init() } if (!mysql_ps_subsystem_initialized) mysql_init_ps_subsystem(); +#ifdef HAVE_TLS + ma_tls_start(0, 0); +#endif ignore_sigpipe(); mysql_client_init = 1; #ifdef _WIN32 @@ -3522,6 +3551,7 @@ void STDCALL mysql_server_end(void) if (!mysql_client_init) return; + release_configuration_dirs(); mysql_client_plugin_deinit(); list_free(pvio_callback, 0); diff --git a/unittest/libmariadb/connection.c b/unittest/libmariadb/connection.c index f7814ad6..1faf1d83 100644 --- a/unittest/libmariadb/connection.c +++ b/unittest/libmariadb/connection.c @@ -1105,7 +1105,152 @@ static int test_auth256(MYSQL *my) return OK; } +static int test_mdev13100(MYSQL *my __attribute__((unused))) +{ + MYSQL *mysql= mysql_init(NULL); + int rc; + FILE *fp; + + if (!(fp= fopen("./mdev13100.cnf", "w"))) + return FAIL; + + /* [client] group only */ + fprintf(fp, "[client]\n"); + fprintf(fp, "default-character-set=latin2\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./mdev13100.cnf"); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + FAIL_IF(strcmp("latin2", mysql_character_set_name(mysql)), "Expected charset latin2"); + mysql_close(mysql); + + /* value from client-mariadb group */ + mysql= mysql_init(NULL); + if (!(fp= fopen("./mdev13100.cnf", "w"))) + return FAIL; + + fprintf(fp, "[client]\n"); + fprintf(fp, "default-character-set=latin1\n"); + fprintf(fp, "[client-server]\n"); + fprintf(fp, "default-character-set=latin2\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./mdev13100.cnf"); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + FAIL_IF(strcmp("latin2", mysql_character_set_name(mysql)), "Expected charset latin2"); + mysql_close(mysql); + +/* values from client-mariadb group */ + mysql= mysql_init(NULL); + +if (!(fp= fopen("./mdev13100.cnf", "w"))) + return FAIL; + + fprintf(fp, "[client]\n"); + fprintf(fp, "default-character-set=latin1\n"); + fprintf(fp, "[client-server]\n"); + fprintf(fp, "default-character-set=utf8\n"); + fprintf(fp, "[client-mariadb]\n"); + fprintf(fp, "default-character-set=latin2\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./mdev13100.cnf"); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + FAIL_IF(strcmp("latin2", mysql_character_set_name(mysql)), "Expected charset latin2"); + mysql_close(mysql); + +/* values from mdev-13100 group */ + mysql= mysql_init(NULL); + if (!(fp= fopen("./mdev13100.cnf", "w"))) + return FAIL; + + fprintf(fp, "[client]\n"); + fprintf(fp, "default-character-set=latin1\n"); + fprintf(fp, "[client-server]\n"); + fprintf(fp, "default-character-set=latin1\n"); + fprintf(fp, "[client-mariadb]\n"); + fprintf(fp, "default-character-set=utf8\n"); + fprintf(fp, "[mdev13100]\n"); + fprintf(fp, "default-character-set=latin2\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./mdev13100.cnf"); + check_mysql_rc(rc, mysql); + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "mdev13100"); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + FAIL_IF(strcmp("latin2", mysql_character_set_name(mysql)), "Expected charset latin2"); + mysql_close(mysql); + +/* values from [programname] group */ + mysql= mysql_init(NULL); + if (!(fp= fopen("./mdev13100.cnf", "w"))) + return FAIL; + + fprintf(fp, "[client]\n"); + fprintf(fp, "default-character-set=latin1\n"); + fprintf(fp, "[client-server]\n"); + fprintf(fp, "default-character-set=latin1\n"); + fprintf(fp, "[client-mariadb]\n"); + fprintf(fp, "default-character-set=utf8\n"); + fprintf(fp, "[connection]\n"); + fprintf(fp, "default-character-set=latin2\n"); + + fclose(fp); + + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "./mdev13100.cnf"); + check_mysql_rc(rc, mysql); + rc= mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, ""); + check_mysql_rc(rc, mysql); + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + return FAIL; + } + FAIL_IF(strcmp("latin2", mysql_character_set_name(mysql)), "Expected charset latin2"); + mysql_close(mysql); + + remove("./mdev13100.cnf"); + + return OK; +} + + struct my_tests_st my_tests[] = { + {"test_mdev13100", test_mdev13100, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_auth256", test_auth256, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_reset", test_reset, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_unix_socket_close", test_unix_socket_close, TEST_CONNECTION_NONE, 0, NULL, NULL}, diff --git a/unittest/libmariadb/misc.c b/unittest/libmariadb/misc.c index 0b11917e..76d1914c 100644 --- a/unittest/libmariadb/misc.c +++ b/unittest/libmariadb/misc.c @@ -30,6 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc., /* Bug#28075 "COM_DEBUG crashes mysqld" */ +#ifdef _WIN32 +#define R_OK 4 +#endif static int test_bug28075(MYSQL *mysql) { @@ -1051,6 +1054,49 @@ static int test_remote2(MYSQL *my) } #endif +#ifndef _WIN32 +static int test_mdev12965(MYSQL *unused __attribute__((unused))) +{ + MYSQL *mysql; + my_bool reconnect = 0; + FILE *fp= NULL; + char *env= getenv("HOME"); + char cnf_file1[FN_REFLEN + 1], + cnf_file2[FN_REFLEN + 1]; + + snprintf(cnf_file1, FN_REFLEN, "%s%c.my.cnf", env, FN_LIBCHAR); + snprintf(cnf_file2, FN_REFLEN, "%s%cmy.cnf", env, FN_LIBCHAR); + + if (!access(cnf_file1, R_OK) || !access(cnf_file2, R_OK)) + { + diag("Skip this test, it would overwrite configuration files in your home directory"); + return SKIP; + } + + mysql= mysql_init(NULL); + fp= fopen(cnf_file1, "w"); + fprintf(fp, "[test]\ndefault-character-set=latin2"); + fclose(fp); + + fp= fopen(cnf_file2, "w"); + fprintf(fp, "[test]\nreconnect=1"); + fclose(fp); + + mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "test"); + my_test_connect(mysql, hostname, username, password, schema, + 0, socketname, 0), mysql_error(mysql); + + remove(cnf_file1); + remove(cnf_file2); + + FAIL_IF(strcmp(mysql_character_set_name(mysql), "latin2"), "expected charset latin2"); + mysql_get_optionv(mysql, MYSQL_OPT_RECONNECT, &reconnect); + FAIL_IF(reconnect != 1, "expected reconnect=1"); + mysql_close(mysql); + return OK; +} +#endif + static int test_get_info(MYSQL *mysql) { size_t sval; @@ -1267,6 +1313,9 @@ static int test_wl6797(MYSQL *mysql) } struct my_tests_st my_tests[] = { +#ifndef _WIN32 + {"test_mdev12965", test_mdev12965, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, +#endif {"test_wl6797", test_wl6797, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_server_status", test_server_status, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_read_timeout", test_read_timeout, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},