diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp index 97f5920171..05f26883d3 100644 --- a/src/network/socket/qlocalserver.cpp +++ b/src/network/socket/qlocalserver.cpp @@ -298,6 +298,40 @@ bool QLocalServer::listen(const QString &name) return true; } +/*! + \since 5.0 + + Instructs the server to listen for incoming connections on + \a socketDescriptor. The property returns \c false if the server is + currently listening. It returns \c true on success; otherwise, + it returns \c false. The socket must be ready to accept + new connections with no extra platform-specific functions + called. The socket is set into non-blocking mode. + + serverName(), fullServerName() may return a string with + a name if this option is supported by the platform; + otherwise, they return an empty QString. + + \sa isListening(), close() + */ +bool QLocalServer::listen(qintptr socketDescriptor) +{ + Q_D(QLocalServer); + if (isListening()) { + qWarning("QLocalServer::listen() called when already listening"); + return false; + } + + d->serverName.clear(); + d->fullServerName.clear(); + + if (!d->listen(socketDescriptor)) { + return false; + } + + return true; +} + /*! Returns the maximum number of pending accepted connections. The default is 30. diff --git a/src/network/socket/qlocalserver.h b/src/network/socket/qlocalserver.h index 6f883adc24..291122e10d 100644 --- a/src/network/socket/qlocalserver.h +++ b/src/network/socket/qlocalserver.h @@ -82,6 +82,7 @@ public: virtual bool hasPendingConnections() const; bool isListening() const; bool listen(const QString &name); + bool listen(qintptr socketDescriptor); int maxPendingConnections() const; virtual QLocalSocket *nextPendingConnection(); QString serverName() const; diff --git a/src/network/socket/qlocalserver_p.h b/src/network/socket/qlocalserver_p.h index 03c06a42e3..84081159c7 100644 --- a/src/network/socket/qlocalserver_p.h +++ b/src/network/socket/qlocalserver_p.h @@ -87,6 +87,7 @@ public: void init(); bool listen(const QString &name); + bool listen(qintptr socketDescriptor); static bool removeServer(const QString &name); void closeServer(); void waitForNewConnection(int msec, bool *timedOut); diff --git a/src/network/socket/qlocalserver_tcp.cpp b/src/network/socket/qlocalserver_tcp.cpp index d6c6a1af92..9ac1bfaf0e 100644 --- a/src/network/socket/qlocalserver_tcp.cpp +++ b/src/network/socket/qlocalserver_tcp.cpp @@ -78,6 +78,13 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName) return true; } +bool QLocalServerPrivate::listen(qintptr socketDescriptor) +{ + Q_Q(QLocalServer); + + return tcpServer.setSocketDescriptor(socketDescriptor); +} + void QLocalServerPrivate::closeServer() { QSettings settings(QLatin1String("Trolltech"), QLatin1String("Qt")); diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp index ce0c283f0b..0c2edef578 100644 --- a/src/network/socket/qlocalserver_unix.cpp +++ b/src/network/socket/qlocalserver_unix.cpp @@ -203,6 +203,48 @@ bool QLocalServerPrivate::listen(const QString &requestedServerName) return true; } +bool QLocalServerPrivate::listen(qintptr socketDescriptor) +{ + Q_Q(QLocalServer); + + // Attach to the localsocket + listenSocket = socketDescriptor; + + ::fcntl(listenSocket, F_SETFD, FD_CLOEXEC); + ::fcntl(listenSocket, F_SETFL, ::fcntl(listenSocket, F_GETFL) | O_NONBLOCK); + +#ifdef Q_OS_LINUX + struct ::sockaddr_un addr; + socklen_t len; + memset(&addr, 0, sizeof(addr)); + if (0 == ::getsockname(listenSocket, (sockaddr *)&addr, &len)) { + // check for absract sockets + if (addr.sun_family == PF_UNIX && addr.sun_path[0] == 0) { + addr.sun_path[0] = '@'; + } + QString name = QString::fromLatin1(addr.sun_path); + if (!name.isEmpty()) { + fullServerName = name; + serverName = fullServerName.mid(fullServerName.lastIndexOf(QLatin1String("/"))+1); + if (serverName.isEmpty()) { + serverName = fullServerName; + } + } + } +#else + serverName.clear(); + fullServerName.clear(); +#endif + + Q_ASSERT(!socketNotifier); + socketNotifier = new QSocketNotifier(listenSocket, + QSocketNotifier::Read, q); + q->connect(socketNotifier, SIGNAL(activated(int)), + q, SLOT(_q_onNewConnection())); + socketNotifier->setEnabled(maxPendingConnections > 0); + return true; +} + /*! \internal diff --git a/src/network/socket/qlocalserver_win.cpp b/src/network/socket/qlocalserver_win.cpp index 67e319cdbb..07357e5789 100644 --- a/src/network/socket/qlocalserver_win.cpp +++ b/src/network/socket/qlocalserver_win.cpp @@ -144,6 +144,12 @@ bool QLocalServerPrivate::listen(const QString &name) return true; } +bool QLocalServerPrivate::listen(qintptr) +{ + qWarning("QLocalServer::listen(qintptr) is not supported on Windows QTBUG-24230"); + return false; +} + void QLocalServerPrivate::_q_onNewConnection() { Q_Q(QLocalServer); diff --git a/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp index 03b95bfa2f..72fa9f74a4 100644 --- a/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp +++ b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp @@ -46,6 +46,12 @@ #include #include +#ifdef Q_OS_UNIX +#include +#include +#include +#endif + Q_DECLARE_METATYPE(QLocalSocket::LocalSocketError) Q_DECLARE_METATYPE(QLocalSocket::LocalSocketState) Q_DECLARE_METATYPE(QLocalServer::SocketOption) @@ -109,6 +115,10 @@ private slots: void verifySocketOptions(); void verifySocketOptions_data(); + + void verifyListenWithDescriptor(); + void verifyListenWithDescriptor_data(); + }; void tst_QLocalSocket::init() @@ -1026,19 +1036,19 @@ void tst_QLocalSocket::verifySocketOptions_data() QFile::Permissions p = QFile::ExeOwner|QFile::WriteOwner|QFile::ReadOwner | QFile::ExeUser|QFile::WriteUser|QFile::ReadUser; - QTest::newRow("user") << "userPerms" << QLocalServer::UserAccess << p; + QTest::newRow("user") << "userPerms" << QLocalServer::UserAccessOption << p; p = QFile::ExeGroup|QFile::WriteGroup|QFile::ReadGroup; - QTest::newRow("group") << "groupPerms" << QLocalServer::GroupAccess << p; + QTest::newRow("group") << "groupPerms" << QLocalServer::GroupAccessOption << p; p = QFile::ExeOther|QFile::WriteOther|QFile::ReadOther; - QTest::newRow("other") << "otherPerms" << QLocalServer::OtherAccess << p; + QTest::newRow("other") << "otherPerms" << QLocalServer::OtherAccessOption << p; p = QFile::ExeOwner|QFile::WriteOwner|QFile::ReadOwner| QFile::ExeUser|QFile::WriteUser|QFile::ReadUser | QFile::ExeGroup|QFile::WriteGroup|QFile::ReadGroup| QFile::ExeOther|QFile::WriteOther|QFile::ReadOther; - QTest::newRow("all") << "worldPerms" << QLocalServer::WorldAccess << p; + QTest::newRow("all") << "worldPerms" << QLocalServer::WorldAccessOption << p; #endif } @@ -1065,6 +1075,92 @@ void tst_QLocalSocket::verifySocketOptions() #endif } +void tst_QLocalSocket::verifyListenWithDescriptor() +{ +#ifdef Q_OS_UNIX + QFETCH(QString, path); + QFETCH(bool, abstract); + QFETCH(bool, bound); + + qDebug() << "socket" << path << abstract; + + int listenSocket; + + if (bound) { + // create the unix socket + listenSocket = ::socket(PF_UNIX, SOCK_STREAM, 0); + QVERIFY2(listenSocket != -1, "failed to create test socket"); + + // Construct the unix address + struct ::sockaddr_un addr; + addr.sun_family = PF_UNIX; + + QVERIFY2(sizeof(addr.sun_path) > ((uint)path.size() + 1), "path to large to create socket"); + + ::memset(addr.sun_path, 0, sizeof(addr.sun_path)); + if (abstract) + ::memcpy(addr.sun_path+1, path.toLatin1().data(), path.toLatin1().size()); + else + ::memcpy(addr.sun_path, path.toLatin1().data(), path.toLatin1().size()); + + if (path.startsWith(QLatin1Char('/'))) { + ::unlink(path.toLatin1()); + } + + QVERIFY2(-1 != ::bind(listenSocket, (sockaddr *)&addr, sizeof(sockaddr_un)), "failed to bind test socket to address"); + + // listen for connections + QVERIFY2(-1 != ::listen(listenSocket, 50), "failed to call listen on test socket"); + } else { + int fds[2]; + QVERIFY2(-1 != ::socketpair(PF_UNIX, SOCK_STREAM, 0, fds), "failed to create socket pair"); + + listenSocket = fds[0]; + close(fds[1]); + } + + QLocalServer server; + QVERIFY2(server.listen(listenSocket), "failed to start create QLocalServer with local socket"); + +#ifdef Q_OS_LINUX + if (!bound) { + QVERIFY(server.serverName().at(0) == QLatin1Char('@')); + QVERIFY(server.fullServerName().at(0) == QLatin1Char('@')); + } else if (abstract) { + QVERIFY2(server.fullServerName().at(0) == QLatin1Char('@'), "abstract sockets should start with a '@'"); + } else { + QVERIFY2(server.fullServerName() == path, "full server path doesn't match patch provided"); + if (path.contains(QLatin1String("/"))) { + QVERIFY2(server.serverName() == path.mid(path.lastIndexOf(QLatin1Char('/'))+1), "server name invalid short name"); + } else { + QVERIFY2(server.serverName() == path, "servier name doesn't match the path provided"); + } + } +#else + QVERIFY(server.serverName().isEmpty()); + QVERIFY(server.fullServerName().isEmpty()); +#endif + + +#endif +} + +void tst_QLocalSocket::verifyListenWithDescriptor_data() +{ +#ifdef Q_OS_UNIX + QTest::addColumn("path"); + QTest::addColumn("abstract"); + QTest::addColumn("bound"); + + QTest::newRow("normal") << QDir::tempPath() + QLatin1Literal("/testsocket") << false << true; + QTest::newRow("absrtact") << QString::fromLatin1("abstractsocketname") << true << true; + QTest::newRow("abstractwithslash") << QString::fromLatin1("abstractsocketwitha/inthename") << true << true; + QTest::newRow("no path") << QString::fromLatin1("/invalid/no path name speficied") << true << false; + +#endif + +} + QTEST_MAIN(tst_QLocalSocket) #include "tst_qlocalsocket.moc"