1 | /* |
2 | SPDX-FileCopyrightText: 2012-2015 Vishesh Handa <me@vhanda.in> |
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 | #include "clearcommand.h" |
40 | #include "indexcommand.h" |
41 | |
42 | using namespace Baloo; |
43 | |
44 | void start() |
45 | { |
46 | const QString exe = QStringLiteral(KDE_INSTALL_FULL_LIBEXECDIR_KF "/baloo_file" ); |
47 | QProcess::startDetached(program: exe, arguments: QStringList()); |
48 | } |
49 | |
50 | int main(int argc, char* argv[]) |
51 | { |
52 | QCoreApplication app(argc, argv); |
53 | |
54 | KAboutData aboutData(QStringLiteral("baloo" ), i18n("balooctl" ), QStringLiteral(PROJECT_VERSION)); |
55 | aboutData.addAuthor(i18n("Vishesh Handa" ), task: QString(), QStringLiteral("vhanda@kde.org" )); |
56 | |
57 | KAboutData::setApplicationData(aboutData); |
58 | |
59 | QCommandLineParser parser; |
60 | parser.addPositionalArgument(QStringLiteral("command" ), i18n("The command to execute" )); |
61 | |
62 | parser.addPositionalArgument(QStringLiteral("status" ), i18n("Print the status of the indexer" )); |
63 | parser.addPositionalArgument(QStringLiteral("enable" ), i18n("Enable the file indexer" )); |
64 | parser.addPositionalArgument(QStringLiteral("disable" ), i18n("Disable the file indexer" )); |
65 | parser.addPositionalArgument(QStringLiteral("purge" ), i18n("Remove the index database" )); |
66 | parser.addPositionalArgument(QStringLiteral("suspend" ), i18n("Suspend the file indexer" )); |
67 | parser.addPositionalArgument(QStringLiteral("resume" ), i18n("Resume the file indexer" )); |
68 | parser.addPositionalArgument(QStringLiteral("check" ), i18n("Check for any unindexed files and index them" )); |
69 | parser.addPositionalArgument(QStringLiteral("index" ), i18n("Index the specified files" )); |
70 | parser.addPositionalArgument(QStringLiteral("clear" ), i18n("Forget the specified files" )); |
71 | parser.addPositionalArgument(QStringLiteral("config" ), i18n("Modify the Baloo configuration" )); |
72 | parser.addPositionalArgument(QStringLiteral("monitor" ), i18n("Monitor the file indexer" )); |
73 | parser.addPositionalArgument(QStringLiteral("indexSize" ), i18n("Display the disk space used by index" )); |
74 | parser.addPositionalArgument(QStringLiteral("failed" ), i18n("Display files which could not be indexed" )); |
75 | |
76 | QString statusFormatDescription = i18nc("Format to use for status command, %1|%2|%3 are option values, %4 is a CLI command" , |
77 | "Output format <%1|%2|%3>.\nThe default format is \"%1\".\nOnly applies to \"%4\"" , |
78 | QStringLiteral("multiline" ), |
79 | QStringLiteral("json" ), |
80 | QStringLiteral("simple" ), |
81 | QStringLiteral("balooctl status <file>" )); |
82 | |
83 | parser.addOption(commandLineOption: {{QStringLiteral("f" ), QStringLiteral("format" )}, statusFormatDescription, i18n("format" ), QStringLiteral("multiline" )}); |
84 | |
85 | parser.addVersionOption(); |
86 | parser.addHelpOption(); |
87 | |
88 | parser.process(app); |
89 | if (parser.positionalArguments().isEmpty()) { |
90 | parser.showHelp(exitCode: 1); |
91 | } |
92 | |
93 | QTextStream out(stdout); |
94 | |
95 | QString command = parser.positionalArguments().first(); |
96 | |
97 | org::kde::baloo::main mainInterface(QStringLiteral("org.kde.baloo" ), |
98 | QStringLiteral("/" ), |
99 | QDBusConnection::sessionBus()); |
100 | |
101 | org::kde::baloo::scheduler schedulerinterface(QStringLiteral("org.kde.baloo" ), |
102 | QStringLiteral("/scheduler" ), |
103 | QDBusConnection::sessionBus()); |
104 | |
105 | // Deal with the various balooctl commands: |
106 | |
107 | if (command == QLatin1String("config" )) { |
108 | ConfigCommand command; |
109 | return command.exec(parser); |
110 | } |
111 | |
112 | if (command == QLatin1String("status" )) { |
113 | StatusCommand commandStatus; |
114 | return commandStatus.exec(parser); |
115 | } |
116 | |
117 | if (command == QLatin1String("enable" ) || command == QLatin1String("disable" )) { |
118 | bool isEnabled = false; |
119 | if (command == QLatin1String("enable" )) { |
120 | isEnabled = true; |
121 | } |
122 | else if (command == QLatin1String("disable" )) { |
123 | isEnabled = false; |
124 | } |
125 | |
126 | IndexerConfig cfg; |
127 | cfg.setFileIndexingEnabled(isEnabled); |
128 | |
129 | if (isEnabled) { |
130 | bool running = mainInterface.isValid(); |
131 | if (running) { |
132 | out << "File Indexer already running\n" ; |
133 | } else { |
134 | out << "Enabling and starting the File Indexer\n" ; |
135 | start(); |
136 | } |
137 | } else { |
138 | out << "Disabling and stopping the File Indexer\n" ; |
139 | |
140 | mainInterface.quit(); |
141 | } |
142 | |
143 | return 0; |
144 | } |
145 | |
146 | if (command == QLatin1String("purge" )) { |
147 | bool running = mainInterface.isValid(); |
148 | |
149 | if (running) { |
150 | mainInterface.quit(); |
151 | out << "Stopping the File Indexer ..." ; |
152 | for (int i = 5 * 60; i; --i) { |
153 | QCoreApplication::processEvents(); |
154 | if (!mainInterface.isValid()) { |
155 | break; |
156 | } |
157 | out << "." << Qt::flush; |
158 | QThread::msleep(200); |
159 | } |
160 | if (!mainInterface.isValid()) { |
161 | out << " - done\n" ; |
162 | } else { |
163 | out << " - failed to stop!\n" ; |
164 | return 1; |
165 | } |
166 | } |
167 | |
168 | const QString path = fileIndexDbPath() + QStringLiteral("/index" ); |
169 | QFile(path).remove(); |
170 | out << "Deleted the index database\n" ; |
171 | |
172 | if (running) { |
173 | start(); |
174 | out << "Restarting the File Indexer\n" ; |
175 | } |
176 | |
177 | return 0; |
178 | } |
179 | |
180 | if (command == QLatin1String("suspend" )) { |
181 | schedulerinterface.suspend(); |
182 | out << "File Indexer suspended\n" ; |
183 | return 0; |
184 | } |
185 | |
186 | if (command == QLatin1String("resume" )) { |
187 | schedulerinterface.resume(); |
188 | out << "File Indexer resumed\n" ; |
189 | return 0; |
190 | } |
191 | |
192 | if (command == QLatin1String("check" )) { |
193 | schedulerinterface.checkUnindexedFiles(); |
194 | out << "Started search for unindexed files\n" ; |
195 | return 0; |
196 | } |
197 | |
198 | if (command == QLatin1String("index" )) { |
199 | IndexCommand commandIndex; |
200 | return commandIndex.exec(parser); |
201 | } |
202 | |
203 | if (command == QLatin1String("clear" )) { |
204 | ClearCommand commandClear; |
205 | return commandClear.exec(parser); |
206 | } |
207 | |
208 | if (command == QLatin1String("failed" )) { |
209 | Database *db = globalDatabaseInstance(); |
210 | if (db->open(mode: Database::ReadOnlyDatabase) != Database::OpenResult::Success) { |
211 | out << "Baloo Index could not be opened\n" ; |
212 | return 1; |
213 | } |
214 | |
215 | Transaction tr(db, Transaction::ReadOnly); |
216 | |
217 | const quint64 limit = 128; |
218 | const QVector<quint64> failedIds = tr.failedIds(limit); |
219 | if (failedIds.isEmpty()) { |
220 | out << "All Files were indexed successfully\n" ; |
221 | return 0; |
222 | } |
223 | |
224 | out << "The following files could not be indexed:\n" ; |
225 | for (auto id : failedIds) { |
226 | out << tr.documentUrl(id) << '\n'; |
227 | } |
228 | if (failedIds.size() == limit) { |
229 | out << "... list truncated\n" ; |
230 | } |
231 | return 0; |
232 | } |
233 | |
234 | if (command == QLatin1String("indexSize" )) { |
235 | Database *db = globalDatabaseInstance(); |
236 | if (db->open(mode: Database::ReadOnlyDatabase) != Database::OpenResult::Success) { |
237 | out << "Baloo Index could not be opened\n" ; |
238 | return 1; |
239 | } |
240 | |
241 | DatabaseSize size; |
242 | { |
243 | Transaction tr(db, Transaction::ReadOnly); |
244 | size = tr.dbSize(); |
245 | } |
246 | uint totalDataSize = size.expectedSize; |
247 | |
248 | KFormat format(QLocale::system()); |
249 | auto prFunc = [&](const QString& name, uint size) { |
250 | out.setFieldWidth(20); |
251 | out << name; |
252 | out.setFieldWidth(0); |
253 | out << ":" ; |
254 | out.setFieldWidth(15); |
255 | out << format.formatByteSize(size, precision: 2); |
256 | out.setFieldWidth(10); |
257 | out << QString::number((100.0 * size / totalDataSize), format: 'f', precision: 3); |
258 | out.setFieldWidth(0); |
259 | out << " %\n" ; |
260 | }; |
261 | |
262 | out << "File Size: " << format.formatByteSize(size: size.actualSize, precision: 2) << "\n" ; |
263 | out << "Used: " << format.formatByteSize(size: totalDataSize, precision: 2) << "\n\n" ; |
264 | prFunc(QStringLiteral("PostingDB" ), size.postingDb); |
265 | prFunc(QStringLiteral("PositionDB" ), size.positionDb); |
266 | prFunc(QStringLiteral("DocTerms" ), size.docTerms); |
267 | prFunc(QStringLiteral("DocFilenameTerms" ), size.docFilenameTerms); |
268 | prFunc(QStringLiteral("DocXattrTerms" ), size.docXattrTerms); |
269 | prFunc(QStringLiteral("IdTree" ), size.idTree); |
270 | prFunc(QStringLiteral("IdFileName" ), size.idFilename); |
271 | prFunc(QStringLiteral("DocTime" ), size.docTime); |
272 | prFunc(QStringLiteral("DocData" ), size.docData); |
273 | prFunc(QStringLiteral("ContentIndexingDB" ), size.contentIndexingIds); |
274 | prFunc(QStringLiteral("FailedIdsDB" ), size.failedIds); |
275 | prFunc(QStringLiteral("MTimeDB" ), size.mtimeDb); |
276 | |
277 | return 0; |
278 | } |
279 | |
280 | if (command == QLatin1String("monitor" )) { |
281 | MonitorCommand mon; |
282 | return mon.exec(parser); |
283 | } |
284 | |
285 | /* |
286 | TODO: Make separate executable |
287 | if (command == QLatin1String("checkDb")) { |
288 | Database *db = globalDatabaseInstance(); |
289 | if (!db->open(Database::ReadOnlyDatabase)) { |
290 | out << "Baloo Index could not be opened\n"; |
291 | return 1; |
292 | } |
293 | |
294 | Transaction tr(db, Transaction::ReadOnly); |
295 | tr.checkPostingDbinTermsDb(); |
296 | tr.checkTermsDbinPostingDb(); |
297 | out << "Checking file paths .. "<< '\n'; |
298 | tr.checkFsTree(); |
299 | return 0; |
300 | } |
301 | */ |
302 | |
303 | parser.showHelp(exitCode: 1); |
304 | return 0; |
305 | } |
306 | |