From bf382cbea0455689f1992657169ed023b26cb9ed Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 May 2017 13:52:58 +0200 Subject: [PATCH] Updated updater. Yes, the irony is not lost on me. --- src/controller/controller.cpp | 22 +- src/controller/controller_api.cpp | 9 +- src/controller/controller_updater.cpp | 292 ++++++++++++-------------- src/controller/controller_updater.h | 25 +-- 4 files changed, 155 insertions(+), 193 deletions(-) diff --git a/src/controller/controller.cpp b/src/controller/controller.cpp index 59b05906..6dd27c1b 100644 --- a/src/controller/controller.cpp +++ b/src/controller/controller.cpp @@ -104,21 +104,9 @@ void createAccount (std::string account){ /// Status monitoring thread. /// Will check outputs, inputs and converters every five seconds void statusMonitor(void * np){ - #ifdef UPDATER - unsigned long updatechecker = Util::epoch(); /*LTS*/ - #endif IPC::semaphore configLock(SEM_CONF, O_CREAT | O_RDWR, ACCESSPERMS, 1); Controller::loadActiveConnectors(); while (Controller::conf.is_active){ - /*LTS-START*/ - #ifdef UPDATER - if (Util::epoch() - updatechecker > 3600){ - updatechecker = Util::epoch(); - Controller::CheckUpdateInfo(); - } - #endif - /*LTS-END*/ - //this scope prevents the configMutex from being locked constantly { tthread::lock_guard guard(Controller::configMutex); @@ -349,7 +337,7 @@ int main_loop(int argc, char ** argv){ /*LTS-START*/ #ifdef UPDATER if (Controller::conf.getBool("update")){ - Controller::CheckUpdates(); + Controller::checkUpdates(); } #endif #ifdef LICENSING @@ -370,6 +358,10 @@ int main_loop(int argc, char ** argv){ tthread::thread pushThread(Controller::pushCheckLoop, 0); //start UDP API thread tthread::thread UDPAPIThread(Controller::handleUDPAPI, 0); +#ifdef UPDATER + //start updater thread + tthread::thread updaterThread(Controller::updateThread, 0); +#endif //start main loop @@ -424,6 +416,10 @@ int main_loop(int argc, char ** argv){ HIGH_MSG("Joining license thread..."); licenseThread.join(); #endif + #ifdef UPDATER + HIGH_MSG("Joining updater thread..."); + updaterThread.join(); + #endif /*LTS-END*/ //write config tthread::lock_guard guard(Controller::logMutex); diff --git a/src/controller/controller_api.cpp b/src/controller/controller_api.cpp index 44e86da8..c7e048c1 100644 --- a/src/controller/controller_api.cpp +++ b/src/controller/controller_api.cpp @@ -404,13 +404,10 @@ void Controller::handleAPICommands(JSON::Value & Request, JSON::Value & Response /// #ifdef UPDATER if (Request.isMember("autoupdate")){ - Controller::CheckUpdates(); + Controller::checkUpdates(); } - if (Request.isMember("checkupdate")){ - Controller::updates = Controller::CheckUpdateInfo(); - } - if (Request.isMember("update") || Request.isMember("checkupdate")){ - Response["update"] = Controller::updates; + if (Request.isMember("update") || Request.isMember("checkupdate") || Request.isMember("autoupdate")){ + Controller::insertUpdateInfo(Response["update"]); } #endif /*LTS-END*/ diff --git a/src/controller/controller_updater.cpp b/src/controller/controller_updater.cpp index d3bc39a7..6a1ba897 100644 --- a/src/controller/controller_updater.cpp +++ b/src/controller/controller_updater.cpp @@ -1,87 +1,112 @@ /// \file controller_updater.cpp /// Contains all code for the controller updater. -#include //for files -#include //for stdio -#include //for unlink -#include //for chmod -#include //for srand, rand -#include //for time -#include //for raise -#include -#include -#include -#include -#include -#include "controller_storage.h" -#include "controller_connectors.h" #include "controller_updater.h" +#include "controller_connectors.h" +#include "controller_storage.h" +#include //for files +#include //for stdio +#include +#include +#include +#include +#include +#include //for raise +#include //for chmod +#include //for time +#include //for unlink +#define UPDATE_INTERVAL 3600 +#ifndef SHARED_SECRET +#define SHARED_SECRET "empty" +#endif -namespace Controller { - JSON::Value updates; +static std::string readFile(std::string filename){ + std::ifstream file(filename.c_str()); + if (!file.good()){return "";} + file.seekg(0, std::ios::end); + unsigned int len = file.tellg(); + file.seekg(0, std::ios::beg); + std::string out; + out.reserve(len); + unsigned int i = 0; + while (file.good() && i++ < len){out += file.get();} + file.close(); + return out; +} +static bool writeFile(std::string filename, std::string &contents){ + unlink(filename.c_str()); + std::ofstream file(filename.c_str(), std::ios_base::trunc | std::ios_base::out); + if (!file.is_open()){return false;} + file << contents; + file.close(); + chmod(filename.c_str(), S_IRWXU | S_IRWXG); + return true; +} - std::string readFile(std::string filename){ - std::ifstream file(filename.c_str()); - if ( !file.good()){ - return ""; +tthread::mutex updaterMutex; +uint8_t updatePerc = 0; +JSON::Value updates; + +namespace Controller{ + + void updateThread(void *np){ + uint64_t updateChecker = Util::epoch() - UPDATE_INTERVAL; + while (Controller::conf.is_active){ + if (Util::epoch() - updateChecker > UPDATE_INTERVAL || updatePerc){ + JSON::Value result = Controller::checkUpdateInfo(); + if (result.isMember("error")){ + FAIL_MSG("Error retrieving update information: %s", + result["error"].asStringRef().c_str()); + } + {// Lock the mutex, update the updates object + tthread::lock_guard guard(updaterMutex); + updates = result; + } + if (!result["uptodate"] && updatePerc){ + Socket::Connection updrConn("releases.mistserver.org", 80, true); + if (!updrConn){ + FAIL_MSG("Could not connect to releases.mistserver.org for update"); + }else{ + // loop through the available components, update them + unsigned int needCount = result["needs_update"].size(); + if (needCount){ + jsonForEach(result["needs_update"], it){ + if (!Controller::conf.is_active){break;} + updatePerc = ((it.num() * 99) / needCount) + 1; + updateComponent(it->asStringRef(), result[it->asStringRef()].asStringRef(), + updrConn); + } + } + updrConn.close(); + } + updatePerc = 0; + } + updateChecker = Util::epoch(); + } + Util::sleep(3000); } - file.seekg(0, std::ios::end); - unsigned int len = file.tellg(); - file.seekg(0, std::ios::beg); - std::string out; - out.reserve(len); - unsigned int i = 0; - while (file.good() && i++ < len){ - out += file.get(); - } - file.close(); - return out; - } //readFile + } - bool writeFile(std::string filename, std::string & contents){ - unlink(filename.c_str()); - std::ofstream file(filename.c_str(), std::ios_base::trunc | std::ios_base::out); - if ( !file.is_open()){ - return false; - } - file << contents; - file.close(); - chmod(filename.c_str(), S_IRWXU | S_IRWXG); - return true; - } //writeFile + void insertUpdateInfo(JSON::Value &ret){ + tthread::lock_guard guard(updaterMutex); + ret = updates; + if (updatePerc){ret["progress"] = (long long)updatePerc;} + } - /// \api - /// `"update"` and `"checkupdate"` requests (LTS-only) are responded to as: - /// ~~~~~~~~~~~~~~~{.js} - /// { - /// "error": "Something went wrong", // 'Optional' - /// "release": "LTS64_99", - /// "version": "1.2 / 6.0.0", - /// "date": "January 5th, 2014", - /// "uptodate": 0, - /// "needs_update": ["MistBuffer", "MistController"], //Controller is guaranteed to be last - /// "MistController": "abcdef1234567890", //md5 sum of latest version - /// //... all other MD5 sums follow - /// } - /// ~~~~~~~~~~~~~~~ - /// Note that `"update"` will only list known information, while `"checkupdate"` triggers an information refresh from the update server. - JSON::Value CheckUpdateInfo(){ + /// Downloads the latest details on updates + JSON::Value checkUpdateInfo(){ JSON::Value ret; - - - //initialize connection HTTP::Parser http; JSON::Value updrInfo; + // retrieve update information Socket::Connection updrConn("releases.mistserver.org", 80, true); - if ( !updrConn){ + if (!updrConn){ Log("UPDR", "Could not connect to releases.mistserver.org to get update information."); ret["error"] = "Could not connect to releases.mistserver.org to get update information."; return ret; } - - //retrieve update information http.url = "/getsums.php?verinfo=1&rel=" RELEASE "&pass=" SHARED_SECRET "&iid=" + instanceId; http.method = "GET"; http.SetHeader("Host", "releases.mistserver.org"); @@ -90,22 +115,11 @@ namespace Controller { http.Clean(); unsigned int startTime = Util::epoch(); while ((Util::epoch() - startTime < 10) && (updrConn || updrConn.Received().size())){ - if (updrConn.spool() || updrConn.Received().size()){ - if ( *(updrConn.Received().get().rbegin()) != '\n'){ - std::string tmp = updrConn.Received().get(); - updrConn.Received().get().clear(); - if (updrConn.Received().size()){ - updrConn.Received().get().insert(0, tmp); - }else{ - updrConn.Received().append(tmp); - } - continue; - } - if (http.Read(updrConn.Received().get())){ - updrInfo = JSON::fromString(http.body); - break; //break out of while loop - } + if (updrConn.spool() && http.Read(updrConn)){ + updrInfo = JSON::fromString(http.body); + break; // break out of while loop } + Util::sleep(250); } updrConn.close(); @@ -117,20 +131,14 @@ namespace Controller { return ret; } ret["release"] = RELEASE; - if (updrInfo.isMember("version")){ - ret["version"] = updrInfo["version"]; - } - if (updrInfo.isMember("date")){ - ret["date"] = updrInfo["date"]; - } + if (updrInfo.isMember("version")){ret["version"] = updrInfo["version"];} + if (updrInfo.isMember("date")){ret["date"] = updrInfo["date"];} ret["uptodate"] = 1; ret["needs_update"].null(); - + // check if everything is up to date or not jsonForEach(updrInfo, it){ - if (it.key().substr(0, 4) != "Mist"){ - continue; - } + if (it.key().substr(0, 4) != "Mist"){continue;} ret[it.key()] = *it; if (it->asString() != Secure::md5(readFile(Util::getMyPath() + it.key()))){ ret["uptodate"] = 0; @@ -148,99 +156,63 @@ namespace Controller { return ret; } - /// Calls CheckUpdateInfo(), uses the resulting JSON::Value to download any needed updates. - /// Will shut down the server if the JSON::Value contained a "shutdown" member. - void CheckUpdates(){ - JSON::Value updrInfo = CheckUpdateInfo(); - if (updrInfo.isMember("error")){ - Log("UPDR", "Error retrieving update information: " + updrInfo["error"].asString()); - return; - } + /// Causes the updater thread to download an update, if available + void checkUpdates(){updatePerc = 1;}// CheckUpdates - if (updrInfo.isMember("shutdown")){ - Log("DDVT", "Shutting down: " + updrInfo["shutdown"].asString()); - restarting = false; - raise(SIGINT); //trigger shutdown - return; - } - - if (updrInfo["uptodate"]){ - //nothing to do - return; - } - - //initialize connection - Socket::Connection updrConn("releases.mistserver.org", 80, true); - if ( !updrConn){ - Log("UPDR", "Could not connect to releases.mistserver.org."); - return; - } - - //loop through the available components, update them - jsonForEach(updrInfo["needs_update"], it){ - updateComponent(it->asStringRef(), updrInfo[it->asStringRef()].asStringRef(), updrConn); - } - updrConn.close(); - } //CheckUpdates - /// Attempts to download an update for the listed component. /// \param component Filename of the component being checked. /// \param md5sum The MD5 sum of the latest version of this file. - /// \param updrConn An connection to releases.mistserver.org to (re)use. Will be (re)opened if closed. - void updateComponent(const std::string & component, const std::string & md5sum, Socket::Connection & updrConn){ - Log("UPDR", "Downloading update for " + component); + /// \param updrConn An connection to releases.mistserver.org to (re)use. Will be (re)opened if + /// closed. + void updateComponent(const std::string &component, const std::string &md5sum, + Socket::Connection &updrConn){ + Log("UPDR", "Updating " + component); std::string new_file; HTTP::Parser http; http.url = "/getfile.php?rel=" RELEASE "&pass=" SHARED_SECRET "&file=" + component; http.method = "GET"; http.SetHeader("Host", "releases.mistserver.org"); - if ( !updrConn){ + http.SetHeader("X-Version", PACKAGE_VERSION); + if (!updrConn){ updrConn = Socket::Connection("releases.mistserver.org", 80, true); - if ( !updrConn){ - Log("UPDR", "Could not connect to releases.mistserver.org for file download."); + if (!updrConn){ + FAIL_MSG("Could not connect to releases.mistserver.org for file download."); return; } } http.SendRequest(updrConn); http.Clean(); - unsigned int startTime = Util::epoch(); - while ((Util::epoch() - startTime < 90) && (updrConn || updrConn.Received().size())){ - if (updrConn.spool() || updrConn.Received().size()){ - if ( *(updrConn.Received().get().rbegin()) != '\n'){ - std::string tmp = updrConn.Received().get(); - updrConn.Received().get().clear(); - if (updrConn.Received().size()){ - updrConn.Received().get().insert(0, tmp); - }else{ - updrConn.Received().append(tmp); - } - } - if (http.Read(updrConn.Received().get())){ - new_file = http.body; - break; //break out of while loop - } + uint64_t startTime = Util::bootSecs(); + while ((Util::bootSecs() < startTime + 10) && updrConn && Controller::conf.is_active){ + if (!updrConn.spool()){ + Util::sleep(250); + continue; } + if (http.Read(updrConn)){ + new_file = http.body; + break; // break out of while loop + } + startTime = Util::bootSecs(); } http.Clean(); if (new_file == ""){ - Log("UPDR", "Could not retrieve new version of " + component + " - retrying next time."); + FAIL_MSG("Could not retrieve new version of %s, continuing without", component.c_str()); return; } if (Secure::md5(new_file) != md5sum){ - Log("UPDR", "Checksum "+Secure::md5(new_file)+" of " + component + " does not match "+md5sum+" - retrying next time."); + FAIL_MSG("Checksum of %s incorrect, continuing without", component.c_str()); return; } - if (writeFile(Util::getMyPath() + component, new_file)){ - Controller::UpdateProtocol(component); - if (component == "MistController"){ - restarting = true; - raise(SIGINT); //trigger restart - } - Log("UPDR", "New version of " + component + " installed."); - }else{ - Log("UPDR", component + " could not be updated! (No write access to file?)"); + if (!writeFile(Util::getMyPath() + component, new_file)){ + FAIL_MSG("Could not write updated version of %s, continuing without", component.c_str()); + return; } + Controller::UpdateProtocol(component); + if (component == "MistController"){ + restarting = true; + raise(SIGINT); // trigger restart + } + Log("UPDR", "New version of " + component + " installed."); } - - -} //Controller namespace +} + diff --git a/src/controller/controller_updater.h b/src/controller/controller_updater.h index 550e04af..eb52abc2 100644 --- a/src/controller/controller_updater.h +++ b/src/controller/controller_updater.h @@ -1,19 +1,16 @@ -/// \file controller_updater.cpp +/// \file controller_updater.h /// Contains all code for the controller updater. +#include +#include #include -#ifndef SHARED_SECRET -#define SHARED_SECRET "empty" -#endif +namespace Controller{ + void updateThread(void *np); + JSON::Value checkUpdateInfo(); + void checkUpdates(); + void insertUpdateInfo(JSON::Value &ret); + void updateComponent(const std::string &component, const std::string &md5sum, + Socket::Connection &updrConn); +} -namespace Controller { - extern JSON::Value updates; - - std::string readFile(std::string filename); - bool writeFile(std::string filename, std::string & contents); - JSON::Value CheckUpdateInfo(); - void CheckUpdates(); - void updateComponent(const std::string & component, const std::string & md5sum, Socket::Connection & updrConn); - -} //Controller namespace