From 8cf47d1f778f756309e8e7e0ccd40d6fd3164d57 Mon Sep 17 00:00:00 2001 From: Simo Piiroinen Date: Wed, 18 Apr 2018 21:45:07 +0300 Subject: [PATCH] [sensormanager] Add plugin availability config and D-Bus queries. JB#41369 Allow disabling / enabling sensors plugins via config entries like: [available] proximitysensor=True magnetometersensor=False lidsensor=Feature_CoverSensor orientationsensor=Feature_GyroSensor|Feature_AccelerationSensor Where: - "True" means that loading of the sensor plugin is made available via D-Bus interface and can be loaded. - "False" means the plugin will not be loaded and sensor is not made available via D-Bus - "Feature_*" means that sensor availability is checked from hw settings. If sensorfwd is compiled without ssu-sysinfo support these will be treated similarly to "True". - "" (or plugin that does not have config entry) is taken as "True", but a warning is logged in case of sensor plugins. If a plugin that is enabled in configuration fails to load, it is marked as not available until sensorfwd restart. Add new D-Bus method calls: - availablePlugins() lists all available plugins - availableSensorPlugins() lists available sensor plugins (which, when loaded, make new sensor objects and interfaces available) - pluginAvailable(name) can be used to check whether a named plugin is installed and available Package default sensor availability configuration file that disables all sensors except those that can be evaluated based on the hw settings configuration. Add example of device specific configuration file - these should be installed from hw adaptation packages and can override the defaults. Signed-off-by: Simo Piiroinen --- config/20-sensors-default.conf | 36 ++++ config/60-sensor-selection-oeverrides.conf | 25 +++ core/core.pro | 5 + core/loader.cpp | 234 ++++++++++++++------- core/loader.h | 38 +++- core/sensormanager.cpp | 18 ++ core/sensormanager.h | 21 ++ core/sensormanager_a.cpp | 15 ++ core/sensormanager_a.h | 21 ++ rpm/sensorfw-qt5.spec | 2 + sensorfw.pro | 3 +- 11 files changed, 340 insertions(+), 78 deletions(-) create mode 100644 config/20-sensors-default.conf create mode 100644 config/60-sensor-selection-oeverrides.conf diff --git a/config/20-sensors-default.conf b/config/20-sensors-default.conf new file mode 100644 index 00000000..201f816c --- /dev/null +++ b/config/20-sensors-default.conf @@ -0,0 +1,36 @@ +[available] +; Availability of some sensors can be defined in hw-settings configuration +; files. By enabling ssu-sysinfo usage, sensorfwd configuration can be +; written so that it just refers to hw-settings config. If ssu-sysinfo +; is not used, these will default to "True". +; +; In case of virtual sensors can be implemented on top of multiple real +; sensors, multiple '|' separated features can be given and the sensor +; is enabled if at least one of those is set in hw-settings. + +accelerometersensor=Feature_AccelerationSensor +alssensor=Feature_LightSensor +compasssensor=Feature_CompassSensor +gyroscopesensor=Feature_GyroSensor +orientationsensor=Feature_GyroSensor|Feature_AccelerationSensor +proximitysensor=Feature_ProximitySensor + +; In theory having Feature_CoverSensor == have lidsensor. However +; in practice only mce is expected to track lidsensor and atm it +; does it via suspend proofed evdev inputs rather than sensorfwd. +; So, even if the lidsensor adaptors provided by sensorfwd would +; work, they might interfere with the only user of the sensor. +; lidsensor=Feature_CoverSensor +lidsensor=False + +; To avoid revisiting config files for all old ports, the defaults +; added sensors should be set "False" by default here, and to "True" +; in device specific override config as appropriate. + +humiditysensor=False +magnetometersensor=False +pressuresensor=False +rotationsensor=False +stepcountersensor=False +tapsensor=False +temperaturesensor=False diff --git a/config/60-sensor-selection-oeverrides.conf b/config/60-sensor-selection-oeverrides.conf new file mode 100644 index 00000000..fd8adb9f --- /dev/null +++ b/config/60-sensor-selection-oeverrides.conf @@ -0,0 +1,25 @@ +[available] + +; Sensors that are disabled by default. +; -> Enable as appropriate + +;humiditysensor=True +;magnetometersensor=True +;pressuresensor=True +;rotationsensor=True +;stepcountersensor=True +;tapsensor=True +;temperaturesensor=True + +; Sensors that should/can be enabled/disabled based on +; hw settings config - or are enabled if sensorfwd is +; built without ssu-sysinfo support. +; -> Override as appropriate. + +;accelerometersensor=False +;alssensor=False +;compasssensor=False +;gyroscopesensor=False +;lidsensor=False +;orientationsensor=False +;proximitysensor=False diff --git a/core/core.pro b/core/core.pro index ff4062f0..b5b7eca6 100644 --- a/core/core.pro +++ b/core/core.pro @@ -80,6 +80,11 @@ mce { DEFINES += SENSORFW_MCE_WATCHER } +contains(CONFIG,ssusysinfo) { + PKGCONFIG += ssu-sysinfo + QMAKE_CXXFLAGS += -DUSE_SSUSYSINFO +} + lunaservice { SOURCES += lsclient.cpp HEADERS += lsclient.h diff --git a/core/loader.cpp b/core/loader.cpp index 060f1f93..6b45c2a9 100644 --- a/core/loader.cpp +++ b/core/loader.cpp @@ -31,13 +31,19 @@ #include #include #include +#include #include #include "logging.h" #include "config.h" +#ifdef USE_SSUSYSINFO +# include +#endif + Loader::Loader() { + scanAvailablePlugins(); } Loader& Loader::instance() @@ -47,99 +53,183 @@ Loader& Loader::instance() return the_loader; } -bool Loader::loadPluginFile(const QString& name, QString *errorString, QStringList& newPluginNames, QList& newPlugins) const -{ - sensordLogT() << "Loading plugin:" << name; +#define PLUGIN_PREFIX_ENV "SENSORFW_LIBRARY_PATH" +#define PLUGIN_DIRECTORY "/usr/lib/sensord-qt5" +#define PLUGIN_PREFIX "lib" +#define PLUGIN_SUFFIX "-qt5.so" +#define SENSOR_SUFFIX "sensor" -#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) - QString pluginPath = QString::fromLatin1("/usr/lib/sensord/lib%1.so").arg(name); -#else - QString pluginPath; - QByteArray env = qgetenv("SENSORFW_LIBRARY_PATH"); - if (env.isEmpty()) - pluginPath = QString::fromLatin1("/usr/lib/sensord-qt5/lib%1-qt5.so").arg(name); - else - pluginPath = QString::fromLatin1(env+"/usr/lib/sensord-qt5/lib%1-qt5.so").arg(name); +static QString getPluginDirectory() +{ + QByteArray env = qgetenv(PLUGIN_PREFIX_ENV); + return QString::fromUtf8(env + PLUGIN_DIRECTORY); +} -#endif +static QString getPluginPath(const QString &name) +{ + return QString("%1/" PLUGIN_PREFIX "%2" PLUGIN_SUFFIX).arg(getPluginDirectory()).arg(name); +} - QPluginLoader qpl(pluginPath); +bool Loader::loadPluginFile(const QString &name, QString &errorString, QStringList &stack) +{ + const QString resolvedName(resolveRealPluginName(name)); + QPluginLoader qpl(getPluginPath(resolvedName)); qpl.setLoadHints(QLibrary::ExportExternalSymbolsHint); - if (!qpl.load()) { - *errorString = qpl.errorString(); - sensordLogC() << "plugin loading error: " << *errorString; - return false; + QObject *object = 0; + PluginBase *plugin = 0; + sensordLogD() << "Loader loading plugin:" << resolvedName << "as:" << name << "from:" << qpl.fileName(); + bool loaded = false; + bool cyclic = stack.contains(resolvedName); + stack.prepend(resolvedName); + if (cyclic) { + errorString = "cyclic plugin dependency"; + sensordLogC() << "Plugin has cyclic dependency:" << resolvedName; + } else if (loadedPluginNames_.contains(resolvedName)) { + sensordLogD() << "Plugin is already loaded:" << resolvedName; + loaded = true; + } else if (!pluginAvailable(resolvedName)) { + errorString = "plugin not available"; + sensordLogW() << "Plugin not available:" << resolvedName; + } else if (!qpl.load()) { + errorString = qpl.errorString(); + sensordLogC() << "Plugin loading error:" << resolvedName << "-" << errorString; + } else if (!(object = qpl.instance())) { + errorString = "not able to instanciate"; + sensordLogC() << "Plugin loading error: " << resolvedName << "-" << errorString; + } else if (!(plugin = qobject_cast(object))) { + errorString = "not a Plugin type"; + sensordLogC() << "Plugin loading error: " << resolvedName << "-" << errorString; + } else { + loaded = true; + QStringList dependencies(plugin->Dependencies()); + sensordLogD() << resolvedName << "requires:" << dependencies; + foreach (const QString &dependency, dependencies) { + if (!(loaded = loadPluginFile(dependency, errorString, stack))) { + break; + } + } + if (loaded) { + plugin->Register(*this); + loadedPluginNames_.append(resolvedName); + plugin->Init(*this); + } } - - QObject* object = qpl.instance(); - if (!object) { - *errorString = "not able to instanciate"; - sensordLogC() << "plugin loading error: " << *errorString; - return false; + stack.removeOne(resolvedName); + if (!loaded) { + invalidatePlugin(resolvedName); } + return loaded; +} - PluginBase* plugin = qobject_cast(object); - if (!plugin) { - *errorString = "not a Plugin type"; - sensordLogC() << "plugin loading error: " << *errorString; - return false; +bool Loader::loadPlugin(const QString& name, QString *errorString) +{ + QString error; + QStringList stack; + bool loaded = loadPluginFile(name, error, stack); + if (!loaded && errorString) { + *errorString = error; } + return loaded; +} - // Add plugins to the front of the list so they are initialized in reverse order. This will guarantee that dependencies are initialized first for each plugin. - newPluginNames.prepend(name); - newPlugins.prepend(plugin); - - // Get dependencies - QStringList requiredPlugins(plugin->Dependencies()); - sensordLogT() << name << " requires: " << requiredPlugins; - - bool loaded = true; - for (int i = 0; i < requiredPlugins.size() && loaded; ++i) { - if (!(loadedPluginNames_.contains(requiredPlugins.at(i)) || - newPluginNames.contains(requiredPlugins.at(i)))) - { - sensordLogT() << requiredPlugins.at(i) << " is not yet loaded, trying to load."; - QString resolvedName = resolveRealPluginName(requiredPlugins.at(i)); - sensordLogT() << requiredPlugins.at(i) << " resolved as " << resolvedName << ". Loading"; - loaded = loadPluginFile(resolvedName, errorString, newPluginNames, newPlugins); +#ifdef USE_SSUSYSINFO +static ssusysinfo_t *ssusysinfo = 0; +#endif + +static bool evaluateAvailabilityValue(const QString &name, const QString &val) +{ + bool available = true; + if (val.startsWith("Feature_")) { +#ifdef USE_SSUSYSINFO + const QStringList features(val.split("|")); + bool allow = false; + bool deny = false; + foreach(const QString &feature, features) { + hw_feature_t id = ssusysinfo_hw_feature_from_name(feature.toUtf8().constData()); + if (id == Feature_Invalid ) { + sensordLogW() << "unknown hw feature:" << feature; + continue; + } + if( ssusysinfo_has_hw_feature(ssusysinfo, id) ) { + allow = true; + break; + } + deny = true; } + if( deny && !allow ) { + sensordLogD() << "plugin disabled in hw-config: " << name << "value" << val; + available = false; + } +#else + // When compiled without ssu-support, these are enabled by design + sensordLogD() << "sensor plugin enabled implicitly: " << name << "value" << val; +#endif + } else if (val == "False") { + sensordLogD() << "plugin disabled sensorfwd config: " << name << "value" << val; + available = false; + } else if (name.endsWith(SENSOR_SUFFIX) && val != "True") { + // Warn about implicitly enabled sensor plugins + sensordLogW() << "sensor plugin enabled implicitly: " << name << "value" << val; } - return loaded; + return available; } -bool Loader::loadPlugin(const QString& name, QString* errorString) +void Loader::scanAvailablePlugins() { - QString error; - bool loaded = false; - QStringList newPluginNames; - QList newPlugins; - - if (loadedPluginNames_.contains(name)) { - sensordLogD() << "Plugin already loaded."; - return true; +#ifdef USE_SSUSYSINFO + if (!ssusysinfo) { + ssusysinfo = ssusysinfo_create(); } +#endif + QStringList res; + QDir dir(getPluginDirectory()); + dir.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDot | QDir::NoDotDot); + const QString prefix(PLUGIN_PREFIX); + const QString suffix(PLUGIN_SUFFIX); + foreach (QString file, dir.entryList()) { + if (file.startsWith(prefix) && file.endsWith(suffix)) { + int beg = prefix.size(); + int end = file.size() - suffix.size(); + const QString name(file.mid(beg, end-beg)); + QString key = QString("available/%1").arg(name); + QString val = Config::configuration()->value(key).toString(); + if( evaluateAvailabilityValue(name, val) ) { + res.append(name); + } + } + } + availablePluginNames_ = res; +#ifdef USE_SSUSYSINFO + ssusysinfo_delete(ssusysinfo), ssusysinfo = 0; +#endif +} - if (loadPluginFile(name, &error, newPluginNames, newPlugins)) { +QStringList Loader::availablePlugins() const +{ + return availablePluginNames_; +} - // Register newly loaded plugins - foreach (PluginBase* base, newPlugins) { - base->Register(*this); +QStringList Loader::availableSensorPlugins() const +{ + QStringList res; + foreach(const QString &name, availablePluginNames_) { + if (name.endsWith(SENSOR_SUFFIX)) { + res.append(name); } - loadedPluginNames_.append(newPluginNames); - loaded = true; + } + return res; +} - // Init newly loaded plugins - foreach (PluginBase* base, newPlugins) { - base->Init(*this); - } +bool Loader::pluginAvailable(const QString &name) const +{ + return availablePluginNames_.contains(name); +} - } else { - if(errorString) - *errorString = error; - loaded = false; +void Loader::invalidatePlugin(const QString &name) +{ + if (availablePluginNames_.removeAll(name) > 0) { + sensordLogW() << "plugin marked invalid: " << name; } - - return loaded; } QString Loader::resolveRealPluginName(const QString& pluginName) const diff --git a/core/loader.h b/core/loader.h index 84231b1a..fc00f25b 100644 --- a/core/loader.h +++ b/core/loader.h @@ -50,8 +50,31 @@ class Loader * @param name plugin name. * @param errorMessage object to write error message if plugin loading * fails. If NULL then error message is not written. + * @return true on success, false on failure */ - bool loadPlugin(const QString& name, QString* errorMessage = 0); + bool loadPlugin(const QString &name, QString *errorMessage = 0); + + /** + * Test if a plugin is available for loading + * + * @param name plugin name + * @return true if plugin is available, false if not + */ + bool pluginAvailable(const QString &name) const; + + /** + * Get a list of plugins available for loading + * + * @return Array of plugin names + */ + QStringList availablePlugins() const; + + /** + * Get a list of sensor plugins available for loading + * + * @return Array of plugin names + */ + QStringList availableSensorPlugins() const; private: Loader(); @@ -63,10 +86,11 @@ class Loader * * @param name plugin to load. * @param errorString object to write error message if plugin loading fails. - * @param newPluginNames List of new loaded plugin names. - * @param newPlugin List of new loaded plugin objects. + * @param stack Pending plugin load stack for detecting circular dependencies. */ - bool loadPluginFile(const QString& name, QString *errorString, QStringList& newPluginNames, QList& newPlugins) const; + bool loadPluginFile(const QString &name, QString &errorString, QStringList &stack); + + void invalidatePlugin(const QString &name); /** * Resolve plugin name. @@ -74,9 +98,13 @@ class Loader * @param pluginName plugin name. * @return resolved plugin name. */ - QString resolveRealPluginName(const QString& pluginName) const; + QString resolveRealPluginName(const QString &pluginName) const; QStringList loadedPluginNames_; /**< list of loaded plugins */ + + QStringList availablePluginNames_; /**< list of loaded plugins */ + + void scanAvailablePlugins(); }; #endif diff --git a/core/sensormanager.cpp b/core/sensormanager.cpp index 4f6f26ff..329ffe30 100644 --- a/core/sensormanager.cpp +++ b/core/sensormanager.cpp @@ -333,6 +333,24 @@ bool SensorManager::loadPlugin(const QString& name) return result; } +QStringList SensorManager::availablePlugins() const +{ + Loader& l = Loader::instance(); + return l.availablePlugins(); +} + +bool SensorManager::pluginAvailable(const QString &name) const +{ + Loader& l = Loader::instance(); + return l.pluginAvailable(name); +} + +QStringList SensorManager::availableSensorPlugins() const +{ + Loader& l = Loader::instance(); + return l.availableSensorPlugins(); +} + int SensorManager::requestSensor(const QString& id) { sensordLogD() << "Requesting sensor:" << id; diff --git a/core/sensormanager.h b/core/sensormanager.h index 67f95ddb..04b30e4f 100644 --- a/core/sensormanager.h +++ b/core/sensormanager.h @@ -276,6 +276,27 @@ class SensorManager : public QObject */ bool loadPlugin(const QString& name); + /** + * Test if a plugin is available + * + * @return true if plugin exists, false otherwise + */ + bool pluginAvailable(const QString &name) const; + + /** + * List all available plugins. + * + * @return array of plugin names + */ + QStringList availablePlugins() const; + + /** + * List available sensor plugins. + * + * @return array of plugin names + */ + QStringList availableSensorPlugins() const; + /** * Request sensor. * diff --git a/core/sensormanager_a.cpp b/core/sensormanager_a.cpp index 826352b1..8729ee4e 100644 --- a/core/sensormanager_a.cpp +++ b/core/sensormanager_a.cpp @@ -54,6 +54,21 @@ bool SensorManagerAdaptor::loadPlugin(const QString& name) return sensorManager()->loadPlugin(name); } +QStringList SensorManagerAdaptor::availablePlugins() const +{ + return sensorManager()->availablePlugins(); +} + +bool SensorManagerAdaptor::pluginAvailable(const QString &name) const +{ + return sensorManager()->pluginAvailable(name); +} + +QStringList SensorManagerAdaptor::availableSensorPlugins() const +{ + return sensorManager()->availableSensorPlugins(); +} + int SensorManagerAdaptor::requestSensor(const QString &id, qint64 pid) { int session = sensorManager()->requestSensor(id); diff --git a/core/sensormanager_a.h b/core/sensormanager_a.h index bb9c602e..7f425421 100644 --- a/core/sensormanager_a.h +++ b/core/sensormanager_a.h @@ -87,6 +87,27 @@ public Q_SLOTS: */ bool loadPlugin(const QString& name); + /** + * Test if a plugin is available + * + * @return true if plugin exists, false otherwise + */ + bool pluginAvailable(const QString& name) const; + + /** + * List all available plugins. + * + * @return array of plugin names + */ + QStringList availablePlugins() const; + + /** + * List available sensor plugins. + * + * @return array of plugin names + */ + QStringList availableSensorPlugins() const; + /** * Request new sensor session to be created. * diff --git a/rpm/sensorfw-qt5.spec b/rpm/sensorfw-qt5.spec index 5e092521..f8fbc627 100644 --- a/rpm/sensorfw-qt5.spec +++ b/rpm/sensorfw-qt5.spec @@ -21,6 +21,7 @@ BuildRequires: pkgconfig(Qt5Network) BuildRequires: pkgconfig(Qt5Test) BuildRequires: pkgconfig(mlite5) BuildRequires: pkgconfig(libsystemd) +BuildRequires: pkgconfig(ssu-sysinfo) BuildRequires: doxygen BuildRequires: systemd BuildRequires: libudev-devel @@ -94,6 +95,7 @@ export LD_RUN_PATH=/usr/lib/sensord-qt5/ export QT_SELECT=5 %qmake5 \ + CONFIG+=ssusysinfo\ CONFIG+=mce make %{?_smp_mflags} diff --git a/sensorfw.pro b/sensorfw.pro index 2e8c4a6c..3581630a 100644 --- a/sensorfw.pro +++ b/sensorfw.pro @@ -104,7 +104,8 @@ equals(QT_MAJOR_VERSION, 5): { DBUSCONFIGFILES.path = /etc/dbus-1/system.d INSTALLS += DBUSCONFIGFILES - SENSORDCONFIGFILES.files = config/10-sensord-default.conf + SENSORDCONFIGFILES.files = config/10-sensord-default.conf + SENSORDCONFIGFILES.files += config/20-sensors-default.conf SENSORDCONFIGFILES.path = /etc/sensorfw/sensord.conf.d INSTALLS += SENSORDCONFIGFILES