12 #include "ksycoca_p.h"
13 #include "ksycocafactory_p.h"
14 #include "ksycocatype.h"
15 #include "ksycocautils_p.h"
16 #include "sycocadebug.h"
17 #include <KConfigGroup>
19 #include <KSharedConfig>
21 #include <QCoreApplication>
22 #include <QDataStream>
25 #include <QMetaMethod>
26 #include <QStandardPaths>
28 #include <QThreadStorage>
30 #include <QCryptographicHash>
32 #include <kmimetypefactory_p.h>
33 #include <kservicefactory_p.h>
34 #include <kservicegroupfactory_p.h>
35 #include <kservicetypefactory_p.h>
38 #include "kbuildsycoca_p.h"
39 #include "ksycocadevices_p.h"
53 #define KSYCOCA_VERSION 305
55 #if HAVE_MADVISE || HAVE_MMAP
60 #define MAP_FAILED ((void *)-1)
65 in >> h.prefixes >> h.timeStamp >> h.language >> h.updateSignature;
77 Q_DECLARE_OPERATORS_FOR_FLAGS(KSycocaPrivate::BehaviorsIfNotFound)
79 KSycocaPrivate::KSycocaPrivate(
KSycoca *qq)
80 : databaseStatus(DatabaseNotOpen)
86 , m_haveListeners(false)
89 , sycoca_mmap(nullptr)
92 , m_mimeTypeFactory(nullptr)
93 , m_serviceTypeFactory(nullptr)
94 , m_serviceFactory(nullptr)
95 , m_serviceGroupFactory(nullptr)
102 m_sycocaStrategy = StrategyMemFile;
104 m_sycocaStrategy = StrategyMmap;
107 setStrategyFromString(
config.readEntry(
"strategy"));
110 void KSycocaPrivate::setStrategyFromString(
const QString &strategy)
113 m_sycocaStrategy = StrategyMmap;
115 m_sycocaStrategy = StrategyFile;
117 m_sycocaStrategy = StrategyMemFile;
118 }
else if (!strategy.
isEmpty()) {
119 qCWarning(SYCOCA) <<
"Unknown sycoca strategy:" << strategy;
123 bool KSycocaPrivate::tryMmap()
126 Q_ASSERT(!m_databasePath.isEmpty());
127 m_mmapFile =
new QFile(m_databasePath);
133 fcntl(m_mmapFile->handle(), F_SETFD, FD_CLOEXEC);
134 sycoca_size = m_mmapFile->size();
135 void *mmapRet = mmap(
nullptr, sycoca_size, PROT_READ, MAP_SHARED, m_mmapFile->handle(), 0);
138 if (mmapRet == MAP_FAILED || mmapRet ==
nullptr) {
139 qCDebug(SYCOCA).nospace() <<
"mmap failed. (length = " << sycoca_size <<
")";
140 sycoca_mmap =
nullptr;
143 sycoca_mmap =
static_cast<const char *
>(mmapRet);
145 (void)posix_madvise(mmapRet, sycoca_size, POSIX_MADV_WILLNEED);
146 #endif // HAVE_MADVISE
156 return KSYCOCA_VERSION;
159 class KSycocaSingleton
169 bool hasSycoca()
const
171 return m_threadSycocas.hasLocalData();
175 if (!m_threadSycocas.hasLocalData()) {
176 m_threadSycocas.setLocalData(
new KSycoca);
178 return m_threadSycocas.localData();
182 m_threadSycocas.setLocalData(s);
191 QString KSycocaPrivate::findDatabase()
193 Q_ASSERT(databaseStatus == DatabaseNotOpen);
197 if (info.isReadable()) {
198 if (m_haveListeners && m_fileWatcher) {
199 m_fileWatcher->addFile(path);
205 m_fileWatcher->addFile(path);
213 : d(new KSycocaPrivate(this))
215 if (d->m_fileWatcher) {
218 d->slotDatabaseChanged();
222 d->slotDatabaseChanged();
227 bool KSycocaPrivate::openDatabase()
229 Q_ASSERT(databaseStatus == DatabaseNotOpen);
234 if (m_databasePath.isEmpty()) {
235 m_databasePath = findDatabase();
239 if (!m_databasePath.isEmpty()) {
240 static bool firstTime =
true;
246 qCDebug(SYCOCA) <<
"flatpak detected, ignoring" << m_databasePath;
251 qCDebug(SYCOCA) <<
"Opening ksycoca from" << m_databasePath;
261 KSycocaAbstractDevice *KSycocaPrivate::device()
267 KSycocaAbstractDevice *device = m_device;
268 Q_ASSERT(!m_databasePath.isEmpty());
270 if (m_sycocaStrategy == StrategyMmap && tryMmap()) {
271 device =
new KSycocaMmapDevice(sycoca_mmap, sycoca_size);
278 #ifndef QT_NO_SHAREDMEMORY
279 if (!device && m_sycocaStrategy == StrategyMemFile) {
280 device =
new KSycocaMemFileDevice(m_databasePath);
288 device =
new KSycocaFileDevice(m_databasePath);
290 qCWarning(SYCOCA) <<
"Couldn't open" << m_databasePath <<
"even though it is readable? Impossible.";
303 if (databaseStatus == DatabaseNotOpen) {
304 checkDatabase(KSycocaPrivate::IfNotFoundRecreate);
310 return m_device->stream();
313 void KSycocaPrivate::slotDatabaseChanged()
316 changeList =
QStringList() << QStringLiteral(
"services") << QStringLiteral(
"servicetypes") << QStringLiteral(
"xdgdata-mime") << QStringLiteral(
"apps");
324 m_databasePath = findDatabase();
327 Q_EMIT q->databaseChanged();
329 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 80)
330 Q_EMIT q->databaseChanged(changeList);
334 KMimeTypeFactory *KSycocaPrivate::mimeTypeFactory()
336 if (!m_mimeTypeFactory) {
337 m_mimeTypeFactory =
new KMimeTypeFactory(q);
339 return m_mimeTypeFactory;
342 KServiceTypeFactory *KSycocaPrivate::serviceTypeFactory()
344 if (!m_serviceTypeFactory) {
345 m_serviceTypeFactory =
new KServiceTypeFactory(q);
347 return m_serviceTypeFactory;
350 KServiceFactory *KSycocaPrivate::serviceFactory()
352 if (!m_serviceFactory) {
353 m_serviceFactory =
new KServiceFactory(q);
355 return m_serviceFactory;
358 KServiceGroupFactory *KSycocaPrivate::serviceGroupFactory()
360 if (!m_serviceGroupFactory) {
361 m_serviceGroupFactory =
new KServiceGroupFactory(q);
363 return m_serviceGroupFactory;
367 void KSycocaPrivate::addLocalResourceDir(
const QString &path)
370 allResourceDirs.insert(path, timeStamp);
375 : d(new KSycocaPrivate(this))
381 KSycoca *s = ksycocaInstance()->sycoca();
397 return self()->d->checkDatabase(KSycocaPrivate::IfNotFoundDoNothing);
400 void KSycocaPrivate::closeDatabase()
409 qDeleteAll(m_factories);
412 m_mimeTypeFactory =
nullptr;
413 m_serviceFactory =
nullptr;
414 m_serviceTypeFactory =
nullptr;
415 m_serviceGroupFactory =
nullptr;
421 munmap(
const_cast<char *
>(sycoca_mmap), sycoca_size);
422 sycoca_mmap =
nullptr;
425 m_mmapFile =
nullptr;
428 databaseStatus = DatabaseNotOpen;
429 m_databasePath.clear();
435 d->addFactory(factory);
438 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
453 type = KSycocaType(aType);
458 KSycocaFactoryList *KSycoca::factories()
460 return d->factories();
464 bool KSycocaPrivate::checkVersion()
471 if (aVersion < KSYCOCA_VERSION) {
472 qCDebug(SYCOCA) <<
"Found version" << aVersion <<
", expecting version" << KSYCOCA_VERSION <<
"or higher.";
473 databaseStatus = BadVersion;
476 databaseStatus = DatabaseOK;
482 extern KSERVICE_EXPORT
bool kservice_require_kded;
483 KSERVICE_EXPORT
bool kservice_require_kded =
true;
487 bool KSycocaPrivate::checkDatabase(BehaviorsIfNotFound ifNotFound)
489 if (databaseStatus == DatabaseOK) {
498 if (openDatabase()) {
501 if (qAppName() !=
QLatin1String(KBUILDSYCOCA_EXENAME) && ifNotFound != IfNotFoundDoNothing) {
512 if (ifNotFound & IfNotFoundRecreate) {
513 return buildSycoca();
522 if (!d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate)) {
534 qCWarning(SYCOCA) <<
"Error, KSycocaFactory (id =" << int(
id) <<
") not found!";
547 bool KSycoca::needsRebuild()
549 return d->needsRebuild();
552 KSycocaHeader KSycocaPrivate::readSycocaHeader()
554 KSycocaHeader header;
556 if (!checkDatabase(KSycocaPrivate::IfNotFoundDoNothing)) {
576 *str >> header >> directoryList;
577 allResourceDirs.
clear();
578 for (
int i = 0; i < directoryList.count(); ++i) {
581 allResourceDirs.insert(directoryList.at(i), mtime);
587 for (
const auto &fileName : std::as_const(fileList)) {
590 extraFiles.insert(fileName, mtime);
595 timeStamp = header.timeStamp;
598 language = header.language;
599 updateSig = header.updateSignature;
604 class TimestampChecker
617 Q_ASSERT(!
dirs.isEmpty());
619 for (
auto it =
dirs.begin(); it !=
dirs.end(); ++it) {
621 const qint64 lastStamp = it.value();
623 auto visitor = [&](
const QFileInfo &fi) {
624 const QDateTime mtime = fi.lastModified();
627 qCDebug(SYCOCA) << fi.filePath() <<
"has a modification time in the future" << mtime;
637 if (!KSycocaUtilsPrivate::visitResourceDirectory(dir, visitor)) {
646 for (
auto it = files.
begin(); it != files.
end(); ++it) {
647 const QString fileName = it.key();
648 const qint64 lastStamp = it.value();
654 const QDateTime mtime = fi.lastModified();
657 qCDebug(SYCOCA) << fi.filePath() <<
"has a modification time in the future" << mtime;
670 void KSycocaPrivate::checkDirectories()
672 if (needsRebuild()) {
677 bool KSycocaPrivate::needsRebuild()
679 if (!timeStamp && databaseStatus == DatabaseOK) {
680 (void)readSycocaHeader();
684 const auto timestampChecker = TimestampChecker();
685 bool ret = timeStamp != 0
686 && (!timestampChecker.checkDirectoriesTimestamps(allResourceDirs)
687 || !timestampChecker.checkFilesTimestamps(extraFiles));
691 auto files = KBuildSycoca::factoryExtraFiles();
695 return extraFiles.
keys() != files;
698 bool KSycocaPrivate::buildSycoca()
700 KBuildSycoca builder;
701 if (!builder.recreate()) {
708 if (!openDatabase()) {
709 qCDebug(SYCOCA) <<
"Still no database...";
715 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 15)
719 (void)d->readSycocaHeader();
721 return d->timeStamp / 1000;
725 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 15)
729 (void)d->readSycocaHeader();
741 const QByteArray ksycoca_env = qgetenv(
"KDESYCOCA");
756 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 15)
759 if (d->language.isEmpty()) {
760 (void)d->readSycocaHeader();
769 (void)d->readSycocaHeader();
771 return d->allResourceDirs.keys();
776 qCWarning(SYCOCA) <<
"ERROR: KSycoca database corruption!";
778 if (sycoca->d->readError) {
781 sycoca->d->readError =
true;
784 KBuildSycoca builder;
785 (void)builder.recreate();
796 ksycocaInstance->sycoca()->d->m_fileWatcher =
nullptr;
804 void KSycoca::connectNotify(
const QMetaMethod &signal)
806 if (signal.
name() ==
"databaseChanged" && !d->m_haveListeners) {
807 d->m_haveListeners =
true;
808 if (d->m_databasePath.isEmpty()) {
809 d->m_databasePath = d->findDatabase();
810 }
else if (d->m_fileWatcher) {
811 d->m_fileWatcher->addFile(d->m_databasePath);
816 void KSycoca::clearCaches()
818 if (ksycocaInstance.exists() && ksycocaInstance()->hasSycoca()) {
819 ksycocaInstance()->sycoca()->d->closeDatabase();
823 extern KSERVICE_EXPORT
int ksycoca_ms_between_checks;
824 KSERVICE_EXPORT
int ksycoca_ms_between_checks = 1500;
832 if (d->databaseStatus != KSycocaPrivate::DatabaseOK) {
833 if (!d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate)) {
838 if (d->m_lastCheck.isValid() && d->m_lastCheck.elapsed() < ksycoca_ms_between_checks) {
841 d->m_lastCheck.start();
847 d->checkDirectories();