#ifndef FACT_DimCtrl #define FACT_DimCtrl #include "Main.h" #include "tools.h" #include "MessageDim.h" #include "RemoteControl.h" #include "InterpreterV8.h" using namespace std; // ======================================================================== /* Extract usage clause(s) [if any] for SYNOPSIS. Translators: "Usage" and "or" here are patterns (regular expressions) which are used to match the usage synopsis in program output. An example from cp (GNU coreutils) which contains both strings: Usage: cp [OPTION]... [-T] SOURCE DEST or: cp [OPTION]... SOURCE... DIRECTORY or: cp [OPTION]... -t DIRECTORY SOURCE... */ void PrintUsage() { cout << "\n" "The console connects to all available Dim Servers and allows to " "easily access all of their commands.\n" "\n" "Usage: dimctrl [-c type] [OPTIONS]\n" " or: dimctrl [OPTIONS]\n\n"; cout << endl; } void PrintHelp() { Main::PrintUsage(); } // A simple dummy state machine class DimCtrl : public MainImp, public DimCommandHandler, public MessageDimTX { int fLabel; int fStop; int fVerbosity; bool fDebug; bool fIsServer; string fUser; DimDescribedService fSrvState; DimCommand fDimStart; DimCommand fDimStop; map fData; string fScript; string fScriptUser; void ProcessStart() { if (!fScript.empty() || fLabel>-3) { Error("Script execution still in progress."); return; } string opt(fDimStart.getString()); fData = Tools::Split(opt); if (opt.size()>0) Debug("Start '"+opt+"' received."); if (fDebug) Debug("Received data: "+string(fDimStart.getString())); if (opt.size()==0) { if (fData.size()==0) Error("File name missing in DIM_CONTROL/START"); else Error("Equal sign missing in argument '"+fData.begin()->first+"'"); return; } const auto user = fData.find("user"); if (user!=fData.end()) fScriptUser = user->second; if (fDebug) { for (auto it=fData.begin(); it!=fData.end(); it++) Debug(" Arg: "+it->first+" = "+it->second); } fScript = opt; } void StopScript() { Readline::StopScript(); InterpreterV8::JsStop(); } void commandHandler() { if (getCommand()==&fDimStop) { const string user = fDimStop.getSize()>0 ? fDimStop.getString() : ""; string msg = "Stop received"; if (!user.empty()) msg += " from user '"+user+"'"; Debug(msg); StopScript(); } if (getCommand()==&fDimStart) ProcessStart(); } public: DimCtrl(ostream &out=cout) : MessageDimTX("DIM_CONTROL", out), fLabel(-3), fStop(-1), fVerbosity(0), fDebug(false), fIsServer(false), fSrvState("DIM_CONTROL/STATE", "C", "Provides the state of the state machine as quality of service." "|Text[string]:A human readable string sent by the last state change."), fDimStart("DIM_CONTROL/START", "C", this), fDimStop("DIM_CONTROL/STOP", "C", this) { } ~DimCtrl() { DimServer::stop(); } bool check(string &str) { for (auto c=str.begin(); c='A' && *c<='Z') || *c=='_') continue; if (*c++!=':') return false; if (c==str.end()) return false; if (*c!=' ') return false; str = string(c+1, str.end()); return true; } return false; } string Line(const string &txt, char fill) { const int n = (55-txt.length())/2; ostringstream out; out << setfill(fill); out << setw(n) << fill << ' '; out << txt; out << ' ' << setw(n) << fill; if (2*n+txt.length()+2 != 57) out << fill; return out.str(); } int ChangeState(int qos, const Time &time=Time()) { ostringstream pid; pid << getpid(); fLabel = qos; string msg; switch (fLabel) { case -4: msg = "Boot"; break; case -3: msg = "End"; break; case -2: msg = "Load"; break; case -1: msg = "Start"; break; default: { ostringstream out; out << "Label " << fLabel; msg = out.str(); } } if (fLabel<0) msg += "-"+to_string(Readline::GetScriptDepth()); msg += ": "+Readline::GetScript()+" ["; if (!fScriptUser.empty()) msg += fScriptUser+":"+pid.str(); msg += "]"; if (fDebug) MessageDimTX::Write(time, Line(msg, fLabel<-1 ? '=' :'-'), 90); fSrvState.setQuality(fLabel); return fSrvState.Update(msg); } int Write(const Time &time, const std::string &txt, int qos=kMessage) { if (txt=="") return ChangeState(qos, time); if (qos("user"); fScriptUser = fUser; if (conf.Get("stop")) return Dim::SendCommand("DIM_CONTROL/STOP", fUser) + 1; if (conf.Has("start")) return Dim::SendCommand("DIM_CONTROL/START", conf.Get("start")+" user="+fUser) + 1; fVerbosity = 40; if (conf.Has("verbosity")) fVerbosity = conf.Get("verbosity"); if (conf.Get("quiet")) fVerbosity = 90; fIsServer = conf.Get("server"); fDebug = conf.Get("debug"); if (fIsServer) { // Sleep needed to ensure that the server can send // an EXIT if another instance is already running DimServer::start("DIM_CONTROL"); //sleep(1); ChangeState(-4); } return -1; } void Stop(int stop=0) { fStop = stop; StopScript(); } int Run(bool) { while (fStop<0) { const string s = fScript; if (!s.empty()) { Readline::Instance()->Execute(s, fData); fScript = ""; fScriptUser = fUser; } usleep(1000); } return fStop; } }; void SetupConfiguration(Configuration &conf) { po::options_description control("Dim Control"); control.add_options() ("server", po_bool(false), "Start dimctrl as a dim server") ("verbosity,v", var()->implicit_value(0), "Set a new verbosity level (see MessageImp)") ("quiet,q", po_bool(false), "Suppress all output except comments (log-level>=90)") ("debug", po_bool(false), "Print the labels for debugging purpose") ("start", var(), "Start a script with the given name at the given label (script.dim[:N])") ("stop", po_switch(), "Stop a currently running script") ("user,u", var(""), "A user name - just for logging purposes (default is ${USER})") ; conf.AddEnv("user", "USER"); conf.AddOptions(control); } int main(int argc, const char *argv[]) { Configuration conf(argv[0]); conf.SetPrintUsage(PrintUsage); Main::SetupConfiguration(conf); SetupConfiguration(conf); if (!conf.DoParse(argc, argv, PrintHelp)) return 127; if (!conf.Has("console")) return Main::execute(conf); if (conf.Get("console")==0) return Main::execute(conf); else return Main::execute(conf); return 0; } #endif