File indexing completed on 2024-04-28 03:51:53
0001 /* 0002 This file is part of the KDE Baloo project. 0003 SPDX-FileCopyrightText: 2015 Vishesh Handa <vhanda@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "statuscommand.h" 0009 #include "indexerconfig.h" 0010 0011 #include "global.h" 0012 #include "database.h" 0013 #include "transaction.h" 0014 #include "idutils.h" 0015 0016 #include "fileindexerinterface.h" 0017 #include "schedulerinterface.h" 0018 #include "maininterface.h" 0019 #include "indexerstate.h" 0020 0021 #include <KLocalizedString> 0022 #include <KFormat> 0023 0024 using namespace Baloo; 0025 0026 QString StatusCommand::command() 0027 { 0028 return QStringLiteral("status"); 0029 } 0030 0031 QString StatusCommand::description() 0032 { 0033 return i18n("Print the status of the Indexer"); 0034 } 0035 0036 class FileIndexStatus 0037 { 0038 public: 0039 enum class FileStatus : uint8_t { 0040 NonExisting, 0041 Directory, 0042 RegularFile, 0043 SymLink, 0044 Other, 0045 }; 0046 enum class IndexStateReason : uint8_t { 0047 NoFileOrDirectory, 0048 ExcludedByPath, // FIXME - be more specific, requires changes to shouldBeIndexed(path) 0049 WaitingForIndexingBoth, 0050 WaitingForBasicIndexing, 0051 BasicIndexingDone, 0052 WaitingForContentIndexing, 0053 FailedToIndex, 0054 Done, 0055 }; 0056 const QString m_filePath; 0057 FileStatus m_fileStatus; 0058 IndexStateReason m_indexState; 0059 uint32_t m_dataSize; 0060 }; 0061 0062 FileIndexStatus collectFileStatus(Transaction& tr, IndexerConfig& cfg, const QString& file) 0063 { 0064 using FileStatus = FileIndexStatus::FileStatus; 0065 using IndexStateReason = FileIndexStatus::IndexStateReason; 0066 0067 bool onlyBasicIndexing = cfg.onlyBasicIndexing(); 0068 0069 const QFileInfo fileInfo = QFileInfo(file); 0070 const QString filePath = fileInfo.absoluteFilePath(); 0071 quint64 id = filePathToId(QFile::encodeName(filePath)); 0072 if (id == 0) { 0073 return FileIndexStatus{filePath, FileStatus::NonExisting, IndexStateReason::NoFileOrDirectory, 0}; 0074 } 0075 0076 FileStatus fileStatus = fileInfo.isSymLink() ? FileStatus::SymLink : 0077 fileInfo.isFile() ? FileStatus::RegularFile : 0078 fileInfo.isDir() ? FileStatus::Directory : FileStatus::Other; 0079 0080 if (fileStatus == FileStatus::Other || fileStatus == FileStatus::SymLink) { 0081 return FileIndexStatus{filePath, fileStatus, IndexStateReason::NoFileOrDirectory, 0}; 0082 } 0083 0084 if (!cfg.shouldBeIndexed(filePath)) { 0085 return FileIndexStatus{filePath, fileStatus, IndexStateReason::ExcludedByPath, 0}; 0086 } 0087 0088 if (onlyBasicIndexing || fileStatus == FileStatus::Directory) { 0089 if (!tr.hasDocument(id)) { 0090 return FileIndexStatus{filePath, fileStatus, IndexStateReason::WaitingForBasicIndexing, 0}; 0091 } else { 0092 return FileIndexStatus{filePath, fileStatus, IndexStateReason::BasicIndexingDone, 0}; 0093 } 0094 } 0095 0096 // File && shouldBeIndexed && contentIndexing 0097 if (!tr.hasDocument(id)) { 0098 return FileIndexStatus{filePath, fileStatus, IndexStateReason::WaitingForIndexingBoth, 0}; 0099 } else if (tr.inPhaseOne(id)) { 0100 return FileIndexStatus{filePath, fileStatus, IndexStateReason::WaitingForContentIndexing, 0}; 0101 } else if (tr.hasFailed(id)) { 0102 return FileIndexStatus{filePath, fileStatus, IndexStateReason::FailedToIndex, 0}; 0103 } else { 0104 uint32_t size = tr.documentData(id).size(); 0105 return FileIndexStatus{filePath, fileStatus, IndexStateReason::Done, size}; 0106 } 0107 } 0108 0109 void printMultiLine(Transaction& tr, IndexerConfig& cfg, const QStringList& args) { 0110 using FileStatus = FileIndexStatus::FileStatus; 0111 using IndexStateReason = FileIndexStatus::IndexStateReason; 0112 0113 QTextStream out(stdout); 0114 QTextStream err(stderr); 0115 0116 const QMap<IndexStateReason, QString> basicIndexStateValue = { 0117 { IndexStateReason::NoFileOrDirectory, i18n("File ignored") }, 0118 { IndexStateReason::ExcludedByPath, i18n("Basic Indexing: Disabled") }, 0119 { IndexStateReason::WaitingForIndexingBoth, i18n("Basic Indexing: Scheduled") }, 0120 { IndexStateReason::WaitingForBasicIndexing, i18n("Basic Indexing: Scheduled") }, 0121 { IndexStateReason::BasicIndexingDone, i18n("Basic Indexing: Done") }, 0122 { IndexStateReason::WaitingForContentIndexing, i18n("Basic Indexing: Done") }, 0123 { IndexStateReason::FailedToIndex, i18n("Basic Indexing: Done") }, 0124 { IndexStateReason::Done, i18n("Basic Indexing: Done") }, 0125 }; 0126 0127 const QMap<IndexStateReason, QString> contentIndexStateValue = { 0128 { IndexStateReason::NoFileOrDirectory, QString() }, 0129 { IndexStateReason::ExcludedByPath, QString() }, 0130 { IndexStateReason::WaitingForIndexingBoth, i18n("Content Indexing: Scheduled") }, 0131 { IndexStateReason::WaitingForBasicIndexing, QString() }, 0132 { IndexStateReason::BasicIndexingDone, QString() }, 0133 { IndexStateReason::WaitingForContentIndexing, i18n("Content Indexing: Scheduled") }, 0134 { IndexStateReason::FailedToIndex, i18n("Content Indexing: Failed") }, 0135 { IndexStateReason::Done, i18n("Content Indexing: Done") }, 0136 }; 0137 0138 for (const auto& fileName : args) { 0139 const auto file = collectFileStatus(tr, cfg, fileName); 0140 0141 if (file.m_fileStatus == FileStatus::NonExisting) { 0142 err << i18n("Ignoring non-existent file %1", file.m_filePath) << '\n'; 0143 continue; 0144 } 0145 0146 if (file.m_fileStatus == FileStatus::SymLink || file.m_fileStatus == FileStatus::Other) { 0147 err << i18n("Ignoring symlink/special file %1", file.m_filePath) << '\n'; 0148 continue; 0149 } 0150 0151 out << i18n("File: %1", file.m_filePath) << '\n'; 0152 out << basicIndexStateValue[file.m_indexState] << '\n'; 0153 const QString contentState = contentIndexStateValue[file.m_indexState]; 0154 if (!contentState.isEmpty()) { 0155 out << contentState << '\n'; 0156 } 0157 } 0158 } 0159 0160 void printSimpleFormat(Transaction& tr, IndexerConfig& cfg, const QStringList& args) { 0161 using FileStatus = FileIndexStatus::FileStatus; 0162 using IndexStateReason = FileIndexStatus::IndexStateReason; 0163 0164 QTextStream out(stdout); 0165 QTextStream err(stderr); 0166 0167 const QMap<IndexStateReason, QString> simpleIndexStateValue = { 0168 { IndexStateReason::NoFileOrDirectory, QStringLiteral("No regular file or directory") }, 0169 { IndexStateReason::ExcludedByPath, QStringLiteral("Indexing disabled") }, 0170 { IndexStateReason::WaitingForIndexingBoth, QStringLiteral("Basic and Content indexing scheduled") }, 0171 { IndexStateReason::WaitingForBasicIndexing, QStringLiteral("Basic indexing scheduled") }, 0172 { IndexStateReason::BasicIndexingDone, QStringLiteral("Basic indexing done") }, 0173 { IndexStateReason::WaitingForContentIndexing, QStringLiteral("Content indexing scheduled") }, 0174 { IndexStateReason::FailedToIndex, QStringLiteral("Content indexing failed") }, 0175 { IndexStateReason::Done, QStringLiteral("Content indexing done") }, 0176 }; 0177 0178 for (const auto& fileName : args) { 0179 const auto file = collectFileStatus(tr, cfg, fileName); 0180 0181 if (file.m_fileStatus == FileStatus::NonExisting) { 0182 err << i18n("Ignoring non-existent file %1", file.m_filePath) << '\n'; 0183 continue; 0184 } 0185 0186 if (file.m_fileStatus == FileStatus::SymLink || file.m_fileStatus == FileStatus::Other) { 0187 err << i18n("Ignoring symlink/special file %1", file.m_filePath) << '\n'; 0188 continue; 0189 } 0190 0191 out << simpleIndexStateValue[file.m_indexState]; 0192 out << ": " << file.m_filePath << '\n'; 0193 } 0194 } 0195 0196 void printJSON(Transaction& tr, IndexerConfig& cfg, const QStringList& args) 0197 { 0198 using FileStatus = FileIndexStatus::FileStatus; 0199 using IndexStateReason = FileIndexStatus::IndexStateReason; 0200 0201 QJsonArray filesInfo; 0202 QTextStream err(stderr); 0203 0204 const QMap<IndexStateReason, QString> jsonIndexStateValue = { 0205 { IndexStateReason::NoFileOrDirectory, QStringLiteral("nofile") }, 0206 { IndexStateReason::ExcludedByPath, QStringLiteral("disabled") }, 0207 { IndexStateReason::WaitingForIndexingBoth, QStringLiteral("scheduled") }, 0208 { IndexStateReason::WaitingForBasicIndexing, QStringLiteral("scheduled") }, 0209 { IndexStateReason::BasicIndexingDone, QStringLiteral("done") }, 0210 { IndexStateReason::WaitingForContentIndexing, QStringLiteral("scheduled") }, 0211 { IndexStateReason::FailedToIndex, QStringLiteral("failed") }, 0212 { IndexStateReason::Done, QStringLiteral("done") }, 0213 }; 0214 0215 const QMap<IndexStateReason, QString> jsonIndexLevelValue = { 0216 { IndexStateReason::NoFileOrDirectory, QStringLiteral("nofile") }, 0217 { IndexStateReason::ExcludedByPath, QStringLiteral("none") }, 0218 { IndexStateReason::WaitingForIndexingBoth, QStringLiteral("content") }, 0219 { IndexStateReason::WaitingForBasicIndexing, QStringLiteral("basic") }, 0220 { IndexStateReason::BasicIndexingDone, QStringLiteral("basic") }, 0221 { IndexStateReason::WaitingForContentIndexing, QStringLiteral("content") }, 0222 { IndexStateReason::FailedToIndex, QStringLiteral("content") }, 0223 { IndexStateReason::Done, QStringLiteral("content") }, 0224 }; 0225 0226 for (const auto& fileName : args) { 0227 const auto file = collectFileStatus(tr, cfg, fileName); 0228 0229 if (file.m_fileStatus == FileStatus::NonExisting) { 0230 err << i18n("Ignoring non-existent file %1", file.m_filePath) << '\n'; 0231 continue; 0232 } 0233 0234 if (file.m_fileStatus == FileStatus::SymLink || file.m_fileStatus == FileStatus::Other) { 0235 err << i18n("Ignoring symlink/special file %1", file.m_filePath) << '\n'; 0236 continue; 0237 } 0238 0239 QJsonObject fileInfo; 0240 fileInfo[QStringLiteral("file")] = file.m_filePath; 0241 fileInfo[QStringLiteral("indexing")] = jsonIndexLevelValue[file.m_indexState]; 0242 fileInfo[QStringLiteral("status")] = jsonIndexStateValue[file.m_indexState]; 0243 0244 filesInfo.append(fileInfo); 0245 } 0246 0247 QJsonDocument json; 0248 json.setArray(filesInfo); 0249 QTextStream out(stdout); 0250 out << json.toJson(QJsonDocument::Indented); 0251 } 0252 0253 int StatusCommand::exec(const QCommandLineParser& parser) 0254 { 0255 QTextStream out(stdout); 0256 QTextStream err(stderr); 0257 0258 const QStringList allowedFormats({QStringLiteral("simple"), QStringLiteral("json"), QStringLiteral("multiline")}); 0259 const QString format = parser.value(QStringLiteral("format")); 0260 0261 if (!allowedFormats.contains(format)) { 0262 err << i18n("Output format \"%1\" is invalid, use one of:\n", format); 0263 for (const auto& format : allowedFormats) { 0264 err << i18nc("bullet list item with output format", "- %1\n", format); 0265 } 0266 return 1; 0267 } 0268 0269 IndexerConfig cfg; 0270 if (!cfg.fileIndexingEnabled()) { 0271 err << i18n("Baloo is currently disabled. To enable, please run %1\n", QStringLiteral("balooctl enable")); 0272 return 1; 0273 } 0274 0275 Database *db = globalDatabaseInstance(); 0276 if (!db->open(Database::ReadOnlyDatabase)) { 0277 err << i18n("Baloo Index could not be opened\n"); 0278 return 1; 0279 } 0280 0281 Transaction tr(db, Transaction::ReadOnly); 0282 0283 QStringList args = parser.positionalArguments(); 0284 args.pop_front(); 0285 0286 if (args.isEmpty()) { 0287 org::kde::baloo::main mainInterface(QStringLiteral("org.kde.baloo"), 0288 QStringLiteral("/"), 0289 QDBusConnection::sessionBus()); 0290 0291 org::kde::baloo::scheduler schedulerinterface(QStringLiteral("org.kde.baloo"), 0292 QStringLiteral("/scheduler"), 0293 QDBusConnection::sessionBus()); 0294 0295 bool running = mainInterface.isValid(); 0296 0297 if (running) { 0298 org::kde::baloo::fileindexer indexerInterface(QStringLiteral("org.kde.baloo"), 0299 QStringLiteral("/fileindexer"), 0300 QDBusConnection::sessionBus()); 0301 0302 const QString currentFile = indexerInterface.currentFile(); 0303 0304 out << i18n("Baloo File Indexer is running\n"); 0305 if (!currentFile.isEmpty()) { 0306 out << i18n("Indexer state: %1", stateString(IndexerState::ContentIndexing)) << '\n'; 0307 out << i18nc("currently indexed file", "Indexing: %1", currentFile) << '\n'; 0308 } else { 0309 out << i18n("Indexer state: %1", stateString(schedulerinterface.state())) << '\n'; 0310 } 0311 } 0312 else { 0313 out << i18n("Baloo File Indexer is not running\n"); 0314 } 0315 0316 uint phaseOne = tr.phaseOneSize(); 0317 uint total = tr.size(); 0318 uint failed = tr.failedIds(100).size(); 0319 0320 out << i18n("Total files indexed: %1", total) << '\n'; 0321 out << i18n("Files waiting for content indexing: %1", phaseOne) << '\n'; 0322 out << i18n("Files failed to index: %1", failed) << '\n'; 0323 0324 const QString path = fileIndexDbPath(); 0325 0326 const QFileInfo indexInfo(path + QLatin1String("/index")); 0327 const auto size = indexInfo.size(); 0328 KFormat format(QLocale::system()); 0329 if (size) { 0330 out << i18n("Current size of index is %1", format.formatByteSize(size, 2)) << '\n'; 0331 } else { 0332 out << i18n("Index does not exist yet\n"); 0333 } 0334 } else if (format == allowedFormats[0]){ 0335 printSimpleFormat(tr, cfg, args); 0336 } else if (format == allowedFormats[1]){ 0337 printJSON(tr, cfg, args); 0338 } else { 0339 printMultiLine(tr, cfg, args); 0340 } 0341 0342 return 0; 0343 }