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