--- /dev/null
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2011 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+ version 1.2.8, April 28th, 2013
+
+ Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+
+ The data format used by the zlib library is described by RFCs (Request for
+ Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+ (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#include "../include/adler32.h"
+
+#define local static
+#define uLong uint32_t
+#define uInt uint32_t
+#define Bytef unsigned char
+#define z_off_t int32_t
+#define z_off64_t int64_t
+#define Z_NULL NULL
+#define ZEXPORT
+
+#ifndef OF /* function prototypes */
+# ifdef STDC
+# define OF(args) args
+# else
+# define OF(args) ()
+# endif
+#endif
+
+#define BASE 65521 /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;}
+#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf) DO8(buf,0); DO8(buf,8);
+
+#define MOD(a) a %= BASE
+#define MOD28(a) a %= BASE
+#define MOD63(a) a %= BASE
+
+/* ========================================================================= */
+uLong ZEXPORT MUtils::Internal::adler32(uLong adler, const Bytef *buf, uInt len)
+{
+ unsigned long sum2;
+ unsigned n;
+
+ /* split Adler-32 into component sums */
+ sum2 = (adler >> 16) & 0xffff;
+ adler &= 0xffff;
+
+ /* in case user likes doing a byte at a time, keep it fast */
+ if (len == 1) {
+ adler += buf[0];
+ if (adler >= BASE)
+ adler -= BASE;
+ sum2 += adler;
+ if (sum2 >= BASE)
+ sum2 -= BASE;
+ return adler | (sum2 << 16);
+ }
+
+ /* initial Adler-32 value (deferred check for len == 1 speed) */
+ if (buf == Z_NULL)
+ return 1L;
+
+ /* in case short lengths are provided, keep it somewhat fast */
+ if (len < 16) {
+ while (len--) {
+ adler += *buf++;
+ sum2 += adler;
+ }
+ if (adler >= BASE)
+ adler -= BASE;
+ MOD28(sum2); /* only added so many BASE's */
+ return adler | (sum2 << 16);
+ }
+
+ /* do length NMAX blocks -- requires just one modulo operation */
+ while (len >= NMAX) {
+ len -= NMAX;
+ n = NMAX / 16; /* NMAX is divisible by 16 */
+ do {
+ DO16(buf); /* 16 sums unrolled */
+ buf += 16;
+ } while (--n);
+ MOD(adler);
+ MOD(sum2);
+ }
+
+ /* do remaining bytes (less than NMAX, still just one modulo) */
+ if (len) { /* avoid modulos if none remaining */
+ while (len >= 16) {
+ len -= 16;
+ DO16(buf);
+ buf += 16;
+ }
+ while (len--) {
+ adler += *buf++;
+ sum2 += adler;
+ }
+ MOD(adler);
+ MOD(sum2);
+ }
+
+ /* return recombined sums */
+ return adler | (sum2 << 16);
+}
#include <MUtils/IPCChannel.h>
#include <MUtils/Exception.h>
+//Internal
+#include "3rd_party/adler32/include/adler32.h"
+
//Qt includes
#include <QRegExp>
#include <QSharedMemory>
#include <QSystemSemaphore>
+#include <QMutex>
#include <QWriteLocker>
+#include <QCryptographicHash>
+
+//CRT
+#include <cassert>
+
+static const quint32 ADLER_SEED = 0x5D90C356;
///////////////////////////////////////////////////////////////////////////////
// TYPES
namespace MUtils
{
- static const size_t IPC_SLOTS = 128;
- static const size_t MAX_MESSAGE_LEN = 4096;
-
- typedef struct
+ namespace Internal
{
- unsigned int command;
- unsigned int reserved_1;
- unsigned int reserved_2;
- char parameter[MAX_MESSAGE_LEN];
- }
- ipc_data_t;
+ static const size_t HDR_LEN = 40;
+ static const size_t IPC_SLOTS = 128;
- typedef struct
- {
- unsigned int pos_wr;
- unsigned int pos_rd;
- ipc_data_t data[IPC_SLOTS];
+ typedef struct
+ {
+ quint32 command_id;
+ quint32 flags;
+ char param[MUtils::IPCChannel::MAX_MESSAGE_LEN];
+ quint64 timestamp;
+ }
+ ipc_data_t;
+
+ typedef struct
+ {
+ ipc_data_t payload;
+ quint32 checksum;
+ }
+ ipc_msg_t;
+
+ typedef struct
+ {
+ char header[HDR_LEN];
+ quint64 counter;
+ quint32 pos_wr;
+ quint32 pos_rd;
+ ipc_msg_t data[IPC_SLOTS];
+ }
+ ipc_t;
}
- ipc_t;
}
///////////////////////////////////////////////////////////////////////////////
// UTILITIES
///////////////////////////////////////////////////////////////////////////////
-static inline QString ESCAPE(QString str)
-{
- return str.replace(QRegExp("[^A-Za-z0-9_]"), "_").toLower();
-}
+static QScopedPointer<QRegExp> g_escape_regexp;
+static QMutex g_escape_lock;
+
+#define ESCAPE(STR) (QString((STR)).replace(*g_escape_regexp, QLatin1String("_")).toLower())
-static QString MAKE_ID(const QString &applicationId, const QString &channelId, const QString &itemId)
+static QString MAKE_ID(const QString &applicationId, const unsigned int &appVersionNo, const QString &channelId, const QString &itemId)
{
- return QString("ipc://mutilities.muldersoft.com:37402/%1/%2/%3").arg(ESCAPE(applicationId), ESCAPE(channelId), ESCAPE(itemId));
+ QMutexLocker locker(&g_escape_lock);
+
+ if(g_escape_regexp.isNull())
+ {
+ g_escape_regexp.reset(new QRegExp(QLatin1String("[^A-Za-z0-9_\\-]")));
+ }
+
+ return QString("com.muldersoft.mutilities.ipc.%1.r%2.%3.%4").arg(ESCAPE(applicationId), QString::number(appVersionNo, 16).toUpper(), ESCAPE(channelId), ESCAPE(itemId));
}
///////////////////////////////////////////////////////////////////////////////
// CONSTRUCTOR & DESTRUCTOR
///////////////////////////////////////////////////////////////////////////////
-MUtils::IPCChannel::IPCChannel(const QString &applicationId, const QString &channelId)
+MUtils::IPCChannel::IPCChannel(const QString &applicationId, const quint32 &appVersionNo, const QString &channelId)
:
p(new IPCChannel_Private()),
m_applicationId(applicationId),
- m_channelId(channelId)
+ m_channelId(channelId),
+ m_appVersionNo(appVersionNo),
+ m_headerStr(QCryptographicHash::hash(MAKE_ID(applicationId, appVersionNo, channelId, "header").toLatin1(), QCryptographicHash::Sha1).toHex())
{
+ assert(m_headerStr.length() == HDR_LEN);
p->initialized = false;
}
if(p->initialized)
{
- return IPC_RET_ALREADY_INITIALIZED;
+ return RET_ALREADY_INITIALIZED;
}
- p->sharedmem.reset(new QSharedMemory(MAKE_ID(m_applicationId, m_channelId, "sharedmem"), NULL));
- p->semaphore_rd.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_channelId, "semaphore_rd"), 0));
- p->semaphore_wr.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_channelId, "semaphore_wr"), 0));
+ p->sharedmem. reset(new QSharedMemory (MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "sharedmem"), 0));
+ p->semaphore_rd.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "semaph_rd"), 0));
+ p->semaphore_wr.reset(new QSystemSemaphore(MAKE_ID(m_applicationId, m_appVersionNo, m_channelId, "semaph_wr"), 0));
if(p->semaphore_rd->error() != QSystemSemaphore::NoError)
{
const QString errorMessage = p->semaphore_rd->errorString();
qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
- return IPC_RET_FAILURE;
+ return RET_FAILURE;
}
if(p->semaphore_wr->error() != QSystemSemaphore::NoError)
{
const QString errorMessage = p->semaphore_wr->errorString();
qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
- return IPC_RET_FAILURE;
+ return RET_FAILURE;
}
- if(!p->sharedmem->create(sizeof(ipc_t)))
+ if(!p->sharedmem->create(sizeof(Internal::ipc_t)))
{
if(p->sharedmem->error() == QSharedMemory::AlreadyExists)
{
{
const QString errorMessage = p->sharedmem->errorString();
qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
- return IPC_RET_FAILURE;
+ return RET_FAILURE;
}
if(p->sharedmem->error() != QSharedMemory::NoError)
{
const QString errorMessage = p->sharedmem->errorString();
qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
- return IPC_RET_FAILURE;
+ return RET_FAILURE;
+ }
+ if(p->sharedmem->size() < sizeof(Internal::ipc_t))
+ {
+ qWarning("Failed to attach to shared memory: Size verification has failed!");
+ return RET_FAILURE;
+ }
+ if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
+ {
+ if(memcmp(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN) != 0)
+ {
+ qWarning("Failed to attach to shared memory: Header verification has failed!");
+ return RET_FAILURE;
+ }
+ }
+ else
+ {
+ const QString errorMessage = p->sharedmem->errorString();
+ qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
+ return RET_FAILURE;
}
p->initialized = true;
- return IPC_RET_SUCCESS_SLAVE;
+ return RET_SUCCESS_SLAVE;
}
else
{
const QString errorMessage = p->sharedmem->errorString();
qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
- return IPC_RET_FAILURE;
+ return RET_FAILURE;
}
}
{
const QString errorMessage = p->sharedmem->errorString();
qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
- return IPC_RET_FAILURE;
+ return RET_FAILURE;
}
- if(void *const data = p->sharedmem->data())
+ if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
+ {
+ memset(ptr, 0, sizeof(Internal::ipc_t));
+ memcpy(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN);
+ }
+ else
{
- memset(data, 0, sizeof(ipc_t));
+ const QString errorMessage = p->sharedmem->errorString();
+ qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
+ return RET_FAILURE;
}
- if(!p->semaphore_wr->release(IPC_SLOTS))
+ if(!p->semaphore_wr->release(Internal::IPC_SLOTS))
{
const QString errorMessage = p->semaphore_wr->errorString();
qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
- return IPC_RET_FAILURE;
+ return RET_FAILURE;
}
+ //qDebug("IPC KEY #1: %s", MUTILS_UTF8(p->sharedmem->key()));
+ //qDebug("IPC KEY #2: %s", MUTILS_UTF8(p->semaphore_rd->key()));
+ //qDebug("IPC KEY #3: %s", MUTILS_UTF8(p->semaphore_wr->key()));
+
p->initialized = true;
- return IPC_RET_SUCCESS_MASTER;
+ return RET_SUCCESS_MASTER;
}
///////////////////////////////////////////////////////////////////////////////
// SEND MESSAGE
///////////////////////////////////////////////////////////////////////////////
-bool MUtils::IPCChannel::send(const unsigned int &command, const char *const message)
+bool MUtils::IPCChannel::send(const quint32 &command, const quint32 &flags, const char *const message)
{
bool success = false;
QReadLocker readLock(&p->lock);
MUTILS_THROW("Shared memory for IPC not initialized yet.");
}
- ipc_data_t ipc_data;
- memset(&ipc_data, 0, sizeof(ipc_data_t));
- ipc_data.command = command;
-
- if(message)
- {
- strncpy_s(ipc_data.parameter, MAX_MESSAGE_LEN, message, _TRUNCATE);
- }
-
if(!p->semaphore_wr->acquire())
{
const QString errorMessage = p->semaphore_wr->errorString();
return false;
}
- if(ipc_t *const ptr = reinterpret_cast<ipc_t*>(p->sharedmem->data()))
+ if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
{
+ Internal::ipc_msg_t ipc_msg;
+ memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
+
+ ipc_msg.payload.command_id = command;
+ ipc_msg.payload.flags = flags;
+ if(message)
+ {
+ strncpy_s(ipc_msg.payload.param, MAX_MESSAGE_LEN, message, _TRUNCATE);
+ }
+ ipc_msg.payload.timestamp = ptr->counter++;
+ ipc_msg.checksum = Internal::adler32(ADLER_SEED, &ipc_msg.payload, sizeof(Internal::ipc_data_t));
+
+ memcpy(&ptr->data[ptr->pos_wr], &ipc_msg, sizeof(Internal::ipc_msg_t));
+ ptr->pos_wr = (ptr->pos_wr + 1) % Internal::IPC_SLOTS;
success = true;
- memcpy(&ptr->data[ptr->pos_wr], &ipc_data, sizeof(ipc_data_t));
- ptr->pos_wr = (ptr->pos_wr + 1) % IPC_SLOTS;
}
else
{
if(!p->sharedmem->unlock())
{
const QString errorMessage = p->sharedmem->errorString();
- qWarning("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
- return false;
+ qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
}
if(!p->semaphore_rd->release())
{
const QString errorMessage = p->semaphore_rd->errorString();
- qWarning("Failed to acquire release semaphore: %s", MUTILS_UTF8(errorMessage));
- return false;
+ qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
}
return success;
// READ MESSAGE
///////////////////////////////////////////////////////////////////////////////
-bool MUtils::IPCChannel::read(unsigned int &command, char *const message, const size_t &buffSize)
+bool MUtils::IPCChannel::read(quint32 &command, quint32 &flags, char *const message, const size_t &buffSize)
{
bool success = false;
QReadLocker readLock(&p->lock);
MUTILS_THROW("Shared memory for IPC not initialized yet.");
}
- ipc_data_t ipc_data;
- memset(&ipc_data, 0, sizeof(ipc_data_t));
+ Internal::ipc_msg_t ipc_msg;
+ memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
if(!p->semaphore_rd->acquire())
{
return false;
}
- if(ipc_t *const ptr = reinterpret_cast<ipc_t*>(p->sharedmem->data()))
+ if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
{
- success = true;
- memcpy(&ipc_data, &ptr->data[ptr->pos_rd], sizeof(ipc_data_t));
- ptr->pos_rd = (ptr->pos_rd + 1) % IPC_SLOTS;
+ success = true;
+ memcpy(&ipc_msg, &ptr->data[ptr->pos_rd], sizeof(Internal::ipc_msg_t));
+ ptr->pos_rd = (ptr->pos_rd + 1) % Internal::IPC_SLOTS;
- if(!(ipc_data.reserved_1 || ipc_data.reserved_2))
+ const quint32 expected_checksum = Internal::adler32(ADLER_SEED, &ipc_msg.payload, sizeof(Internal::ipc_data_t));
+ if((expected_checksum == ipc_msg.checksum) || (ipc_msg.payload.timestamp < ptr->counter))
{
- command = ipc_data.command;
- strncpy_s(message, buffSize, ipc_data.parameter, _TRUNCATE);
+ command = ipc_msg.payload.command_id;
+ flags = ipc_msg.payload.flags;
+ strncpy_s(message, buffSize, ipc_msg.payload.param, _TRUNCATE);
}
else
{
- qWarning("Malformed IPC message, will be ignored");
+ qWarning("Malformed or corrupted IPC message, will be ignored");
}
}
else
if(!p->sharedmem->unlock())
{
const QString errorMessage = p->sharedmem->errorString();
- qWarning("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
- return false;
+ qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
}
if(!p->semaphore_wr->release())
{
const QString errorMessage = p->semaphore_wr->errorString();
- qWarning("Failed to acquire release semaphore: %s", MUTILS_UTF8(errorMessage));
- return false;
+ qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
}
return success;