/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtSystems module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qremoteserviceregister_p.h" #include "qremoteserviceregister_ls_p.h" #include "ipcendpoint_p.h" #include "objectendpoint_p.h" #include "qserviceclientcredentials_p.h" #include "qserviceclientcredentials.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifndef Q_OS_WIN #include #include #include #else // Needed for ::Sleep, while we wait for a better solution #include #include #endif #ifdef LOCAL_PEERCRED /* from sys/un.h */ #include #endif QT_BEGIN_NAMESPACE //IPC based on QLocalSocket class LocalSocketEndPoint : public QServiceIpcEndPoint { Q_OBJECT public: LocalSocketEndPoint(QLocalSocket* s, QObject* parent = 0) : QServiceIpcEndPoint(parent), socket(s), pending_bytes(0) { Q_ASSERT(socket); socket->setParent(this); connect(s, SIGNAL(readyRead()), this, SLOT(readIncoming())); connect(s, SIGNAL(disconnected()), this, SIGNAL(disconnected())); connect(s, SIGNAL(disconnected()), this, SLOT(ipcfault())); connect(s, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(socketError(QLocalSocket::LocalSocketError))); if (socket->bytesAvailable()) QTimer::singleShot(0, this, SLOT(readIncoming())); } ~LocalSocketEndPoint() { disconnect(this, SLOT(ipcfault())); socket->close(); } void getSecurityCredentials(QServiceClientCredentials &creds) { //LocalSocketEndPoint owns socket int fd = socket->socketDescriptor(); #if defined(LOCAL_PEERCRED) struct xucred xuc; socklen_t len = sizeof(struct xucred); if (getsockopt(fd, SOL_SOCKET, LOCAL_PEERCRED, &xuc, &len) == 0) { creds.d->pid = -1; // No PID on bsd creds.d->uid = xuc.cr_uid; creds.d->gid = xuc.cr_gid; } else { qDebug("SFW getsockopt failed: %s", qPrintable(qt_error_string(errno))); } #elif defined(SO_PEERCRED) struct ucred uc; socklen_t len = sizeof(struct ucred); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) == 0) { creds.d->pid = uc.pid; creds.d->uid = uc.uid; creds.d->gid = uc.gid; } else { qDebug("SFW getsockopt failed: %s", qPrintable(qt_error_string(errno))); } #else Q_UNUSED(creds); Q_UNUSED(fd); #endif } void terminateConnection() { socket->close(); } Q_SIGNALS: void errorUnrecoverableIPCFault(QService::UnrecoverableIPCError); protected: void flushPackage(const QServicePackage& package) { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_4_6); out << package; QByteArray sizeblock; QDataStream outsize(&sizeblock, QIODevice::WriteOnly); outsize.setVersion(QDataStream::Qt_4_6); quint32 size = block.length(); outsize << size; int bytes = socket->write(sizeblock); if (bytes != sizeof(quint32)) { qWarning() << "Failed to write length"; socket->close(); return; } bytes = socket->write(block); if (bytes != block.length()){ qWarning() << "Can't send package, socket error" << block.length() << bytes; socket->close(); return; } } protected Q_SLOTS: void readIncoming() { while (socket->bytesAvailable()) { if (pending_bytes == 0) { /* New packet */ QByteArray data = socket->read(4 - pending_header.length()); pending_header.append(data); if (pending_header.length() == 4) { QDataStream in_size(&pending_header, QIODevice::ReadOnly); in_size.setVersion(QDataStream::Qt_4_6); in_size >> pending_bytes; pending_buf.clear(); pending_header.clear(); } } if (pending_bytes) { /* read any new data and add to buffer */ int readsize = pending_bytes; if (socket->bytesAvailable() < readsize) { readsize = socket->bytesAvailable(); } pending_buf.append(socket->read(readsize)); pending_bytes -= readsize; } if (pending_bytes == 0 && !pending_buf.isEmpty()) { QDataStream in(pending_buf); in.setVersion(QDataStream::Qt_4_6); QServicePackage package; in >> package; incoming.enqueue(package); pending_buf.clear(); emit readyRead(); } } } void socketError(QLocalSocket::LocalSocketError err) { Q_UNUSED(err) // qWarning() << "Socket error!" << err << socket->errorString(); } protected Q_SLOTS: void ipcfault() { emit errorUnrecoverableIPCFault(QService::ErrorServiceNoLongerAvailable); } private: QLocalSocket* socket; QRemoteServiceRegisterLocalSocketPrivate *serviceRegPriv; QByteArray pending_header; QByteArray pending_buf; quint32 pending_bytes; }; QRemoteServiceRegisterLocalSocketPrivate::QRemoteServiceRegisterLocalSocketPrivate(QObject* parent) : QRemoteServiceRegisterPrivate(parent) { } void QRemoteServiceRegisterLocalSocketPrivate::publishServices( const QString& ident) { createServiceEndPoint(ident); } void QRemoteServiceRegisterLocalSocketPrivate::processIncoming() { if (localServer->hasPendingConnections()) { QLocalSocket* s = localServer->nextPendingConnection(); //LocalSocketEndPoint owns socket LocalSocketEndPoint* ipcEndPoint = new LocalSocketEndPoint(s, this); if (getSecurityFilter()){ QServiceClientCredentials creds; ipcEndPoint->getSecurityCredentials(creds); qDebug() << "Security filter call"; getSecurityFilter()(&creds); if (!creds.isClientAccepted()) { s->close(); return; } } ObjectEndPoint* endpoint = new ObjectEndPoint(ObjectEndPoint::Service, ipcEndPoint, this); Q_UNUSED(endpoint); } } /* Creates endpoint on service side. */ bool QRemoteServiceRegisterLocalSocketPrivate::createServiceEndPoint(const QString& ident) { localServer = new QLocalServer(this); connect(localServer, SIGNAL(newConnection()), this, SLOT(processIncoming())); //other IPC mechanisms such as dbus may have to publish the //meta object definition for all registered service types QLocalServer::removeServer(ident); if ( !localServer->listen(ident) ) { qWarning() << "Cannot create local socket endpoint"; return false; } if (localServer->hasPendingConnections()) QMetaObject::invokeMethod(this, "processIncoming", Qt::QueuedConnection); return true; } QRemoteServiceRegisterPrivate* QRemoteServiceRegisterPrivate::constructPrivateObject(QObject *parent) { return new QRemoteServiceRegisterLocalSocketPrivate(parent); } /* Creates endpoint on client side. */ QObject* QRemoteServiceRegisterPrivate::proxyForService(const QRemoteServiceRegister::Entry& entry, const QString& location) { qWarning() << "(all ok) starting SFW proxyForService" << location; QLocalSocket* socket = new QLocalSocket(); socket->connectToServer(location); if (!socket->waitForConnected()){ if (!socket->isValid()) { QString path = location; qWarning() << "Cannot connect to remote service, trying to start service " << path; qint64 pid = 0; // Start the service as a detached process if (QProcess::startDetached(path, QStringList(), QString(), &pid)){ int i; socket->connectToServer(location); for (i = 0; !socket->isValid() && i < 1000; i++){ // Temporary hack till we can improve startup signaling #ifdef Q_OS_WIN ::Sleep(10); #else struct timespec tm; tm.tv_sec = 0; tm.tv_nsec = 1000000; nanosleep(&tm, 0x0); #endif /* Q_OS_WIN */ socket->connectToServer(location); // keep trying for a while } if (!socket->isValid()){ qWarning() << "Server failed to start within waiting period"; return false; } } else { qWarning() << "Server could not be started"; } } } if (socket->isValid()){ LocalSocketEndPoint* ipcEndPoint = new LocalSocketEndPoint(socket); ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Client, ipcEndPoint); QObject *proxy = endPoint->constructProxy(entry); if (proxy){ QObject::connect(proxy, SIGNAL(destroyed()), endPoint, SLOT(deleteLater())); QObject::connect(ipcEndPoint, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), proxy, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError))); } ipcEndPoint->setParent(proxy); endPoint->setParent(proxy); qWarning() << "SFW create object"; return proxy; } return 0; } /*! Returns true if the service is running */ bool QRemoteServiceRegisterPrivate::isServiceRunning(const QRemoteServiceRegister::Entry & /*entry*/, const QString &location) { QLocalSocket* socket = new QLocalSocket(); socket->connectToServer(location); // give a short timeout to block, no running services should fail almost instantly return socket->waitForConnected(1000); } #include "moc_qremoteserviceregister_ls_p.cpp" #include "qremoteserviceregister_ls_p.moc" QT_END_NAMESPACE