OSDN Git Service

Bump version.
[mutilities/MUtilities.git] / src / IPCChannel.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2019 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                 QAtomicInt 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         if(m_headerStr.length() != Internal::HDR_LEN)
176         {
177                 MUTILS_THROW("Invalid header length has been detected!");
178         }
179 }
180
181 MUtils::IPCChannel::~IPCChannel(void)
182 {
183         if(MUTILS_BOOLIFY(p->initialized))
184         {
185                 if(p->sharedmem->isAttached())
186                 {
187                         p->sharedmem->detach();
188                 }
189         }
190
191         delete p;
192 }
193
194 ///////////////////////////////////////////////////////////////////////////////
195 // INITIALIZATION
196 ///////////////////////////////////////////////////////////////////////////////
197
198 int MUtils::IPCChannel::initialize(void)
199 {
200         QWriteLocker writeLock(&p->lock);
201         
202         if(MUTILS_BOOLIFY(p->initialized))
203         {
204                 return RET_ALREADY_INITIALIZED;
205         }
206
207         p->sharedmem.   reset(new QSharedMemory   (MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "sharedmem"), 0));
208         p->semaphore_rd.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "semaph_rd"), 0));
209         p->semaphore_wr.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "semaph_wr"), 0));
210
211         if(p->semaphore_rd->error() != QSystemSemaphore::NoError)
212         {
213                 const QString errorMessage = p->semaphore_rd->errorString();
214                 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
215                 return RET_FAILURE;
216         }
217
218         if(p->semaphore_wr->error() != QSystemSemaphore::NoError)
219         {
220                 const QString errorMessage = p->semaphore_wr->errorString();
221                 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
222                 return RET_FAILURE;
223         }
224         
225         if(!p->sharedmem->create(sizeof(Internal::ipc_t)))
226         {
227                 if(p->sharedmem->error() == QSharedMemory::AlreadyExists)
228                 {
229                         if(!p->sharedmem->attach())
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->error() != QSharedMemory::NoError)
236                         {
237                                 const QString errorMessage = p->sharedmem->errorString();
238                                 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
239                                 return RET_FAILURE;
240                         }
241                         if(p->sharedmem->size() < sizeof(Internal::ipc_t))
242                         {
243                                 qWarning("Failed to attach to shared memory: Size verification has failed!");
244                                 return RET_FAILURE;
245                         }
246                         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
247                         {
248                                 if(memcmp(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN) != 0)
249                                 {
250                                         qWarning("Failed to attach to shared memory: Header verification has failed!");
251                                         return RET_FAILURE;
252                                 }
253                         }
254                         else
255                         {
256                                 const QString errorMessage = p->sharedmem->errorString();
257                                 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
258                                 return RET_FAILURE;
259                         }
260                         p->initialized.ref();
261                         return RET_SUCCESS_SLAVE;
262                 }
263                 else
264                 {
265                         const QString errorMessage = p->sharedmem->errorString();
266                         qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
267                         return RET_FAILURE;
268                 }
269         }
270         
271         if(p->sharedmem->error() != QSharedMemory::NoError)
272         {
273                 const QString errorMessage = p->sharedmem->errorString();
274                 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
275                 return RET_FAILURE;
276         }
277
278         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
279         {
280                 memset(ptr, 0, sizeof(Internal::ipc_t));
281                 memcpy(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN);
282                 UPDATE_CHECKSUM(ptr->status);
283         }
284         else
285         {
286                 const QString errorMessage = p->sharedmem->errorString();
287                 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
288                 return RET_FAILURE;
289         }
290
291         if(!p->semaphore_wr->release(Internal::IPC_SLOTS))
292         {
293                 const QString errorMessage = p->semaphore_wr->errorString();
294                 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
295                 return RET_FAILURE;
296         }
297         
298         //qDebug("IPC KEY #1: %s", MUTILS_UTF8(p->sharedmem->key()));
299         //qDebug("IPC KEY #2: %s", MUTILS_UTF8(p->semaphore_rd->key()));
300         //qDebug("IPC KEY #3: %s", MUTILS_UTF8(p->semaphore_wr->key()));
301
302         p->initialized.ref();
303         return RET_SUCCESS_MASTER;
304 }
305
306 ///////////////////////////////////////////////////////////////////////////////
307 // SEND MESSAGE
308 ///////////////////////////////////////////////////////////////////////////////
309
310 bool MUtils::IPCChannel::send(const quint32 &command, const quint32 &flags, const QStringList &params)
311 {
312         bool success = false;
313         QReadLocker readLock(&p->lock);
314
315         if(!p->initialized)
316         {
317                 MUTILS_THROW("Shared memory for IPC not initialized yet.");
318         }
319
320         if(!p->semaphore_wr->acquire())
321         {
322                 const QString errorMessage = p->semaphore_wr->errorString();
323                 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
324                 return false;
325         }
326
327         if(!p->sharedmem->lock())
328         {
329                 const QString errorMessage = p->sharedmem->errorString();
330                 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
331                 return false;
332         }
333
334         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
335         {
336                 if(VERIFY_CHECKSUM(ptr->status))
337                 {
338                         Internal::ipc_msg_t ipc_msg;
339                         memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
340
341                         ipc_msg.payload.command_id = command;
342                         ipc_msg.payload.flags = flags;
343                         if(!params.isEmpty())
344                         {
345                                 const quint32 param_count = qMin(MAX_PARAM_CNT, (quint32)params.count());
346                                 for(quint32 i = 0; i < param_count; i++)
347                                 {
348                                         strncpy_s(ipc_msg.payload.params.values[i], MAX_PARAM_LEN, MUTILS_UTF8(params[i].trimmed()), _TRUNCATE);
349                                 }
350                                 ipc_msg.payload.params.count = param_count;
351                         }
352                         ipc_msg.payload.timestamp = ptr->status.payload.counter++;
353                         UPDATE_CHECKSUM(ipc_msg);
354
355                         memcpy(&ptr->data[ptr->status.payload.pos_wr], &ipc_msg, sizeof(Internal::ipc_msg_t));
356                         ptr->status.payload.pos_wr = (ptr->status.payload.pos_wr + 1) % Internal::IPC_SLOTS;
357                         UPDATE_CHECKSUM(ptr->status);
358
359                         success = true;
360                 }
361                 else
362                 {
363                         qWarning("Corrupted IPC status detected -> skipping!");
364                 }
365         }
366         else
367         {
368                 qWarning("Shared memory pointer is NULL -> unable to write data!");
369         }
370
371         if(!p->sharedmem->unlock())
372         {
373                 const QString errorMessage = p->sharedmem->errorString();
374                 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
375         }
376
377         if(!p->semaphore_rd->release())
378         {
379                 const QString errorMessage = p->semaphore_rd->errorString();
380                 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
381         }
382
383         return success;
384 }
385
386 ///////////////////////////////////////////////////////////////////////////////
387 // READ MESSAGE
388 ///////////////////////////////////////////////////////////////////////////////
389
390 bool MUtils::IPCChannel::read(quint32 &command, quint32 &flags, QStringList &params)
391 {
392         bool success = false;
393         QReadLocker readLock(&p->lock);
394         command = 0;
395         params.clear();
396
397         if(!p->initialized)
398         {
399                 MUTILS_THROW("Shared memory for IPC not initialized yet.");
400         }
401
402         Internal::ipc_msg_t ipc_msg;
403         memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
404
405         if(!p->semaphore_rd->acquire())
406         {
407                 const QString errorMessage = p->semaphore_rd->errorString();
408                 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
409                 return false;
410         }
411
412         if(!p->sharedmem->lock())
413         {
414                 const QString errorMessage = p->sharedmem->errorString();
415                 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
416                 return false;
417         }
418
419         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
420         {
421                 if(VERIFY_CHECKSUM(ptr->status))
422                 {
423                         memcpy(&ipc_msg, &ptr->data[ptr->status.payload.pos_rd], sizeof(Internal::ipc_msg_t));
424                         ptr->status.payload.pos_rd = (ptr->status.payload.pos_rd + 1) % Internal::IPC_SLOTS;
425                         UPDATE_CHECKSUM(ptr->status);
426
427                         if(VERIFY_CHECKSUM(ipc_msg) || (ipc_msg.payload.timestamp < ptr->status.payload.counter))
428                         {
429                                 command = ipc_msg.payload.command_id;
430                                 flags = ipc_msg.payload.flags;
431                                 const quint32 param_count = qMin(ipc_msg.payload.params.count, MAX_PARAM_CNT);
432                                 char temp[MAX_PARAM_LEN];
433                                 for(quint32 i = 0; i < param_count; i++)
434                                 {
435                                         strncpy_s(temp, MAX_PARAM_LEN, ipc_msg.payload.params.values[i], _TRUNCATE);
436                                         params.append(QString::fromUtf8(temp));
437                                 }
438                                 success = true;
439                         }
440                         else
441                         {
442                                 qWarning("Malformed or corrupted IPC message, will be ignored!");
443                         }
444                 }
445                 else
446                 {
447                         qWarning("Corrupted IPC status detected -> skipping!");
448                 }
449         }
450         else
451         {
452                 qWarning("Shared memory pointer is NULL -> unable to read data!");
453         }
454
455         if(!p->sharedmem->unlock())
456         {
457                 const QString errorMessage = p->sharedmem->errorString();
458                 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
459         }
460
461         if(!p->semaphore_wr->release())
462         {
463                 const QString errorMessage = p->semaphore_wr->errorString();
464                 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
465         }
466
467         return success;
468 }