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 }