File indexing completed on 2024-05-05 10:08:43
0001 /* 0002 SPDX-FileCopyrightText: 2007 Nicolas Ternisien <nicolas.ternisien@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "localLogFileReader.h" 0008 0009 #include <QFile> 0010 #include <QFileInfo> 0011 #include <QMimeDatabase> 0012 #include <QMutex> 0013 0014 #include <KCompressionDevice> 0015 #include <KDirWatch> 0016 #include <KLocalizedString> 0017 0018 #include "logFileReaderPrivate.h" 0019 0020 #include "ksystemlog_debug.h" 0021 0022 class LocalLogFileReaderPrivate : public LogFileReaderPrivate 0023 { 0024 public: 0025 KDirWatch *mWatch = nullptr; 0026 0027 long mPreviousFilePosition; 0028 0029 /** 0030 * Mutex avoiding multiple logFileModified() calls 0031 */ 0032 QMutex mInsertionLocking; 0033 }; 0034 0035 LocalLogFileReader::LocalLogFileReader(const LogFile &logFile) 0036 : LogFileReader(*new LocalLogFileReaderPrivate(), logFile) 0037 { 0038 init(); 0039 } 0040 0041 LocalLogFileReader::LocalLogFileReader(LocalLogFileReaderPrivate &dd, const LogFile &logFile) 0042 : LogFileReader(dd, logFile) 0043 { 0044 init(); 0045 } 0046 0047 LocalLogFileReader::~LocalLogFileReader() 0048 { 0049 Q_D(LocalLogFileReader); 0050 0051 // Delete the watching object 0052 delete d->mWatch; 0053 0054 // d pointer is deleted by the parent class 0055 } 0056 0057 void LocalLogFileReader::init() 0058 { 0059 Q_D(LocalLogFileReader); 0060 0061 d->mWatch = new KDirWatch(); 0062 connect(d->mWatch, &KDirWatch::dirty, this, &LocalLogFileReader::logFileModified); 0063 0064 // Init current file position 0065 d->mPreviousFilePosition = 0; 0066 0067 qCDebug(KSYSTEMLOG) << "Reading local file " << d->logFile.url().toLocalFile(); 0068 } 0069 0070 void LocalLogFileReader::watchFile(bool enable) 0071 { 0072 Q_D(LocalLogFileReader); 0073 const QString filePath = d->logFile.url().toLocalFile(); 0074 0075 if (enable) { 0076 qCDebug(KSYSTEMLOG) << "Monitoring file : " << filePath; 0077 0078 if (!d->mWatch->contains(filePath)) { 0079 d->mWatch->addFile(filePath); 0080 } 0081 0082 // Reinit current file position 0083 d->mPreviousFilePosition = 0; 0084 0085 // If we enable the watching, then we first try to see if new lines have appeared 0086 logFileModified(); 0087 } else { 0088 d->mWatch->removeFile(filePath); 0089 } 0090 } 0091 0092 QIODevice *LocalLogFileReader::open() 0093 { 0094 Q_D(LocalLogFileReader); 0095 const QString filePath = d->logFile.url().toLocalFile(); 0096 0097 if (!d->logFile.url().isValid()) { 0098 const QString message(i18n("This file is not valid. Please adjust it in the settings of KSystemLog.")); 0099 Q_EMIT errorOccured(i18n("File Does Not Exist"), message); 0100 Q_EMIT statusBarChanged(message); 0101 } 0102 0103 QMimeDatabase const db; 0104 const QString mimeType = db.mimeTypeForFile(filePath, QMimeDatabase::MatchContent).name(); 0105 0106 qCDebug(KSYSTEMLOG) << filePath << " : " << mimeType; 0107 std::unique_ptr<QIODevice> inputDevice; 0108 0109 // Try to see if this file exists 0110 const QFileInfo info(filePath); 0111 // If the file does not exist 0112 if (!info.exists()) { 0113 const QString message(i18n("The file '%1' does not exist.", filePath)); 0114 Q_EMIT errorOccured(i18n("File Does Not Exist"), message); 0115 Q_EMIT statusBarChanged(message); 0116 return nullptr; 0117 } 0118 0119 // Plain text file : we use a QFile object 0120 if (mimeType == QLatin1String("text/plain") || mimeType == QLatin1String("application/octet-stream")) { 0121 qCDebug(KSYSTEMLOG) << "Using QFile input device"; 0122 0123 inputDevice.reset(new QFile(filePath)); 0124 } 0125 // Compressed file : we use the KFilterDev helper 0126 else { 0127 qCDebug(KSYSTEMLOG) << "Using KFilterDev input device"; 0128 0129 // inputDevice = KFilterDev::deviceForFile(filePath, mimeType); 0130 0131 inputDevice.reset(new KCompressionDevice(filePath, KCompressionDevice::compressionTypeForMimeType(mimeType))); 0132 0133 if (!inputDevice) { 0134 const QString message(i18n("Unable to uncompress the '%2' format of '%1'.", filePath, mimeType)); 0135 Q_EMIT errorOccured(i18n("Unable to Uncompress File"), message); 0136 Q_EMIT statusBarChanged(message); 0137 return nullptr; 0138 } 0139 } 0140 0141 if (!inputDevice->open(QIODevice::ReadOnly)) { 0142 const QString message(i18n("You do not have sufficient permissions to read '%1'.", filePath)); 0143 Q_EMIT errorOccured(i18n("Insufficient Permissions"), message); 0144 Q_EMIT statusBarChanged(message); 0145 return nullptr; 0146 } 0147 0148 return inputDevice.release(); 0149 } 0150 0151 void LocalLogFileReader::close(QIODevice *inputDevice) 0152 { 0153 inputDevice->close(); 0154 delete inputDevice; 0155 } 0156 0157 QStringList LocalLogFileReader::readContent(QIODevice *inputDevice) 0158 { 0159 qCDebug(KSYSTEMLOG) << "Retrieving raw buffer..."; 0160 0161 Q_D(LocalLogFileReader); 0162 0163 QStringList rawBuffer; 0164 0165 QTextStream inputStream(inputDevice); 0166 while (!inputStream.atEnd()) { 0167 rawBuffer.append(inputStream.readLine()); 0168 } 0169 0170 qCDebug(KSYSTEMLOG) << "Raw buffer retrieved."; 0171 0172 // Get the size file for the next calculation 0173 d->mPreviousFilePosition = inputDevice->size(); 0174 qCDebug(KSYSTEMLOG) << "New file position : " << d->mPreviousFilePosition << " (" << d->logFile.url().toLocalFile() << ")"; 0175 0176 return rawBuffer; 0177 } 0178 0179 void LocalLogFileReader::logFileModified() 0180 { 0181 Q_D(LocalLogFileReader); 0182 0183 qCDebug(KSYSTEMLOG) << "Locking log file modification..."; 0184 if (!d->mInsertionLocking.tryLock()) { 0185 qCDebug(KSYSTEMLOG) << "Log file modification already detected."; 0186 return; 0187 } 0188 0189 QIODevice *inputDevice = open(); 0190 if (!inputDevice) { 0191 qCCritical(KSYSTEMLOG) << "Could not open file " << d->logFile.url().toLocalFile(); 0192 return; 0193 } 0194 0195 // If there are new lines in the file, insert only them or this is the first time we read this file 0196 if (d->mPreviousFilePosition != 0 && d->mPreviousFilePosition <= inputDevice->size()) { 0197 qCDebug(KSYSTEMLOG) << "Reading from position " << d->mPreviousFilePosition << " (" << d->logFile.url().toLocalFile() << ")"; 0198 0199 if (inputDevice->isSequential()) { 0200 qCCritical(KSYSTEMLOG) << "The file current position could not be modified"; 0201 } else { 0202 // Place the cursor to the last line opened 0203 inputDevice->seek(d->mPreviousFilePosition); 0204 } 0205 0206 qCDebug(KSYSTEMLOG) << "Retrieving a part of the file..."; 0207 0208 Q_EMIT contentChanged(this, Analyzer::UpdatingRead, readContent(inputDevice)); 0209 } 0210 // Else reread all lines, clear log list 0211 else { 0212 qCDebug(KSYSTEMLOG) << "New file or file truncated. (Re-)Loading log file"; 0213 0214 Q_EMIT contentChanged(this, Analyzer::FullRead, readContent(inputDevice)); 0215 } 0216 0217 close(inputDevice); 0218 0219 qCDebug(KSYSTEMLOG) << "Unlocking log file modification..."; 0220 d->mInsertionLocking.unlock(); 0221 } 0222 0223 #include "moc_localLogFileReader.cpp"