1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2015 LoRd_MuldeR <MuldeR2@GMX.de>
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.
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.
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
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
23 #include <MUtils/IPCChannel.h>
24 #include <MUtils/Exception.h>
27 #include "3rd_party/adler32/include/adler32.h"
31 #include <QSharedMemory>
32 #include <QSystemSemaphore>
34 #include <QWriteLocker>
35 #include <QCryptographicHash>
40 ///////////////////////////////////////////////////////////////////////////////
42 ///////////////////////////////////////////////////////////////////////////////
48 static const quint32 ADLER_SEED = 0x5D90C356;
51 static inline void UPDATE_CHECKSUM(T &data)
53 data.checksum = Internal::adler32(ADLER_SEED, &data.payload, sizeof(data.payload));
57 static inline bool VERIFY_CHECKSUM(const T &data)
59 return (data.checksum == Internal::adler32(ADLER_SEED, &data.payload, sizeof(data.payload)));
64 ///////////////////////////////////////////////////////////////////////////////
66 ///////////////////////////////////////////////////////////////////////////////
72 static const size_t HDR_LEN = 40;
73 static const size_t IPC_SLOTS = 128;
85 ipc_status_data_t payload;
94 char param[MUtils::IPCChannel::MAX_MESSAGE_LEN];
101 ipc_msg_data_t payload;
108 char header[HDR_LEN];
110 ipc_msg_t data[IPC_SLOTS];
116 ///////////////////////////////////////////////////////////////////////////////
118 ///////////////////////////////////////////////////////////////////////////////
120 static QScopedPointer<QRegExp> g_escape_regexp;
121 static QMutex g_escape_lock;
123 #define ESCAPE(STR) (QString((STR)).replace(*g_escape_regexp, QLatin1String("_")).toLower())
125 static QString MAKE_ID(const QString &applicationId, const unsigned int &appVersionNo, const QString &channelId, const QString &itemId)
127 QMutexLocker locker(&g_escape_lock);
129 if(g_escape_regexp.isNull())
131 g_escape_regexp.reset(new QRegExp(QLatin1String("[^A-Za-z0-9_\\-]")));
134 return QString("com.muldersoft.mutilities.ipc.%1.r%2.%3.%4").arg(ESCAPE(applicationId), QString::number(appVersionNo, 16).toUpper(), ESCAPE(channelId), ESCAPE(itemId));
137 ///////////////////////////////////////////////////////////////////////////////
139 ///////////////////////////////////////////////////////////////////////////////
143 class IPCChannel_Private
145 friend class IPCChannel;
148 volatile bool initialized;
149 QScopedPointer<QSharedMemory> sharedmem;
150 QScopedPointer<QSystemSemaphore> semaphore_rd;
151 QScopedPointer<QSystemSemaphore> semaphore_wr;
156 ///////////////////////////////////////////////////////////////////////////////
157 // CONSTRUCTOR & DESTRUCTOR
158 ///////////////////////////////////////////////////////////////////////////////
160 MUtils::IPCChannel::IPCChannel(const QString &applicationId, const quint32 &appVersionNo, const QString &channelId)
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())
168 p->initialized = false;
169 if(m_headerStr.length() != Internal::HDR_LEN)
171 MUTILS_THROW("Invalid header length has been detected!");
175 MUtils::IPCChannel::~IPCChannel(void)
179 if(p->sharedmem->isAttached())
181 p->sharedmem->detach();
188 ///////////////////////////////////////////////////////////////////////////////
190 ///////////////////////////////////////////////////////////////////////////////
192 int MUtils::IPCChannel::initialize(void)
194 QWriteLocker writeLock(&p->lock);
198 return RET_ALREADY_INITIALIZED;
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));
205 if(p->semaphore_rd->error() != QSystemSemaphore::NoError)
207 const QString errorMessage = p->semaphore_rd->errorString();
208 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
212 if(p->semaphore_wr->error() != QSystemSemaphore::NoError)
214 const QString errorMessage = p->semaphore_wr->errorString();
215 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
219 if(!p->sharedmem->create(sizeof(Internal::ipc_t)))
221 if(p->sharedmem->error() == QSharedMemory::AlreadyExists)
223 if(!p->sharedmem->attach())
225 const QString errorMessage = p->sharedmem->errorString();
226 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
229 if(p->sharedmem->error() != QSharedMemory::NoError)
231 const QString errorMessage = p->sharedmem->errorString();
232 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
235 if(p->sharedmem->size() < sizeof(Internal::ipc_t))
237 qWarning("Failed to attach to shared memory: Size verification has failed!");
240 if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
242 if(memcmp(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN) != 0)
244 qWarning("Failed to attach to shared memory: Header verification has failed!");
250 const QString errorMessage = p->sharedmem->errorString();
251 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
254 p->initialized = true;
255 return RET_SUCCESS_SLAVE;
259 const QString errorMessage = p->sharedmem->errorString();
260 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
265 if(p->sharedmem->error() != QSharedMemory::NoError)
267 const QString errorMessage = p->sharedmem->errorString();
268 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
272 if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
274 memset(ptr, 0, sizeof(Internal::ipc_t));
275 memcpy(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN);
276 UPDATE_CHECKSUM(ptr->status);
280 const QString errorMessage = p->sharedmem->errorString();
281 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
285 if(!p->semaphore_wr->release(Internal::IPC_SLOTS))
287 const QString errorMessage = p->semaphore_wr->errorString();
288 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
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()));
296 p->initialized = true;
297 return RET_SUCCESS_MASTER;
300 ///////////////////////////////////////////////////////////////////////////////
302 ///////////////////////////////////////////////////////////////////////////////
304 bool MUtils::IPCChannel::send(const quint32 &command, const quint32 &flags, const char *const message)
306 bool success = false;
307 QReadLocker readLock(&p->lock);
311 MUTILS_THROW("Shared memory for IPC not initialized yet.");
314 if(!p->semaphore_wr->acquire())
316 const QString errorMessage = p->semaphore_wr->errorString();
317 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
321 if(!p->sharedmem->lock())
323 const QString errorMessage = p->sharedmem->errorString();
324 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
328 if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
330 if(VERIFY_CHECKSUM(ptr->status))
332 Internal::ipc_msg_t ipc_msg;
333 memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
335 ipc_msg.payload.command_id = command;
336 ipc_msg.payload.flags = flags;
339 strncpy_s(ipc_msg.payload.param, MAX_MESSAGE_LEN, message, _TRUNCATE);
341 ipc_msg.payload.timestamp = ptr->status.payload.counter++;
342 UPDATE_CHECKSUM(ipc_msg);
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);
352 qWarning("Corrupted IPC status detected -> skipping!");
357 qWarning("Shared memory pointer is NULL -> unable to write data!");
360 if(!p->sharedmem->unlock())
362 const QString errorMessage = p->sharedmem->errorString();
363 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
366 if(!p->semaphore_rd->release())
368 const QString errorMessage = p->semaphore_rd->errorString();
369 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
375 ///////////////////////////////////////////////////////////////////////////////
377 ///////////////////////////////////////////////////////////////////////////////
379 bool MUtils::IPCChannel::read(quint32 &command, quint32 &flags, char *const message, const size_t &buffSize)
381 bool success = false;
382 QReadLocker readLock(&p->lock);
385 if(message && (buffSize > 0))
392 MUTILS_THROW("Shared memory for IPC not initialized yet.");
395 Internal::ipc_msg_t ipc_msg;
396 memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
398 if(!p->semaphore_rd->acquire())
400 const QString errorMessage = p->semaphore_rd->errorString();
401 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
405 if(!p->sharedmem->lock())
407 const QString errorMessage = p->sharedmem->errorString();
408 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
412 if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
414 if(VERIFY_CHECKSUM(ptr->status))
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);
420 if(VERIFY_CHECKSUM(ipc_msg) || (ipc_msg.payload.timestamp < ptr->status.payload.counter))
422 command = ipc_msg.payload.command_id;
423 flags = ipc_msg.payload.flags;
424 strncpy_s(message, buffSize, ipc_msg.payload.param, _TRUNCATE);
429 qWarning("Malformed or corrupted IPC message, will be ignored!");
434 qWarning("Corrupted IPC status detected -> skipping!");
439 qWarning("Shared memory pointer is NULL -> unable to read data!");
442 if(!p->sharedmem->unlock())
444 const QString errorMessage = p->sharedmem->errorString();
445 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
448 if(!p->semaphore_wr->release())
450 const QString errorMessage = p->semaphore_wr->errorString();
451 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));