Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qlocalsocket_unix.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qlocalsocket.h"
5#include "qlocalsocket_p.h"
6#include "qnet_unix_p.h"
7
8#include <sys/types.h>
9#include <sys/socket.h>
10#include <sys/un.h>
11#include <unistd.h>
12#include <fcntl.h>
13#include <errno.h>
14
15#include <qdir.h>
16#include <qdeadlinetimer.h>
17#include <qdebug.h>
18#include <qstringconverter.h>
19
20#ifdef Q_OS_VXWORKS
21# include <selectLib.h>
22#endif
23
24using namespace std::chrono_literals;
25
26#define QT_CONNECT_TIMEOUT 30000
27
29
30using namespace Qt::StringLiterals;
31
32namespace {
33// determine the full server path
34static QString pathNameForConnection(const QString &connectingName,
35 QLocalSocket::SocketOptions options)
36{
37 if (options.testFlag(QLocalSocket::AbstractNamespaceOption)
38 || connectingName.startsWith(u'/')) {
39 return connectingName;
40 }
41
42 return QDir::tempPath() + u'/' + connectingName;
43}
44
45static QLocalSocket::SocketOptions optionsForPlatform(QLocalSocket::SocketOptions srcOptions)
46{
47 // For OS that does not support abstract namespace the AbstractNamespaceOption
48 // option is cleared.
51 return srcOptions;
52}
53}
54
56 delayConnect(nullptr),
57 connectTimer(nullptr),
58 connectingSocket(-1),
59 state(QLocalSocket::UnconnectedState),
60 socketOptions(QLocalSocket::NoOptions)
61{
62}
63
65{
66 Q_Q(QLocalSocket);
67 // QIODevice signals
70 q->connect(&unixSocket, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
71 // QAbstractSocket signals
72 q->connect(&unixSocket, SIGNAL(connected()), q, SIGNAL(connected()));
73 q->connect(&unixSocket, SIGNAL(disconnected()), q, SIGNAL(disconnected()));
74 q->connect(&unixSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
76 q->connect(&unixSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
78 q->connect(&unixSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
80}
81
83{
84 Q_Q(QLocalSocket);
85 QString function = "QLocalSocket"_L1;
88 q->setErrorString(errorString);
89 emit q->errorOccurred(error);
90}
91
93{
94 Q_Q(QLocalSocket);
96 switch(newState) {
101 break;
104 break;
107 break;
110 break;
111 default:
112#if defined QLOCALSOCKET_DEBUG
113 qWarning() << "QLocalSocket::Unhandled socket state change:" << newState;
114#endif
115 return;
116 }
117 if (currentState != state)
118 emit q->stateChanged(state);
119}
120
122{
124 switch (error) {
126 errorString = QLocalSocket::tr("%1: Connection refused").arg(function);
127 break;
129 errorString = QLocalSocket::tr("%1: Remote closed").arg(function);
130 break;
132 errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
133 break;
135 errorString = QLocalSocket::tr("%1: Socket access error").arg(function);
136 break;
138 errorString = QLocalSocket::tr("%1: Socket resource error").arg(function);
139 break;
141 errorString = QLocalSocket::tr("%1: Socket operation timed out").arg(function);
142 break;
144 errorString = QLocalSocket::tr("%1: Datagram too large").arg(function);
145 break;
147 errorString = QLocalSocket::tr("%1: Connection error").arg(function);
148 break;
150 errorString = QLocalSocket::tr("%1: The socket operation is not supported").arg(function);
151 break;
153 errorString = QLocalSocket::tr("%1: Operation not permitted when socket is in this state").arg(function);
154 break;
156 default:
157 errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(errno);
158 }
159 return errorString;
160}
161
163{
164 Q_Q(QLocalSocket);
165 switch (error) {
168 break;
171 break;
174 break;
177 break;
180 break;
183 break;
186 break;
189 break;
192 break;
194 default:
196 }
197
199 q->setErrorString(errorString);
200 emit q->errorOccurred(error);
201
202 // errors cause a disconnect
204 bool stateChanged = (state != QLocalSocket::UnconnectedState);
206 q->close();
207 if (stateChanged)
208 q->emit stateChanged(state);
209}
210
211void QLocalSocket::connectToServer(OpenMode openMode)
212{
213 Q_D(QLocalSocket);
214 if (state() == ConnectedState || state() == ConnectingState) {
215 QString errorString = d->generateErrorString(QLocalSocket::OperationError, "QLocalSocket::connectToserver"_L1);
218 return;
219 }
220
221 d->errorString.clear();
222 d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
223 d->state = ConnectingState;
224 emit stateChanged(d->state);
225
226 if (d->serverName.isEmpty()) {
227 d->setErrorAndEmit(ServerNotFoundError, "QLocalSocket::connectToServer"_L1);
228 return;
229 }
230
231 // create the socket
232 if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0, O_NONBLOCK))) {
233 d->setErrorAndEmit(UnsupportedSocketOperationError, "QLocalSocket::connectToServer"_L1);
234 return;
235 }
236
237 // _q_connectToSocket does the actual connecting
238 d->connectingName = d->serverName;
239 d->connectingOpenMode = openMode;
240 d->_q_connectToSocket();
241 return;
242}
243
252{
253 Q_Q(QLocalSocket);
254
255 QLocalSocket::SocketOptions options = optionsForPlatform(socketOptions);
256 const QString connectingPathName = pathNameForConnection(connectingName, options);
257 const QByteArray encodedConnectingPathName = QFile::encodeName(connectingPathName);
258 struct ::sockaddr_un addr;
259 addr.sun_family = PF_UNIX;
260 memset(addr.sun_path, 0, sizeof(addr.sun_path));
261
262 // for abstract socket add 2 to length, to take into account trailing AND leading null
263 constexpr unsigned int extraCharacters = PlatformSupportsAbstractNamespace ? 2 : 1;
264
265 if (sizeof(addr.sun_path) < static_cast<size_t>(encodedConnectingPathName.size() + extraCharacters)) {
266 QString function = "QLocalSocket::connectToServer"_L1;
268 return;
269 }
270
271 QT_SOCKLEN_T addrSize = sizeof(::sockaddr_un);
272 if (options.testFlag(QLocalSocket::AbstractNamespaceOption)) {
273 ::memcpy(addr.sun_path + 1, encodedConnectingPathName.constData(),
274 encodedConnectingPathName.size() + 1);
275 addrSize = offsetof(::sockaddr_un, sun_path) + encodedConnectingPathName.size() + 1;
276 } else {
277 ::memcpy(addr.sun_path, encodedConnectingPathName.constData(),
278 encodedConnectingPathName.size() + 1);
279 }
280 if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&addr, addrSize)) {
281 QString function = "QLocalSocket::connectToServer"_L1;
282 switch (errno)
283 {
284 case EINVAL:
285 case ECONNREFUSED:
287 break;
288 case ENOENT:
290 break;
291 case EACCES:
292 case EPERM:
294 break;
295 case ETIMEDOUT:
297 break;
298 case EAGAIN:
299 // Try again later, all of the sockets listening are full
300 if (!delayConnect) {
302 q->connect(delayConnect, SIGNAL(activated(QSocketDescriptor)), q, SLOT(_q_connectToSocket()));
303 }
304 if (!connectTimer) {
305 connectTimer = new QTimer(q);
306 q->connect(connectTimer, SIGNAL(timeout()),
310 }
312 break;
313 default:
315 }
316 return;
317 }
318
319 // connected!
321
323 fullServerName = connectingPathName;
326 q->QIODevice::open(connectingOpenMode);
327 q->emit connected();
328 } else {
329 QString function = "QLocalSocket::connectToServer"_L1;
331 }
332 connectingSocket = -1;
334 connectingOpenMode = { };
335}
336
337bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
338 LocalSocketState socketState, OpenMode openMode)
339{
340 Q_D(QLocalSocket);
342 switch (socketState) {
343 case ConnectingState:
344 newSocketState = QAbstractSocket::ConnectingState;
345 break;
346 case ConnectedState:
347 newSocketState = QAbstractSocket::ConnectedState;
348 break;
349 case ClosingState:
350 newSocketState = QAbstractSocket::ClosingState;
351 break;
352 case UnconnectedState:
353 newSocketState = QAbstractSocket::UnconnectedState;
354 break;
355 }
357 d->state = socketState;
358 d->describeSocket(socketDescriptor);
359 return d->unixSocket.setSocketDescriptor(socketDescriptor,
360 newSocketState, openMode);
361}
362
364{
365 bool abstractAddress = false;
366
367 struct ::sockaddr_un addr;
368 QT_SOCKLEN_T len = sizeof(addr);
369 memset(&addr, 0, sizeof(addr));
370 const int getpeernameStatus = ::getpeername(socketDescriptor, (sockaddr *)&addr, &len);
371 if (getpeernameStatus != 0 || len == offsetof(sockaddr_un, sun_path)) {
372 // this is the case when we call it from QLocalServer, then there is no peername
373 len = sizeof(addr);
374 if (::getsockname(socketDescriptor, (sockaddr *)&addr, &len) != 0)
375 return;
376 }
377 if (parseSockaddr(addr, static_cast<uint>(len), fullServerName, serverName, abstractAddress)) {
378 QLocalSocket::SocketOptions options = socketOptions.value();
379 socketOptions = options.setFlag(QLocalSocket::AbstractNamespaceOption, abstractAddress);
380 }
381}
382
383bool QLocalSocketPrivate::parseSockaddr(const struct ::sockaddr_un &addr,
384 uint len,
385 QString &fullServerName,
386 QString &serverName,
387 bool &abstractNamespace)
388{
389 if (len <= offsetof(::sockaddr_un, sun_path))
390 return false;
391 len -= offsetof(::sockaddr_un, sun_path);
392 // check for abstract socket address
393 abstractNamespace = PlatformSupportsAbstractNamespace
394 && (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0);
396 // An abstract socket address can be arbitrary binary. To properly handle such a case,
397 // we'd have to add new access functions for this very specific case. Instead, we just
398 // attempt to decode it according to OS text encoding. If it fails we ignore the result.
399 QByteArrayView textData(addr.sun_path + (abstractNamespace ? 1 : 0),
400 len - (abstractNamespace ? 1 : 0));
401 QString name = toUtf16(textData);
402 if (!name.isEmpty() && !toUtf16.hasError()) {
403 //conversion encodes the trailing zeros. So, in case of non-abstract namespace we
404 //chop them off as \0 character is not allowed in filenames
405 if (!abstractNamespace && (name.at(name.size() - 1) == QChar::fromLatin1('\0'))) {
406 int truncPos = name.size() - 1;
407 while (truncPos > 0 && name.at(truncPos - 1) == QChar::fromLatin1('\0'))
408 truncPos--;
409 name.truncate(truncPos);
410 }
412 serverName = abstractNamespace
413 ? name
415 if (serverName.isEmpty())
417 }
418 return true;
419}
420
422{
423 Q_Q(QLocalSocket);
424 q->close();
425}
426
428{
429 if (delayConnect) {
430 delayConnect->setEnabled(false);
431 delete delayConnect;
432 delayConnect = nullptr;
434 delete connectTimer;
435 connectTimer = nullptr;
436 }
437}
438
440{
441 Q_D(const QLocalSocket);
442 return d->unixSocket.socketDescriptor();
443}
444
446{
447 Q_D(QLocalSocket);
448 return d->unixSocket.read(data, c);
449}
450
452{
453 if (!maxSize)
454 return 0;
455
456 // QIODevice::readLine() reserves space for the trailing '\0' byte,
457 // so we must read 'maxSize + 1' bytes.
458 return d_func()->unixSocket.readLine(data, maxSize + 1);
459}
460
462{
463 return d_func()->unixSocket.skip(maxSize);
464}
465
467{
468 Q_D(QLocalSocket);
469 return d->unixSocket.writeData(data, c);
470}
471
473{
474 Q_D(QLocalSocket);
475 d->unixSocket.abort();
476 close();
477}
478
480{
481 Q_D(const QLocalSocket);
482 return QIODevice::bytesAvailable() + d->unixSocket.bytesAvailable();
483}
484
486{
487 Q_D(const QLocalSocket);
488 return d->unixSocket.bytesToWrite();
489}
490
491bool QLocalSocket::canReadLine() const
492{
493 Q_D(const QLocalSocket);
494 return QIODevice::canReadLine() || d->unixSocket.canReadLine();
495}
496
498{
499 Q_D(QLocalSocket);
500
502 d->unixSocket.close();
503 d->cancelDelayedConnect();
504 if (d->connectingSocket != -1)
505 ::close(d->connectingSocket);
506 d->connectingSocket = -1;
507 d->connectingName.clear();
508 d->connectingOpenMode = { };
509 d->serverName.clear();
510 d->fullServerName.clear();
511}
512
514{
515 Q_D(QLocalSocket);
516 return d->unixSocket.waitForBytesWritten(msecs);
517}
518
520{
521 Q_D(QLocalSocket);
522 return d->unixSocket.flush();
523}
524
526{
527 Q_D(QLocalSocket);
528 d->unixSocket.disconnectFromHost();
529}
530
532{
533 Q_D(const QLocalSocket);
534 switch (d->unixSocket.error()) {
555 default:
556#if defined QLOCALSOCKET_DEBUG
557 qWarning() << "QLocalSocket error not handled:" << d->unixSocket.error();
558#endif
559 break;
560 }
561 return UnknownSocketError;
562}
563
564bool QLocalSocket::isValid() const
565{
566 Q_D(const QLocalSocket);
567 return d->unixSocket.isValid();
568}
569
571{
572 Q_D(const QLocalSocket);
573 return d->unixSocket.readBufferSize();
574}
575
577{
578 Q_D(QLocalSocket);
579 d->unixSocket.setReadBufferSize(size);
580}
581
583{
584 Q_D(QLocalSocket);
585
586 if (state() != ConnectingState)
587 return (state() == ConnectedState);
588
589 pollfd pfd = qt_make_pollfd(d->connectingSocket, POLLIN);
590
592 auto remainingTime = deadline.remainingTimeAsDuration();
593
594 do {
595 const int result = qt_safe_poll(&pfd, 1, deadline);
596 if (result == -1)
597 d->setErrorAndEmit(QLocalSocket::UnknownSocketError,
598 "QLocalSocket::waitForConnected"_L1);
599 else if (result > 0)
600 d->_q_connectToSocket();
601 } while (state() == ConnectingState
602 && (remainingTime = deadline.remainingTimeAsDuration()) > 0ns);
603
604 return (state() == ConnectedState);
605}
606
608{
609 Q_D(QLocalSocket);
610 if (state() == UnconnectedState) {
611 qWarning("QLocalSocket::waitForDisconnected() is not allowed in UnconnectedState");
612 return false;
613 }
614 return (d->unixSocket.waitForDisconnected(msecs));
615}
616
617bool QLocalSocket::waitForReadyRead(int msecs)
618{
619 Q_D(QLocalSocket);
621 return false;
622 return (d->unixSocket.waitForReadyRead(msecs));
623}
624
bool connected
SocketState
This enum describes the different states in which a socket can be.
SocketError
This enum describes the socket errors that can occur.
virtual bool setSocketDescriptor(qintptr socketDescriptor, SocketState state=ConnectedState, OpenMode openMode=ReadWrite)
Initializes QAbstractSocket with the native socket descriptor socketDescriptor.
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
std::chrono::nanoseconds remainingTimeAsDuration() const noexcept
Returns the time remaining before the deadline.
static QString tempPath()
Returns the absolute canonical path of the system's temporary directory.
Definition qdir.cpp:2133
static QByteArray encodeName(const QString &fileName)
Converts fileName to an 8-bit encoding that you can use in native APIs.
Definition qfile.h:158
virtual bool open(QIODeviceBase::OpenMode mode)
Opens the device and sets its OpenMode to mode.
void setErrorString(const QString &errorString)
Sets the human readable description of the last device error that occurred to str.
QIODeviceBase::OpenMode openMode() const
Returns the mode in which the device has been opened; i.e.
virtual qint64 bytesAvailable() const
Returns the number of bytes that are available for reading.
QString errorString() const
Returns a human-readable description of the last device error that occurred.
virtual void close()
First emits aboutToClose(), then closes the device and sets its OpenMode to NotOpen.
virtual bool canReadLine() const
Returns true if a complete line of data can be read from the device; otherwise returns false.
static bool parseSockaddr(const sockaddr_un &addr, uint len, QString &fullServerName, QString &serverName, bool &abstractNamespace)
void _q_stateChanged(QAbstractSocket::SocketState newState)
void describeSocket(qintptr socketDescriptor)
QLocalUnixSocket unixSocket
QLocalSocket::LocalSocketState state
void _q_errorOccurred(QAbstractSocket::SocketError newError)
void setErrorAndEmit(QLocalSocket::LocalSocketError, const QString &function)
QIODevice::OpenMode connectingOpenMode
QSocketNotifier * delayConnect
QString generateErrorString(QLocalSocket::LocalSocketError, const QString &function) const
The QLocalSocket class provides a local socket.
bool setSocketDescriptor(qintptr socketDescriptor, LocalSocketState socketState=ConnectedState, OpenMode openMode=ReadWrite)
Initializes QLocalSocket with the native socket descriptor socketDescriptor.
LocalSocketState state() const
Returns the state of the socket.
virtual qint64 readData(char *, qint64) override
\reimp
void abort()
Aborts the current connection and resets the socket.
@ AbstractNamespaceOption
virtual qint64 bytesToWrite() const override
\reimp
virtual bool canReadLine() const override
\reimp
bool waitForBytesWritten(int msecs=30000) override
\reimp
LocalSocketState
This enum describes the different states in which a socket can be.
bool isValid() const
Returns true if the socket is valid and ready for use; otherwise returns false.
bool flush()
This function writes as much as possible from the internal write buffer to the socket,...
LocalSocketError error() const
Returns the type of error that last occurred.
qint64 readBufferSize() const
Returns the size of the internal read buffer.
bool waitForDisconnected(int msecs=30000)
Waits until the socket has disconnected, up to msecs milliseconds.
void disconnectFromServer()
Attempts to close the socket.
qint64 readLineData(char *data, qint64 maxSize) override
\reimp
bool waitForConnected(int msecs=30000)
Waits until the socket is connected, up to msecs milliseconds.
bool waitForReadyRead(int msecs=30000) override
This function blocks until data is available for reading and the \l{QIODevice::}{readyRead()} signal ...
void connectToServer(OpenMode openMode=ReadWrite)
void setReadBufferSize(qint64 size)
Sets the size of QLocalSocket's internal read buffer to be size bytes.
qintptr socketDescriptor() const
Returns the native socket descriptor of the QLocalSocket object if this is available; otherwise retur...
virtual void close() override
Closes the I/O device for the socket and calls disconnectFromServer() to close the socket's connectio...
qint64 skipData(qint64 maxSize) override
\reimp
virtual qint64 bytesAvailable() const override
\reimp
void stateChanged(QLocalSocket::LocalSocketState socketState)
This signal is emitted whenever QLocalSocket's state changes.
virtual qint64 writeData(const char *, qint64) override
\reimp
LocalSocketError
The LocalServerError enumeration represents the errors that can occur.
@ UnsupportedSocketOperationError
void errorOccurred(QLocalSocket::LocalSocketError socketError)
void setSocketState(QAbstractSocket::SocketState state)
void setSocketError(QAbstractSocket::SocketError error)
void setParent(QObject *parent)
Makes the object a child of parent.
Definition qobject.cpp:2195
\inmodule QtCore
\inmodule QtCore
void setEnabled(bool)
If enable is true, the notifier is enabled; otherwise the notifier is disabled.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
\inmodule QtCore
Definition qtimer.h:20
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
void stop()
Stops the timer.
Definition qtimer.cpp:267
else opt state
[0]
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
QAbstractSocket::SocketState socketState(QIODevice *device)
Combined button and popup list for selecting options.
@ DirectConnection
int qt_safe_poll(struct pollfd *fds, nfds_t nfds, QDeadlineTimer deadline)
static struct pollfd qt_make_pollfd(int fd, short events)
DBusConnection const char DBusError * error
#define QT_CONNECT_TIMEOUT
#define qWarning
Definition qlogging.h:166
static int qt_safe_socket(int domain, int type, int protocol, int flags=0)
Definition qnet_unix_p.h:45
static int qt_safe_connect(int sockfd, const struct sockaddr *addr, QT_SOCKLEN_T addrlen)
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLenum GLsizei GLuint GLint * bytesWritten
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLbitfield GLuint64 timeout
[4]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
const GLubyte * c
GLenum const void * addr
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLenum GLsizei len
#define emit
@ PlatformSupportsAbstractNamespace
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
ptrdiff_t qintptr
Definition qtypes.h:166
QDeadlineTimer deadline(30s)
QObject::connect nullptr