From 43d743e75b7b747341b9a5c36a933b490548bebb Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Sat, 4 Nov 2017 01:17:16 +0300 Subject: Add implementation --- mysql/plugins/pvio/pvio_npipe.c | 383 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100644 mysql/plugins/pvio/pvio_npipe.c (limited to 'mysql/plugins/pvio/pvio_npipe.c') diff --git a/mysql/plugins/pvio/pvio_npipe.c b/mysql/plugins/pvio/pvio_npipe.c new file mode 100644 index 0000000..f16beec --- /dev/null +++ b/mysql/plugins/pvio/pvio_npipe.c @@ -0,0 +1,383 @@ +/************************************************************************************ + Copyright (C) 2015 Georg Richter and 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 + +*************************************************************************************/ + +/* MariaDB virtual IO plugin for Windows named pipe communication */ + +#ifdef _WIN32 + +#include +#include +#include +#include +#include +#include +#include + +/* Function prototypes */ +my_bool pvio_npipe_set_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type, int timeout); +int pvio_npipe_get_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type); +ssize_t pvio_npipe_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length); +ssize_t pvio_npipe_async_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length); +ssize_t pvio_npipe_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length); +ssize_t pvio_npipe_async_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length); +int pvio_npipe_wait_io_or_timeout(MARIADB_PVIO *pvio, my_bool is_read, int timeout); +my_bool pvio_npipe_blocking(MARIADB_PVIO *pvio, my_bool value, my_bool *old_value); +my_bool pvio_npipe_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo); +my_bool pvio_npipe_close(MARIADB_PVIO *pvio); +int pvio_npipe_fast_send(MARIADB_PVIO *pvio); +int pvio_npipe_keepalive(MARIADB_PVIO *pvio); +my_bool pvio_npipe_get_handle(MARIADB_PVIO *pvio, void *handle); +my_bool pvio_npipe_is_blocking(MARIADB_PVIO *pvio); +int pvio_npipe_shutdown(MARIADB_PVIO *pvio); +my_bool pvio_npipe_is_alive(MARIADB_PVIO *pvio); + +struct st_ma_pvio_methods pvio_npipe_methods= { + pvio_npipe_set_timeout, + pvio_npipe_get_timeout, + pvio_npipe_read, + NULL, + pvio_npipe_write, + NULL, + pvio_npipe_wait_io_or_timeout, + pvio_npipe_blocking, + pvio_npipe_connect, + pvio_npipe_close, + pvio_npipe_fast_send, + pvio_npipe_keepalive, + pvio_npipe_get_handle, + pvio_npipe_is_blocking, + pvio_npipe_is_alive, + NULL, + pvio_npipe_shutdown +}; + +#ifndef HAVE_NPIPE_DYNAMIC +MARIADB_PVIO_PLUGIN pvio_npipe_plugin = +#else +MARIADB_PVIO_PLUGIN _mysql_client_plugin_declaration_ = +#endif +{ + MARIADB_CLIENT_PVIO_PLUGIN, + MARIADB_CLIENT_PVIO_PLUGIN_INTERFACE_VERSION, + "pvio_npipe", + "Georg Richter", + "MariaDB virtual IO plugin for named pipe connection", + {1, 0, 0}, + "LGPL", + NULL, + NULL, + NULL, + NULL, + &pvio_npipe_methods +}; + +struct st_pvio_npipe { + HANDLE pipe; + OVERLAPPED overlapped; + size_t rw_size; + MYSQL *mysql; +}; + +my_bool pvio_npipe_set_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type, int timeout) +{ + if (!pvio) + return 1; + pvio->timeout[type]= (timeout > 0) ? timeout * 1000 : -1; + return 0; +} + +int pvio_npipe_get_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type) +{ + if (!pvio) + return -1; + return pvio->timeout[type] / 1000; +} + +ssize_t pvio_npipe_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length) +{ + DWORD dwRead= 0; + ssize_t r= -1; + struct st_pvio_npipe *cpipe= NULL; + + if (!pvio || !pvio->data) + return -1; + + cpipe= (struct st_pvio_npipe *)pvio->data; + + if (ReadFile(cpipe->pipe, (LPVOID)buffer, (DWORD)length, &dwRead, &cpipe->overlapped)) + { + r= (ssize_t)dwRead; + goto end; + } + if (GetLastError() == ERROR_IO_PENDING) + { + if (!pvio_npipe_wait_io_or_timeout(pvio, 1, 0)) + r= cpipe->rw_size; + } +end: + return r; +} + +ssize_t pvio_npipe_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length) +{ + DWORD dwWrite= 0; + ssize_t r= -1; + struct st_pvio_npipe *cpipe= NULL; + + if (!pvio || !pvio->data) + return -1; + + cpipe= (struct st_pvio_npipe *)pvio->data; + + if (WriteFile(cpipe->pipe, buffer, (DWORD)length, &dwWrite, &cpipe->overlapped)) + { + r= (ssize_t)dwWrite; + goto end; + } + if (GetLastError() == ERROR_IO_PENDING) + { + if (!pvio_npipe_wait_io_or_timeout(pvio, 0, 0)) + r= cpipe->rw_size; + } +end: + return r; +} + +int pvio_npipe_wait_io_or_timeout(MARIADB_PVIO *pvio, my_bool is_read, int timeout) +{ + int r= -1; + DWORD status; + int save_error; + struct st_pvio_npipe *cpipe= NULL; + + cpipe= (struct st_pvio_npipe *)pvio->data; + + if (!timeout) + timeout= (is_read) ? pvio->timeout[PVIO_READ_TIMEOUT] : pvio->timeout[PVIO_WRITE_TIMEOUT]; + if (!timeout) + timeout= INFINITE; + + status= WaitForSingleObject(cpipe->overlapped.hEvent, timeout); + if (status == WAIT_OBJECT_0) + { + if (GetOverlappedResult(cpipe->pipe, &cpipe->overlapped, (LPDWORD)&cpipe->rw_size, FALSE)) + return 0; + } + /* For other status codes (WAIT_ABANDONED, WAIT_TIMEOUT and WAIT_FAILED) + we return error */ + save_error= GetLastError(); + CancelIo(cpipe->pipe); + SetLastError(save_error); + return -1; +} + +my_bool pvio_npipe_blocking(MARIADB_PVIO *pvio, my_bool block, my_bool *previous_mode) +{ + /* not supported */ + DWORD flags= 0; + struct st_pvio_npipe *cpipe= NULL; + + cpipe= (struct st_pvio_npipe *)pvio->data; + + if (previous_mode) + { + if (!GetNamedPipeHandleState(cpipe->pipe, &flags, NULL, NULL, NULL, NULL, 0)) + return 1; + *previous_mode= flags & PIPE_NOWAIT ? 0 : 1; + } + + flags= block ? PIPE_WAIT : PIPE_NOWAIT; + if (!SetNamedPipeHandleState(cpipe->pipe, &flags, NULL, NULL)) + return 1; + return 0; +} + +int pvio_npipe_keepalive(MARIADB_PVIO *pvio) +{ + /* keep alive is used for TCP/IP connections only */ + return 0; +} + +int pvio_npipe_fast_send(MARIADB_PVIO *pvio) +{ + /* not supported */ + return 0; +} +my_bool pvio_npipe_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo) +{ + struct st_pvio_npipe *cpipe= NULL; + + if (!pvio || !cinfo) + return 1; + + /* if connect timeout is set, we will overwrite read/write timeout */ + if (pvio->timeout[PVIO_CONNECT_TIMEOUT]) + { + pvio->timeout[PVIO_READ_TIMEOUT]= pvio->timeout[PVIO_WRITE_TIMEOUT]= pvio->timeout[PVIO_CONNECT_TIMEOUT]; + } + + if (!(cpipe= (struct st_pvio_npipe *)LocalAlloc(LMEM_ZEROINIT, sizeof(struct st_pvio_npipe)))) + { + PVIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, unknown_sqlstate, 0, ""); + return 1; + } + memset(cpipe, 0, sizeof(struct st_pvio_npipe)); + pvio->data= (void *)cpipe; + cpipe->pipe= INVALID_HANDLE_VALUE; + pvio->mysql= cinfo->mysql; + pvio->type= cinfo->type; + + if (cinfo->type == PVIO_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 = MARIADB_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, + FILE_FLAG_OVERLAPPED, + NULL)) != INVALID_HANDLE_VALUE) + break; + + if (GetLastError() != ERROR_PIPE_BUSY) + { + pvio->set_error(pvio->mysql, CR_NAMEDPIPEOPEN_ERROR, SQLSTATE_UNKNOWN, 0, + cinfo->host, cinfo->unix_socket, GetLastError()); + goto end; + } + + if (has_timedout || !WaitNamedPipe(szPipeName, pvio->timeout[PVIO_CONNECT_TIMEOUT])) + { + pvio->set_error(pvio->mysql, 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)) + { + pvio->set_error(pvio->mysql, 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))) + { + pvio->set_error(pvio->mysql, CR_EVENT_CREATE_FAILED, SQLSTATE_UNKNOWN, 0, + GetLastError()); + goto end; + } + return 0; + } +end: + if (cpipe) + { + if (cpipe->pipe != INVALID_HANDLE_VALUE) + CloseHandle(cpipe->pipe); + LocalFree(cpipe); + pvio->data= NULL; + } + return 1; +} + +my_bool pvio_npipe_close(MARIADB_PVIO *pvio) +{ + struct st_pvio_npipe *cpipe= NULL; + int r= 0; + + if (!pvio) + return 1; + + if (pvio->data) + { + cpipe= (struct st_pvio_npipe *)pvio->data; + CloseHandle(cpipe->overlapped.hEvent); + if (cpipe->pipe != INVALID_HANDLE_VALUE) + { + CloseHandle(cpipe->pipe); + cpipe->pipe= INVALID_HANDLE_VALUE; + } + LocalFree(pvio->data); + pvio->data= NULL; + } + return r; +} + +my_bool pvio_npipe_get_handle(MARIADB_PVIO *pvio, void *handle) +{ + if (pvio && pvio->data) + { + *(HANDLE *)handle= ((struct st_pvio_npipe *)pvio->data)->pipe; + return 0; + } + return 1; +} + +my_bool pvio_npipe_is_blocking(MARIADB_PVIO *pvio) +{ + DWORD flags= 0; + struct st_pvio_npipe *cpipe= NULL; + + cpipe= (struct st_pvio_npipe *)pvio->data; + + if (!GetNamedPipeHandleState(cpipe->pipe, &flags, NULL, NULL, NULL, NULL, 0)) + return 1; + return (flags & PIPE_NOWAIT) ? 0 : 1; +} + +int pvio_npipe_shutdown(MARIADB_PVIO *pvio) +{ + HANDLE h; + if (pvio_npipe_get_handle(pvio, &h) == 0) + { + return(CancelIoEx(h, NULL) ? 0 : 1); + } + return 1; +} + +my_bool pvio_npipe_is_alive(MARIADB_PVIO *pvio) +{ + HANDLE handle; + if (!pvio || !pvio->data) + return FALSE; + handle= ((struct st_pvio_npipe *)pvio->data)->pipe; + /* Copy data fron named pipe without removing it */ + if (PeekNamedPipe(handle, NULL, 0, NULL, NULL, NULL)) + return TRUE; + return test(GetLastError() != ERROR_BROKEN_PIPE); +} +#endif -- cgit v1.1