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