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(&unixSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
76 q->connect(&unixSocket, SIGNAL(bytesWritten(qint64)),
77 q, SIGNAL(bytesWritten(qint64)));
78 q->connect(&unixSocket, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
79 // QAbstractSocket signals
80 q->connect(&unixSocket, SIGNAL(connected()), q, SIGNAL(connected()));
81 q->connect(&unixSocket, SIGNAL(disconnected()), q, SIGNAL(disconnected()));
82 q->connect(&unixSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
83 q, SLOT(_q_stateChanged(QAbstractSocket::SocketState)));
84 q->connect(&unixSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
85 q, SLOT(_q_errorOccurred(QAbstractSocket::SocketError)));
86 q->connect(&unixSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
87 unixSocket.setParent(q);
88}
89
90void QLocalSocketPrivate::_q_errorOccurred(QAbstractSocket::SocketError socketError)
91{
92 Q_Q(QLocalSocket);
93 QString function = QLatin1String("QLocalSocket");
94 QLocalSocket::LocalSocketError error = (QLocalSocket::LocalSocketError)socketError;
95 QString errorString = generateErrorString(error, function);
96 q->setErrorString(errorString);
97 emit q->errorOccurred(error);
98}
99
100void QLocalSocketPrivate::_q_stateChanged(QAbstractSocket::SocketState newState)
101{
102 Q_Q(QLocalSocket);
103 QLocalSocket::LocalSocketState currentState = state;
104 switch(newState) {
105 case QAbstractSocket::UnconnectedState:
106 state = QLocalSocket::UnconnectedState;
107 serverName.clear();
108 fullServerName.clear();
109 break;
110 case QAbstractSocket::ConnectingState:
111 state = QLocalSocket::ConnectingState;
112 break;
113 case QAbstractSocket::ConnectedState:
114 state = QLocalSocket::ConnectedState;
115 break;
116 case QAbstractSocket::ClosingState:
117 state = QLocalSocket::ClosingState;
118 break;
119 default:
120#if defined QLOCALSOCKET_DEBUG
121 qWarning() << "QLocalSocket::Unhandled socket state change:" << newState;
122#endif
123 return;
124 }
125 if (currentState != state)
126 emit q->stateChanged(state);
127}
128
129QString QLocalSocketPrivate::generateErrorString(QLocalSocket::LocalSocketError error, const QString &function) const
130{
131 QString errorString;
132 switch (error) {
133 case QLocalSocket::ConnectionRefusedError:
134 errorString = QLocalSocket::tr("%1: Connection refused").arg(function);
135 break;
136 case QLocalSocket::PeerClosedError:
137 errorString = QLocalSocket::tr("%1: Remote closed").arg(function);
138 break;
139 case QLocalSocket::ServerNotFoundError:
140 errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
141 break;
142 case QLocalSocket::SocketAccessError:
143 errorString = QLocalSocket::tr("%1: Socket access error").arg(function);
144 break;
145 case QLocalSocket::SocketResourceError:
146 errorString = QLocalSocket::tr("%1: Socket resource error").arg(function);
147 break;
148 case QLocalSocket::SocketTimeoutError:
149 errorString = QLocalSocket::tr("%1: Socket operation timed out").arg(function);
150 break;
151 case QLocalSocket::DatagramTooLargeError:
152 errorString = QLocalSocket::tr("%1: Datagram too large").arg(function);
153 break;
154 case QLocalSocket::ConnectionError:
155 errorString = QLocalSocket::tr("%1: Connection error").arg(function);
156 break;
157 case QLocalSocket::UnsupportedSocketOperationError:
158 errorString = QLocalSocket::tr("%1: The socket operation is not supported").arg(function);
159 break;
160 case QLocalSocket::OperationError:
161 errorString = QLocalSocket::tr("%1: Operation not permitted when socket is in this state").arg(function);
162 break;
163 case QLocalSocket::UnknownSocketError:
164 default:
165 errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(errno);
166 }
167 return errorString;
168}
169
170void QLocalSocketPrivate::setErrorAndEmit(QLocalSocket::LocalSocketError error, const QString &function)
171{
172 Q_Q(QLocalSocket);
173 switch (error) {
174 case QLocalSocket::ConnectionRefusedError:
175 unixSocket.setSocketError(QAbstractSocket::ConnectionRefusedError);
176 break;
177 case QLocalSocket::PeerClosedError:
178 unixSocket.setSocketError(QAbstractSocket::RemoteHostClosedError);
179 break;
180 case QLocalSocket::ServerNotFoundError:
181 unixSocket.setSocketError(QAbstractSocket::HostNotFoundError);
182 break;
183 case QLocalSocket::SocketAccessError:
184 unixSocket.setSocketError(QAbstractSocket::SocketAccessError);
185 break;
186 case QLocalSocket::SocketResourceError:
187 unixSocket.setSocketError(QAbstractSocket::SocketResourceError);
188 break;
189 case QLocalSocket::SocketTimeoutError:
190 unixSocket.setSocketError(QAbstractSocket::SocketTimeoutError);
191 break;
192 case QLocalSocket::DatagramTooLargeError:
193 unixSocket.setSocketError(QAbstractSocket::DatagramTooLargeError);
194 break;
195 case QLocalSocket::ConnectionError:
196 unixSocket.setSocketError(QAbstractSocket::NetworkError);
197 break;
198 case QLocalSocket::UnsupportedSocketOperationError:
199 unixSocket.setSocketError(QAbstractSocket::UnsupportedSocketOperationError);
200 break;
201 case QLocalSocket::UnknownSocketError:
202 default:
203 unixSocket.setSocketError(QAbstractSocket::UnknownSocketError);
204 }
205
206 QString errorString = generateErrorString(error, function);
207 q->setErrorString(errorString);
208 emit q->errorOccurred(error);
209
210 // errors cause a disconnect
211 unixSocket.setSocketState(QAbstractSocket::UnconnectedState);
212 bool stateChanged = (state != QLocalSocket::UnconnectedState);
213 state = QLocalSocket::UnconnectedState;
214 q->close();
215 if (stateChanged)
216 q->emit stateChanged(state);
217}
218
219void QLocalSocket::connectToServer(OpenMode openMode)
220{
221 Q_D(QLocalSocket);
222 if (state() == ConnectedState || state() == ConnectingState) {
223 QString errorString = d->generateErrorString(QLocalSocket::OperationError, QLatin1String("QLocalSocket::connectToserver"));
224 setErrorString(errorString);
225 emit errorOccurred(QLocalSocket::OperationError);
226 return;
227 }
228
229 d->errorString.clear();
230 d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
231 d->state = ConnectingState;
232 emit stateChanged(d->state);
233
234 if (d->serverName.isEmpty()) {
235 d->setErrorAndEmit(ServerNotFoundError,
236 QLatin1String("QLocalSocket::connectToServer"));
237 return;
238 }
239
240 // create the socket
241 if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0, O_NONBLOCK))) {
242 d->setErrorAndEmit(UnsupportedSocketOperationError,
243 QLatin1String("QLocalSocket::connectToServer"));
244 return;
245 }
246
247 // _q_connectToSocket does the actual connecting
248 d->connectingName = d->serverName;
249 d->connectingOpenMode = openMode;
250 d->_q_connectToSocket();
251 return;
252}
253
254/*!
255 \internal
256
257 Tries to connect connectingName and connectingOpenMode
258
259 \sa connectToServer(), waitForConnected()
260 */
261void QLocalSocketPrivate::_q_connectToSocket()
262{
263 Q_Q(QLocalSocket);
264 QString connectingPathName;
265
266 // determine the full server path
267 if (connectingName.startsWith(QLatin1Char('/'))) {
268 connectingPathName = connectingName;
269 } else {
270 connectingPathName = QDir::tempPath();
271 connectingPathName += QLatin1Char('/') + connectingName;
272 }
273
274 const QByteArray encodedConnectingPathName = QFile::encodeName(connectingPathName);
275 struct sockaddr_un name;
276 name.sun_family = PF_UNIX;
277 if (sizeof(name.sun_path) < (uint)encodedConnectingPathName.size() + 1) {
278 QString function = QLatin1String("QLocalSocket::connectToServer");
279 setErrorAndEmit(QLocalSocket::ServerNotFoundError, function);
280 return;
281 }
282 ::memcpy(name.sun_path, encodedConnectingPathName.constData(),
283 encodedConnectingPathName.size() + 1);
284 if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&name, sizeof(name))) {
285 QString function = QLatin1String("QLocalSocket::connectToServer");
286 switch (errno)
287 {
288 case EINVAL:
289 case ECONNREFUSED:
290 setErrorAndEmit(QLocalSocket::ConnectionRefusedError, function);
291 break;
292 case ENOENT:
293 setErrorAndEmit(QLocalSocket::ServerNotFoundError, function);
294 break;
295 case EACCES:
296 case EPERM:
297 setErrorAndEmit(QLocalSocket::SocketAccessError, function);
298 break;
299 case ETIMEDOUT:
300 setErrorAndEmit(QLocalSocket::SocketTimeoutError, function);
301 break;
302 case EAGAIN:
303 // Try again later, all of the sockets listening are full
304 if (!delayConnect) {
305 delayConnect = new QSocketNotifier(connectingSocket, QSocketNotifier::Write, q);
306 q->connect(delayConnect, SIGNAL(activated(QSocketDescriptor)), q, SLOT(_q_connectToSocket()));
307 }
308 if (!connectTimer) {
309 connectTimer = new QTimer(q);
310 q->connect(connectTimer, SIGNAL(timeout()),
311 q, SLOT(_q_abortConnectionAttempt()),
312 Qt::DirectConnection);
313 connectTimer->start(QT_CONNECT_TIMEOUT);
314 }
315 delayConnect->setEnabled(true);
316 break;
317 default:
318 setErrorAndEmit(QLocalSocket::UnknownSocketError, function);
319 }
320 return;
321 }
322
323 // connected!
324 cancelDelayedConnect();
325
326 serverName = connectingName;
327 fullServerName = connectingPathName;
328 if (unixSocket.setSocketDescriptor(connectingSocket,
329 QAbstractSocket::ConnectedState, connectingOpenMode)) {
330 q->QIODevice::open(connectingOpenMode | QIODevice::Unbuffered);
331 q->emit connected();
332 } else {
333 QString function = QLatin1String("QLocalSocket::connectToServer");
334 setErrorAndEmit(QLocalSocket::UnknownSocketError, function);
335 }
336 connectingSocket = -1;
337 connectingName.clear();
338 connectingOpenMode = { };
339}
340
341bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
342 LocalSocketState socketState, OpenMode openMode)
343{
344 Q_D(QLocalSocket);
345 QAbstractSocket::SocketState newSocketState = QAbstractSocket::UnconnectedState;
346 switch (socketState) {
347 case ConnectingState:
348 newSocketState = QAbstractSocket::ConnectingState;
349 break;
350 case ConnectedState:
351 newSocketState = QAbstractSocket::ConnectedState;
352 break;
353 case ClosingState:
354 newSocketState = QAbstractSocket::ClosingState;
355 break;
356 case UnconnectedState:
357 newSocketState = QAbstractSocket::UnconnectedState;
358 break;
359 }
360 QIODevice::open(openMode);
361 d->state = socketState;
362 return d->unixSocket.setSocketDescriptor(socketDescriptor,
363 newSocketState, openMode);
364}
365
366void QLocalSocketPrivate::_q_abortConnectionAttempt()
367{
368 Q_Q(QLocalSocket);
369 q->close();
370}
371
372void QLocalSocketPrivate::cancelDelayedConnect()
373{
374 if (delayConnect) {
375 delayConnect->setEnabled(false);
376 delete delayConnect;
377 delayConnect = nullptr;
378 connectTimer->stop();
379 delete connectTimer;
380 connectTimer = nullptr;
381 }
382}
383
384qintptr QLocalSocket::socketDescriptor() const
385{
386 Q_D(const QLocalSocket);
387 return d->unixSocket.socketDescriptor();
388}
389
390qint64 QLocalSocket::readData(char *data, qint64 c)
391{
392 Q_D(QLocalSocket);
393 return d->unixSocket.read(data, c);
394}
395
396qint64 QLocalSocket::skipData(qint64 maxSize)
397{
398 return d_func()->unixSocket.skip(maxSize);
399}
400
401qint64 QLocalSocket::writeData(const char *data, qint64 c)
402{
403 Q_D(QLocalSocket);
404 return d->unixSocket.writeData(data, 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(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(d->connectingSocket, POLLIN);
526
527 do {
528 const int timeout = (msec > 0) ? qMax(msec - timer.elapsed(), Q_INT64_C(0)) : msec;
529 const int result = qt_poll_msecs(&pfd, 1, timeout);
530
531 if (result == -1)
532 d->setErrorAndEmit(QLocalSocket::UnknownSocketError,
533 QLatin1String("QLocalSocket::waitForConnected"));
534 else if (result > 0)
535 d->_q_connectToSocket();
536 } while (state() == ConnectingState && !timer.hasExpired(msec));
537
538 return (state() == ConnectedState);
539}
540
541bool QLocalSocket::waitForDisconnected(int msecs)
542{
543 Q_D(QLocalSocket);
544 if (state() == UnconnectedState) {
545 qWarning("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