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

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 "processOutputLogFileReader.h"
0008 
0009 #include <QStringList>
0010 #include <QTimer>
0011 
0012 #include <KLocalizedString>
0013 
0014 #include "logFileReaderPrivate.h"
0015 
0016 #include "ksystemlog_debug.h"
0017 
0018 class ProcessOutputLogFileReaderPrivate : public LogFileReaderPrivate
0019 {
0020 public:
0021     long mPreviousLinesCount;
0022 
0023     QTimer mProcessUpdater;
0024 
0025     QProcess *mProcess = nullptr;
0026 
0027     QString mBuffer;
0028     QStringList mAvailableStandardOutput;
0029 };
0030 
0031 ProcessOutputLogFileReader::ProcessOutputLogFileReader(const LogFile &logFile)
0032     : LogFileReader(*new ProcessOutputLogFileReaderPrivate(), logFile)
0033 {
0034     init();
0035 }
0036 
0037 ProcessOutputLogFileReader::ProcessOutputLogFileReader(ProcessOutputLogFileReaderPrivate &dd, const LogFile &logFile)
0038     : LogFileReader(dd, logFile)
0039 {
0040     init();
0041 }
0042 
0043 ProcessOutputLogFileReader::~ProcessOutputLogFileReader()
0044 {
0045     // d pointer is deleted by the parent class
0046 }
0047 
0048 void ProcessOutputLogFileReader::init()
0049 {
0050     Q_D(ProcessOutputLogFileReader);
0051 
0052     // Init current file position
0053     d->mPreviousLinesCount = 0;
0054     d->mAvailableStandardOutput.clear();
0055     d->mProcess = nullptr;
0056 
0057     d->mProcessUpdater.setInterval(PROCESS_OUTPUT_UPDATER_INTERVAL);
0058     connect(&(d->mProcessUpdater), &QTimer::timeout, this, &ProcessOutputLogFileReader::startProcess);
0059 
0060     qCDebug(KSYSTEMLOG) << "Using process name " << d->logFile.url().toLocalFile();
0061 }
0062 
0063 void ProcessOutputLogFileReader::watchFile(bool enable)
0064 {
0065     Q_D(ProcessOutputLogFileReader);
0066 
0067     if (enable) {
0068         qCDebug(KSYSTEMLOG) << "Monitoring process : " << d->logFile.url().toLocalFile();
0069 
0070         // Reinit current file position
0071         d->mPreviousLinesCount = 0;
0072 
0073         // Launch the timer
0074         d->mProcessUpdater.start();
0075 
0076         // Launch immediately the process updater
0077         startProcess();
0078     } else {
0079         // Stop regularly start process
0080         d->mProcessUpdater.stop();
0081     }
0082 }
0083 
0084 void ProcessOutputLogFileReader::startProcess()
0085 {
0086     qCDebug(KSYSTEMLOG) << "Starting process...";
0087 
0088     Q_D(ProcessOutputLogFileReader);
0089 
0090     if (!d->logFile.url().isValid()) {
0091         const QString message(i18n("This file is not valid. Please adjust it in the settings of KSystemLog."));
0092         Q_EMIT errorOccured(i18n("File Does Not Exist"), message);
0093         Q_EMIT statusBarChanged(message);
0094     }
0095 
0096     qCDebug(KSYSTEMLOG) << "Starting process...";
0097 
0098     d->mProcess = new QProcess();
0099     connect(d->mProcess, &QProcess::readyReadStandardOutput, this, &ProcessOutputLogFileReader::logFileModified);
0100     connect(d->mProcess, &QProcess::finished, this, &ProcessOutputLogFileReader::emitProcessOutput);
0101 
0102     d->mProcess->start(d->logFile.url().toLocalFile(), QStringList(), QIODevice::ReadOnly | QIODevice::Text);
0103 
0104     d->mProcess->waitForStarted();
0105 
0106     qCDebug(KSYSTEMLOG) << "Process started";
0107 }
0108 
0109 void ProcessOutputLogFileReader::closeProcess()
0110 {
0111     qCDebug(KSYSTEMLOG) << "Closing process...";
0112 
0113     Q_D(ProcessOutputLogFileReader);
0114 
0115     // Get the size file for the next calculation
0116     d->mPreviousLinesCount = d->mAvailableStandardOutput.count();
0117     qCDebug(KSYSTEMLOG) << "New lines count : " << d->mPreviousLinesCount << " (" << d->logFile.url().toLocalFile() << ")";
0118 
0119     d->mAvailableStandardOutput.clear();
0120 
0121     if (d->mProcess) {
0122         d->mProcess->close();
0123         delete d->mProcess;
0124         d->mProcess = nullptr;
0125     }
0126 
0127     qCDebug(KSYSTEMLOG) << "Process closed";
0128 }
0129 
0130 void ProcessOutputLogFileReader::emitProcessOutput(int /*exitCode*/, QProcess::ExitStatus exitStatus)
0131 {
0132     Q_D(ProcessOutputLogFileReader);
0133 
0134     // First commit last lines of the buffer to the line list
0135     emptyBuffer();
0136 
0137     qCDebug(KSYSTEMLOG) << "Process terminated" << d->mPreviousLinesCount << "previously /" << d->mAvailableStandardOutput.count() << "currently";
0138 
0139     if (exitStatus == QProcess::CrashExit) {
0140         QString const message(i18n("The process '%1' crashed.", d->logFile.url().toLocalFile()));
0141         Q_EMIT errorOccured(i18n("Process Crashed"), message);
0142         Q_EMIT statusBarChanged(message);
0143     }
0144 
0145     // If there is no new lines
0146     if (d->mPreviousLinesCount == d->mAvailableStandardOutput.count()) {
0147         /*
0148         //Q_EMIT an empty log lines list
0149         Q_EMIT contentChanged(this, false, QStringList());
0150         */
0151     }
0152     // If there are new lines in the file, insert only them or this is the first time we read this file
0153     else if (d->mPreviousLinesCount != 0 && d->mPreviousLinesCount <= d->mAvailableStandardOutput.count()) {
0154         qCDebug(KSYSTEMLOG) << "Reading from line " << d->mPreviousLinesCount << " (" << d->logFile.url().toLocalFile() << ")";
0155 
0156         QStringList newOutputs;
0157 
0158         int index = d->mPreviousLinesCount - 1;
0159         while (index < d->mAvailableStandardOutput.count()) {
0160             newOutputs.append(d->mAvailableStandardOutput.at(index));
0161 
0162             ++index;
0163         }
0164 
0165         qCDebug(KSYSTEMLOG) << "Retrieving a part of the file...";
0166 
0167         Q_EMIT contentChanged(this, Analyzer::UpdatingRead, newOutputs);
0168     }
0169     // Else reread all lines, clear log list
0170     else {
0171         qCDebug(KSYSTEMLOG) << "New process or process already read. Reading entire content";
0172 
0173         Q_EMIT contentChanged(this, Analyzer::FullRead, d->mAvailableStandardOutput);
0174     }
0175 
0176     closeProcess();
0177 }
0178 
0179 void ProcessOutputLogFileReader::logFileModified()
0180 {
0181     Q_D(ProcessOutputLogFileReader);
0182 
0183     qCDebug(KSYSTEMLOG) << "Content available on process output...";
0184 
0185     // New added lines
0186     QByteArray const bytesOutput = d->mProcess->readAllStandardOutput();
0187     d->mBuffer.append(QLatin1String(bytesOutput));
0188 
0189     // Parse buffer
0190     int endLinePos = d->mBuffer.indexOf(QLatin1String("\n"));
0191     while (true) {
0192         if (endLinePos == -1) {
0193             break;
0194         }
0195 
0196         // Add the new found lines and
0197         d->mAvailableStandardOutput.append(d->mBuffer.left(endLinePos));
0198         d->mBuffer.remove(0, endLinePos + 1);
0199 
0200         endLinePos = d->mBuffer.indexOf(QLatin1String("\n"));
0201     }
0202 
0203     qCDebug(KSYSTEMLOG) << "Received a total of" << d->mAvailableStandardOutput.count() << "new lines";
0204 }
0205 
0206 /**
0207  * The buffer could contains some last characters that are added at last
0208  * (Normally useless)
0209  */
0210 void ProcessOutputLogFileReader::emptyBuffer()
0211 {
0212     Q_D(ProcessOutputLogFileReader);
0213 
0214     if (!d->mBuffer.isEmpty()) {
0215         qCWarning(KSYSTEMLOG) << "Buffer was not empty !!";
0216         d->mAvailableStandardOutput.append(d->mBuffer);
0217         d->mBuffer.clear();
0218     }
0219 }
0220 
0221 #include "moc_processOutputLogFileReader.cpp"