1 /*
2    Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
23 */
24 
25 
26 #include <ndb_global.h>
27 
28 #include "SHM_Transporter.hpp"
29 #include "TransporterInternalDefinitions.hpp"
30 #include <TransporterCallback.hpp>
31 #include <NdbSleep.h>
32 #include <NdbOut.hpp>
33 
34 #include <sys/ipc.h>
35 #include <sys/shm.h>
36 
37 #include <EventLogger.hpp>
38 extern EventLogger * g_eventLogger;
39 
40 #if 0
41 #define DEBUG_FPRINTF(arglist) do { fprintf arglist ; } while (0)
42 #else
43 #define DEBUG_FPRINTF(a)
44 #endif
45 
make_error_info(char info[],int sz)46 void SHM_Transporter::make_error_info(char info[], int sz)
47 {
48   snprintf(info,sz,"Shm key=%d sz=%d id=%d",
49 	   shmKey, shmSize, shmId);
50 }
51 
52 bool
ndb_shm_create()53 SHM_Transporter::ndb_shm_create()
54 {
55   if (!isServer)
56   {
57     ndbout_c("Trying to create shared memory segment on the client side");
58     return false;
59   }
60   shmId = shmget(shmKey, shmSize, IPC_CREAT | 960);
61   if (shmId == -1)
62   {
63     DEBUG_FPRINTF((stderr, "(%u)shmget(IPC_CREAT)(%u) failed LINE:%d,shmId:%d,"
64                            " errno %d(%s)\n",
65                    localNodeId,
66                    remoteNodeId,
67                    __LINE__,
68                    shmId,
69                    errno,
70                    strerror(errno)));
71     fprintf(stderr,
72       "ERROR: Failed to create SHM segment of size %u with errno: %d(%s)\n",
73       shmSize, errno, strerror(errno));
74     require(false);
75     return false;
76   }
77   return true;
78 }
79 
80 bool
ndb_shm_get()81 SHM_Transporter::ndb_shm_get()
82 {
83   shmId = shmget(shmKey, shmSize, 0);
84   if (shmId == -1)
85   {
86     DEBUG_FPRINTF((stderr, "(%u)shmget(0)(%u) failed LINE:%d, shmId:%d,"
87                            " errno %d(%s)\n",
88                    localNodeId,
89                    remoteNodeId,
90                    __LINE__,
91                    shmId,
92                    errno,
93                    strerror(errno)));
94     if (errno != ENOENT)
95     {
96       fprintf(stderr,
97         "ERROR: Failed to get SHM segment of size %u with errno: %d(%s)\n",
98         shmSize, errno, strerror(errno));
99       require(false);
100     }
101     return false;
102   }
103   return true;
104 }
105 
106 bool
ndb_shm_attach()107 SHM_Transporter::ndb_shm_attach()
108 {
109   assert(shmBuf == 0);
110   shmBuf = (char *)shmat(shmId, 0, 0);
111   if (shmBuf == (char*)-1)
112   {
113     DEBUG_FPRINTF((stderr,
114             "(%u)shmat(%u) failed LINE:%d, shmId:%d,"
115             " errno %d(%s)\n",
116             localNodeId,
117             remoteNodeId,
118             __LINE__,
119             shmId,
120             errno,
121             strerror(errno)));
122     if (isServer)
123       shmctl(shmId, IPC_RMID, 0);
124     _shmSegCreated = false;
125     return false;
126   }
127   return true;
128 }
129 
130 void
ndb_shm_destroy()131 SHM_Transporter::ndb_shm_destroy()
132 {
133   /**
134    * We have attached to the shared memory segment.
135    * The shared memory won't be removed until all
136    * attached processes have detached. To ensure
137    * that we remove the shared memory segment even
138    * after a crash we now remove it immediately.
139    * Otherwise the shared memory segment will be
140    * left after a crash.
141    */
142   const int res = shmctl(shmId, IPC_RMID, 0);
143   if(res == -1)
144   {
145     DEBUG_FPRINTF((stderr, "(%u)shmctl(IPC_RMID)(%u) failed LINE:%d, shmId:%d,"
146                            " errno %d(%s)\n",
147                    localNodeId,
148                    remoteNodeId,
149                    __LINE__,
150                    shmId,
151                    errno,
152                    strerror(errno)));
153     return;
154   }
155   _shmSegCreated = false;
156 }
157 
158 bool
checkConnected()159 SHM_Transporter::checkConnected()
160 {
161   struct shmid_ds info;
162   const int res = shmctl(shmId, IPC_STAT, &info);
163   if (res == -1)
164   {
165     DEBUG_FPRINTF((stderr, "(%u)checkConnected(%u) failed LINE:%d, shmId:%d,"
166                            " errno %d(%s)\n",
167                    localNodeId,
168                    remoteNodeId,
169                    __LINE__,
170                    shmId,
171                    errno,
172                    strerror(errno)));
173     return false;
174   }
175 
176   if (info.shm_nattch != 2)
177   {
178     DEBUG_FPRINTF((stderr, "(%u)checkConnected(%u) failed LINE:%d, nattch != 2",
179                    localNodeId,
180                    remoteNodeId,
181                    __LINE__));
182     DBUG_PRINT("error", ("Already connected to node %d",
183                 remoteNodeId));
184     return false;
185   }
186   return true;
187 }
188 
189 void
detach_shm(bool rep_error)190 SHM_Transporter::detach_shm(bool rep_error)
191 {
192   if (_attached)
193   {
194     struct shmid_ds info;
195     const int ret_val = shmctl(shmId, IPC_STAT, &info);
196     if (ret_val != -1)
197     {
198       if (info.shm_nattch > 0)
199       {
200         /**
201          * Ensure that the last process to detach from the
202          * shared memory is the one that removes the mutexes.
203          * This ensures that we synchronize the removal of the
204          * mutexes and ensures also that we will destroy the
205          * mutexes before we detach the last shared memory user.
206          */
207         NdbMutex_Lock(serverMutex);
208         if (isServer)
209         {
210           * serverUpFlag = 0;
211         }
212         else
213         {
214           * clientUpFlag = 0;
215         }
216         bool last = (*serverUpFlag == 0 && clientUpFlag == 0);
217         NdbMutex_Unlock(serverMutex);
218         if (last)
219         {
220           remove_mutexes();
221         }
222       }
223     }
224     const int res = shmdt(shmBuf);
225     if(res == -1)
226     {
227       DEBUG_FPRINTF((stderr, "(%u)shmdt(%u) failed LINE:%d, shmId:%d,"
228                              " errno %d(%s)\n",
229                      localNodeId,
230                      remoteNodeId,
231                      __LINE__,
232                      shmId,
233                      errno,
234                      strerror(errno)));
235       if (rep_error)
236         report_error(TE_SHM_UNABLE_TO_REMOVE_SEGMENT);
237     }
238     _attached = false;
239     DEBUG_FPRINTF((stderr, "(%u)shmdt(%d)\n",
240                    localNodeId, remoteNodeId));
241   }
242 
243   if (isServer && _shmSegCreated)
244   {
245     /**
246      * Normally should not happen.
247      */
248     assert(!rep_error);
249     const int res = shmctl(shmId, IPC_RMID, 0);
250     if(res == -1)
251     {
252       DEBUG_FPRINTF((stderr, "(%u)shmctl(IPC_RMID)(%u) failed LINE:%d,"
253                              " shmId:%d, errno %d(%s)\n",
254                      localNodeId,
255                      remoteNodeId,
256                      __LINE__,
257                      shmId,
258                      errno,
259                      strerror(errno)));
260       if (rep_error)
261         report_error(TE_SHM_UNABLE_TO_REMOVE_SEGMENT);
262     }
263     DEBUG_FPRINTF((stderr, "(%u)shmctl(IPC_RMID)(%d)\n",
264                    localNodeId, remoteNodeId));
265   }
266   _shmSegCreated = false;
267   if (reader != 0)
268   {
269     DEBUG_FPRINTF((stderr, "(%u)detach_shm(%u) LINE:%d",
270                    localNodeId, __LINE__, remoteNodeId));
271     reader->~SHM_Reader();
272     writer->~SHM_Writer();
273     shmBuf = 0;
274     reader = 0;
275     writer = 0;
276   }
277   else
278   {
279     DEBUG_FPRINTF((stderr, "(%u)reader==0(%u) LINE:%d",
280                    localNodeId, __LINE__, remoteNodeId));
281   }
282 }
283 
284 void
disconnectImpl()285 SHM_Transporter::disconnectImpl()
286 {
287   DEBUG_FPRINTF((stderr, "(%u)disconnectImpl(%d)\n",
288                  localNodeId, remoteNodeId));
289   disconnect_socket();
290   setupBuffersUndone();
291 }
292