KService

ksycoca.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 1999-2000 Waldo Bastian <[email protected]>
4  SPDX-FileCopyrightText: 2005-2009 David Faure <[email protected]>
5  SPDX-FileCopyrightText: 2008 Hamish Rodda <[email protected]>
6  SPDX-FileCopyrightText: 2020-2022 Harald Sitter <[email protected]>
7 
8  SPDX-License-Identifier: LGPL-2.0-only
9 */
10 
11 #include "ksycoca.h"
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>
18 #include <KSandbox>
19 #include <KSharedConfig>
20 
21 #include <QCoreApplication>
22 #include <QDataStream>
23 #include <QFile>
24 #include <QFileInfo>
25 #include <QMetaMethod>
26 #include <QStandardPaths>
27 #include <QThread>
28 #include <QThreadStorage>
29 
30 #include <QCryptographicHash>
31 #include <fcntl.h>
32 #include <kmimetypefactory_p.h>
33 #include <kservicefactory_p.h>
34 #include <kservicegroupfactory_p.h>
35 #include <kservicetypefactory_p.h>
36 #include <stdlib.h>
37 
38 #include "kbuildsycoca_p.h"
39 #include "ksycocadevices_p.h"
40 
41 #ifdef Q_OS_UNIX
42 #include <sys/time.h>
43 #include <utime.h>
44 #endif
45 
46 /**
47  * Sycoca file version number.
48  * If the existing file is outdated, it will not get read
49  * but instead we'll regenerate a new one.
50  * However running apps should still be able to read it, so
51  * only add to the data, never remove/modify.
52  */
53 #define KSYCOCA_VERSION 305
54 
55 #if HAVE_MADVISE || HAVE_MMAP
56 #include <sys/mman.h> // This #include was checked when looking for posix_madvise
57 #endif
58 
59 #ifndef MAP_FAILED
60 #define MAP_FAILED ((void *)-1)
61 #endif
62 
63 QDataStream &operator>>(QDataStream &in, KSycocaHeader &h)
64 {
65  in >> h.prefixes >> h.timeStamp >> h.language >> h.updateSignature;
66  return in;
67 }
68 
69 // The following limitations are in place:
70 // Maximum length of a single string: 8192 bytes
71 // Maximum length of a string list: 1024 strings
72 // Maximum number of entries: 8192
73 //
74 // The purpose of these limitations is to limit the impact
75 // of database corruption.
76 
77 Q_DECLARE_OPERATORS_FOR_FLAGS(KSycocaPrivate::BehaviorsIfNotFound)
78 
79 KSycocaPrivate::KSycocaPrivate(KSycoca *qq)
80  : databaseStatus(DatabaseNotOpen)
81  , readError(false)
82  , timeStamp(0)
83  , m_databasePath()
84  , updateSig(0)
85  , m_fileWatcher(new KDirWatch)
86  , m_haveListeners(false)
87  , q(qq)
88  , sycoca_size(0)
89  , sycoca_mmap(nullptr)
90  , m_mmapFile(nullptr)
91  , m_device(nullptr)
92  , m_mimeTypeFactory(nullptr)
93  , m_serviceTypeFactory(nullptr)
94  , m_serviceFactory(nullptr)
95  , m_serviceGroupFactory(nullptr)
96 {
97 #ifdef Q_OS_WIN
98  /*
99  on windows we use KMemFile (QSharedMemory) to avoid problems
100  with mmap (can't delete a mmap'd file)
101  */
102  m_sycocaStrategy = StrategyMemFile;
103 #else
104  m_sycocaStrategy = StrategyMmap;
105 #endif
107  setStrategyFromString(config.readEntry("strategy"));
108 }
109 
110 void KSycocaPrivate::setStrategyFromString(const QString &strategy)
111 {
112  if (strategy == QLatin1String("mmap")) {
113  m_sycocaStrategy = StrategyMmap;
114  } else if (strategy == QLatin1String("file")) {
115  m_sycocaStrategy = StrategyFile;
116  } else if (strategy == QLatin1String("sharedmem")) {
117  m_sycocaStrategy = StrategyMemFile;
118  } else if (!strategy.isEmpty()) {
119  qCWarning(SYCOCA) << "Unknown sycoca strategy:" << strategy;
120  }
121 }
122 
123 bool KSycocaPrivate::tryMmap()
124 {
125 #if HAVE_MMAP
126  Q_ASSERT(!m_databasePath.isEmpty());
127  m_mmapFile = new QFile(m_databasePath);
128  const bool canRead = m_mmapFile->open(QIODevice::ReadOnly);
129  Q_ASSERT(canRead);
130  if (!canRead) {
131  return false;
132  }
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);
136  /* POSIX mandates only MAP_FAILED, but we are paranoid so check for
137  null pointer too. */
138  if (mmapRet == MAP_FAILED || mmapRet == nullptr) {
139  qCDebug(SYCOCA).nospace() << "mmap failed. (length = " << sycoca_size << ")";
140  sycoca_mmap = nullptr;
141  return false;
142  } else {
143  sycoca_mmap = static_cast<const char *>(mmapRet);
144 #if HAVE_MADVISE
145  (void)posix_madvise(mmapRet, sycoca_size, POSIX_MADV_WILLNEED);
146 #endif // HAVE_MADVISE
147  return true;
148  }
149 #else
150  return false;
151 #endif // HAVE_MMAP
152 }
153 
155 {
156  return KSYCOCA_VERSION;
157 }
158 
159 class KSycocaSingleton
160 {
161 public:
162  KSycocaSingleton()
163  {
164  }
165  ~KSycocaSingleton()
166  {
167  }
168 
169  bool hasSycoca() const
170  {
171  return m_threadSycocas.hasLocalData();
172  }
173  KSycoca *sycoca()
174  {
175  if (!m_threadSycocas.hasLocalData()) {
176  m_threadSycocas.setLocalData(new KSycoca);
177  }
178  return m_threadSycocas.localData();
179  }
180  void setSycoca(KSycoca *s)
181  {
182  m_threadSycocas.setLocalData(s);
183  }
184 
185 private:
186  QThreadStorage<KSycoca *> m_threadSycocas;
187 };
188 
189 Q_GLOBAL_STATIC(KSycocaSingleton, ksycocaInstance)
190 
191 QString KSycocaPrivate::findDatabase()
192 {
193  Q_ASSERT(databaseStatus == DatabaseNotOpen);
194 
196  const QFileInfo info(path);
197  if (info.isReadable()) {
198  if (m_haveListeners && m_fileWatcher) {
199  m_fileWatcher->addFile(path);
200  }
201  return path;
202  }
203  // Let's be notified when it gets created - by another process or by ourselves
204  if (m_fileWatcher) {
205  m_fileWatcher->addFile(path);
206  }
207  return QString();
208 }
209 
210 // Read-only constructor
211 // One instance per thread
213  : d(new KSycocaPrivate(this))
214 {
215  if (d->m_fileWatcher) {
216  // We always delete and recreate the DB, so KDirWatch normally emits created
217  connect(d->m_fileWatcher.get(), &KDirWatch::created, this, [this]() {
218  d->slotDatabaseChanged();
219  });
220  // In some cases, KDirWatch only thinks the file was modified though
221  connect(d->m_fileWatcher.get(), &KDirWatch::dirty, this, [this]() {
222  d->slotDatabaseChanged();
223  });
224  }
225 }
226 
227 bool KSycocaPrivate::openDatabase()
228 {
229  Q_ASSERT(databaseStatus == DatabaseNotOpen);
230 
231  delete m_device;
232  m_device = nullptr;
233 
234  if (m_databasePath.isEmpty()) {
235  m_databasePath = findDatabase();
236  }
237 
238  bool result = true;
239  if (!m_databasePath.isEmpty()) {
240  static bool firstTime = true;
241  if (firstTime) {
242  firstTime = false;
243  if (KSandbox::isFlatpak()) {
244  // We're running inside flatpak, which sets all times to 1970
245  // So the first very time, don't use an existing database, recreate it
246  qCDebug(SYCOCA) << "flatpak detected, ignoring" << m_databasePath;
247  return false;
248  }
249  }
250 
251  qCDebug(SYCOCA) << "Opening ksycoca from" << m_databasePath;
252  m_dbLastModified = QFileInfo(m_databasePath).lastModified();
253  result = checkVersion();
254  } else { // No database file
255  // qCDebug(SYCOCA) << "Could not open ksycoca";
256  result = false;
257  }
258  return result;
259 }
260 
261 KSycocaAbstractDevice *KSycocaPrivate::device()
262 {
263  if (m_device) {
264  return m_device;
265  }
266 
267  KSycocaAbstractDevice *device = m_device;
268  Q_ASSERT(!m_databasePath.isEmpty());
269 #if HAVE_MMAP
270  if (m_sycocaStrategy == StrategyMmap && tryMmap()) {
271  device = new KSycocaMmapDevice(sycoca_mmap, sycoca_size);
272  if (!device->device()->open(QIODevice::ReadOnly)) {
273  delete device;
274  device = nullptr;
275  }
276  }
277 #endif
278 #ifndef QT_NO_SHAREDMEMORY
279  if (!device && m_sycocaStrategy == StrategyMemFile) {
280  device = new KSycocaMemFileDevice(m_databasePath);
281  if (!device->device()->open(QIODevice::ReadOnly)) {
282  delete device;
283  device = nullptr;
284  }
285  }
286 #endif
287  if (!device) {
288  device = new KSycocaFileDevice(m_databasePath);
289  if (!device->device()->open(QIODevice::ReadOnly)) {
290  qCWarning(SYCOCA) << "Couldn't open" << m_databasePath << "even though it is readable? Impossible.";
291  // delete device; device = 0; // this would crash in the return statement...
292  }
293  }
294  if (device) {
295  m_device = device;
296  }
297  return m_device;
298 }
299 
300 QDataStream *&KSycocaPrivate::stream()
301 {
302  if (!m_device) {
303  if (databaseStatus == DatabaseNotOpen) {
304  checkDatabase(KSycocaPrivate::IfNotFoundRecreate);
305  }
306 
307  device(); // create m_device
308  }
309 
310  return m_device->stream();
311 }
312 
313 void KSycocaPrivate::slotDatabaseChanged()
314 {
315  // We don't have information anymore on what resources changed, so emit them all
316  changeList = QStringList() << QStringLiteral("services") << QStringLiteral("servicetypes") << QStringLiteral("xdgdata-mime") << QStringLiteral("apps");
317 
318  qCDebug(SYCOCA) << QThread::currentThread() << "got a notifyDatabaseChanged signal";
319  // KDirWatch tells us the database file changed
320  // We would have found out in the next call to ensureCacheValid(), but for
321  // now keep the call to closeDatabase, to help refcounting to 0 the old mmapped file earlier.
322  closeDatabase();
323  // Start monitoring the new file right away
324  m_databasePath = findDatabase();
325 
326  // Now notify applications
327  Q_EMIT q->databaseChanged();
328 
329 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 80)
330  Q_EMIT q->databaseChanged(changeList);
331 #endif
332 }
333 
334 KMimeTypeFactory *KSycocaPrivate::mimeTypeFactory()
335 {
336  if (!m_mimeTypeFactory) {
337  m_mimeTypeFactory = new KMimeTypeFactory(q);
338  }
339  return m_mimeTypeFactory;
340 }
341 
342 KServiceTypeFactory *KSycocaPrivate::serviceTypeFactory()
343 {
344  if (!m_serviceTypeFactory) {
345  m_serviceTypeFactory = new KServiceTypeFactory(q);
346  }
347  return m_serviceTypeFactory;
348 }
349 
350 KServiceFactory *KSycocaPrivate::serviceFactory()
351 {
352  if (!m_serviceFactory) {
353  m_serviceFactory = new KServiceFactory(q);
354  }
355  return m_serviceFactory;
356 }
357 
358 KServiceGroupFactory *KSycocaPrivate::serviceGroupFactory()
359 {
360  if (!m_serviceGroupFactory) {
361  m_serviceGroupFactory = new KServiceGroupFactory(q);
362  }
363  return m_serviceGroupFactory;
364 }
365 
366 // Add local paths to the list of dirs we got from the global database
367 void KSycocaPrivate::addLocalResourceDir(const QString &path)
368 {
369  // If any local path is more recent than the time the global sycoca was created, build a local sycoca.
370  allResourceDirs.insert(path, timeStamp);
371 }
372 
373 // Read-write constructor - only for KBuildSycoca
374 KSycoca::KSycoca(bool /* dummy */)
375  : d(new KSycocaPrivate(this))
376 {
377 }
378 
380 {
381  KSycoca *s = ksycocaInstance()->sycoca();
382  Q_ASSERT(s);
383  return s;
384 }
385 
386 KSycoca::~KSycoca()
387 {
388  d->closeDatabase();
389  delete d;
390  // if (ksycocaInstance.exists()
391  // && ksycocaInstance->self == this)
392  // ksycocaInstance->self = 0;
393 }
394 
395 bool KSycoca::isAvailable() // TODO KF6: make it non-static (mostly useful for unittests)
396 {
397  return self()->d->checkDatabase(KSycocaPrivate::IfNotFoundDoNothing);
398 }
399 
400 void KSycocaPrivate::closeDatabase()
401 {
402  delete m_device;
403  m_device = nullptr;
404 
405  // It is very important to delete all factories here
406  // since they cache information about the database file
407  // But other threads might be using them, so this class is
408  // refcounted, and deleted when the last thread is done with them
409  qDeleteAll(m_factories);
410  m_factories.clear();
411 
412  m_mimeTypeFactory = nullptr;
413  m_serviceFactory = nullptr;
414  m_serviceTypeFactory = nullptr;
415  m_serviceGroupFactory = nullptr;
416 
417 #if HAVE_MMAP
418  if (sycoca_mmap) {
419  // Solaris has munmap(char*, size_t) and everything else should
420  // be happy with a char* for munmap(void*, size_t)
421  munmap(const_cast<char *>(sycoca_mmap), sycoca_size);
422  sycoca_mmap = nullptr;
423  }
424  delete m_mmapFile;
425  m_mmapFile = nullptr;
426 #endif
427 
428  databaseStatus = DatabaseNotOpen;
429  m_databasePath.clear();
430  timeStamp = 0;
431 }
432 
433 void KSycoca::addFactory(KSycocaFactory *factory)
434 {
435  d->addFactory(factory);
436 }
437 
438 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 0)
439 bool KSycoca::isChanged(const char *type)
440 {
441  return self()->d->changeList.contains(QString::fromLatin1(type));
442 }
443 #endif
444 
446 {
447  QDataStream *str = stream();
448  Q_ASSERT(str);
449  // qCDebug(SYCOCA) << QString("KSycoca::_findEntry(offset=%1)").arg(offset,8,16);
450  str->device()->seek(offset);
451  qint32 aType;
452  *str >> aType;
453  type = KSycocaType(aType);
454  // qCDebug(SYCOCA) << QString("KSycoca::found type %1").arg(aType);
455  return str;
456 }
457 
458 KSycocaFactoryList *KSycoca::factories()
459 {
460  return d->factories();
461 }
462 
463 // Warning, checkVersion rewinds to the beginning of stream().
464 bool KSycocaPrivate::checkVersion()
465 {
466  QDataStream *m_str = device()->stream();
467  Q_ASSERT(m_str);
468  m_str->device()->seek(0);
469  qint32 aVersion;
470  *m_str >> aVersion;
471  if (aVersion < KSYCOCA_VERSION) {
472  qCDebug(SYCOCA) << "Found version" << aVersion << ", expecting version" << KSYCOCA_VERSION << "or higher.";
473  databaseStatus = BadVersion;
474  return false;
475  } else {
476  databaseStatus = DatabaseOK;
477  return true;
478  }
479 }
480 
481 // This is now completely useless. KF6: remove
482 extern KSERVICE_EXPORT bool kservice_require_kded;
483 KSERVICE_EXPORT bool kservice_require_kded = true;
484 
485 // If it returns true, we have a valid database and the stream has rewinded to the beginning
486 // and past the version number.
487 bool KSycocaPrivate::checkDatabase(BehaviorsIfNotFound ifNotFound)
488 {
489  if (databaseStatus == DatabaseOK) {
490  if (checkVersion()) { // we know the version is ok, but we must rewind the stream anyway
491  return true;
492  }
493  }
494 
495  closeDatabase(); // close the dummy one
496 
497  // Check if new database already available
498  if (openDatabase()) {
499  // Database exists, and version is ok, we can read it.
500 
501  if (qAppName() != QLatin1String(KBUILDSYCOCA_EXENAME) && ifNotFound != IfNotFoundDoNothing) {
502  // Ensure it's up-to-date, rebuild if needed
503  checkDirectories();
504 
505  // Don't check again for some time
506  m_lastCheck.start();
507  }
508 
509  return true;
510  }
511 
512  if (ifNotFound & IfNotFoundRecreate) {
513  return buildSycoca();
514  }
515 
516  return false;
517 }
518 
520 {
521  // Ensure we have a valid database (right version, and rewinded to beginning)
522  if (!d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate)) {
523  return nullptr;
524  }
525 
526  QDataStream *str = stream();
527  Q_ASSERT(str);
528 
529  qint32 aId;
530  qint32 aOffset;
531  while (true) {
532  *str >> aId;
533  if (aId == 0) {
534  qCWarning(SYCOCA) << "Error, KSycocaFactory (id =" << int(id) << ") not found!";
535  break;
536  }
537  *str >> aOffset;
538  if (aId == id) {
539  // qCDebug(SYCOCA) << "KSycoca::findFactory(" << id << ") offset " << aOffset;
540  str->device()->seek(aOffset);
541  return str;
542  }
543  }
544  return nullptr;
545 }
546 
547 bool KSycoca::needsRebuild()
548 {
549  return d->needsRebuild();
550 }
551 
552 KSycocaHeader KSycocaPrivate::readSycocaHeader()
553 {
554  KSycocaHeader header;
555  // do not try to launch kbuildsycoca from here; this code is also called by kbuildsycoca.
556  if (!checkDatabase(KSycocaPrivate::IfNotFoundDoNothing)) {
557  return header;
558  }
559  QDataStream *str = stream();
560  qint64 oldPos = str->device()->pos();
561 
562  Q_ASSERT(str);
563  qint32 aId;
564  qint32 aOffset;
565  // skip factories offsets
566  while (true) {
567  *str >> aId;
568  if (aId) {
569  *str >> aOffset;
570  } else {
571  break; // just read 0
572  }
573  }
574  // We now point to the header
575  QStringList directoryList;
576  *str >> header >> directoryList;
577  allResourceDirs.clear();
578  for (int i = 0; i < directoryList.count(); ++i) {
579  qint64 mtime;
580  *str >> mtime;
581  allResourceDirs.insert(directoryList.at(i), mtime);
582  }
583 
584  QStringList fileList;
585  *str >> fileList;
586  extraFiles.clear();
587  for (const auto &fileName : std::as_const(fileList)) {
588  qint64 mtime;
589  *str >> mtime;
590  extraFiles.insert(fileName, mtime);
591  }
592 
593  str->device()->seek(oldPos);
594 
595  timeStamp = header.timeStamp;
596 
597  // for the useless public accessors. KF6: remove these two lines, the accessors and the vars.
598  language = header.language;
599  updateSig = header.updateSignature;
600 
601  return header;
602 }
603 
604 class TimestampChecker
605 {
606 public:
607  TimestampChecker()
608  : m_now(QDateTime::currentDateTime())
609  {
610  }
611 
612  // Check times of last modification of all directories on which ksycoca depends,
613  // If none of them is newer than the mtime we stored for that directory at the
614  // last rebuild, this means that there's no need to rebuild ksycoca.
615  bool checkDirectoriesTimestamps(const QMap<QString, qint64> &dirs) const
616  {
617  Q_ASSERT(!dirs.isEmpty());
618  // qCDebug(SYCOCA) << "checking file timestamps";
619  for (auto it = dirs.begin(); it != dirs.end(); ++it) {
620  const QString dir = it.key();
621  const qint64 lastStamp = it.value();
622 
623  auto visitor = [&](const QFileInfo &fi) {
624  const QDateTime mtime = fi.lastModified();
625  if (mtime.toMSecsSinceEpoch() > lastStamp) {
626  if (mtime > m_now) {
627  qCDebug(SYCOCA) << fi.filePath() << "has a modification time in the future" << mtime;
628  }
629  qCDebug(SYCOCA) << "dir timestamp changed:" << fi.filePath() << mtime << ">" << QDateTime::fromMSecsSinceEpoch(lastStamp);
630  // no need to continue search
631  return false;
632  }
633 
634  return true;
635  };
636 
637  if (!KSycocaUtilsPrivate::visitResourceDirectory(dir, visitor)) {
638  return false;
639  }
640  }
641  return true;
642  }
643 
644  bool checkFilesTimestamps(const QMap<QString, qint64> &files) const
645  {
646  for (auto it = files.begin(); it != files.end(); ++it) {
647  const QString fileName = it.key();
648  const qint64 lastStamp = it.value();
649 
650  QFileInfo fi(fileName);
651  if (!fi.exists()) {
652  return false;
653  }
654  const QDateTime mtime = fi.lastModified();
655  if (mtime.toMSecsSinceEpoch() > lastStamp) {
656  if (mtime > m_now) {
657  qCDebug(SYCOCA) << fi.filePath() << "has a modification time in the future" << mtime;
658  }
659  qCDebug(SYCOCA) << "file timestamp changed:" << fi.filePath() << mtime << ">" << QDateTime::fromMSecsSinceEpoch(lastStamp);
660  return false;
661  }
662  }
663  return true;
664  }
665 
666 private:
667  QDateTime m_now;
668 };
669 
670 void KSycocaPrivate::checkDirectories()
671 {
672  if (needsRebuild()) {
673  buildSycoca();
674  }
675 }
676 
677 bool KSycocaPrivate::needsRebuild()
678 {
679  if (!timeStamp && databaseStatus == DatabaseOK) {
680  (void)readSycocaHeader();
681  }
682  // these days timeStamp is really a "bool headerFound", the value itself doesn't matter...
683  // KF6: replace it with bool.
684  const auto timestampChecker = TimestampChecker();
685  bool ret = timeStamp != 0
686  && (!timestampChecker.checkDirectoriesTimestamps(allResourceDirs) //
687  || !timestampChecker.checkFilesTimestamps(extraFiles));
688  if (ret) {
689  return true;
690  }
691  auto files = KBuildSycoca::factoryExtraFiles();
692  // ensure files are ordered so next comparison works
693  files.sort();
694  // to cover cases when extra files were added
695  return extraFiles.keys() != files;
696 }
697 
698 bool KSycocaPrivate::buildSycoca()
699 {
700  KBuildSycoca builder;
701  if (!builder.recreate()) {
702  return false; // error
703  }
704 
705  closeDatabase(); // close the dummy one
706 
707  // Ok, the new database should be here now, open it.
708  if (!openDatabase()) {
709  qCDebug(SYCOCA) << "Still no database...";
710  return false;
711  }
712  return true;
713 }
714 
715 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 15)
717 {
718  if (!d->timeStamp) {
719  (void)d->readSycocaHeader();
720  }
721  return d->timeStamp / 1000; // from ms to s
722 }
723 #endif
724 
725 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 15)
727 {
728  if (!d->timeStamp) {
729  (void)d->readSycocaHeader();
730  }
731  return d->updateSig;
732 }
733 #endif
734 
736 {
737  Q_UNUSED(type); // GlobalDatabase concept removed in 5.61
739  QString suffix = QLatin1Char('_') + QLocale().bcp47Name();
740 
741  const QByteArray ksycoca_env = qgetenv("KDESYCOCA");
742  if (ksycoca_env.isEmpty()) {
743  const QByteArray pathHash = QCryptographicHash::hash(paths.join(QLatin1Char(':')).toUtf8(), QCryptographicHash::Sha1);
744  suffix += QLatin1Char('_') + QString::fromLatin1(pathHash.toBase64());
745  suffix.replace(QLatin1Char('/'), QLatin1Char('_'));
746 #ifdef Q_OS_WIN
747  suffix.replace(QLatin1Char(':'), QLatin1Char('_'));
748 #endif
749  const QString fileName = QLatin1String("ksycoca5") + suffix;
751  } else {
752  return QFile::decodeName(ksycoca_env);
753  }
754 }
755 
756 #if KSERVICE_BUILD_DEPRECATED_SINCE(5, 15)
758 {
759  if (d->language.isEmpty()) {
760  (void)d->readSycocaHeader();
761  }
762  return d->language;
763 }
764 #endif
765 
767 {
768  if (!d->timeStamp) {
769  (void)d->readSycocaHeader();
770  }
771  return d->allResourceDirs.keys();
772 }
773 
775 {
776  qCWarning(SYCOCA) << "ERROR: KSycoca database corruption!";
777  KSycoca *sycoca = self();
778  if (sycoca->d->readError) {
779  return;
780  }
781  sycoca->d->readError = true;
782  if (qAppName() != QLatin1String(KBUILDSYCOCA_EXENAME) && !sycoca->isBuilding()) {
783  // Rebuild the damned thing.
784  KBuildSycoca builder;
785  (void)builder.recreate();
786  }
787 }
788 
790 {
791  return false;
792 }
793 
795 {
796  ksycocaInstance->sycoca()->d->m_fileWatcher = nullptr;
797 }
798 
799 QDataStream *&KSycoca::stream()
800 {
801  return d->stream();
802 }
803 
804 void KSycoca::connectNotify(const QMetaMethod &signal)
805 {
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);
812  }
813  }
814 }
815 
816 void KSycoca::clearCaches()
817 {
818  if (ksycocaInstance.exists() && ksycocaInstance()->hasSycoca()) {
819  ksycocaInstance()->sycoca()->d->closeDatabase();
820  }
821 }
822 
823 extern KSERVICE_EXPORT int ksycoca_ms_between_checks;
824 KSERVICE_EXPORT int ksycoca_ms_between_checks = 1500;
825 
827 {
828  if (qAppName() == QLatin1String(KBUILDSYCOCA_EXENAME)) {
829  return;
830  }
831 
832  if (d->databaseStatus != KSycocaPrivate::DatabaseOK) {
833  if (!d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate)) {
834  return;
835  }
836  }
837 
838  if (d->m_lastCheck.isValid() && d->m_lastCheck.elapsed() < ksycoca_ms_between_checks) {
839  return;
840  }
841  d->m_lastCheck.start();
842 
843  // Check if the file on disk was modified since we last checked it.
844  QFileInfo info(d->m_databasePath);
845  if (info.lastModified() == d->m_dbLastModified) {
846  // Check if the watched directories were modified, then the cache needs a rebuild.
847  d->checkDirectories();
848  return;
849  }
850 
851  // Close the database and forget all about what we knew.
852  // The next call to any public method will recreate
853  // everything that's needed.
854  d->closeDatabase();
855 }
static KSycoca * self()
Get or create the only instance of KSycoca (read-only)
Definition: ksycoca.cpp:379
QString language()
Definition: ksycoca.cpp:757
void ensureCacheValid()
Ensures the ksycoca database is up to date.
Definition: ksycoca.cpp:826
static void disableAutoRebuild()
Disables automatic rebuilding of the cache on service file changes.
Definition: ksycoca.cpp:794
QMap::iterator begin()
QDateTime fromMSecsSinceEpoch(qint64 msecs)
QIODevice * device() const const
static bool isAvailable()
Definition: ksycoca.cpp:395
QString bcp47Name() const const
QByteArray name() const const
QString writableLocation(QStandardPaths::StandardLocation type)
NETWORKMANAGERQT_EXPORT bool checkVersion(const int x, const int y, const int z)
static void flagError()
A read error occurs.
Definition: ksycoca.cpp:774
virtual bool seek(qint64 pos)
QByteArray toBase64(QByteArray::Base64Options options) const const
static int version()
Definition: ksycoca.cpp:154
DatabaseType
type of database
Definition: ksycoca.h:50
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
QMap::iterator end()
QStringList standardLocations(QStandardPaths::StandardLocation type)
KSycoca()
Read-only database.
Definition: ksycoca.cpp:212
virtual qint64 pos() const const
bool isEmpty() const const
quint32 timeStamp()
Definition: ksycoca.cpp:716
QThread * currentThread()
qint64 toMSecsSinceEpoch() const const
static bool isChanged(const char *type)
When you receive a "databaseChanged" signal, you can query here if a change has occurred in a specifi...
Definition: ksycoca.cpp:439
QString join(const QString &separator) const const
KSharedConfigPtr config()
QString & replace(int position, int n, QChar after)
quint32 updateSignature()
Definition: ksycoca.cpp:726
QStringList allResourceDirs()
Definition: ksycoca.cpp:766
QByteArray hash(const QByteArray &data, QCryptographicHash::Algorithm method)
QDataStream * findEntry(int offset, KSycocaType &type)
Definition: ksycoca.cpp:445
bool isEmpty() const const
KIOFILEWIDGETS_EXPORT QString dir(const QString &fileClass)
QList< Key > keys() const const
KCOREADDONS_EXPORT bool isFlatpak()
QString path(const QString &relativePath)
QString fromLatin1(const char *str, int size)
KSycocaFactoryId
Definition: ksycocatype.h:39
QDataStream * findFactory(KSycocaFactoryId id)
Definition: ksycoca.cpp:519
KStandardDirs * dirs()
void dirty(const QString &path)
void clear()
void created(const QString &path)
virtual bool isBuilding()
Definition: ksycoca.cpp:789
static QString absoluteFilePath(DatabaseType type=LocalDatabase)
Definition: ksycoca.cpp:735
QDateTime lastModified() const const
QDataStream & operator>>(QDataStream &in, KDateTime &dateTime)
void addFactory(KSycocaFactory *)
Definition: ksycoca.cpp:433
QString decodeName(const QByteArray &localFileName)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 04:00:21 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.