OSDN Git Service

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