1/*
2 SPDX-FileCopyrightText: 2015 Vishesh Handa <vhanda@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7#include "baloodebug.h"
8#include "config.h"
9#include "filecontentindexer.h"
10#include "filecontentindexerprovider.h"
11#include "extractorprocess.h"
12
13#include <QEventLoop>
14#include <QElapsedTimer>
15#include <QDBusConnection>
16
17using namespace Baloo;
18
19namespace {
20 // TODO KF6 -- remove/combine with started/finished DBus signal
21 void sendChangedSignal(const QStringList& updatedFiles)
22 {
23 auto message = QDBusMessage::createSignal(QStringLiteral("/files"),
24 QStringLiteral("org.kde"),
25 QStringLiteral("changed"));
26 message.setArguments({updatedFiles});
27 QDBusConnection::sessionBus().send(message);
28 }
29}
30
31FileContentIndexer::FileContentIndexer(uint batchSize,
32 FileContentIndexerProvider* provider,
33 uint& finishedCount, QObject* parent)
34 : QObject(parent)
35 , m_batchSize(batchSize)
36 , m_provider(provider)
37 , m_finishedCount(finishedCount)
38 , m_stop(0)
39 , m_extractorPath(QStringLiteral(KDE_INSTALL_FULL_LIBEXECDIR_KF "/baloo_file_extractor"))
40{
41 Q_ASSERT(provider);
42
43 QDBusConnection bus = QDBusConnection::sessionBus();
44 m_monitorWatcher.setConnection(bus);
45 m_monitorWatcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
46 connect(sender: &m_monitorWatcher, signal: &QDBusServiceWatcher::serviceUnregistered, context: this,
47 slot: &FileContentIndexer::monitorClosed);
48
49 bus.registerObject(QStringLiteral("/fileindexer"),
50 object: this, options: QDBusConnection::ExportScriptableContents);
51}
52
53void FileContentIndexer::run()
54{
55 ExtractorProcess process{m_extractorPath};
56 connect(sender: &process, signal: &ExtractorProcess::startedIndexingFile, context: this, slot: &FileContentIndexer::slotStartedIndexingFile);
57 connect(sender: &process, signal: &ExtractorProcess::finishedIndexingFile, context: this, slot: &FileContentIndexer::slotFinishedIndexingFile);
58 m_stop.storeRelaxed(newValue: false);
59 auto batchSize = m_batchSize;
60 while (true) {
61 //
62 // WARNING: This will go mad, if the Extractor does not commit after N=m_batchSize files
63 // cause then we will keep fetching the same N files again and again.
64 //
65 QVector<quint64> idList = m_provider->fetch(size: batchSize);
66 if (idList.isEmpty() || m_stop.loadRelaxed()) {
67 break;
68 }
69 QEventLoop loop;
70 connect(sender: &process, signal: &ExtractorProcess::done, context: &loop, slot: &QEventLoop::quit);
71
72 bool hadErrors = false;
73 connect(sender: &process, signal: &ExtractorProcess::failed, context: &loop, slot: [&hadErrors, &loop]() { hadErrors = true; loop.quit(); });
74
75 uint batchStartCount = m_finishedCount;
76 connect(sender: &process, signal: &ExtractorProcess::finishedIndexingFile, context: &loop, slot: [this]() { m_finishedCount++; });
77
78 QElapsedTimer timer;
79 timer.start();
80
81 process.index(fileIds: idList);
82 loop.exec();
83 batchSize = idList.size();
84
85 if (hadErrors && !m_stop.loadRelaxed()) {
86 if (batchSize == 1) {
87 auto failedId = idList.first();
88 auto ok = m_provider->markFailed(id: failedId);
89 if (!ok) {
90 qCCritical(BALOO) << "Not able to commit to DB, DB likely is in a bad state. Exiting";
91 exit(status: 1);
92 }
93 batchSize = m_batchSize;
94 } else {
95 batchSize /= 2;
96 }
97 m_updatedFiles.clear();
98 // reset to old value - nothing committed
99 m_finishedCount = batchStartCount;
100 process.start();
101 } else {
102 // Notify some metadata may have changed
103 sendChangedSignal(updatedFiles: m_updatedFiles);
104 m_updatedFiles.clear();
105
106 // Update remaining time estimate
107 auto elapsed = timer.elapsed();
108 QMetaObject::invokeMethod(object: this,
109 function: [this, elapsed, batchSize] { committedBatch(time: elapsed, batchSize); },
110 type: Qt::QueuedConnection);
111 }
112 }
113 QMetaObject::invokeMethod(object: this, function: &FileContentIndexer::done, type: Qt::QueuedConnection);
114}
115
116void FileContentIndexer::slotStartedIndexingFile(const QString& filePath)
117{
118 m_currentFile = filePath;
119 if (!m_registeredMonitors.isEmpty()) {
120 Q_EMIT startedIndexingFile(filePath);
121 }
122}
123
124void FileContentIndexer::slotFinishedIndexingFile(const QString& filePath, bool fileUpdated)
125{
126 if (fileUpdated) {
127 m_updatedFiles.append(t: filePath);
128 }
129
130 m_currentFile = QString();
131 if (!m_registeredMonitors.isEmpty()) {
132 Q_EMIT finishedIndexingFile(filePath);
133 }
134}
135
136void FileContentIndexer::registerMonitor(const QDBusMessage& message)
137{
138 if (!m_registeredMonitors.contains(str: message.service())) {
139 m_registeredMonitors << message.service();
140 m_monitorWatcher.addWatchedService(newService: message.service());
141 }
142}
143
144void FileContentIndexer::unregisterMonitor(const QDBusMessage& message)
145{
146 m_registeredMonitors.removeAll(t: message.service());
147 m_monitorWatcher.removeWatchedService(service: message.service());
148}
149
150void FileContentIndexer::monitorClosed(const QString& service)
151{
152 m_registeredMonitors.removeAll(t: service);
153 m_monitorWatcher.removeWatchedService(service);
154}
155
156#include "moc_filecontentindexer.cpp"
157

source code of baloo/src/file/filecontentindexer.cpp