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 "qservicemanager.h" |
35 | #include "qserviceplugininterface.h" |
36 | #include "qserviceinterfacedescriptor_p.h" |
37 | #include "qremoteserviceregister_p.h" |
38 | #include "qremoteserviceregisterentry_p.h" |
39 | #include "qserviceoperations_p.h" |
40 | #include "qservicereply.h" |
41 | #include "qservicerequest_p.h" |
42 | #include "qservicedebuglog_p.h" |
43 | |
44 | #include "databasemanager_p.h" |
45 | |
46 | #include <QObject> |
47 | #include <QMetaMethod> |
48 | #include <QPluginLoader> |
49 | #include <QFile> |
50 | #include <QCoreApplication> |
51 | #include <QDir> |
52 | #include <QSystemSemaphore> |
53 | #include <QScopedPointer> |
54 | |
55 | QT_BEGIN_NAMESPACE |
56 | |
57 | QString QServiceManager::resolveLibraryPath(const QString &libNameOrPath) |
58 | { |
59 | if (QFile::exists(fileName: libNameOrPath)) |
60 | return libNameOrPath; |
61 | |
62 | // try to find plug-in via QLibrary |
63 | QStringList paths = QCoreApplication::libraryPaths(); |
64 | #ifdef QTM_PLUGIN_PATH |
65 | paths << QLatin1String(QTM_PLUGIN_PATH)+QLatin1String("/serviceframework" ); |
66 | #endif |
67 | for (int i=0; i<paths.count(); i++) { |
68 | QString libPath = QDir::toNativeSeparators(pathName: paths[i]) + QDir::separator() + libNameOrPath; |
69 | |
70 | QLibrary lib(libPath); |
71 | if (lib.load()) { |
72 | lib.unload(); |
73 | return lib.fileName(); |
74 | } |
75 | } |
76 | return QString(); |
77 | } |
78 | |
79 | class QServicePluginCleanup : public QObject |
80 | { |
81 | Q_OBJECT |
82 | public: |
83 | QServicePluginCleanup(QPluginLoader *loader, QObject *parent = 0) |
84 | : QObject(parent), |
85 | m_loader(loader) |
86 | { |
87 | } |
88 | |
89 | ~QServicePluginCleanup() |
90 | { |
91 | if (m_loader) { |
92 | //m_loader->unload(); |
93 | delete m_loader; |
94 | } |
95 | } |
96 | |
97 | QPluginLoader *m_loader; |
98 | }; |
99 | |
100 | class QServiceManagerPrivate : public QObject |
101 | { |
102 | Q_OBJECT |
103 | public: |
104 | QServiceManager *manager; |
105 | DatabaseManager *dbManager; |
106 | QServiceOperations *ops; |
107 | QService::Scope scope; |
108 | QServiceManager::Error error; |
109 | |
110 | QServiceManagerPrivate(QServiceManager *parent = 0) |
111 | : QObject(parent), |
112 | manager(parent), |
113 | dbManager(new DatabaseManager), |
114 | ops(0) |
115 | { |
116 | connect(asender: dbManager, SIGNAL(serviceAdded(QString, DatabaseManager::DbScope)), |
117 | SLOT(serviceAdded(QString, DatabaseManager::DbScope))); |
118 | connect(asender: dbManager, SIGNAL(serviceRemoved(QString, DatabaseManager::DbScope)), |
119 | SLOT(serviceRemoved(QString, DatabaseManager::DbScope))); |
120 | } |
121 | |
122 | ~QServiceManagerPrivate() |
123 | { |
124 | delete dbManager; |
125 | } |
126 | |
127 | void setError(QServiceManager::Error err) |
128 | { |
129 | if (error != err) |
130 | { |
131 | error = err; |
132 | manager->errorChanged(); |
133 | } |
134 | } |
135 | |
136 | void setError() |
137 | { |
138 | QServiceManager::Error prev = error; |
139 | switch (dbManager->lastError().code()) { |
140 | case DBError::NoError: |
141 | error = QServiceManager::NoError; |
142 | break; |
143 | case DBError::DatabaseNotOpen: |
144 | case DBError::InvalidDatabaseConnection: |
145 | case DBError::CannotCreateDbDir: |
146 | case DBError::CannotOpenServiceDb: |
147 | case DBError::NoWritePermissions: |
148 | case DBError::InvalidDatabaseFile: |
149 | error = QServiceManager::StorageAccessError; |
150 | break; |
151 | case DBError::LocationAlreadyRegistered: |
152 | error = QServiceManager::ServiceAlreadyExists; |
153 | break; |
154 | case DBError::IfaceImplAlreadyRegistered: |
155 | error = QServiceManager::ImplementationAlreadyExists; |
156 | break; |
157 | case DBError::NotFound: |
158 | error = QServiceManager::ComponentNotFound; |
159 | break; |
160 | case DBError::InvalidDescriptorScope: |
161 | error = QServiceManager::InvalidServiceInterfaceDescriptor; |
162 | break; |
163 | case DBError::SqlError: |
164 | case DBError::IfaceIDNotExternal: |
165 | case DBError::ExternalIfaceIDFound: |
166 | case DBError::UnknownError: |
167 | error = QServiceManager::UnknownError; |
168 | break; |
169 | } |
170 | if (prev != error) |
171 | manager->errorChanged(); |
172 | } |
173 | |
174 | private Q_SLOTS: |
175 | void serviceAdded(const QString &service, DatabaseManager::DbScope dbScope) |
176 | { |
177 | QService::Scope s = (dbScope == DatabaseManager::SystemScope ? |
178 | QService::SystemScope : QService::UserScope); |
179 | emit manager->serviceAdded(serviceName: service, scope: s); |
180 | } |
181 | |
182 | void serviceRemoved(const QString &service, DatabaseManager::DbScope dbScope) |
183 | { |
184 | QService::Scope s = (dbScope == DatabaseManager::SystemScope ? |
185 | QService::SystemScope : QService::UserScope); |
186 | emit manager->serviceRemoved(serviceName: service, scope: s); |
187 | } |
188 | }; |
189 | |
190 | /*! |
191 | \class QServiceManager |
192 | \ingroup servicefw |
193 | \inmodule QtServiceFramework |
194 | \brief The QServiceManager class enables the loading of service plugins |
195 | and the (de)registration of services. |
196 | |
197 | A service is a stand-alone component that can be used by multiple clients. |
198 | Each service implementation must derive from QObject. Clients request a |
199 | reference to a service via \l loadInterface() or \l loadLocalTypedInterface(). |
200 | |
201 | Services are separate deliveries in the form of plug-ins. New services can be (de)registered |
202 | at any time via \l addService() and \l removeService() respectively. Such an event is |
203 | published via the \l serviceAdded() and \l serviceRemoved() signal. |
204 | Each service plug-in must implement QServicePluginInterface. |
205 | |
206 | Each plug-in may support multiple interfaces and may even provide multiple implementations |
207 | for the same interface. Individual implementations are identified via |
208 | QServiceInterfaceDescriptor. For a more detailed explanation of services and how they relate to |
209 | interface and their implementations please see QServiceInterfaceDescriptor. |
210 | |
211 | \sa QServicePluginInterface |
212 | */ |
213 | |
214 | /*! |
215 | \enum QServiceManager::Error |
216 | Defines the possible errors for the service manager. |
217 | |
218 | \value NoError No error occurred. |
219 | \value StorageAccessError The service data storage is not accessible. This could be because the caller does not have the required permissions. |
220 | \value InvalidServiceLocation The service was not found at its specified \l{QServiceInterfaceDescriptor::Location}{location}. |
221 | \value InvalidServiceXml The XML defining the service metadata is invalid. |
222 | \value InvalidServiceInterfaceDescriptor The service interface descriptor is invalid, or refers to an interface implementation that cannot be accessed in the current scope. |
223 | \value ServiceAlreadyExists Another service has previously been registered with the same \l{QServiceInterfaceDescriptor::Location}{location}. |
224 | \value ImplementationAlreadyExists Another service that implements the same interface version has previously been registered. |
225 | \value PluginLoadingFailed The service plugin cannot be loaded. |
226 | \value ComponentNotFound The service or interface implementation has not been registered. |
227 | \value ServiceCapabilityDenied The security session does not permit service access based on its capabilities. |
228 | \value UnknownError An unknown error occurred. |
229 | */ |
230 | |
231 | /*! |
232 | \fn void QServiceManager::serviceAdded(const QString& serviceName, QService::Scope scope) |
233 | |
234 | This signal is emited whenever a new service with the given |
235 | \a serviceName has been registered with the service manager. |
236 | \a scope indicates where the service was added. |
237 | |
238 | If the manager scope is QService::SystemScope, it will not receive |
239 | notifications about services added in the user scope. |
240 | |
241 | \sa addService() |
242 | */ |
243 | |
244 | /*! |
245 | \fn void QServiceManager::serviceRemoved(const QString& serviceName, QService::Scope scope) |
246 | |
247 | This signal is emited whenever a service with the given |
248 | \a serviceName has been deregistered with the service manager. |
249 | \a scope indicates where the service was added. |
250 | |
251 | If the manager scope is QService::SystemScope, it will not receive |
252 | notifications about services removed in the user scope. |
253 | |
254 | \sa removeService() |
255 | */ |
256 | |
257 | /*! |
258 | Creates a service manager with the given \a parent. |
259 | |
260 | The scope will default to QService::UserScope. |
261 | |
262 | The service manager will also ensure that a background thread is started to handle |
263 | service manager requests. If you need to supress this behavior so that all requests |
264 | are handled in the foreground (in the main GUI thread) export the following environment |
265 | variable: |
266 | \code |
267 | env QT_NO_SFW_BACKGROUND_OPERATION /path/to/my_sfw_app |
268 | \endcode |
269 | */ |
270 | QServiceManager::QServiceManager(QObject *parent) |
271 | : QObject(parent), |
272 | d(new QServiceManagerPrivate(this)) |
273 | { |
274 | qRegisterMetaType<QService::UnrecoverableIPCError>(typeName: "QService::UnrecoverableIPCError" ); |
275 | d->scope = QService::UserScope; |
276 | } |
277 | |
278 | /*! |
279 | Creates a service manager with the given \a scope and \a parent. |
280 | */ |
281 | QServiceManager::QServiceManager(QService::Scope scope, QObject *parent) |
282 | : QObject(parent), |
283 | d(new QServiceManagerPrivate(this)) |
284 | { |
285 | d->scope = scope; |
286 | } |
287 | |
288 | /*! |
289 | Destroys the service manager. |
290 | */ |
291 | QServiceManager::~QServiceManager() |
292 | { |
293 | if (d->ops) |
294 | d->ops->disengage(); |
295 | delete d; |
296 | } |
297 | |
298 | /*! |
299 | Returns the scope used for registering and searching of services. |
300 | */ |
301 | QService::Scope QServiceManager::scope() const |
302 | { |
303 | return d->scope; |
304 | } |
305 | |
306 | /*! |
307 | Returns a list of the services that provide the interface specified by |
308 | \a interfaceName. If \a interfaceName is empty, this function returns |
309 | a list of all available services in this manager's scope. |
310 | */ |
311 | QStringList QServiceManager::findServices(const QString& interfaceName) const |
312 | { |
313 | d->setError(QServiceManager::NoError); |
314 | QStringList services; |
315 | services = d->dbManager->getServiceNames(interfaceName, |
316 | scope: d->scope == QService::SystemScope ? DatabaseManager::SystemScope : DatabaseManager::UserScope); |
317 | d->setError(); |
318 | return services; |
319 | } |
320 | |
321 | /*! |
322 | Returns a list of the interfaces that match the specified \a filter. |
323 | */ |
324 | QList<QServiceInterfaceDescriptor> QServiceManager::findInterfaces(const QServiceFilter& filter) const |
325 | { |
326 | d->setError(QServiceManager::NoError); |
327 | QList<QServiceInterfaceDescriptor> descriptors = d->dbManager->getInterfaces(filter, |
328 | scope: d->scope == QService::SystemScope ? DatabaseManager::SystemScope : DatabaseManager::UserScope); |
329 | if (descriptors.isEmpty() && d->dbManager->lastError().code() != DBError::NoError) { |
330 | d->setError(); |
331 | return QList<QServiceInterfaceDescriptor>(); |
332 | } |
333 | return descriptors; |
334 | } |
335 | |
336 | /*! |
337 | Returns a list of the interfaces provided by the service named |
338 | \a serviceName. If \a serviceName is empty, this function returns |
339 | a list of all available interfaces in this manager's scope. |
340 | */ |
341 | QList<QServiceInterfaceDescriptor> QServiceManager::findInterfaces(const QString& serviceName) const |
342 | { |
343 | QServiceFilter filter; |
344 | if (!serviceName.isEmpty()) |
345 | filter.setServiceName(serviceName); |
346 | return findInterfaces(filter); |
347 | } |
348 | |
349 | /*! |
350 | Loads and returns the interface specified by \a interfaceName. |
351 | |
352 | The caller takes ownership of the returned pointer. |
353 | |
354 | This function returns a null pointer if the requested service cannot be found. |
355 | |
356 | \sa setInterfaceDefault(), interfaceDefault() |
357 | */ |
358 | QObject* QServiceManager::loadInterface(const QString& interfaceName) |
359 | { |
360 | return loadInterface(descriptor: interfaceDefault(interfaceName)); |
361 | } |
362 | |
363 | /*! |
364 | \internal |
365 | Private helper function to get the QObject for an InterProcess service. |
366 | */ |
367 | QObject *QServiceManager::loadInterProcessService(const QServiceInterfaceDescriptor &descriptor, |
368 | const QString &serviceLocation) const |
369 | { |
370 | //ipc service |
371 | const int majorversion = descriptor.majorVersion(); |
372 | const int minorversion = descriptor.minorVersion(); |
373 | QString version = QString::number(majorversion) + QLatin1String("." ) + QString::number(minorversion); |
374 | |
375 | QRemoteServiceRegister::Entry serviceEntry; |
376 | serviceEntry.d->iface = descriptor.interfaceName(); |
377 | serviceEntry.d->service = descriptor.serviceName(); |
378 | serviceEntry.d->ifaceVersion = version; |
379 | |
380 | QObject* service = QRemoteServiceRegisterPrivate::proxyForService(entry: serviceEntry, location: serviceLocation); |
381 | if (!service) |
382 | d->setError(QServiceManager::InvalidServiceLocation); |
383 | |
384 | //client owns proxy object |
385 | return service; |
386 | } |
387 | |
388 | /*! |
389 | \internal |
390 | Private helper function to get the QObject for an InProcess (plugin-based) service. |
391 | */ |
392 | QObject *QServiceManager::loadInProcessService(const QServiceInterfaceDescriptor& descriptor, |
393 | const QString &serviceFilePath) const |
394 | { |
395 | QScopedPointer<QPluginLoader> loader(new QPluginLoader(serviceFilePath)); |
396 | QObject *obj = 0; |
397 | |
398 | // pluginIFace is same for all service instances of the same plugin |
399 | // calling loader->unload deletes pluginIFace automatically if no other |
400 | // service instance is around |
401 | QServicePluginInterface *pluginIFace = qobject_cast<QServicePluginInterface *>(object: loader->instance()); |
402 | if (pluginIFace) { |
403 | //check initialization first as the service may be a pre-registered one |
404 | bool doLoading = true; |
405 | QString serviceInitialized = descriptor.customAttribute(which: QLatin1String(SERVICE_INITIALIZED_ATTR)); |
406 | if (!serviceInitialized.isEmpty() && (serviceInitialized == QLatin1String("NO" ))) { |
407 | // open/create the semaphore using the service's name as identifier |
408 | QSystemSemaphore semaphore(descriptor.serviceName(), 1); |
409 | if (semaphore.error() != QSystemSemaphore::NoError) { |
410 | //try to create it |
411 | semaphore.setKey(key: descriptor.serviceName(), initialValue: 1, mode: QSystemSemaphore::Create); |
412 | } |
413 | if (semaphore.error() == QSystemSemaphore::NoError && semaphore.acquire()) { |
414 | pluginIFace->installService(); |
415 | DatabaseManager::DbScope scope = d->scope == QService::UserScope ? |
416 | DatabaseManager::UserOnlyScope : DatabaseManager::SystemScope; |
417 | d->dbManager->serviceInitialized(serviceName: descriptor.serviceName(), scope); |
418 | semaphore.release(); |
419 | } else { |
420 | qWarning() << semaphore.errorString(); |
421 | doLoading = false; |
422 | |
423 | } |
424 | } |
425 | if (doLoading) { |
426 | obj = pluginIFace->createInstance(descriptor); |
427 | if (obj) { |
428 | QServicePluginCleanup *cleanup = new QServicePluginCleanup(loader.take()); |
429 | QObject::connect(sender: obj, SIGNAL(destroyed()), receiver: cleanup, SLOT(deleteLater())); |
430 | } else { |
431 | qWarning() << "Cannot create object instance for " |
432 | << descriptor.interfaceName() << ":" |
433 | << serviceFilePath; |
434 | } |
435 | } |
436 | } else { |
437 | qWarning() << "QServiceManager::loadInterface():" << serviceFilePath << loader->errorString(); |
438 | } |
439 | return obj; |
440 | } |
441 | |
442 | /*! |
443 | Loads and returns the interface specified by \a descriptor. |
444 | |
445 | The caller takes ownership of the returned pointer. |
446 | |
447 | This function returns a null pointer if the requested service cannot be found. |
448 | |
449 | \sa loadInterfaceRequest() |
450 | */ |
451 | QObject* QServiceManager::loadInterface(const QServiceInterfaceDescriptor& descriptor) |
452 | { |
453 | qServiceLog() << "class" << "QServiceManager" |
454 | << "event" << "loadInterface" |
455 | << "interface" << descriptor.interfaceName() |
456 | << "service" << descriptor.serviceName(); |
457 | |
458 | d->setError(QServiceManager::NoError); |
459 | if (!descriptor.isValid()) { |
460 | d->setError(QServiceManager::InvalidServiceInterfaceDescriptor); |
461 | return 0; |
462 | } |
463 | |
464 | QObject *obj = 0; |
465 | int serviceType = descriptor.attribute(which: QServiceInterfaceDescriptor::ServiceType).toInt(); |
466 | QString location = descriptor.attribute(which: QServiceInterfaceDescriptor::Location).toString(); |
467 | |
468 | if (serviceType == QService::InterProcess) |
469 | { |
470 | return loadInterProcessService(descriptor, serviceLocation: location); |
471 | } |
472 | else |
473 | { |
474 | QString serviceFilePath = resolveLibraryPath(libNameOrPath: location); |
475 | if (serviceFilePath.isEmpty()) |
476 | { |
477 | d->setError(QServiceManager::InvalidServiceLocation); |
478 | return 0; |
479 | } |
480 | |
481 | obj = loadInProcessService(descriptor, serviceFilePath); |
482 | if (!obj) |
483 | d->setError(QServiceManager::PluginLoadingFailed); |
484 | } |
485 | return obj; |
486 | } |
487 | |
488 | /*! |
489 | \fn QServiceReply *loadInterfaceRequest(const QString &interfaceName) |
490 | Initiate a background request to load the interface specified by \a interfaceName, and |
491 | return a QServiceReply object to track the results of the request. |
492 | */ |
493 | |
494 | /*! |
495 | \fn QServiceReply *loadInterfaceRequest(const QServiceInterfaceDescriptor& descriptor) |
496 | Initiate a background request to load the interface specified by \a descriptor, and |
497 | return a QServiceReply object to track the results of the request. |
498 | */ |
499 | |
500 | |
501 | /*! |
502 | \fn QServiceReplyTyped<T> *loadLocalTypedInterfaceRequest(const QString& interfaceName) |
503 | Initiate a background request to load the interface specified by \a interfaceName, and |
504 | return a QServiceReplyTyped object to track the results of the request. |
505 | \code |
506 | // a member variable to track the reply |
507 | QServiceReply<SomeKnownService> *m_reply; |
508 | |
509 | // make the request |
510 | m_reply = *mgr->loadLocalTypedInterfaceRequest<SomeKnownService>(sksIfaceName); |
511 | connect(m_reply, SIGNAL(finished()), this, SLOT(handleServiceInfo)); |
512 | |
513 | // ...later, in the handler |
514 | SomeKnownService *svc = m_reply->proxyObject(); |
515 | \endcode |
516 | */ |
517 | |
518 | /*! |
519 | \fn QServiceReplyTyped<T> *loadLocalTypedInterfaceRequest(const QServiceInterfaceDescriptor& descriptor) |
520 | Initiate a background request to load the interface specified by \a interfaceName, and |
521 | return a QServiceReplyTyped object to track the results of the request. |
522 | */ |
523 | |
524 | /*! |
525 | \internal |
526 | Initiate a background request to load and return the interface specified by \a interfaceName, |
527 | and using the given \a reply object, which (if non-null) will be a typed sub-class object. |
528 | If it is null a default QServiceReply is constructed. |
529 | */ |
530 | QServiceReply *QServiceManager::loadInterfaceRequest(const QString &interfaceName) |
531 | { |
532 | QServiceReply *reply = new QServiceReply; |
533 | |
534 | if (!qgetenv(varName: "QT_NO_SFW_BACKGROUND_OPERATION" ).isEmpty()) |
535 | { |
536 | qWarning(msg: "Turning off sfw background operations as requested." ); |
537 | return 0; |
538 | } |
539 | |
540 | if (!d->ops) { |
541 | d->ops = QServiceOperations::instance(); |
542 | d->ops->engage(); |
543 | } |
544 | |
545 | reply->setRequest(interfaceName); |
546 | |
547 | QServiceRequest req(interfaceName); |
548 | req.setReply(reply); |
549 | req.setScope(scope()); |
550 | d->ops->initiateRequest(req); |
551 | |
552 | return reply; |
553 | } |
554 | |
555 | /*! |
556 | \internal |
557 | Initiate a background request to load and return the interface specified by \a descriptor, |
558 | and using the given \a reply object, which (if non-null) will be a typed sub-class object. |
559 | If it is null a default QServiceReply is constructed. |
560 | */ |
561 | QServiceReply *QServiceManager::loadInterfaceRequest(const QServiceInterfaceDescriptor &descriptor) |
562 | { |
563 | QServiceReply *reply = new QServiceReply(); |
564 | |
565 | if (!d->ops) { |
566 | d->ops = QServiceOperations::instance(); |
567 | d->ops->engage(); |
568 | } |
569 | |
570 | reply->setRequest(descriptor.interfaceName()); |
571 | |
572 | QServiceRequest req(descriptor); |
573 | req.setReply(reply); |
574 | req.setScope(scope()); |
575 | d->ops->initiateRequest(req); |
576 | |
577 | return reply; |
578 | } |
579 | |
580 | |
581 | /*! |
582 | If \a interfaceName is an out of process service this verifies the |
583 | interface is running. If the service is in process this function |
584 | always returns false. |
585 | |
586 | Use this function to verify the interface requested is running. This |
587 | is useful is you only want to call loadInterface when the service |
588 | is already running. This call does not guarantee that the service |
589 | will remain running, as such a race condition exists if the service |
590 | quits between this call being made and loadInterface being called. |
591 | |
592 | If the service can not be fount this returns false. |
593 | |
594 | \sa setInterfaceDefault(), interfaceDefault(), loadInterface() |
595 | */ |
596 | bool QServiceManager::isInterfaceRunning(const QString& interfaceName) |
597 | { |
598 | return isInterfaceRunning(descriptor: interfaceDefault(interfaceName)); |
599 | } |
600 | |
601 | /*! |
602 | If \a descriptor is an out of process service this verifies the |
603 | service is running. If the service is in process this function |
604 | always returns false. |
605 | |
606 | Use this function to verify the interface requested is running. This |
607 | is useful is you only want to call loadInterface when the service |
608 | is already running. This call does not guarantee that the service |
609 | will remain running, as such a race condition exists if the service |
610 | quits between this call being made and loadInterface being called. |
611 | |
612 | If the service can not be found this returns false. Error is set |
613 | if an error occurs. |
614 | |
615 | */ |
616 | bool QServiceManager::isInterfaceRunning(const QServiceInterfaceDescriptor& descriptor) |
617 | { |
618 | d->setError(NoError); |
619 | if (!descriptor.isValid()) { |
620 | d->setError(InvalidServiceInterfaceDescriptor); |
621 | return false; |
622 | } |
623 | |
624 | const QString location = descriptor.attribute(which: QServiceInterfaceDescriptor::Location).toString(); |
625 | const bool isInterProcess = (descriptor.attribute(which: QServiceInterfaceDescriptor::ServiceType).toInt() |
626 | == QService::InterProcess); |
627 | if (isInterProcess) { |
628 | //ipc service |
629 | const int majorversion = descriptor.majorVersion(); |
630 | const int minorversion = descriptor.minorVersion(); |
631 | QString version = QString::number(majorversion) + QLatin1String("." ) + QString::number(minorversion); |
632 | |
633 | QRemoteServiceRegister::Entry serviceEntry; |
634 | serviceEntry.d->iface = descriptor.interfaceName(); |
635 | serviceEntry.d->service = descriptor.serviceName(); |
636 | serviceEntry.d->ifaceVersion = version; |
637 | |
638 | return QRemoteServiceRegisterPrivate::isServiceRunning(serviceEntry, location); |
639 | } |
640 | |
641 | return false; |
642 | } |
643 | |
644 | |
645 | |
646 | /*! |
647 | \fn T* QServiceManager::loadLocalTypedInterface(const QString& interfaceName) |
648 | |
649 | Loads the service object implementing \a interfaceName, |
650 | as provided by the default service for this interface. |
651 | The template class must be derived from QObject. |
652 | |
653 | If \a interfaceName is not a known interface the returned pointer will be null. |
654 | |
655 | Note that using this function implies that service and client share |
656 | the implementation of T which means that service and client become tightly coupled. |
657 | This may cause issue during later updates as certain changes may require code changes |
658 | to the service and client. |
659 | |
660 | The caller takes ownership of the returned pointer. |
661 | |
662 | \sa setInterfaceDefault(), interfaceDefault() |
663 | */ |
664 | |
665 | |
666 | /*! |
667 | \fn T* QServiceManager::loadLocalTypedInterface(const QServiceInterfaceDescriptor& serviceDescriptor) |
668 | |
669 | Loads the service object identified by \a serviceDescriptor. |
670 | The template class must be derived from QObject. |
671 | |
672 | If the \a serviceDescriptor is not valid the returned pointer will be null. |
673 | |
674 | Note that using this function implies that service and client share |
675 | the implementation of T which means that service and client become tightly coupled. |
676 | This may cause issue during later updates as certain changes may require code changes |
677 | to the service and client. |
678 | |
679 | The caller takes ownership of the returned pointer. |
680 | |
681 | */ |
682 | |
683 | /*! |
684 | Registers the service defined by the XML file at \a xmlFilePath. |
685 | Returns true if the registration succeeded, and false otherwise. |
686 | |
687 | If a previously unkown interface is added the newly registered service automatically |
688 | becomes the new default service provider for the new interface. |
689 | |
690 | A service plugin cannot be added if another service is already registered |
691 | with the same plugin file path. A service plugin also cannot be added if |
692 | the service is already registered and implements any of the same interface |
693 | versions that the new plugin implements. |
694 | |
695 | \sa removeService(), setInterfaceDefault() |
696 | */ |
697 | bool QServiceManager::addService(const QString& xmlFilePath) |
698 | { |
699 | QFile *f = new QFile(xmlFilePath); |
700 | bool b = addService(xmlDevice: f); |
701 | delete f; |
702 | return b; |
703 | } |
704 | |
705 | /*! |
706 | Registers the service defined by the XML data from the given \a device. |
707 | Returns true if the registration succeeded, and false otherwise. If a |
708 | previously unkown interface is added the newly registered service |
709 | automatically becomes the new default service provider for the new |
710 | interface. |
711 | |
712 | Registering a service also causes QServicePluginInterface::installService() |
713 | to be called on the service. If the service plugin is not accessible |
714 | (e.g. if the plugin file is not found) and \c installService() cannot |
715 | be invoked on the service, the registration fails and this method returns |
716 | false. |
717 | |
718 | A service plugin cannot be added if another service is already registered |
719 | with the same plugin file path. A service plugin also cannot be added if |
720 | the service is already registered and implements any of the same interface |
721 | versions that the new plugin implements. |
722 | |
723 | Services are always added based on the \l scope() of the current |
724 | service manager instance. |
725 | |
726 | \sa removeService(), setInterfaceDefault() |
727 | */ |
728 | bool QServiceManager::addService(QIODevice *device) |
729 | { |
730 | d->setError(QServiceManager::NoError); |
731 | ServiceMetaData parser(device); |
732 | if (!parser.extractMetadata()) { |
733 | d->setError(QServiceManager::InvalidServiceXml); |
734 | return false; |
735 | } |
736 | const ServiceMetaDataResults data = parser.parseResults(); |
737 | |
738 | DatabaseManager::DbScope scope = d->scope == QService::UserScope ? |
739 | DatabaseManager::UserOnlyScope : DatabaseManager::SystemScope; |
740 | ServiceMetaDataResults results = parser.parseResults(); |
741 | |
742 | bool result = d->dbManager->registerService(service&: results, scope); |
743 | |
744 | if (results.type == QService::InterProcess) |
745 | return result; |
746 | |
747 | //test the new plug-in |
748 | if (result) { |
749 | QPluginLoader *loader = new QPluginLoader(resolveLibraryPath(libNameOrPath: data.location)); |
750 | QServicePluginInterface *pluginIFace = qobject_cast<QServicePluginInterface *>(object: loader->instance()); |
751 | if (pluginIFace) { |
752 | pluginIFace->installService(); |
753 | } else { |
754 | d->setError(QServiceManager::PluginLoadingFailed); |
755 | result = false; |
756 | qWarning() << "QServiceManager::addService()" << data.location << "->" |
757 | << resolveLibraryPath(libNameOrPath: data.location) << ":" |
758 | << loader->errorString() << " - Aborting registration" ; |
759 | d->dbManager->unregisterService(serviceName: data.name, scope); |
760 | } |
761 | //loader->unload(); |
762 | delete loader; |
763 | } else { |
764 | d->setError(); |
765 | } |
766 | |
767 | return result; |
768 | } |
769 | |
770 | /*! |
771 | Unregisters the service specified by \a serviceName. |
772 | |
773 | Returns true if the unregistration succeeded, and false otherwise. |
774 | |
775 | If a default service implementation is removed and there are other implementations |
776 | for the same interface, the service manager chooses the implementation with the |
777 | highest version number as the new default. If there is more than one serivce |
778 | with the same version number, the service manager makes a random choice with |
779 | regards to the new default implementation. If this is |
780 | not the desired behaviour the default selection should be updated |
781 | via setInterfaceDefault(). |
782 | |
783 | Services are always removed based on the \l scope() of the current |
784 | service manager instance. |
785 | |
786 | \sa addService() |
787 | */ |
788 | bool QServiceManager::removeService(const QString& serviceName) |
789 | { |
790 | d->setError(QServiceManager::NoError); |
791 | if (serviceName.isEmpty()) { |
792 | d->setError(QServiceManager::ComponentNotFound); |
793 | return false; |
794 | } |
795 | |
796 | // Call QServicePluginInterface::uninstallService() on all plugins that |
797 | // match this service |
798 | |
799 | QSet<QString> pluginPathsSet; |
800 | QList<QServiceInterfaceDescriptor> descriptors = findInterfaces(serviceName); |
801 | for (int i=0; i<descriptors.count(); i++) { |
802 | const QString loc = descriptors[i].attribute(which: QServiceInterfaceDescriptor::Location).toString(); |
803 | const int type = descriptors[i].attribute(which: QServiceInterfaceDescriptor::ServiceType).toInt(); |
804 | //exclude ipc services |
805 | if (type <= QService::Plugin) |
806 | pluginPathsSet << loc; |
807 | } |
808 | |
809 | QList<QString> pluginPaths = pluginPathsSet.toList(); |
810 | for (int i=0; i<pluginPaths.count(); i++) { |
811 | QPluginLoader *loader = new QPluginLoader(resolveLibraryPath(libNameOrPath: pluginPaths[i])); |
812 | QServicePluginInterface *pluginIFace = qobject_cast<QServicePluginInterface *>(object: loader->instance()); |
813 | if (pluginIFace) |
814 | pluginIFace->uninstallService(); |
815 | else |
816 | qWarning() << "QServiceManager: unable to invoke uninstallService() on removed service" ; |
817 | //loader->unload(); |
818 | delete loader; |
819 | } |
820 | |
821 | if (!d->dbManager->unregisterService(serviceName, scope: d->scope == QService::UserScope ? |
822 | DatabaseManager::UserOnlyScope : DatabaseManager::SystemScope)) { |
823 | d->setError(); |
824 | return false; |
825 | } |
826 | return true; |
827 | } |
828 | |
829 | /*! |
830 | Sets the default interface implementation for \a interfaceName to the |
831 | matching interface implementation provided by \a service. |
832 | |
833 | If \a service provides more than one interface implementation for |
834 | \a interfaceName, the newest version of the interface is set as the |
835 | default. |
836 | |
837 | Returns true if the operation succeeded, and false otherwise. |
838 | |
839 | \b {Note:} When in system scope, the \a service must be a system-wide |
840 | service rather than a user-specific service; otherwise, this will fail. |
841 | */ |
842 | bool QServiceManager::setInterfaceDefault(const QString &service, const QString &interfaceName) |
843 | { |
844 | d->setError(QServiceManager::NoError); |
845 | if (service.isEmpty() || interfaceName.isEmpty()) { |
846 | d->setError(QServiceManager::ComponentNotFound); |
847 | return false; |
848 | } |
849 | DatabaseManager::DbScope scope = d->scope == QService::SystemScope ? |
850 | DatabaseManager::SystemScope : DatabaseManager::UserScope; |
851 | if (!d->dbManager->setInterfaceDefault(serviceName: service, interfaceName, scope)) { |
852 | d->setError(); |
853 | return false; |
854 | } |
855 | return true; |
856 | } |
857 | |
858 | /*! |
859 | \overload |
860 | |
861 | Sets the interface implementation specified by \a descriptor to be the |
862 | default implementation for the particular interface specified in the |
863 | descriptor. |
864 | |
865 | Returns true if the operation succeeded, and false otherwise. |
866 | |
867 | \b {Note:} When in system scope, the \a descriptor must refer to a |
868 | system-wide service rather than a user-specific service; otherwise, this |
869 | will fail. |
870 | */ |
871 | bool QServiceManager::setInterfaceDefault(const QServiceInterfaceDescriptor& descriptor) |
872 | { |
873 | d->setError(QServiceManager::NoError); |
874 | DatabaseManager::DbScope scope = d->scope == QService::SystemScope ? |
875 | DatabaseManager::SystemScope : DatabaseManager::UserScope; |
876 | if (!d->dbManager->setInterfaceDefault(serviceInterface: descriptor, scope)) { |
877 | d->setError(); |
878 | return false; |
879 | } |
880 | return true; |
881 | } |
882 | |
883 | /*! |
884 | Returns the default interface implementation for the given \a interfaceName. |
885 | */ |
886 | QServiceInterfaceDescriptor QServiceManager::interfaceDefault(const QString& interfaceName) const |
887 | { |
888 | qDebug() << "QServiceManager::interfaceDefault" << interfaceName; |
889 | d->setError(QServiceManager::NoError); |
890 | DatabaseManager::DbScope scope = d->scope == QService::SystemScope ? |
891 | DatabaseManager::SystemScope : DatabaseManager::UserScope; |
892 | QServiceInterfaceDescriptor info = d->dbManager->interfaceDefault(interfaceName, scope); |
893 | if (d->dbManager->lastError().code() != DBError::NoError) { |
894 | d->setError(); |
895 | qDebug() << "error" << d->dbManager->lastError().text(); |
896 | return QServiceInterfaceDescriptor(); |
897 | } |
898 | return info; |
899 | } |
900 | |
901 | /*! |
902 | Returns the type of error that last occurred. |
903 | */ |
904 | QServiceManager::Error QServiceManager::error() const |
905 | { |
906 | return d->error; |
907 | } |
908 | |
909 | /*! |
910 | \internal |
911 | */ |
912 | void QServiceManager::connectNotify(const QMetaMethod &signal) |
913 | { |
914 | static const QMetaMethod serviceAddedSignal = QMetaMethod::fromSignal(signal: &QServiceManager::serviceAdded); |
915 | static const QMetaMethod serviceRemovedSignal = QMetaMethod::fromSignal(signal: &QServiceManager::serviceRemoved); |
916 | if (signal == serviceAddedSignal |
917 | || signal == serviceRemovedSignal) { |
918 | if (d->scope != QService::SystemScope) |
919 | d->dbManager->setChangeNotificationsEnabled(scope: DatabaseManager::UserScope, enabled: true); |
920 | d->dbManager->setChangeNotificationsEnabled(scope: DatabaseManager::SystemScope, enabled: true); |
921 | } |
922 | } |
923 | |
924 | /*! |
925 | \internal |
926 | */ |
927 | void QServiceManager::disconnectNotify(const QMetaMethod &signal) |
928 | { |
929 | static const QMetaMethod serviceAddedSignal = QMetaMethod::fromSignal(signal: &QServiceManager::serviceAdded); |
930 | static const QMetaMethod serviceRemovedSignal = QMetaMethod::fromSignal(signal: &QServiceManager::serviceRemoved); |
931 | if (signal == serviceAddedSignal |
932 | || signal == serviceRemovedSignal) { |
933 | if (!isSignalConnected(signal: serviceAddedSignal) |
934 | && !isSignalConnected(signal: serviceRemovedSignal)) { |
935 | if (d->scope != QService::SystemScope) |
936 | d->dbManager->setChangeNotificationsEnabled(scope: DatabaseManager::UserScope, enabled: false); |
937 | d->dbManager->setChangeNotificationsEnabled(scope: DatabaseManager::SystemScope, enabled: false); |
938 | } |
939 | } |
940 | } |
941 | |
942 | bool QServiceManager::event(QEvent *e) |
943 | { |
944 | if (e->type() == QEvent::ThreadChange) { |
945 | qWarning() << "QServiceManager CANNOT BE MOVED THREADS!" ; |
946 | } |
947 | |
948 | return QObject::event(event: e); |
949 | } |
950 | |
951 | #include "moc_qservicemanager.cpp" |
952 | #include "qservicemanager.moc" |
953 | QT_END_NAMESPACE |
954 | |
955 | |