File indexing completed on 2024-05-05 17:39:48

0001 /*
0002     KSysGuard, the KDE System Guard
0003 
0004     SPDX-FileCopyrightText: 1999-2001 Chris Schlaeger <cs@kde.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 
0008 */
0009 
0010 //#include <stdlib.h>
0011 
0012 #include "ksgrd_debug.h"
0013 #include <KLocalizedString>
0014 
0015 #include "SensorClient.h"
0016 #include "SensorManager.h"
0017 
0018 #include "SensorAgent.h"
0019 
0020 /**
0021   This can be used to debug communication problems with the daemon.
0022   Should be set to 0 in any production version.
0023 */
0024 #define SA_TRACE 0
0025 
0026 using namespace KSGRD;
0027 
0028 SensorAgent::SensorAgent(SensorManager *sm)
0029     : QObject(sm)
0030     , mSensorManager(sm)
0031 {
0032     mDaemonOnLine = false;
0033 }
0034 
0035 SensorAgent::~SensorAgent()
0036 {
0037     for (int i = mInputFIFO.size() - 1; i >= 0; --i)
0038         delete mInputFIFO.takeAt(i);
0039     for (int i = mProcessingFIFO.size() - 1; i >= 0; --i)
0040         delete mProcessingFIFO.takeAt(i);
0041 }
0042 
0043 void SensorAgent::sendRequest(const QString &req, SensorClient *client, int id)
0044 {
0045     SensorRequest nRequest{req, client, id};
0046     if (mUnderwayRequests.contains(nRequest))
0047         return;
0048     mUnderwayRequests.insert(nRequest);
0049 
0050     /* The request is registered with the FIFO so that the answer can be
0051      * routed back to the requesting client. */
0052     mInputFIFO.enqueue(new SensorRequest(req, client, id));
0053 
0054 #if SA_TRACE
0055     qCDebug(LIBKSYSGUARD_KSGRD) << "-> " << req << "(" << mInputFIFO.count() << "/" << mProcessingFIFO.count() << ")" << endl;
0056 #endif
0057     executeCommand();
0058 }
0059 
0060 void SensorAgent::processAnswer(const char *buf, int buflen)
0061 {
0062     // It is possible for an answer/error message  to be split across multiple processAnswer calls.  This makes our life more difficult
0063     // We have to keep track of the state we are in.  Any characters that we have not parsed yet we put in
0064     // mLeftOverBuffer
0065     QByteArray buffer = QByteArray::fromRawData(buf, buflen);
0066     if (!mLeftOverBuffer.isEmpty()) {
0067         buffer = mLeftOverBuffer + buffer; // If we have data left over from a previous processAnswer, then we have to prepend this on
0068         mLeftOverBuffer.clear();
0069     }
0070 
0071 #if SA_TRACE
0072     qCDebug(LIBKSYSGUARD_KSGRD) << "<- " << QString::fromUtf8(buffer, buffer.size());
0073 #endif
0074     int startOfAnswer = 0; // This can become >= buffer.size(), so check before using!
0075     for (int i = 0; i < buffer.size(); ++i) {
0076         if (buffer.at(i) == '\033') { // 033 in octal is the escape character.  The signifies the start of an error
0077             int startOfError = i;
0078             bool found = false;
0079             while (++i < buffer.size()) {
0080                 if (buffer.at(i) == '\033') {
0081                     QString error = QString::fromUtf8(buffer.constData() + startOfError + 1, i - startOfError - 1);
0082                     if (error.startsWith(QLatin1String("RECONFIGURE"))) {
0083                         Q_EMIT reconfigure(this);
0084                     } else {
0085                         /* We just received the end of an error message, so we
0086                          * can display it. */
0087                         SensorMgr->notify(i18nc("%1 is a host name", "Message from %1:\n%2", mHostName, error));
0088                     }
0089                     found = true;
0090                     break;
0091                 }
0092             }
0093             if (found) {
0094                 buffer.remove(startOfError, i - startOfError + 1);
0095                 i = startOfAnswer - 1;
0096                 continue;
0097             } else {
0098                 // We have not found the end of the escape string.  Try checking in the next packet
0099                 mLeftOverBuffer = QByteArray(buffer.constData() + startOfAnswer, buffer.size() - startOfAnswer);
0100                 return;
0101             }
0102         }
0103 
0104         // The spec was supposed to be that it returned "\nksysguardd> " but some seem to forget the space, so we have to compensate.  Sigh
0105         if ((i == startOfAnswer && buffer.size() - i >= (signed)(sizeof("ksysguardd>")) - 1
0106              && qstrncmp(buffer.constData() + i, "ksysguardd>", sizeof("ksysguardd>") - 1) == 0)
0107             || (buffer.size() - i >= (signed)(sizeof("\nksysguardd>")) - 1
0108                 && qstrncmp(buffer.constData() + i, "\nksysguardd>", sizeof("\nksysguardd>") - 1) == 0)) {
0109             QByteArray answer(buffer.constData() + startOfAnswer, i - startOfAnswer);
0110             if (!answer.isEmpty())
0111                 mAnswerBuffer << answer;
0112 #if SA_TRACE
0113             qCDebug(LIBKSYSGUARD_KSGRD) << "<= " << mAnswerBuffer << "(" << mInputFIFO.count() << "/" << mProcessingFIFO.count() << ")" << endl;
0114 #endif
0115             if (buffer.at(i) == '\n')
0116                 i++;
0117             i += sizeof("ksysguardd>")
0118                 - 2; // Move i on to the next answer (if any). -2 because sizeof adds one for \0  and the for loop will increment by 1 also
0119             if (i + 1 < buffer.size() && buffer.at(i + 1) == ' ')
0120                 i++;
0121             startOfAnswer = i + 1;
0122 
0123             // We have found the end of one reply
0124             if (!mDaemonOnLine) {
0125                 /* First '\nksysguardd> ' signals that the daemon is
0126                  * ready to serve requests now. */
0127                 mDaemonOnLine = true;
0128 #if SA_TRACE
0129                 qCDebug(LIBKSYSGUARD_KSGRD) << "Daemon now online!";
0130 #endif
0131                 mAnswerBuffer.clear();
0132                 continue;
0133             }
0134 
0135             // Deal with the answer we have now read in
0136 
0137             // remove pending request from FIFO
0138             if (mProcessingFIFO.isEmpty()) {
0139                 qCDebug(LIBKSYSGUARD_KSGRD) << "ERROR: Received answer but have no pending "
0140                                             << "request!";
0141                 mAnswerBuffer.clear();
0142                 continue;
0143             }
0144 
0145             SensorRequest *req = mProcessingFIFO.dequeue();
0146             mUnderwayRequests.remove(*req);
0147             // we are now responsible for the memory of req - we must delete it!
0148             if (!req->client()) {
0149                 /* The client has disappeared before receiving the answer
0150                  * to his request. */
0151                 delete req;
0152                 mAnswerBuffer.clear();
0153                 continue;
0154             }
0155 
0156             if (!mAnswerBuffer.isEmpty() && mAnswerBuffer[0] == "UNKNOWN COMMAND") {
0157                 /* Notify client that the sensor seems to be no longer available. */
0158                 qCDebug(LIBKSYSGUARD_KSGRD) << "Received UNKNOWN COMMAND for: " << req->request();
0159                 req->client()->sensorLost(req->id());
0160             } else {
0161                 // Notify client of newly arrived answer.
0162                 req->client()->answerReceived(req->id(), mAnswerBuffer);
0163             }
0164             delete req;
0165             mAnswerBuffer.clear();
0166         } else if (buffer.at(i) == '\n') {
0167             mAnswerBuffer << QByteArray(buffer.constData() + startOfAnswer, i - startOfAnswer);
0168             startOfAnswer = i + 1;
0169         }
0170     }
0171 
0172     mLeftOverBuffer += QByteArray(buffer.constData() + startOfAnswer, buffer.size() - startOfAnswer);
0173     executeCommand();
0174 }
0175 
0176 void SensorAgent::executeCommand()
0177 {
0178     /* This function is called whenever there is a chance that we have a
0179      * command to pass to the daemon. But the command may only be sent
0180      * if the daemon is online and there is no other command currently
0181      * being sent. */
0182     if (mDaemonOnLine && !mInputFIFO.isEmpty()) {
0183         SensorRequest *req = mInputFIFO.dequeue();
0184 
0185 #if SA_TRACE
0186         qCDebug(LIBKSYSGUARD_KSGRD) << ">> " << req->request() << "(" << mInputFIFO.count() << "/" << mProcessingFIFO.count() << ")" << endl;
0187 #endif
0188         // send request to daemon
0189         QString cmdWithNL = req->request() + '\n';
0190         if (!writeMsg(cmdWithNL.toLatin1().constData(), cmdWithNL.length()))
0191             qCDebug(LIBKSYSGUARD_KSGRD) << "SensorAgent::writeMsg() failed";
0192 
0193         // add request to processing FIFO.
0194         // Note that this means that mProcessingFIFO is now responsible for managing the memory for it.
0195         mProcessingFIFO.enqueue(req);
0196     }
0197 }
0198 
0199 void SensorAgent::disconnectClient(SensorClient *client)
0200 {
0201     for (int i = 0, total = mInputFIFO.size(); i < total; ++i)
0202         if (mInputFIFO[i]->client() == client)
0203             mInputFIFO[i]->setClient(nullptr);
0204     for (int i = 0, total = mProcessingFIFO.size(); i < total; ++i)
0205         if (mProcessingFIFO[i]->client() == client)
0206             mProcessingFIFO[i]->setClient(nullptr);
0207 }
0208 
0209 SensorManager *SensorAgent::sensorManager()
0210 {
0211     return mSensorManager;
0212 }
0213 
0214 void SensorAgent::setDaemonOnLine(bool value)
0215 {
0216     mDaemonOnLine = value;
0217 }
0218 
0219 bool SensorAgent::daemonOnLine() const
0220 {
0221     return mDaemonOnLine;
0222 }
0223 
0224 void SensorAgent::setHostName(const QString &hostName)
0225 {
0226     mHostName = hostName;
0227 }
0228 
0229 QString SensorAgent::hostName() const
0230 {
0231     return mHostName;
0232 }
0233 
0234 QString SensorAgent::reasonForOffline() const
0235 {
0236     return mReasonForOffline;
0237 }
0238 
0239 void SensorAgent::setReasonForOffline(const QString &reasonForOffline)
0240 {
0241     mReasonForOffline = reasonForOffline;
0242 }
0243 
0244 SensorRequest::SensorRequest(const QString &request, SensorClient *client, int id)
0245     : mRequest(request)
0246     , mClient(client)
0247     , mId(id)
0248 {
0249 }
0250 
0251 SensorRequest::~SensorRequest()
0252 {
0253 }
0254 
0255 void SensorRequest::setRequest(const QString &request)
0256 {
0257     mRequest = request;
0258 }
0259 
0260 QString SensorRequest::request() const
0261 {
0262     return mRequest;
0263 }
0264 
0265 void SensorRequest::setClient(SensorClient *client)
0266 {
0267     mClient = client;
0268 }
0269 
0270 SensorClient *SensorRequest::client()
0271 {
0272     return mClient;
0273 }
0274 
0275 void SensorRequest::setId(int id)
0276 {
0277     mId = id;
0278 }
0279 
0280 int SensorRequest::id()
0281 {
0282     return mId;
0283 }