diff --git a/ksgrd/SensorAgent.cpp b/ksgrd/SensorAgent.cpp index d299519..924243c 100644 --- a/ksgrd/SensorAgent.cpp +++ b/ksgrd/SensorAgent.cpp @@ -1,309 +1,302 @@ /* KSysGuard, the KDE System Guard Copyright (c) 1999 - 2001 Chris Schlaeger This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ //#include #include "ksgrd_debug.h" #include #include "SensorClient.h" #include "SensorManager.h" #include "SensorAgent.h" /** This can be used to debug communication problems with the daemon. Should be set to 0 in any production version. */ #define SA_TRACE 0 using namespace KSGRD; SensorAgent::SensorAgent( SensorManager *sm ) : QObject(sm) { mSensorManager = sm; mDaemonOnLine = false; } SensorAgent::~SensorAgent() { for(int i = mInputFIFO.size()-1; i >= 0; --i) delete mInputFIFO.takeAt(i); for(int i = mProcessingFIFO.size()-1; i >= 0; --i) delete mProcessingFIFO.takeAt(i); } void SensorAgent::sendRequest( const QString &req, SensorClient *client, int id ) { SensorRequest *sensorreq = nullptr; - for(int i =0, total = mInputFIFO.size(); i < total; ++i) { - sensorreq = mInputFIFO.at(i); - if(id == sensorreq->id() && client == sensorreq->client() && req == sensorreq->request()) { - executeCommand(); - return; //don't bother to resend the same request if we already have it in our queue to send - } - } - for(int i =0, total = mProcessingFIFO.size(); i < total; ++i) { - sensorreq = mProcessingFIFO.at(i); - if(id == sensorreq->id() && client == sensorreq->client() && req == sensorreq->request()) - return; //don't bother to resend the same request if we have already sent the request to client and just waiting for an answer - } + SensorRequest nRequest { req, client, id }; + if (mUnderwayRequests.contains(nRequest)) + return; + mUnderwayRequests.insert(nRequest); /* The request is registered with the FIFO so that the answer can be * routed back to the requesting client. */ mInputFIFO.enqueue( new SensorRequest( req, client, id ) ); #if SA_TRACE qCDebug(LIBKSYSGUARD_KSGRD) << "-> " << req << "(" << mInputFIFO.count() << "/" << mProcessingFIFO.count() << ")" << endl; #endif executeCommand(); } void SensorAgent::processAnswer( const char *buf, int buflen ) { //It is possible for an answer/error message to be split across multiple processAnswer calls. This makes our life more difficult //We have to keep track of the state we are in. Any characters that we have not parsed yet we put in //mLeftOverBuffer QByteArray buffer = QByteArray::fromRawData(buf, buflen); if(!mLeftOverBuffer.isEmpty()) { buffer = mLeftOverBuffer + buffer; //If we have data left over from a previous processAnswer, then we have to prepend this on mLeftOverBuffer.clear(); } #if SA_TRACE qCDebug(LIBKSYSGUARD_KSGRD) << "<- " << QString::fromUtf8(buffer, buffer.size()); #endif int startOfAnswer = 0; //This can become >= buffer.size(), so check before using! for ( int i = 0; i < buffer.size(); ++i ) { if ( buffer.at(i) == '\033' ) { // 033 in octal is the escape character. The signifies the start of an error int startOfError = i; bool found = false; while(++i < buffer.size()) { if(buffer.at(i) == '\033') { QString error = QString::fromUtf8(buffer.constData() + startOfError+1, i-startOfError-1); if ( error.startsWith(QLatin1String("RECONFIGURE")) ) { emit reconfigure( this ); } else { /* We just received the end of an error message, so we * can display it. */ SensorMgr->notify( i18nc( "%1 is a host name", "Message from %1:\n%2", mHostName , error ) ); } found = true; break; } } if(found) { buffer.remove(startOfError, i-startOfError+1); i = startOfAnswer - 1; continue; } else { //We have not found the end of the escape string. Try checking in the next packet mLeftOverBuffer = QByteArray(buffer.constData()+startOfAnswer, buffer.size()-startOfAnswer); return; } } //The spec was supposed to be that it returned "\nksysguardd> " but some seem to forget the space, so we have to compensate. Sigh if( (i==startOfAnswer && buffer.size() -i >= (signed)(sizeof("ksysguardd>" ))-1 && qstrncmp(buffer.constData()+i, "ksysguardd>", sizeof("ksysguardd>" )-1) == 0) || (buffer.size() -i >= (signed)(sizeof("\nksysguardd>"))-1 && qstrncmp(buffer.constData()+i, "\nksysguardd>", sizeof("\nksysguardd>")-1) == 0)) { QByteArray answer(buffer.constData()+startOfAnswer, i-startOfAnswer); if(!answer.isEmpty()) mAnswerBuffer << answer; #if SA_TRACE qCDebug(LIBKSYSGUARD_KSGRD) << "<= " << mAnswerBuffer << "(" << mInputFIFO.count() << "/" << mProcessingFIFO.count() << ")" << endl; #endif if(buffer.at(i) == '\n') i++; i += sizeof("ksysguardd>") -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 if(i+1 < buffer.size() && buffer.at(i+1) == ' ') i++; startOfAnswer = i+1; //We have found the end of one reply if ( !mDaemonOnLine ) { /* First '\nksysguardd> ' signals that the daemon is * ready to serve requests now. */ mDaemonOnLine = true; #if SA_TRACE qCDebug(LIBKSYSGUARD_KSGRD) << "Daemon now online!"; #endif mAnswerBuffer.clear(); continue; } //Deal with the answer we have now read in // remove pending request from FIFO if ( mProcessingFIFO.isEmpty() ) { qCDebug(LIBKSYSGUARD_KSGRD) << "ERROR: Received answer but have no pending " << "request!" << endl; mAnswerBuffer.clear(); continue; } SensorRequest *req = mProcessingFIFO.dequeue(); + mUnderwayRequests.remove(*req); // we are now responsible for the memory of req - we must delete it! if ( !req->client() ) { /* The client has disappeared before receiving the answer * to his request. */ delete req; mAnswerBuffer.clear(); continue; } if(!mAnswerBuffer.isEmpty() && mAnswerBuffer[0] == "UNKNOWN COMMAND") { /* Notify client that the sensor seems to be no longer available. */ qCDebug(LIBKSYSGUARD_KSGRD) << "Received UNKNOWN COMMAND for: " << req->request(); req->client()->sensorLost( req->id() ); } else { // Notify client of newly arrived answer. req->client()->answerReceived( req->id(), mAnswerBuffer ); } delete req; mAnswerBuffer.clear(); } else if(buffer.at(i) == '\n'){ mAnswerBuffer << QByteArray(buffer.constData()+startOfAnswer, i-startOfAnswer); startOfAnswer = i+1; } } mLeftOverBuffer += QByteArray(buffer.constData()+startOfAnswer, buffer.size()-startOfAnswer); executeCommand(); } void SensorAgent::executeCommand() { /* This function is called whenever there is a chance that we have a * command to pass to the daemon. But the command may only be sent * if the daemon is online and there is no other command currently * being sent. */ if ( mDaemonOnLine && !mInputFIFO.isEmpty() ) { SensorRequest *req = mInputFIFO.dequeue(); #if SA_TRACE qCDebug(LIBKSYSGUARD_KSGRD) << ">> " << req->request() << "(" << mInputFIFO.count() << "/" << mProcessingFIFO.count() << ")" << endl; #endif // send request to daemon QString cmdWithNL = req->request() + '\n'; if ( !writeMsg( cmdWithNL.toLatin1().constData(), cmdWithNL.length() ) ) qCDebug(LIBKSYSGUARD_KSGRD) << "SensorAgent::writeMsg() failed"; // add request to processing FIFO. // Note that this means that mProcessingFIFO is now responsible for managing the memory for it. mProcessingFIFO.enqueue( req ); } } void SensorAgent::disconnectClient( SensorClient *client ) { for (int i = 0, total = mInputFIFO.size(); i < total; ++i) if ( mInputFIFO[i]->client() == client ) mInputFIFO[i]->setClient(nullptr); for (int i = 0, total = mProcessingFIFO.size(); i < total; ++i) if ( mProcessingFIFO[i]->client() == client ) mProcessingFIFO[i]->setClient( nullptr ); } SensorManager *SensorAgent::sensorManager() { return mSensorManager; } void SensorAgent::setDaemonOnLine( bool value ) { mDaemonOnLine = value; } bool SensorAgent::daemonOnLine() const { return mDaemonOnLine; } void SensorAgent::setHostName( const QString &hostName ) { mHostName = hostName; } QString SensorAgent::hostName() const { return mHostName; } QString SensorAgent::reasonForOffline() const { return mReasonForOffline; } void SensorAgent::setReasonForOffline(const QString &reasonForOffline) { mReasonForOffline = reasonForOffline; } SensorRequest::SensorRequest( const QString &request, SensorClient *client, int id ) : mRequest( request ), mClient( client ), mId( id ) { } SensorRequest::~SensorRequest() { } void SensorRequest::setRequest( const QString &request ) { mRequest = request; } QString SensorRequest::request() const { return mRequest; } void SensorRequest::setClient( SensorClient *client ) { mClient = client; } SensorClient *SensorRequest::client() { return mClient; } void SensorRequest::setId( int id ) { mId = id; } int SensorRequest::id() { return mId; } diff --git a/ksgrd/SensorAgent.h b/ksgrd/SensorAgent.h index 62a3b25..8311f5b 100644 --- a/ksgrd/SensorAgent.h +++ b/ksgrd/SensorAgent.h @@ -1,138 +1,147 @@ /* KSysGuard, the KDE System Guard Copyright (c) 1999, 2000 Chris Schlaeger This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KSG_SENSORAGENT_H #define KSG_SENSORAGENT_H #include #include #include - +#include class QString; namespace KSGRD { class SensorClient; class SensorManager; class SensorRequest; /** The SensorAgent depending on the type of requested connection starts a ksysguardd process or connects through a tcp connection to a running ksysguardd and handles the asynchronous communication. It keeps a list of pending requests that have not been answered yet by ksysguardd. The current implementation only allows one pending requests. Incoming requests are queued in an input FIFO. */ class Q_DECL_EXPORT SensorAgent : public QObject { Q_OBJECT public: explicit SensorAgent( SensorManager *sm ); ~SensorAgent() override; virtual bool start( const QString &host, const QString &shell, const QString &command = QLatin1String(""), int port = -1 ) = 0; /** This function should only be used by the SensorManager and never by the SensorClients directly since the pointer returned by engaged is not guaranteed to be valid. Only the SensorManager knows whether a SensorAgent pointer is still valid or not. This function sends out a command to the sensor and notifies the agent to return the answer to 'client'. The 'id' can be used by the client to identify the answer. It is only passed through and never used by the SensorAgent. So it can be any value the client suits to use. */ void sendRequest( const QString &req, SensorClient *client, int id = 0 ); virtual void hostInfo( QString &sh, QString &cmd, int &port ) const = 0; void disconnectClient( SensorClient *client ); QString hostName() const; bool daemonOnLine() const; QString reasonForOffline() const; Q_SIGNALS: void reconfigure( const SensorAgent* ); protected: void processAnswer( const char *buf, int buflen ); void executeCommand(); SensorManager *sensorManager(); void setDaemonOnLine( bool value ); void setHostName( const QString &hostName ); void setReasonForOffline(const QString &reasonForOffline); private: virtual bool writeMsg( const char *msg, int len ) = 0; QString mReasonForOffline; QQueue< SensorRequest* > mInputFIFO; QQueue< SensorRequest* > mProcessingFIFO; QList mAnswerBuffer; ///A single reply can be on multiple lines. QString mErrorBuffer; QByteArray mLeftOverBuffer; ///Any data read in but not terminated is copied into here, awaiting the next load of data QPointer mSensorManager; bool mDaemonOnLine; QString mHostName; + QSet mUnderwayRequests; }; /** This auxiliary class is used to store requests during their processing. */ class SensorRequest { public: SensorRequest( const QString &request, SensorClient *client, int id ); ~SensorRequest(); void setRequest( const QString& ); QString request() const; void setClient( SensorClient* ); SensorClient *client(); void setId( int ); int id(); + friend uint qHash(const SensorRequest& sr, uint seed=0) { + return qHash(qMakePair(sr.mRequest, qMakePair(sr.mClient, sr.mId)), seed); + } + friend bool operator==(const SensorRequest& a, const SensorRequest& b) { + return a.mRequest == b.mRequest && + a.mClient == b.mClient && + a.mId == b.mId; + } private: QString mRequest; SensorClient *mClient = nullptr; int mId; }; } #endif