From ce5371526526100fa2992ef10d82e2c6ba462aa7 Mon Sep 17 00:00:00 2001 From: Ken Moore Date: Fri, 27 Oct 2017 13:35:42 -0400 Subject: [PATCH] Make sure the system update check is done via a DISPATCHER process. This prevent possible hangs in the main server thread from the health check, and also ensures that the system update check API call can now return a new "checkingforupdates" status. --- src/server/Dispatcher.cpp | 22 +++++++++++++++++-- src/server/Dispatcher.h | 3 ++- src/server/DispatcherParsing.cpp | 33 ++++++++++++++++++---------- src/server/EventWatcher.cpp | 3 +++ src/server/library/sysadm-update.cpp | 32 ++++++++++++++++++++++----- src/server/library/sysadm-update.h | 4 +++- 6 files changed, 76 insertions(+), 21 deletions(-) diff --git a/src/server/Dispatcher.cpp b/src/server/Dispatcher.cpp index 98ba91f..671464e 100644 --- a/src/server/Dispatcher.cpp +++ b/src/server/Dispatcher.cpp @@ -182,6 +182,24 @@ QJsonObject Dispatcher::killJobs(QStringList ids){ return obj; } +bool Dispatcher::isJobActive(QString ID){ + //qDebug() << " - Is Job Active:" << ID; + for(int i=0; i(i); + if(HASH.contains(queue)){ + QList list = HASH[queue]; + for(int j=0; jID){ + //qDebug() << " -- " << !list[j]->isDone(); + return !(list[j]->isDone()); + } + } //end loop over list + } + } + //qDebug() << " -- NO"; + return false; //could not find process with this ID +} + void Dispatcher::start(QString queuefile){ //Setup connections here (in case it was moved to different thread after creation) //connect(this, SIGNAL(mkprocs(Dispatcher::PROC_QUEUE, DProcess*)), this, SLOT(mkProcs(Dispatcher::PROC_QUEUE, DProcess*)) ); @@ -243,7 +261,7 @@ void Dispatcher::ProcFinished(QString ID, QJsonObject log){ //qDebug() << " - Got Proc Finished Signal:" << ID; LogManager::log(LogManager::DISPATCH, log); //First emit any subsystem-specific event, falling back on the raw log - QJsonObject ev = CreateDispatcherEventNotification(ID,log); + QJsonObject ev = CreateDispatcherEventNotification(ID,log, true); if(!ev.isEmpty()){ emit DispatchEvent(ev); }else{ @@ -254,7 +272,7 @@ void Dispatcher::ProcFinished(QString ID, QJsonObject log){ void Dispatcher::ProcUpdated(QString ID, QJsonObject log){ //See if this needs to generate an event - QJsonObject ev = CreateDispatcherEventNotification(ID,log); + QJsonObject ev = CreateDispatcherEventNotification(ID,log, false); if(!ev.isEmpty()){ emit DispatchEvent(ev); } diff --git a/src/server/Dispatcher.h b/src/server/Dispatcher.h index 2e7a796..db3122e 100644 --- a/src/server/Dispatcher.h +++ b/src/server/Dispatcher.h @@ -63,6 +63,7 @@ public: QJsonObject listJobs(); QJsonObject killJobs(QStringList ids); + bool isJobActive(QString ID); //returns true if a job with this ID is running/pending public slots: //Main start/stop @@ -84,7 +85,7 @@ private: //Simplification routine for setting up a process DProcess* createProcess(QString ID, QStringList cmds, QString workdir = ""); - QJsonObject CreateDispatcherEventNotification(QString, QJsonObject); + QJsonObject CreateDispatcherEventNotification(QString, QJsonObject, bool); // Functions to do parsing out dispatcher queued tasks // Please keep these sorted diff --git a/src/server/DispatcherParsing.cpp b/src/server/DispatcherParsing.cpp index b418f65..6413385 100644 --- a/src/server/DispatcherParsing.cpp +++ b/src/server/DispatcherParsing.cpp @@ -9,8 +9,11 @@ #include "globals-qt.h" #include "EventWatcher.h" #include "Dispatcher.h" +#include "library/sysadm-update.h" + +QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObject log, bool full_log){ + //NOTE: full_log = true when the process has finished. If it is false, the process is still running and you are probably getting an incremental update of the process log -QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObject log){ //key outputs - need to set these if an event is going to be sent out QJsonObject args; //any arguments to send out QString namesp, name; //the namespace/name of the subsystem used @@ -26,7 +29,7 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec //Add the generic process values args.insert("state",log.value("state").toString()); args.insert("process_details", log); //full process log array here - + //Now parse the notification based on the dispatch ID or current command //NOTE: There might be a random string on the end of the ID (to accomodate similar process calls) // == sysadm/iohyve == @@ -37,14 +40,18 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec //Do some parsing of the log parseIohyveFetchOutput(cLog,&args); } - + // == sysadm/update == }else if(ID.startsWith("sysadm_update")){ - namesp = "sysadm"; name="update"; - //No special parsing here: the pc-updatemanager output should be available as-is - args.insert("update_log",cLog); - - + if(ID.section("::",0,0)=="sysadm_update_runupdates"){ + namesp = "sysadm"; name="update"; + //No special parsing here: the pc-updatemanager output should be available as-is + args.insert("update_log",cLog); + }else if(full_log && ID.section("::",0,0)=="sysadm_update_checkupdates"){ + //qDebug() << "Got update check process finished"; + sysadm::Update::saveCheckUpdateLog(cLog); //save this for use later + } + // == sysadm/pkg == }else if(ID.startsWith("sysadm_pkg")){ namesp = "sysadm"; name="pkg"; @@ -56,7 +63,7 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec bool hasupdates = !cLog.contains("Your packages are up to date."); args.insert("updates_available", hasupdates ? "true" : "false"); } - + }else if(ID.section("-",0,0)=="sysadm_pkg_audit" && isFinished){ QStringList info = cLog.split("\n"); QStringList vuln, effects; @@ -71,9 +78,9 @@ QJsonObject Dispatcher::CreateDispatcherEventNotification(QString ID, QJsonObjec args.insert("vulnerable_pkgs",QJsonArray::fromStringList(vuln)); args.insert("impacts_pkgs",QJsonArray::fromStringList(effects)); } - + } - + //Now assemble the output as needed if(namesp.isEmpty() || name.isEmpty()){ return QJsonObject(); } //no event args.insert("event_system",namesp+"/"+name); @@ -93,3 +100,7 @@ void Dispatcher::parseIohyveFetchOutput(QString outputLog, QJsonObject *out){ break; } } + +/*void Dispatcher::parseUpdateCheckOutput(QString outputLog, QJsonObject *out){ + +}*/ diff --git a/src/server/EventWatcher.cpp b/src/server/EventWatcher.cpp index b67e114..3770bb6 100644 --- a/src/server/EventWatcher.cpp +++ b/src/server/EventWatcher.cpp @@ -368,6 +368,7 @@ void EventWatcher::CheckSystemState(){ //Next Check for Updates QJsonObject updates = sysadm::Update::checkUpdates(true); //do the "fast" version of updates + //qDebug() << "Health check - got updates status:" << updates; if(!updates.isEmpty()){ if(updates.value("status").toString()!="noupdates"){ int tmp = 2; @@ -387,6 +388,8 @@ void EventWatcher::CheckSystemState(){ } } } + }else if(updates.value("status").toString()=="checkingforupdates"){ + //do nothing more here - still checking for updates }else if(updates.value("status").toString()!="updaterunning"){ //updates are available - see if the auto-update flag is set, and start the updates as needed QJsonObject upset = sysadm::Update::readSettings(); diff --git a/src/server/library/sysadm-update.cpp b/src/server/library/sysadm-update.cpp index 800bd7a..2b5220f 100644 --- a/src/server/library/sysadm-update.cpp +++ b/src/server/library/sysadm-update.cpp @@ -39,6 +39,11 @@ QJsonObject Update::checkUpdates(bool fast) { if(!QFile::exists("/usr/local/bin/pc-updatemanager")){ return retObject; } + //See if the check for updates is currently running - just return nothing at the moment for that + if(DISPATCHER->isJobActive("sysadm_update_checkupdates")){ + retObject.insert("status", "checkingforupdates"); + return retObject; + } //Check if the system is waiting to reboot if(QFile::exists(UP_RBFILE)){ retObject.insert("status","rebootrequired"); @@ -57,7 +62,7 @@ QJsonObject Update::checkUpdates(bool fast) { return retObject; } } - //Get the list of deatils from the update checks (fast/full) + //Get the list of details from the update checks (fast/full) QStringList output; QDateTime cdt = QDateTime::currentDateTime(); QDateTime fdt = cdt.addDays(-1); //just enough to trip the checks below if needed @@ -72,18 +77,23 @@ QJsonObject Update::checkUpdates(bool fast) { //Note: The "fast" check will only be used if the last full check was less than 12 hours earlier. //qDebug() << " - UseFast Re-read"; output = General::readTextFile(UP_UPFILE); - }else if(secs<600 ){ - //Note: This will re-use the previous check if it was less than 10 minutes ago (prevent hammering servers from user checks) - //qDebug() << " - Use Fast Re-read (failsafe - less than 10 minute interval)"; + }else if(secs<300 ){ + //Note: This will re-use the previous check if it was less than 5 minutes ago (prevent hammering servers from user checks) + //qDebug() << " - Use Fast Re-read (failsafe - less than 5 minute interval)"; output = General::readTextFile(UP_UPFILE); }else{ //qDebug() << " - Run full check"; - General::RunCommand("pc-updatemanager syncconf"); //always resync the config file before starting an update check + QStringList cmds; cmds << "pc-updatemanager syncconf" << "pc-updatemanager pkgcheck"; + DISPATCHER->queueProcess("sysadm_update_checkupdates", cmds ); + retObject.insert("status", "checkingforupdates"); + //qDebug() << " - Done starting check"; + return retObject; + /*General::RunCommand("pc-updatemanager syncconf"); //always resync the config file before starting an update check output.append( General::RunCommand("pc-updatemanager pkgcheck").split("\n") ); while(output.last().simplified()==""){ output.removeLast(); } if(!output.last().contains("ERROR:")){ //make sure there was network access available first - otherwise let it try again soon General::writeTextFile(UP_UPFILE, output); //save this check for later "fast" updates - } + }*/ } //qDebug() << "pc-updatemanager checks:" << output; @@ -139,6 +149,16 @@ QJsonObject Update::checkUpdates(bool fast) { return retObject; } +void Update::saveCheckUpdateLog(QString log){ + QStringList output = log.split("\n"); + qDebug() << "Got Check Update Log:" << log << output; + while(!output.isEmpty() && output.last().simplified().isEmpty()){ output.removeLast(); } + if(output.isEmpty()){ return; } + if(!output.last().contains("ERROR:")){ //make sure there was network access available first - otherwise let it try again soon + General::writeTextFile(UP_UPFILE, output); //save this check for later "fast" updates + } +} + // List available branches we can switch to QJsonObject Update::listBranches() { QJsonObject retObject; diff --git a/src/server/library/sysadm-update.h b/src/server/library/sysadm-update.h index 64438cf..183fc7f 100644 --- a/src/server/library/sysadm-update.h +++ b/src/server/library/sysadm-update.h @@ -19,6 +19,8 @@ public: static QDateTime rebootRequiredSince(); //Listing routines static QJsonObject checkUpdates(bool fast = false); + static void saveCheckUpdateLog(QString); //Internal for Dispatcher process usage - do not expose to public API + static QJsonObject listBranches(); //Start/stop update routine static QJsonObject startUpdate(QJsonObject); @@ -31,7 +33,7 @@ public: static QJsonObject readLog(QJsonObject); }; - + } #endif