1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2018 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qfactoryloader_p.h"
42
43#ifndef QT_NO_QOBJECT
44#include "qfactoryinterface.h"
45#include "qmap.h"
46#include <qdir.h>
47#include <qdebug.h>
48#include "qmutex.h"
49#include "qplugin.h"
50#include "qplugin_p.h"
51#include "qpluginloader.h"
52#include "private/qobject_p.h"
53#include "private/qcoreapplication_p.h"
54#include "qcbormap.h"
55#include "qcborvalue.h"
56#include "qjsondocument.h"
57#include "qjsonvalue.h"
58#include "qjsonobject.h"
59#include "qjsonarray.h"
60#include "private/qduplicatetracker_p.h"
61
62#include <qtcore_tracepoints_p.h>
63
64QT_BEGIN_NAMESPACE
65
66static inline int metaDataSignatureLength()
67{
68 return sizeof("QTMETADATA ") - 1;
69}
70
71static QJsonDocument jsonFromCborMetaData(const char *raw, qsizetype size, QString *errMsg)
72{
73 // extract the keys not stored in CBOR
74 int qt_metadataVersion = quint8(raw[0]);
75 int qt_version = qFromBigEndian<quint16>(raw + 1);
76 int qt_archRequirements = quint8(raw[3]);
77 if (Q_UNLIKELY(raw[-1] != '!' || qt_metadataVersion != 0)) {
78 *errMsg = QStringLiteral("Invalid metadata version");
79 return QJsonDocument();
80 }
81
82 raw += 4;
83 size -= 4;
84 QByteArray ba = QByteArray::fromRawData(raw, int(size));
85 QCborParserError err;
86 QCborValue metadata = QCborValue::fromCbor(ba, &err);
87
88 if (err.error != QCborError::NoError) {
89 *errMsg = QLatin1String("Metadata parsing error: ") + err.error.toString();
90 return QJsonDocument();
91 }
92
93 if (!metadata.isMap()) {
94 *errMsg = QStringLiteral("Unexpected metadata contents");
95 return QJsonDocument();
96 }
97
98 QJsonObject o;
99 o.insert(QLatin1String("version"), qt_version << 8);
100 o.insert(QLatin1String("debug"), bool(qt_archRequirements & 1));
101 o.insert(QLatin1String("archreq"), qt_archRequirements);
102
103 // convert the top-level map integer keys
104 for (auto it : metadata.toMap()) {
105 QString key;
106 if (it.first.isInteger()) {
107 switch (it.first.toInteger()) {
108#define CONVERT_TO_STRING(IntKey, StringKey, Description) \
109 case int(IntKey): key = QStringLiteral(StringKey); break;
110 QT_PLUGIN_FOREACH_METADATA(CONVERT_TO_STRING)
111#undef CONVERT_TO_STRING
112
113 case int(QtPluginMetaDataKeys::Requirements):
114 // special case: recreate the debug key
115 o.insert(QLatin1String("debug"), bool(it.second.toInteger() & 1));
116 key = QStringLiteral("archreq");
117 break;
118 }
119 } else {
120 key = it.first.toString();
121 }
122
123 if (!key.isEmpty())
124 o.insert(key, it.second.toJsonValue());
125 }
126 return QJsonDocument(o);
127}
128
129QT_WARNING_PUSH
130QT_WARNING_DISABLE_DEPRECATED
131QJsonDocument qJsonFromRawLibraryMetaData(const char *raw, qsizetype sectionSize, QString *errMsg)
132{
133 raw += metaDataSignatureLength();
134 sectionSize -= metaDataSignatureLength();
135
136 return jsonFromCborMetaData(raw, sectionSize, errMsg);
137}
138QT_WARNING_POP
139
140class QFactoryLoaderPrivate : public QObjectPrivate
141{
142 Q_DECLARE_PUBLIC(QFactoryLoader)
143public:
144 QFactoryLoaderPrivate() { }
145 QByteArray iid;
146#if QT_CONFIG(library)
147 ~QFactoryLoaderPrivate();
148 mutable QMutex mutex;
149 QList<QLibraryPrivate*> libraryList;
150 QMap<QString,QLibraryPrivate*> keyMap;
151 QString suffix;
152 Qt::CaseSensitivity cs;
153 QDuplicateTracker<QString> loadedPaths;
154#endif
155};
156
157#if QT_CONFIG(library)
158
159Q_GLOBAL_STATIC(QList<QFactoryLoader *>, qt_factory_loaders)
160
161Q_GLOBAL_STATIC(QRecursiveMutex, qt_factoryloader_mutex)
162
163QFactoryLoaderPrivate::~QFactoryLoaderPrivate()
164{
165 for (QLibraryPrivate *library : qAsConst(libraryList))
166 library->release();
167}
168
169void QFactoryLoader::update()
170{
171#ifdef QT_SHARED
172 Q_D(QFactoryLoader);
173 QStringList paths = QCoreApplication::libraryPaths();
174 for (int i = 0; i < paths.count(); ++i) {
175 const QString &pluginDir = paths.at(i);
176 // Already loaded, skip it...
177 if (d->loadedPaths.hasSeen(pluginDir))
178 continue;
179
180#ifdef Q_OS_ANDROID
181 QString path = pluginDir;
182#else
183 QString path = pluginDir + d->suffix;
184#endif
185
186 if (qt_debug_component())
187 qDebug() << "QFactoryLoader::QFactoryLoader() checking directory path" << path << "...";
188
189 if (!QDir(path).exists(QLatin1String(".")))
190 continue;
191
192 QStringList plugins = QDir(path).entryList(
193#if defined(Q_OS_WIN)
194 QStringList(QStringLiteral("*.dll")),
195#elif defined(Q_OS_ANDROID)
196 QStringList(QLatin1String("libplugins_%1_*.so").arg(d->suffix)),
197#endif
198 QDir::Files);
199 QLibraryPrivate *library = nullptr;
200
201 for (int j = 0; j < plugins.count(); ++j) {
202 QString fileName = QDir::cleanPath(path + QLatin1Char('/') + plugins.at(j));
203
204#ifdef Q_OS_MAC
205 const bool isDebugPlugin = fileName.endsWith(QLatin1String("_debug.dylib"));
206 const bool isDebugLibrary =
207 #ifdef QT_DEBUG
208 true;
209 #else
210 false;
211 #endif
212
213 // Skip mismatching plugins so that we don't end up loading both debug and release
214 // versions of the same Qt libraries (due to the plugin's dependencies).
215 if (isDebugPlugin != isDebugLibrary)
216 continue;
217#elif defined(Q_PROCESSOR_X86)
218 if (fileName.endsWith(QLatin1String(".avx2")) || fileName.endsWith(QLatin1String(".avx512"))) {
219 // ignore AVX2-optimized file, we'll do a bait-and-switch to it later
220 continue;
221 }
222#endif
223 if (qt_debug_component()) {
224 qDebug() << "QFactoryLoader::QFactoryLoader() looking at" << fileName;
225 }
226
227 Q_TRACE(QFactoryLoader_update, fileName);
228
229 library = QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath());
230 if (!library->isPlugin()) {
231 if (qt_debug_component()) {
232 qDebug() << library->errorString << Qt::endl
233 << " not a plugin";
234 }
235 library->release();
236 continue;
237 }
238
239 QStringList keys;
240 bool metaDataOk = false;
241
242 QString iid = library->metaData.value(QLatin1String("IID")).toString();
243 if (iid == QLatin1String(d->iid.constData(), d->iid.size())) {
244 QJsonObject object = library->metaData.value(QLatin1String("MetaData")).toObject();
245 metaDataOk = true;
246
247 QJsonArray k = object.value(QLatin1String("Keys")).toArray();
248 for (int i = 0; i < k.size(); ++i)
249 keys += d->cs ? k.at(i).toString() : k.at(i).toString().toLower();
250 }
251 if (qt_debug_component())
252 qDebug() << "Got keys from plugin meta data" << keys;
253
254
255 if (!metaDataOk) {
256 library->release();
257 continue;
258 }
259
260 int keyUsageCount = 0;
261 for (int k = 0; k < keys.count(); ++k) {
262 // first come first serve, unless the first
263 // library was built with a future Qt version,
264 // whereas the new one has a Qt version that fits
265 // better
266 const QString &key = keys.at(k);
267 QLibraryPrivate *previous = d->keyMap.value(key);
268 int prev_qt_version = 0;
269 if (previous) {
270 prev_qt_version = (int)previous->metaData.value(QLatin1String("version")).toDouble();
271 }
272 int qt_version = (int)library->metaData.value(QLatin1String("version")).toDouble();
273 if (!previous || (prev_qt_version > QT_VERSION && qt_version <= QT_VERSION)) {
274 d->keyMap[key] = library;
275 ++keyUsageCount;
276 }
277 }
278 if (keyUsageCount || keys.isEmpty()) {
279 library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload
280 QMutexLocker locker(&d->mutex);
281 d->libraryList += library;
282 } else {
283 library->release();
284 }
285 }
286 }
287#else
288 Q_D(QFactoryLoader);
289 if (qt_debug_component()) {
290 qDebug() << "QFactoryLoader::QFactoryLoader() ignoring" << d->iid
291 << "since plugins are disabled in static builds";
292 }
293#endif
294}
295
296QFactoryLoader::~QFactoryLoader()
297{
298 QMutexLocker locker(qt_factoryloader_mutex());
299 qt_factory_loaders()->removeAll(this);
300}
301
302#if defined(Q_OS_UNIX) && !defined (Q_OS_MAC)
303QLibraryPrivate *QFactoryLoader::library(const QString &key) const
304{
305 Q_D(const QFactoryLoader);
306 return d->keyMap.value(d->cs ? key : key.toLower());
307}
308#endif
309
310void QFactoryLoader::refreshAll()
311{
312 QMutexLocker locker(qt_factoryloader_mutex());
313 QList<QFactoryLoader *> *loaders = qt_factory_loaders();
314 for (QList<QFactoryLoader *>::const_iterator it = loaders->constBegin();
315 it != loaders->constEnd(); ++it) {
316 (*it)->update();
317 }
318}
319
320#endif // QT_CONFIG(library)
321
322QFactoryLoader::QFactoryLoader(const char *iid,
323 const QString &suffix,
324 Qt::CaseSensitivity cs)
325 : QObject(*new QFactoryLoaderPrivate)
326{
327 moveToThread(QCoreApplicationPrivate::mainThread());
328 Q_D(QFactoryLoader);
329 d->iid = iid;
330#if QT_CONFIG(library)
331 d->cs = cs;
332 d->suffix = suffix;
333# ifdef Q_OS_ANDROID
334 if (!d->suffix.isEmpty() && d->suffix.at(0) == QLatin1Char('/'))
335 d->suffix.remove(0, 1);
336# endif
337
338 QMutexLocker locker(qt_factoryloader_mutex());
339 update();
340 qt_factory_loaders()->append(this);
341#else
342 Q_UNUSED(suffix);
343 Q_UNUSED(cs);
344#endif
345}
346
347QList<QJsonObject> QFactoryLoader::metaData() const
348{
349 Q_D(const QFactoryLoader);
350 QList<QJsonObject> metaData;
351#if QT_CONFIG(library)
352 QMutexLocker locker(&d->mutex);
353 for (int i = 0; i < d->libraryList.size(); ++i)
354 metaData.append(d->libraryList.at(i)->metaData);
355#endif
356
357 const auto staticPlugins = QPluginLoader::staticPlugins();
358 for (const QStaticPlugin &plugin : staticPlugins) {
359 const QJsonObject object = plugin.metaData();
360 if (object.value(QLatin1String("IID")) != QLatin1String(d->iid.constData(), d->iid.size()))
361 continue;
362 metaData.append(object);
363 }
364 return metaData;
365}
366
367QObject *QFactoryLoader::instance(int index) const
368{
369 Q_D(const QFactoryLoader);
370 if (index < 0)
371 return nullptr;
372
373#if QT_CONFIG(library)
374 QMutexLocker lock(&d->mutex);
375 if (index < d->libraryList.size()) {
376 QLibraryPrivate *library = d->libraryList.at(index);
377 if (QObject *obj = library->pluginInstance()) {
378 if (!obj->parent())
379 obj->moveToThread(QCoreApplicationPrivate::mainThread());
380 return obj;
381 }
382 return nullptr;
383 }
384 index -= d->libraryList.size();
385 lock.unlock();
386#endif
387
388 QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins();
389 for (int i = 0; i < staticPlugins.count(); ++i) {
390 const QJsonObject object = staticPlugins.at(i).metaData();
391 if (object.value(QLatin1String("IID")) != QLatin1String(d->iid.constData(), d->iid.size()))
392 continue;
393
394 if (index == 0)
395 return staticPlugins.at(i).instance();
396 --index;
397 }
398
399 return nullptr;
400}
401
402QMultiMap<int, QString> QFactoryLoader::keyMap() const
403{
404 QMultiMap<int, QString> result;
405 const QList<QJsonObject> metaDataList = metaData();
406 for (int i = 0; i < metaDataList.size(); ++i) {
407 const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject();
408 const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray();
409 const int keyCount = keys.size();
410 for (int k = 0; k < keyCount; ++k)
411 result.insert(i, keys.at(k).toString());
412 }
413 return result;
414}
415
416int QFactoryLoader::indexOf(const QString &needle) const
417{
418 const QList<QJsonObject> metaDataList = metaData();
419 for (int i = 0; i < metaDataList.size(); ++i) {
420 const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject();
421 const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray();
422 const int keyCount = keys.size();
423 for (int k = 0; k < keyCount; ++k) {
424 if (!keys.at(k).toString().compare(needle, Qt::CaseInsensitive))
425 return i;
426 }
427 }
428 return -1;
429}
430
431QT_END_NAMESPACE
432
433#include "moc_qfactoryloader_p.cpp"
434
435#endif // QT_NO_QOBJECT
436