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
61#include <qtcore_tracepoints_p.h>
62
63QT_BEGIN_NAMESPACE
64
65static inline int metaDataSignatureLength()
66{
67 return sizeof("QTMETADATA ") - 1;
68}
69
70static QJsonDocument jsonFromCborMetaData(const char *raw, qsizetype size, QString *errMsg)
71{
72 // extract the keys not stored in CBOR
73 int qt_metadataVersion = quint8(raw[0]);
74 int qt_version = qFromBigEndian<quint16>(src: raw + 1);
75 int qt_archRequirements = quint8(raw[3]);
76 if (Q_UNLIKELY(raw[-1] != '!' || qt_metadataVersion != 0)) {
77 *errMsg = QStringLiteral("Invalid metadata version");
78 return QJsonDocument();
79 }
80
81 raw += 4;
82 size -= 4;
83 QByteArray ba = QByteArray::fromRawData(raw, size: int(size));
84 QCborParserError err;
85 QCborValue metadata = QCborValue::fromCbor(ba, error: &err);
86
87 if (err.error != QCborError::NoError) {
88 *errMsg = QLatin1String("Metadata parsing error: ") + err.error.toString();
89 return QJsonDocument();
90 }
91
92 if (!metadata.isMap()) {
93 *errMsg = QStringLiteral("Unexpected metadata contents");
94 return QJsonDocument();
95 }
96
97 QJsonObject o;
98 o.insert(key: QLatin1String("version"), value: qt_version << 8);
99 o.insert(key: QLatin1String("debug"), value: bool(qt_archRequirements & 1));
100 o.insert(key: QLatin1String("archreq"), value: qt_archRequirements);
101
102 // convert the top-level map integer keys
103 for (auto it : metadata.toMap()) {
104 QString key;
105 if (it.first.isInteger()) {
106 switch (it.first.toInteger()) {
107#define CONVERT_TO_STRING(IntKey, StringKey, Description) \
108 case int(IntKey): key = QStringLiteral(StringKey); break;
109 QT_PLUGIN_FOREACH_METADATA(CONVERT_TO_STRING)
110#undef CONVERT_TO_STRING
111
112 case int(QtPluginMetaDataKeys::Requirements):
113 // special case: recreate the debug key
114 o.insert(key: QLatin1String("debug"), value: bool(it.second.toInteger() & 1));
115 key = QStringLiteral("archreq");
116 break;
117 }
118 } else {
119 key = it.first.toString();
120 }
121
122 if (!key.isEmpty())
123 o.insert(key, value: it.second.toJsonValue());
124 }
125 return QJsonDocument(o);
126}
127
128QT_WARNING_PUSH
129QT_WARNING_DISABLE_DEPRECATED
130QJsonDocument qJsonFromRawLibraryMetaData(const char *raw, qsizetype sectionSize, QString *errMsg)
131{
132 raw += metaDataSignatureLength();
133 sectionSize -= metaDataSignatureLength();
134
135#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
136 if (Q_UNLIKELY(raw[-1] == ' ')) {
137 // the size of the embedded JSON object can be found 8 bytes into the data (see qjson_p.h)
138 uint size = qFromLittleEndian<uint>(src: raw + 8);
139 // but the maximum size of binary JSON is 128 MB
140 size = qMin(a: size, b: 128U * 1024 * 1024);
141 // and it doesn't include the size of the header (8 bytes)
142 size += 8;
143 // finally, it can't be bigger than the file or section size
144 size = qMin(a: sectionSize, b: qsizetype(size));
145
146 QByteArray json(raw, size);
147 return QJsonDocument::fromBinaryData(data: json);
148 }
149#endif
150
151 return jsonFromCborMetaData(raw, size: sectionSize, errMsg);
152}
153QT_WARNING_POP
154
155class QFactoryLoaderPrivate : public QObjectPrivate
156{
157 Q_DECLARE_PUBLIC(QFactoryLoader)
158public:
159 QFactoryLoaderPrivate(){}
160 QByteArray iid;
161#if QT_CONFIG(library)
162 ~QFactoryLoaderPrivate();
163 mutable QMutex mutex;
164 QList<QLibraryPrivate*> libraryList;
165 QMap<QString,QLibraryPrivate*> keyMap;
166 QString suffix;
167 Qt::CaseSensitivity cs;
168 QStringList loadedPaths;
169#endif
170};
171
172#if QT_CONFIG(library)
173
174Q_GLOBAL_STATIC(QList<QFactoryLoader *>, qt_factory_loaders)
175
176Q_GLOBAL_STATIC(QRecursiveMutex, qt_factoryloader_mutex)
177
178QFactoryLoaderPrivate::~QFactoryLoaderPrivate()
179{
180 for (int i = 0; i < libraryList.count(); ++i) {
181 QLibraryPrivate *library = libraryList.at(i);
182 library->unload();
183 library->release();
184 }
185}
186
187void QFactoryLoader::update()
188{
189#ifdef QT_SHARED
190 Q_D(QFactoryLoader);
191 QStringList paths = QCoreApplication::libraryPaths();
192 for (int i = 0; i < paths.count(); ++i) {
193 const QString &pluginDir = paths.at(i);
194 // Already loaded, skip it...
195 if (d->loadedPaths.contains(str: pluginDir))
196 continue;
197 d->loadedPaths << pluginDir;
198
199#ifdef Q_OS_ANDROID
200 QString path = pluginDir;
201#else
202 QString path = pluginDir + d->suffix;
203#endif
204
205 if (qt_debug_component())
206 qDebug() << "QFactoryLoader::QFactoryLoader() checking directory path" << path << "...";
207
208 if (!QDir(path).exists(name: QLatin1String(".")))
209 continue;
210
211 QStringList plugins = QDir(path).entryList(
212#if defined(Q_OS_WIN)
213 QStringList(QStringLiteral("*.dll")),
214#elif defined(Q_OS_ANDROID)
215 QStringList(QLatin1String("libplugins_%1_*.so").arg(d->suffix)),
216#endif
217 filters: QDir::Files);
218 QLibraryPrivate *library = nullptr;
219
220 for (int j = 0; j < plugins.count(); ++j) {
221 QString fileName = QDir::cleanPath(path: path + QLatin1Char('/') + plugins.at(i: j));
222
223#ifdef Q_OS_MAC
224 const bool isDebugPlugin = fileName.endsWith(QLatin1String("_debug.dylib"));
225 const bool isDebugLibrary =
226 #ifdef QT_DEBUG
227 true;
228 #else
229 false;
230 #endif
231
232 // Skip mismatching plugins so that we don't end up loading both debug and release
233 // versions of the same Qt libraries (due to the plugin's dependencies).
234 if (isDebugPlugin != isDebugLibrary)
235 continue;
236#elif defined(Q_PROCESSOR_X86)
237 if (fileName.endsWith(s: QLatin1String(".avx2")) || fileName.endsWith(s: QLatin1String(".avx512"))) {
238 // ignore AVX2-optimized file, we'll do a bait-and-switch to it later
239 continue;
240 }
241#endif
242 if (qt_debug_component()) {
243 qDebug() << "QFactoryLoader::QFactoryLoader() looking at" << fileName;
244 }
245
246 Q_TRACE(QFactoryLoader_update, fileName);
247
248 library = QLibraryPrivate::findOrCreate(fileName: QFileInfo(fileName).canonicalFilePath());
249 if (!library->isPlugin()) {
250 if (qt_debug_component()) {
251 qDebug() << library->errorString << Qt::endl
252 << " not a plugin";
253 }
254 library->release();
255 continue;
256 }
257
258 QStringList keys;
259 bool metaDataOk = false;
260
261 QString iid = library->metaData.value(key: QLatin1String("IID")).toString();
262 if (iid == QLatin1String(d->iid.constData(), d->iid.size())) {
263 QJsonObject object = library->metaData.value(key: QLatin1String("MetaData")).toObject();
264 metaDataOk = true;
265
266 QJsonArray k = object.value(key: QLatin1String("Keys")).toArray();
267 for (int i = 0; i < k.size(); ++i)
268 keys += d->cs ? k.at(i).toString() : k.at(i).toString().toLower();
269 }
270 if (qt_debug_component())
271 qDebug() << "Got keys from plugin meta data" << keys;
272
273
274 if (!metaDataOk) {
275 library->release();
276 continue;
277 }
278
279 int keyUsageCount = 0;
280 for (int k = 0; k < keys.count(); ++k) {
281 // first come first serve, unless the first
282 // library was built with a future Qt version,
283 // whereas the new one has a Qt version that fits
284 // better
285 const QString &key = keys.at(i: k);
286 QLibraryPrivate *previous = d->keyMap.value(akey: key);
287 int prev_qt_version = 0;
288 if (previous) {
289 prev_qt_version = (int)previous->metaData.value(key: QLatin1String("version")).toDouble();
290 }
291 int qt_version = (int)library->metaData.value(key: QLatin1String("version")).toDouble();
292 if (!previous || (prev_qt_version > QT_VERSION && qt_version <= QT_VERSION)) {
293 d->keyMap[key] = library;
294 ++keyUsageCount;
295 }
296 }
297 if (keyUsageCount || keys.isEmpty()) {
298 library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload
299 QMutexLocker locker(&d->mutex);
300 d->libraryList += library;
301 } else {
302 library->release();
303 }
304 }
305 }
306#else
307 Q_D(QFactoryLoader);
308 if (qt_debug_component()) {
309 qDebug() << "QFactoryLoader::QFactoryLoader() ignoring" << d->iid
310 << "since plugins are disabled in static builds";
311 }
312#endif
313}
314
315QFactoryLoader::~QFactoryLoader()
316{
317 QMutexLocker locker(qt_factoryloader_mutex());
318 qt_factory_loaders()->removeAll(t: this);
319}
320
321#if defined(Q_OS_UNIX) && !defined (Q_OS_MAC)
322QLibraryPrivate *QFactoryLoader::library(const QString &key) const
323{
324 Q_D(const QFactoryLoader);
325 return d->keyMap.value(akey: d->cs ? key : key.toLower());
326}
327#endif
328
329void QFactoryLoader::refreshAll()
330{
331 QMutexLocker locker(qt_factoryloader_mutex());
332 QList<QFactoryLoader *> *loaders = qt_factory_loaders();
333 for (QList<QFactoryLoader *>::const_iterator it = loaders->constBegin();
334 it != loaders->constEnd(); ++it) {
335 (*it)->update();
336 }
337}
338
339#endif // QT_CONFIG(library)
340
341QFactoryLoader::QFactoryLoader(const char *iid,
342 const QString &suffix,
343 Qt::CaseSensitivity cs)
344 : QObject(*new QFactoryLoaderPrivate)
345{
346 moveToThread(thread: QCoreApplicationPrivate::mainThread());
347 Q_D(QFactoryLoader);
348 d->iid = iid;
349#if QT_CONFIG(library)
350 d->cs = cs;
351 d->suffix = suffix;
352# ifdef Q_OS_ANDROID
353 if (!d->suffix.isEmpty() && d->suffix.at(0) == QLatin1Char('/'))
354 d->suffix.remove(0, 1);
355# endif
356
357 QMutexLocker locker(qt_factoryloader_mutex());
358 update();
359 qt_factory_loaders()->append(t: this);
360#else
361 Q_UNUSED(suffix);
362 Q_UNUSED(cs);
363#endif
364}
365
366QList<QJsonObject> QFactoryLoader::metaData() const
367{
368 Q_D(const QFactoryLoader);
369 QList<QJsonObject> metaData;
370#if QT_CONFIG(library)
371 QMutexLocker locker(&d->mutex);
372 for (int i = 0; i < d->libraryList.size(); ++i)
373 metaData.append(t: d->libraryList.at(i)->metaData);
374#endif
375
376 const auto staticPlugins = QPluginLoader::staticPlugins();
377 for (const QStaticPlugin &plugin : staticPlugins) {
378 const QJsonObject object = plugin.metaData();
379 if (object.value(key: QLatin1String("IID")) != QLatin1String(d->iid.constData(), d->iid.size()))
380 continue;
381 metaData.append(t: object);
382 }
383 return metaData;
384}
385
386QObject *QFactoryLoader::instance(int index) const
387{
388 Q_D(const QFactoryLoader);
389 if (index < 0)
390 return nullptr;
391
392#if QT_CONFIG(library)
393 QMutexLocker lock(&d->mutex);
394 if (index < d->libraryList.size()) {
395 QLibraryPrivate *library = d->libraryList.at(i: index);
396 if (QObject *obj = library->pluginInstance()) {
397 if (!obj->parent())
398 obj->moveToThread(thread: QCoreApplicationPrivate::mainThread());
399 return obj;
400 }
401 return nullptr;
402 }
403 index -= d->libraryList.size();
404 lock.unlock();
405#endif
406
407 QVector<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins();
408 for (int i = 0; i < staticPlugins.count(); ++i) {
409 const QJsonObject object = staticPlugins.at(i).metaData();
410 if (object.value(key: QLatin1String("IID")) != QLatin1String(d->iid.constData(), d->iid.size()))
411 continue;
412
413 if (index == 0)
414 return staticPlugins.at(i).instance();
415 --index;
416 }
417
418 return nullptr;
419}
420
421QMultiMap<int, QString> QFactoryLoader::keyMap() const
422{
423 QMultiMap<int, QString> result;
424 const QList<QJsonObject> metaDataList = metaData();
425 for (int i = 0; i < metaDataList.size(); ++i) {
426 const QJsonObject metaData = metaDataList.at(i).value(key: QLatin1String("MetaData")).toObject();
427 const QJsonArray keys = metaData.value(key: QLatin1String("Keys")).toArray();
428 const int keyCount = keys.size();
429 for (int k = 0; k < keyCount; ++k)
430 result.insert(akey: i, avalue: keys.at(i: k).toString());
431 }
432 return result;
433}
434
435int QFactoryLoader::indexOf(const QString &needle) const
436{
437 const QList<QJsonObject> metaDataList = metaData();
438 for (int i = 0; i < metaDataList.size(); ++i) {
439 const QJsonObject metaData = metaDataList.at(i).value(key: QLatin1String("MetaData")).toObject();
440 const QJsonArray keys = metaData.value(key: QLatin1String("Keys")).toArray();
441 const int keyCount = keys.size();
442 for (int k = 0; k < keyCount; ++k) {
443 if (!keys.at(i: k).toString().compare(s: needle, cs: Qt::CaseInsensitive))
444 return i;
445 }
446 }
447 return -1;
448}
449
450QT_END_NAMESPACE
451
452#include "moc_qfactoryloader_p.cpp"
453
454#endif // QT_NO_QOBJECT
455

source code of qtbase/src/corelib/plugin/qfactoryloader.cpp