1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2016 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>
36 #include <QStringList>
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
;
92 char values
[MUtils::IPCChannel::MAX_PARAM_CNT
][MUtils::IPCChannel::MAX_PARAM_LEN
];
95 ipc_msg_data_params_t
;
101 ipc_msg_data_params_t params
;
108 ipc_msg_data_t payload
;
115 char header
[HDR_LEN
];
117 ipc_msg_t data
[IPC_SLOTS
];
123 ///////////////////////////////////////////////////////////////////////////////
125 ///////////////////////////////////////////////////////////////////////////////
127 static QScopedPointer
<QRegExp
> g_escape_regexp
;
128 static QMutex g_escape_lock
;
130 #define ESCAPE(STR) (QString((STR)).replace(*g_escape_regexp, QLatin1String("_")).toLower())
132 static QString
MAKE_ID(const QString
&applicationId
, const unsigned int &appVersionNo
, const QString
&channelId
, const QString
&itemId
)
134 QMutexLocker
locker(&g_escape_lock
);
136 if(g_escape_regexp
.isNull())
138 g_escape_regexp
.reset(new QRegExp(QLatin1String("[^A-Za-z0-9_\\-]")));
141 return QString("com.muldersoft.mutilities.ipc.%1.r%2.%3.%4").arg(ESCAPE(applicationId
), QString::number(appVersionNo
, 16).toUpper(), ESCAPE(channelId
), ESCAPE(itemId
));
144 ///////////////////////////////////////////////////////////////////////////////
146 ///////////////////////////////////////////////////////////////////////////////
150 class IPCChannel_Private
152 friend class IPCChannel
;
155 volatile bool initialized
;
156 QScopedPointer
<QSharedMemory
> sharedmem
;
157 QScopedPointer
<QSystemSemaphore
> semaphore_rd
;
158 QScopedPointer
<QSystemSemaphore
> semaphore_wr
;
163 ///////////////////////////////////////////////////////////////////////////////
164 // CONSTRUCTOR & DESTRUCTOR
165 ///////////////////////////////////////////////////////////////////////////////
167 MUtils::IPCChannel::IPCChannel(const QString
&applicationId
, const quint32
&appVersionNo
, const QString
&channelId
)
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())
175 p
->initialized
= false;
176 if(m_headerStr
.length() != Internal::HDR_LEN
)
178 MUTILS_THROW("Invalid header length has been detected!");
182 MUtils::IPCChannel::~IPCChannel(void)
186 if(p
->sharedmem
->isAttached())
188 p
->sharedmem
->detach();
195 ///////////////////////////////////////////////////////////////////////////////
197 ///////////////////////////////////////////////////////////////////////////////
199 int MUtils::IPCChannel::initialize(void)
201 QWriteLocker
writeLock(&p
->lock
);
205 return RET_ALREADY_INITIALIZED
;
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));
212 if(p
->semaphore_rd
->error() != QSystemSemaphore::NoError
)
214 const QString errorMessage
= p
->semaphore_rd
->errorString();
215 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage
));
219 if(p
->semaphore_wr
->error() != QSystemSemaphore::NoError
)
221 const QString errorMessage
= p
->semaphore_wr
->errorString();
222 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage
));
226 if(!p
->sharedmem
->create(sizeof(Internal::ipc_t
)))
228 if(p
->sharedmem
->error() == QSharedMemory::AlreadyExists
)
230 if(!p
->sharedmem
->attach())
232 const QString errorMessage
= p
->sharedmem
->errorString();
233 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage
));
236 if(p
->sharedmem
->error() != QSharedMemory::NoError
)
238 const QString errorMessage
= p
->sharedmem
->errorString();
239 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage
));
242 if(p
->sharedmem
->size() < sizeof(Internal::ipc_t
))
244 qWarning("Failed to attach to shared memory: Size verification has failed!");
247 if(Internal::ipc_t
*const ptr
= reinterpret_cast<Internal::ipc_t
*>(p
->sharedmem
->data()))
249 if(memcmp(&ptr
->header
[0], m_headerStr
.constData(), Internal::HDR_LEN
) != 0)
251 qWarning("Failed to attach to shared memory: Header verification has failed!");
257 const QString errorMessage
= p
->sharedmem
->errorString();
258 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage
));
261 p
->initialized
= true;
262 return RET_SUCCESS_SLAVE
;
266 const QString errorMessage
= p
->sharedmem
->errorString();
267 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage
));
272 if(p
->sharedmem
->error() != QSharedMemory::NoError
)
274 const QString errorMessage
= p
->sharedmem
->errorString();
275 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage
));
279 if(Internal::ipc_t
*const ptr
= reinterpret_cast<Internal::ipc_t
*>(p
->sharedmem
->data()))
281 memset(ptr
, 0, sizeof(Internal::ipc_t
));
282 memcpy(&ptr
->header
[0], m_headerStr
.constData(), Internal::HDR_LEN
);
283 UPDATE_CHECKSUM(ptr
->status
);
287 const QString errorMessage
= p
->sharedmem
->errorString();
288 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage
));
292 if(!p
->semaphore_wr
->release(Internal::IPC_SLOTS
))
294 const QString errorMessage
= p
->semaphore_wr
->errorString();
295 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage
));
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()));
303 p
->initialized
= true;
304 return RET_SUCCESS_MASTER
;
307 ///////////////////////////////////////////////////////////////////////////////
309 ///////////////////////////////////////////////////////////////////////////////
311 bool MUtils::IPCChannel::send(const quint32
&command
, const quint32
&flags
, const QStringList
¶ms
)
313 bool success
= false;
314 QReadLocker
readLock(&p
->lock
);
318 MUTILS_THROW("Shared memory for IPC not initialized yet.");
321 if(!p
->semaphore_wr
->acquire())
323 const QString errorMessage
= p
->semaphore_wr
->errorString();
324 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage
));
328 if(!p
->sharedmem
->lock())
330 const QString errorMessage
= p
->sharedmem
->errorString();
331 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage
));
335 if(Internal::ipc_t
*const ptr
= reinterpret_cast<Internal::ipc_t
*>(p
->sharedmem
->data()))
337 if(VERIFY_CHECKSUM(ptr
->status
))
339 Internal::ipc_msg_t ipc_msg
;
340 memset(&ipc_msg
, 0, sizeof(Internal::ipc_msg_t
));
342 ipc_msg
.payload
.command_id
= command
;
343 ipc_msg
.payload
.flags
= flags
;
344 if(!params
.isEmpty())
346 const quint32 param_count
= qMin(MAX_PARAM_CNT
, (quint32
)params
.count());
347 for(quint32 i
= 0; i
< param_count
; i
++)
349 strncpy_s(ipc_msg
.payload
.params
.values
[i
], MAX_PARAM_LEN
, MUTILS_UTF8(params
[i
].trimmed()), _TRUNCATE
);
351 ipc_msg
.payload
.params
.count
= param_count
;
353 ipc_msg
.payload
.timestamp
= ptr
->status
.payload
.counter
++;
354 UPDATE_CHECKSUM(ipc_msg
);
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
);
364 qWarning("Corrupted IPC status detected -> skipping!");
369 qWarning("Shared memory pointer is NULL -> unable to write data!");
372 if(!p
->sharedmem
->unlock())
374 const QString errorMessage
= p
->sharedmem
->errorString();
375 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage
));
378 if(!p
->semaphore_rd
->release())
380 const QString errorMessage
= p
->semaphore_rd
->errorString();
381 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage
));
387 ///////////////////////////////////////////////////////////////////////////////
389 ///////////////////////////////////////////////////////////////////////////////
391 bool MUtils::IPCChannel::read(quint32
&command
, quint32
&flags
, QStringList
¶ms
)
393 bool success
= false;
394 QReadLocker
readLock(&p
->lock
);
400 MUTILS_THROW("Shared memory for IPC not initialized yet.");
403 Internal::ipc_msg_t ipc_msg
;
404 memset(&ipc_msg
, 0, sizeof(Internal::ipc_msg_t
));
406 if(!p
->semaphore_rd
->acquire())
408 const QString errorMessage
= p
->semaphore_rd
->errorString();
409 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage
));
413 if(!p
->sharedmem
->lock())
415 const QString errorMessage
= p
->sharedmem
->errorString();
416 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage
));
420 if(Internal::ipc_t
*const ptr
= reinterpret_cast<Internal::ipc_t
*>(p
->sharedmem
->data()))
422 if(VERIFY_CHECKSUM(ptr
->status
))
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
);
428 if(VERIFY_CHECKSUM(ipc_msg
) || (ipc_msg
.payload
.timestamp
< ptr
->status
.payload
.counter
))
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
++)
436 strncpy_s(temp
, MAX_PARAM_LEN
, ipc_msg
.payload
.params
.values
[i
], _TRUNCATE
);
437 params
.append(QString::fromUtf8(temp
));
443 qWarning("Malformed or corrupted IPC message, will be ignored!");
448 qWarning("Corrupted IPC status detected -> skipping!");
453 qWarning("Shared memory pointer is NULL -> unable to read data!");
456 if(!p
->sharedmem
->unlock())
458 const QString errorMessage
= p
->sharedmem
->errorString();
459 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage
));
462 if(!p
->semaphore_wr
->release())
464 const QString errorMessage
= p
->semaphore_wr
->errorString();
465 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage
));