Rabbit Remote Control 0.0.30
Loading...
Searching...
No Matches
Event.cpp
1#include <QLoggingCategory>
2#include <QStandardPaths>
3#include <QThread>
4#include <QDir>
5
6#include <chrono>
7#include "Event.h"
8
9#if defined(Q_OS_WIN)
10 #if defined(HAVE_UNIX_DOMAIN_SOCKET)
11 /* [AF_UNIX comes to Windows](https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/)
12 * How can I write a Windows AF_UNIX app?
13 * - Download the Windows Insiders SDK for the Windows build 17061 — available here.
14 * - Check whether your Windows build has support for unix socket by running “sc query afunix” from a Windows admin command prompt.
15 * - #include <afunix.h> in your Windows application and write a Windows unix socket winsock application as you would write any other unix socket application, but, using Winsock API’s.
16 */
17 #include <afunix.h>
18 #endif
19 #define close closesocket
20 #define socklen_t int
21#else
22 #if HAVE_EVENTFD
23 #include <sys/eventfd.h>
24 #endif
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <netinet/in.h>
30 #include <netinet/tcp.h>
31 #include <arpa/inet.h>
32 #include <ifaddrs.h>
33 #if defined(HAVE_UNIX_DOMAIN_SOCKET)
34 #include <sys/un.h>
35 #endif
36 #define INVALID_SOCKET -1
37 #define SOCKET_ERROR -1
38#endif
39
40namespace Channel {
41
42static Q_LOGGING_CATEGORY(log, "Channel.Event")
43
44CEvent::CEvent(QObject *parent)
45 : QObject{parent}
46{
47 qDebug(log) << "CEvent::CEvent";
48 fd[0] = INVALID_SOCKET;
49 fd[1] = INVALID_SOCKET;
50 Init();
51}
52
53CEvent::~CEvent()
54{
55 qDebug(log) << "Event::~Event()";
56 Clear();
57}
58
59int CEvent::Init()
60{
61#if HAVE_EVENTFD
62 qDebug(log) << "Use eventfd";
63 fd[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC | EFD_SEMAPHORE);
64#else
65 return CreateSocketPair(fd);
66#endif
67 return 0;
68}
69
70int CEvent::Clear()
71{
72 if(INVALID_SOCKET != fd[0]) {
73 close(fd[0]);
74 fd[0] = INVALID_SOCKET;
75 }
76
77 if(INVALID_SOCKET != fd[1]) {
78 close(fd[1]);
79 fd[1] = INVALID_SOCKET;
80 }
81 return 0;
82}
83
84int CEvent::Reset()
85{
86 int nRet = 0;
87 if(INVALID_SOCKET == fd[0])
88 return -1;
89
90 //qDebug(log) << "Reset()";
91#if HAVE_EVENTFD
92 eventfd_t value;
93 nRet = eventfd_read(fd[0], &value);
94 //qDebug(log) << "read eventfd:" << value;
95 if(nRet) {
96 if(EAGAIN == errno || EWOULDBLOCK == errno)
97 return 0;
98 qDebug(log) << "eventfd_read fail" << errno << "-" << strerror(errno);
99 }
100#else
101 const int len = 1;
102 char buf[len];
103 nRet = recv(fd[0], buf, len, 0);
104 if(nRet >= 0) {
105 //qDebug(log) << "recv" << nRet;
106 return 0;
107 }
108 if(nRet < 0) {
109 if(EAGAIN == errno || EWOULDBLOCK == errno)
110 return 0;
111 qDebug(log) << "recv fail. nRet:" << nRet << "fd:" << fd[0]
112 << "error:" << errno << "-" << strerror(errno);
113 }
114#endif
115 return nRet;
116}
117
118int CEvent::WakeUp()
119{
120 int nRet = 0;
121 //qDebug(log) << "WakeUp()";
122#if HAVE_EVENTFD
123 if(INVALID_SOCKET == fd[0])
124 return -1;
125 nRet = eventfd_write(fd[0], 1);
126 if(nRet) {
127 if(EAGAIN == errno || EWOULDBLOCK == errno)
128 return 0;
129 qDebug(log) << "eventfd_write fail" << errno << "-" << strerror(errno);
130 }
131#else
132 if(INVALID_SOCKET == fd[1])
133 return -1;
134 const int len = 1;
135 char buf[len];
136 nRet = send(fd[1], buf, len, 0);
137 if(nRet >= 0) {
138 //qDebug(log) << "send" << nRet;
139 return 0;
140 }
141 if(nRet < 0) {
142 if(EAGAIN == errno || EWOULDBLOCK == errno)
143 return 0;
144 qDebug(log) << "send fail" << "fd:" << fd[1]
145 << "error:" << errno << "-" << strerror(errno);
146 }
147#endif
148 return nRet;
149}
150
151qintptr CEvent::GetFd()
152{
153 return fd[0];
154}
155
156int CEvent::CreateSocketPair(qintptr fd[])
157{
158 int family = AF_INET;
159 int type = SOCK_STREAM;
160 SOCKET listener = INVALID_SOCKET;
161 SOCKET connector = INVALID_SOCKET;
162 SOCKET acceptor = INVALID_SOCKET;
163 socklen_t size = 0;
164
165 if (!fd) {
166 qCritical(log) << "The fd is null";
167 return -1;
168 }
169
170#if defined(HAVE_UNIX_DOMAIN_SOCKET)
171 struct sockaddr_un listen_addr;
172 struct sockaddr_un connect_addr;
173 QString szPath;
174 QString szUnixDomainSocket;
175 szPath = QStandardPaths::writableLocation(
176 QStandardPaths::TempLocation)
177 + QDir::separator() + "RabbitRemoteControl";
178 auto now = std::chrono::system_clock::now();
179 auto tick = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch()).count();
180 szUnixDomainSocket = szPath + QDir::separator() + "RRC_" + QString::number(tick) + "_"
181 + QString::number((unsigned long)QThread::currentThreadId()) + ".socket";
182 QDir p(szPath);
183 if(!p.exists())
184 p.mkpath(szPath);
185 QDir f(szUnixDomainSocket);
186 if(f.exists())
187 f.remove(szUnixDomainSocket);
188 size = sizeof(sockaddr_un::sun_path);
189 if(szUnixDomainSocket.size() > size)
190 {
191 qCritical(log) << "The unix domain socket length greater then" << size;
192 return -2;
193 }
194
195 family = AF_UNIX;
196#else
197 struct sockaddr_in listen_addr;
198 struct sockaddr_in connect_addr;
199#endif
200
201 size = sizeof(listen_addr);
202 memset(&listen_addr, 0, sizeof(listen_addr));
203 memset(&connect_addr, 0, sizeof(connect_addr));
204
205 listener = socket(family, type, 0);
206 if (INVALID_SOCKET == listener) {
207 qCritical(log) << "Create server socket fail:"
208 << errno << "[" << strerror(errno) << "]";
209 return errno;
210 }
211
212 do {
213
214#if defined(HAVE_UNIX_DOMAIN_SOCKET)
215 listen_addr.sun_family = AF_UNIX;
216 strcpy(listen_addr.sun_path, szUnixDomainSocket.toStdString().c_str());
217#else
218 listen_addr.sin_family = AF_INET;
219 listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
220 listen_addr.sin_port = 0; /* kernel chooses port. */
221#endif
222 if (-1 == bind(listener, (struct sockaddr *) &listen_addr, sizeof(listen_addr)))
223 {
224 qCritical(log) << "bind fail:" << strerror(errno)
225 << GetAddress(&listen_addr);
226 break;
227 }
228 if (-1 == listen(listener, 1))
229 {
230 qCritical(log) << "listen fail:" << strerror(errno)
231 << GetAddress(&listen_addr);
232 break;
233 }
234
235#if !defined(HAVE_UNIX_DOMAIN_SOCKET)
236 /* We want to find out the port number to connect to. */
237 if (-1 == getsockname(listener, (struct sockaddr *) &listen_addr, &size))
238 {
239 qCritical(log) << "Get listen socket address fail";
240 break;
241 }
242 if (sizeof(listen_addr) != size) {
243 qCritical(log) << "getsockname return size greater then supplied to the call:"
244 << "return size:" << size
245 << "call size:" << sizeof(listen_addr);
246 break;
247 }
248#endif
249 qDebug(log) << "Socket listener:" << GetAddress(&listen_addr);
250
251 connector = socket(family, type, 0);
252 if (INVALID_SOCKET == connector) {
253 qCritical(log) << "Create connect socket fail:"
254 << errno << "[" << strerror(errno) << "]";
255 break;
256 }
257
258 if (-1 == ::connect(connector, (struct sockaddr *) &listen_addr, size)) {
259 qDebug(log) << "connect fail:" << strerror(errno)
260 << GetAddress(&listen_addr);
261 break;
262 }
263
264 acceptor = accept(listener, (struct sockaddr *) &listen_addr, &size);
265 if (INVALID_SOCKET == acceptor)
266 {
267 qDebug(log) << "accept fail:" << strerror(errno)
268 << GetAddress(&listen_addr);
269 break;
270 }
271 qDebug(log) << "accept:" << GetAddress(&listen_addr)
272 << "accept fd:" << acceptor << "connect fd:" << connector;
273#if !defined(HAVE_UNIX_DOMAIN_SOCKET)
274 if (sizeof(listen_addr) != size) {
275 qCritical(log) << "accept return size greater then supplied to the call:"
276 << "return size:" << size
277 << "call size:" << sizeof(listen_addr);
278 break;
279 }
280 /* Now check we are talking to ourself by matching port and host on the
281 two sockets. */
282 if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1) {
283 qCritical(log) << "Get connector socket address fail";
284 break;
285 }
286 qDebug(log) << "Connector port:" << ntohs(connect_addr.sin_port) << "fd:" << connector;
287 if (size != sizeof (connect_addr)
288 || listen_addr.sin_family != connect_addr.sin_family
289 || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
290 || listen_addr.sin_port != connect_addr.sin_port) {
291 qCritical(log) << "Accept address"
292 << ntohs(listen_addr.sin_port)
293 << "is not same connect address"
294 << ntohs(connect_addr.sin_port);
295 break;
296 }
297#endif
298 close(listener);
299 fd[0] = connector;
300 fd[1] = acceptor;
301
302 SetSocketNonBlocking(fd[0]);
303 SetSocketNonBlocking(fd[1]);
304#if !defined(HAVE_UNIX_DOMAIN_SOCKET)
305 EnableNagles(fd[0], false);
306 EnableNagles(fd[1], false);
307#endif
308 return 0;
309 } while(0);
310
311 if (INVALID_SOCKET != listener)
312 close(listener);
313 if (INVALID_SOCKET != connector)
314 close(connector);
315 if (INVALID_SOCKET != acceptor)
316 close(acceptor);
317
318 return errno;
319}
320
321QString CEvent::GetAddress(void* address)
322{
323 QString szAdd;
324#if defined(HAVE_UNIX_DOMAIN_SOCKET)
325 struct sockaddr_un* pAdd = (struct sockaddr_un*)address;
326 szAdd = pAdd->sun_path;
327#else
328 struct sockaddr_in* pAdd = (struct sockaddr_in*)address;
329 szAdd = inet_ntoa(pAdd->sin_addr) + QString(":") + QString::number(ntohs(pAdd->sin_port));
330#endif
331
332 return szAdd;
333}
334
335int CEvent::SetSocketNonBlocking(SOCKET fd)
336{
337#if defined(Q_OS_WIN)
338
339 unsigned long nonblocking = 1;
340 if (ioctlsocket(fd, FIONBIO, &nonblocking) == SOCKET_ERROR) {
341 qCritical(log, "ioctlsocket(%d, FIONBIO, &%lu)", (int)fd, (unsigned long)nonblocking);
342 return -1;
343 }
344
345#else
346
347 int flags;
348 if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) {
349 qCritical(log, "fcntl(%d, F_GETFL)", fd);
350 return -1;
351 }
352 if (!(flags & O_NONBLOCK)) {
353 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == SOCKET_ERROR) {
354 qCritical(log, "fcntl(%d, F_SETFL)", fd);
355 return -1;
356 }
357 }
358
359#endif
360 return 0;
361}
362
363int CEvent::SetSocketBlocking(SOCKET fd, bool block)
364{
365#if defined(Q_OS_WIN)
366 unsigned long nonblocking = 1;
367 if(block)
368 nonblocking = 0;
369 if (ioctlsocket(fd, FIONBIO, &nonblocking) == SOCKET_ERROR) {
370 qCritical(log, "ioctlsocket(%d, FIONBIO, &%lu)", (int)fd, (unsigned long)nonblocking);
371 return -1;
372 }
373#else
374 int flags;
375 if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) {
376 qCritical(log, "fcntl(%d, F_GETFL)", fd);
377 return -1;
378 }
379
380 if (block)
381 flags &= ~O_NONBLOCK;
382 else
383 flags |= O_NONBLOCK;
384
385 if (fcntl(fd, F_SETFL, flags) == SOCKET_ERROR) {
386 qCritical(log, "fcntl(%d, F_SETFL)", fd);
387 return -1;
388 }
389#endif
390 return 0;
391}
392
393bool CEvent::EnableNagles(SOCKET fd, bool enable) {
394 int rc;
395 int opt = enable ? 0 : 1;
396 socklen_t optlen;
397 optlen = sizeof(opt);
398 /*
399 rc = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, &optlen);
400 if (SOCKET_ERROR == rc) {
401 qCritical(log) << "unable to getsockopt TCP_NODELAY:"
402 << errno << "-" << strerror(errno);
403 return false;
404 }
405 if (opt == 1)
406 qDebug(log) << fd << "is TCP_NODELAY";
407 else
408 qDebug(log) << fd << "is not TCP_NODELAY";
409 //*/
410 opt = enable ? 0 : 1;
411 rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, optlen);
412 if (SOCKET_ERROR == rc) {
413 qCritical(log) << "unable to setsockopt TCP_NODELAY:"
414 << errno << "-" << strerror(errno);
415 return false;
416 }
417 /*
418 rc = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, &optlen);
419 if (SOCKET_ERROR == rc) {
420 qCritical(log) << "unable to getsockopt TCP_NODELAY:"
421 << errno << "-" << strerror(errno);
422 return false;
423 }
424 if (opt == 1)
425 qDebug(log) << fd << "is TCP_NODELAY";
426 else
427 qDebug(log) << fd << "is not TCP_NODELAY";//*/
428 return true;
429}
430
431} //namespace RabbitCommon