10#include <sys/socket.h>
17#include <qdeadlinetimer.h>
19#include <qstringconverter.h>
22# include <selectLib.h>
25using namespace std::chrono_literals;
27#define QT_CONNECT_TIMEOUT 30000
31using namespace Qt::StringLiterals;
35static QString pathNameForConnection(
const QString &connectingName,
36 QLocalSocket::SocketOptions options)
38 if (options.testFlag(QLocalSocket::AbstractNamespaceOption)
39 || connectingName.startsWith(u'/')) {
40 return connectingName;
43 return QDir::tempPath() + u'/' + connectingName;
46static QLocalSocket::SocketOptions optionsForPlatform(QLocalSocket::SocketOptions srcOptions)
50 if (!PlatformSupportsAbstractNamespace)
51 return QLocalSocket::NoOptions;
57 delayConnect(
nullptr),
58 connectTimer(
nullptr),
60 state(QLocalSocket::UnconnectedState),
61 socketOptions(QLocalSocket::NoOptions)
69 q->connect(&unixSocket, SIGNAL(bytesWritten(qint64)),
70 q, SIGNAL(bytesWritten(qint64)));
71 q->connect(&unixSocket, SIGNAL(channelBytesWritten(
int, qint64)),
72 q, SIGNAL(channelBytesWritten(
int, qint64)));
73 q->connect(&unixSocket, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
74 q->connect(&unixSocket, SIGNAL(channelReadyRead(
int)),
75 q, SIGNAL(channelReadyRead(
int)));
77 q->connect(&unixSocket, SIGNAL(connected()), q, SIGNAL(connected()));
78 q->connect(&unixSocket, SIGNAL(disconnected()), q, SIGNAL(disconnected()));
79 q->connect(&unixSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
80 q, SLOT(_q_stateChanged(QAbstractSocket::SocketState)));
81 q->connect(&unixSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
82 q, SLOT(_q_errorOccurred(QAbstractSocket::SocketError)));
83 q->connect(&unixSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
90 QString function =
"QLocalSocket"_L1;
91 QLocalSocket::LocalSocketError error = (QLocalSocket::LocalSocketError)socketError;
92 QString errorString = generateErrorString(error, function);
93 q->setErrorString(errorString);
94 emit q->errorOccurred(error);
100 QLocalSocket::LocalSocketState currentState = state;
102 case QAbstractSocket::UnconnectedState:
103 state = QLocalSocket::UnconnectedState;
105 fullServerName.clear();
107 case QAbstractSocket::ConnectingState:
108 state = QLocalSocket::ConnectingState;
110 case QAbstractSocket::ConnectedState:
111 state = QLocalSocket::ConnectedState;
113 case QAbstractSocket::ClosingState:
114 state = QLocalSocket::ClosingState;
117#if defined QLOCALSOCKET_DEBUG
118 qWarning() <<
"QLocalSocket::Unhandled socket state change:" << newState;
122 if (currentState != state)
123 emit q->stateChanged(state);
130 case QLocalSocket::ConnectionRefusedError:
131 errorString = QLocalSocket::tr(
"%1: Connection refused").arg(function);
133 case QLocalSocket::PeerClosedError:
134 errorString = QLocalSocket::tr(
"%1: Remote closed").arg(function);
136 case QLocalSocket::ServerNotFoundError:
137 errorString = QLocalSocket::tr(
"%1: Invalid name").arg(function);
139 case QLocalSocket::SocketAccessError:
140 errorString = QLocalSocket::tr(
"%1: Socket access error").arg(function);
142 case QLocalSocket::SocketResourceError:
143 errorString = QLocalSocket::tr(
"%1: Socket resource error").arg(function);
145 case QLocalSocket::SocketTimeoutError:
146 errorString = QLocalSocket::tr(
"%1: Socket operation timed out").arg(function);
148 case QLocalSocket::DatagramTooLargeError:
149 errorString = QLocalSocket::tr(
"%1: Datagram too large").arg(function);
151 case QLocalSocket::ConnectionError:
152 errorString = QLocalSocket::tr(
"%1: Connection error").arg(function);
154 case QLocalSocket::UnsupportedSocketOperationError:
155 errorString = QLocalSocket::tr(
"%1: The socket operation is not supported").arg(function);
157 case QLocalSocket::OperationError:
158 errorString = QLocalSocket::tr(
"%1: Operation not permitted when socket is in this state").arg(function);
160 case QLocalSocket::UnknownSocketError:
162 errorString = QLocalSocket::tr(
"%1: Unknown error %2").arg(function).arg(errno);
171 case QLocalSocket::ConnectionRefusedError:
172 unixSocket.setSocketError(QAbstractSocket::ConnectionRefusedError);
174 case QLocalSocket::PeerClosedError:
175 unixSocket.setSocketError(QAbstractSocket::RemoteHostClosedError);
177 case QLocalSocket::ServerNotFoundError:
178 unixSocket.setSocketError(QAbstractSocket::HostNotFoundError);
180 case QLocalSocket::SocketAccessError:
181 unixSocket.setSocketError(QAbstractSocket::SocketAccessError);
183 case QLocalSocket::SocketResourceError:
184 unixSocket.setSocketError(QAbstractSocket::SocketResourceError);
186 case QLocalSocket::SocketTimeoutError:
187 unixSocket.setSocketError(QAbstractSocket::SocketTimeoutError);
189 case QLocalSocket::DatagramTooLargeError:
190 unixSocket.setSocketError(QAbstractSocket::DatagramTooLargeError);
192 case QLocalSocket::ConnectionError:
193 unixSocket.setSocketError(QAbstractSocket::NetworkError);
195 case QLocalSocket::UnsupportedSocketOperationError:
196 unixSocket.setSocketError(QAbstractSocket::UnsupportedSocketOperationError);
198 case QLocalSocket::UnknownSocketError:
200 unixSocket.setSocketError(QAbstractSocket::UnknownSocketError);
203 QString errorString = generateErrorString(error, function);
204 q->setErrorString(errorString);
205 emit q->errorOccurred(error);
208 unixSocket.setSocketState(QAbstractSocket::UnconnectedState);
209 bool stateChanged = (state != QLocalSocket::UnconnectedState);
210 state = QLocalSocket::UnconnectedState;
213 q->emit stateChanged(state);
216void QLocalSocket::connectToServer(OpenMode openMode)
219 if (state() == ConnectedState || state() == ConnectingState) {
220 QString errorString = d->generateErrorString(QLocalSocket::OperationError,
"QLocalSocket::connectToserver"_L1);
221 setErrorString(errorString);
222 emit errorOccurred(QLocalSocket::OperationError);
226 d->errorString.clear();
227 d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
228 d->state = ConnectingState;
229 emit stateChanged(d->state);
231 if (d->serverName.isEmpty()) {
232 d->setErrorAndEmit(ServerNotFoundError,
"QLocalSocket::connectToServer"_L1);
237 if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0, O_NONBLOCK))) {
238 d->setErrorAndEmit(UnsupportedSocketOperationError,
"QLocalSocket::connectToServer"_L1);
243 d->connectingName = d->serverName;
244 d->connectingOpenMode = openMode;
245 d->_q_connectToSocket();
250
251
252
253
254
255
260 QLocalSocket::SocketOptions options = optionsForPlatform(socketOptions);
261 const QString connectingPathName = pathNameForConnection(connectingName, options);
262 const QByteArray encodedConnectingPathName = QFile::encodeName(connectingPathName);
263 struct ::sockaddr_un addr;
264 addr.sun_family = PF_UNIX;
265 memset(addr.sun_path, 0,
sizeof(addr.sun_path));
268 constexpr unsigned int extraCharacters = PlatformSupportsAbstractNamespace ? 2 : 1;
270 if (
sizeof(addr.sun_path) <
static_cast<size_t>(encodedConnectingPathName.size() + extraCharacters)) {
271 QString function =
"QLocalSocket::connectToServer"_L1;
272 setErrorAndEmit(QLocalSocket::ServerNotFoundError, function);
276 QT_SOCKLEN_T addrSize =
sizeof(::sockaddr_un);
277 if (options.testFlag(QLocalSocket::AbstractNamespaceOption)) {
278 ::memcpy(addr.sun_path + 1, encodedConnectingPathName.constData(),
279 encodedConnectingPathName.size() + 1);
280 addrSize = offsetof(::sockaddr_un, sun_path) + encodedConnectingPathName.size() + 1;
282 ::memcpy(addr.sun_path, encodedConnectingPathName.constData(),
283 encodedConnectingPathName.size() + 1);
285 if (-1 == qt_safe_connect(connectingSocket, (
struct sockaddr *)&addr, addrSize)) {
286 QString function =
"QLocalSocket::connectToServer"_L1;
291 setErrorAndEmit(QLocalSocket::ConnectionRefusedError, function);
294 setErrorAndEmit(QLocalSocket::ServerNotFoundError, function);
298 setErrorAndEmit(QLocalSocket::SocketAccessError, function);
301 setErrorAndEmit(QLocalSocket::SocketTimeoutError, function);
306 delayConnect =
new QSocketNotifier(connectingSocket, QSocketNotifier::Write, q);
307 q->connect(delayConnect, SIGNAL(activated(QSocketDescriptor)), q, SLOT(_q_connectToSocket()));
310 connectTimer =
new QTimer(q);
311 q->connect(connectTimer, SIGNAL(timeout()),
312 q, SLOT(_q_abortConnectionAttempt()),
313 Qt::DirectConnection);
316 delayConnect->setEnabled(
true);
319 setErrorAndEmit(QLocalSocket::UnknownSocketError, function);
327 serverName = connectingName;
328 fullServerName = connectingPathName;
330 QAbstractSocket::ConnectedState, connectingOpenMode)) {
331 q->QIODevice::open(connectingOpenMode);
334 QString function =
"QLocalSocket::connectToServer"_L1;
335 setErrorAndEmit(QLocalSocket::UnknownSocketError, function);
338 connectingName.clear();
339 connectingOpenMode = { };
342bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
343 LocalSocketState socketState, OpenMode openMode)
346 QAbstractSocket::SocketState newSocketState = QAbstractSocket::UnconnectedState;
347 switch (socketState) {
348 case ConnectingState:
349 newSocketState = QAbstractSocket::ConnectingState;
352 newSocketState = QAbstractSocket::ConnectedState;
355 newSocketState = QAbstractSocket::ClosingState;
357 case UnconnectedState:
358 newSocketState = QAbstractSocket::UnconnectedState;
361 QIODevice::open(openMode);
362 d->state = socketState;
363 d->describeSocket(socketDescriptor);
364 const bool result = d->unixSocket.setSocketDescriptor(socketDescriptor,
365 newSocketState, openMode);
370 emit stateChanged(d->state);
376 bool abstractAddress =
false;
378 struct ::sockaddr_un addr;
379 QT_SOCKLEN_T len =
sizeof(addr);
380 memset(&addr, 0,
sizeof(addr));
381 const int getpeernameStatus = ::getpeername(socketDescriptor, (sockaddr *)&addr, &len);
382 if (getpeernameStatus != 0 || len == offsetof(sockaddr_un, sun_path)) {
385 if (::getsockname(socketDescriptor, (sockaddr *)&addr, &len) != 0)
388 if (parseSockaddr(addr,
static_cast<uint>(len), fullServerName, serverName, abstractAddress)) {
389 QLocalSocket::SocketOptions options = socketOptions.value();
390 socketOptions = options.setFlag(QLocalSocket::AbstractNamespaceOption, abstractAddress);
396 QString &fullServerName,
398 bool &abstractNamespace)
400 if (len <= offsetof(::sockaddr_un, sun_path))
402 len -= offsetof(::sockaddr_un, sun_path);
404 abstractNamespace = PlatformSupportsAbstractNamespace
405 && (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0);
406 QStringDecoder toUtf16(QStringDecoder::System, QStringDecoder::Flag::Stateless);
410 QByteArrayView textData(addr.sun_path + (abstractNamespace ? 1 : 0),
411 len - (abstractNamespace ? 1 : 0));
412 QString name = toUtf16(textData);
413 if (!name.isEmpty() && !toUtf16.hasError()) {
416 if (!abstractNamespace && (name.at(name.size() - 1) == QChar::fromLatin1(
'\0'))) {
417 int truncPos = name.size() - 1;
418 while (truncPos > 0 && name.at(truncPos - 1) == QChar::fromLatin1(
'\0'))
420 name.truncate(truncPos);
422 fullServerName = name;
423 serverName = abstractNamespace
425 : fullServerName.mid(fullServerName.lastIndexOf(u'/') + 1);
426 if (serverName.isEmpty())
427 serverName = fullServerName;
441 delayConnect->setEnabled(
false);
443 delayConnect =
nullptr;
444 connectTimer->stop();
446 connectTimer =
nullptr;
450qintptr QLocalSocket::socketDescriptor()
const
452 Q_D(
const QLocalSocket);
453 return d->unixSocket.socketDescriptor();
456qint64 QLocalSocket::readData(
char *data, qint64 c)
459 return d->unixSocket.read(data, c);
462qint64 QLocalSocket::readLineData(
char *data, qint64 maxSize)
469 return d_func()->unixSocket.readLine(data, maxSize + 1);
472qint64 QLocalSocket::skipData(qint64 maxSize)
474 return d_func()->unixSocket.skip(maxSize);
477qint64 QLocalSocket::writeData(
const char *data, qint64 c)
480 return d->unixSocket.writeData(data, c);
483void QLocalSocket::abort()
486 d->unixSocket.abort();
490qint64 QLocalSocket::bytesAvailable()
const
492 Q_D(
const QLocalSocket);
493 return QIODevice::bytesAvailable() + d->unixSocket.bytesAvailable();
496qint64 QLocalSocket::bytesToWrite()
const
498 Q_D(
const QLocalSocket);
499 return d->unixSocket.bytesToWrite();
502bool QLocalSocket::canReadLine()
const
504 Q_D(
const QLocalSocket);
505 return QIODevice::canReadLine() || d->unixSocket.canReadLine();
508void QLocalSocket::close()
513 d->unixSocket.close();
514 d->cancelDelayedConnect();
515 if (d->connectingSocket != -1)
516 ::close(d->connectingSocket);
517 d->connectingSocket = -1;
518 d->connectingName.clear();
519 d->connectingOpenMode = { };
520 d->serverName.clear();
521 d->fullServerName.clear();
524bool QLocalSocket::waitForBytesWritten(
int msecs)
527 return d->unixSocket.waitForBytesWritten(msecs);
530bool QLocalSocket::flush()
533 return d->unixSocket.flush();
536void QLocalSocket::disconnectFromServer()
539 d->unixSocket.disconnectFromHost();
542QLocalSocket::LocalSocketError QLocalSocket::error()
const
544 Q_D(
const QLocalSocket);
545 switch (d->unixSocket.error()) {
546 case QAbstractSocket::ConnectionRefusedError:
547 return QLocalSocket::ConnectionRefusedError;
548 case QAbstractSocket::RemoteHostClosedError:
549 return QLocalSocket::PeerClosedError;
550 case QAbstractSocket::HostNotFoundError:
551 return QLocalSocket::ServerNotFoundError;
552 case QAbstractSocket::SocketAccessError:
553 return QLocalSocket::SocketAccessError;
554 case QAbstractSocket::SocketResourceError:
555 return QLocalSocket::SocketResourceError;
556 case QAbstractSocket::SocketTimeoutError:
557 return QLocalSocket::SocketTimeoutError;
558 case QAbstractSocket::DatagramTooLargeError:
559 return QLocalSocket::DatagramTooLargeError;
560 case QAbstractSocket::NetworkError:
561 return QLocalSocket::ConnectionError;
562 case QAbstractSocket::UnsupportedSocketOperationError:
563 return QLocalSocket::UnsupportedSocketOperationError;
564 case QAbstractSocket::UnknownSocketError:
565 return QLocalSocket::UnknownSocketError;
567#if defined QLOCALSOCKET_DEBUG
568 qWarning() <<
"QLocalSocket error not handled:" << d->unixSocket.error();
572 return UnknownSocketError;
575bool QLocalSocket::isValid()
const
577 Q_D(
const QLocalSocket);
578 return d->unixSocket.isValid();
581qint64 QLocalSocket::readBufferSize()
const
583 Q_D(
const QLocalSocket);
584 return d->unixSocket.readBufferSize();
587void QLocalSocket::setReadBufferSize(qint64 size)
590 d->unixSocket.setReadBufferSize(size);
593bool QLocalSocket::waitForConnected(
int msec)
597 if (state() != ConnectingState)
598 return (state() == ConnectedState);
600 pollfd pfd = qt_make_pollfd(d->connectingSocket, POLLIN);
602 QDeadlineTimer deadline{msec};
603 auto remainingTime = deadline.remainingTimeAsDuration();
606 const int result = qt_safe_poll(&pfd, 1, deadline);
608 d->setErrorAndEmit(QLocalSocket::UnknownSocketError,
609 "QLocalSocket::waitForConnected"_L1);
611 d->_q_connectToSocket();
612 }
while (state() == ConnectingState
613 && (remainingTime = deadline.remainingTimeAsDuration()) > 0ns);
615 return (state() == ConnectedState);
618bool QLocalSocket::waitForDisconnected(
int msecs)
621 if (state() == UnconnectedState) {
622 qWarning(
"QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState");
625 return (d->unixSocket.waitForDisconnected(msecs));
628bool QLocalSocket::waitForReadyRead(
int msecs)
631 if (state() == QLocalSocket::UnconnectedState)
633 return (d->unixSocket.waitForReadyRead(msecs));
void _q_stateChanged(QAbstractSocket::SocketState newState)
void describeSocket(qintptr socketDescriptor)
QLocalUnixSocket unixSocket
void _q_abortConnectionAttempt()
void cancelDelayedConnect()
void _q_errorOccurred(QAbstractSocket::SocketError newError)
void setErrorAndEmit(QLocalSocket::LocalSocketError, const QString &function)
void _q_connectToSocket()
QString generateErrorString(QLocalSocket::LocalSocketError, const QString &function) const
#define QT_CONNECT_TIMEOUT