From 11747732444dd211da605333ee28447993ed02b0 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Thu, 5 Jun 2014 09:55:30 +0200 Subject: [PATCH] Finalized error propagation and debug level setting code. --- src/controller/controller.cpp | 13 +++++ src/controller/controller_connectors.cpp | 69 ++++++++++++------------ src/controller/controller_storage.cpp | 28 ++++++++++ src/controller/controller_storage.h | 2 + src/controller/controller_streams.cpp | 17 ++++-- 5 files changed, 91 insertions(+), 38 deletions(-) diff --git a/src/controller/controller.cpp b/src/controller/controller.cpp index c59a42fb..7284637f 100644 --- a/src/controller/controller.cpp +++ b/src/controller/controller.cpp @@ -287,6 +287,7 @@ void createAccount (std::string account){ ///\brief The main entry point for the controller. int main(int argc, char ** argv){ + Controller::Storage = JSON::fromFile("config.json"); JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}"); stored_port["default"] = Controller::Storage["config"]["controller"]["port"]; @@ -353,6 +354,18 @@ int main(int argc, char ** argv){ //Input custom config here Controller::Storage = JSON::fromFile(Controller::conf.getString("configFile")); + { + //spawn thread that reads stderr of process + int pipeErr[2]; + if (pipe(pipeErr) >= 0){ + dup2(pipeErr[1], STDERR_FILENO);//cause stderr to write to the pipe + close(pipeErr[1]);//close the unneeded pipe file descriptor + tthread::thread msghandler(Controller::handleMsg, (void*)(((char*)0) + pipeErr[0])); + msghandler.detach(); + } + } + + if (Controller::conf.getOption("debug",true).size() > 1){ Controller::Storage["config"]["debug"] = Controller::conf.getInteger("debug"); } diff --git a/src/controller/controller_connectors.cpp b/src/controller/controller_connectors.cpp index 072e1763..d12b77e3 100644 --- a/src/controller/controller_connectors.cpp +++ b/src/controller/controller_connectors.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "controller_storage.h" #include "controller_connectors.h" @@ -38,18 +39,28 @@ namespace Controller { static inline void builPipedPart(JSON::Value & p, char * argarr[], int & argnum, JSON::Value & argset){ for (JSON::ObjIter it = argset.ObjBegin(); it != argset.ObjEnd(); ++it){ - if (it->second.isMember("option") && p.isMember(it->first)){ - if (it->second.isMember("type")){ - if (it->second["type"].asStringRef() == "str" && !p[it->first].isString()){ - p[it->first] = p[it->first].asString(); + if (it->second.isMember("option")){ + if (p.isMember(it->first)){ + if (it->second.isMember("type")){ + if (it->second["type"].asStringRef() == "str" && !p[it->first].isString()){ + p[it->first] = p[it->first].asString(); + } + if ((it->second["type"].asStringRef() == "uint" || it->second["type"].asStringRef() == "int") && !p[it->first].isInt()){ + p[it->first] = p[it->first].asString(); + } } - if ((it->second["type"].asStringRef() == "uint" || it->second["type"].asStringRef() == "int") && !p[it->first].isInt()){ - p[it->first] = JSON::Value(p[it->first].asInt()).asString(); + if (p[it->first].asStringRef().size() > 0){ + argarr[argnum++] = (char*)(it->second["option"].c_str()); + argarr[argnum++] = (char*)(p[it->first].c_str()); + } + }else{ + if (it->first == "debug"){ + static std::string debugLvlStr; + debugLvlStr = JSON::Value((long long)Util::Config::printDebugLevel).asString(); + argarr[argnum++] = (char*)(it->second["option"].c_str()); + argarr[argnum++] = (char*)debugLvlStr.c_str(); + DEVEL_MSG("Setting debug for %s to %s", p["connector"].asStringRef().c_str(), debugLvlStr.c_str()); } - } - if (p[it->first].asStringRef().size() > 0){ - argarr[argnum++] = (char*)(it->second["option"].c_str()); - argarr[argnum++] = (char*)(p[it->first].c_str()); } } } @@ -73,24 +84,6 @@ namespace Controller { if (pipedCapa.isMember("optional")){builPipedPart(p, argarr, argnum, pipedCapa["optional"]);} } - void handleMsg(void * err){ - char buf[1024]; - FILE * output = fdopen((long long int)err, "r"); - while (fgets(buf, 1024, output)){ - unsigned int i = 0; - while (i < 9 && buf[i] != ' '){ - ++i; - } - if(i < 9){ - buf[i] = 0; - Log(buf,buf+i+1); - }else{ - printf("%s\n",buf); - } - } - } - - ///\brief Checks current protocol coguration, updates state of enabled connectors if neccesary. ///\param p An object containing all protocols. ///\param capabilities An object containing the detected capabilities. @@ -109,14 +102,18 @@ namespace Controller { long long counter = 0; for (JSON::ArrIter ait = p.ArrBegin(); ait != p.ArrEnd(); ait++){ - ( *ait).removeMember("online"); + std::string prevOnline = ( *ait)["online"].asString(); #define connName (*ait)["connector"].asStringRef() if ( !(*ait).isMember("connector") || connName == ""){ + ( *ait)["online"] = "Missing connector name"; continue; } if ( !capabilities["connectors"].isMember(connName)){ - Log("WARN", connName + " connector is enabled but doesn't exist on system! Ignoring connector."); + ( *ait)["online"] = "Not installed"; + if (( *ait)["online"].asString() != prevOnline){ + Log("WARN", connName + " connector is enabled but doesn't exist on system! Ignoring connector."); + } continue; } @@ -126,13 +123,17 @@ namespace Controller { for (JSON::ObjIter it = connCapa["required"].ObjBegin(); it != connCapa["required"].ObjEnd(); ++it){ if ( !(*ait).isMember(it->first) || (*ait)[it->first].asStringRef().size() < 1){ gotAll = false; - Log("WARN", connName + " connector is missing required parameter " + it->first + "! Ignoring connector."); + ( *ait)["online"] = "Invalid configuration"; + if (( *ait)["online"].asString() != prevOnline){ + Log("WARN", connName + " connector is missing required parameter " + it->first + "! Ignoring connector."); + } break; } } if (!gotAll){continue;} } + ( *ait).removeMember("online"); /// \todo Check dependencies? new_connectors[counter] = (*ait).toString(); @@ -164,9 +165,9 @@ namespace Controller { err = -1; Util::Procs::StartPiped(toConn(iter->first), argarr, &zero, &out, &err);//redirects output to out. Must make a new pipe, redirect std err if(err != -1){ - //spawn new thread where err is read, it reads err until there is nothing more to be read - tthread::thread * msghandler = new tthread::thread(handleMsg, (void*)err); - msghandler->detach(); + //spawn thread that reads stderr of process + tthread::thread msghandler(Controller::handleMsg, (void*)(((char*)0) + err)); + msghandler.detach(); } } } diff --git a/src/controller/controller_storage.cpp b/src/controller/controller_storage.cpp index 3d253006..13afa680 100644 --- a/src/controller/controller_storage.cpp +++ b/src/controller/controller_storage.cpp @@ -38,5 +38,33 @@ namespace Controller { File.close(); return File.good(); } + + /// Handles output of a Mist application, detecting and catching debug messages. + /// Debug messages are automatically converted into Log messages. + /// Closes the file descriptor on read error. + /// \param err File descriptor of the stderr output of the process to monitor. + void handleMsg(void * err){ + char buf[1024]; + FILE * output = fdopen((long long int)err, "r"); + while (fgets(buf, 1024, output)){ + unsigned int i = 0; + while (i < 9 && buf[i] != '|' && buf[i] != 0){ + ++i; + } + unsigned int j = i; + while (j < 1024 && buf[j] != '\n' && buf[j] != 0){ + ++j; + } + buf[j] = 0; + if(i < 9){ + buf[i] = 0; + Log(buf,buf+i+1); + }else{ + printf("%s", buf); + } + } + fclose(output); + close((long long int)err); + } } diff --git a/src/controller/controller_storage.h b/src/controller/controller_storage.h index 57f18ba7..ada5a277 100644 --- a/src/controller/controller_storage.h +++ b/src/controller/controller_storage.h @@ -13,5 +13,7 @@ namespace Controller { /// Write contents to Filename. bool WriteFile(std::string Filename, std::string contents); + + void handleMsg(void * err); } diff --git a/src/controller/controller_streams.cpp b/src/controller/controller_streams.cpp index 907b279a..7c21448e 100644 --- a/src/controller/controller_streams.cpp +++ b/src/controller/controller_streams.cpp @@ -35,6 +35,7 @@ namespace Controller { ///\param name The name of the stream ///\param data The corresponding configuration values. void startStream(std::string name, JSON::Value & data){ + std::string prevState = data["error"].asStringRef(); data["online"] = (std::string)"Checking..."; data.removeMember("error"); std::string URL; @@ -45,8 +46,10 @@ namespace Controller { URL = data["source"].asString(); } if (URL == ""){ - Log("STRM", "Error for stream " + name + "! Source parameter missing."); data["error"] = "Stream offline: Missing source parameter!"; + if (data["error"].asStringRef() != prevState){ + Log("STRM", "Error for stream " + name + "! Source parameter missing."); + } return; } if (URL.substr(0, 4) == "push"){ @@ -71,8 +74,10 @@ namespace Controller { data.removeMember("error"); struct stat fileinfo; if (stat(URL.c_str(), &fileinfo) != 0 || S_ISDIR(fileinfo.st_mode)){ - Log("BUFF", "Warning for VoD stream " + name + "! File not found: " + URL); data["error"] = "Stream offline: Not found: " + URL; + if (data["error"].asStringRef() != prevState){ + Log("BUFF", "Warning for VoD stream " + name + "! File not found: " + URL); + } data["online"] = 0; return; } @@ -89,8 +94,10 @@ namespace Controller { } if ( !getMeta && data.isMember("meta") && data["meta"].isMember("tracks")){ if ( !data["meta"] || !data["meta"]["tracks"]){ - Log("WARN", "Source file " + URL + " seems to be corrupt."); data["error"] = "Stream offline: Corrupt file?"; + if (data["error"].asStringRef() != prevState){ + Log("WARN", "Source file " + URL + " seems to be corrupt."); + } data["online"] = 0; return; } @@ -118,8 +125,10 @@ namespace Controller { tmp_cmd[1] = (char*)URL.c_str(); data["meta"] = JSON::fromString(Util::Procs::getOutputOf(tmp_cmd)); if ( !data["meta"] || !data["meta"].isMember("tracks") || !data["meta"]["tracks"]){ - Log("WARN", "Source file " + URL + " seems to be corrupt."); data["error"] = "Stream offline: Corrupt file?"; + if (data["error"].asStringRef() != prevState){ + Log("WARN", "Source file " + URL + " seems to be corrupt."); + } data["online"] = 0; return; }