IPC status fields will be protected by an Adler-32 checksum too.
[MUtilities.git] / src / IPCChannel.cpp
blob403c21c58331ff8c9d12eee8ff202f49431bbccd
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.
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 //////////////////////////////////////////////////////////////////////////////////
22 //MUtils
23 #include <MUtils/IPCChannel.h>
24 #include <MUtils/Exception.h>
26 //Internal
27 #include "3rd_party/adler32/include/adler32.h"
29 //Qt includes
30 #include <QRegExp>
31 #include <QSharedMemory>
32 #include <QSystemSemaphore>
33 #include <QMutex>
34 #include <QWriteLocker>
35 #include <QCryptographicHash>
37 //CRT
38 #include <cassert>
40 static const quint32 ADLER_SEED = 0x5D90C356;
42 ///////////////////////////////////////////////////////////////////////////////
43 // TYPES
44 ///////////////////////////////////////////////////////////////////////////////
46 namespace MUtils
48 namespace Internal
50 static const size_t HDR_LEN = 40;
51 static const size_t IPC_SLOTS = 128;
53 typedef struct
55 quint64 counter;
56 quint32 pos_wr;
57 quint32 pos_rd;
59 ipc_status_data_t;
61 typedef struct
63 ipc_status_data_t payload;
64 quint32 checksum;
66 ipc_status_t;
68 typedef struct
70 quint32 command_id;
71 quint32 flags;
72 char param[MUtils::IPCChannel::MAX_MESSAGE_LEN];
73 quint64 timestamp;
75 ipc_msg_data_t;
77 typedef struct
79 ipc_msg_data_t payload;
80 quint32 checksum;
82 ipc_msg_t;
84 typedef struct
86 char header[HDR_LEN];
87 ipc_status_t status;
88 ipc_msg_t data[IPC_SLOTS];
90 ipc_t;
94 ///////////////////////////////////////////////////////////////////////////////
95 // UTILITIES
96 ///////////////////////////////////////////////////////////////////////////////
98 static QScopedPointer<QRegExp> g_escape_regexp;
99 static QMutex g_escape_lock;
101 #define ESCAPE(STR) (QString((STR)).replace(*g_escape_regexp, QLatin1String("_")).toLower())
103 static QString MAKE_ID(const QString &applicationId, const unsigned int &appVersionNo, const QString &channelId, const QString &itemId)
105 QMutexLocker locker(&g_escape_lock);
107 if(g_escape_regexp.isNull())
109 g_escape_regexp.reset(new QRegExp(QLatin1String("[^A-Za-z0-9_\\-]")));
112 return QString("com.muldersoft.mutilities.ipc.%1.r%2.%3.%4").arg(ESCAPE(applicationId), QString::number(appVersionNo, 16).toUpper(), ESCAPE(channelId), ESCAPE(itemId));
115 ///////////////////////////////////////////////////////////////////////////////
116 // PRIVATE DATA
117 ///////////////////////////////////////////////////////////////////////////////
119 namespace MUtils
121 class IPCChannel_Private
123 friend class IPCChannel;
125 protected:
126 volatile bool initialized;
127 QScopedPointer<QSharedMemory> sharedmem;
128 QScopedPointer<QSystemSemaphore> semaphore_rd;
129 QScopedPointer<QSystemSemaphore> semaphore_wr;
130 QReadWriteLock lock;
134 ///////////////////////////////////////////////////////////////////////////////
135 // CONSTRUCTOR & DESTRUCTOR
136 ///////////////////////////////////////////////////////////////////////////////
138 MUtils::IPCChannel::IPCChannel(const QString &applicationId, const quint32 &appVersionNo, const QString &channelId)
140 p(new IPCChannel_Private()),
141 m_applicationId(applicationId),
142 m_channelId(channelId),
143 m_appVersionNo(appVersionNo),
144 m_headerStr(QCryptographicHash::hash(MAKE_ID(applicationId, appVersionNo, channelId, "header").toLatin1(), QCryptographicHash::Sha1).toHex())
146 p->initialized = false;
147 if(m_headerStr.length() != Internal::HDR_LEN)
149 MUTILS_THROW("Invalid header length has been detected!");
153 MUtils::IPCChannel::~IPCChannel(void)
155 if(p->initialized)
157 if(p->sharedmem->isAttached())
159 p->sharedmem->detach();
163 delete p;
166 ///////////////////////////////////////////////////////////////////////////////
167 // INITIALIZATION
168 ///////////////////////////////////////////////////////////////////////////////
170 int MUtils::IPCChannel::initialize(void)
172 QWriteLocker writeLock(&p->lock);
174 if(p->initialized)
176 return RET_ALREADY_INITIALIZED;
179 p->sharedmem. reset(new QSharedMemory (MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "sharedmem"), 0));
180 p->semaphore_rd.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "semaph_rd"), 0));
181 p->semaphore_wr.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "semaph_wr"), 0));
183 if(p->semaphore_rd->error() != QSystemSemaphore::NoError)
185 const QString errorMessage = p->semaphore_rd->errorString();
186 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
187 return RET_FAILURE;
190 if(p->semaphore_wr->error() != QSystemSemaphore::NoError)
192 const QString errorMessage = p->semaphore_wr->errorString();
193 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
194 return RET_FAILURE;
197 if(!p->sharedmem->create(sizeof(Internal::ipc_t)))
199 if(p->sharedmem->error() == QSharedMemory::AlreadyExists)
201 if(!p->sharedmem->attach())
203 const QString errorMessage = p->sharedmem->errorString();
204 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
205 return RET_FAILURE;
207 if(p->sharedmem->error() != QSharedMemory::NoError)
209 const QString errorMessage = p->sharedmem->errorString();
210 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
211 return RET_FAILURE;
213 if(p->sharedmem->size() < sizeof(Internal::ipc_t))
215 qWarning("Failed to attach to shared memory: Size verification has failed!");
216 return RET_FAILURE;
218 if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
220 if(memcmp(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN) != 0)
222 qWarning("Failed to attach to shared memory: Header verification has failed!");
223 return RET_FAILURE;
226 else
228 const QString errorMessage = p->sharedmem->errorString();
229 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
230 return RET_FAILURE;
232 p->initialized = true;
233 return RET_SUCCESS_SLAVE;
235 else
237 const QString errorMessage = p->sharedmem->errorString();
238 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
239 return RET_FAILURE;
243 if(p->sharedmem->error() != QSharedMemory::NoError)
245 const QString errorMessage = p->sharedmem->errorString();
246 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
247 return RET_FAILURE;
250 if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
252 memset(ptr, 0, sizeof(Internal::ipc_t));
253 memcpy(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN);
254 ptr->status.checksum = Internal::adler32(ADLER_SEED, &ptr->status.payload, sizeof(Internal::ipc_status_data_t));
256 else
258 const QString errorMessage = p->sharedmem->errorString();
259 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
260 return RET_FAILURE;
263 if(!p->semaphore_wr->release(Internal::IPC_SLOTS))
265 const QString errorMessage = p->semaphore_wr->errorString();
266 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
267 return RET_FAILURE;
270 //qDebug("IPC KEY #1: %s", MUTILS_UTF8(p->sharedmem->key()));
271 //qDebug("IPC KEY #2: %s", MUTILS_UTF8(p->semaphore_rd->key()));
272 //qDebug("IPC KEY #3: %s", MUTILS_UTF8(p->semaphore_wr->key()));
274 p->initialized = true;
275 return RET_SUCCESS_MASTER;
278 ///////////////////////////////////////////////////////////////////////////////
279 // SEND MESSAGE
280 ///////////////////////////////////////////////////////////////////////////////
282 bool MUtils::IPCChannel::send(const quint32 &command, const quint32 &flags, const char *const message)
284 bool success = false;
285 QReadLocker readLock(&p->lock);
287 if(!p->initialized)
289 MUTILS_THROW("Shared memory for IPC not initialized yet.");
292 if(!p->semaphore_wr->acquire())
294 const QString errorMessage = p->semaphore_wr->errorString();
295 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
296 return false;
299 if(!p->sharedmem->lock())
301 const QString errorMessage = p->sharedmem->errorString();
302 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
303 return false;
306 if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
308 const quint32 status_checksum = Internal::adler32(ADLER_SEED, &ptr->status.payload, sizeof(Internal::ipc_status_data_t));
309 if(status_checksum == ptr->status.checksum)
311 Internal::ipc_msg_t ipc_msg;
312 memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
314 ipc_msg.payload.command_id = command;
315 ipc_msg.payload.flags = flags;
316 if(message)
318 strncpy_s(ipc_msg.payload.param, MAX_MESSAGE_LEN, message, _TRUNCATE);
320 ipc_msg.payload.timestamp = ptr->status.payload.counter++;
321 ipc_msg.checksum = Internal::adler32(ADLER_SEED, &ipc_msg.payload, sizeof(Internal::ipc_msg_data_t));
323 memcpy(&ptr->data[ptr->status.payload.pos_wr], &ipc_msg, sizeof(Internal::ipc_msg_t));
324 ptr->status.payload.pos_wr = (ptr->status.payload.pos_wr + 1) % Internal::IPC_SLOTS;
325 ptr->status.checksum = Internal::adler32(ADLER_SEED, &ptr->status.payload, sizeof(Internal::ipc_status_data_t));
327 success = true;
329 else
331 qWarning("Corrupted IPC status detected -> skipping!");
334 else
336 qWarning("Shared memory pointer is NULL -> unable to write data!");
339 if(!p->sharedmem->unlock())
341 const QString errorMessage = p->sharedmem->errorString();
342 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
345 if(!p->semaphore_rd->release())
347 const QString errorMessage = p->semaphore_rd->errorString();
348 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
351 return success;
354 ///////////////////////////////////////////////////////////////////////////////
355 // READ MESSAGE
356 ///////////////////////////////////////////////////////////////////////////////
358 bool MUtils::IPCChannel::read(quint32 &command, quint32 &flags, char *const message, const size_t &buffSize)
360 bool success = false;
361 QReadLocker readLock(&p->lock);
363 command = 0;
364 if(message && (buffSize > 0))
366 message[0] = '\0';
369 if(!p->initialized)
371 MUTILS_THROW("Shared memory for IPC not initialized yet.");
374 Internal::ipc_msg_t ipc_msg;
375 memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
377 if(!p->semaphore_rd->acquire())
379 const QString errorMessage = p->semaphore_rd->errorString();
380 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
381 return false;
384 if(!p->sharedmem->lock())
386 const QString errorMessage = p->sharedmem->errorString();
387 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
388 return false;
391 if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
393 const quint32 status_checksum = Internal::adler32(ADLER_SEED, &ptr->status.payload, sizeof(Internal::ipc_status_data_t));
394 if(status_checksum == ptr->status.checksum)
396 memcpy(&ipc_msg, &ptr->data[ptr->status.payload.pos_rd], sizeof(Internal::ipc_msg_t));
397 ptr->status.payload.pos_rd = (ptr->status.payload.pos_rd + 1) % Internal::IPC_SLOTS;
398 ptr->status.checksum = Internal::adler32(ADLER_SEED, &ptr->status.payload, sizeof(Internal::ipc_status_data_t));
400 const quint32 msg_checksum = Internal::adler32(ADLER_SEED, &ipc_msg.payload, sizeof(Internal::ipc_msg_data_t));
401 if((msg_checksum == ipc_msg.checksum) || (ipc_msg.payload.timestamp < ptr->status.payload.counter))
403 command = ipc_msg.payload.command_id;
404 flags = ipc_msg.payload.flags;
405 strncpy_s(message, buffSize, ipc_msg.payload.param, _TRUNCATE);
406 success = true;
408 else
410 qWarning("Malformed or corrupted IPC message, will be ignored!");
413 else
415 qWarning("Corrupted IPC status detected -> skipping!");
418 else
420 qWarning("Shared memory pointer is NULL -> unable to read data!");
423 if(!p->sharedmem->unlock())
425 const QString errorMessage = p->sharedmem->errorString();
426 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
429 if(!p->semaphore_wr->release())
431 const QString errorMessage = p->semaphore_wr->errorString();
432 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
435 return success;