| 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 | |