Baloo

statuscommand.cpp
1 /*
2  This file is part of the KDE Baloo project.
3  SPDX-FileCopyrightText: 2015 Vishesh Handa <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7 
8 #include "statuscommand.h"
9 #include "indexerconfig.h"
10 
11 #include "global.h"
12 #include "database.h"
13 #include "transaction.h"
14 #include "idutils.h"
15 
16 #include "fileindexerinterface.h"
17 #include "schedulerinterface.h"
18 #include "maininterface.h"
19 #include "indexerstate.h"
20 
21 #include <KLocalizedString>
22 #include <KFormat>
23 
24 using namespace Baloo;
25 
26 QString StatusCommand::command()
27 {
28  return QStringLiteral("status");
29 }
30 
31 QString StatusCommand::description()
32 {
33  return i18n("Print the status of the Indexer");
34 }
35 
36 class FileIndexStatus
37 {
38 public:
39  enum class FileStatus : uint8_t {
40  NonExisting,
41  Directory,
42  RegularFile,
43  SymLink,
44  Other,
45  };
46  enum class IndexStateReason : uint8_t {
47  NoFileOrDirectory,
48  ExcludedByPath, // FIXME - be more specific, requires changes to shouldBeIndexed(path)
49  WaitingForIndexingBoth,
50  WaitingForBasicIndexing,
51  BasicIndexingDone,
52  WaitingForContentIndexing,
53  FailedToIndex,
54  Done,
55  };
56  const QString m_filePath;
57  FileStatus m_fileStatus;
58  IndexStateReason m_indexState;
59  uint32_t m_dataSize;
60 };
61 
62 FileIndexStatus collectFileStatus(Transaction& tr, IndexerConfig& cfg, const QString& file)
63 {
64  using FileStatus = FileIndexStatus::FileStatus;
65  using IndexStateReason = FileIndexStatus::IndexStateReason;
66 
67  bool onlyBasicIndexing = cfg.onlyBasicIndexing();
68 
69  const QFileInfo fileInfo = QFileInfo(file);
70  const QString filePath = fileInfo.absoluteFilePath();
71  quint64 id = filePathToId(QFile::encodeName(filePath));
72  if (id == 0) {
73  return FileIndexStatus{filePath, FileStatus::NonExisting, IndexStateReason::NoFileOrDirectory, 0};
74  }
75 
76  FileStatus fileStatus = fileInfo.isSymLink() ? FileStatus::SymLink :
77  fileInfo.isFile() ? FileStatus::RegularFile :
78  fileInfo.isDir() ? FileStatus::Directory : FileStatus::Other;
79 
80  if (fileStatus == FileStatus::Other || fileStatus == FileStatus::SymLink) {
81  return FileIndexStatus{filePath, fileStatus, IndexStateReason::NoFileOrDirectory, 0};
82  }
83 
84  if (!cfg.shouldBeIndexed(filePath)) {
85  return FileIndexStatus{filePath, fileStatus, IndexStateReason::ExcludedByPath, 0};
86  }
87 
88  if (onlyBasicIndexing || fileStatus == FileStatus::Directory) {
89  if (!tr.hasDocument(id)) {
90  return FileIndexStatus{filePath, fileStatus, IndexStateReason::WaitingForBasicIndexing, 0};
91  } else {
92  return FileIndexStatus{filePath, fileStatus, IndexStateReason::BasicIndexingDone, 0};
93  }
94  }
95 
96  // File && shouldBeIndexed && contentIndexing
97  if (!tr.hasDocument(id)) {
98  return FileIndexStatus{filePath, fileStatus, IndexStateReason::WaitingForIndexingBoth, 0};
99  } else if (tr.inPhaseOne(id)) {
100  return FileIndexStatus{filePath, fileStatus, IndexStateReason::WaitingForContentIndexing, 0};
101  } else if (tr.hasFailed(id)) {
102  return FileIndexStatus{filePath, fileStatus, IndexStateReason::FailedToIndex, 0};
103  } else {
104  uint32_t size = tr.documentData(id).size();
105  return FileIndexStatus{filePath, fileStatus, IndexStateReason::Done, size};
106  }
107 }
108 
109 void printMultiLine(Transaction& tr, IndexerConfig& cfg, const QStringList& args) {
110  using FileStatus = FileIndexStatus::FileStatus;
111  using IndexStateReason = FileIndexStatus::IndexStateReason;
112 
113  QTextStream out(stdout);
114  QTextStream err(stderr);
115 
116  const QMap<IndexStateReason, QString> basicIndexStateValue = {
117  { IndexStateReason::NoFileOrDirectory, i18n("File ignored") },
118  { IndexStateReason::ExcludedByPath, i18n("Basic Indexing: Disabled") },
119  { IndexStateReason::WaitingForIndexingBoth, i18n("Basic Indexing: Scheduled") },
120  { IndexStateReason::WaitingForBasicIndexing, i18n("Basic Indexing: Scheduled") },
121  { IndexStateReason::BasicIndexingDone, i18n("Basic Indexing: Done") },
122  { IndexStateReason::WaitingForContentIndexing, i18n("Basic Indexing: Done") },
123  { IndexStateReason::FailedToIndex, i18n("Basic Indexing: Done") },
124  { IndexStateReason::Done, i18n("Basic Indexing: Done") },
125  };
126 
127  const QMap<IndexStateReason, QString> contentIndexStateValue = {
128  { IndexStateReason::NoFileOrDirectory, QString() },
129  { IndexStateReason::ExcludedByPath, QString() },
130  { IndexStateReason::WaitingForIndexingBoth, i18n("Content Indexing: Scheduled") },
131  { IndexStateReason::WaitingForBasicIndexing, QString() },
132  { IndexStateReason::BasicIndexingDone, QString() },
133  { IndexStateReason::WaitingForContentIndexing, i18n("Content Indexing: Scheduled") },
134  { IndexStateReason::FailedToIndex, i18n("Content Indexing: Failed") },
135  { IndexStateReason::Done, i18n("Content Indexing: Done") },
136  };
137 
138  for (const auto& fileName : args) {
139  const auto file = collectFileStatus(tr, cfg, fileName);
140 
141  if (file.m_fileStatus == FileStatus::NonExisting) {
142  err << i18n("Ignoring non-existent file %1", file.m_filePath) << '\n';
143  continue;
144  }
145 
146  if (file.m_fileStatus == FileStatus::SymLink || file.m_fileStatus == FileStatus::Other) {
147  err << i18n("Ignoring symlink/special file %1", file.m_filePath) << '\n';
148  continue;
149  }
150 
151  out << i18n("File: %1", file.m_filePath) << '\n';
152  out << basicIndexStateValue[file.m_indexState] << '\n';
153  const QString contentState = contentIndexStateValue[file.m_indexState];
154  if (!contentState.isEmpty()) {
155  out << contentState << '\n';
156  }
157  }
158 }
159 
160 void printSimpleFormat(Transaction& tr, IndexerConfig& cfg, const QStringList& args) {
161  using FileStatus = FileIndexStatus::FileStatus;
162  using IndexStateReason = FileIndexStatus::IndexStateReason;
163 
164  QTextStream out(stdout);
165  QTextStream err(stderr);
166 
167  const QMap<IndexStateReason, QString> simpleIndexStateValue = {
168  { IndexStateReason::NoFileOrDirectory, QStringLiteral("No regular file or directory") },
169  { IndexStateReason::ExcludedByPath, QStringLiteral("Indexing disabled") },
170  { IndexStateReason::WaitingForIndexingBoth, QStringLiteral("Basic and Content indexing scheduled") },
171  { IndexStateReason::WaitingForBasicIndexing, QStringLiteral("Basic indexing scheduled") },
172  { IndexStateReason::BasicIndexingDone, QStringLiteral("Basic indexing done") },
173  { IndexStateReason::WaitingForContentIndexing, QStringLiteral("Content indexing scheduled") },
174  { IndexStateReason::FailedToIndex, QStringLiteral("Content indexing failed") },
175  { IndexStateReason::Done, QStringLiteral("Content indexing done") },
176  };
177 
178  for (const auto& fileName : args) {
179  const auto file = collectFileStatus(tr, cfg, fileName);
180 
181  if (file.m_fileStatus == FileStatus::NonExisting) {
182  err << i18n("Ignoring non-existent file %1", file.m_filePath) << '\n';
183  continue;
184  }
185 
186  if (file.m_fileStatus == FileStatus::SymLink || file.m_fileStatus == FileStatus::Other) {
187  err << i18n("Ignoring symlink/special file %1", file.m_filePath) << '\n';
188  continue;
189  }
190 
191  out << simpleIndexStateValue[file.m_indexState];
192  out << ": " << file.m_filePath << '\n';
193  }
194 }
195 
196 void printJSON(Transaction& tr, IndexerConfig& cfg, const QStringList& args)
197 {
198  using FileStatus = FileIndexStatus::FileStatus;
199  using IndexStateReason = FileIndexStatus::IndexStateReason;
200 
201  QJsonArray filesInfo;
202  QTextStream err(stderr);
203 
204  const QMap<IndexStateReason, QString> jsonIndexStateValue = {
205  { IndexStateReason::NoFileOrDirectory, QStringLiteral("nofile") },
206  { IndexStateReason::ExcludedByPath, QStringLiteral("disabled") },
207  { IndexStateReason::WaitingForIndexingBoth, QStringLiteral("scheduled") },
208  { IndexStateReason::WaitingForBasicIndexing, QStringLiteral("scheduled") },
209  { IndexStateReason::BasicIndexingDone, QStringLiteral("done") },
210  { IndexStateReason::WaitingForContentIndexing, QStringLiteral("scheduled") },
211  { IndexStateReason::FailedToIndex, QStringLiteral("failed") },
212  { IndexStateReason::Done, QStringLiteral("done") },
213  };
214 
215  const QMap<IndexStateReason, QString> jsonIndexLevelValue = {
216  { IndexStateReason::NoFileOrDirectory, QStringLiteral("nofile") },
217  { IndexStateReason::ExcludedByPath, QStringLiteral("none") },
218  { IndexStateReason::WaitingForIndexingBoth, QStringLiteral("content") },
219  { IndexStateReason::WaitingForBasicIndexing, QStringLiteral("basic") },
220  { IndexStateReason::BasicIndexingDone, QStringLiteral("basic") },
221  { IndexStateReason::WaitingForContentIndexing, QStringLiteral("content") },
222  { IndexStateReason::FailedToIndex, QStringLiteral("content") },
223  { IndexStateReason::Done, QStringLiteral("content") },
224  };
225 
226  for (const auto& fileName : args) {
227  const auto file = collectFileStatus(tr, cfg, fileName);
228 
229  if (file.m_fileStatus == FileStatus::NonExisting) {
230  err << i18n("Ignoring non-existent file %1", file.m_filePath) << '\n';
231  continue;
232  }
233 
234  if (file.m_fileStatus == FileStatus::SymLink || file.m_fileStatus == FileStatus::Other) {
235  err << i18n("Ignoring symlink/special file %1", file.m_filePath) << '\n';
236  continue;
237  }
238 
239  QJsonObject fileInfo;
240  fileInfo[QStringLiteral("file")] = file.m_filePath;
241  fileInfo[QStringLiteral("indexing")] = jsonIndexLevelValue[file.m_indexState];
242  fileInfo[QStringLiteral("status")] = jsonIndexStateValue[file.m_indexState];
243 
244  filesInfo.append(fileInfo);
245  }
246 
247  QJsonDocument json;
248  json.setArray(filesInfo);
249  QTextStream out(stdout);
250  out << json.toJson(QJsonDocument::Indented);
251 }
252 
253 int StatusCommand::exec(const QCommandLineParser& parser)
254 {
255  QTextStream out(stdout);
256  QTextStream err(stderr);
257 
258  const QStringList allowedFormats({QStringLiteral("simple"), QStringLiteral("json"), QStringLiteral("multiline")});
259  const QString format = parser.value(QStringLiteral("format"));
260 
261  if (!allowedFormats.contains(format)) {
262  err << i18n("Output format \"%1\" is invalid, use one of:\n", format);
263  for (const auto& format : allowedFormats) {
264  err << i18nc("bullet list item with output format", "- %1\n", format);
265  }
266  return 1;
267  }
268 
269  IndexerConfig cfg;
270  if (!cfg.fileIndexingEnabled()) {
271  err << i18n("Baloo is currently disabled. To enable, please run %1\n", QStringLiteral("balooctl enable"));
272  return 1;
273  }
274 
275  Database *db = globalDatabaseInstance();
276  if (!db->open(Database::ReadOnlyDatabase)) {
277  err << i18n("Baloo Index could not be opened\n");
278  return 1;
279  }
280 
281  Transaction tr(db, Transaction::ReadOnly);
282 
283  QStringList args = parser.positionalArguments();
284  args.pop_front();
285 
286  if (args.isEmpty()) {
287  org::kde::baloo::main mainInterface(QStringLiteral("org.kde.baloo"),
288  QStringLiteral("/"),
290 
291  org::kde::baloo::scheduler schedulerinterface(QStringLiteral("org.kde.baloo"),
292  QStringLiteral("/scheduler"),
294 
295  bool running = mainInterface.isValid();
296 
297  if (running) {
298  org::kde::baloo::fileindexer indexerInterface(QStringLiteral("org.kde.baloo"),
299  QStringLiteral("/fileindexer"),
301 
302  const QString currentFile = indexerInterface.currentFile();
303 
304  out << i18n("Baloo File Indexer is running\n");
305  if (!currentFile.isEmpty()) {
306  out << i18n("Indexer state: %1", stateString(IndexerState::ContentIndexing)) << '\n';
307  out << i18nc("currently indexed file", "Indexing: %1", currentFile) << '\n';
308  } else {
309  out << i18n("Indexer state: %1", stateString(schedulerinterface.state())) << '\n';
310  }
311  }
312  else {
313  out << i18n("Baloo File Indexer is not running\n");
314  }
315 
316  uint phaseOne = tr.phaseOneSize();
317  uint total = tr.size();
318  uint failed = tr.failedIds(100).size();
319 
320  out << i18n("Total files indexed: %1", total) << '\n';
321  out << i18n("Files waiting for content indexing: %1", phaseOne) << '\n';
322  out << i18n("Files failed to index: %1", failed) << '\n';
323 
324  const QString path = fileIndexDbPath();
325 
326  const QFileInfo indexInfo(path + QLatin1String("/index"));
327  const auto size = indexInfo.size();
328  KFormat format(QLocale::system());
329  if (size) {
330  out << i18n("Current size of index is %1", format.formatByteSize(size, 2)) << '\n';
331  } else {
332  out << i18n("Index does not exist yet\n");
333  }
334  } else if (format == allowedFormats[0]){
335  printSimpleFormat(tr, cfg, args);
336  } else if (format == allowedFormats[1]){
337  printJSON(tr, cfg, args);
338  } else {
339  printMultiLine(tr, cfg, args);
340  }
341 
342  return 0;
343 }
bool isDir() const const
QByteArray encodeName(const QString &fileName)
void setArray(const QJsonArray &array)
QStringList positionalArguments() const const
bool isFile() const const
bool shouldBeIndexed(const QString &path) const
Check if the file or folder path should be indexed.
QLocale system()
QString i18n(const char *text, const TYPE &arg...)
QDBusConnection sessionBus()
QString absoluteFilePath() const const
Implements storage for docIds without any associated data Instantiated for:
Definition: coding.cpp:11
void pop_front()
bool isEmpty() const const
QString value(const QString &optionName) const const
bool isEmpty() const const
QString path(const QString &relativePath)
QByteArray toJson() const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void append(const QJsonValue &value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Nov 29 2023 03:56:26 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.