1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2014 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 static const quint32 ADLER_SEED
= 0x5D90C356;
42 ///////////////////////////////////////////////////////////////////////////////
44 ///////////////////////////////////////////////////////////////////////////////
50 static const size_t HDR_LEN
= 40;
51 static const size_t IPC_SLOTS
= 128;
63 ipc_status_data_t payload
;
72 char param
[MUtils::IPCChannel::MAX_MESSAGE_LEN
];
79 ipc_msg_data_t payload
;
88 ipc_msg_t data
[IPC_SLOTS
];
94 ///////////////////////////////////////////////////////////////////////////////
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 ///////////////////////////////////////////////////////////////////////////////
117 ///////////////////////////////////////////////////////////////////////////////
121 class IPCChannel_Private
123 friend class IPCChannel
;
126 volatile bool initialized
;
127 QScopedPointer
<QSharedMemory
> sharedmem
;
128 QScopedPointer
<QSystemSemaphore
> semaphore_rd
;
129 QScopedPointer
<QSystemSemaphore
> semaphore_wr
;
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)
157 if(p
->sharedmem
->isAttached())
159 p
->sharedmem
->detach();
166 ///////////////////////////////////////////////////////////////////////////////
168 ///////////////////////////////////////////////////////////////////////////////
170 int MUtils::IPCChannel::initialize(void)
172 QWriteLocker
writeLock(&p
->lock
);
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
));
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
));
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
));
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
));
213 if(p
->sharedmem
->size() < sizeof(Internal::ipc_t
))
215 qWarning("Failed to attach to shared memory: Size verification has failed!");
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!");
228 const QString errorMessage
= p
->sharedmem
->errorString();
229 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage
));
232 p
->initialized
= true;
233 return RET_SUCCESS_SLAVE
;
237 const QString errorMessage
= p
->sharedmem
->errorString();
238 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage
));
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
));
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
));
258 const QString errorMessage
= p
->sharedmem
->errorString();
259 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage
));
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
));
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 ///////////////////////////////////////////////////////////////////////////////
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
);
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
));
299 if(!p
->sharedmem
->lock())
301 const QString errorMessage
= p
->sharedmem
->errorString();
302 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage
));
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
;
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
));
331 qWarning("Corrupted IPC status detected -> skipping!");
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
));
354 ///////////////////////////////////////////////////////////////////////////////
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
);
364 if(message
&& (buffSize
> 0))
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
));
384 if(!p
->sharedmem
->lock())
386 const QString errorMessage
= p
->sharedmem
->errorString();
387 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage
));
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
);
410 qWarning("Malformed or corrupted IPC message, will be ignored!");
415 qWarning("Corrupted IPC status detected -> skipping!");
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
));