summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Stanley-Jones <andrew.stanley-jones@nokia.com>2012-04-19 13:34:26 +1000
committerQt by Nokia <qt-info@nokia.com>2012-04-19 06:06:08 +0200
commite2d6c4d0892624b143289b779e62cc32a90f2d9c (patch)
tree9ac531201b21c00b740c740450165717fba4b6fa
parent67255291c23bf979732268534f04c75837efc617 (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>
-rw-r--r--src/serviceframework/ipc/ipc.pri38
-rw-r--r--src/serviceframework/ipc/ipcendpoint.cpp26
-rw-r--r--src/serviceframework/ipc/ipcendpoint_p.h11
-rw-r--r--src/serviceframework/ipc/objectendpoint.cpp97
-rw-r--r--src/serviceframework/ipc/proxyobject.cpp41
-rw-r--r--src/serviceframework/ipc/qremoteserviceregister_ls_p.cpp19
-rw-r--r--src/serviceframework/ipc/qremoteserviceregister_p.cpp2
-rw-r--r--src/serviceframework/ipc/qremoteserviceregister_unix_p.cpp1074
-rw-r--r--src/serviceframework/ipc/qremoteserviceregister_unix_p.h89
-rw-r--r--src/serviceframework/ipc/qservicepackage_p.h2
-rw-r--r--src/serviceframework/ipc/qslotinvoker.cpp7
-rw-r--r--src/serviceframework/qserviceclientcredentials.h1
-rw-r--r--src/serviceframework/qservicedebuglog.cpp88
-rw-r--r--src/serviceframework/qservicedebuglog_p.h77
-rw-r--r--src/serviceframework/servicedatabase.cpp2
-rw-r--r--src/serviceframework/serviceframework.pro22
-rw-r--r--tests/auto/serviceframework/qservicemanager_ipc/client/tst_qservicemanager_ipc.cpp189
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()
{