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 }