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