From 05659c872e8fc4238a9ac4af69ccd11575298699 Mon Sep 17 00:00:00 2001 From: Georg Richter Date: Fri, 2 Oct 2015 10:09:41 +0200 Subject: [PATCH] More plugin configuration changes (last commit was incomplete) --- CMakeLists.txt | 2 + cmake/plugins.cmake | 21 +- include/mysql.h | 6 +- include/mysql/client_plugin.h | 43 ++- include/mysql_com.h | 12 +- libmariadb/CMakeLists.txt | 11 +- libmariadb/client_plugin.c.in | 77 +++-- libmariadb/libmariadb.c | 62 +++- libmariadb/ma_cio.c | 2 +- libmariadb/ma_io.c | 2 +- libmariadb/secure/ma_schannel.c | 4 +- plugins/auth/CMakeLists.txt | 54 ++-- plugins/auth/dialog.c | 12 +- plugins/auth/mariadb_cleartext.c | 12 +- plugins/cio/CMakeLists.txt | 40 +-- plugins/cio/cio_npipe.c | 34 ++- plugins/cio/cio_shmem.c | 499 ++++++++++++++++++++----------- plugins/cio/cio_socket.c | 12 +- plugins/io/CMakeLists.txt | 26 +- plugins/io/remote_io.c | 10 +- plugins/trace/CMakeLists.txt | 26 +- plugins/trace/trace_example.c | 22 +- plugins/trace/trace_example.so | Bin 43254 -> 36957 bytes unittest/libmariadb/misc.c | 3 +- 24 files changed, 649 insertions(+), 343 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ede4184..d0e22ae1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ IF(WITH_RTC) SET(RTC_OPTIONS "/RTC1 /RTCc") ENDIF() +INCLUDE(${CMAKE_SOURCE_DIR}/cmake/plugins.cmake) + IF(MSVC) # Speedup system tests INCLUDE(${CMAKE_SOURCE_DIR}/cmake/WindowsCache.cmake) diff --git a/cmake/plugins.cmake b/cmake/plugins.cmake index 9d929bbc..dac7de73 100644 --- a/cmake/plugins.cmake +++ b/cmake/plugins.cmake @@ -15,17 +15,26 @@ ENDMACRO() MARK_AS_ADVANCED(PLUGINS) # CIO -REGISTER_PLUGIN("SOCKET" "${CMAKE_SOURCE_DIR}/plugins/builtin/cio_socket.c" "cio_socket_plugin" "STATIC" 0) +REGISTER_PLUGIN("SOCKET" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_socket.c" "cio_socket_plugin" "STATIC" 0) IF(WIN32) REGISTER_PLUGIN("NPIPE" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_npipe.c" "cio_npipe_plugin" "DYNAMIC" 1) REGISTER_PLUGIN("SHMEM" "${CMAKE_SOURCE_DIR}/plugins/cio/cio_shmem.c" "cio_shmem_plugin" "DYNAMIC" 1) ENDIF() # AUTHENTICATION -REGISTER_PLUGIN("AUTH_NATIVE" "${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c" "native_password_client_plugin" "STATIC" 0) -REGISTER_PLUGIN("AUTH_OLDPASSWORD" "${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c" "old_password_client_plugin" "STATIC" "0") -REGISTER_PLUGIN("AUTH_DIALOG" "${CMAKE_SOURCE_DIR}/plugins/auth/dialog.c" "" "DYNAMIC" 0) -REGISTER_PLUGIN("AUTH_CLEARTEXT" "${CMAKE_SOURCE_DIR}/plugins/auth/mariadb_clear_text.c" "" "DYNAMIC" 0) +REGISTER_PLUGIN("AUTH_NATIVE" "${CMAKE_SOURCE_DIR}/plugins/auth/my_auth.c" "native_password_client_plugin" "STATIC" 0) +REGISTER_PLUGIN("AUTH_OLDPASSWORD" "${CMAKE_SOURCE_DIR}/plugins/auth/my_auth.c" "old_password_client_plugin" "STATIC" 0) +REGISTER_PLUGIN("AUTH_DIALOG" "${CMAKE_SOURCE_DIR}/plugins/auth/dialog.c" "auth_dialog_plugin" "DYNAMIC" 1) +REGISTER_PLUGIN("AUTH_CLEARTEXT" "${CMAKE_SOURCE_DIR}/plugins/auth/mariadb_clear_text.c" "auth_cleartext_plugin" "DYNAMIC" 1) + +#Remote_IO +REGISTER_PLUGIN("REMOTEIO" "${CMAKE_SOURCE_DIR}/plugins/io/remote_io.c" "remote_io_plugin" "DYNAMIC" 1) + +#Trace +REGISTER_PLUGIN("TRACE_EXAMPLE" "${CMAKE_SOURCE_DIR}/plugins/trace/trace_example.c" "trace_example_plugin" "DYNAMIC" 1) + +#Connection +REGISTER_PLUGIN("REPLICATION" "${CMAKE_SOURCE_DIR}/plugins/connection/replication.c" "connection_replication_plugin" "STATIC" 1) # Allow registration of additional plugins IF(PLUGIN_CONF_FILE) @@ -50,7 +59,7 @@ FOREACH(PLUGIN ${PLUGINS}) ENDFOREACH() # since some files contain multiple plugins, remove duplicates from source files -LIST(REMOvE_DUPLICATES LIBMARIADB_SOURCES) +LIST(REMOVE_DUPLICATES LIBMARIADB_SOURCES) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/libmariadb/client_plugin.c.in ${CMAKE_BINARY_DIR}/libmariadb/client_plugin.c) diff --git a/include/mysql.h b/include/mysql.h index e2a10e69..c7b204ab 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -212,7 +212,8 @@ extern unsigned int mariadb_deinitialize_ssl; /* MariaDB Connector/C specific */ MYSQL_DATABASE_DRIVER=7000, MARIADB_OPT_SSL_FP, /* single finger print for server certificate verification */ - MARIADB_OPT_SSL_FP_LIST /* finger print white list for server certificate verification */ + MARIADB_OPT_SSL_FP_LIST, /* finger print white list for server certificate verification */ + MARIADB_OPT_CONNECTION_READ_ONLY }; enum mysql_status { MYSQL_STATUS_READY, @@ -231,8 +232,6 @@ extern unsigned int mariadb_deinitialize_ssl; MYSQL_PROTOCOL_PIPE, MYSQL_PROTOCOL_MEMORY }; -struct st_mysql_options_extension; - struct st_mysql_options { unsigned int connect_timeout, read_timeout, write_timeout; unsigned int port, protocol; @@ -627,6 +626,7 @@ struct st_mysql_methods { int (*db_stmt_fetch)(MYSQL_STMT *stmt, unsigned char **row); int (*db_stmt_fetch_to_bind)(MYSQL_STMT *stmt, unsigned char *row); void (*db_stmt_flush_unbuffered)(MYSQL_STMT *stmt); + void (*set_error)(MYSQL *mysql, unsigned int error_nr, const char *sqlstate, const char *format, ...); }; /* synonyms/aliases functions */ diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index 0f4059a1..76feb01e 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -31,25 +31,31 @@ #include #endif + #ifndef PLUGINDIR #define PLUGINDIR "lib/plugin" #endif /* known plugin types */ -#define MYSQL_CLIENT_GENERIC_PLUGIN 0 -#define MYSQL_CLIENT_CIO_PLUGIN 1 /* communication IO */ +#define MYSQL_CLIENT_PLUGIN_RESERVED 0 +#define MYSQL_CLIENT_PLUGIN_RESERVED2 1 #define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2 /* authentication */ -#define MYSQL_CLIENT_TRACE_PLUGIN 3 /* cio trace */ -#define MYSQL_CLIENT_REMOTEIO_PLUGIN 4 #define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0100 -#define MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION 0x0100 -#define MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION 0x0100 -#define MYSQL_CLIENT_SSL_INTERFACE_VERSION 0x0100 -#define MYSQL_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION 0x01000 -#define MYSQL_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION 0x0100 +#define MYSQL_CLIENT_MAX_PLUGINS 3 -#define MYSQL_CLIENT_MAX_PLUGINS 5 +/* Connector/C specific plugin types */ +#define MARIADB_CLIENT_REMOTEIO_PLUGIN 100 /* communication IO */ +#define MARIADB_CLIENT_CIO_PLUGIN 101 +#define MARIADB_CLIENT_TRACE_PLUGIN 102 +#define MARIADB_CLIENT_CONNECTION_PLUGIN 103 + +#define MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION 0x0100 +#define MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION 0x0100 +#define MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION 0x0100 +#define MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION 0x0100 + +#define MARIADB_CLIENT_MAX_PLUGINS 4 #define mysql_declare_client_plugin(X) \ struct st_mysql_client_plugin_ ## X \ @@ -76,26 +82,31 @@ struct st_mysql_client_plugin struct st_mysql; -/********* database api plugin specific declarations **********/ -struct st_mariadb_client_plugin_DB +/********* connection handler plugin specific declarations **********/ +typedef struct st_ma_connection_plugin { MYSQL_CLIENT_PLUGIN_HEADER /* functions */ - struct st_mysql_methods *methods; -} MARIADB_DB_PLUGIN; + MYSQL *(*connect)(MYSQL *mysql, const char *host, const char *user, const char *passwd, + const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag); + void (*close)(MYSQL *mysql); + int (*options)(MYSQL *mysql, enum mysql_option, void *arg); + int (*set_connection)(MYSQL *mysql,enum enum_server_command command, const char *arg, + size_t length, my_bool skipp_check, void *opt_arg); +} MARIADB_CONNECTION_PLUGIN; #define MARIADB_DB_DRIVER(a) ((a)->ext_db) /******************* Communication IO plugin *****************/ #include +#ifdef HAVE_SSL #include +#endif typedef struct st_mariadb_client_plugin_CIO { MYSQL_CLIENT_PLUGIN_HEADER struct st_ma_cio_methods *methods; - struct st_ma_cio_ssl_methods *ssl_methods; - void *compress_methods; } MARIADB_CIO_PLUGIN; /******** authentication plugin specific declarations *********/ diff --git a/include/mysql_com.h b/include/mysql_com.h index 2974cc1b..77394eaa 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -227,6 +227,14 @@ typedef struct st_ma_cio MARIADB_CIO; #define MAX_INT_WIDTH 10 #define MAX_BIGINT_WIDTH 20 +struct st_ma_connection_plugin; + +typedef struct st_connection_handler +{ + struct st_ma_connection_plugin *plugin; + void *data; + my_bool free_data; +} MA_CONNECTION_HANDLER; typedef struct st_net { MARIADB_CIO *cio; @@ -245,11 +253,11 @@ typedef struct st_net { my_bool unused_1, unused_2; my_bool compress; my_bool unused_3; - unsigned char *unused_4; + MA_CONNECTION_HANDLER *conn_hdlr; unsigned int last_errno; unsigned char error; + my_bool unused_4; my_bool unused_5; - my_bool unused_6; char last_error[MYSQL_ERRMSG_SIZE]; char sqlstate[SQLSTATE_LENGTH+1]; diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index a340f163..520a1d9a 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -12,7 +12,6 @@ SET(EXPORT_SYMBOLS ma_cio_register_callback mariadb_connection mariadb_convert_string - mariadb_deinitialize_ssl mariadb_dyncol_check mariadb_dyncol_column_cmp_named mariadb_dyncol_column_count @@ -221,6 +220,9 @@ SET(EXPORT_SYMBOLS mysql_thread_safe mysql_use_result mysql_warning_count) +IF(WITH_SSL) + SET(EXPORTS ${EXPORTS} mariadb_deinitialize_ssl) +ENDIF() IF(WIN32) SET(EXPORT_CONTENT "EXPORTS\n") @@ -240,7 +242,7 @@ ENDIF() FILE(WRITE ${EXPORT_FILE} ${EXPORT_CONTENT}) -SET(LIBMARIADB_SOURCES +SET(LIBMARIADB_SOURCES ${LIBMARIADB_SOURCES} array.c ma_dyncol.c bchange.c @@ -321,14 +323,11 @@ sha1.c my_stmt.c my_loaddata.c my_stmt_codec.c -client_plugin.c +${CMAKE_BINARY_DIR}/libmariadb/client_plugin.c ma_io.c -${CMAKE_SOURCE_DIR}/plugins/builtin/my_auth.c -${CMAKE_SOURCE_DIR}/plugins/builtin/cio_socket.c ${SSL_SOURCES} ) -MESSAGE(STATUS "${LIBMARIADB_SOURCES}") IF(WIN32) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/win-iconv) diff --git a/libmariadb/client_plugin.c.in b/libmariadb/client_plugin.c.in index c0d594bb..5b9cadcb 100644 --- a/libmariadb/client_plugin.c.in +++ b/libmariadb/client_plugin.c.in @@ -58,13 +58,13 @@ static MEM_ROOT mem_root; #define plugin_declarations_sym "_mysql_client_plugin_declaration_" -static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= -{ - MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION, /* these two are taken by Connector/C */ - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, /* these two are taken by Connector/C */ - MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, - MYSQL_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION, - MYSQL_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION +static uint valid_plugins[][2]= { + {MYSQL_CLIENT_AUTHENTICATION_PLUGIN, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION}, + {MARIADB_CLIENT_CIO_PLUGIN, MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION}, + {MARIADB_CLIENT_TRACE_PLUGIN, MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION}, + {MARIADB_CLIENT_REMOTEIO_PLUGIN, MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION}, + {MARIADB_CLIENT_CONNECTION_PLUGIN, MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION}, + {0, 0} }; /* @@ -75,21 +75,18 @@ static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= there. The main purpose of a mutex is to prevent two threads from loading the same plugin twice in parallel. */ -struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS]; + + +struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS + MARIADB_CLIENT_MAX_PLUGINS]; #ifdef THREAD static pthread_mutex_t LOCK_load_client_plugin; #endif -extern struct st_mysql_client_plugin old_password_client_plugin; -extern struct st_mysql_client_plugin native_password_client_plugin; - -extern MARIADB_CIO_PLUGIN cio_socket_plugin; +@EXTERNAL_PLUGINS@ struct st_mysql_client_plugin *mysql_client_builtins[]= { - (struct st_mysql_client_plugin *)&old_password_client_plugin, - (struct st_mysql_client_plugin *)&native_password_client_plugin, - (struct st_mysql_client_plugin *)&cio_socket_plugin, + @BUILTIN_PLUGINS@ 0 }; @@ -105,6 +102,25 @@ static int is_not_initialized(MYSQL *mysql, const char *name) return 1; } +static int get_plugin_nr(int type) +{ + uint i= 0; + for(; valid_plugins[i][1]; i++) + if (valid_plugins[i][0]== type) + return i; + return -1; +} + +static my_bool check_plugin_version(struct st_mysql_client_plugin *plugin, int version, char *errmsg) +{ + if (plugin->interface_version < version || + (plugin->interface_version >> 8) > (version >> 8)) + { + errmsg= "Incompatible client plugin interface"; + return 1; + } + return 0; +} /** finds a plugin in the list @@ -116,20 +132,20 @@ static int is_not_initialized(MYSQL *mysql, const char *name) @retval a pointer to a found plugin or 0 */ - static struct st_mysql_client_plugin *find_plugin(const char *name, int type) { struct st_client_plugin_int *p; + int plugin_nr= get_plugin_nr(type); DBUG_ASSERT(initialized); - DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS); - if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) + DBUG_ASSERT(plugin_nr != -1); + if (plugin_nr == -1) return 0; if (!name) - return plugin_list[type]->plugin; + return plugin_list[plugin_nr]->plugin; - for (p= plugin_list[type]; p; p= p->next) + for (p= plugin_list[plugin_nr]; p; p= p->next) { if (strcmp(p->plugin->name, name) == 0) return p->plugin; @@ -137,6 +153,7 @@ static struct st_mysql_client_plugin *find_plugin(const char *name, int type) return NULL; } + /** verifies the plugin and adds it to the list @@ -154,28 +171,23 @@ static struct st_mysql_client_plugin * add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, int argc, va_list args) { - const char *errmsg; + char *errmsg; struct st_client_plugin_int plugin_int, *p; char errbuf[1024]; + int plugin_nr; DBUG_ASSERT(initialized); plugin_int.plugin= plugin; plugin_int.dlhandle= dlhandle; - if (plugin->type >= MYSQL_CLIENT_MAX_PLUGINS) + if ((plugin_nr= get_plugin_nr(plugin->type)) == -1) { errmsg= "Unknown client plugin type"; goto err1; } - - if (plugin->interface_version < plugin_version[plugin->type] || - (plugin->interface_version >> 8) > - (plugin_version[plugin->type] >> 8)) - { - errmsg= "Incompatible client plugin interface"; + if (check_plugin_version(plugin, valid_plugins[plugin_nr][1], errmsg)) goto err1; - } /* Call the plugin initialization function, if any */ if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args)) @@ -197,8 +209,8 @@ add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, safe_mutex_assert_owner(&LOCK_load_client_plugin); #endif - p->next= plugin_list[plugin->type]; - plugin_list[plugin->type]= p; + p->next= plugin_list[plugin_nr]; + plugin_list[plugin_nr]= p; return plugin; @@ -460,11 +472,12 @@ struct st_mysql_client_plugin * STDCALL mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) { struct st_mysql_client_plugin *p; + int plugin_nr= find_plugin(name, type); if (is_not_initialized(mysql, name)) return NULL; - if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS) + if (plugin_nr == -1) { my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, "invalid type"); diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index bbc03e18..c3e839b7 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -67,6 +67,7 @@ #endif #include #include +#include #define ASYNC_CONTEXT_DEFAULT_STACK_SIZE (4096*15) @@ -342,7 +343,6 @@ void free_rows(MYSQL_DATA *cur) } } - int mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, size_t length, my_bool skipp_check, void *opt_arg) @@ -354,6 +354,13 @@ mthd_my_send_cmd(MYSQL *mysql,enum enum_server_command command, const char *arg, DBUG_PRINT("info", ("server_command: %d packet_size: %u", command, length)); + if (mysql->net.conn_hdlr && mysql->net.conn_hdlr->data) + { + result= mysql->net.conn_hdlr->plugin->set_connection(mysql, command, arg, length, skipp_check, opt_arg); + if (result== -1) + DBUG_RETURN(result); + } + if (mysql->net.cio == 0) { /* Do reconnect if possible */ if (mysql_reconnect(mysql)) @@ -552,6 +559,9 @@ static void end_server(MYSQL *mysql) { DBUG_ENTER("end_server"); + + /* if net->error 2 and reconnect is activated, we need to inforn + connection handler */ if (mysql->net.cio != 0) { ma_cio_close(mysql->net.cio); @@ -1300,6 +1310,35 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, db ? db : "(Null)", user ? user : "(Null)")); + if (!mysql->methods) + mysql->methods= &MARIADB_DEFAULT_METHODS; + /* special case: + * If hostname contains "://", e.g. "repl://localhost", we need to process connection + * by connection plugin + */ + if (host && (end= strstr(host, "://"))) + { + MARIADB_CONNECTION_PLUGIN *plugin; + char plugin_name[64]; + + bzero(plugin_name, 64); + strncpy(plugin_name, host, MIN(end - host, 63)); + end+= 3; + if (!(plugin= (MARIADB_CONNECTION_PLUGIN *)mysql_client_find_plugin(mysql, plugin_name, MARIADB_CLIENT_CONNECTION_PLUGIN))) + DBUG_RETURN(NULL); + + if (!(mysql->net.conn_hdlr= (MA_CONNECTION_HANDLER *)my_malloc(sizeof(MA_CONNECTION_HANDLER), MYF(MY_ZEROFILL)))) + { + SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0); + DBUG_RETURN(NULL); + } + + mysql->net.conn_hdlr->plugin= plugin; + + if (plugin->connect) + return plugin->connect(mysql, end, user, passwd, db, port, unix_socket, client_flag); + } + ma_set_connect_attrs(mysql); if (net->cio) /* check if we are already connected */ @@ -1871,8 +1910,6 @@ static void mysql_close_memory(MYSQL *mysql) mysql->host_info= mysql->server_version=mysql->user=mysql->passwd=mysql->db=0; } - - void my_set_error(MYSQL *mysql, unsigned int error_nr, const char *sqlstate, @@ -1908,12 +1945,20 @@ void mysql_close_slow_part(MYSQL *mysql) void STDCALL mysql_close(MYSQL *mysql) { - MYSQL_STMT *stmt; + MYSQL_STMT *stmt; DBUG_ENTER("mysql_close"); if (mysql) /* Some simple safety */ { LIST *li_stmt= mysql->stmts; + if (mysql->net.conn_hdlr && mysql->net.conn_hdlr->data) + { + void *p= (void *)mysql->net.conn_hdlr; + mysql->net.conn_hdlr->plugin->close(mysql); + my_free(p); + DBUG_VOID_RETURN; + } + if (mysql->methods) mysql->methods->db_close(mysql); @@ -1931,6 +1976,10 @@ mysql_close(MYSQL *mysql) /* Clear pointers for better safety */ bzero((char*) &mysql->options,sizeof(mysql->options)); + + if (mysql->extension) + my_free(mysql->extension); + mysql->net.cio= 0; if (mysql->free_me) my_free(mysql); @@ -2797,6 +2846,11 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) case MARIADB_OPT_SSL_FP_LIST: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_fp_list, (char *)arg1); break; + case MARIADB_OPT_CONNECTION_READ_ONLY: + if (mysql->net.conn_hdlr) + DBUG_RETURN(mysql->net.conn_hdlr->plugin->options(mysql, MARIADB_OPT_CONNECTION_READ_ONLY, arg1)); + else + return -1; default: va_end(ap); DBUG_RETURN(-1); diff --git a/libmariadb/ma_cio.c b/libmariadb/ma_cio.c index fabb1417..e9244fca 100644 --- a/libmariadb/ma_cio.c +++ b/libmariadb/ma_cio.c @@ -90,7 +90,7 @@ MARIADB_CIO *ma_cio_init(MA_CIO_CINFO *cinfo) if (!(cio_plugin= (MARIADB_CIO_PLUGIN *) mysql_client_find_plugin(cinfo->mysql, cio_plugins[type], - MYSQL_CLIENT_CIO_PLUGIN))) + MARIADB_CLIENT_CIO_PLUGIN))) { /* error handling */ return NULL; diff --git a/libmariadb/ma_io.c b/libmariadb/ma_io.c index 15277818..4d4e5a6e 100644 --- a/libmariadb/ma_io.c +++ b/libmariadb/ma_io.c @@ -124,7 +124,7 @@ remote: { MYSQL mysql; if (rio_plugin ||(rio_plugin= (struct st_mysql_client_plugin_REMOTEIO *) - mysql_client_find_plugin(&mysql, NULL, MYSQL_CLIENT_REMOTEIO_PLUGIN))) + mysql_client_find_plugin(&mysql, NULL, MARIADB_CLIENT_REMOTEIO_PLUGIN))) return rio_plugin->methods->open(location, mode); return NULL; } diff --git a/libmariadb/secure/ma_schannel.c b/libmariadb/secure/ma_schannel.c index d27d895f..fe363283 100644 --- a/libmariadb/secure/ma_schannel.c +++ b/libmariadb/secure/ma_schannel.c @@ -718,12 +718,12 @@ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_CIO *cio, { /* server closed connection */ // todo: error - printf("Server closed connection\n"); return NULL; } if (dwBytesRead < 0) { - printf("Socket error\n"); + /* socket error */ + // todo: error return NULL; } dwOffset+= dwBytesRead; diff --git a/plugins/auth/CMakeLists.txt b/plugins/auth/CMakeLists.txt index 9531ea64..c30c9873 100644 --- a/plugins/auth/CMakeLists.txt +++ b/plugins/auth/CMakeLists.txt @@ -1,32 +1,36 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) # Dialog plugin -SET(DIALOG_SOURCES dialog.c ${CMAKE_SOURCE_DIR}/libmariadb/get_password.c) -IF(WIN32) - SET(DIALOG_SOURCES ${DIALOG_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) -ENDIF() -ADD_LIBRARY(dialog SHARED ${DIALOG_SOURCES}) -SET_TARGET_PROPERTIES(dialog PROPERTIES PREFIX "") +IF(AUTH_DIALOG_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_DIALOG_DYNAMIC=1) + SET(DIALOG_SOURCES dialog.c ${CMAKE_SOURCE_DIR}/libmariadb/get_password.c) + IF(WIN32) + SET(DIALOG_SOURCES ${DIALOG_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) + ENDIF() + ADD_LIBRARY(dialog SHARED ${DIALOG_SOURCES}) + SET_TARGET_PROPERTIES(dialog PROPERTIES PREFIX "") + + INSTALL(TARGETS + dialog + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") +ENDIF() -INSTALL(TARGETS - dialog - RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" - LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" - ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") # Cleartext -# Dialog plugin -SET(CTEXT_SOURCES mariadb_cleartext.c) -IF(WIN32) - SET(CTEXT_SOURCES ${CTEXT_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) +IF(AUTH_CLEARTEXT_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_CLEARTEXT_DYNAMIC=1) + SET(CTEXT_SOURCES mariadb_cleartext.c) + IF(WIN32) + SET(CTEXT_SOURCES ${CTEXT_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) + ENDIF() + ADD_LIBRARY(mysql_clear_password SHARED ${CTEXT_SOURCES}) + SET_TARGET_PROPERTIES(mysql_clear_password PROPERTIES PREFIX "") + + INSTALL(TARGETS + mysql_clear_password + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") ENDIF() -ADD_LIBRARY(mysql_clear_password SHARED ${CTEXT_SOURCES}) -SET_TARGET_PROPERTIES(mysql_clear_password PROPERTIES PREFIX "") - -INSTALL(TARGETS - mysql_clear_password - RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" - LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" - ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") - -# Cleartext diff --git a/plugins/auth/dialog.c b/plugins/auth/dialog.c index bf07984e..0424e01f 100644 --- a/plugins/auth/dialog.c +++ b/plugins/auth/dialog.c @@ -41,15 +41,23 @@ static int auth_dialog_init(char *unused1, mysql_authentication_dialog_ask_t auth_dialog_func; -mysql_declare_client_plugin(AUTHENTICATION) +#ifndef HAVE_DIALOG_DYNAMIC +struct st_mysql_client_plugin_AUTHENTICATION auth_dialog_plugin= +#else +struct st_mysql_client_plugin_AUTHENTICATION _mysql_client_plugin_declaration_ = +#endif +{ + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, "dialog", "Sergei Golubchik, Georg Richter", "Dialog Client Authentication Plugin", {0,1,0}, + "LGPL", auth_dialog_init, NULL, auth_dialog_open -mysql_end_client_plugin; +}; /* {{{ static char *auth_dialog_native_prompt */ diff --git a/plugins/auth/mariadb_cleartext.c b/plugins/auth/mariadb_cleartext.c index f9bce576..a1dbb485 100644 --- a/plugins/auth/mariadb_cleartext.c +++ b/plugins/auth/mariadb_cleartext.c @@ -53,14 +53,22 @@ static int clear_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) } /* }}} */ -mysql_declare_client_plugin(AUTHENTICATION) +#ifndef HAVE_DIALOG_DYNAMIC +struct st_mysql_client_plugin_AUTHENTICATION auth_cleartext_plugin= +#else +struct st_mysql_client_plugin_AUTHENTICATION _mysql_client_plugin_declaration_ = +#endif +{ + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, "mysql_clear_password", "Georg Richter", "MariaDB clear password authentication plugin", {0,1,0}, + "LGPL", NULL, NULL, clear_password_auth_client -mysql_end_client_plugin; +}; diff --git a/plugins/cio/CMakeLists.txt b/plugins/cio/CMakeLists.txt index febc3d55..dc0e6616 100644 --- a/plugins/cio/CMakeLists.txt +++ b/plugins/cio/CMakeLists.txt @@ -2,27 +2,31 @@ IF(WIN32) SET(EXPORT_FILE "cio_plugin.def") ENDIF() -IF(GNUTLS_FOUND) - IF (NOT ${DEFAULT_SSL} STREQUAL "GNUTLS") - SET(CMAKE_SHARED_LIBRARY_PREFIX "") - ADD_LIBRARY(cio_gnutls SHARED cio_gnutls.c ${EXPORT_FILE}) - TARGET_LINK_LIBRARIES(cio_gnutls ${GNUTLS_LIBRARIES}) - ENDIF() -ENDIF() +SET(CMAKE_SHARED_LIBRARY_PREFIX "") -IF(OPENSSL_FOUND) - IF (NOT ${DEFAULT_SSL} STREQUAL "OPENSSL") - SET(CMAKE_SHARED_LIBRARY_PREFIX "") - SET(source_files cio_openssl.c ${EXPORT_FILE}) - ADD_LIBRARY(cio_openssl SHARED ${source_files}) - TARGET_LINK_LIBRARIES(cio_openssl ${OPENSSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) - ENDIF() +IF(SOCKET_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_SOCKET_DYNAMIC=1) + ADD_LIBRARY(cio_socket SHARED cio_socket.c ${EXPORT_FILE}) + SET(INSTALL_LIBS cio_socket) ENDIF() IF(WIN32) - SET(CMAKE_SHARED_LIBRARY_PREFIX "") - IF (NOT ${DEFAULT_SSL} STREQUAL "SCHANNEL") - ADD_LIBRARY(cio_schannel SHARED cio_schannel.c ${EXPORT_FILE}) + IF(NPIPE_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_NPIPE_DYNAMIC=1) + ADD_LIBRARY(cio_npipe SHARED cio_npipe.c ${EXPORT_FILE}) + SET(INSTALL_LIBS ${INSTALL_LIBS} cio_npipe) + ENDIF() + IF(SHMEM_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_SHMEM_DYNAMIC=1) + ADD_LIBRARY(cio_shmem SHARED cio_shmem.c ${EXPORT_FILE}) + SET(INSTALL_LIBS ${INSTALL_LIBS} cio_shmem) ENDIF() - ADD_LIBRARY(cio_npipe SHARED cio_npipe.c ${EXPORT_FILE}) +ENDIF() + +IF(INSTALL_LIBS) + INSTALL(TARGETS + ${INSTALL_LIBS} + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") ENDIF() diff --git a/plugins/cio/cio_npipe.c b/plugins/cio/cio_npipe.c index e9ad6439..1a644290 100644 --- a/plugins/cio/cio_npipe.c +++ b/plugins/cio/cio_npipe.c @@ -1,3 +1,25 @@ +/************************************************************************************ + Copyright (C) 2015 MariaDB Corporation AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*************************************************************************************/ + /* MariaDB Communication IO (CIO) plugin for named pipe communication */ #ifdef _WIN32 @@ -10,13 +32,13 @@ #include #include -#ifndef HAVE_NPIPE_DEFAULT +#ifdef HAVE_NPIPE_DYNAMIC #define my_malloc(A, B) malloc((A)) #undef my_free #define my_free(A,B) free(((A))) #endif - +/* Function prototypes */ my_bool cio_npipe_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout); int cio_npipe_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type); size_t cio_npipe_read(MARIADB_CIO *cio, uchar *buffer, size_t length); @@ -45,10 +67,14 @@ struct st_ma_cio_methods cio_npipe_methods= { cio_npipe_is_blocking }; +#ifndef HAVE_NPIPE_DYNAMIC +MARIADB_CIO_PLUGIN cio_npipe_plugin = +#else MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = +#endif { - MYSQL_CLIENT_CIO_PLUGIN, - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + MARIADB_CLIENT_CIO_PLUGIN, + MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, "cio_npipe", "Georg Richter", "MariaDB communication IO plugin for named pipe communication", diff --git a/plugins/cio/cio_shmem.c b/plugins/cio/cio_shmem.c index ca282587..6c02ede0 100644 --- a/plugins/cio/cio_shmem.c +++ b/plugins/cio/cio_shmem.c @@ -1,9 +1,25 @@ -/* MariaDB Communication IO (CIO) plugin for shate memory communication - * - * During initialization MariaDB serve creates a named file mapping - * object, named : * - * - * */ +/************************************************************************************ + Copyright (C) 2015 MariaDB Corporation AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*************************************************************************************/ +/* MariaDB Communication IO (CIO) plugin for named pipe communication */ #ifdef _WIN32 @@ -15,51 +31,56 @@ #include #include -#ifndef HAVE_NPIPE_DEFAULT +#ifdef HAVE_SHMEM_DYNAMIC #define my_malloc(A, B) malloc((A)) #undef my_free #define my_free(A,B) free(((A))) #endif - +#define SHM_DEFAULT_NAME "MYSQL" +#define CIO_SHM_BUFFER_SIZE 16000 + 4 my_bool cio_shm_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout); int cio_shm_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type); -size_t cio_shm_read(MARIADB_CIO *cio, uchar *buffer, size_t length); +size_t cio_shm_read(MARIADB_CIO *cio, const uchar *buffer, size_t length); size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length); int cio_shm_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout); my_bool cio_shm_blocking(MARIADB_CIO *cio, my_bool value, my_bool *old_value); my_bool cio_shm_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo); my_bool cio_shm_close(MARIADB_CIO *cio); -int cio_shm_fast_send(MARIADB_CIO *cio); -int cio_shm_keepalive(MARIADB_CIO *cio); -my_socket cio_shm_get_socket(MARIADB_CIO *cio); -my_bool cio_shm_is_blocking(MARIADB_CIO *cio); + struct st_ma_cio_methods cio_shm_methods= { cio_shm_set_timeout, cio_shm_get_timeout, cio_shm_read, + NULL, cio_shm_write, + NULL, cio_shm_wait_io_or_timeout, cio_shm_blocking, cio_shm_connect, cio_shm_close, - cio_shm_fast_send, - cio_shm_keepalive, - cio_shm_get_socket, - cio_shm_is_blocking + NULL, + NULL, + NULL, + NULL, + NULL }; -MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = +#ifndef HAVE_SHMEM_DYNAMIC +MARIADB_CIO_PLUGIN cio_shmem_plugin= +#else +MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_= +#endif { - MYSQL_CLIENT_CIO_PLUGIN, - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, - "cio_shm", + MARIADB_CLIENT_CIO_PLUGIN, + MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + "cio_shmem", "Georg Richter", - "MariaDB communication IO plugin for named pipe communication", + "MariaDB communication IO plugin for Windows shared memory communication", {1, 0, 0}, - "LGPL", + "LGPPL", NULL, NULL, &cio_shm_methods, @@ -67,19 +88,34 @@ MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_ = NULL }; +enum enum_shm_events +{ + CIO_SHM_SERVER_WROTE= 0, + CIO_SHM_SERVER_READ, + CIO_SHM_CLIENT_WROTE, + CIO_SHM_CLIENT_READ, + CIO_SHM_CONNECTION_CLOSED +}; + +typedef struct { + HANDLE event[5]; + HANDLE file_map; + LPVOID *map; + char *read_pos; + size_t buffer_size; +} CIO_SHM; + +char *StrEvent[]= {"SERVER_WROTE", "SERVER_READ", "CLIENT_WROTE", "CLIENT_READ", "CONNECTION_CLOSED"}; + struct st_cio_shm { - HANDLE pipe; - OVERLAPPED overlapped; - size_t rw_size; - int fcntl_mode; - MYSQL *mysql; + char *shm_name; }; my_bool cio_shm_set_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type, int timeout) { if (!cio) return 1; - cio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1; + cio->timeout[type]= (timeout > 0) ? timeout * 1000 : INFINITE; return 0; } @@ -90,79 +126,99 @@ int cio_shm_get_timeout(MARIADB_CIO *cio, enum enum_cio_timeout type) return cio->timeout[type] / 1000; } -size_t cio_shm_read(MARIADB_CIO *cio, uchar *buffer, size_t length) +size_t cio_shm_read(MARIADB_CIO *cio, const uchar *buffer, size_t length) { - DWORD dwRead= 0; - size_t r= -1; - struct st_cio_shm *cpipe= NULL; - - if (!cio || !cio->data) + CIO_SHM *cio_shm= (CIO_SHM *)cio->data; + size_t copy_size= length; + HANDLE events[2]; + + if (!cio_shm) return -1; - cpipe= (struct st_cio_shm *)cio->data; - - if (ReadFile(cpipe->pipe, buffer, length, &dwRead, &cpipe->overlapped)) + /* we need to wait for write and close events */ + if (!cio_shm->buffer_size) { - r= (size_t)dwRead; - goto end; + events[0]= cio_shm->event[CIO_SHM_CONNECTION_CLOSED]; + events[1]= cio_shm->event[CIO_SHM_SERVER_WROTE]; + + switch(WaitForMultipleObjects(2, events, 0, cio->timeout[CIO_READ_TIMEOUT])) + { + case WAIT_OBJECT_0: /* server closed connection */ + SetLastError(ERROR_GRACEFUL_DISCONNECT); + return -1; + case WAIT_OBJECT_0 +1: /* server_wrote event */ + break; + case WAIT_TIMEOUT: + SetLastError(ETIMEDOUT); + default: + return -1; + } + /* server sent data */ + cio_shm->read_pos= cio_shm->map; + cio_shm->buffer_size= uint4korr(cio_shm->read_pos); + cio_shm->read_pos+= 4; } - if (GetLastError() == ERROR_IO_PENDING) - r= cio_shm_wait_io_or_timeout(cio, 1, 0); + + if (cio_shm->buffer_size < copy_size) + copy_size= cio_shm->buffer_size; - if (!r) - r= cpipe->rw_size; -end: - return r; + if (copy_size) + { + memcpy(buffer, cio_shm->read_pos, cio_shm->buffer_size); + cio_shm->read_pos+= copy_size; + cio_shm->buffer_size-= copy_size; + } + + /* we need to read again */ + if (!cio_shm->buffer_size) + if (!SetEvent(cio_shm->event[CIO_SHM_CLIENT_READ])) + return -1; + + return copy_size; } -size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length) +size_t cio_shm_write(MARIADB_CIO *cio, uchar *buffer, size_t length) { - DWORD dwWrite= 0; - size_t r= -1; - struct st_cio_shm *cpipe= NULL; - - if (!cio || !cio->data) + HANDLE events[2]; + CIO_SHM *cio_shm= (CIO_SHM *)cio->data; + size_t bytes_to_write= length; + uchar *buffer_pos= buffer; + + if (!cio_shm) return -1; - cpipe= (struct st_cio_shm *)cio->data; + events[0]= cio_shm->event[CIO_SHM_CONNECTION_CLOSED]; + events[1]= cio_shm->event[CIO_SHM_SERVER_READ]; - if (WriteFile(cpipe->pipe, buffer, length, &dwWrite, &cpipe->overlapped)) + while (bytes_to_write) { - r= (size_t)dwWrite; - goto end; + size_t pkt_length; + switch (WaitForMultipleObjects(2, events, 0, cio->timeout[CIO_WRITE_TIMEOUT])) { + case WAIT_OBJECT_0: /* connection closed */ + SetLastError(ERROR_GRACEFUL_DISCONNECT); + return -1; + case WAIT_OBJECT_0 + 1: /* server_read */ + break; + case WAIT_TIMEOUT: + SetLastError(ETIMEDOUT); + default: + return -1; + } + pkt_length= MIN(CIO_SHM_BUFFER_SIZE, length); + int4store(cio_shm->map, pkt_length); + memcpy((uchar *)cio_shm->map + 4, buffer_pos, length); + buffer_pos+= length; + bytes_to_write-= length; + + if (!SetEvent(cio_shm->event[CIO_SHM_CLIENT_WROTE])) + return -1; } - if (GetLastError() == ERROR_IO_PENDING) - r= cio_shm_wait_io_or_timeout(cio, 1, 0); - - if (!r) - r= cpipe->rw_size; -end: - return r; + return length; } + int cio_shm_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout) { - int r= -1; - DWORD status; - int save_error; - struct st_cio_shm *cpipe= NULL; - - cpipe= (struct st_cio_shm *)cio->data; - - if (!timeout) - timeout= (is_read) ? cio->timeout[CIO_READ_TIMEOUT] : cio->timeout[CIO_WRITE_TIMEOUT]; - - status= WaitForSingleObject(cpipe->overlapped.hEvent, timeout); - if (status == WAIT_OBJECT_0) - { - if (GetOverlappedResult(cpipe->pipe, &cpipe->overlapped, &cpipe->rw_size, FALSE)) - return 0; - } - /* other status codes are: WAIT_ABANDONED, WAIT_TIMEOUT and WAIT_FAILED */ - save_error= GetLastError(); - CancelIo(cpipe->pipe); - SetLastError(save_error); - return -1; } my_bool cio_shm_blocking(MARIADB_CIO *cio, my_bool block, my_bool *previous_mode) @@ -182,122 +238,206 @@ int cio_shm_fast_send(MARIADB_CIO *cio) /* not supported */ return 0; } + my_bool cio_shm_connect(MARIADB_CIO *cio, MA_CIO_CINFO *cinfo) { - struct st_cio_shm *cpipe= NULL; + char *base_memory_name; + char *prefixes[]= {"", "Global\\", NULL}; + char *shm_name, *shm_suffix, *shm_prefix; + uchar i= 0; + int len; + DWORD cid; + char connection_id[28]; + char *connection_id_str; + DWORD dwDesiredAccess= EVENT_MODIFY_STATE | SYNCHRONIZE; + HANDLE hdlConnectRequest= NULL, + hdlConnectRequestAnswer= NULL, + file_map= NULL; + LPVOID map= NULL; + CIO_SHM *cio_shm= (CIO_SHM*)LocalAlloc(LMEM_ZEROINIT, sizeof(CIO_SHM)); - if (!cio || !cinfo) - return 1; - - if (!(cpipe= (struct st_cio_shm *)my_malloc(sizeof(struct st_cio_shm), MYF(0)))) + if (!cio_shm) { CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, ""); - return 1; - } - bzero(cpipe, sizeof(struct st_cio_shm)); - cio->data= (void *)cpipe; - cpipe->pipe= INVALID_HANDLE_VALUE; - cio->mysql= cinfo->mysql; - cio->type= cinfo->type; - - if (cinfo->type == CIO_TYPE_NAMEDPIPE) - { - my_bool has_timedout= 0; - char szPipeName[MAX_PATH]; - DWORD dwMode; - - if ( ! cinfo->unix_socket || (cinfo->unix_socket)[0] == 0x00) - cinfo->unix_socket = MYSQL_NAMEDPIPE; - if (!cinfo->host || !strcmp(cinfo->host,LOCAL_HOST)) - cinfo->host=LOCAL_HOST_NAMEDPIPE; - - szPipeName[MAX_PATH - 1]= 0; - snprintf(szPipeName, MAX_PATH - 1, "\\\\%s\\pipe\\%s", cinfo->host, cinfo->unix_socket); - - while (1) - { - if ((cpipe->pipe = CreateFile(szPipeName, - GENERIC_READ | - GENERIC_WRITE, - 0, /* no sharing */ - NULL, /* default security attributes */ - OPEN_EXISTING, - 0, /* default attributes */ - NULL)) != INVALID_HANDLE_VALUE) - break; - - if (GetLastError() != ERROR_PIPE_BUSY) - { - cio->set_error(cio, CR_NAMEDPIPEOPEN_ERROR, SQLSTATE_UNKNOWN, 0, - cinfo->host, cinfo->unix_socket, GetLastError()); - goto end; - } - - if (has_timedout || !WaitNamedPipe(szPipeName, cio->timeout[CIO_CONNECT_TIMEOUT])) - { - cio->set_error(cio, CR_NAMEDPIPEWAIT_ERROR, SQLSTATE_UNKNOWN, 0, - cinfo->host, cinfo->unix_socket, GetLastError()); - goto end; - } - has_timedout= 1; - } - - dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; - if (!SetNamedPipeHandleState(cpipe->pipe, &dwMode, NULL, NULL)) - { - cio->set_error(cio, CR_NAMEDPIPESETSTATE_ERROR, SQLSTATE_UNKNOWN, 0, - cinfo->host, cinfo->unix_socket, (ulong) GetLastError()); - goto end; - } - - /* Register event handler for overlapped IO */ - if (!(cpipe->overlapped.hEvent= CreateEvent(NULL, FALSE, FALSE, NULL))) - { - cio->set_error(cio, CR_CREATE_EVENT_FAILED, SQLSTATE_UNKNOWN, 0, - GetLastError()); - goto end; - } return 0; } -end: - if (cpipe) + + /* MariaDB server constructs the event name as follows: + "Global\\base_memory_name" or + "\\base_memory_name" + */ + + + base_memory_name= (cinfo->mysql->options.shared_memory_base_name) ? + cinfo->mysql->options.shared_memory_base_name : SHM_DEFAULT_NAME; + + + if (!(shm_name= LocalAlloc(LMEM_ZEROINIT, strlen(base_memory_name) + 40))) { - if (cpipe->pipe != INVALID_HANDLE_VALUE) - CloseHandle(cpipe->pipe); - my_free((gptr)cpipe, MYF(0)); - cio->data= NULL; + CIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, ""); + goto error; + } + + /* iterate through prefixes */ + while (prefixes[i]) + { + len= sprintf(shm_name, "%s%s_", prefixes[i], base_memory_name); + shm_suffix= shm_name + len; + strcpy(shm_suffix, "CONNECT_REQUEST"); + if ((hdlConnectRequest= OpenEvent(dwDesiredAccess, 0, shm_name))) + { + /* save prefix to prevent further loop */ + shm_prefix= prefixes[i]; + break; + } + i++; + } + if (!hdlConnectRequest) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Opening CONNECT_REQUEST event failed", GetLastError()); + goto error; + } + + strcpy(shm_suffix, "CONNECT_ANSWER"); + if (!(hdlConnectRequestAnswer= OpenEvent(dwDesiredAccess, 0, shm_name))) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Opening CONNECT_ANSWER event failed", GetLastError()); + goto error; + } + + /* get connection id, so we can build the filename used for connection */ + strcpy(shm_suffix, "CONNECT_DATA"); + if (!(file_map= OpenFileMapping(FILE_MAP_WRITE, 0, shm_name))) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "OpenFileMapping failed", GetLastError()); + goto error; + } + + /* try to get first 4 bytes, which represents connection_id */ + if (!(map= MapViewOfFile(file_map, FILE_MAP_WRITE, 0, 0, sizeof(cid)))) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Reading connection_id failed", GetLastError()); + goto error; + } + + /* notify server */ + if (!SetEvent(hdlConnectRequest)) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Failed sending connection request", GetLastError()); + goto error; + } + + /* Wait for server answer */ + switch(WaitForSingleObject(hdlConnectRequestAnswer, cio->timeout[CIO_CONNECT_TIMEOUT])) { + case WAIT_ABANDONED: + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Mutex was not released in time", GetLastError()); + goto error; + break; + case WAIT_FAILED: + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Operation wait failed", GetLastError()); + goto error; + break; + case WAIT_TIMEOUT: + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Operation timed out", GetLastError()); + goto error; + break; + case WAIT_OBJECT_0: + break; + default: + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Wait for server failed", GetLastError()); + break; + } + + cid= uint4korr(map); + + len= sprintf(shm_name, "%s%s_%d_", shm_prefix, base_memory_name, cid); + shm_suffix= shm_name + len; + + strcpy(shm_suffix, "DATA"); + cio_shm->file_map= OpenFileMapping(FILE_MAP_WRITE, 0, shm_name); + if (cio_shm->file_map == NULL) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "OpenFileMapping failed", GetLastError()); + goto error; + } + if (!(cio_shm->map= MapViewOfFile(cio_shm->file_map, FILE_MAP_WRITE, 0, 0, CIO_SHM_BUFFER_SIZE))) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "MapViewOfFile failed", GetLastError()); + goto error; + } + + for (i=0; i < 5; i++) + { + strcpy(shm_suffix, StrEvent[i]); + if (!(cio_shm->event[i]= OpenEvent(dwDesiredAccess, 0, shm_name))) + { + CIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, unknown_sqlstate, 0, "Couldn't create event", GetLastError()); + goto error; + } + } + /* we will first read from server */ + SetEvent(cio_shm->event[CIO_SHM_SERVER_READ]); + +error: + if (hdlConnectRequest) + CloseHandle(hdlConnectRequest); + if (hdlConnectRequestAnswer) + CloseHandle(hdlConnectRequestAnswer); + if (shm_name) + LocalFree(shm_name); + if (map) + UnmapViewOfFile(map); + if (file_map) + CloseHandle(file_map); + if (cio_shm) + { + /* check if all events are set */ + if (cio_shm->event[4]) + { + cio->data= (void *)cio_shm; + cio->mysql= cinfo->mysql; + cio->type= cinfo->type; + cio_shm->read_pos= cio_shm->map; + cio->mysql->net.cio= cio; + return 0; + } + for (i=0;i < 5; i++) + if (cio_shm->event[i]) + CloseHandle(cio_shm->event[i]); + if (cio_shm->map) + UnmapViewOfFile(cio_shm->map); + if (cio_shm->file_map) + CloseHandle(cio_shm->file_map); + LocalFree(cio_shm); } return 1; + } my_bool cio_shm_close(MARIADB_CIO *cio) { - struct st_cio_shm *cpipe= NULL; - int r= 0; + CIO_SHM *cio_shm= (CIO_SHM *)cio->data; + int i; - if (!cio) + if (!cio_shm) return 1; - if (cio->data) - { - cpipe= (struct st_cio_shm *)cio->data; - CloseHandle(cpipe->overlapped.hEvent); - if (cpipe->pipe != INVALID_HANDLE_VALUE) - { - CloseHandle(cpipe->pipe); - cpipe->pipe= INVALID_HANDLE_VALUE; - } - my_free((gptr)cio->data, MYF(0)); - cio->data= NULL; - } - return r; + /* notify server */ + SetEvent(cio_shm->event[CIO_SHM_CONNECTION_CLOSED]); + + UnmapViewOfFile(cio_shm->map); + CloseHandle(cio_shm->file_map); + + for (i=0; i < 5; i++) + CloseHandle(cio_shm->event[i]); + + LocalFree(cio_shm); + cio->data= NULL; + return 0; } my_socket cio_shm_get_socket(MARIADB_CIO *cio) { - if (cio && cio->data) - return (my_socket)((struct st_cio_shm *)cio->data)->pipe; - return INVALID_SOCKET; } my_bool cio_shm_is_blocking(MARIADB_CIO *cio) @@ -306,3 +446,4 @@ my_bool cio_shm_is_blocking(MARIADB_CIO *cio) } #endif + diff --git a/plugins/cio/cio_socket.c b/plugins/cio/cio_socket.c index 485b1c2c..64c229b6 100644 --- a/plugins/cio/cio_socket.c +++ b/plugins/cio/cio_socket.c @@ -101,10 +101,14 @@ struct st_ma_cio_methods cio_socket_methods= { cio_socket_is_alive }; +#ifndef HAVE_SOCKET_DYNAMIC MARIADB_CIO_PLUGIN cio_socket_plugin= +#else +MARIADB_CIO_PLUGIN _mysql_client_plugin_declare_ +#endif { - MYSQL_CLIENT_CIO_PLUGIN, - MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, + MARIADB_CLIENT_CIO_PLUGIN, + MARIADB_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, "cio_socket", "Georg Richter", "MariaDB communication IO plugin for socket communication", @@ -112,9 +116,7 @@ MARIADB_CIO_PLUGIN cio_socket_plugin= "LGPL", &cio_socket_init, &cio_socket_end, - &cio_socket_methods, - NULL, - NULL + &cio_socket_methods }; struct st_cio_socket { diff --git a/plugins/io/CMakeLists.txt b/plugins/io/CMakeLists.txt index d5aee9cf..9ffc27c2 100644 --- a/plugins/io/CMakeLists.txt +++ b/plugins/io/CMakeLists.txt @@ -1,17 +1,19 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) -IF(CURL_FOUND) - # remote file plugin - INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) - SET(REMOTE_IO_SOURCES remote_io.c) - ADD_LIBRARY(remote_io SHARED ${REMOTE_IO_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) - TARGET_LINK_LIBRARIES(remote_io ${CURL_LIBRARIES}) +IF(REMOTEIO_PLUGIN_TYPE MATCHES "DYNAMIC") + IF(CURL_FOUND) + # remote file plugin + INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) + SET(REMOTE_IO_SOURCES remote_io.c) + ADD_LIBRARY(remote_io SHARED ${REMOTE_IO_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) + TARGET_LINK_LIBRARIES(remote_io ${CURL_LIBRARIES}) - SET_TARGET_PROPERTIES(remote_io PROPERTIES PREFIX "") + SET_TARGET_PROPERTIES(remote_io PROPERTIES PREFIX "") - INSTALL(TARGETS - remote_io - RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" - LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" - ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") + INSTALL(TARGETS + remote_io + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") + ENDIF() ENDIF() diff --git a/plugins/io/remote_io.c b/plugins/io/remote_io.c index 2b1f326f..dd9be58d 100644 --- a/plugins/io/remote_io.c +++ b/plugins/io/remote_io.c @@ -89,7 +89,15 @@ typedef struct CURLM *multi_handle= NULL; -mysql_declare_client_plugin(REMOTEIO) +#ifndef HAVE_REMOTE_IO_DYNAMIC +struct st_mysql_client_plugin remote_io_plugin= +{ +#else +struct st_mysql_client_plugin _mysql_client_plugin_declare_ = +#endif +{ + MARIADB_CLIENT_REMOTEIO_PLUGIN, + MARIADB_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION, "remote_io", "Georg Richter", "Remote IO plugin", diff --git a/plugins/trace/CMakeLists.txt b/plugins/trace/CMakeLists.txt index 124e05e6..7c36f294 100644 --- a/plugins/trace/CMakeLists.txt +++ b/plugins/trace/CMakeLists.txt @@ -1,16 +1,18 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) # Trace example plugin -SET(TRACE_EXAMPLE_SOURCES trace_example.c) -IF(WIN32) - SET(TRACE_EXAMPLE_SOURCES ${TRACE_EXAMPLE_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) +IF(TRACE_EXAMPLE_PLUGIN_TYPE MATCHES "DYNAMIC") + ADD_DEFINITIONS(-DHAVE_TRACE_EXAMPLE_PLUGIN_DYNAMIC=1) + SET(TRACE_EXAMPLE_SOURCES trace_example.c) + IF(WIN32) + SET(TRACE_EXAMPLE_SOURCES ${TRACE_EXAMPLE_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) + ENDIF() + ADD_LIBRARY(trace_example SHARED ${TRACE_EXAMPLE_SOURCES}) + SET_TARGET_PROPERTIES(trace_example PROPERTIES PREFIX "") + + INSTALL(TARGETS + trace_example + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") ENDIF() -ADD_LIBRARY(trace_example SHARED ${TRACE_EXAMPLE_SOURCES}) -SET_TARGET_PROPERTIES(trace_example PROPERTIES PREFIX "") - -INSTALL(TARGETS - trace_example - RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" - LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" - ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") - diff --git a/plugins/trace/trace_example.c b/plugins/trace/trace_example.c index 1ce5d1b8..0dd85d44 100644 --- a/plugins/trace/trace_example.c +++ b/plugins/trace/trace_example.c @@ -44,15 +44,22 @@ int (*register_callback)(my_bool register_callback, void (*callback_function)(int mode, MYSQL *mysql, const uchar *buffer, size_t length)); void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length); -mysql_declare_client_plugin(TRACE) +#ifndef HAVE_TRACE_EXAMPLE_PLUGIN_DYNAMIC +struct st_mysql_client_plugin trace_example_plugin= +#else +struct st_mysql_client_plugin _mysql_client_plugin_declare_= +#endif +{ + MARIADB_CLIENT_TRACE_PLUGIN, + MARIADB_CLIENT_TRACE_PLUGIN_INTERFACE_VERSION, "trace_example", "Georg Richter", "Trace example plugin", {1,0,0}, "LGPL", &trace_init, - &trace_deinit, -mysql_end_client_plugin; + &trace_deinit +}; static char *commands[]= { "MYSQL_COM_SLEEP", @@ -222,7 +229,7 @@ static void trace_set_command(TRACE_INFO *info, char *buffer, size_t size) void dump_buffer(uchar *buffer, size_t len) { - char *p= buffer; + uchar *p= buffer; while (p < buffer + len) { printf("%02x ", *p); @@ -267,7 +274,7 @@ void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length) * and set thread_id */ if (!thread_id && mode == READ) { - char *p= buffer; + char *p= (char *)buffer; p+= 4; /* packet length */ if (*p != 0xFF) /* protocol version 0xFF indicates error */ { @@ -279,7 +286,7 @@ void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length) } else { - char *p= buffer; + char *p= (char *)buffer; info= get_trace_info(thread_id); if (info->last_command == -1) @@ -300,7 +307,6 @@ void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length) * 36 username (zero terminated) * len (1 byte) + password or */ - int len; p+= 4; info->client_flags= uint4korr(p); @@ -329,7 +335,7 @@ void trace_callback(int mode, MYSQL *mysql, const uchar *buffer, size_t length) } } else { - char *p= buffer; + char *p= (char *)buffer; int len; if (mode == WRITE) diff --git a/plugins/trace/trace_example.so b/plugins/trace/trace_example.so index c26ca8261e24bde91e467fe4e5d004be123b6773..91fec60a1744d99194e31b041ca637931ec19857 100755 GIT binary patch literal 36957 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 E8-ApIdjJ3c literal 43254 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