OSDN Git Service

IPC status fields will be protected by an Adler-32 checksum too.
[mutilities/MUtilities.git] / src / IPCChannel.cpp
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.
14 //
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
18 //
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
21
22 //MUtils
23 #include <MUtils/IPCChannel.h>
24 #include <MUtils/Exception.h>
25
26 //Internal
27 #include "3rd_party/adler32/include/adler32.h"
28
29 //Qt includes
30 #include <QRegExp>
31 #include <QSharedMemory>
32 #include <QSystemSemaphore>
33 #include <QMutex>
34 #include <QWriteLocker>
35 #include <QCryptographicHash>
36
37 //CRT
38 #include <cassert>
39
40 static const quint32 ADLER_SEED = 0x5D90C356;
41
42 ///////////////////////////////////////////////////////////////////////////////
43 // TYPES
44 ///////////////////////////////////////////////////////////////////////////////
45
46 namespace MUtils
47 {
48         namespace Internal
49         {
50                 static const size_t HDR_LEN = 40;
51                 static const size_t IPC_SLOTS = 128;
52
53                 typedef struct
54                 {
55                         quint64 counter;
56                         quint32 pos_wr;
57                         quint32 pos_rd;
58                 }
59                 ipc_status_data_t;
60
61                 typedef struct
62                 {
63                         ipc_status_data_t payload;
64                         quint32           checksum;
65                 }
66                 ipc_status_t;
67
68                 typedef struct
69                 {
70                         quint32 command_id;
71                         quint32 flags;
72                         char    param[MUtils::IPCChannel::MAX_MESSAGE_LEN];
73                         quint64 timestamp;
74                 }
75                 ipc_msg_data_t;
76
77                 typedef struct
78                 {
79                         ipc_msg_data_t payload;
80                         quint32        checksum;
81                 }
82                 ipc_msg_t;
83
84                 typedef struct
85                 {
86                         char         header[HDR_LEN];
87                         ipc_status_t status;
88                         ipc_msg_t    data[IPC_SLOTS];
89                 }
90                 ipc_t;
91         }
92 }
93
94 ///////////////////////////////////////////////////////////////////////////////
95 // UTILITIES
96 ///////////////////////////////////////////////////////////////////////////////
97
98 static QScopedPointer<QRegExp> g_escape_regexp;
99 static QMutex g_escape_lock;
100
101 #define ESCAPE(STR) (QString((STR)).replace(*g_escape_regexp, QLatin1String("_")).toLower())
102
103 static QString MAKE_ID(const QString &applicationId, const unsigned int &appVersionNo, const QString &channelId, const QString &itemId)
104 {
105         QMutexLocker locker(&g_escape_lock);
106
107         if(g_escape_regexp.isNull())
108         {
109                 g_escape_regexp.reset(new QRegExp(QLatin1String("[^A-Za-z0-9_\\-]")));
110         }
111
112         return QString("com.muldersoft.mutilities.ipc.%1.r%2.%3.%4").arg(ESCAPE(applicationId), QString::number(appVersionNo, 16).toUpper(), ESCAPE(channelId), ESCAPE(itemId));
113 }
114
115 ///////////////////////////////////////////////////////////////////////////////
116 // PRIVATE DATA
117 ///////////////////////////////////////////////////////////////////////////////
118
119 namespace MUtils
120 {
121         class IPCChannel_Private
122         {
123                 friend class IPCChannel;
124
125         protected:
126                 volatile bool initialized;
127                 QScopedPointer<QSharedMemory> sharedmem;
128                 QScopedPointer<QSystemSemaphore> semaphore_rd;
129                 QScopedPointer<QSystemSemaphore> semaphore_wr;
130                 QReadWriteLock lock;
131         };
132 }
133
134 ///////////////////////////////////////////////////////////////////////////////
135 // CONSTRUCTOR & DESTRUCTOR
136 ///////////////////////////////////////////////////////////////////////////////
137
138 MUtils::IPCChannel::IPCChannel(const QString &applicationId, const quint32 &appVersionNo, const QString &channelId)
139 :
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())
145 {
146         p->initialized = false;
147         if(m_headerStr.length() != Internal::HDR_LEN)
148         {
149                 MUTILS_THROW("Invalid header length has been detected!");
150         }
151 }
152
153 MUtils::IPCChannel::~IPCChannel(void)
154 {
155         if(p->initialized)
156         {
157                 if(p->sharedmem->isAttached())
158                 {
159                         p->sharedmem->detach();
160                 }
161         }
162
163         delete p;
164 }
165
166 ///////////////////////////////////////////////////////////////////////////////
167 // INITIALIZATION
168 ///////////////////////////////////////////////////////////////////////////////
169
170 int MUtils::IPCChannel::initialize(void)
171 {
172         QWriteLocker writeLock(&p->lock);
173         
174         if(p->initialized)
175         {
176                 return RET_ALREADY_INITIALIZED;
177         }
178
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));
182
183         if(p->semaphore_rd->error() != QSystemSemaphore::NoError)
184         {
185                 const QString errorMessage = p->semaphore_rd->errorString();
186                 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
187                 return RET_FAILURE;
188         }
189
190         if(p->semaphore_wr->error() != QSystemSemaphore::NoError)
191         {
192                 const QString errorMessage = p->semaphore_wr->errorString();
193                 qWarning("Failed to create system smaphore: %s", MUTILS_UTF8(errorMessage));
194                 return RET_FAILURE;
195         }
196         
197         if(!p->sharedmem->create(sizeof(Internal::ipc_t)))
198         {
199                 if(p->sharedmem->error() == QSharedMemory::AlreadyExists)
200                 {
201                         if(!p->sharedmem->attach())
202                         {
203                                 const QString errorMessage = p->sharedmem->errorString();
204                                 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
205                                 return RET_FAILURE;
206                         }
207                         if(p->sharedmem->error() != QSharedMemory::NoError)
208                         {
209                                 const QString errorMessage = p->sharedmem->errorString();
210                                 qWarning("Failed to attach to shared memory: %s", MUTILS_UTF8(errorMessage));
211                                 return RET_FAILURE;
212                         }
213                         if(p->sharedmem->size() < sizeof(Internal::ipc_t))
214                         {
215                                 qWarning("Failed to attach to shared memory: Size verification has failed!");
216                                 return RET_FAILURE;
217                         }
218                         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
219                         {
220                                 if(memcmp(&ptr->header[0], m_headerStr.constData(), Internal::HDR_LEN) != 0)
221                                 {
222                                         qWarning("Failed to attach to shared memory: Header verification has failed!");
223                                         return RET_FAILURE;
224                                 }
225                         }
226                         else
227                         {
228                                 const QString errorMessage = p->sharedmem->errorString();
229                                 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
230                                 return RET_FAILURE;
231                         }
232                         p->initialized = true;
233                         return RET_SUCCESS_SLAVE;
234                 }
235                 else
236                 {
237                         const QString errorMessage = p->sharedmem->errorString();
238                         qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
239                         return RET_FAILURE;
240                 }
241         }
242         
243         if(p->sharedmem->error() != QSharedMemory::NoError)
244         {
245                 const QString errorMessage = p->sharedmem->errorString();
246                 qWarning("Failed to create shared memory: %s", MUTILS_UTF8(errorMessage));
247                 return RET_FAILURE;
248         }
249
250         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
251         {
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));
255         }
256         else
257         {
258                 const QString errorMessage = p->sharedmem->errorString();
259                 qWarning("Failed to access shared memory: %s", MUTILS_UTF8(errorMessage));
260                 return RET_FAILURE;
261         }
262
263         if(!p->semaphore_wr->release(Internal::IPC_SLOTS))
264         {
265                 const QString errorMessage = p->semaphore_wr->errorString();
266                 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
267                 return RET_FAILURE;
268         }
269         
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()));
273
274         p->initialized = true;
275         return RET_SUCCESS_MASTER;
276 }
277
278 ///////////////////////////////////////////////////////////////////////////////
279 // SEND MESSAGE
280 ///////////////////////////////////////////////////////////////////////////////
281
282 bool MUtils::IPCChannel::send(const quint32 &command, const quint32 &flags, const char *const message)
283 {
284         bool success = false;
285         QReadLocker readLock(&p->lock);
286
287         if(!p->initialized)
288         {
289                 MUTILS_THROW("Shared memory for IPC not initialized yet.");
290         }
291
292         if(!p->semaphore_wr->acquire())
293         {
294                 const QString errorMessage = p->semaphore_wr->errorString();
295                 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
296                 return false;
297         }
298
299         if(!p->sharedmem->lock())
300         {
301                 const QString errorMessage = p->sharedmem->errorString();
302                 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
303                 return false;
304         }
305
306         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
307         {
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)
310                 {
311                         Internal::ipc_msg_t ipc_msg;
312                         memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
313
314                         ipc_msg.payload.command_id = command;
315                         ipc_msg.payload.flags = flags;
316                         if(message)
317                         {
318                                 strncpy_s(ipc_msg.payload.param, MAX_MESSAGE_LEN, message, _TRUNCATE);
319                         }
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));
322
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));
326
327                         success = true;
328                 }
329                 else
330                 {
331                         qWarning("Corrupted IPC status detected -> skipping!");
332                 }
333         }
334         else
335         {
336                 qWarning("Shared memory pointer is NULL -> unable to write data!");
337         }
338
339         if(!p->sharedmem->unlock())
340         {
341                 const QString errorMessage = p->sharedmem->errorString();
342                 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
343         }
344
345         if(!p->semaphore_rd->release())
346         {
347                 const QString errorMessage = p->semaphore_rd->errorString();
348                 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
349         }
350
351         return success;
352 }
353
354 ///////////////////////////////////////////////////////////////////////////////
355 // READ MESSAGE
356 ///////////////////////////////////////////////////////////////////////////////
357
358 bool MUtils::IPCChannel::read(quint32 &command, quint32 &flags, char *const message, const size_t &buffSize)
359 {
360         bool success = false;
361         QReadLocker readLock(&p->lock);
362         
363         command = 0;
364         if(message && (buffSize > 0))
365         {
366                 message[0] = '\0';
367         }
368         
369         if(!p->initialized)
370         {
371                 MUTILS_THROW("Shared memory for IPC not initialized yet.");
372         }
373
374         Internal::ipc_msg_t ipc_msg;
375         memset(&ipc_msg, 0, sizeof(Internal::ipc_msg_t));
376
377         if(!p->semaphore_rd->acquire())
378         {
379                 const QString errorMessage = p->semaphore_rd->errorString();
380                 qWarning("Failed to acquire system semaphore: %s", MUTILS_UTF8(errorMessage));
381                 return false;
382         }
383
384         if(!p->sharedmem->lock())
385         {
386                 const QString errorMessage = p->sharedmem->errorString();
387                 qWarning("Failed to lock shared memory: %s", MUTILS_UTF8(errorMessage));
388                 return false;
389         }
390
391         if(Internal::ipc_t *const ptr = reinterpret_cast<Internal::ipc_t*>(p->sharedmem->data()))
392         {
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)
395                 {
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));
399
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))
402                         {
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;
407                         }
408                         else
409                         {
410                                 qWarning("Malformed or corrupted IPC message, will be ignored!");
411                         }
412                 }
413                 else
414                 {
415                         qWarning("Corrupted IPC status detected -> skipping!");
416                 }
417         }
418         else
419         {
420                 qWarning("Shared memory pointer is NULL -> unable to read data!");
421         }
422
423         if(!p->sharedmem->unlock())
424         {
425                 const QString errorMessage = p->sharedmem->errorString();
426                 qFatal("Failed to unlock shared memory: %s", MUTILS_UTF8(errorMessage));
427         }
428
429         if(!p->semaphore_wr->release())
430         {
431                 const QString errorMessage = p->semaphore_wr->errorString();
432                 qWarning("Failed to release system semaphore: %s", MUTILS_UTF8(errorMessage));
433         }
434
435         return success;
436 }