File indexing completed on 2024-05-05 05:34:26

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