diff --git a/ksgrd/SensorAgent.cpp b/ksgrd/SensorAgent.cpp index d299519..60a38e0 100644 --- a/ksgrd/SensorAgent.cpp +++ b/ksgrd/SensorAgent.cpp @@ -1,309 +1,309 @@ /* 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 } /* 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; + << "request!"; mAnswerBuffer.clear(); continue; } SensorRequest *req = mProcessingFIFO.dequeue(); // 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/SensorShellAgent.cpp b/ksgrd/SensorShellAgent.cpp index bf943f7..f0aee29 100644 --- a/ksgrd/SensorShellAgent.cpp +++ b/ksgrd/SensorShellAgent.cpp @@ -1,149 +1,149 @@ /* 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 #include #include #include "ksgrd_debug.h" #include "SensorManager.h" #include "SensorShellAgent.h" using namespace KSGRD; SensorShellAgent::SensorShellAgent( SensorManager *sm ) : SensorAgent( sm ), mDaemon( nullptr ) { } SensorShellAgent::~SensorShellAgent() { if ( mDaemon ) { mDaemon->write( "quit\n", sizeof( "quit\n" )-1 ); mDaemon->disconnect(); mDaemon->waitForFinished(); delete mDaemon; mDaemon = nullptr; } } bool SensorShellAgent::start( const QString &host, const QString &shell, const QString &command, int ) { mDaemon = new KProcess(); mDaemon->setOutputChannelMode( KProcess::SeparateChannels ); mRetryCount=3; setHostName( host ); mShell = shell; mCommand = command; connect( mDaemon, SIGNAL(error(QProcess::ProcessError)), SLOT(daemonError(QProcess::ProcessError)) ); connect( mDaemon, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(daemonExited(int,QProcess::ExitStatus)) ); connect( mDaemon.data(), &QProcess::readyReadStandardOutput, this, &SensorShellAgent::msgRcvd ); connect( mDaemon.data(), &QProcess::readyReadStandardError, this, &SensorShellAgent::errMsgRcvd ); if ( !command.isEmpty() ) { *mDaemon << KShell::splitArgs(command); } else *mDaemon << mShell << hostName() << QStringLiteral("ksysguardd"); mDaemon->start(); return true; } void SensorShellAgent::hostInfo( QString &shell, QString &command, int &port) const { shell = mShell; command = mCommand; port = -1; } void SensorShellAgent::msgRcvd( ) { QByteArray buffer = mDaemon->readAllStandardOutput(); mRetryCount = 3; //we received an answer, so reset our retry count back to 3 processAnswer( buffer.constData(), buffer.size()); } void SensorShellAgent::errMsgRcvd( ) { const QByteArray buffer = mDaemon->readAllStandardOutput(); // Because we read the error buffer in chunks, we may not have a proper utf8 string. // We should never get input over stderr anyway, so no need to worry too much about it. // But if this is extended, we will need to handle this better const QString buf = QString::fromUtf8( buffer ); qCDebug(LIBKSYSGUARD_KSGRD) << "SensorShellAgent: Warning, received text over stderr!" - << endl << buf << endl; + << "\n" << buf; } void SensorShellAgent::daemonExited( int exitCode, QProcess::ExitStatus exitStatus ) { Q_UNUSED(exitCode); qCDebug(LIBKSYSGUARD_KSGRD) << "daemon exited, exit status " << exitStatus; if ( mRetryCount-- <= 0 || (mDaemon->start(), !mDaemon->waitForStarted()) ) { setDaemonOnLine( false ); if(sensorManager()) { sensorManager()->disengage( this ); //delete ourselves } } } void SensorShellAgent::daemonError( QProcess::ProcessError errorStatus ) { QString error; switch(errorStatus) { case QProcess::FailedToStart: qCDebug(LIBKSYSGUARD_KSGRD) << "failed to run" << mDaemon->program().join(QLatin1Char(' ')); error = i18n("Could not run daemon program '%1'.", mDaemon->program().join(" ")); break; case QProcess::Crashed: case QProcess::Timedout: case QProcess::WriteError: case QProcess::ReadError: default: error = i18n("The daemon program '%1' failed.", mDaemon->program().join(" ")); } setReasonForOffline(error); qCDebug(LIBKSYSGUARD_KSGRD) << "Error received " << error << "(" << errorStatus << ")"; setDaemonOnLine( false ); if(sensorManager()) sensorManager()->disengage( this ); //delete ourselves } bool SensorShellAgent::writeMsg( const char *msg, int len ) { //write returns -1 on error, in which case we should return false. true otherwise. return mDaemon->write( msg, len ) != -1; }