1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtSystems module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL21$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 or version 3 as published by the Free
20** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22** following information to ensure the GNU Lesser General Public License
23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** As a special exception, The Qt Company gives you certain additional
27** rights. These rights are described in The Qt Company LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** $QT_END_LICENSE$
31**
32****************************************************************************/
33
34#include "qremoteserviceregister_p.h"
35#include "qremoteserviceregister_dbus_p.h"
36#include "qserviceclientcredentials_p.h"
37
38#include <QDataStream>
39#include <QTimer>
40
41
42QT_BEGIN_NAMESPACE
43
44struct dbus_creds
45{
46 int pid;
47 int uid;
48};
49
50class DBusEndPoint : public QServiceIpcEndPoint
51{
52 Q_OBJECT
53
54public:
55 DBusEndPoint(QDBusInterface* iface, int type, QObject* parent = 0)
56 : QServiceIpcEndPoint(parent), interface(iface), endType(type)
57 {
58 Q_ASSERT(interface);
59 interface->setParent(this);
60
61 connect(sender: interface, SIGNAL(packageReceived(QByteArray,int,QString,int,int)),
62 receiver: this, SLOT(readPackage(QByteArray,int,QString,int,int)));
63
64 if (endType == CLIENT) {
65 QDBusServiceWatcher *watcher = new QDBusServiceWatcher(interface->service(),
66 interface->connection(),
67 QDBusServiceWatcher::WatchForUnregistration);
68
69 QObject::connect(sender: watcher, SIGNAL(serviceUnregistered(QString)),
70 receiver: this, SLOT(serviceRemoved(QString)));
71 }
72 }
73
74 ~DBusEndPoint()
75 {
76 }
77
78public Q_SLOTS:
79 void closeIncoming()
80 {
81 QDBusMessage msg = interface->callWithArgumentList(mode: QDBus::AutoDetect, method: QLatin1String("closeIncoming"),
82 args: QList<QVariant>() << instanceId);
83 }
84
85 void setInstanceId(const QString& id)
86 {
87 instanceId = id;
88 }
89
90Q_SIGNALS:
91 void ipcFault(QService::UnrecoverableIPCError);
92
93protected:
94 void flushPackage(const QServicePackage& package)
95 {
96 if (!QDBusConnection::sessionBus().isConnected()) {
97 qWarning() << "Cannot connect to DBus";
98 }
99
100 QByteArray block;
101 QDataStream out(&block, QIODevice::WriteOnly);
102 out.setVersion(QDataStream::Qt_4_6);
103 out << package;
104
105 packageId = package.d->messageId.toString();
106 interface->asyncCall(method: QLatin1String("writePackage"), args&: block, args&: endType, args&: packageId);
107 }
108
109protected Q_SLOTS:
110 void readPackage(const QByteArray &package, int type, const QString &id, int pid, int uid) {
111 // Check that its of a client-server nature
112 if (endType != type) {
113 // Client to Server
114 if (type != SERVER) {
115 readIncoming(package, pid, uid);
116 } else {
117 // Server to Client
118 if (id == packageId) {
119 readIncoming(package, pid, uid);
120 }
121 }
122 }
123 }
124
125 void readIncoming(const QByteArray &package, int pid, int uid)
126 {
127 QDataStream data(package);
128 QServicePackage pack;
129 data >> pack;
130
131 dbus_creds dcreds;
132
133 dcreds.pid = pid;
134 dcreds.uid = uid;
135
136 creds_list.enqueue(t: dcreds);
137 incoming.enqueue(t: pack);
138 emit readyRead();
139 }
140
141 void serviceRemoved(const QString& name)
142 {
143 Q_UNUSED(name);
144 QString serviceName = interface->service();
145 QDBusReply<bool> reply = interface->connection().interface()->isServiceRegistered(serviceName);
146 if (!reply.value()) {
147 emit ipcFault(QService::ErrorServiceNoLongerAvailable);
148 }
149 }
150
151 void getSecurityCredentials(QServiceClientCredentials &creds)
152 {
153 if (!creds_list.isEmpty()) {
154 dbus_creds dcreds = creds_list.dequeue();
155 creds.d->pid = dcreds.pid;
156 creds.d->uid = dcreds.uid;
157 creds.d->gid = -1;
158 }
159 }
160
161private:
162 QDBusInterface* interface;
163 QString packageId;
164 int endType;
165 QString instanceId;
166 QQueue<dbus_creds> creds_list;
167};
168
169class DBusSessionAdaptor: public QDBusAbstractAdaptor
170{
171 Q_OBJECT
172 Q_CLASSINFO("D-Bus Interface", "com.nokia.qtmobility.sfw.DBusSession")
173
174public:
175 DBusSessionAdaptor(QObject *parent);
176 ~DBusSessionAdaptor() {}
177
178public Q_SLOTS:
179 QByteArray writePackage(const QByteArray &package, int type, const QString &id) {
180 QByteArray ret;
181 QMetaObject::invokeMethod(obj: parent(), member: "writePackage",
182 Q_RETURN_ARG(QByteArray, ret),
183 Q_ARG(QByteArray, package),
184 Q_ARG(int, type),
185 Q_ARG(QString, id));
186 return ret;
187 }
188
189 bool processIncoming() {
190 bool ret;
191 QMetaObject::invokeMethod(obj: parent(), member: "processIncoming",
192 Q_RETURN_ARG(bool, ret));
193 return ret;
194 }
195
196 void acceptIncoming(bool accept) {
197 QMetaObject::invokeMethod(obj: parent(), member: "acceptIncoming",
198 Q_ARG(bool, accept));
199 }
200
201 void closeIncoming(const QString& instanceId) {
202 QMetaObject::invokeMethod(obj: parent(), member: "closeIncoming",
203 Q_ARG(QString, instanceId));
204 }
205
206 void q_autostart() {
207 }
208
209Q_SIGNALS:
210 void packageReceived(const QByteArray &package, int type, const QString &id, int uid, int pid);
211 void newConnection(int pid, int uid);
212};
213
214DBusSessionAdaptor::DBusSessionAdaptor(QObject *parent)
215 : QDBusAbstractAdaptor(parent)
216{
217 setAutoRelaySignals(true);
218}
219
220QRemoteServiceRegisterDBusPrivate::QRemoteServiceRegisterDBusPrivate(QObject* parent)
221 : QRemoteServiceRegisterPrivate(parent)
222{
223}
224
225QRemoteServiceRegisterDBusPrivate::~QRemoteServiceRegisterDBusPrivate()
226{
227}
228
229void QRemoteServiceRegisterDBusPrivate::publishServices(const QString& ident)
230{
231 if (!createServiceEndPoint(ident))
232 QTimer::singleShot(msec: 0, receiver: QCoreApplication::instance(), SLOT(quit()));
233}
234
235/*!
236 Creates endpoint on service side.
237*/
238bool QRemoteServiceRegisterDBusPrivate::createServiceEndPoint(const QString& ident)
239{
240 int endPoints = 0;
241
242 InstanceManager *iManager = InstanceManager::instance();
243 QList<QRemoteServiceRegister::Entry> list = iManager->allEntries();
244
245 if (list.size() < 1)
246 return false;
247
248 QDBusConnection connection = QDBusConnection::sessionBus();
249 if (!connection.isConnected()) {
250 qWarning() << "Cannot connect to DBus";
251 return 0;
252 }
253
254 // Registers the service and session object on DBus if needed
255 for (int i=0; i<list.size(); i++) {
256 QString serviceName = QStringLiteral("com.nokia.qtmobility.sfw.") + list.at(i).serviceName();
257 QDBusReply<bool> reply = connection.interface()->isServiceRegistered(serviceName);
258 if (reply.value())
259 continue;
260
261 if (!connection.registerService(serviceName)) {
262 qWarning() << "Cannot register service to DBus:" << serviceName;
263 continue;
264 }
265
266 // Create and register our DBusSession server/client
267 session = new DBusSession(this);
268 new DBusSessionAdaptor(session);
269 QObject::connect(sender: session, SIGNAL(newConnection(int,int)),
270 receiver: this, SLOT(processIncoming(int,int)));
271
272 QString path = QLatin1Char('/') + list.at(i).interfaceName() + QLatin1Char('/') + ident;
273 path.replace(before: QLatin1Char('.'), after: QLatin1Char('/'));
274 if (!connection.objectRegisteredAt(path)) {
275 if (!connection.registerObject(path, object: session)) {
276 qWarning() << "Cannot register service session to DBus:" << path;
277 continue;
278 }
279
280 iface = new QDBusInterface(serviceName, path, QString(), QDBusConnection::sessionBus());
281 if (!iface->isValid()) {
282 qWarning() << "createServiceEndPoint: Cannot connect to remote service" << serviceName << path;;
283 qWarning() << QString::fromLatin1(str: "%1 %2 %3").arg(a: iface->lastError().name()).
284 arg(a: iface->lastError().message()).
285 arg(a: iface->lastError().type());
286 continue;
287 }
288
289 DBusEndPoint* ipcEndPoint = new DBusEndPoint(iface, SERVER);
290 ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Service, ipcEndPoint, this);
291
292 // Connect session process disconnections
293 QObject::connect(sender: session, SIGNAL(closeConnection(QString,QString)),
294 receiver: endPoint, SLOT(disconnected(QString,QString)));
295
296 endPoints++;
297 }
298 }
299
300 if (endPoints > 0)
301 return true;
302
303 return false;
304}
305
306void QRemoteServiceRegisterDBusPrivate::processIncoming(int pid, int uid)
307{
308 if (getSecurityFilter()) {
309 QServiceClientCredentials cred;
310 cred.d->pid = pid;
311 cred.d->uid = uid;
312 cred.d->gid = -1;
313
314 getSecurityFilter()(&cred);
315
316 if (!cred.isClientAccepted()) {
317 session->acceptIncoming(accept: false);
318
319 // Close service if no instances
320 if (quitOnLastInstanceClosed() &&
321 InstanceManager::instance()->totalInstances() < 1)
322 QCoreApplication::exit();
323
324 return;
325 }
326 }
327
328 session->acceptIncoming(accept: true);
329}
330
331QRemoteServiceRegisterPrivate* QRemoteServiceRegisterPrivate::constructPrivateObject(QObject *parent)
332{
333 return new QRemoteServiceRegisterDBusPrivate(parent);
334}
335
336/*!
337 Creates endpoint on client side.
338*/
339QObject* QRemoteServiceRegisterPrivate::proxyForService(const QRemoteServiceRegister::Entry& entry, const QString& location)
340{
341 const QString serviceName = QStringLiteral("com.nokia.qtmobility.sfw.") + entry.serviceName();
342 QString path = QLatin1Char('/') + entry.interfaceName() + QLatin1Char('/') + location;
343 path.replace(before: QLatin1Char('.'), after: QLatin1Char('/'));
344
345 QDBusConnection connection = QDBusConnection::sessionBus();
346 if (!connection.isConnected()) {
347 qWarning() << "Cannot connect to DBus";
348 return 0;
349 }
350
351 // Dummy call to autostart the service if not running
352 connection.call(message: QDBusMessage::createMethodCall(destination: serviceName, path, interface: QString(), QStringLiteral("q_autostart")));
353
354 QDBusInterface *iface = new QDBusInterface(serviceName, path, QString(), QDBusConnection::sessionBus());
355 if (!iface->isValid()) {
356 qWarning() << "ProxyForService: Cannot connect to remote service" << serviceName << path;
357 qWarning() << QString::fromLatin1(str: "%1 %2 %3").arg(a: iface->lastError().name()).
358 arg(a: iface->lastError().message()).
359 arg(a: iface->lastError().type());
360 return 0;
361 }
362
363 QDBusReply<bool> reply = iface->call(mode: QDBus::Block, method: QLatin1String("processIncoming"));
364 if (reply.value()) {
365 DBusEndPoint* ipcEndPoint = new DBusEndPoint(iface, CLIENT);
366 ObjectEndPoint* endPoint = new ObjectEndPoint(ObjectEndPoint::Client, ipcEndPoint);
367
368 QObject *proxy = endPoint->constructProxy(entry);
369 ipcEndPoint->setInstanceId(endPoint->getInstanceId());
370
371 if (proxy) {
372 QObject::connect(sender: proxy, SIGNAL(destroyed()), receiver: endPoint, SLOT(deleteLater()));
373 QObject::connect(sender: proxy, SIGNAL(destroyed()), receiver: ipcEndPoint, SLOT(closeIncoming()));
374 QObject::connect(sender: ipcEndPoint, SIGNAL(ipcFault(QService::UnrecoverableIPCError)),
375 receiver: proxy, SIGNAL(errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)));
376 }
377 return proxy;
378 }
379
380 qDebug() << "Insufficient credentials to load a service instance";
381 return 0;
382}
383
384/*!
385 Returns true is the service is running
386*/
387
388bool QRemoteServiceRegisterPrivate::isServiceRunning(const QRemoteServiceRegister::Entry &entry, const QString & /*location*/)
389{
390 QDBusConnection connection = QDBusConnection::sessionBus();
391 if (!connection.isConnected()) {
392 qWarning() << "Cannot connect to DBus";
393 return false;
394 }
395
396 // Not exacly right, just because it's registered doesn't mean it's running
397 QString serviceName = QStringLiteral("com.nokia.qtmobility.sfw.") + entry.serviceName();
398 QDBusReply<bool> reply = connection.interface()->isServiceRegistered(serviceName);
399 return reply.value();
400}
401
402#include "moc_qremoteserviceregister_dbus_p.cpp"
403#include "qremoteserviceregister_dbus_p.moc"
404QT_END_NAMESPACE
405

source code of qtsystems/src/serviceframework/ipc/qremoteserviceregister_dbus_p.cpp