annotate plugin/PluginScan.cpp @ 1427:622d193a00dc

Rework canonicalisation so as to avoid theoretical possibility of integer overflow
author Chris Cannam
date Mon, 11 Dec 2017 09:28:40 +0000
parents 0bbf91b05164
children 48e9f538e6e9
rev   line source
Chris@1178 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1178 2
Chris@1178 3 /*
Chris@1178 4 Sonic Visualiser
Chris@1178 5 An audio file viewer and annotation editor.
Chris@1178 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1178 7
Chris@1178 8 This program is free software; you can redistribute it and/or
Chris@1178 9 modify it under the terms of the GNU General Public License as
Chris@1178 10 published by the Free Software Foundation; either version 2 of the
Chris@1178 11 License, or (at your option) any later version. See the file
Chris@1178 12 COPYING included with this distribution for more information.
Chris@1178 13 */
Chris@1178 14
Chris@1178 15 #include "PluginScan.h"
Chris@1178 16
Chris@1178 17 #include "base/Debug.h"
Chris@1246 18 #include "base/Preferences.h"
Chris@1241 19 #include "base/HelperExecPath.h"
Chris@1178 20
Chris@1249 21 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1180 22 #include "checker/knownplugins.h"
Chris@1249 23 #else
Chris@1249 24 class KnownPlugins {};
Chris@1249 25 #endif
Chris@1180 26
Chris@1178 27 #include <QMutex>
Chris@1181 28 #include <QCoreApplication>
Chris@1178 29
Chris@1178 30 using std::string;
Chris@1178 31
Chris@1249 32 class PluginScan::Logger
Chris@1249 33 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 34 : public PluginCandidates::LogCallback
Chris@1249 35 #endif
Chris@1180 36 {
Chris@1180 37 protected:
Chris@1180 38 void log(std::string message) {
Chris@1264 39 SVDEBUG << "PluginScan: " << message << endl;
Chris@1180 40 }
Chris@1180 41 };
Chris@1180 42
Chris@1180 43 PluginScan *PluginScan::getInstance()
Chris@1180 44 {
Chris@1178 45 static QMutex mutex;
Chris@1178 46 static PluginScan *m_instance = 0;
Chris@1178 47 mutex.lock();
Chris@1178 48 if (!m_instance) m_instance = new PluginScan();
Chris@1178 49 mutex.unlock();
Chris@1178 50 return m_instance;
Chris@1178 51 }
Chris@1178 52
Chris@1246 53 PluginScan::PluginScan() : m_succeeded(false), m_logger(new Logger) {
Chris@1178 54 }
Chris@1178 55
Chris@1178 56 PluginScan::~PluginScan() {
Chris@1246 57 QMutexLocker locker(&m_mutex);
Chris@1241 58 clear();
Chris@1180 59 delete m_logger;
Chris@1396 60 SVDEBUG << "PluginScan::~PluginScan completed" << endl;
Chris@1178 61 }
Chris@1178 62
Chris@1178 63 void
Chris@1241 64 PluginScan::scan()
Chris@1178 65 {
Chris@1249 66 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 67
Chris@1246 68 QMutexLocker locker(&m_mutex);
Chris@1246 69
Chris@1246 70 bool inProcess = Preferences::getInstance()->getRunPluginsInProcess();
Chris@1246 71
Chris@1246 72 HelperExecPath hep(inProcess ?
Chris@1246 73 HelperExecPath::NativeArchitectureOnly :
Chris@1246 74 HelperExecPath::AllInstalled);
Chris@1246 75
Chris@1352 76 QString helperName("vamp-plugin-load-checker");
Chris@1246 77 auto helpers = hep.getHelperExecutables(helperName);
Chris@1241 78
Chris@1241 79 clear();
Chris@1241 80
Chris@1246 81 for (auto p: helpers) {
Chris@1247 82 SVDEBUG << "NOTE: PluginScan: Found helper: " << p.executable << endl;
Chris@1246 83 }
Chris@1246 84
Chris@1246 85 if (helpers.empty()) {
Chris@1247 86 SVDEBUG << "NOTE: No plugin checker helpers found in installation;"
Chris@1246 87 << " found none of the following:" << endl;
Chris@1246 88 for (auto d: hep.getHelperCandidatePaths(helperName)) {
Chris@1247 89 SVDEBUG << "NOTE: " << d << endl;
Chris@1246 90 }
Chris@1246 91 }
Chris@1246 92
Chris@1246 93 for (auto p: helpers) {
Chris@1241 94 try {
Chris@1246 95 KnownPlugins *kp = new KnownPlugins
Chris@1246 96 (p.executable.toStdString(), m_logger);
Chris@1246 97 if (m_kp.find(p.tag) != m_kp.end()) {
Chris@1247 98 SVDEBUG << "WARNING: PluginScan::scan: Duplicate tag " << p.tag
Chris@1246 99 << " for helpers" << endl;
Chris@1246 100 continue;
Chris@1246 101 }
Chris@1246 102 m_kp[p.tag] = kp;
Chris@1241 103 m_succeeded = true;
Chris@1241 104 } catch (const std::exception &e) {
Chris@1247 105 SVDEBUG << "ERROR: PluginScan::scan: " << e.what()
Chris@1246 106 << " (with helper path = " << p.executable << ")" << endl;
Chris@1241 107 }
Chris@1241 108 }
Chris@1249 109
Chris@1396 110 SVDEBUG << "PluginScan::scan complete" << endl;
Chris@1249 111 #endif
Chris@1241 112 }
Chris@1241 113
Chris@1246 114 bool
Chris@1246 115 PluginScan::scanSucceeded() const
Chris@1246 116 {
Chris@1246 117 QMutexLocker locker(&m_mutex);
Chris@1246 118 return m_succeeded;
Chris@1246 119 }
Chris@1246 120
Chris@1241 121 void
Chris@1241 122 PluginScan::clear()
Chris@1241 123 {
Chris@1246 124 for (auto &p: m_kp) {
Chris@1246 125 delete p.second;
Chris@1246 126 }
Chris@1241 127 m_kp.clear();
Chris@1179 128 m_succeeded = false;
Chris@1179 129 }
Chris@1179 130
Chris@1246 131 QList<PluginScan::Candidate>
Chris@1278 132 PluginScan::getCandidateLibrariesFor(PluginType
Chris@1278 133 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1278 134 type
Chris@1278 135 #endif
Chris@1278 136 ) const
Chris@1179 137 {
Chris@1249 138 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 139
Chris@1246 140 QMutexLocker locker(&m_mutex);
Chris@1246 141
Chris@1180 142 KnownPlugins::PluginType kpt;
Chris@1180 143 switch (type) {
Chris@1180 144 case VampPlugin: kpt = KnownPlugins::VampPlugin; break;
Chris@1180 145 case LADSPAPlugin: kpt = KnownPlugins::LADSPAPlugin; break;
Chris@1180 146 case DSSIPlugin: kpt = KnownPlugins::DSSIPlugin; break;
Chris@1180 147 default: throw std::logic_error("Inconsistency in plugin type enums");
Chris@1180 148 }
Chris@1180 149
Chris@1246 150 QList<Candidate> candidates;
Chris@1245 151
Chris@1246 152 for (auto rec: m_kp) {
Chris@1245 153
Chris@1246 154 KnownPlugins *kp = rec.second;
Chris@1246 155
Chris@1241 156 auto c = kp->getCandidateLibrariesFor(kpt);
Chris@1245 157
Chris@1247 158 SVDEBUG << "PluginScan: helper \"" << kp->getHelperExecutableName()
Chris@1247 159 << "\" likes " << c.size() << " libraries of type "
Chris@1247 160 << kp->getTagFor(kpt) << endl;
Chris@1245 161
Chris@1246 162 for (auto s: c) {
Chris@1246 163 candidates.push_back({ s.c_str(), rec.first });
Chris@1246 164 }
Chris@1245 165
Chris@1245 166 if (type != VampPlugin) {
Chris@1245 167 // We are only interested in querying multiple helpers
Chris@1245 168 // when dealing with Vamp plugins, for which we can use
Chris@1245 169 // external servers and so in some cases can support
Chris@1245 170 // additional architectures. Other plugin formats are
Chris@1245 171 // loaded directly and so must match the host, which is
Chris@1245 172 // what the first helper is supposed to handle -- so
Chris@1245 173 // break after the first one if not querying Vamp
Chris@1245 174 break;
Chris@1245 175 }
Chris@1241 176 }
Chris@1245 177
Chris@1179 178 return candidates;
Chris@1249 179
Chris@1249 180 #else
Chris@1249 181 return {};
Chris@1249 182 #endif
Chris@1178 183 }
Chris@1178 184
Chris@1178 185 QString
Chris@1178 186 PluginScan::getStartupFailureReport() const
Chris@1178 187 {
Chris@1249 188 #ifdef HAVE_PLUGIN_CHECKER_HELPER
Chris@1249 189
Chris@1246 190 QMutexLocker locker(&m_mutex);
Chris@1246 191
Chris@1179 192 if (!m_succeeded) {
Chris@1179 193 return QObject::tr("<b>Failed to scan for plugins</b>"
Chris@1181 194 "<p>Failed to scan for plugins at startup. Possibly "
Chris@1352 195 "the plugin checker program was not correctly "
Chris@1181 196 "installed alongside %1?</p>")
Chris@1181 197 .arg(QCoreApplication::applicationName());
Chris@1179 198 }
Chris@1241 199 if (m_kp.empty()) {
Chris@1179 200 return QObject::tr("<b>Did not scan for plugins</b>"
Chris@1179 201 "<p>Apparently no scan for plugins was attempted "
Chris@1179 202 "(internal error?)</p>");
Chris@1179 203 }
Chris@1179 204
Chris@1241 205 QString report;
Chris@1241 206 for (auto kp: m_kp) {
Chris@1246 207 report += QString::fromStdString(kp.second->getFailureReport());
Chris@1241 208 }
Chris@1179 209 if (report == "") {
Chris@1241 210 return report;
Chris@1179 211 }
Chris@1179 212
Chris@1179 213 return QObject::tr("<b>Failed to load plugins</b>"
Chris@1179 214 "<p>Failed to load one or more plugin libraries:</p>")
Chris@1241 215 + report
Chris@1181 216 + QObject::tr("<p>These plugins may be incompatible with the system, "
Chris@1181 217 "and will be ignored during this run of %1.</p>")
Chris@1181 218 .arg(QCoreApplication::applicationName());
Chris@1249 219
Chris@1249 220 #else
Chris@1249 221 return "";
Chris@1249 222 #endif
Chris@1178 223 }
Chris@1178 224