1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qlocalsocket.h"
41#include "qlocalsocket_p.h"
42#include "qnet_unix_p.h"
43
44#include <sys/types.h>
45#include <sys/socket.h>
46#include <sys/un.h>
47#include <unistd.h>
48#include <fcntl.h>
49#include <errno.h>
50
51#include <qdir.h>
52#include <qdebug.h>
53#include <qelapsedtimer.h>
54
55#ifdef Q_OS_VXWORKS
56# include <selectLib.h>
57#endif
58
59#define QT_CONNECT_TIMEOUT 30000
60
61QT_BEGIN_NAMESPACE
62
63QLocalSocketPrivate::QLocalSocketPrivate() : QIODevicePrivate(),
64 delayConnect(nullptr),
65 connectTimer(nullptr),
66 connectingSocket(-1),
67 state(QLocalSocket::UnconnectedState)
68{
69}
70
71void QLocalSocketPrivate::init()
72{
73 Q_Q(QLocalSocket);
74 // QIODevice signals
75 q->connect(sender: &unixSocket, SIGNAL(aboutToClose()), receiver: q, SIGNAL(aboutToClose()));
76 q->connect(sender: &unixSocket, SIGNAL(bytesWritten(qint64)),
77 receiver: q, SIGNAL(bytesWritten(qint64)));
78 q->connect(sender: &unixSocket, SIGNAL(readyRead()), receiver: q, SIGNAL(readyRead()));
79 // QAbstractSocket signals
80 q->connect(sender: &unixSocket, SIGNAL(connected()), receiver: q, SIGNAL(connected()));
81 q->connect(sender: &unixSocket, SIGNAL(disconnected()), receiver: q, SIGNAL(disconnected()));
82 q->connect(sender: &unixSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
83 receiver: q, SLOT(_q_stateChanged(QAbstractSocket::SocketState)));
84 q->connect(sender: &unixSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
85 receiver: q, SLOT(_q_errorOccurred(QAbstractSocket::SocketError)));
86 q->connect(sender: &unixSocket, SIGNAL(readChannelFinished()), receiver: q, SIGNAL(readChannelFinished()));
87 unixSocket.setParent(q);
88}
89
90qint64 QLocalSocketPrivate::skip(qint64 maxSize)
91{
92 return unixSocket.skip(maxSize);
93}
94
95void QLocalSocketPrivate::_q_errorOccurred(QAbstractSocket::SocketError socketError)
96{
97 Q_Q(QLocalSocket);
98 QString function = QLatin1String("QLocalSocket");
99 QLocalSocket::LocalSocketError error = (QLocalSocket::LocalSocketError)socketError;
100 QString errorString = generateErrorString(error, function);
101 q->setErrorString(errorString);
102 emit q->errorOccurred(socketError: error);
103}
104
105void QLocalSocketPrivate::_q_stateChanged(QAbstractSocket::SocketState newState)
106{
107 Q_Q(QLocalSocket);
108 QLocalSocket::LocalSocketState currentState = state;
109 switch(newState) {
110 case QAbstractSocket::UnconnectedState:
111 state = QLocalSocket::UnconnectedState;
112 serverName.clear();
113 fullServerName.clear();
114 break;
115 case QAbstractSocket::ConnectingState:
116 state = QLocalSocket::ConnectingState;
117 break;
118 case QAbstractSocket::ConnectedState:
119 state = QLocalSocket::ConnectedState;
120 break;
121 case QAbstractSocket::ClosingState:
122 state = QLocalSocket::ClosingState;
123 break;
124 default:
125#if defined QLOCALSOCKET_DEBUG
126 qWarning() << "QLocalSocket::Unhandled socket state change:" << newState;
127#endif
128 return;
129 }
130 if (currentState != state)
131 emit q->stateChanged(socketState: state);
132}
133
134QString QLocalSocketPrivate::generateErrorString(QLocalSocket::LocalSocketError error, const QString &function) const
135{
136 QString errorString;
137 switch (error) {
138 case QLocalSocket::ConnectionRefusedError:
139 errorString = QLocalSocket::tr(s: "%1: Connection refused").arg(a: function);
140 break;
141 case QLocalSocket::PeerClosedError:
142 errorString = QLocalSocket::tr(s: "%1: Remote closed").arg(a: function);
143 break;
144 case QLocalSocket::ServerNotFoundError:
145 errorString = QLocalSocket::tr(s: "%1: Invalid name").arg(a: function);
146 break;
147 case QLocalSocket::SocketAccessError:
148 errorString = QLocalSocket::tr(s: "%1: Socket access error").arg(a: function);
149 break;
150 case QLocalSocket::SocketResourceError:
151 errorString = QLocalSocket::tr(s: "%1: Socket resource error").arg(a: function);
152 break;
153 case QLocalSocket::SocketTimeoutError:
154 errorString = QLocalSocket::tr(s: "%1: Socket operation timed out").arg(a: function);
155 break;
156 case QLocalSocket::DatagramTooLargeError:
157 errorString = QLocalSocket::tr(s: "%1: Datagram too large").arg(a: function);
158 break;
159 case QLocalSocket::ConnectionError:
160 errorString = QLocalSocket::tr(s: "%1: Connection error").arg(a: function);
161 break;
162 case QLocalSocket::UnsupportedSocketOperationError:
163 errorString = QLocalSocket::tr(s: "%1: The socket operation is not supported").arg(a: function);
164 break;
165 case QLocalSocket::OperationError:
166 errorString = QLocalSocket::tr(s: "%1: Operation not permitted when socket is in this state").arg(a: function);
167 break;
168 case QLocalSocket::UnknownSocketError:
169 default:
170 errorString = QLocalSocket::tr(s: "%1: Unknown error %2").arg(a: function).arg(errno);
171 }
172 return errorString;
173}
174
175void QLocalSocketPrivate::setErrorAndEmit(QLocalSocket::LocalSocketError error, const QString &function)
176{
177 Q_Q(QLocalSocket);
178 switch (error) {
179 case QLocalSocket::ConnectionRefusedError:
180 unixSocket.setSocketError(QAbstractSocket::ConnectionRefusedError);
181 break;
182 case QLocalSocket::PeerClosedError:
183 unixSocket.setSocketError(QAbstractSocket::RemoteHostClosedError);
184 break;
185 case QLocalSocket::ServerNotFoundError:
186 unixSocket.setSocketError(QAbstractSocket::HostNotFoundError);
187 break;
188 case QLocalSocket::SocketAccessError:
189 unixSocket.setSocketError(QAbstractSocket::SocketAccessError);
190 break;
191 case QLocalSocket::SocketResourceError:
192 unixSocket.setSocketError(QAbstractSocket::SocketResourceError);
193 break;
194 case QLocalSocket::SocketTimeoutError:
195 unixSocket.setSocketError(QAbstractSocket::SocketTimeoutError);
196 break;
197 case QLocalSocket::DatagramTooLargeError:
198 unixSocket.setSocketError(QAbstractSocket::DatagramTooLargeError);
199 break;
200 case QLocalSocket::ConnectionError:
201 unixSocket.setSocketError(QAbstractSocket::NetworkError);
202 break;
203 case QLocalSocket::UnsupportedSocketOperationError:
204 unixSocket.setSocketError(QAbstractSocket::UnsupportedSocketOperationError);
205 break;
206 case QLocalSocket::UnknownSocketError:
207 default:
208 unixSocket.setSocketError(QAbstractSocket::UnknownSocketError);
209 }
210
211 QString errorString = generateErrorString(error, function);
212 q->setErrorString(errorString);
213 emit q->errorOccurred(socketError: error);
214
215 // errors cause a disconnect
216 unixSocket.setSocketState(QAbstractSocket::UnconnectedState);
217 bool stateChanged = (state != QLocalSocket::UnconnectedState);
218 state = QLocalSocket::UnconnectedState;
219 q->close();
220 if (stateChanged)
221 q->emit stateChanged(socketState: state);
222}
223
224void QLocalSocket::connectToServer(OpenMode openMode)
225{
226 Q_D(QLocalSocket);
227 if (state() == ConnectedState || state() == ConnectingState) {
228 QString errorString = d->generateErrorString(error: QLocalSocket::OperationError, function: QLatin1String("QLocalSocket::connectToserver"));
229 setErrorString(errorString);
230 emit errorOccurred(socketError: QLocalSocket::OperationError);
231 return;
232 }
233
234 d->errorString.clear();
235 d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
236 d->state = ConnectingState;
237 emit stateChanged(socketState: d->state);
238
239 if (d->serverName.isEmpty()) {
240 d->setErrorAndEmit(error: ServerNotFoundError,
241 function: QLatin1String("QLocalSocket::connectToServer"));
242 return;
243 }
244
245 // create the socket
246 if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, protocol: 0, O_NONBLOCK))) {
247 d->setErrorAndEmit(error: UnsupportedSocketOperationError,
248 function: QLatin1String("QLocalSocket::connectToServer"));
249 return;
250 }
251
252 // _q_connectToSocket does the actual connecting
253 d->connectingName = d->serverName;
254 d->connectingOpenMode = openMode;
255 d->_q_connectToSocket();
256 return;
257}
258
259/*!
260 \internal
261
262 Tries to connect connectingName and connectingOpenMode
263
264 \sa connectToServer(), waitForConnected()
265 */
266void QLocalSocketPrivate::_q_connectToSocket()
267{
268 Q_Q(QLocalSocket);
269 QString connectingPathName;
270
271 // determine the full server path
272 if (connectingName.startsWith(c: QLatin1Char('/'))) {
273 connectingPathName = connectingName;
274 } else {
275 connectingPathName = QDir::tempPath();
276 connectingPathName += QLatin1Char('/') + connectingName;
277 }
278
279 const QByteArray encodedConnectingPathName = QFile::encodeName(fileName: connectingPathName);
280 struct sockaddr_un name;
281 name.sun_family = PF_UNIX;
282 if (sizeof(name.sun_path) < (uint)encodedConnectingPathName.size() + 1) {
283 QString function = QLatin1String("QLocalSocket::connectToServer");
284 setErrorAndEmit(error: QLocalSocket::ServerNotFoundError, function);
285 return;
286 }
287 ::memcpy(dest: name.sun_path, src: encodedConnectingPathName.constData(),
288 n: encodedConnectingPathName.size() + 1);
289 if (-1 == qt_safe_connect(sockfd: connectingSocket, addr: (struct sockaddr *)&name, addrlen: sizeof(name))) {
290 QString function = QLatin1String("QLocalSocket::connectToServer");
291 switch (errno)
292 {
293 case EINVAL:
294 case ECONNREFUSED:
295 setErrorAndEmit(error: QLocalSocket::ConnectionRefusedError, function);
296 break;
297 case ENOENT:
298 setErrorAndEmit(error: QLocalSocket::ServerNotFoundError, function);
299 break;
300 case EACCES:
301 case EPERM:
302 setErrorAndEmit(error: QLocalSocket::SocketAccessError, function);
303 break;
304 case ETIMEDOUT:
305 setErrorAndEmit(error: QLocalSocket::SocketTimeoutError, function);
306 break;
307 case EAGAIN:
308 // Try again later, all of the sockets listening are full
309 if (!delayConnect) {
310 delayConnect = new QSocketNotifier(connectingSocket, QSocketNotifier::Write, q);
311 q->connect(sender: delayConnect, SIGNAL(activated(QSocketDescriptor)), receiver: q, SLOT(_q_connectToSocket()));
312 }
313 if (!connectTimer) {
314 connectTimer = new QTimer(q);
315 q->connect(sender: connectTimer, SIGNAL(timeout()),
316 receiver: q, SLOT(_q_abortConnectionAttempt()),
317 Qt::DirectConnection);
318 connectTimer->start(QT_CONNECT_TIMEOUT);
319 }
320 delayConnect->setEnabled(true);
321 break;
322 default:
323 setErrorAndEmit(error: QLocalSocket::UnknownSocketError, function);
324 }
325 return;
326 }
327
328 // connected!
329 cancelDelayedConnect();
330
331 serverName = connectingName;
332 fullServerName = connectingPathName;
333 if (unixSocket.setSocketDescriptor(socketDescriptor: connectingSocket,
334 state: QAbstractSocket::ConnectedState, openMode: connectingOpenMode)) {
335 q->QIODevice::open(mode: connectingOpenMode | QIODevice::Unbuffered);
336 q->emit connected();
337 } else {
338 QString function = QLatin1String("QLocalSocket::connectToServer");
339 setErrorAndEmit(error: QLocalSocket::UnknownSocketError, function);
340 }
341 connectingSocket = -1;
342 connectingName.clear();
343 connectingOpenMode = { };
344}
345
346bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
347 LocalSocketState socketState, OpenMode openMode)
348{
349 Q_D(QLocalSocket);
350 QAbstractSocket::SocketState newSocketState = QAbstractSocket::UnconnectedState;
351 switch (socketState) {
352 case ConnectingState:
353 newSocketState = QAbstractSocket::ConnectingState;
354 break;
355 case ConnectedState:
356 newSocketState = QAbstractSocket::ConnectedState;
357 break;
358 case ClosingState:
359 newSocketState = QAbstractSocket::ClosingState;
360 break;
361 case UnconnectedState:
362 newSocketState = QAbstractSocket::UnconnectedState;
363 break;
364 }
365 QIODevice::open(mode: openMode);
366 d->state = socketState;
367 return d->unixSocket.setSocketDescriptor(socketDescriptor,
368 state: newSocketState, openMode);
369}
370
371void QLocalSocketPrivate::_q_abortConnectionAttempt()
372{
373 Q_Q(QLocalSocket);
374 q->close();
375}
376
377void QLocalSocketPrivate::cancelDelayedConnect()
378{
379 if (delayConnect) {
380 delayConnect->setEnabled(false);
381 delete delayConnect;
382 delayConnect = nullptr;
383 connectTimer->stop();
384 delete connectTimer;
385 connectTimer = nullptr;
386 }
387}
388
389qintptr QLocalSocket::socketDescriptor() const
390{
391 Q_D(const QLocalSocket);
392 return d->unixSocket.socketDescriptor();
393}
394
395qint64 QLocalSocket::readData(char *data, qint64 c)
396{
397 Q_D(QLocalSocket);
398 return d->unixSocket.read(data, maxlen: c);
399}
400
401qint64 QLocalSocket::writeData(const char *data, qint64 c)
402{
403 Q_D(QLocalSocket);
404 return d->unixSocket.writeData(data, maxSize: c);
405}
406
407void QLocalSocket::abort()
408{
409 Q_D(QLocalSocket);
410 d->unixSocket.abort();
411}
412
413qint64 QLocalSocket::bytesAvailable() const
414{
415 Q_D(const QLocalSocket);
416 return QIODevice::bytesAvailable() + d->unixSocket.bytesAvailable();
417}
418
419qint64 QLocalSocket::bytesToWrite() const
420{
421 Q_D(const QLocalSocket);
422 return d->unixSocket.bytesToWrite();
423}
424
425bool QLocalSocket::canReadLine() const
426{
427 Q_D(const QLocalSocket);
428 return QIODevice::canReadLine() || d->unixSocket.canReadLine();
429}
430
431void QLocalSocket::close()
432{
433 Q_D(QLocalSocket);
434 d->unixSocket.close();
435 d->cancelDelayedConnect();
436 if (d->connectingSocket != -1)
437 ::close(fd: d->connectingSocket);
438 d->connectingSocket = -1;
439 d->connectingName.clear();
440 d->connectingOpenMode = { };
441 d->serverName.clear();
442 d->fullServerName.clear();
443 QIODevice::close();
444}
445
446bool QLocalSocket::waitForBytesWritten(int msecs)
447{
448 Q_D(QLocalSocket);
449 return d->unixSocket.waitForBytesWritten(msecs);
450}
451
452bool QLocalSocket::flush()
453{
454 Q_D(QLocalSocket);
455 return d->unixSocket.flush();
456}
457
458void QLocalSocket::disconnectFromServer()
459{
460 Q_D(QLocalSocket);
461 d->unixSocket.disconnectFromHost();
462}
463
464QLocalSocket::LocalSocketError QLocalSocket::error() const
465{
466 Q_D(const QLocalSocket);
467 switch (d->unixSocket.error()) {
468 case QAbstractSocket::ConnectionRefusedError:
469 return QLocalSocket::ConnectionRefusedError;
470 case QAbstractSocket::RemoteHostClosedError:
471 return QLocalSocket::PeerClosedError;
472 case QAbstractSocket::HostNotFoundError:
473 return QLocalSocket::ServerNotFoundError;
474 case QAbstractSocket::SocketAccessError:
475 return QLocalSocket::SocketAccessError;
476 case QAbstractSocket::SocketResourceError:
477 return QLocalSocket::SocketResourceError;
478 case QAbstractSocket::SocketTimeoutError:
479 return QLocalSocket::SocketTimeoutError;
480 case QAbstractSocket::DatagramTooLargeError:
481 return QLocalSocket::DatagramTooLargeError;
482 case QAbstractSocket::NetworkError:
483 return QLocalSocket::ConnectionError;
484 case QAbstractSocket::UnsupportedSocketOperationError:
485 return QLocalSocket::UnsupportedSocketOperationError;
486 case QAbstractSocket::UnknownSocketError:
487 return QLocalSocket::UnknownSocketError;
488 default:
489#if defined QLOCALSOCKET_DEBUG
490 qWarning() << "QLocalSocket error not handled:" << d->unixSocket.error();
491#endif
492 break;
493 }
494 return UnknownSocketError;
495}
496
497bool QLocalSocket::isValid() const
498{
499 Q_D(const QLocalSocket);
500 return d->unixSocket.isValid();
501}
502
503qint64 QLocalSocket::readBufferSize() const
504{
505 Q_D(const QLocalSocket);
506 return d->unixSocket.readBufferSize();
507}
508
509void QLocalSocket::setReadBufferSize(qint64 size)
510{
511 Q_D(QLocalSocket);
512 d->unixSocket.setReadBufferSize(size);
513}
514
515bool QLocalSocket::waitForConnected(int msec)
516{
517 Q_D(QLocalSocket);
518
519 if (state() != ConnectingState)
520 return (state() == ConnectedState);
521
522 QElapsedTimer timer;
523 timer.start();
524
525 pollfd pfd = qt_make_pollfd(fd: d->connectingSocket, POLLIN);
526
527 do {
528 const int timeout = (msec > 0) ? qMax(a: msec - timer.elapsed(), Q_INT64_C(0)) : msec;
529 const int result = qt_poll_msecs(fds: &pfd, nfds: 1, timeout);
530
531 if (result == -1)
532 d->setErrorAndEmit(error: QLocalSocket::UnknownSocketError,
533 function: QLatin1String("QLocalSocket::waitForConnected"));
534 else if (result > 0)
535 d->_q_connectToSocket();
536 } while (state() == ConnectingState && !timer.hasExpired(timeout: msec));
537
538 return (state() == ConnectedState);
539}
540
541bool QLocalSocket::waitForDisconnected(int msecs)
542{
543 Q_D(QLocalSocket);
544 if (state() == UnconnectedState) {
545 qWarning(msg: "QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState");
546 return false;
547 }
548 return (d->unixSocket.waitForDisconnected(msecs));
549}
550
551bool QLocalSocket::waitForReadyRead(int msecs)
552{
553 Q_D(QLocalSocket);
554 if (state() == QLocalSocket::UnconnectedState)
555 return false;
556 return (d->unixSocket.waitForReadyRead(msecs));
557}
558
559QT_END_NAMESPACE
560

source code of qtbase/src/network/socket/qlocalsocket_unix.cpp