File indexing completed on 2024-04-28 03:51:53
0001 /* 0002 SPDX-FileCopyrightText: 2012-2015 Vishesh Handa <me@vhanda.in> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include <QCoreApplication> 0008 #include <QCommandLineParser> 0009 #include <QFile> 0010 0011 #include <KAboutData> 0012 #include <KLocalizedString> 0013 #include <KFormat> 0014 #include <QProcess> 0015 #include <QTextStream> 0016 #include <QFileInfo> 0017 #include <QLocale> 0018 0019 #include <QDBusConnection> 0020 #include <QDBusConnectionInterface> 0021 0022 #include "global.h" 0023 #include "database.h" 0024 #include "transaction.h" 0025 #include "databasesize.h" 0026 #include "config.h" 0027 0028 #include "indexer.h" 0029 #include "indexerconfig.h" 0030 #include "idutils.h" 0031 #include "fileindexerconfig.h" 0032 #include "monitorcommand.h" 0033 #include "schedulerinterface.h" 0034 #include "maininterface.h" 0035 #include "indexerstate.h" 0036 #include "configcommand.h" 0037 #include "statuscommand.h" 0038 0039 using namespace Baloo; 0040 0041 void start() 0042 { 0043 const QString exe = QStringLiteral(KDE_INSTALL_FULL_LIBEXECDIR_KF "/baloo_file"); 0044 QProcess::startDetached(exe, QStringList()); 0045 } 0046 0047 int main(int argc, char* argv[]) 0048 { 0049 QCoreApplication app(argc, argv); 0050 0051 KAboutData aboutData(QStringLiteral("baloo"), i18n("balooctl"), QStringLiteral(PROJECT_VERSION)); 0052 aboutData.addAuthor(i18n("Vishesh Handa"), QString(), QStringLiteral("vhanda@kde.org")); 0053 0054 KAboutData::setApplicationData(aboutData); 0055 0056 QCommandLineParser parser; 0057 parser.addPositionalArgument(QStringLiteral("command"), i18n("The command to execute")); 0058 0059 parser.addPositionalArgument(QStringLiteral("status"), i18n("Print the status of the indexer")); 0060 parser.addPositionalArgument(QStringLiteral("enable"), i18n("Enable the file indexer")); 0061 parser.addPositionalArgument(QStringLiteral("disable"), i18n("Disable the file indexer")); 0062 parser.addPositionalArgument(QStringLiteral("purge"), i18n("Remove the index database")); 0063 parser.addPositionalArgument(QStringLiteral("suspend"), i18n("Suspend the file indexer")); 0064 parser.addPositionalArgument(QStringLiteral("resume"), i18n("Resume the file indexer")); 0065 parser.addPositionalArgument(QStringLiteral("check"), i18n("Check for any unindexed files and index them")); 0066 parser.addPositionalArgument(QStringLiteral("index"), i18n("Index the specified files")); 0067 parser.addPositionalArgument(QStringLiteral("clear"), i18n("Forget the specified files")); 0068 parser.addPositionalArgument(QStringLiteral("config"), i18n("Modify the Baloo configuration")); 0069 parser.addPositionalArgument(QStringLiteral("monitor"), i18n("Monitor the file indexer")); 0070 parser.addPositionalArgument(QStringLiteral("indexSize"), i18n("Display the disk space used by index")); 0071 parser.addPositionalArgument(QStringLiteral("failed"), i18n("Display files which could not be indexed")); 0072 0073 QString statusFormatDescription = i18nc("Format to use for status command, %1|%2|%3 are option values, %4 is a CLI command", 0074 "Output format <%1|%2|%3>.\nThe default format is \"%1\".\nOnly applies to \"%4\"", 0075 QStringLiteral("multiline"), 0076 QStringLiteral("json"), 0077 QStringLiteral("simple"), 0078 QStringLiteral("balooctl status <file>")); 0079 parser.addOption({{QStringLiteral("f"), QStringLiteral("format")}, 0080 statusFormatDescription, i18n("format"), QStringLiteral("multiline")}); 0081 0082 parser.addVersionOption(); 0083 parser.addHelpOption(); 0084 0085 parser.process(app); 0086 if (parser.positionalArguments().isEmpty()) { 0087 parser.showHelp(1); 0088 } 0089 0090 QTextStream out(stdout); 0091 0092 QString command = parser.positionalArguments().first(); 0093 0094 org::kde::baloo::main mainInterface(QStringLiteral("org.kde.baloo"), 0095 QStringLiteral("/"), 0096 QDBusConnection::sessionBus()); 0097 0098 org::kde::baloo::scheduler schedulerinterface(QStringLiteral("org.kde.baloo"), 0099 QStringLiteral("/scheduler"), 0100 QDBusConnection::sessionBus()); 0101 0102 if (command == QLatin1String("config")) { 0103 ConfigCommand command; 0104 return command.exec(parser); 0105 } 0106 0107 if (command == QLatin1String("status")) { 0108 StatusCommand commandStatus; 0109 return commandStatus.exec(parser); 0110 } 0111 0112 if (command == QLatin1String("enable") || command == QLatin1String("disable")) { 0113 bool isEnabled = false; 0114 if (command == QLatin1String("enable")) { 0115 isEnabled = true; 0116 } 0117 else if (command == QLatin1String("disable")) { 0118 isEnabled = false; 0119 } 0120 0121 IndexerConfig cfg; 0122 cfg.setFileIndexingEnabled(isEnabled); 0123 0124 if (isEnabled) { 0125 bool running = mainInterface.isValid(); 0126 if (running) { 0127 out << "File Indexer already running\n"; 0128 } else { 0129 out << "Enabling and starting the File Indexer\n"; 0130 start(); 0131 } 0132 } else { 0133 out << "Disabling and stopping the File Indexer\n"; 0134 0135 mainInterface.quit(); 0136 } 0137 0138 return 0; 0139 } 0140 0141 if (command == QLatin1String("purge")) { 0142 bool running = mainInterface.isValid(); 0143 0144 if (running) { 0145 mainInterface.quit(); 0146 out << "Stopping the File Indexer ..."; 0147 for (int i = 5 * 60; i; --i) { 0148 QCoreApplication::processEvents(); 0149 if (!mainInterface.isValid()) { 0150 break; 0151 } 0152 out << "." << Qt::flush; 0153 QThread::msleep(200); 0154 } 0155 if (!mainInterface.isValid()) { 0156 out << " - done\n"; 0157 } else { 0158 out << " - failed to stop!\n"; 0159 return 1; 0160 } 0161 } 0162 0163 const QString path = fileIndexDbPath() + QStringLiteral("/index"); 0164 QFile(path).remove(); 0165 out << "Deleted the index database\n"; 0166 0167 if (running) { 0168 start(); 0169 out << "Restarting the File Indexer\n"; 0170 } 0171 0172 return 0; 0173 } 0174 0175 if (command == QLatin1String("suspend")) { 0176 schedulerinterface.suspend(); 0177 out << "File Indexer suspended\n"; 0178 return 0; 0179 } 0180 0181 if (command == QLatin1String("resume")) { 0182 schedulerinterface.resume(); 0183 out << "File Indexer resumed\n"; 0184 return 0; 0185 } 0186 0187 if (command == QLatin1String("check")) { 0188 schedulerinterface.checkUnindexedFiles(); 0189 out << "Started search for unindexed files\n"; 0190 return 0; 0191 } 0192 0193 if (command == QLatin1String("index")) { 0194 if (parser.positionalArguments().size() < 2) { 0195 out << "Please enter a filename to index\n"; 0196 return 1; 0197 } 0198 0199 Database *db = globalDatabaseInstance(); 0200 if (!db->open(Database::ReadWriteDatabase)) { 0201 out << "Baloo Index could not be opened\n"; 0202 return 1; 0203 } 0204 0205 Transaction tr(db, Transaction::ReadWrite); 0206 0207 for (int i = 1; i < parser.positionalArguments().size(); ++i) { 0208 const QString url = QFileInfo(parser.positionalArguments().at(i)).absoluteFilePath(); 0209 quint64 id = filePathToId(QFile::encodeName(url)); 0210 if (id == 0) { 0211 out << "Could not stat file: " << url << '\n'; 0212 continue; 0213 } 0214 if (tr.inPhaseOne(id)) { 0215 out << "Skipping: " << url << " Reason: Already scheduled for indexing\n"; 0216 continue; 0217 } 0218 if (!tr.documentData(id).isEmpty()) { 0219 out << "Skipping: " << url << " Reason: Already indexed\n"; 0220 continue; 0221 } 0222 Indexer indexer(url, &tr); 0223 out << "Indexing " << url << '\n'; 0224 indexer.index(); 0225 } 0226 tr.commit(); 0227 out << "File(s) indexed\n"; 0228 0229 return 0; 0230 } 0231 0232 if (command == QLatin1String("clear")) { 0233 if (parser.positionalArguments().size() < 2) { 0234 out << "Please enter a filename to index\n"; 0235 return 1; 0236 } 0237 0238 Database *db = globalDatabaseInstance(); 0239 if (!db->open(Database::ReadWriteDatabase)) { 0240 out << "Baloo Index could not be opened\n"; 0241 return 1; 0242 } 0243 0244 Transaction tr(db, Transaction::ReadWrite); 0245 0246 for (int i = 1; i < parser.positionalArguments().size(); ++i) { 0247 const QString url = QFileInfo(parser.positionalArguments().at(i)).absoluteFilePath(); 0248 quint64 id = filePathToId(QFile::encodeName(url)); 0249 if (id == 0) { 0250 id = tr.documentId(QFile::encodeName(url)); 0251 if (id == 0) { 0252 out << "File not found on filesystem or in DB: " << url << '\n'; 0253 continue; 0254 } else { 0255 out << "File has been deleted, clearing from DB: " << url << '\n'; 0256 } 0257 } else { 0258 out << "Clearing " << url << '\n'; 0259 } 0260 0261 tr.removeDocument(id); 0262 } 0263 tr.commit(); 0264 out << "File(s) cleared\n"; 0265 0266 return 0; 0267 } 0268 0269 if (command == QLatin1String("failed")) { 0270 Database *db = globalDatabaseInstance(); 0271 if (!db->open(Database::ReadOnlyDatabase)) { 0272 out << "Baloo Index could not be opened\n"; 0273 return 1; 0274 } 0275 0276 Transaction tr(db, Transaction::ReadOnly); 0277 0278 const quint64 limit = 128; 0279 const QVector<quint64> failedIds = tr.failedIds(limit); 0280 if (failedIds.isEmpty()) { 0281 out << "All Files were indexed successfully\n"; 0282 return 0; 0283 } 0284 0285 out << "The following files could not be indexed:\n"; 0286 for (auto id : failedIds) { 0287 out << tr.documentUrl(id) << '\n'; 0288 } 0289 if (failedIds.size() == limit) { 0290 out << "... list truncated\n"; 0291 } 0292 return 0; 0293 } 0294 0295 if (command == QLatin1String("indexSize")) { 0296 Database *db = globalDatabaseInstance(); 0297 if (!db->open(Database::ReadOnlyDatabase)) { 0298 out << "Baloo Index could not be opened\n"; 0299 return 1; 0300 } 0301 0302 DatabaseSize size; 0303 { 0304 Transaction tr(db, Transaction::ReadOnly); 0305 size = tr.dbSize(); 0306 } 0307 uint totalDataSize = size.expectedSize; 0308 0309 KFormat format(QLocale::system()); 0310 auto prFunc = [&](const QString& name, uint size) { 0311 out.setFieldWidth(20); 0312 out << name; 0313 out.setFieldWidth(0); 0314 out << ":"; 0315 out.setFieldWidth(15); 0316 out << format.formatByteSize(size, 2); 0317 out.setFieldWidth(10); 0318 out << QString::number((100.0 * size / totalDataSize), 'f', 3); 0319 out.setFieldWidth(0); 0320 out << " %\n"; 0321 }; 0322 0323 out << "File Size: " << format.formatByteSize(size.actualSize, 2) << "\n"; 0324 out << "Used: " << format.formatByteSize(totalDataSize, 2) << "\n\n"; 0325 prFunc(QStringLiteral("PostingDB"), size.postingDb); 0326 prFunc(QStringLiteral("PositionDB"), size.positionDb); 0327 prFunc(QStringLiteral("DocTerms"), size.docTerms); 0328 prFunc(QStringLiteral("DocFilenameTerms"), size.docFilenameTerms); 0329 prFunc(QStringLiteral("DocXattrTerms"), size.docXattrTerms); 0330 prFunc(QStringLiteral("IdTree"), size.idTree); 0331 prFunc(QStringLiteral("IdFileName"), size.idFilename); 0332 prFunc(QStringLiteral("DocTime"), size.docTime); 0333 prFunc(QStringLiteral("DocData"), size.docData); 0334 prFunc(QStringLiteral("ContentIndexingDB"), size.contentIndexingIds); 0335 prFunc(QStringLiteral("FailedIdsDB"), size.failedIds); 0336 prFunc(QStringLiteral("MTimeDB"), size.mtimeDb); 0337 0338 return 0; 0339 } 0340 0341 if (command == QLatin1String("monitor")) { 0342 MonitorCommand mon; 0343 return mon.exec(parser); 0344 } 0345 0346 /* 0347 TODO: Make separate executable 0348 if (command == QLatin1String("checkDb")) { 0349 Database *db = globalDatabaseInstance(); 0350 if (!db->open(Database::ReadOnlyDatabase)) { 0351 out << "Baloo Index could not be opened\n"; 0352 return 1; 0353 } 0354 0355 Transaction tr(db, Transaction::ReadOnly); 0356 tr.checkPostingDbinTermsDb(); 0357 tr.checkTermsDbinPostingDb(); 0358 out << "Checking file paths .. "<< '\n'; 0359 tr.checkFsTree(); 0360 return 0; 0361 } 0362 */ 0363 0364 parser.showHelp(1); 0365 return 0; 0366 }