Baloo

tools/balooctl/main.cpp
1 /*
2  SPDX-FileCopyrightText: 2012-2015 Vishesh Handa <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
5 */
6 
7 #include <QCoreApplication>
8 #include <QCommandLineParser>
9 #include <QFile>
10 
11 #include <KAboutData>
12 #include <KLocalizedString>
13 #include <KFormat>
14 #include <QProcess>
15 #include <QTextStream>
16 #include <QFileInfo>
17 #include <QLocale>
18 
19 #include <QDBusConnection>
20 #include <QDBusConnectionInterface>
21 
22 #include "global.h"
23 #include "database.h"
24 #include "transaction.h"
25 #include "databasesize.h"
26 #include "config.h"
27 
28 #include "indexer.h"
29 #include "indexerconfig.h"
30 #include "idutils.h"
31 #include "fileindexerconfig.h"
32 #include "monitorcommand.h"
33 #include "schedulerinterface.h"
34 #include "maininterface.h"
35 #include "indexerstate.h"
36 #include "configcommand.h"
37 #include "statuscommand.h"
38 
39 using namespace Baloo;
40 
41 void start()
42 {
43  const QString exe = QStringLiteral(KDE_INSTALL_FULL_LIBEXECDIR "/baloo_file");
45 }
46 
47 int main(int argc, char* argv[])
48 {
49  QCoreApplication app(argc, argv);
50 
51  KAboutData aboutData(QStringLiteral("baloo"), i18n("balooctl"), QStringLiteral(PROJECT_VERSION));
52  aboutData.addAuthor(i18n("Vishesh Handa"), QString(), QStringLiteral("[email protected]"));
53 
55 
56  QCommandLineParser parser;
57  parser.addPositionalArgument(QStringLiteral("command"), i18n("The command to execute"));
58 
59  parser.addPositionalArgument(QStringLiteral("status"), i18n("Print the status of the indexer"));
60  parser.addPositionalArgument(QStringLiteral("enable"), i18n("Enable the file indexer"));
61  parser.addPositionalArgument(QStringLiteral("disable"), i18n("Disable the file indexer"));
62  parser.addPositionalArgument(QStringLiteral("purge"), i18n("Remove the index database"));
63  parser.addPositionalArgument(QStringLiteral("suspend"), i18n("Suspend the file indexer"));
64  parser.addPositionalArgument(QStringLiteral("resume"), i18n("Resume the file indexer"));
65  parser.addPositionalArgument(QStringLiteral("check"), i18n("Check for any unindexed files and index them"));
66  parser.addPositionalArgument(QStringLiteral("index"), i18n("Index the specified files"));
67  parser.addPositionalArgument(QStringLiteral("clear"), i18n("Forget the specified files"));
68  parser.addPositionalArgument(QStringLiteral("config"), i18n("Modify the Baloo configuration"));
69  parser.addPositionalArgument(QStringLiteral("monitor"), i18n("Monitor the file indexer"));
70  parser.addPositionalArgument(QStringLiteral("indexSize"), i18n("Display the disk space used by index"));
71  parser.addPositionalArgument(QStringLiteral("failed"), i18n("Display files which could not be indexed"));
72 
73  QString statusFormatDescription = i18nc("Format to use for status command, %1|%2|%3 are option values, %4 is a CLI command",
74  "Output format <%1|%2|%3>.\nThe default format is \"%1\".\nOnly applies to \"%4\"",
75  QStringLiteral("multiline"),
76  QStringLiteral("json"),
77  QStringLiteral("simple"),
78  QStringLiteral("balooctl status <file>"));
79  parser.addOption({{QStringLiteral("f"), QStringLiteral("format")},
80  statusFormatDescription, i18n("format"), QStringLiteral("multiline")});
81 
82  parser.addVersionOption();
83  parser.addHelpOption();
84 
85  parser.process(app);
86  if (parser.positionalArguments().isEmpty()) {
87  parser.showHelp(1);
88  }
89 
90  QTextStream out(stdout);
91 
92  QString command = parser.positionalArguments().first();
93 
94  org::kde::baloo::main mainInterface(QStringLiteral("org.kde.baloo"),
95  QStringLiteral("/"),
97 
98  org::kde::baloo::scheduler schedulerinterface(QStringLiteral("org.kde.baloo"),
99  QStringLiteral("/scheduler"),
101 
102  if (command == QLatin1String("config")) {
103  ConfigCommand command;
104  return command.exec(parser);
105  }
106 
107  if (command == QLatin1String("status")) {
108  StatusCommand commandStatus;
109  return commandStatus.exec(parser);
110  }
111 
112  if (command == QLatin1String("enable") || command == QLatin1String("disable")) {
113  bool isEnabled = false;
114  if (command == QLatin1String("enable")) {
115  isEnabled = true;
116  }
117  else if (command == QLatin1String("disable")) {
118  isEnabled = false;
119  }
120 
121  IndexerConfig cfg;
122  cfg.setFileIndexingEnabled(isEnabled);
123 
124  if (isEnabled) {
125  bool running = mainInterface.isValid();
126  if (running) {
127  out << "File Indexer already running\n";
128  } else {
129  out << "Enabling and starting the File Indexer\n";
130  start();
131  }
132  } else {
133  out << "Disabling and stopping the File Indexer\n";
134 
135  mainInterface.quit();
136  }
137 
138  return 0;
139  }
140 
141  if (command == QLatin1String("purge")) {
142  bool running = mainInterface.isValid();
143 
144  if (running) {
145  mainInterface.quit();
146  out << "Stopping the File Indexer ...";
147  for (int i = 5 * 60; i; --i) {
149  if (!mainInterface.isValid()) {
150  break;
151  }
152  out << "." << Qt::flush;
153  QThread::msleep(200);
154  }
155  if (!mainInterface.isValid()) {
156  out << " - done\n";
157  } else {
158  out << " - failed to stop!\n";
159  return 1;
160  }
161  }
162 
163  const QString path = fileIndexDbPath() + QStringLiteral("/index");
164  QFile(path).remove();
165  out << "Deleted the index database\n";
166 
167  if (running) {
168  start();
169  out << "Restarting the File Indexer\n";
170  }
171 
172  return 0;
173  }
174 
175  if (command == QLatin1String("suspend")) {
176  schedulerinterface.suspend();
177  out << "File Indexer suspended\n";
178  return 0;
179  }
180 
181  if (command == QLatin1String("resume")) {
182  schedulerinterface.resume();
183  out << "File Indexer resumed\n";
184  return 0;
185  }
186 
187  if (command == QLatin1String("check")) {
188  schedulerinterface.checkUnindexedFiles();
189  out << "Started search for unindexed files\n";
190  return 0;
191  }
192 
193  if (command == QLatin1String("index")) {
194  if (parser.positionalArguments().size() < 2) {
195  out << "Please enter a filename to index\n";
196  return 1;
197  }
198 
199  Database *db = globalDatabaseInstance();
200  if (!db->open(Database::ReadWriteDatabase)) {
201  out << "Baloo Index could not be opened\n";
202  return 1;
203  }
204 
205  Transaction tr(db, Transaction::ReadWrite);
206 
207  for (int i = 1; i < parser.positionalArguments().size(); ++i) {
208  const QString url = QFileInfo(parser.positionalArguments().at(i)).absoluteFilePath();
209  quint64 id = filePathToId(QFile::encodeName(url));
210  if (id == 0) {
211  out << "Could not stat file: " << url << '\n';
212  continue;
213  }
214  if (tr.inPhaseOne(id)) {
215  out << "Skipping: " << url << " Reason: Already scheduled for indexing\n";
216  continue;
217  }
218  if (!tr.documentData(id).isEmpty()) {
219  out << "Skipping: " << url << " Reason: Already indexed\n";
220  continue;
221  }
222  Indexer indexer(url, &tr);
223  out << "Indexing " << url << '\n';
224  indexer.index();
225  }
226  tr.commit();
227  out << "File(s) indexed\n";
228 
229  return 0;
230  }
231 
232  if (command == QLatin1String("clear")) {
233  if (parser.positionalArguments().size() < 2) {
234  out << "Please enter a filename to index\n";
235  return 1;
236  }
237 
238  Database *db = globalDatabaseInstance();
239  if (!db->open(Database::ReadWriteDatabase)) {
240  out << "Baloo Index could not be opened\n";
241  return 1;
242  }
243 
244  Transaction tr(db, Transaction::ReadWrite);
245 
246  for (int i = 1; i < parser.positionalArguments().size(); ++i) {
247  const QString url = QFileInfo(parser.positionalArguments().at(i)).absoluteFilePath();
248  quint64 id = filePathToId(QFile::encodeName(url));
249  if (id == 0) {
250  id = tr.documentId(QFile::encodeName(url));
251  if (id == 0) {
252  out << "File not found on filesystem or in DB: " << url << '\n';
253  continue;
254  } else {
255  out << "File has been deleted, clearing from DB: " << url << '\n';
256  }
257  } else {
258  out << "Clearing " << url << '\n';
259  }
260 
261  tr.removeDocument(id);
262  }
263  tr.commit();
264  out << "File(s) cleared\n";
265 
266  return 0;
267  }
268 
269  if (command == QLatin1String("failed")) {
270  Database *db = globalDatabaseInstance();
271  if (!db->open(Database::ReadOnlyDatabase)) {
272  out << "Baloo Index could not be opened\n";
273  return 1;
274  }
275 
276  Transaction tr(db, Transaction::ReadOnly);
277 
278  const quint64 limit = 128;
279  const QVector<quint64> failedIds = tr.failedIds(limit);
280  if (failedIds.isEmpty()) {
281  out << "All Files were indexed successfully\n";
282  return 0;
283  }
284 
285  out << "The following files could not be indexed:\n";
286  for (auto id : failedIds) {
287  out << tr.documentUrl(id) << '\n';
288  }
289  if (failedIds.size() == limit) {
290  out << "... list truncated\n";
291  }
292  return 0;
293  }
294 
295  if (command == QLatin1String("indexSize")) {
296  Database *db = globalDatabaseInstance();
297  if (!db->open(Database::ReadOnlyDatabase)) {
298  out << "Baloo Index could not be opened\n";
299  return 1;
300  }
301 
302  DatabaseSize size;
303  {
304  Transaction tr(db, Transaction::ReadOnly);
305  size = tr.dbSize();
306  }
307  uint totalDataSize = size.expectedSize;
308 
309  KFormat format(QLocale::system());
310  auto prFunc = [&](const QString& name, uint size) {
311  out.setFieldWidth(20);
312  out << name;
313  out.setFieldWidth(0);
314  out << ":";
315  out.setFieldWidth(15);
316  out << format.formatByteSize(size, 2);
317  out.setFieldWidth(10);
318  out << QString::number((100.0 * size / totalDataSize), 'f', 3);
319  out.setFieldWidth(0);
320  out << " %\n";
321  };
322 
323  out << "File Size: " << format.formatByteSize(size.actualSize, 2) << "\n";
324  out << "Used: " << format.formatByteSize(totalDataSize, 2) << "\n\n";
325  prFunc(QStringLiteral("PostingDB"), size.postingDb);
326  prFunc(QStringLiteral("PositionDB"), size.positionDb);
327  prFunc(QStringLiteral("DocTerms"), size.docTerms);
328  prFunc(QStringLiteral("DocFilenameTerms"), size.docFilenameTerms);
329  prFunc(QStringLiteral("DocXattrTerms"), size.docXattrTerms);
330  prFunc(QStringLiteral("IdTree"), size.idTree);
331  prFunc(QStringLiteral("IdFileName"), size.idFilename);
332  prFunc(QStringLiteral("DocTime"), size.docTime);
333  prFunc(QStringLiteral("DocData"), size.docData);
334  prFunc(QStringLiteral("ContentIndexingDB"), size.contentIndexingIds);
335  prFunc(QStringLiteral("FailedIdsDB"), size.failedIds);
336  prFunc(QStringLiteral("MTimeDB"), size.mtimeDb);
337 
338  return 0;
339  }
340 
341  if (command == QLatin1String("monitor")) {
342  MonitorCommand mon;
343  return mon.exec(parser);
344  }
345 
346  /*
347  TODO: Make separate executable
348  if (command == QLatin1String("checkDb")) {
349  Database *db = globalDatabaseInstance();
350  if (!db->open(Database::ReadOnlyDatabase)) {
351  out << "Baloo Index could not be opened\n";
352  return 1;
353  }
354 
355  Transaction tr(db, Transaction::ReadOnly);
356  tr.checkPostingDbinTermsDb();
357  tr.checkTermsDbinPostingDb();
358  out << "Checking file paths .. "<< '\n';
359  tr.checkFsTree();
360  return 0;
361  }
362  */
363 
364  parser.showHelp(1);
365  return 0;
366 }
T & first()
void msleep(unsigned long msecs)
bool isEmpty() const const
QString number(int n, int base)
bool remove()
static void setApplicationData(const KAboutData &aboutData)
QByteArray encodeName(const QString &fileName)
Q_SCRIPTABLE Q_NOREPLY void start()
void addPositionalArgument(const QString &name, const QString &description, const QString &syntax)
QStringList positionalArguments() const const
void showHelp(int exitCode)
QLocale system()
int size() const const
QString i18n(const char *text, const TYPE &arg...)
QDBusConnection sessionBus()
QString absoluteFilePath() const const
void process(const QStringList &arguments)
QCommandLineOption addVersionOption()
Implements storage for docIds without any associated data Instantiated for:
Definition: coding.cpp:11
const T & at(int i) const const
bool isEmpty() const const
bool startDetached(qint64 *pid)
QTextStream & flush(QTextStream &stream)
QString path(const QString &relativePath)
const char * name(StandardAction id)
void processEvents(QEventLoop::ProcessEventsFlags flags)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
bool addOption(const QCommandLineOption &option)
QCommandLineOption addHelpOption()
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.