OSDN Git Service

Happy new year 2015 !!!
[mutilities/MUtilities.git] / src / IPCChannel.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2015 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 //Internal
27 #include "3rd_party/adler32/include/adler32.h"
28
29 //Qt includes
30 #include <QRegExp>
31 #include <QSharedMemory>
32 #include <QSystemSemaphore>
33 #include <QMutex>
34 #include <QWriteLocker>
35 #include <QCryptographicHash>
36
37 //CRT
38 #include <cassert>
39
40 ///////////////////////////////////////////////////////////////////////////////
41 // UTILITIES
42 ///////////////////////////////////////////////////////////////////////////////
43
44 namespace MUtils
45 {
46         namespace Internal
47         {
48                 static const quint32 ADLER_SEED = 0x5D90C356;
49
50                 template<class T>
51                 static inline void UPDATE_CHECKSUM(T &data)
52                 {
53                         data.checksum = Internal::adler32(ADLER_SEED, &data.payload, sizeof(data.payload));
54                 }
55
56                 template<class T>
57                 static inline bool VERIFY_CHECKSUM(const T &data)
58                 {
59                         return (data.checksum == Internal::adler32(ADLER_SEED, &data.payload, sizeof(data.payload)));
60                 }
61         }
62 }
63
64 ///////////////////////////////////////////////////////////////////////////////
65 // TYPES
66 ///////////////////////////////////////////////////////////////////////////////
67
68 namespace MUtils
69 {
70         namespace Internal
71         {
72                 static const size_t HDR_LEN = 40;
73                 static const size_t IPC_SLOTS = 128;
74
75                 typedef struct
76                 {
77                         quint64 counter;
78                         quint32 pos_wr;
79                         quint32 pos_rd;
80                 }
81                 ipc_status_data_t;
82
83                 typedef struct
84                 {
85                         ipc_status_data_t payload;
86                         quint32           checksum;
87                 }
88                 ipc_status_t;
89
90                 typedef struct
91                 {
92                         quint32 command_id;
93                         quint32 flags;
94                         char    param[MUtils::IPCChannel::MAX_MESSAGE_LEN];
95                         quint64 timestamp;
96                 }
97                 ipc_msg_data_t;
98
99                 typedef struct
100                 {
101                         ipc_msg_data_t payload;
102                         quint32        checksum;
103                 }
104                 ipc_msg_t;
105
106                 typedef struct
107                 {
108                         char         header[HDR_LEN];
109                         ipc_status_t status;
110                         ipc_msg_t    data[IPC_SLOTS];
111                 }
112                 ipc_t;
113         }
114 }
115
116 ///////////////////////////////////////////////////////////////////////////////
117 // UTILITIES
118 ///////////////////////////////////////////////////////////////////////////////
119
120 static QScopedPointer<QRegExp> g_escape_regexp;
121 static QMutex g_escape_lock;
122
123 #define ESCAPE(STR) (QString((STR)).replace(*g_escape_regexp, QLatin1String("_")).toLower())
124
125 static QString MAKE_ID(const QString &applicationId, const unsigned int &appVersionNo, const QString &channelId, const QString &itemId)
126 {
127         QMutexLocker locker(&g_escape_lock);
128
129         if(g_escape_regexp.isNull())
130         {
131                 g_escape_regexp.reset(new QRegExp(QLatin1String("[^A-Za-z0-9_\\-]")));
132         }
133
134         return QString("com.muldersoft.mutilities.ipc.%1.r%2.%3.%4").arg(ESCAPE(applicationId), QString::number(appVersionNo, 16).toUpper(), ESCAPE(channelId), ESCAPE(itemId));
135 }
136
137 ///////////////////////////////////////////////////////////////////////////////
138 // PRIVATE DATA
139 ///////////////////////////////////////////////////////////////////////////////
140
141 namespace MUtils
142 {
143         class IPCChannel_Private
144         {
145                 friend class IPCChannel;
146
147         protected:
148                 volatile bool initialized;
149                 QScopedPointer<QSharedMemory> sharedmem;
150                 QScopedPointer<QSystemSemaphore> semaphore_rd;
151                 QScopedPointer<QSystemSemaphore> semaphore_wr;
152                 QReadWriteLock lock;
153         };
154 }
155
156 ///////////////////////////////////////////////////////////////////////////////
157 // CONSTRUCTOR & DESTRUCTOR
158 ///////////////////////////////////////////////////////////////////////////////
159
160 MUtils::IPCChannel::IPCChannel(const QString &applicationId, const quint32 &appVersionNo, const QString &channelId)
161 :
162         p(new IPCChannel_Private()),
163         m_applicationId(applicationId),
164         m_channelId(channelId),
165         m_appVersionNo(appVersionNo),
166         m_headerStr(QCryptographicHash::hash(MAKE_ID(applicationId, appVersionNo, channelId, "header").toLatin1(), QCryptographicHash::Sha1).toHex())
167 {
168         p->initialized = false;
169         if(m_headerStr.length() != Internal::HDR_LEN)
170         {
171                 MUTILS_THROW("Invalid header length has been detected!");
172         }
173 }
174
175 MUtils::IPCChannel::~IPCChannel(void)
176 {
177         if(p->initialized)
178         {
179                 if(p->sharedmem->isAttached())
180                 {
181                         p->sharedmem->detach();
182                 }
183         }
184
185         delete p;
186 }
187
188 ///////////////////////////////////////////////////////////////////////////////
189 // INITIALIZATION
190 ///////////////////////////////////////////////////////////////////////////////
191
192 int MUtils::IPCChannel::initialize(void)
193 {
194         QWriteLocker writeLock(&p->lock);
195         
196         if(p->initialized)
197         {
198                 return RET_ALREADY_INITIALIZED;
199         }
200
201         p->sharedmem.   reset(new QSharedMemory   (MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "sharedmem"), 0));
202         p->semaphore_rd.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "semaph_rd"), 0));
203         p->semaphore_wr.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "semaph_wr"), 0));
204
205         if(p->semaphore_rd->error() != QSystemSemaphore::NoError)
206         {
207                 const QString errorMessage = p->semaphore_rd->errorString();
208                 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
209                 return RET_FAILURE;
210         }
211
212         if(p->semaphore_wr->error() != QSystemSemaphore::NoError)
213         {
214                 const QString errorMessage = p->semaphore_wr->errorString();
215                 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
216                 return RET_FAILURE;
217         }
218         
219         if(!p->sharedmem->create(sizeof(Internal::ipc_t)))
220         {
221                 if(p->sharedmem->error() == QSharedMemory::AlreadyExists)
222                 {
223                         if(!p->sharedmem->attach())
224                         {
225                                 const QString errorMessage = p->sharedmem->errorString();
226                                 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
227                                 return RET_FAILURE;
228                         }
229                         if(p->sharedmem->error() != QSharedMemory::NoError)
230                         {
231                                 const QString errorMessage = p->sharedmem->errorString();
232                                 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
233                                 return RET_FAILURE;
234                         }
235                         if(p->sharedmem->size() < sizeof(Internal::ipc_t))
236                         {
237                                 qWarning("Failed to attach to shared memory: Size verification has failed!");
238                                 return RET_FAILURE;
239                         }
240                         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
241                         {
242                                 if(memcmp(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN) != 0)
243                                 {
244                                         qWarning("Failed to attach to shared memory: Header verification has failed!");
245                                         return RET_FAILURE;
246                                 }
247                         }
248                         else
249                         {
250                                 const QString errorMessage = p->sharedmem->errorString();
251                                 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
252                                 return RET_FAILURE;
253                         }
254                         p->initialized = true;
255                         return RET_SUCCESS_SLAVE;
256                 }
257                 else
258                 {
259                         const QString errorMessage = p->sharedmem->errorString();
260                         qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
261                         return RET_FAILURE;
262                 }
263         }
264         
265         if(p->sharedmem->error() != QSharedMemory::NoError)
266         {
267                 const QString errorMessage = p->sharedmem->errorString();
268                 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
269                 return RET_FAILURE;
270         }
271
272         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
273         {
274                 memset(ptr, 0, sizeof(Internal::ipc_t));
275                 memcpy(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN);
276                 UPDATE_CHECKSUM(ptr->status);
277         }
278         else
279         {
280                 const QString errorMessage = p->sharedmem->errorString();
281                 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
282                 return RET_FAILURE;
283         }
284
285         if(!p->semaphore_wr->release(Internal::IPC_SLOTS))
286         {
287                 const QString errorMessage = p->semaphore_wr->errorString();
288                 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
289                 return RET_FAILURE;
290         }
291         
292         //qDebug("IPC KEY #1: %s", MUTILS_UTF8(p->sharedmem->key()));
293         //qDebug("IPC KEY #2: %s", MUTILS_UTF8(p->semaphore_rd->key()));
294         //qDebug("IPC KEY #3: %s", MUTILS_UTF8(p->semaphore_wr->key()));
295
296         p->initialized = true;
297         return RET_SUCCESS_MASTER;
298 }
299
300 ///////////////////////////////////////////////////////////////////////////////
301 // SEND MESSAGE
302 ///////////////////////////////////////////////////////////////////////////////
303
304 bool MUtils::IPCChannel::send(const quint32 &command, const quint32 &flags, const char *const message)
305 {
306         bool success = false;
307         QReadLocker readLock(&p->lock);
308
309         if(!p->initialized)
310         {
311                 MUTILS_THROW("Shared memory for IPC not initialized yet.");
312         }
313
314         if(!p->semaphore_wr->acquire())
315         {
316                 const QString errorMessage = p->semaphore_wr->errorString();
317                 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
318                 return false;
319         }
320
321         if(!p->sharedmem->lock())
322         {
323                 const QString errorMessage = p->sharedmem->errorString();
324                 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
325                 return false;
326         }
327
328         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
329         {
330                 if(VERIFY_CHECKSUM(ptr->status))
331                 {
332                         Internal::ipc_msg_t ipc_msg;
333                         memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
334
335                         ipc_msg.payload.command_id = command;
336                         ipc_msg.payload.flags = flags;
337                         if(message)
338                         {
339                                 strncpy_s(ipc_msg.payload.param, MAX_MESSAGE_LEN, message, _TRUNCATE);
340                         }
341                         ipc_msg.payload.timestamp = ptr->status.payload.counter++;
342                         UPDATE_CHECKSUM(ipc_msg);
343
344                         memcpy(&ptr->data[ptr->status.payload.pos_wr], &ipc_msg, sizeof(Internal::ipc_msg_t));
345                         ptr->status.payload.pos_wr = (ptr->status.payload.pos_wr + 1) % Internal::IPC_SLOTS;
346                         UPDATE_CHECKSUM(ptr->status);
347
348                         success = true;
349                 }
350                 else
351                 {
352                         qWarning("Corrupted IPC status detected -> skipping!");
353                 }
354         }
355         else
356         {
357                 qWarning("Shared memory pointer is NULL -> unable to write data!");
358         }
359
360         if(!p->sharedmem->unlock())
361         {
362                 const QString errorMessage = p->sharedmem->errorString();
363                 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
364         }
365
366         if(!p->semaphore_rd->release())
367         {
368                 const QString errorMessage = p->semaphore_rd->errorString();
369                 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
370         }
371
372         return success;
373 }
374
375 ///////////////////////////////////////////////////////////////////////////////
376 // READ MESSAGE
377 ///////////////////////////////////////////////////////////////////////////////
378
379 bool MUtils::IPCChannel::read(quint32 &command, quint32 &flags, char *const message, const size_t &buffSize)
380 {
381         bool success = false;
382         QReadLocker readLock(&p->lock);
383         
384         command = 0;
385         if(message && (buffSize > 0))
386         {
387                 message[0] = '\0';
388         }
389         
390         if(!p->initialized)
391         {
392                 MUTILS_THROW("Shared memory for IPC not initialized yet.");
393         }
394
395         Internal::ipc_msg_t ipc_msg;
396         memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
397
398         if(!p->semaphore_rd->acquire())
399         {
400                 const QString errorMessage = p->semaphore_rd->errorString();
401                 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
402                 return false;
403         }
404
405         if(!p->sharedmem->lock())
406         {
407                 const QString errorMessage = p->sharedmem->errorString();
408                 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
409                 return false;
410         }
411
412         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
413         {
414                 if(VERIFY_CHECKSUM(ptr->status))
415                 {
416                         memcpy(&ipc_msg, &ptr->data[ptr->status.payload.pos_rd], sizeof(Internal::ipc_msg_t));
417                         ptr->status.payload.pos_rd = (ptr->status.payload.pos_rd + 1) % Internal::IPC_SLOTS;
418                         UPDATE_CHECKSUM(ptr->status);
419
420                         if(VERIFY_CHECKSUM(ipc_msg) || (ipc_msg.payload.timestamp < ptr->status.payload.counter))
421                         {
422                                 command = ipc_msg.payload.command_id;
423                                 flags = ipc_msg.payload.flags;
424                                 strncpy_s(message, buffSize, ipc_msg.payload.param, _TRUNCATE);
425                                 success = true;
426                         }
427                         else
428                         {
429                                 qWarning("Malformed or corrupted IPC message, will be ignored!");
430                         }
431                 }
432                 else
433                 {
434                         qWarning("Corrupted IPC status detected -> skipping!");
435                 }
436         }
437         else
438         {
439                 qWarning("Shared memory pointer is NULL -> unable to read data!");
440         }
441
442         if(!p->sharedmem->unlock())
443         {
444                 const QString errorMessage = p->sharedmem->errorString();
445                 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
446         }
447
448         if(!p->semaphore_wr->release())
449         {
450                 const QString errorMessage = p->semaphore_wr->errorString();
451                 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
452         }
453
454         return success;
455 }