1 /************************************************************************************
2 Copyright (C) 2015 Georg Richter and MariaDB Corporation AB
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with this library; if not see <http://www.gnu.org/licenses>
16 or write to the Free Software Foundation, Inc.,
17 51 Franklin St., Fifth Floor, Boston, MA 02110, USA
18
19 *************************************************************************************/
20 /* MariaDB virtual IO plugin for Windows shared memory communication */
21
22 #ifdef _WIN32
23
24 #include <ma_global.h>
25 #include <ma_sys.h>
26 #include <errmsg.h>
27 #include <mysql.h>
28 #include <mysql/client_plugin.h>
29 #include <string.h>
30 #include <ma_string.h>
31
32 #define PVIO_SHM_BUFFER_SIZE (16000 + 4)
33
34 my_bool pvio_shm_set_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type, int timeout);
35 int pvio_shm_get_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type);
36 ssize_t pvio_shm_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length);
37 ssize_t pvio_shm_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length);
38 int pvio_shm_wait_io_or_timeout(MARIADB_PVIO *pvio, my_bool is_read, int timeout);
39 int pvio_shm_blocking(MARIADB_PVIO *pvio, my_bool value, my_bool *old_value);
40 my_bool pvio_shm_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo);
41 my_bool pvio_shm_close(MARIADB_PVIO *pvio);
42 int pvio_shm_shutdown(MARIADB_PVIO *pvio);
43 my_bool pvio_shm_is_alive(MARIADB_PVIO *pvio);
44 my_bool pvio_shm_get_handle(MARIADB_PVIO *pvio, void *handle);
45
46 struct st_ma_pvio_methods pvio_shm_methods= {
47 pvio_shm_set_timeout,
48 pvio_shm_get_timeout,
49 pvio_shm_read,
50 NULL,
51 pvio_shm_write,
52 NULL,
53 pvio_shm_wait_io_or_timeout,
54 pvio_shm_blocking,
55 pvio_shm_connect,
56 pvio_shm_close,
57 NULL,
58 NULL,
59 pvio_shm_get_handle,
60 NULL,
61 pvio_shm_is_alive,
62 NULL,
63 pvio_shm_shutdown
64 };
65
66 #ifndef PLUGIN_DYNAMIC
67 MARIADB_PVIO_PLUGIN pvio_shmem_client_plugin=
68 #else
69 MARIADB_PVIO_PLUGIN _mysql_client_plugin_declaration_=
70 #endif
71 {
72 MARIADB_CLIENT_PVIO_PLUGIN,
73 MARIADB_CLIENT_PVIO_PLUGIN_INTERFACE_VERSION,
74 "pvio_shmem",
75 "Georg Richter",
76 "MariaDB virtual IO plugin for Windows shared memory communication",
77 {1, 0, 0},
78 "LGPPL",
79 NULL,
80 NULL,
81 NULL,
82 NULL,
83 &pvio_shm_methods,
84
85 };
86
87 enum enum_shm_events
88 {
89 PVIO_SHM_SERVER_WROTE= 0,
90 PVIO_SHM_SERVER_READ,
91 PVIO_SHM_CLIENT_WROTE,
92 PVIO_SHM_CLIENT_READ,
93 PVIO_SHM_CONNECTION_CLOSED
94 };
95
96 typedef struct {
97 HANDLE event[5];
98 HANDLE file_map;
99 LPVOID *map;
100 char *read_pos;
101 size_t buffer_size;
102 } PVIO_SHM;
103
104 const char *StrEvent[]= {"SERVER_WROTE", "SERVER_READ", "CLIENT_WROTE", "CLIENT_READ", "CONNECTION_CLOSED"};
105
106 struct st_pvio_shm {
107 char *shm_name;
108 };
109
pvio_shm_set_timeout(MARIADB_PVIO * pvio,enum enum_pvio_timeout type,int timeout)110 my_bool pvio_shm_set_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type, int timeout)
111 {
112 if (!pvio)
113 return 1;
114 pvio->timeout[type]= (timeout > 0) ? timeout * 1000 : INFINITE;
115 return 0;
116 }
117
pvio_shm_get_timeout(MARIADB_PVIO * pvio,enum enum_pvio_timeout type)118 int pvio_shm_get_timeout(MARIADB_PVIO *pvio, enum enum_pvio_timeout type)
119 {
120 if (!pvio)
121 return -1;
122 return pvio->timeout[type] / 1000;
123 }
124
pvio_shm_read(MARIADB_PVIO * pvio,uchar * buffer,size_t length)125 ssize_t pvio_shm_read(MARIADB_PVIO *pvio, uchar *buffer, size_t length)
126 {
127 PVIO_SHM *pvio_shm= (PVIO_SHM *)pvio->data;
128 size_t copy_size= length;
129 HANDLE events[2];
130
131 if (!pvio_shm)
132 return -1;
133
134 /* we need to wait for write and close events */
135 if (!pvio_shm->buffer_size)
136 {
137 events[0]= pvio_shm->event[PVIO_SHM_CONNECTION_CLOSED];
138 events[1]= pvio_shm->event[PVIO_SHM_SERVER_WROTE];
139
140 switch(WaitForMultipleObjects(2, events, 0, pvio->timeout[PVIO_READ_TIMEOUT]))
141 {
142 case WAIT_OBJECT_0: /* server closed connection */
143 SetLastError(ERROR_GRACEFUL_DISCONNECT);
144 return -1;
145 case WAIT_OBJECT_0 +1: /* server_wrote event */
146 break;
147 case WAIT_TIMEOUT:
148 SetLastError(ETIMEDOUT);
149 default:
150 return -1;
151 }
152 /* server sent data */
153 pvio_shm->read_pos= (char *)pvio_shm->map;
154 pvio_shm->buffer_size= uint4korr(pvio_shm->read_pos);
155 pvio_shm->read_pos+= 4;
156 }
157
158 if (pvio_shm->buffer_size < copy_size)
159 copy_size= pvio_shm->buffer_size;
160
161 if (copy_size)
162 {
163 memcpy(buffer, (uchar *)pvio_shm->read_pos, pvio_shm->buffer_size);
164 pvio_shm->read_pos+= copy_size;
165 pvio_shm->buffer_size-= copy_size;
166 }
167
168 /* we need to read again */
169 if (!pvio_shm->buffer_size)
170 if (!SetEvent(pvio_shm->event[PVIO_SHM_CLIENT_READ]))
171 return -1;
172
173 return (ssize_t)copy_size;
174 }
175
pvio_shm_write(MARIADB_PVIO * pvio,const uchar * buffer,size_t length)176 ssize_t pvio_shm_write(MARIADB_PVIO *pvio, const uchar *buffer, size_t length)
177 {
178 HANDLE events[2];
179 PVIO_SHM *pvio_shm= (PVIO_SHM *)pvio->data;
180 size_t bytes_to_write= length;
181 uchar *buffer_pos= (uchar *)buffer;
182
183 if (!pvio_shm)
184 return -1;
185
186 events[0]= pvio_shm->event[PVIO_SHM_CONNECTION_CLOSED];
187 events[1]= pvio_shm->event[PVIO_SHM_SERVER_READ];
188
189 while (bytes_to_write)
190 {
191 size_t pkt_length;
192 switch (WaitForMultipleObjects(2, events, 0, pvio->timeout[PVIO_WRITE_TIMEOUT])) {
193 case WAIT_OBJECT_0: /* connection closed */
194 SetLastError(ERROR_GRACEFUL_DISCONNECT);
195 return -1;
196 case WAIT_OBJECT_0 + 1: /* server_read */
197 break;
198 case WAIT_TIMEOUT:
199 SetLastError(ETIMEDOUT);
200 default:
201 return -1;
202 }
203 pkt_length= MIN(PVIO_SHM_BUFFER_SIZE, length);
204 int4store(pvio_shm->map, pkt_length);
205 memcpy((uchar *)pvio_shm->map + 4, buffer_pos, length);
206 buffer_pos+= length;
207 bytes_to_write-= length;
208
209 if (!SetEvent(pvio_shm->event[PVIO_SHM_CLIENT_WROTE]))
210 return -1;
211 }
212 return (ssize_t)length;
213 }
214
215
pvio_shm_wait_io_or_timeout(MARIADB_PVIO * pvio,my_bool is_read,int timeout)216 int pvio_shm_wait_io_or_timeout(MARIADB_PVIO *pvio, my_bool is_read, int timeout)
217 {
218 return 0;
219 }
220
pvio_shm_blocking(MARIADB_PVIO * pvio,my_bool block,my_bool * previous_mode)221 int pvio_shm_blocking(MARIADB_PVIO *pvio, my_bool block, my_bool *previous_mode)
222 {
223 /* not supported */
224 return 0;
225 }
226
pvio_shm_keepalive(MARIADB_PVIO * pvio)227 int pvio_shm_keepalive(MARIADB_PVIO *pvio)
228 {
229 /* not supported */
230 return 0;
231 }
232
pvio_shm_fast_send(MARIADB_PVIO * pvio)233 int pvio_shm_fast_send(MARIADB_PVIO *pvio)
234 {
235 /* not supported */
236 return 0;
237 }
238
pvio_shm_connect(MARIADB_PVIO * pvio,MA_PVIO_CINFO * cinfo)239 my_bool pvio_shm_connect(MARIADB_PVIO *pvio, MA_PVIO_CINFO *cinfo)
240 {
241 const char *base_memory_name;
242 char *prefixes[]= {"", "Global\\", NULL};
243 char *shm_name, *shm_suffix, *shm_prefix;
244 uchar i= 0;
245 int len;
246 int cid;
247 DWORD dwDesiredAccess= EVENT_MODIFY_STATE | SYNCHRONIZE;
248 HANDLE hdlConnectRequest= NULL,
249 hdlConnectRequestAnswer= NULL,
250 file_map= NULL;
251 LPVOID map= NULL;
252 PVIO_SHM *pvio_shm= (PVIO_SHM*)LocalAlloc(LMEM_ZEROINIT, sizeof(PVIO_SHM));
253
254 if (!pvio_shm)
255 {
256 PVIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, "HY000", 0, "");
257 return 0;
258 }
259
260 /* MariaDB server constructs the event name as follows:
261 "Global\\base_memory_name" or
262 "\\base_memory_name"
263 */
264
265
266 base_memory_name= (cinfo->host) ? cinfo->host : SHM_DEFAULT_NAME;
267
268 if (!(shm_name= (char *)LocalAlloc(LMEM_ZEROINIT, strlen(base_memory_name) + 40)))
269 {
270 PVIO_SET_ERROR(cinfo->mysql, CR_OUT_OF_MEMORY, "HY000", 0, "");
271 goto error;
272 }
273
274 /* iterate through prefixes */
275 while (prefixes[i])
276 {
277 len= sprintf(shm_name, "%s%s_", prefixes[i], base_memory_name);
278 shm_suffix= shm_name + len;
279 strcpy(shm_suffix, "CONNECT_REQUEST");
280 if ((hdlConnectRequest= OpenEvent(dwDesiredAccess, 0, shm_name)))
281 {
282 /* save prefix to prevent further loop */
283 shm_prefix= prefixes[i];
284 break;
285 }
286 i++;
287 }
288 if (!hdlConnectRequest)
289 {
290 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "Opening CONNECT_REQUEST event failed", GetLastError());
291 goto error;
292 }
293
294 strcpy(shm_suffix, "CONNECT_ANSWER");
295 if (!(hdlConnectRequestAnswer= OpenEvent(dwDesiredAccess, 0, shm_name)))
296 {
297 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "Opening CONNECT_ANSWER event failed", GetLastError());
298 goto error;
299 }
300
301 /* get connection id, so we can build the filename used for connection */
302 strcpy(shm_suffix, "CONNECT_DATA");
303 if (!(file_map= OpenFileMapping(FILE_MAP_WRITE, 0, shm_name)))
304 {
305 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "OpenFileMapping failed", GetLastError());
306 goto error;
307 }
308
309 /* try to get first 4 bytes, which represents connection_id */
310 if (!(map= MapViewOfFile(file_map, FILE_MAP_WRITE, 0, 0, sizeof(cid))))
311 {
312 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "Reading connection_id failed", GetLastError());
313 goto error;
314 }
315
316 /* notify server */
317 if (!SetEvent(hdlConnectRequest))
318 {
319 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "Failed sending connection request", GetLastError());
320 goto error;
321 }
322
323 /* Wait for server answer */
324 switch(WaitForSingleObject(hdlConnectRequestAnswer, pvio->timeout[PVIO_CONNECT_TIMEOUT])) {
325 case WAIT_ABANDONED:
326 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "Mutex was not released in time", GetLastError());
327 goto error;
328 break;
329 case WAIT_FAILED:
330 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "Operation wait failed", GetLastError());
331 goto error;
332 break;
333 case WAIT_TIMEOUT:
334 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "Operation timed out", GetLastError());
335 goto error;
336 break;
337 case WAIT_OBJECT_0:
338 break;
339 default:
340 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "Wait for server failed", GetLastError());
341 break;
342 }
343
344 cid= uint4korr(map);
345
346 len= sprintf(shm_name, "%s%s_%d_", shm_prefix, base_memory_name, cid);
347 shm_suffix= shm_name + len;
348
349 strcpy(shm_suffix, "DATA");
350 pvio_shm->file_map= OpenFileMapping(FILE_MAP_WRITE, 0, shm_name);
351 if (pvio_shm->file_map == NULL)
352 {
353 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "OpenFileMapping failed", GetLastError());
354 goto error;
355 }
356 if (!(pvio_shm->map= MapViewOfFile(pvio_shm->file_map, FILE_MAP_WRITE, 0, 0, PVIO_SHM_BUFFER_SIZE)))
357 {
358 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "MapViewOfFile failed", GetLastError());
359 goto error;
360 }
361
362 for (i=0; i < 5; i++)
363 {
364 strcpy(shm_suffix, StrEvent[i]);
365 if (!(pvio_shm->event[i]= OpenEvent(dwDesiredAccess, 0, shm_name)))
366 {
367 PVIO_SET_ERROR(cinfo->mysql, CR_SHARED_MEMORY_CONNECT_ERROR, "HY000", 0, "Couldn't create event", GetLastError());
368 goto error;
369 }
370 }
371 /* we will first read from server */
372 SetEvent(pvio_shm->event[PVIO_SHM_SERVER_READ]);
373
374 error:
375 if (hdlConnectRequest)
376 CloseHandle(hdlConnectRequest);
377 if (hdlConnectRequestAnswer)
378 CloseHandle(hdlConnectRequestAnswer);
379 if (shm_name)
380 LocalFree(shm_name);
381 if (map)
382 UnmapViewOfFile(map);
383 if (file_map)
384 CloseHandle(file_map);
385 if (pvio_shm)
386 {
387 /* check if all events are set */
388 if (pvio_shm->event[4])
389 {
390 pvio->data= (void *)pvio_shm;
391 pvio->mysql= cinfo->mysql;
392 pvio->type= cinfo->type;
393 pvio_shm->read_pos= (char *)pvio_shm->map;
394 pvio->mysql->net.pvio= pvio;
395 return 0;
396 }
397 for (i=0;i < 5; i++)
398 if (pvio_shm->event[i])
399 CloseHandle(pvio_shm->event[i]);
400 if (pvio_shm->map)
401 UnmapViewOfFile(pvio_shm->map);
402 if (pvio_shm->file_map)
403 CloseHandle(pvio_shm->file_map);
404 LocalFree(pvio_shm);
405 }
406 return 1;
407
408 }
409
pvio_shm_close(MARIADB_PVIO * pvio)410 my_bool pvio_shm_close(MARIADB_PVIO *pvio)
411 {
412 PVIO_SHM *pvio_shm= (PVIO_SHM *)pvio->data;
413 int i;
414
415 if (!pvio_shm)
416 return 1;
417
418 /* notify server */
419 SetEvent(pvio_shm->event[PVIO_SHM_CONNECTION_CLOSED]);
420
421 UnmapViewOfFile(pvio_shm->map);
422 CloseHandle(pvio_shm->file_map);
423
424 for (i=0; i < 5; i++)
425 CloseHandle(pvio_shm->event[i]);
426
427 LocalFree(pvio_shm);
428 pvio->data= NULL;
429 return 0;
430 }
431
pvio_shm_get_socket(MARIADB_PVIO * pvio,void * handle)432 my_bool pvio_shm_get_socket(MARIADB_PVIO *pvio, void *handle)
433 {
434 return 1;
435 }
436
pvio_shm_is_blocking(MARIADB_PVIO * pvio)437 my_bool pvio_shm_is_blocking(MARIADB_PVIO *pvio)
438 {
439 return 1;
440 }
441
pvio_shm_shutdown(MARIADB_PVIO * pvio)442 int pvio_shm_shutdown(MARIADB_PVIO *pvio)
443 {
444 PVIO_SHM *pvio_shm= (PVIO_SHM *)pvio->data;
445 if (pvio_shm)
446 return (SetEvent(pvio_shm->event[PVIO_SHM_CONNECTION_CLOSED]) ? 0 : 1);
447 return 1;
448 }
449
pvio_shm_is_alive(MARIADB_PVIO * pvio)450 my_bool pvio_shm_is_alive(MARIADB_PVIO *pvio)
451 {
452 PVIO_SHM *pvio_shm;
453 if (!pvio || !pvio->data)
454 return FALSE;
455 pvio_shm= (PVIO_SHM *)pvio->data;
456 return WaitForSingleObject(pvio_shm->event[PVIO_SHM_CONNECTION_CLOSED], 0)!=WAIT_OBJECT_0;
457 }
458
pvio_shm_get_handle(MARIADB_PVIO * pvio,void * handle)459 my_bool pvio_shm_get_handle(MARIADB_PVIO *pvio, void *handle)
460 {
461
462 *(HANDLE **)handle= 0;
463 if (!pvio || !pvio->data)
464 return FALSE;
465 *(HANDLE **)handle= ((PVIO_SHM*)pvio->data)->event;
466 return TRUE;
467 }
468 #endif
469
470