OSDN Git Service

Moved all IPC functions into the MUtilities libraries.
[mutilities/MUtilities.git] / src / IPCChannel.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2014 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 //
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
21
22 //MUtils
23 #include <MUtils/IPCChannel.h>
24 #include <MUtils/Exception.h>
25
26 //Qt includes
27 #include <QRegExp>
28 #include <QSharedMemory>
29 #include <QSystemSemaphore>
30 #include <QWriteLocker>
31
32 ///////////////////////////////////////////////////////////////////////////////
33 // TYPES
34 ///////////////////////////////////////////////////////////////////////////////
35
36 namespace MUtils
37 {
38         static const size_t IPC_SLOTS = 128;
39         static const size_t MAX_MESSAGE_LEN = 4096;
40
41         typedef struct
42         {
43                 unsigned int command;
44                 unsigned int reserved_1;
45                 unsigned int reserved_2;
46                 char parameter[MAX_MESSAGE_LEN];
47         }
48         ipc_data_t;
49
50         typedef struct
51         {
52                 unsigned int pos_wr;
53                 unsigned int pos_rd;
54                 ipc_data_t data[IPC_SLOTS];
55         }
56         ipc_t;
57 }
58
59 ///////////////////////////////////////////////////////////////////////////////
60 // UTILITIES
61 ///////////////////////////////////////////////////////////////////////////////
62
63 static inline QString ESCAPE(QString str)
64 {
65         return str.replace(QRegExp("[^A-Za-z0-9_]"), "_").toLower();
66 }
67
68 static QString MAKE_ID(const QString &applicationId, const QString &channelId, const QString &itemId)
69 {
70         return QString("ipc://mutilities.muldersoft.com:37402/%1/%2/%3").arg(ESCAPE(applicationId), ESCAPE(channelId), ESCAPE(itemId));
71 }
72
73 ///////////////////////////////////////////////////////////////////////////////
74 // PRIVATE DATA
75 ///////////////////////////////////////////////////////////////////////////////
76
77 namespace MUtils
78 {
79         class IPCChannel_Private
80         {
81                 friend class IPCChannel;
82
83         protected:
84                 volatile bool initialized;
85                 QScopedPointer<QSharedMemory> sharedmem;
86                 QScopedPointer<QSystemSemaphore> semaphore_rd;
87                 QScopedPointer<QSystemSemaphore> semaphore_wr;
88                 QReadWriteLock lock;
89         };
90 }
91
92 ///////////////////////////////////////////////////////////////////////////////
93 // CONSTRUCTOR & DESTRUCTOR
94 ///////////////////////////////////////////////////////////////////////////////
95
96 MUtils::IPCChannel::IPCChannel(const QString &applicationId, const QString &channelId)
97 :
98         p(new IPCChannel_Private()),
99         m_applicationId(applicationId),
100         m_channelId(channelId)
101 {
102         p->initialized = false;
103 }
104
105 MUtils::IPCChannel::~IPCChannel(void)
106 {
107         if(p->initialized)
108         {
109                 if(p->sharedmem->isAttached())
110                 {
111                         p->sharedmem->detach();
112                 }
113         }
114
115         delete p;
116 }
117
118 ///////////////////////////////////////////////////////////////////////////////
119 // INITIALIZATION
120 ///////////////////////////////////////////////////////////////////////////////
121
122 int MUtils::IPCChannel::initialize(void)
123 {
124         QWriteLocker writeLock(&p->lock);
125         
126         if(p->initialized)
127         {
128                 return IPC_RET_ALREADY_INITIALIZED;
129         }
130
131         p->sharedmem.reset(new QSharedMemory(MAKE_ID(m_applicationId, m_channelId, "sharedmem"), NULL));
132         p->semaphore_rd.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_channelId, "semaphore_rd"), 0));
133         p->semaphore_wr.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_channelId, "semaphore_wr"), 0));
134
135         if(p->semaphore_rd->error() != QSystemSemaphore::NoError)
136         {
137                 const QString errorMessage = p->semaphore_rd->errorString();
138                 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
139                 return IPC_RET_FAILURE;
140         }
141
142         if(p->semaphore_wr->error() != QSystemSemaphore::NoError)
143         {
144                 const QString errorMessage = p->semaphore_wr->errorString();
145                 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
146                 return IPC_RET_FAILURE;
147         }
148         
149         if(!p->sharedmem->create(sizeof(ipc_t)))
150         {
151                 if(p->sharedmem->error() == QSharedMemory::AlreadyExists)
152                 {
153                         if(!p->sharedmem->attach())
154                         {
155                                 const QString errorMessage = p->sharedmem->errorString();
156                                 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
157                                 return IPC_RET_FAILURE;
158                         }
159                         if(p->sharedmem->error() != QSharedMemory::NoError)
160                         {
161                                 const QString errorMessage = p->sharedmem->errorString();
162                                 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
163                                 return IPC_RET_FAILURE;
164                         }
165                         p->initialized = true;
166                         return IPC_RET_SUCCESS_SLAVE;
167                 }
168                 else
169                 {
170                         const QString errorMessage = p->sharedmem->errorString();
171                         qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
172                         return IPC_RET_FAILURE;
173                 }
174         }
175         
176         if(p->sharedmem->error() != QSharedMemory::NoError)
177         {
178                 const QString errorMessage = p->sharedmem->errorString();
179                 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
180                 return IPC_RET_FAILURE;
181         }
182
183         if(void *const data = p->sharedmem->data())
184         {
185                 memset(data, 0, sizeof(ipc_t));
186         }
187
188         if(!p->semaphore_wr->release(IPC_SLOTS))
189         {
190                 const QString errorMessage = p->semaphore_wr->errorString();
191                 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
192                 return IPC_RET_FAILURE;
193         }
194         
195         p->initialized = true;
196         return IPC_RET_SUCCESS_MASTER;
197 }
198
199 ///////////////////////////////////////////////////////////////////////////////
200 // SEND MESSAGE
201 ///////////////////////////////////////////////////////////////////////////////
202
203 bool MUtils::IPCChannel::send(const unsigned int &command, const char *const message)
204 {
205         bool success = false;
206         QReadLocker readLock(&p->lock);
207
208         if(!p->initialized)
209         {
210                 MUTILS_THROW("Shared memory for IPC not initialized yet.");
211         }
212
213         ipc_data_t ipc_data;
214         memset(&ipc_data, 0, sizeof(ipc_data_t));
215         ipc_data.command = command;
216         
217         if(message)
218         {
219                 strncpy_s(ipc_data.parameter, MAX_MESSAGE_LEN, message, _TRUNCATE);
220         }
221
222         if(!p->semaphore_wr->acquire())
223         {
224                 const QString errorMessage = p->semaphore_wr->errorString();
225                 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
226                 return false;
227         }
228
229         if(!p->sharedmem->lock())
230         {
231                 const QString errorMessage = p->sharedmem->errorString();
232                 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
233                 return false;
234         }
235
236         if(ipc_t *const ptr = reinterpret_cast<ipc_t*>(p->sharedmem->data()))
237         {
238                 success = true;
239                 memcpy(&ptr->data[ptr->pos_wr], &ipc_data, sizeof(ipc_data_t));
240                 ptr->pos_wr = (ptr->pos_wr + 1) % IPC_SLOTS;
241         }
242         else
243         {
244                 qWarning("Shared memory pointer is NULL -> unable to write data!");
245         }
246
247         if(!p->sharedmem->unlock())
248         {
249                 const QString errorMessage = p->sharedmem->errorString();
250                 qWarning("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
251                 return false;
252         }
253
254         if(!p->semaphore_rd->release())
255         {
256                 const QString errorMessage = p->semaphore_rd->errorString();
257                 qWarning("Failed to acquire release semaphore: %s", MUTILS_UTF8(errorMessage));
258                 return false;
259         }
260
261         return success;
262 }
263
264 ///////////////////////////////////////////////////////////////////////////////
265 // READ MESSAGE
266 ///////////////////////////////////////////////////////////////////////////////
267
268 bool MUtils::IPCChannel::read(unsigned int &command, char *const message, const size_t &buffSize)
269 {
270         bool success = false;
271         QReadLocker readLock(&p->lock);
272         
273         command = 0;
274         if(message && (buffSize > 0))
275         {
276                 message[0] = '\0';
277         }
278         
279         if(!p->initialized)
280         {
281                 MUTILS_THROW("Shared memory for IPC not initialized yet.");
282         }
283
284         ipc_data_t ipc_data;
285         memset(&ipc_data, 0, sizeof(ipc_data_t));
286
287         if(!p->semaphore_rd->acquire())
288         {
289                 const QString errorMessage = p->semaphore_rd->errorString();
290                 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
291                 return false;
292         }
293
294         if(!p->sharedmem->lock())
295         {
296                 const QString errorMessage = p->sharedmem->errorString();
297                 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
298                 return false;
299         }
300
301         if(ipc_t *const ptr = reinterpret_cast<ipc_t*>(p->sharedmem->data()))
302         {
303                         success = true;
304                 memcpy(&ipc_data, &ptr->data[ptr->pos_rd], sizeof(ipc_data_t));
305                 ptr->pos_rd = (ptr->pos_rd + 1) % IPC_SLOTS;
306
307                 if(!(ipc_data.reserved_1 || ipc_data.reserved_2))
308                 {
309                         command = ipc_data.command;
310                         strncpy_s(message, buffSize, ipc_data.parameter, _TRUNCATE);
311                 }
312                 else
313                 {
314                         qWarning("Malformed IPC message, will be ignored");
315                 }
316         }
317         else
318         {
319                 qWarning("Shared memory pointer is NULL -> unable to write data!");
320         }
321
322         if(!p->sharedmem->unlock())
323         {
324                 const QString errorMessage = p->sharedmem->errorString();
325                 qWarning("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
326                 return false;
327         }
328
329         if(!p->semaphore_wr->release())
330         {
331                 const QString errorMessage = p->semaphore_wr->errorString();
332                 qWarning("Failed to acquire release semaphore: %s", MUTILS_UTF8(errorMessage));
333                 return false;
334         }
335
336         return success;
337 }