diff options
author | Andrew Stanley-Jones <andrew.stanley-jones@nokia.com> | 2012-04-19 13:34:26 +1000 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-04-19 06:06:08 +0200 |
commit | e2d6c4d0892624b143289b779e62cc32a90f2d9c (patch) | |
tree | 9ac531201b21c00b740c740450165717fba4b6fa | |
parent | 67255291c23bf979732268534f04c75837efc617 (diff) |
New backend that uses native unix domain sockets
Add a new unix backend that uses libc instead
of QLocalSocket. This allows for more control
and the option of not spinning the event loop
in the library.
Change-Id: I96e475ed242ee8f6b60751861a7f77457e1954fa
Reviewed-by: Andrew Stanley-Jones <andrew.stanley-jones@nokia.com>
17 files changed, 1714 insertions, 71 deletions
diff --git a/src/serviceframework/ipc/ipc.pri b/src/serviceframework/ipc/ipc.pri index 163b7e85..81cf02a1 100644 --- a/src/serviceframework/ipc/ipc.pri +++ b/src/serviceframework/ipc/ipc.pri @@ -1,23 +1,33 @@ INCLUDEPATH += ipc +win32 { + INCLUDEPATH += . +} QT += core-private -!jsondb:!contains(config_test_jsondb, yes):contains(QT_CONFIG,dbus) { - DEFINES += SFW_USE_DBUS_BACKEND - QT += dbus \ - network - PRIVATE_HEADERS += ipc/qremoteserviceregister_dbus_p.h \ - ipc/objectendpoint_dbus_p.h \ - ipc/qservicemetaobject_dbus_p.h - SOURCES += ipc/qremoteserviceregister_dbus_p.cpp \ - ipc/objectendpoint_dbus.cpp \ - ipc/qservicemetaobject_dbus.cpp -} else { - QT += network - PRIVATE_HEADERS += ipc/qremoteserviceregister_ls_p.h \ +contains(DEFINES, QT_ADDON_JSONDB_LIB): { + PRIVATE_HEADERS += ipc/qremoteserviceregister_unix_p.h \ ipc/objectendpoint_p.h - SOURCES += ipc/qremoteserviceregister_ls_p.cpp \ + SOURCES += ipc/qremoteserviceregister_unix_p.cpp \ ipc/objectendpoint.cpp +} else { + !jsondb:!contains(config_test_jsondb, yes):contains(QT_CONFIG,dbus) { + DEFINES += SFW_USE_DBUS_BACKEND + QT += dbus \ + network + PRIVATE_HEADERS += ipc/qremoteserviceregister_dbus_p.h \ + ipc/objectendpoint_dbus_p.h \ + ipc/qservicemetaobject_dbus_p.h + SOURCES += ipc/qremoteserviceregister_dbus_p.cpp \ + ipc/objectendpoint_dbus.cpp \ + ipc/qservicemetaobject_dbus.cpp + } else { + QT += network + PRIVATE_HEADERS += ipc/qremoteserviceregister_ls_p.h \ + ipc/objectendpoint_p.h + SOURCES += ipc/qremoteserviceregister_ls_p.cpp \ + ipc/objectendpoint.cpp + } } PRIVATE_HEADERS += ipc/qslotinvoker_p.h \ diff --git a/src/serviceframework/ipc/ipcendpoint.cpp b/src/serviceframework/ipc/ipcendpoint.cpp index ce2f0e59..53fd2333 100644 --- a/src/serviceframework/ipc/ipcendpoint.cpp +++ b/src/serviceframework/ipc/ipcendpoint.cpp @@ -41,6 +41,9 @@ #include "ipcendpoint_p.h" +#include <QEventLoop> +#include <QTimer> + QT_BEGIN_NAMESPACE /*! QServiceIpcEndPoint @@ -76,6 +79,29 @@ void QServiceIpcEndPoint::getSecurityCredentials(QServiceClientCredentials &) { } +void QServiceIpcEndPoint::terminateConnection() +{ + qWarning() << "SFW Terminate connection called on base class, should be reimplemented to do something"; +} + +int QServiceIpcEndPoint::waitForData() +{ + QEventLoop loop; + QTimer timer; + timer.setSingleShot(true); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + connect(this, SIGNAL(packageReceived()), &loop, SLOT(quit())); + + timer.start(30000); + loop.exec(); + return 0; +} + +void QServiceIpcEndPoint::waitingDone() +{ +// qDebug() << Q_FUNC_INFO; + emit packageReceived(); +} #include "moc_ipcendpoint_p.cpp" QT_END_NAMESPACE diff --git a/src/serviceframework/ipc/ipcendpoint_p.h b/src/serviceframework/ipc/ipcendpoint_p.h index f8994dc4..59d5b5b6 100644 --- a/src/serviceframework/ipc/ipcendpoint_p.h +++ b/src/serviceframework/ipc/ipcendpoint_p.h @@ -47,8 +47,8 @@ #include <QQueue> #include "qservicepackage_p.h" -#include "../qserviceclientcredentials_p.h" -#include "../qserviceclientcredentials.h" +#include "qserviceclientcredentials_p.h" +#include "qserviceclientcredentials.h" QT_BEGIN_NAMESPACE @@ -64,12 +64,19 @@ public: void writePackage(QServicePackage newPackage); + virtual int waitForData(); + virtual void waitingDone(); + virtual void getSecurityCredentials(QServiceClientCredentials& creds); + virtual void terminateConnection(); + Q_SIGNALS: void readyRead(); void disconnected(); + void packageReceived(); + protected: virtual void flushPackage(const QServicePackage& out) = 0; diff --git a/src/serviceframework/ipc/objectendpoint.cpp b/src/serviceframework/ipc/objectendpoint.cpp index d9d20e1f..56114d06 100644 --- a/src/serviceframework/ipc/objectendpoint.cpp +++ b/src/serviceframework/ipc/objectendpoint.cpp @@ -45,9 +45,8 @@ #include "proxyobject_p.h" #include "qsignalintercepter_p.h" #include "qserviceclientcredentials.h" -#include "../qserviceclientcredentials_p.h" +#include "qserviceclientcredentials_p.h" #include <QTimer> -#include <QEventLoop> #include <QEvent> #include <QVarLengthArray> #include <QTime> @@ -55,20 +54,25 @@ #include <QFile> #include <QStringList> +#include "qservicedebuglog_p.h" + +#ifdef Q_OS_LINUX +#include <execinfo.h> +#endif + QT_BEGIN_NAMESPACE class Response { public: - Response() : isFinished(false), result(0), loop(new QEventLoop) + Response() : isFinished(false), result(0) { } ~Response() - { delete loop; } + { } bool isFinished; void* result; - QEventLoop *loop; QString error; }; @@ -80,7 +84,7 @@ public: ObjectEndPoint* parent) : QSignalIntercepter(sender, signal, parent), endPoint(parent) { - + this->signal = signal; } void setMetaIndex(int index) @@ -91,12 +95,19 @@ public: protected: void activated( const QList<QVariant>& args ) { - //qDebug() << signal() << "emitted." << args; + QServiceDebugLog::instance()->appendToLog( + QString::fromLatin1("--> SIGNAL for %1 index %2/%3 args count %4") + .arg(endPoint->objectName()) + .arg(metaIndex) + .arg(QString::fromLatin1(signal)) + .arg(args.count())); + endPoint->invokeRemote(metaIndex, args, QMetaType::Void); } private: ObjectEndPoint* endPoint; int metaIndex; + QByteArray signal; }; @@ -184,8 +195,13 @@ ObjectEndPoint::ObjectEndPoint(Type type, QServiceIpcEndPoint* comm, QObject* pa d->endPointType = type; dispatch->setParent(this); +#ifdef QT_MTCLIENT_PRESENT + connect(dispatch, SIGNAL(readyRead()), this, SLOT(newPackageReady()), Qt::DirectConnection); + connect(dispatch, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::DirectConnection); +#else connect(dispatch, SIGNAL(readyRead()), this, SLOT(newPackageReady()), Qt::QueuedConnection); connect(dispatch, SIGNAL(disconnected()), this, SLOT(disconnected()), Qt::QueuedConnection); +#endif if (type == Client) { return; //we are waiting for conctructProxy() call } else { @@ -206,7 +222,8 @@ void ObjectEndPoint::disconnected() } foreach (Response *r, openRequests) { r->error = QLatin1Literal("end point disconnected"); - r->loop->exit(-1); + r->isFinished = true; + dispatch->waitingDone(); } } @@ -339,14 +356,15 @@ void ObjectEndPoint::propertyCall(const QServicePackage& p) if (p.d->responseType == QServicePackage::Failed) { response->result = 0; response->error = QLatin1Literal("QServicePackaged::Failed"); - response->loop->exit(-1); + response->isFinished = true; + dispatch->waitingDone(); qWarning() << "Service method call failed"; return; } QVariant* variant = new QVariant(p.d->payload); response->result = reinterpret_cast<void *>(variant); - response->loop->exit(); + dispatch->waitingDone(); } else { qWarning() << "**** FAILED TO FIND MESSAGE ID!!! ****"; @@ -369,7 +387,7 @@ void ObjectEndPoint::objectRequest(const QServicePackage& p) response->result = 0; response->error = QLatin1Literal("objectRequest QServicePackage::Failed"); response->isFinished = true; - response->loop->exit(-1); + dispatch->waitingDone(); qWarning() << "Service instantiation failed"; return; } @@ -380,7 +398,7 @@ void ObjectEndPoint::objectRequest(const QServicePackage& p) response->isFinished = true; //wake up waiting code - response->loop->exit(); + dispatch->waitingDone(); } else { //service side @@ -397,6 +415,9 @@ void ObjectEndPoint::objectRequest(const QServicePackage& p) return; } + setObjectName(p.d->entry.interfaceName() + QLatin1Char(' ') + dispatch->objectName()); + dispatch->setObjectName(objectName()); + //serialize meta object QByteArray data; QDataStream stream( &data, QIODevice::WriteOnly | QIODevice::Append ); @@ -453,6 +474,7 @@ void ObjectEndPoint::objectRequest(const QServicePackage& p) "requesting" << p.d->entry.interfaceName() << p.d->entry.serviceName(); m->removeObjectInstance(p.d->entry, d->serviceInstanceId); disconnected(); + dispatch->terminateConnection(); return; } @@ -516,7 +538,25 @@ void ObjectEndPoint::methodCall(const QServicePackage& p) return; } //service side - Q_ASSERT(d->endPointType == ObjectEndPoint::Service); + if (d->endPointType != ObjectEndPoint::Service) { + + qWarning() << "SFW FATAL ERROR. Client got a method call that wasn't a signal" << objectName(); + + QServiceDebugLog::instance()->dumpLog(); + +#ifdef Q_OS_LINUX + void *symbols[128]; + char **it; + size_t symbol_count; + size_t i; + symbol_count = backtrace(symbols, 128); + it = backtrace_symbols(symbols, symbol_count); + for (i = 0; i < symbol_count; ++i) + printf("TRACE:\t%s\n", it[i]); + printf("\n"); +#endif + return; + } const char* typenames[] = {0,0,0,0,0,0,0,0,0,0}; const void* param[] = {0,0,0,0,0,0,0,0,0,0}; @@ -593,13 +633,14 @@ void ObjectEndPoint::methodCall(const QServicePackage& p) if (p.d->responseType == QServicePackage::Failed) { response->result = 0; response->error = QLatin1Literal("methodCall QServicePakcage::Failed"); - response->loop->exit(-1); + response->isFinished = true; + dispatch->waitingDone(); return; } QVariant* variant = new QVariant(p.d->payload); response->result = reinterpret_cast<void *>(variant); - response->loop->exit(); + dispatch->waitingDone(); } } } @@ -717,13 +758,25 @@ void ObjectEndPoint::waitForResponse(const QUuid& requestId) Q_ASSERT(d->endPointType == ObjectEndPoint::Client); if (openRequests.contains(requestId) ) { Response *r = openRequests.value(requestId); - QTimer timer; - timer.setSingleShot(true); - connect(&timer, SIGNAL(timeout()), r->loop, SLOT(quit())); - timer.start(30000); - r->loop->exec(); - if (timer.isActive()) - timer.stop(); + QTime elapsed; + elapsed.start(); + while (r->isFinished == false && (elapsed.elapsed() < 15000)) { + QServiceDebugLog::instance()->appendToLog(QString::fromLatin1("~~~ Waiting for %1ms blocking call in %2 for uuid %3") + .arg(elapsed.elapsed()) + .arg(objectName()) + .arg(requestId.toString())); + int ret = dispatch->waitForData(); + if (ret != 0) { + qWarning() << this << "SFW ipc error" << r->error; + break; + } + } + QServiceDebugLog::instance()->appendToLog(QString::fromLatin1("=== BLOCKED for %1ms in %2") + .arg(elapsed.elapsed()) + .arg(objectName())); + if (r->isFinished == false) { + qWarning() << "SFW IPC failure, remote end failed to respond to blocking IPC call in" << elapsed.elapsed()/1000 << "seconds." << objectName(); + } if (d->functionReturned) { d->functionReturned = false; QMetaObject::invokeMethod(this, "newPackageReady", Qt::QueuedConnection); diff --git a/src/serviceframework/ipc/proxyobject.cpp b/src/serviceframework/ipc/proxyobject.cpp index 78927abc..779697c1 100644 --- a/src/serviceframework/ipc/proxyobject.cpp +++ b/src/serviceframework/ipc/proxyobject.cpp @@ -42,11 +42,13 @@ #include "proxyobject_p.h" #include <private/qmetaobjectbuilder_p.h> #include "qremoteserviceregisterentry_p.h" +#include "qservicedebuglog_p.h" #include <qtimer.h> #include <qcoreevent.h> #include <QDebug> +#include <QCoreApplication> QT_BEGIN_NAMESPACE @@ -80,8 +82,6 @@ QServiceProxy::QServiceProxy(const QByteArray& metadata, ObjectEndPoint* endPoin if (stream.status() != QDataStream::Ok) { qWarning() << "Invalid metaObject for service received"; } else { - QMetaObjectBuilder sup; - QMetaObject *remote = builder.toMetaObject(); builder.setSuperClass(QServiceProxyBase::metaObject()); @@ -99,6 +99,16 @@ QServiceProxy::QServiceProxy(const QByteArray& metadata, ObjectEndPoint* endPoin d->remoteToLocal[r] = i; } +#if defined(QT_SFW_IPC_DEBUG) && defined(QT_SFW_IPC_DEBUG_VERBOSE) + QString mapping = QString::fromLatin1("%%% QWE Doing lookup table for ") + endPoint->objectName(); + for (int i = 0; i < local->methodCount(); i++){ + const QMetaMethod m = local->method(i); + int r = d->localToRemote[i]; + mapping.append(QString::fromLatin1("\n%%%Mapping %1 from %2 to %3").arg(QString::fromLatin1(m.signature())).arg(i).arg(r)); + } + QServiceDebugLog::instance()->appendToLog(mapping); +#endif + d->meta = local; endPoint->setLookupTable(d->localToRemote, d->remoteToLocal); @@ -160,9 +170,21 @@ int QServiceProxy::qt_metacall(QMetaObject::Call c, int id, void **a) } if (returnType == QMetaType::Void) { + + QServiceDebugLog::instance()->appendToLog( + QString::fromLatin1("--> non-blocking method %1 for %2") + .arg(QString::fromLatin1(method.methodSignature())) + .arg(d->endPoint->objectName())); + d->endPoint->invokeRemote(d->localToRemote[metaIndex], args, returnType); } else { //TODO: invokeRemote() parameter list needs review + + QServiceDebugLog::instance()->appendToLog( + QString::fromLatin1("--> non-BLOCKING method %1 for %2") + .arg(QString::fromLatin1(method.methodSignature())) + .arg(d->endPoint->objectName())); + QVariant result = d->endPoint->invokeRemote(d->localToRemote[metaIndex], args, returnType); if (result.type() != QVariant::Invalid){ if (returnType != QMetaType::QVariant) { @@ -188,6 +210,13 @@ int QServiceProxy::qt_metacall(QMetaObject::Call c, int id, void **a) QVariant arg; if ( c == QMetaObject::WriteProperty ) { + + QServiceDebugLog::instance()->appendToLog( + QString::fromLatin1("--> proerty WRITE operation %1 for %2") + .arg(QString::fromLatin1(property.name())) + .arg(d->endPoint->objectName())); + + if (pType == QMetaType::QVariant) arg = *reinterpret_cast<const QVariant(*)>(a[0]); else if (pType == 0) { @@ -199,6 +228,12 @@ int QServiceProxy::qt_metacall(QMetaObject::Call c, int id, void **a) } QVariant result; if (c == QMetaObject::ReadProperty) { + + QServiceDebugLog::instance()->appendToLog( + QString::fromLatin1("--> proerty READ operation %1 for %2") + .arg(QString::fromLatin1(property.name())) + .arg(d->endPoint->objectName())); + result = d->endPoint->invokeRemoteProperty(metaIndex, arg, pType, c); //wrap result for client if (pType != 0) { @@ -283,7 +318,7 @@ void QServiceProxyBase::connectNotify(const char *signal) void QServiceProxyBase::timerEvent(QTimerEvent *e) { if (e->timerId() == d->timerId) { - qWarning() << this << "Someone is using SFW incorrectly. No one connected to errorUnrecoverableIPCFault for class" << this->metaObject()->className(); + qWarning() << this << "Someone is using SFW incorrectly. No one connected to errorUnrecoverableIPCFault for class" << this->metaObject()->className() << "in" << qApp->applicationFilePath(); killTimer(d->timerId); d->timerId = -1; } else { diff --git a/src/serviceframework/ipc/qremoteserviceregister_ls_p.cpp b/src/serviceframework/ipc/qremoteserviceregister_ls_p.cpp index 6db4d332..e3f22ddd 100644 --- a/src/serviceframework/ipc/qremoteserviceregister_ls_p.cpp +++ b/src/serviceframework/ipc/qremoteserviceregister_ls_p.cpp @@ -43,8 +43,8 @@ #include "qremoteserviceregister_ls_p.h" #include "ipcendpoint_p.h" #include "objectendpoint_p.h" -#include "../qserviceclientcredentials_p.h" -#include "../qserviceclientcredentials.h" +#include "qserviceclientcredentials_p.h" +#include "qserviceclientcredentials.h" #include <QLocalServer> #include <QEventLoop> @@ -58,7 +58,7 @@ #include <QDir> #include <time.h> -#include <sys/types.h> /* See NOTES */ +#include <sys/types.h> #ifdef QT_MTCLIENT_PRESENT #include "qservicemanager.h" @@ -155,6 +155,11 @@ public: #endif } + void terminateConnection() + { + socket->close(); + } + Q_SIGNALS: void errorUnrecoverableIPCFault(QService::UnrecoverableIPCError); @@ -432,7 +437,7 @@ void doStart(const QString &location, QLocalSocket *socket) { QLatin1Literal fmt("hh:mm:ss.zzz"); int fd = ::inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - int wd = ::inotify_add_watch(fd, "/tmp/", IN_MOVED_TO|IN_CREATE); + int wd = ::inotify_add_watch(fd, QDir::tempPath().toLatin1(), IN_MOVED_TO|IN_CREATE); QSocketNotifier notifier(fd, QSocketNotifier::Read); notifier.setEnabled(true); @@ -480,7 +485,7 @@ void doStart(const QString &location, QLocalSocket *socket) { QMetaObject::invokeMethod(serviceRequest, "startService", Q_ARG(QString, location)); - QFileInfo file(QLatin1Literal("/tmp/") + location); + QFileInfo file(QDir::tempPath() + QLatin1Char('/') + location); qWarning() << QTime::currentTime().toString(fmt) << "SFW checking in" << file.path() << "for the socket to come into existance" << file.filePath(); @@ -493,8 +498,8 @@ void doStart(const QString &location, QLocalSocket *socket) { struct sockaddr_un name; name.sun_family = PF_UNIX; - QString fullPath(QLatin1Literal("/tmp/")); - fullPath += location; + QString fullPath(QDir::tempPath()); + fullPath += QLatin1Char('/') + location; ::memcpy(name.sun_path, fullPath.toLatin1().data(), fullPath.toLatin1().size() + 1); diff --git a/src/serviceframework/ipc/qremoteserviceregister_p.cpp b/src/serviceframework/ipc/qremoteserviceregister_p.cpp index 5b79cb5e..e656cd41 100644 --- a/src/serviceframework/ipc/qremoteserviceregister_p.cpp +++ b/src/serviceframework/ipc/qremoteserviceregister_p.cpp @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#include "../qserviceclientcredentials_p.h" +#include "qserviceclientcredentials_p.h" #include "qremoteserviceregister_p.h" #include "instancemanager_p.h" diff --git a/src/serviceframework/ipc/qremoteserviceregister_unix_p.cpp b/src/serviceframework/ipc/qremoteserviceregister_unix_p.cpp new file mode 100644 index 00000000..9a8f836b --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregister_unix_p.cpp @@ -0,0 +1,1074 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtSystems module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qremoteserviceregister_p.h" +#include "qremoteserviceregister_unix_p.h" +#include "ipcendpoint_p.h" +#include "objectendpoint_p.h" +#include "qserviceclientcredentials_p.h" +#include "qserviceclientcredentials.h" +#include "qservicedebuglog_p.h" + +#include <QDataStream> +#include <QTimer> +#include <QProcess> +#include <QFile> +#include <QCoreApplication> +#include <QScopedPointer> +#include <QDir> +#include <QEvent> +#include <QThreadStorage> +#include <QStandardPaths> + +#include <time.h> +#include <sys/types.h> +#include <sys/un.h> +#include <sys/socket.h> +#include <errno.h> + +#include <signal.h> + +#ifdef QT_MTCLIENT_PRESENT +#include "qservicemanager.h" +#include <QCoreApplication> +#include <QTime> +#include <QTextStream> +#include <QFileSystemWatcher> +#include <QSocketNotifier> +#include <sys/stat.h> +#include <stdio.h> +#ifndef Q_OS_MAC +#include <sys/inotify.h> +#endif +#include <unistd.h> +#include <fcntl.h> +#endif + + +#ifdef LOCAL_PEERCRED /* from sys/un.h */ +#include <sys/ucred.h> +#endif + +QT_BEGIN_NAMESPACE + +//IPC based on unix domain sockets + +class UnixEndPoint; +class Waiter; + +Q_GLOBAL_STATIC(QThreadStorage<QList<UnixEndPoint *> >, _q_unixendpoints); +Q_GLOBAL_STATIC(QThreadStorage<QList<QRemoteServiceRegisterUnixPrivate *> >, _q_remoteservice); +Q_GLOBAL_STATIC(QThreadStorage<QList<Waiter *> >, _q_connectionfds); + +class Waiter +{ +public: + enum Type { + Reader = 0, + Writer, + Timer + }; + + Waiter(Type t, int fd) + : fd(fd), + done(false), + type(t), + timeout(0), + enabled(true) + { + QList<Waiter *> &conn = _q_connectionfds()->localData(); + conn.append(this); + } + + ~Waiter() + { + QList<Waiter *> &conn = _q_connectionfds()->localData(); + conn.removeAt(conn.indexOf(this)); + } + + void setEnabled(bool enabled) + { + if (enabled == this->enabled) + return; + + if (enabled) { + QList<Waiter *> &conn = _q_connectionfds()->localData(); + conn.append(this); + } else { + QList<Waiter *> &conn = _q_connectionfds()->localData(); + conn.removeAt(conn.indexOf(this)); + } + this->enabled = enabled; + } + + int fd; + bool done; + Type type; + int timeout; + QString name; + +private: + bool enabled; +}; + +static sighandler_t _qt_service_old_winch = 0; +static bool _qt_service_old_winch_override = false; + +void dump_op_log(int num) { + + qWarning() << "SFW OP LOG"; + QServiceDebugLog::instance()->dumpLog(); + + if (_qt_service_old_winch) + _qt_service_old_winch(num); +} + +class UnixEndPoint : public QServiceIpcEndPoint +{ + Q_OBJECT +public: + UnixEndPoint(int client_fd, QObject* parent = 0); + ~UnixEndPoint(); + + void getSecurityCredentials(QServiceClientCredentials &creds); + + bool event(QEvent *e); + void terminateConnection(); + + int waitForData(); + static int last_packet_size; + static int operation_sequence; + static QStringList op_log; + + static int runLocalEventLoop(int msec = 5000); +Q_SIGNALS: + void errorUnrecoverableIPCFault(QService::UnrecoverableIPCError); + + +protected: + void flushPackage(const QServicePackage& package); +protected slots: + void readIncoming(); + + void registerWithThreadData(); + void socketError(const QString &error); +protected slots: + void ipcfault(); + +private: + + int write(const char *data, int len); + + int client_fd; + bool connection_open; + QSocketNotifier *readNotifier; + + QRemoteServiceRegisterUnixPrivate *serviceRegPriv; + QByteArray pending_header; + QByteArray pending_buf; + quint32 pending_bytes; +}; + +UnixEndPoint::UnixEndPoint(int client_fd, QObject* parent) + : QServiceIpcEndPoint(parent), + client_fd(client_fd), + connection_open(true), + pending_bytes(0) + +{ + if (!_qt_service_old_winch_override) { + _qt_service_old_winch = ::signal(SIGWINCH, dump_op_log); + _qt_service_old_winch_override = true; + } + + registerWithThreadData(); + readNotifier = new QSocketNotifier(client_fd, QSocketNotifier::Read, this); + + QObject::connect(readNotifier, SIGNAL(activated(int)), this, SLOT(readIncoming())); + QString op = QString::fromLatin1("<-> SFW uepc on %1 service %3").arg(client_fd).arg(objectName()); + QServiceDebugLog::instance()->appendToLog(op); +} + +UnixEndPoint::~UnixEndPoint() +{ + if (connection_open) + terminateConnection(); +} + +void UnixEndPoint::getSecurityCredentials(QServiceClientCredentials &creds) +{ + //LocalSocketEndPoint owns socket +#if defined(LOCAL_PEERCRED) + struct xucred xuc; + socklen_t len = sizeof(struct xucred); + + if (getsockopt(client_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(client_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 +} + +bool UnixEndPoint::event(QEvent *e) +{ + if (e->type() == QEvent::ThreadChange) { + QThreadStorage<QList<UnixEndPoint *> > *storage = _q_unixendpoints(); + QList<UnixEndPoint *> &endp = storage->localData(); + endp.removeAt(endp.indexOf(this)); + QMetaObject::invokeMethod(this, "registerWithThreadData", Qt::QueuedConnection); + } + + return QServiceIpcEndPoint::event(e); +} + +void UnixEndPoint::terminateConnection() +{ + if (connection_open) { + QList<UnixEndPoint *> &endp = _q_unixendpoints()->localData(); + QString op = QString::fromLatin1("<-> SFW close on %1 uep %2 interface %3").arg(client_fd) + .arg(endp.indexOf(this)).arg(objectName()); + QServiceDebugLog::instance()->appendToLog(op); + int idx = endp.indexOf(this); + if (idx >= 0) + endp.removeAt(idx); + readNotifier->setEnabled(false); + ::close(client_fd); + client_fd = -1; + connection_open = false; + emit disconnected(); + ipcfault(); + } else { + QString op = QString::fromLatin1("<-> SFW 2nd close on interface %3").arg(objectName()); + QServiceDebugLog::instance()->appendToLog(op); + } +} + +int UnixEndPoint::waitForData() +{ + return UnixEndPoint::runLocalEventLoop(); +} + +int UnixEndPoint::runLocalEventLoop(int msec) { + fd_set reader; + fd_set writer; + struct timeval tv; + + QTime total_time; + total_time.start(); + + FD_ZERO(&reader); + FD_ZERO(&writer); + + QList<UnixEndPoint *> &endp = _q_unixendpoints()->localData(); + + int n = 0; + + foreach (UnixEndPoint *e, endp) { + FD_SET(e->client_fd, &reader); + if (n <= e->client_fd) { + n = e->client_fd+1; + } + } + + QList<QRemoteServiceRegisterUnixPrivate *> &endpu = _q_remoteservice()->localData(); + + foreach (QRemoteServiceRegisterUnixPrivate *e, endpu) { + FD_SET(e->server_fd, &reader); + if (n <= e->server_fd) { + n = e->server_fd+1; + } + } + + QList<Waiter *> &conn = _q_connectionfds()->localData(); + + foreach (Waiter *w, conn) { + if (w->type != Waiter::Timer) { + if (w->type == Waiter::Reader) + FD_SET(w->fd, &reader); + else + FD_SET(w->fd, &writer); + if (n <= w->fd) { + n = w->fd+1; + } + } + if (w->timeout && w->timeout < msec) { + msec = w->timeout; + } + } + + tv.tv_usec = msec*1000; + tv.tv_sec = 0; + +// UnixEndPoint::op_log.append(QStringLiteral("<!> select")); + + int ret = ::select(n, &reader, 0, 0, &tv); + if (ret < 0) { + qWarning() << Q_FUNC_INFO << "SFW Select failed" << qt_error_string(errno); + if (errno == EBADF) { + struct stat buf; + foreach (UnixEndPoint *e, endp) { + if (fstat(e->client_fd, &buf) == -1) { + QServiceDebugLog::instance()->appendToLog(QString::fromLatin1("### holding a bad endpoint file descriptor %1").arg(e->client_fd)); + e->terminateConnection(); + } + } + + foreach (QRemoteServiceRegisterUnixPrivate *e, endpu) { + if (fstat(e->server_fd, &buf) == -1) { + QServiceDebugLog::instance()->appendToLog(QString::fromLatin1("### holding a bad service file descriptor %1").arg(e->server_fd)); + int i = endpu.indexOf(e); + if (i >= 0) + endpu.removeAt(i); + } + } + + foreach (Waiter *w, conn) { + if (w->type != Waiter::Timer) { + if (fstat(w->fd, &buf) == -1) { + QServiceDebugLog::instance()->appendToLog(QString::fromLatin1("### holding a bad file descriptor %1").arg(w->fd)); + int i = conn.indexOf(w); + if (i >= 0) + conn.removeAt(i); + } + } + } + } + return 0; + } else if (ret == 0) { + return 0; + } + + foreach (UnixEndPoint *e, endp) { + if (FD_ISSET(e->client_fd, &reader)) { + e->readIncoming(); + } + } + + foreach (QRemoteServiceRegisterUnixPrivate *e, endpu) { + if (FD_ISSET(e->server_fd, &reader)) { + e->processIncoming(); + } + } + + foreach (Waiter *w, conn) { + if ((w->type == Waiter::Writer && FD_ISSET(w->fd, &writer)) || + (w->type == Waiter::Reader && FD_ISSET(w->fd, &reader))) { + w->done = true; + w->setEnabled(false); + } + } + +#ifdef QT_SFW_IPC_DEBUG + const char *times_str = ::getenv("SFW_BLOCKING_TIMES"); + if (times_str) { + int times = QString::fromLatin1(times_str).toInt(); + if (total_time.elapsed() > times) { + QServiceDebugLog::instance()->appendToLog(QString::fromLatin1("... SFW spun the local eventloop for %1 ms").arg(total_time.elapsed())); + } + } +#endif + + return 0; +} + + +void UnixEndPoint::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; + +#ifdef QT_SFW_IPC_DEBUG + QString op = QString::fromLatin1("--> %1 SFW write to %2 size %3 name %4 ").arg(package.d->messageId.toString()). + arg(client_fd).arg(size).arg(objectName()); + if (package.d->packageType == QServicePackage::ObjectCreation) + op += QLatin1Literal("ObjectCreation "); + else if (package.d->packageType == QServicePackage::MethodCall) + op += QLatin1Literal("MethodCall "); + else if (package.d->packageType == QServicePackage::PropertyCall) + op += QLatin1Literal("PropertyCall "); + if (package.d->responseType == QServicePackage::NotAResponse) + op += QLatin1Literal("NotAResponse"); + if (package.d->responseType == QServicePackage::Success) + op += QLatin1Literal("Success"); + if (package.d->responseType == QServicePackage::Failed) + op += QLatin1Literal("Failed"); + QServiceDebugLog::instance()->appendToLog(op); +#endif + + if (!connection_open) + return; + + int bytes = write(sizeblock.constData(), sizeblock.length()); + if (bytes != sizeof(quint32)) { + qWarning() << "SFW Failed to write length" << client_fd << bytes; + terminateConnection(); + return; + } + bytes = write(block.constData(), block.length()); + if (bytes != block.length()){ + qWarning() << "SFW Can't send package, socket error" << block.length() << bytes; + terminateConnection(); + return; + } +} + +void UnixEndPoint::readIncoming() +{ + if (!connection_open) + return; + + QByteArray raw_data; + raw_data.resize(4096); + int bytes = ::read(client_fd, raw_data.data(), 4096); + if (bytes <= 0) { + readNotifier->setEnabled(false); + terminateConnection(); + socketError(qt_error_string(errno)); + + // socket error, remove the notification + QList<UnixEndPoint *> &endp = _q_unixendpoints()->localData(); + endp.removeAt(endp.indexOf(this)); + return; + } + raw_data.resize(bytes); + + while (!raw_data.isEmpty()) { + + if (pending_bytes == 0) { /* New packet */ + int bytes = 4 - pending_header.length(); + pending_header.append(raw_data.left(bytes)); + raw_data.remove(0, bytes); + 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 (raw_data.length() < readsize) { + readsize = raw_data.length(); + } + pending_buf.append(raw_data.left(readsize)); + raw_data.remove(0, readsize); + pending_bytes -= readsize; + } + + if (pending_bytes == 0 && !pending_buf.isEmpty()) { + int size = pending_buf.length(); + QDataStream in(pending_buf); + in.setVersion(QDataStream::Qt_4_6); + QServicePackage package; + in >> package; + +#ifdef QT_SFW_IPC_DEBUG + QString op = QString::fromLatin1("<-- %1 SFW read fro %2 size %3 name %4 ").arg(package.d->messageId.toString()). + arg(client_fd).arg(size).arg(objectName()); + if (package.d->packageType == QServicePackage::ObjectCreation) + op += QLatin1Literal("ObjectCreation "); + else if (package.d->packageType == QServicePackage::MethodCall) + op += QLatin1Literal("MethodCall "); + else if (package.d->packageType == QServicePackage::PropertyCall) + op += QLatin1Literal("PropertyCall "); + if (package.d->responseType == QServicePackage::NotAResponse) + op += QLatin1Literal("NotAResponse"); + if (package.d->responseType == QServicePackage::Success) + op += QLatin1Literal("Success"); + if (package.d->responseType == QServicePackage::Failed) + op += QLatin1Literal("Failed"); + QServiceDebugLog::instance()->appendToLog(op); +#endif + + incoming.enqueue(package); + pending_buf.clear(); + emit readyRead(); + } + } + Q_ASSERT(raw_data.isEmpty()); +} + +void UnixEndPoint::registerWithThreadData() +{ + QList<UnixEndPoint *> &endp = _q_unixendpoints()->localData(); + endp.append(this); +} + +void UnixEndPoint::socketError(const QString &error) +{ + Q_UNUSED(error) +} + +void UnixEndPoint::ipcfault() +{ + emit errorUnrecoverableIPCFault(QService::ErrorServiceNoLongerAvailable); +} + +int UnixEndPoint::write(const char *data, int len) +{ + int left = len; + + while (left > 0) { + int ret = ::write(client_fd, data+(len-left), left); + if (ret == len) { + break; + } else if ((ret == -1 && errno == EAGAIN) || + (ret >= 0)) { + + left -= ret; + + fd_set write; + + FD_ZERO(&write); + + FD_SET(client_fd, &write); + int n = client_fd+1; + struct timeval tv; + tv.tv_sec = 5; + tv.tv_usec = 0; + + ::select(n, 0, &write, 0, &tv); + + if (!FD_ISSET(client_fd, &write)) { + qWarning() << "Failed to write data to socket" << client_fd << errno << qt_error_string(errno); + QList<UnixEndPoint *> &endp = _q_unixendpoints()->localData(); + endp.removeAt(endp.indexOf(this)); + return -1; + } + } else { + qWarning() << "SFW Error, failed to write to socket" << client_fd << qt_error_string(errno); + return -1; + } + } + return len; +} + +#ifdef QT_MTCLIENT_PRESENT +class ServiceRequestWaiter : public QObject +{ + Q_OBJECT +public: + ServiceRequestWaiter(QObject *request, QObject *parent = 0) + : QObject(parent), receivedLaunched(false) + { + connect(request, SIGNAL(launched(QString)), this, SLOT(launched())); + connect(request, SIGNAL(failed(QString, QString)), this, SLOT(errorEvent(QString, QString))); + connect(request, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), + this, SLOT(ipcFault(QService::UnrecoverableIPCError))); + } + ~ServiceRequestWaiter() { } + + QString errorString; + bool receivedLaunched; + +signals: + void ok(); + void error(); + +protected slots: + void errorEvent(const QString &, const QString& errorString) { + qDebug() << "Got error evernt"; + this->errorString = errorString; + emit error(); + } + + void ipcFault(QService::UnrecoverableIPCError) { + errorEvent(QString(), QLatin1Literal("Unrecoverable IPC fault, unable to request service start")); + } + + void timeout() { + qWarning() << "SFW Timeout talking to the process manager?? exect failure"; + errorEvent(QString(), QLatin1Literal("Timeout waiting for reply")); + } + + void launched() { + qWarning() << "SFW got laucnhed from PM"; + receivedLaunched = true; + emit ok(); + } +}; + +#endif + +QRemoteServiceRegisterUnixPrivate::QRemoteServiceRegisterUnixPrivate(QObject* parent) + : QRemoteServiceRegisterPrivate(parent), server_fd(-1), server_notifier(0) +{ +} + +QRemoteServiceRegisterUnixPrivate::~QRemoteServiceRegisterUnixPrivate() +{ + QList<QRemoteServiceRegisterUnixPrivate *> &endp = _q_remoteservice()->localData(); + int i = endp.indexOf(this); + if (i >= 0) + endp.removeAt(i); +} + +void QRemoteServiceRegisterUnixPrivate::publishServices( const QString& ident) +{ + createServiceEndPoint(ident); +} + +void QRemoteServiceRegisterUnixPrivate::processIncoming() +{ + struct sockaddr_un name; + int len = sizeof(struct sockaddr_un); + + int client_fd = ::accept(server_fd, (struct sockaddr *) &name, (socklen_t *) &len); + + QString op = QString::fromLatin1("<-> SFW accep on %1 for %2 service %5").arg(server_fd). + arg(client_fd).arg(objectName()); + QServiceDebugLog::instance()->appendToLog(op); + + if (client_fd != -1) { + //LocalSocketEndPoint owns socket + UnixEndPoint* ipcEndPoint = new UnixEndPoint(client_fd, this); + + ipcEndPoint->setObjectName(objectName() + QString(QLatin1String(" instance on fd %1")).arg(client_fd)); + + if (getSecurityFilter()){ + QServiceClientCredentials creds; + ipcEndPoint->getSecurityCredentials(creds); + + getSecurityFilter()(&creds); + if (!creds.isClientAccepted()) { + ::close(client_fd); + return; + } + } + ObjectEndPoint* endpoint = new ObjectEndPoint(ObjectEndPoint::Service, ipcEndPoint, this); + endpoint->setObjectName(objectName() + QString(QLatin1String(" instance on fd %1")).arg(client_fd)); + Q_UNUSED(endpoint); + } +} + +/* + Creates endpoint on service side. +*/ +bool QRemoteServiceRegisterUnixPrivate::createServiceEndPoint(const QString& ident) +{ + setObjectName(ident); + server_fd = ::socket(PF_UNIX, SOCK_STREAM, 0); + if (server_fd == -1) { + qWarning() << "SFW Failed to create server socket" << ident << qt_error_string(errno); + return false; + } + + QString location = ident; + location = QDir::cleanPath(QDir::tempPath()); + location += QLatin1Char('/') + ident; + + QString op = QString::fromLatin1("<-> SFW listen on %1 for %2 op %4 service %5").arg(server_fd).arg(objectName()); + QServiceDebugLog::instance()->appendToLog(op); + + + // Note safe, but temporary code + QString tempLocation = location + QLatin1Literal(".temp"); + + ::unlink(location.toLatin1()); + ::unlink(tempLocation.toLatin1()); + + struct sockaddr_un name; + name.sun_family = PF_UNIX; + + ::memcpy(name.sun_path, tempLocation.toLatin1().data(), + tempLocation.toLatin1().size() + 1); + + if (-1 == ::bind(server_fd, (const sockaddr *)&name, sizeof(struct sockaddr_un))) { + qWarning() << "Failed to bind to server socket" << tempLocation << qt_error_string(errno); + return false; + } + + ::chmod(tempLocation.toLatin1(), S_IWUSR|S_IRUSR|S_IWGRP|S_IRGRP|S_IWOTH|S_IROTH); + int uid = getuid(); + int gid = getgid(); + bool doChown = false; + if (isBaseUserIdentifierSet()) { + uid = getBaseUserIdentifier(); + doChown = true; + } + if (isBaseGroupIdentifierSet()) { + gid = getBaseGroupIdentifier(); + doChown = true; + } + if (doChown && (-1 == ::chown(tempLocation.toLatin1(), uid, gid))) { + qWarning() << "Failed to chown socket to request uid/gid" << uid << gid << qt_error_string(errno); + return false; + } + + if (-1 == ::listen(server_fd, 50)) { + qWarning() << "Failed to listen on server socket" << tempLocation << qt_error_string(errno); + return false; + } + + ::rename(tempLocation.toLatin1(), location.toLatin1()); + + server_notifier = new QSocketNotifier(server_fd, QSocketNotifier::Read, this); + connect(server_notifier, SIGNAL(activated(int)), this, SLOT(processIncoming())); + + registerWithThreadData(); + + op = QString::fromLatin1("<-> %1 SFW creat to %2 size %3 name %4").arg(QString()). + arg(server_fd).arg(0).arg(objectName()); + QServiceDebugLog::instance()->appendToLog(op); + + + return true; +} + +bool QRemoteServiceRegisterUnixPrivate::event(QEvent *e) +{ + if (e->type() == QEvent::ThreadChange && server_fd != -1) { + QList<QRemoteServiceRegisterUnixPrivate *> &endp = _q_remoteservice()->localData(); + endp.removeAt(endp.indexOf(this)); + QMetaObject::invokeMethod(this, "registerWithThreadData", Qt::QueuedConnection); + } + + return QRemoteServiceRegisterPrivate::event(e); +} + +void QRemoteServiceRegisterUnixPrivate::registerWithThreadData() +{ + if (server_fd != -1) { + QList<QRemoteServiceRegisterUnixPrivate *> &endp = _q_remoteservice()->localData(); + endp.append(this); + } +} + +QRemoteServiceRegisterPrivate* QRemoteServiceRegisterPrivate::constructPrivateObject(QObject *parent) +{ + return new QRemoteServiceRegisterUnixPrivate(parent); +} + +#ifdef QT_MTCLIENT_PRESENT + +#define SFW_PROCESS_TIMEOUT 5000 + +int doStart(const QString &location) { + + QLatin1Literal fmt("hh:mm:ss.zzz"); + QTime total_time; + total_time.start(); + +#ifndef Q_OS_MAC + int fd = ::inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + int wd = ::inotify_add_watch(fd, "/tmp/", IN_MOVED_TO|IN_CREATE); + + Waiter *w_inotify = new Waiter(Waiter::Reader, fd); + w_inotify->name = QLatin1Literal("inotifier waiter for") + location; +#else + Waiter *w_inotify = new Waiter(Waiter::Timer, -1); + w_inotify->timeout = 100; +#endif + + int socketfd = ::socket(PF_UNIX, SOCK_STREAM, 0); + // set non blocking so we can try to connect and it wont wait + int flags = ::fcntl(socketfd, F_GETFL, 0); + fcntl(socketfd, F_SETFL, flags | O_NONBLOCK); + + struct sockaddr_un name; + name.sun_family = PF_UNIX; + + QString fullPath = QDir::cleanPath(QDir::tempPath()); + fullPath += QLatin1Char('/') + location; + + ::memcpy(name.sun_path, fullPath.toLatin1().data(), + fullPath.toLatin1().size() + 1); + + Waiter *w = new Waiter(Waiter::Reader, socketfd); + w->name = QLatin1Literal("connect waiter for") + location; + + int ret = ::connect(socketfd, (struct sockaddr *) &name, sizeof(struct sockaddr_un)); + + if (ret == -1 && errno == EAGAIN) { + QTime e; + e.start(); + + while ((e.elapsed() < 5000) && !w->done) { + UnixEndPoint::runLocalEventLoop(); + } + + if (w->done) { +#ifndef Q_OS_MAC + ::inotify_rm_watch(fd, wd); + ::close(fd); +#endif + delete w; + delete w_inotify; + return socketfd; + } + } else if (ret == 0) { +#ifndef Q_OS_MAC + ::inotify_rm_watch(fd, wd); + ::close(fd); +#endif + delete w; + delete w_inotify; + return socketfd; + } + + qWarning() << QTime::currentTime().toString(fmt) + << "SFW unable to connect to service, trying to start it." << ret << qt_error_string(errno); + + if (location == QLatin1Literal("com.nokia.mt.processmanager.ServiceRequest") || + location == QLatin1Literal("com.nokia.mt.processmanager.ServiceRequestSocket")) { +#ifndef Q_OS_MAC + ::inotify_rm_watch(fd, wd); + ::close(fd); +#endif + delete w; + delete w_inotify; + close(socketfd); + return -1; + } + + QVariantMap map; + map.insert(QLatin1Literal("interfaceName"), QLatin1Literal("com.nokia.mt.processmanager.ServiceRequest")); + map.insert(QLatin1Literal("serviceName"), QLatin1Literal("com.nokia.mt.processmanager")); + map.insert(QLatin1Literal("major"), 1); + map.insert(QLatin1Literal("minor"), 0); + map.insert(QLatin1Literal("Location"), QLatin1Literal("com.nokia.mt.processmanager.ServiceRequestSocket")); + map.insert(QLatin1Literal("ServiceType"), QService::InterProcess); + + QServiceInterfaceDescriptor desc(map); + QServiceManager manager; + QObject *serviceRequest = manager.loadInterface(desc); + + qWarning() << QTime::currentTime().toString(fmt) + << "SFW Called loadinterface" << serviceRequest; + + if (!serviceRequest) { + qWarning() << "Failed to initiate communications with Process manager, can't start service" << location; +#ifndef Q_OS_MAC + ::inotify_rm_watch(fd, wd); + ::close(fd); +#endif + delete w; + delete w_inotify; + close(socketfd); + return -1; + } + + ServiceRequestWaiter waiter(serviceRequest); + + QMetaObject::invokeMethod(serviceRequest, "startService", Qt::DirectConnection, Q_ARG(QString, location)); + + QFileInfo file(QLatin1Literal("/tmp/") + location); + qWarning() << QTime::currentTime().toString(fmt) + << "SFW checking in" << file.path() << "for the socket to come into existance" << file.filePath(); + + ret = ::connect(socketfd, (struct sockaddr *)&name, sizeof(name)); + if (ret == -1 && errno == EINPROGRESS) { + qWarning() << "SFW got conect in progress"; + w->setEnabled(true); + } else { + qWarning() << QTime::currentTime().toString(fmt) + << "SFW Failed to connect" << qt_error_string(errno) << total_time.elapsed(); + w->setEnabled(false); + } + + bool success = false; + + while (total_time.elapsed() < SFW_PROCESS_TIMEOUT) { + UnixEndPoint::runLocalEventLoop(SFW_PROCESS_TIMEOUT-total_time.elapsed()); + +#ifndef Q_OS_MAC +#define INOTIFY_SIZE (sizeof(struct inotify_event)+1024) + char buffer[INOTIFY_SIZE]; + int n; + while ((n = ::read(fd, buffer, INOTIFY_SIZE)) == INOTIFY_SIZE) { + } + if (n == -1 && errno != EAGAIN) { + qWarning() << QTime::currentTime().toString(fmt) + << "Failed to read inotity, fall back to timeout" << qt_error_string(errno); + } else { + w_inotify->setEnabled(true); + } +#endif + + ret = ::connect(socketfd, (struct sockaddr *)&name, sizeof(name)); + if (ret == 0 || (ret == -1 && errno == EISCONN)) { + if (!waiter.receivedLaunched) { + qWarning() << "SFW asked the PM for a start, PM never replied, application started...something appears broken"; + } + success = true; + break; + } else if (ret == -1 && errno == EINPROGRESS) { + w->setEnabled(true); + } else { + w->setEnabled(false); + } + + if (!waiter.errorString.isEmpty()) { + qWarning() << "SFW Error talking to PM" << socketfd << waiter.errorString; + success = false; + break; + } + } + qWarning() << QTime::currentTime().toString(fmt) << + "SFW doStart done. Total time" << total_time.elapsed() << + "Socket exists:" << file.exists() << + "isSocketValid" << socketfd << + "pm reply" << waiter.receivedLaunched; + + if (success == false) { + ::close(socketfd); + socketfd = -1; + } + + delete w; + delete w_inotify; + delete serviceRequest; +#ifndef Q_OS_MAC + ::inotify_rm_watch(fd, wd); + ::close(fd); +#endif + return socketfd; +} +#endif + +/* + Creates endpoint on client side. +*/ +QObject* QRemoteServiceRegisterPrivate::proxyForService(const QRemoteServiceRegister::Entry& entry, const QString& location) +{ + qWarning() << "(all ok) starting SFW proxyForService" << location; + +#ifdef QT_SFW_IPC_DEBUG + QString op = QString::fromLatin1("<-> SFW proxyx to %2 size %3 name %4"). + arg(entry.d->iface).arg(0).arg(location); + QServiceDebugLog::instance()->appendToLog(op); +#endif + +#ifdef QT_MTCLIENT_PRESENT + int socketfd = doStart(location); +#else + + socket->connectToServer(location); + + if (!socket->waitForConnected()){ + if (!socket->isValid()) { + QString path = location; + qWarning() << "Cannot connect to remote service, trying to start service " << path; + // If we have autotests enable, check for the service in . +#ifdef QT_BUILD_INTERNAL + QFile file(QStringLiteral("./") + path); + if (file.exists()){ + path.prepend(QStringLiteral("./")); + } +#endif /* QT_BUILD_INTERNAL */ + 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 + struct timespec tm; + tm.tv_sec = 0; + tm.tv_nsec = 1000000; + nanosleep(&tm, 0x0); + 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"; + } + } + } +#endif + if (socketfd >= 0){ + UnixEndPoint* ipcEndPoint = new UnixEndPoint(socketfd); + ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Client, ipcEndPoint); + + ipcEndPoint->setObjectName(entry.interfaceName() + QString(QLatin1Literal(" client end %1")).arg(socketfd)); + endPoint->setObjectName(entry.interfaceName() + QString(QLatin1Literal(" client end %1")).arg(socketfd)); + + 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 created object for" << entry.interfaceName(); + return proxy; + } + return 0; +} + +#include "moc_qremoteserviceregister_unix_p.cpp" +#include "qremoteserviceregister_unix_p.moc" +QT_END_NAMESPACE diff --git a/src/serviceframework/ipc/qremoteserviceregister_unix_p.h b/src/serviceframework/ipc/qremoteserviceregister_unix_p.h new file mode 100644 index 00000000..7ea83544 --- /dev/null +++ b/src/serviceframework/ipc/qremoteserviceregister_unix_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtSystems module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREMOTESERVICEREGISTER_UNIX_P_H +#define QREMOTESERVICEREGISTER_UNIX_P_H + +#include "qremoteserviceregister.h" +#include "instancemanager_p.h" +#include "qserviceinterfacedescriptor.h" +#include "qremoteserviceregister_p.h" +#include <QHash> +#include <QSocketNotifier> + +#include "ipcendpoint_p.h" + +QT_BEGIN_NAMESPACE + +class ObjectEndPoint; + +class QRemoteServiceRegisterUnixPrivate: public QRemoteServiceRegisterPrivate +{ + Q_OBJECT +public: + QRemoteServiceRegisterUnixPrivate(QObject* parent); + ~QRemoteServiceRegisterUnixPrivate(); + void publishServices(const QString& ident ); + + void getSecurityCredentials(QServiceClientCredentials &creds); + + bool event(QEvent *e); + +public slots: + void processIncoming(); + +protected slots: + void registerWithThreadData(); + +private: + bool createServiceEndPoint(const QString& ident); + + int server_fd; + QSocketNotifier *server_notifier; + QList<ObjectEndPoint*> pendingConnections; + + friend class UnixEndPoint; +}; + + +QT_END_NAMESPACE + +#endif diff --git a/src/serviceframework/ipc/qservicepackage_p.h b/src/serviceframework/ipc/qservicepackage_p.h index 5129185d..e00db752 100644 --- a/src/serviceframework/ipc/qservicepackage_p.h +++ b/src/serviceframework/ipc/qservicepackage_p.h @@ -47,7 +47,7 @@ #include <QSharedData> #include <QUuid> #include <QVariant> -#include "../qserviceclientcredentials_p.h" +#include "qserviceclientcredentials_p.h" #include "qremoteserviceregister.h" QT_BEGIN_NAMESPACE diff --git a/src/serviceframework/ipc/qslotinvoker.cpp b/src/serviceframework/ipc/qslotinvoker.cpp index 6b4d4339..34dcd8a5 100644 --- a/src/serviceframework/ipc/qslotinvoker.cpp +++ b/src/serviceframework/ipc/qslotinvoker.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qslotinvoker_p.h" #include "qsignalintercepter_p.h" +#include "qservicedebuglog_p.h" #include <qmetaobject.h> #include <qmetatype.h> #include <qvarlengtharray.h> @@ -244,6 +245,12 @@ QVariant QSlotInvoker::invoke( const QList<QVariant>& args ) } } + QServiceDebugLog::instance()->appendToLog( + QString::fromLatin1("<-- slot invoked %1 %2 num of args %2") + .arg(d->receiver->objectName()) + .arg(QString::fromLatin1(d->member)) + .arg(numArgs)); + // Invoke the specified slot. d->receiver->qt_metacall( QMetaObject::InvokeMetaMethod, d->memberIndex, a.data() ); diff --git a/src/serviceframework/qserviceclientcredentials.h b/src/serviceframework/qserviceclientcredentials.h index 7e70b426..753f21c3 100644 --- a/src/serviceframework/qserviceclientcredentials.h +++ b/src/serviceframework/qserviceclientcredentials.h @@ -72,6 +72,7 @@ private: friend class QServiceIpcEndPoint; friend class LocalSocketEndPoint; + friend class UnixEndPoint; friend class DBusEndPoint; friend class QRemoteServiceRegisterDBusPrivate; friend class ObjectEndPoint; diff --git a/src/serviceframework/qservicedebuglog.cpp b/src/serviceframework/qservicedebuglog.cpp new file mode 100644 index 00000000..06374f98 --- /dev/null +++ b/src/serviceframework/qservicedebuglog.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtSystems module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qservicedebuglog_p.h" +#include <QDebug> +#include <QTime> + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QServiceDebugLog, _q_servicedebuglog) + +QServiceDebugLog::QServiceDebugLog() + : logCount(0), length(500), autoDump(false) +{ + +} + +QServiceDebugLog *QServiceDebugLog::instance() +{ + return _q_servicedebuglog(); +} + +void QServiceDebugLog::appendToLog(const QString &message) +{ +#ifdef QT_SFW_IPC_DEBUG + logCount++; + log.append(QTime::currentTime().toString("hh:mm:ss.zzz") + + QString::fromLatin1(" %1 ").arg(logCount) + + message); + if (autoDump && ((logCount%length) == 0)) + dumpLog(); + while (log.length() > length) + log.removeFirst(); +#else + Q_UNUSED(message); +#endif +} + +const QStringList QServiceDebugLog::fetchLog() +{ + return log; +} + +void QServiceDebugLog::dumpLog() +{ + foreach (QString line, log) + qDebug() << line; +} + +QT_END_NAMESPACE diff --git a/src/serviceframework/qservicedebuglog_p.h b/src/serviceframework/qservicedebuglog_p.h new file mode 100644 index 00000000..7281d237 --- /dev/null +++ b/src/serviceframework/qservicedebuglog_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtSystems module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + + +#ifndef QSERVICEDEBUGLOG_P_H +#define QSERVICEDEBUGLOG_P_H + +#include <QString> +#include <QStringList> + +QT_BEGIN_NAMESPACE + +class QServiceDebugLog +{ +public: + QServiceDebugLog(); + + static QServiceDebugLog* instance(); + + void appendToLog(const QString &message); + + void setLength(int length); + void setEnableAutoOutput(bool enabled); + + void dumpLog(); + const QStringList fetchLog(); + +private: + QStringList log; + int logCount; + int length; + bool autoDump; + +}; + +QT_END_NAMESPACE + +#endif // QSERVICEDEBUGLOG_P_H diff --git a/src/serviceframework/servicedatabase.cpp b/src/serviceframework/servicedatabase.cpp index ee014cd7..c985b0f9 100644 --- a/src/serviceframework/servicedatabase.cpp +++ b/src/serviceframework/servicedatabase.cpp @@ -153,7 +153,7 @@ bool ServiceDatabase::open() file.close(); } - m_connectionName = dbFileInfo.completeBaseName(); + m_connectionName = dbFileInfo.completeBaseName() + QString("--%1").arg(reinterpret_cast<unsigned long>(QThread::currentThreadId())); QSqlDatabase database; if (QSqlDatabase::contains(m_connectionName)) { database = QSqlDatabase::database(m_connectionName); diff --git a/src/serviceframework/serviceframework.pro b/src/serviceframework/serviceframework.pro index a748b89d..54354e2a 100644 --- a/src/serviceframework/serviceframework.pro +++ b/src/serviceframework/serviceframework.pro @@ -10,24 +10,17 @@ QT = core DEFINES += QT_BUILD_SFW_LIB QT_MAKEDLL - load(qt_module_config) +sfwdebug: { + DEFINES += QT_SFW_IPC_DEBUG +} + jsondb|contains(QT_CONFIG, jsondb): { mtlib|contains(config_test_mtlib, yes): { DEFINES += QT_NO_DBUS QT_ADDON_JSONDB_LIB QT_MTCLIENT_PRESENT QT += jsondb } - !no_wayland: { - DEFINES += QT_WAYLAND_PRESENT - } - else: { - error(Config option no_wayland is no longer valid in this configuration) - } - no_security: { - warning(SERVICE FRAMEWORK SECURITY IS DISABLED) - DEFINES += QT_DISABLE_SECURITY - } } include(ipc/ipc.pri) @@ -42,7 +35,9 @@ PUBLIC_HEADERS += qservice.h \ PRIVATE_HEADERS += servicemetadata_p.h \ qserviceinterfacedescriptor_p.h \ dberror_p.h \ - qserviceclientcredentials_p.h + qserviceclientcredentials_p.h \ + qservicedebuglog_p.h + SOURCES += servicemetadata.cpp \ qservicemanager.cpp \ qserviceplugininterface.cpp \ @@ -50,7 +45,8 @@ SOURCES += servicemetadata.cpp \ qservicefilter.cpp \ dberror.cpp \ qremoteserviceregister.cpp \ - qserviceclientcredentials.cpp + qserviceclientcredentials.cpp \ + qservicedebuglog.cpp contains(DEFINES, QT_ADDON_JSONDB_LIB): { SOURCES += databasemanager_jsondb.cpp diff --git a/tests/auto/serviceframework/qservicemanager_ipc/client/tst_qservicemanager_ipc.cpp b/tests/auto/serviceframework/qservicemanager_ipc/client/tst_qservicemanager_ipc.cpp index c0122fab..5aed3a12 100644 --- a/tests/auto/serviceframework/qservicemanager_ipc/client/tst_qservicemanager_ipc.cpp +++ b/tests/auto/serviceframework/qservicemanager_ipc/client/tst_qservicemanager_ipc.cpp @@ -75,6 +75,7 @@ inline QDBusArgument &operator<<(QDBusArgument &arg, const QVariantHash &map) #ifdef Q_OS_MAC #include <stdlib.h> +#include <sys/resource.h> #endif QT_USE_NAMESPACE @@ -83,6 +84,97 @@ Q_DECLARE_METATYPE(QVariant); Q_DECLARE_METATYPE(QList<QString>); Q_DECLARE_METATYPE(QVariantHash); +char serviceReuqestXml[] = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<SFW version=\"1.1\">" + "<service>" + "<name>com.nokia.mt.processmanager</name>" + "<ipcaddress>com.nokia.mt.processmanager.ServiceRequestSocket</ipcaddress>" + "<interface>" + "<name>com.nokia.mt.processmanager.ProcessManagerController</name>" + "<version>1.0</version>" + "</interface>" + "</service>" + "</SFW>"; + +char serviceReuqestXmlFake[] = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<SFW version=\"1.1\">" + "<service>" + "<name>com.nokia.qt.does.not.exist</name>" + "<ipcaddress>com.nokia.qt.does.not.exist</ipcaddress>" + "<interface>" + "<name>com.nokia.qt.interface.does.not.exist</name>" + "<version>1.0</version>" + "</interface>" + "</service>" + "</SFW>"; + +char serviceReuqestXmlStarted[] = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<SFW version=\"1.1\">" + "<service>" + "<name>com.nokia.qt.tests.autostart</name>" + "<ipcaddress>com.nokia.qt.tests.autostart</ipcaddress>" + "<interface>" + "<name>com.nokkia.qt.tests.autostarted</name>" + "<version>1.0</version>" + "</interface>" + "</service>" + "</SFW>"; + + +class ServiceRequest : public QObject +{ + Q_OBJECT +public: + ServiceRequest(QObject *o = 0) : QObject(o) + { + } + + Q_INVOKABLE void startService(const QString location) + { + qDebug() << "Got startService"; + if (location == "com.nokia.qt.tests.autostart") { + QMetaObject::invokeMethod(this, "sendOk", Qt::DirectConnection, Q_ARG(QString, location)); + } else { + QMetaObject::invokeMethod(this, "sendError", Qt::DirectConnection, Q_ARG(QString, location)); + } + } + +protected slots: + void sendError(const QString &location) + { + qDebug() << "Send fail"; + emit failed(QLatin1Literal("Test code failing creation"), location); + } + + void sendOk(const QString &location) + { + emit launched(location); + QMetaObject::invokeMethod(this, "startNewService", Qt::DirectConnection); + } + + void startNewService() + { + QRemoteServiceRegister *r = new QRemoteServiceRegister(this); + + QRemoteServiceRegister::Entry pmEntry = + r->createEntry<ServiceRequest>( + QLatin1Literal("com.nokia.qt.tests.autostart"), + QLatin1Literal("com.nokkia.qt.tests.autostarted"), + QLatin1Literal("1.0")); + + r->publishEntries(QLatin1Literal("com.nokia.qt.tests.autostart")); + } + +signals: + + void launched(QString process); + void failed(QString error, QString process); + +}; + class tst_QServiceManager_IPC: public QObject { Q_OBJECT @@ -140,6 +232,8 @@ private slots: void testServiceSecurity(); + void testProcessLaunch(); + void testIpcFailure(); signals: @@ -181,6 +275,20 @@ void tst_QServiceManager_IPC::initTestCase() qDBusRegisterMetaType<QVariantHash>(); #endif +#ifdef Q_OS_MAC + struct rlimit rlp; + int ret = getrlimit(RLIMIT_NOFILE, &rlp); + if (ret == -1) { + qWarning() << "Failed to get fd limit on mac, expect the threading test to fail"; + } else { + rlp.rlim_cur = 512; + ret = setrlimit(RLIMIT_NOFILE, &rlp); + if (ret == -1) { + qWarning() << "Failed to raise the number of open files to 512, expect the threading test to fail"; + } + } +#endif + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); QServiceManager* manager = new QServiceManager(this); @@ -258,6 +366,32 @@ void tst_QServiceManager_IPC::initTestCase() // all objects come from the same service, just need to connect to 1 signal connect(serviceSharedOther, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)), this, SLOT(ipcError(QService::UnrecoverableIPCError))); + + QRemoteServiceRegister *reg = new QRemoteServiceRegister(); + + QRemoteServiceRegister::Entry pmEntry = + reg->createEntry<ServiceRequest>( + QLatin1Literal("com.nokia.mt.processmanager"), + QLatin1Literal("com.nokia.mt.processmanager.ServiceRequest"), + QLatin1Literal("1.0")); + + reg->publishEntries("com.nokia.mt.processmanager.ServiceRequestSocket"); + + QByteArray ba(serviceReuqestXml); + QBuffer b(&ba); + manager->removeService(QLatin1Literal("com.nokia.mt.processmanager")); + manager->addService(&b); + + QByteArray baFake(serviceReuqestXmlFake); + QBuffer bFake(&baFake); + manager->removeService(QLatin1Literal("com.nokia.qt.does.not.exists")); + manager->addService(&bFake); + + QByteArray baAutoStart(serviceReuqestXmlStarted); + QBuffer bAutoStart(&baAutoStart); + manager->removeService(QLatin1Literal("com.nokia.qt.tests.autostart")); + manager->addService(&bAutoStart); + } void tst_QServiceManager_IPC::ipcError(QService::UnrecoverableIPCError err) @@ -299,6 +433,9 @@ void tst_QServiceManager_IPC::cleanupTestCase() // clean up the unit, don't leave it registered QServiceManager m; m.removeService("IPCExampleService"); + m.removeService(QLatin1Literal("com.nokia.mt.processmanager")); + m.removeService(QLatin1Literal("com.nokia.qt.does.not.exists")); + m.removeService(QLatin1Literal("com.nokia.qt.tests.autostart")); } void tst_QServiceManager_IPC::init() @@ -1259,12 +1396,6 @@ void FetchLotsOfProperties::ipcError() void tst_QServiceManager_IPC::verifyThreadSafety() { - -#ifdef Q_OS_MAC - QEXPECT_FAIL("", "Test case known to segfault on MAC, skipping, QTBUG-23182", Abort); - QVERIFY(false); -#else - QFETCH(int, shared); QFETCH(int, runs); QFETCH(int, threads); @@ -1321,7 +1452,6 @@ void tst_QServiceManager_IPC::verifyThreadSafety() QCOMPARE(finished.count(), threads); QVERIFY2(!errors.count(), "Threads reported errors"); -#endif } void tst_QServiceManager_IPC::verifyThreadSafety_data() @@ -1419,6 +1549,51 @@ void tst_QServiceManager_IPC::testServiceSecurity() } +void tst_QServiceManager_IPC::testProcessLaunch() +{ +#ifdef QT_JSONDB + QServiceManager m; + + QVariantMap map; + map.insert(QLatin1Literal("interfaceName"), QLatin1Literal("com.nokia.mt.processmanager.ServiceRequest")); + map.insert(QLatin1Literal("serviceName"), QLatin1Literal("com.nokia.mt.processmanager")); + map.insert(QLatin1Literal("major"), 1); + map.insert(QLatin1Literal("minor"), 0); + map.insert(QLatin1Literal("Location"), QLatin1Literal("com.nokia.mt.processmanager.ServiceRequestSocket")); + map.insert(QLatin1Literal("ServiceType"), QService::InterProcess); + QServiceInterfaceDescriptor desc(map); + + QObject *s = m.loadInterface(desc); + + QVERIFY(s != 0); + + QString name = QLatin1Literal("anything"); + QSignalSpy serviceSpy(s, SIGNAL(failed(QString, QString))); + + QMetaObject::invokeMethod(s, "startService", Q_ARG(QString, name)); + + QTRY_COMPARE(serviceSpy.count(), 1); + + + qDebug() << "******* doing auto start, and fail"; + + QObject *o = m.loadInterface(QLatin1Literal("com.nokia.qt.interface.does.not.exist")); + + // should fail, but verify nothing blocks and we get here + QVERIFY2(o == 0, "We got a valid object from a service that doesn't exist"); + + + qDebug() << "******* doing auto start, and WORK"; + + o = m.loadInterface(QLatin1Literal("com.nokkia.qt.tests.autostarted")); + + QVERIFY2(o != 0,"We must get a valid interface back"); + + delete o; + delete s; +#endif +} + void tst_QServiceManager_IPC::testIpcFailure() { |