File indexing completed on 2024-05-05 05:48:56

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"