Qt 6.x
The Qt SDK
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 <qdebug.h>
17#include <qelapsedtimer.h>
18#include <qstringconverter.h>
19
20#ifdef Q_OS_VXWORKS
21# include <selectLib.h>
22#endif
23
24#define QT_CONNECT_TIMEOUT 30000
25
26
28
29using namespace Qt::StringLiterals;
30
31namespace {
32// determine the full server path
33static QString pathNameForConnection(const QString &connectingName,
34 QLocalSocket::SocketOptions options)
35{
36 if (options.testFlag(QLocalSocket::AbstractNamespaceOption)
37 || connectingName.startsWith(u'/')) {
38 return connectingName;
39 }
40
41 return QDir::tempPath() + u'/' + connectingName;
42}
43
44static QLocalSocket::SocketOptions optionsForPlatform(QLocalSocket::SocketOptions srcOptions)
45{
46 // For OS that does not support abstract namespace the AbstractNamespaceOption
47 // option is cleared.
50 return srcOptions;
51}
52}
53
55 delayConnect(nullptr),
56 connectTimer(nullptr),
57 connectingSocket(-1),
58 state(QLocalSocket::UnconnectedState),
59 socketOptions(QLocalSocket::NoOptions)
60{
61}
62
64{
65 Q_Q(QLocalSocket);
66 // QIODevice signals
69 q->connect(&unixSocket, SIGNAL(readyRead()), q, SIGNAL(readyRead()));
70 // QAbstractSocket signals
71 q->connect(&unixSocket, SIGNAL(connected()), q, SIGNAL(connected()));
72 q->connect(&unixSocket, SIGNAL(disconnected()), q, SIGNAL(disconnected()));
73 q->connect(&unixSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
75 q->connect(&unixSocket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
77 q->connect(&unixSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
79}
80
82{
83 Q_Q(QLocalSocket);
84 QString function = "QLocalSocket"_L1;
87 q->setErrorString(errorString);
88 emit q->errorOccurred(error);
89}
90
92{
93 Q_Q(QLocalSocket);
95 switch(newState) {
100 break;
103 break;
106 break;
109 break;
110 default:
111#if defined QLOCALSOCKET_DEBUG
112 qWarning() << "QLocalSocket::Unhandled socket state change:" << newState;
113#endif
114 return;
115 }
116 if (currentState != state)
117 emit q->stateChanged(state);
118}
119
121{
123 switch (error) {
125 errorString = QLocalSocket::tr("%1: Connection refused").arg(function);
126 break;
128 errorString = QLocalSocket::tr("%1: Remote closed").arg(function);
129 break;
131 errorString = QLocalSocket::tr("%1: Invalid name").arg(function);
132 break;
134 errorString = QLocalSocket::tr("%1: Socket access error").arg(function);
135 break;
137 errorString = QLocalSocket::tr("%1: Socket resource error").arg(function);
138 break;
140 errorString = QLocalSocket::tr("%1: Socket operation timed out").arg(function);
141 break;
143 errorString = QLocalSocket::tr("%1: Datagram too large").arg(function);
144 break;
146 errorString = QLocalSocket::tr("%1: Connection error").arg(function);
147 break;
149 errorString = QLocalSocket::tr("%1: The socket operation is not supported").arg(function);
150 break;
152 errorString = QLocalSocket::tr("%1: Operation not permitted when socket is in this state").arg(function);
153 break;
155 default:
156 errorString = QLocalSocket::tr("%1: Unknown error %2").arg(function).arg(errno);
157 }
158 return errorString;
159}
160
162{
163 Q_Q(QLocalSocket);
164 switch (error) {
167 break;
170 break;
173 break;
176 break;
179 break;
182 break;
185 break;
188 break;
191 break;
193 default:
195 }
196
198 q->setErrorString(errorString);
199 emit q->errorOccurred(error);
200
201 // errors cause a disconnect
203 bool stateChanged = (state != QLocalSocket::UnconnectedState);
205 q->close();
206 if (stateChanged)
207 q->emit stateChanged(state);
208}
209
210void QLocalSocket::connectToServer(OpenMode openMode)
211{
212 Q_D(QLocalSocket);
213 if (state() == ConnectedState || state() == ConnectingState) {
214 QString errorString = d->generateErrorString(QLocalSocket::OperationError, "QLocalSocket::connectToserver"_L1);
217 return;
218 }
219
220 d->errorString.clear();
221 d->unixSocket.setSocketState(QAbstractSocket::ConnectingState);
222 d->state = ConnectingState;
223 emit stateChanged(d->state);
224
225 if (d->serverName.isEmpty()) {
226 d->setErrorAndEmit(ServerNotFoundError, "QLocalSocket::connectToServer"_L1);
227 return;
228 }
229
230 // create the socket
231 if (-1 == (d->connectingSocket = qt_safe_socket(PF_UNIX, SOCK_STREAM, 0, O_NONBLOCK))) {
232 d->setErrorAndEmit(UnsupportedSocketOperationError, "QLocalSocket::connectToServer"_L1);
233 return;
234 }
235
236 // _q_connectToSocket does the actual connecting
237 d->connectingName = d->serverName;
238 d->connectingOpenMode = openMode;
239 d->_q_connectToSocket();
240 return;
241}
242
251{
252 Q_Q(QLocalSocket);
253
254 QLocalSocket::SocketOptions options = optionsForPlatform(socketOptions);
255 const QString connectingPathName = pathNameForConnection(connectingName, options);
256 const QByteArray encodedConnectingPathName = QFile::encodeName(connectingPathName);
257 struct ::sockaddr_un addr;
258 addr.sun_family = PF_UNIX;
259 memset(addr.sun_path, 0, sizeof(addr.sun_path));
260
261 // for abstract socket add 2 to length, to take into account trailing AND leading null
262 constexpr unsigned int extraCharacters = PlatformSupportsAbstractNamespace ? 2 : 1;
263
264 if (sizeof(addr.sun_path) < static_cast<size_t>(encodedConnectingPathName.size() + extraCharacters)) {
265 QString function = "QLocalSocket::connectToServer"_L1;
267 return;
268 }
269
270 QT_SOCKLEN_T addrSize = sizeof(::sockaddr_un);
271 if (options.testFlag(QLocalSocket::AbstractNamespaceOption)) {
272 ::memcpy(addr.sun_path + 1, encodedConnectingPathName.constData(),
273 encodedConnectingPathName.size() + 1);
274 addrSize = offsetof(::sockaddr_un, sun_path) + encodedConnectingPathName.size() + 1;
275 } else {
276 ::memcpy(addr.sun_path, encodedConnectingPathName.constData(),
277 encodedConnectingPathName.size() + 1);
278 }
279 if (-1 == qt_safe_connect(connectingSocket, (struct sockaddr *)&addr, addrSize)) {
280 QString function = "QLocalSocket::connectToServer"_L1;
281 switch (errno)
282 {
283 case EINVAL:
284 case ECONNREFUSED:
286 break;
287 case ENOENT:
289 break;
290 case EACCES:
291 case EPERM:
293 break;
294 case ETIMEDOUT:
296 break;
297 case EAGAIN:
298 // Try again later, all of the sockets listening are full
299 if (!delayConnect) {
301 q->connect(delayConnect, SIGNAL(activated(QSocketDescriptor)), q, SLOT(_q_connectToSocket()));
302 }
303 if (!connectTimer) {
304 connectTimer = new QTimer(q);
305 q->connect(connectTimer, SIGNAL(timeout()),
309 }
311 break;
312 default:
314 }
315 return;
316 }
317
318 // connected!
320
322 fullServerName = connectingPathName;
325 q->QIODevice::open(connectingOpenMode);
326 q->emit connected();
327 } else {
328 QString function = "QLocalSocket::connectToServer"_L1;
330 }
331 connectingSocket = -1;
333 connectingOpenMode = { };
334}
335
336bool QLocalSocket::setSocketDescriptor(qintptr socketDescriptor,
337 LocalSocketState socketState, OpenMode openMode)
338{
339 Q_D(QLocalSocket);
341 switch (socketState) {
342 case ConnectingState:
343 newSocketState = QAbstractSocket::ConnectingState;
344 break;
345 case ConnectedState:
346 newSocketState = QAbstractSocket::ConnectedState;
347 break;
348 case ClosingState:
349 newSocketState = QAbstractSocket::ClosingState;
350 break;
351 case UnconnectedState:
352 newSocketState = QAbstractSocket::UnconnectedState;
353 break;
354 }
356 d->state = socketState;
357 d->describeSocket(socketDescriptor);
358 return d->unixSocket.setSocketDescriptor(socketDescriptor,
359 newSocketState, openMode);
360}
361
363{
364 bool abstractAddress = false;
365
366 struct ::sockaddr_un addr;
367 QT_SOCKLEN_T len = sizeof(addr);
368 memset(&addr, 0, sizeof(addr));
369 const int getpeernameStatus = ::getpeername(socketDescriptor, (sockaddr *)&addr, &len);
370 if (getpeernameStatus != 0 || len == offsetof(sockaddr_un, sun_path)) {
371 // this is the case when we call it from QLocalServer, then there is no peername
372 len = sizeof(addr);
373 if (::getsockname(socketDescriptor, (sockaddr *)&addr, &len) != 0)
374 return;
375 }
376 if (parseSockaddr(addr, static_cast<uint>(len), fullServerName, serverName, abstractAddress)) {
377 QLocalSocket::SocketOptions options = socketOptions.value();
378 socketOptions = options.setFlag(QLocalSocket::AbstractNamespaceOption, abstractAddress);
379 }
380}
381
382bool QLocalSocketPrivate::parseSockaddr(const struct ::sockaddr_un &addr,
383 uint len,
384 QString &fullServerName,
385 QString &serverName,
386 bool &abstractNamespace)
387{
388 if (len <= offsetof(::sockaddr_un, sun_path))
389 return false;
390 len -= offsetof(::sockaddr_un, sun_path);
391 // check for abstract socket address
392 abstractNamespace = PlatformSupportsAbstractNamespace
393 && (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0);
395 // An abstract socket address can be arbitrary binary. To properly handle such a case,
396 // we'd have to add new access functions for this very specific case. Instead, we just
397 // attempt to decode it according to OS text encoding. If it fails we ignore the result.
398 QByteArrayView textData(addr.sun_path + (abstractNamespace ? 1 : 0),
399 len - (abstractNamespace ? 1 : 0));
400 QString name = toUtf16(textData);
401 if (!name.isEmpty() && !toUtf16.hasError()) {
402 //conversion encodes the trailing zeros. So, in case of non-abstract namespace we
403 //chop them off as \0 character is not allowed in filenames
404 if (!abstractNamespace && (name.at(name.size() - 1) == QChar::fromLatin1('\0'))) {
405 int truncPos = name.size() - 1;
406 while (truncPos > 0 && name.at(truncPos - 1) == QChar::fromLatin1('\0'))
407 truncPos--;
408 name.truncate(truncPos);
409 }
411 serverName = abstractNamespace
412 ? name
414 if (serverName.isEmpty())
416 }
417 return true;
418}
419
421{
422 Q_Q(QLocalSocket);
423 q->close();
424}
425
427{
428 if (delayConnect) {
429 delayConnect->setEnabled(false);
430 delete delayConnect;
431 delayConnect = nullptr;
433 delete connectTimer;
434 connectTimer = nullptr;
435 }
436}
437
439{
440 Q_D(const QLocalSocket);
441 return d->unixSocket.socketDescriptor();
442}
443
445{
446 Q_D(QLocalSocket);
447 return d->unixSocket.read(data, c);
448}
449
451{
452 if (!maxSize)
453 return 0;
454
455 // QIODevice::readLine() reserves space for the trailing '\0' byte,
456 // so we must read 'maxSize + 1' bytes.
457 return d_func()->unixSocket.readLine(data, maxSize + 1);
458}
459
461{
462 return d_func()->unixSocket.skip(maxSize);
463}
464
466{
467 Q_D(QLocalSocket);
468 return d->unixSocket.writeData(data, c);
469}
470
472{
473 Q_D(QLocalSocket);
474 d->unixSocket.abort();
475 close();
476}
477
479{
480 Q_D(const QLocalSocket);
481 return QIODevice::bytesAvailable() + d->unixSocket.bytesAvailable();
482}
483
485{
486 Q_D(const QLocalSocket);
487 return d->unixSocket.bytesToWrite();
488}
489
490bool QLocalSocket::canReadLine() const
491{
492 Q_D(const QLocalSocket);
493 return QIODevice::canReadLine() || d->unixSocket.canReadLine();
494}
495
497{
498 Q_D(QLocalSocket);
499
501 d->unixSocket.close();
502 d->cancelDelayedConnect();
503 if (d->connectingSocket != -1)
504 ::close(d->connectingSocket);
505 d->connectingSocket = -1;
506 d->connectingName.clear();
507 d->connectingOpenMode = { };
508 d->serverName.clear();
509 d->fullServerName.clear();
510}
511
513{
514 Q_D(QLocalSocket);
515 return d->unixSocket.waitForBytesWritten(msecs);
516}
517
519{
520 Q_D(QLocalSocket);
521 return d->unixSocket.flush();
522}
523
525{
526 Q_D(QLocalSocket);
527 d->unixSocket.disconnectFromHost();
528}
529
531{
532 Q_D(const QLocalSocket);
533 switch (d->unixSocket.error()) {
554 default:
555#if defined QLOCALSOCKET_DEBUG
556 qWarning() << "QLocalSocket error not handled:" << d->unixSocket.error();
557#endif
558 break;
559 }
560 return UnknownSocketError;
561}
562
563bool QLocalSocket::isValid() const
564{
565 Q_D(const QLocalSocket);
566 return d->unixSocket.isValid();
567}
568
570{
571 Q_D(const QLocalSocket);
572 return d->unixSocket.readBufferSize();
573}
574
576{
577 Q_D(QLocalSocket);
578 d->unixSocket.setReadBufferSize(size);
579}
580
582{
583 Q_D(QLocalSocket);
584
585 if (state() != ConnectingState)
586 return (state() == ConnectedState);
587
589 timer.start();
590
591 pollfd pfd = qt_make_pollfd(d->connectingSocket, POLLIN);
592
593 do {
594 const int timeout = (msec > 0) ? qMax(msec - timer.elapsed(), Q_INT64_C(0)) : msec;
595 const int result = qt_poll_msecs(&pfd, 1, timeout);
596
597 if (result == -1)
598 d->setErrorAndEmit(QLocalSocket::UnknownSocketError,
599 "QLocalSocket::waitForConnected"_L1);
600 else if (result > 0)
601 d->_q_connectToSocket();
602 } while (state() == ConnectingState && !timer.hasExpired(msec));
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
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
static constexpr QChar fromLatin1(char c) noexcept
Converts the Latin-1 character c to its equivalent QChar.
Definition qchar.h:461
static QString tempPath()
Returns the absolute canonical path of the system's temporary directory.
Definition qdir.cpp:2130
\inmodule QtCore
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:2142
\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:127
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
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:5299
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
\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:208
void stop()
Stops the timer.
Definition qtimer.cpp:226
else opt state
[0]
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
Combined button and popup list for selecting options.
@ DirectConnection
static struct pollfd qt_make_pollfd(int fd, short events)
static int qt_poll_msecs(struct pollfd *fds, nfds_t nfds, int timeout)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
#define QT_CONNECT_TIMEOUT
#define qWarning
Definition qlogging.h:162
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
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:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
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 GLsizei len
GLenum const void * addr
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
#define emit
@ PlatformSupportsAbstractNamespace
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
#define Q_INT64_C(c)
Definition qtypes.h:52
ptrdiff_t qintptr
Definition qtypes.h:71
QObject::connect nullptr
QTimer * timer
[3]