source: trunk/FACT++/src/dimctrl.cc@ 14062

Last change on this file since 14062 was 14052, checked in by tbretz, 12 years ago
Stop a readline and JavaScript if STOP is received.
File size: 8.3 KB
Line 
1#ifndef FACT_DimCtrl
2#define FACT_DimCtrl
3
4#include "Main.h"
5#include "tools.h"
6#include "MessageDim.h"
7#include "RemoteControl.h"
8
9#include "InterpreterV8.h"
10
11using namespace std;
12
13// ========================================================================
14
15
16/*
17 Extract usage clause(s) [if any] for SYNOPSIS.
18 Translators: "Usage" and "or" here are patterns (regular expressions) which
19 are used to match the usage synopsis in program output. An example from cp
20 (GNU coreutils) which contains both strings:
21 Usage: cp [OPTION]... [-T] SOURCE DEST
22 or: cp [OPTION]... SOURCE... DIRECTORY
23 or: cp [OPTION]... -t DIRECTORY SOURCE...
24 */
25void PrintUsage()
26{
27 cout << "\n"
28 "The console connects to all available Dim Servers and allows to "
29 "easily access all of their commands.\n"
30 "\n"
31 "Usage: dimctrl [-c type] [OPTIONS]\n"
32 " or: dimctrl [OPTIONS]\n\n";
33 cout << endl;
34}
35
36void PrintHelp()
37{
38 Main::PrintUsage();
39}
40
41// A simple dummy state machine
42class DimCtrl : public MainImp, public DimCommandHandler, public MessageDimTX
43{
44 int fLabel;
45 int fStop;
46 int fVerbosity;
47 bool fDebug;
48 bool fIsServer;
49 string fUser;
50
51 DimDescribedService fSrvState;
52 DimCommand fDimStart;
53 DimCommand fDimStop;
54
55 map<string,string> fData;
56 string fScript;
57 string fScriptUser;
58
59 void ProcessStart()
60 {
61 if (!fScript.empty() || fLabel>-3)
62 {
63 Error("Script execution still in progress.");
64 return;
65 }
66
67 string opt(fDimStart.getString());
68
69 fData = Tools::Split(opt);
70
71 if (opt.size()>0)
72 Debug("Start '"+opt+"' received.");
73
74 if (fDebug)
75 Debug("Received data: "+string(fDimStart.getString()));
76
77 if (opt.size()==0)
78 {
79 if (fData.size()==0)
80 Error("File name missing in DIM_CONTROL/START");
81 else
82 Error("Equal sign missing in argument '"+fData.begin()->first+"'");
83
84 return;
85 }
86
87 const auto user = fData.find("user");
88 if (user!=fData.end())
89 fScriptUser = user->second;
90
91 if (fDebug)
92 {
93 for (auto it=fData.begin(); it!=fData.end(); it++)
94 Debug(" Arg: "+it->first+" = "+it->second);
95 }
96
97 fScript = opt;
98 }
99
100 void StopScript()
101 {
102 Readline::StopScript();
103 InterpreterV8::JsStop();
104 }
105
106 void commandHandler()
107 {
108 if (getCommand()==&fDimStop)
109 {
110 const string user = fDimStop.getSize()>0 ? fDimStop.getString() : "";
111
112 string msg = "Stop received";
113 if (!user.empty())
114 msg += " from user '"+user+"'";
115
116 Debug(msg);
117 StopScript();
118 }
119
120 if (getCommand()==&fDimStart)
121 ProcessStart();
122 }
123
124public:
125 DimCtrl(ostream &out=cout) : MessageDimTX("DIM_CONTROL", out),
126 fLabel(-3), fStop(-1), fVerbosity(0), fDebug(false), fIsServer(false),
127 fSrvState("DIM_CONTROL/STATE", "C",
128 "Provides the state of the state machine as quality of service."
129 "|Text[string]:A human readable string sent by the last state change."),
130 fDimStart("DIM_CONTROL/START", "C", this),
131 fDimStop("DIM_CONTROL/STOP", "C", this)
132 {
133 }
134 ~DimCtrl()
135 {
136 DimServer::stop();
137 }
138
139 bool check(string &str)
140 {
141 for (auto c=str.begin(); c<str.end(); c++)
142 {
143 if ((*c>='A' && *c<='Z') || *c=='_')
144 continue;
145
146 if (*c++!=':')
147 return false;
148
149 if (c==str.end())
150 return false;
151
152 if (*c!=' ')
153 return false;
154
155 str = string(c+1, str.end());
156 return true;
157 }
158
159 return false;
160 }
161
162 string Line(const string &txt, char fill)
163 {
164 const int n = (55-txt.length())/2;
165
166 ostringstream out;
167 out << setfill(fill);
168 out << setw(n) << fill << ' ';
169 out << txt;
170 out << ' ' << setw(n) << fill;
171
172 if (2*n+txt.length()+2 != 57)
173 out << fill;
174
175 return out.str();
176 }
177
178 int ChangeState(int qos, const Time &time=Time())
179 {
180 ostringstream pid;
181 pid << getpid();
182
183 fLabel = qos;
184
185 string msg;
186 switch (fLabel)
187 {
188 case -4: msg = "Boot"; break;
189 case -3: msg = "End"; break;
190 case -2: msg = "Load"; break;
191 case -1: msg = "Start"; break;
192 default:
193 {
194 ostringstream out;
195 out << "Label " << fLabel;
196 msg = out.str();
197 }
198 }
199
200 if (fLabel<0)
201 msg += "-"+to_string(Readline::GetScriptDepth());
202
203 msg += ": "+Readline::GetScript()+" [";
204 if (!fScriptUser.empty())
205 msg += fScriptUser+":"+pid.str();
206 msg += "]";
207
208 if (fDebug)
209 MessageDimTX::Write(time, Line(msg, fLabel<-1 ? '=' :'-'), 90);
210
211 fSrvState.setQuality(fLabel);
212 return fSrvState.Update(msg);
213 }
214
215 int Write(const Time &time, const std::string &txt, int qos=kMessage)
216 {
217 if (txt=="")
218 return ChangeState(qos, time);
219
220 if (qos<fVerbosity)
221 return 0;
222
223 // Avoid recursions
224 if (fIsServer && txt.substr(0, 13)=="DIM_CONTROL: ")
225 return 0;
226
227 // Don't send received messages via dim
228 string cpy(txt);
229 if (fIsServer && check(cpy))
230 return MessageImp::Write(time, cpy, qos);
231
232 // Send all of our own messages via dim
233 return MessageDimTX::Write(time, txt, qos);
234 }
235
236 int EvalOptions(Configuration &conf)
237 {
238 fVerbosity = 90;
239
240 fUser = conf.Get<string>("user");
241 fScriptUser = fUser;
242
243 if (conf.Get<bool>("stop"))
244 return Dim::SendCommand("DIM_CONTROL/STOP", fUser) + 1;
245
246 if (conf.Has("start"))
247 return Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")+" user="+fUser) + 1;
248
249 fVerbosity = 40;
250
251 if (conf.Has("verbosity"))
252 fVerbosity = conf.Get<uint32_t>("verbosity");
253
254 if (conf.Get<bool>("quiet"))
255 fVerbosity = 90;
256
257 fIsServer = conf.Get<bool>("server");
258 fDebug = conf.Get<bool>("debug");
259
260 if (fIsServer)
261 {
262 // Sleep needed to ensure that the server can send
263 // an EXIT if another instance is already running
264 DimServer::start("DIM_CONTROL");
265 //sleep(1);
266 ChangeState(-4);
267 }
268
269 return -1;
270 }
271
272 void Stop(int stop=0) { fStop = stop; StopScript(); }
273 int Run(bool)
274 {
275 while (fStop<0)
276 {
277 const string s = fScript;
278 if (!s.empty())
279 {
280 Readline::Instance()->Execute(s, fData);
281 fScript = "";
282 fScriptUser = fUser;
283 }
284
285 usleep(1000);
286 }
287
288 return fStop;
289 }
290
291};
292
293void SetupConfiguration(Configuration &conf)
294{
295 po::options_description control("Dim Control");
296 control.add_options()
297 ("server", po_bool(false), "Start dimctrl as a dim server")
298 ("verbosity,v", var<uint32_t>()->implicit_value(0), "Set a new verbosity level (see MessageImp)")
299 ("quiet,q", po_bool(false), "Suppress all output except comments (log-level>=90)")
300 ("debug", po_bool(false), "Print the labels for debugging purpose")
301 ("start", var<string>(), "Start a script with the given name at the given label (script.dim[:N])")
302 ("stop", po_switch(), "Stop a currently running script")
303 ("user,u", var<string>(""), "A user name - just for logging purposes (default is ${USER})")
304 ;
305
306 conf.AddEnv("user", "USER");
307
308 conf.AddOptions(control);
309}
310
311int main(int argc, const char *argv[])
312{
313 Configuration conf(argv[0]);
314 conf.SetPrintUsage(PrintUsage);
315 Main::SetupConfiguration(conf);
316 SetupConfiguration(conf);
317
318 if (!conf.DoParse(argc, argv, PrintHelp))
319 return 127;
320
321 if (!conf.Has("console"))
322 return Main::execute<RemoteStream, DimCtrl>(conf);
323
324 if (conf.Get<int>("console")==0)
325 return Main::execute<RemoteShell, DimCtrl>(conf);
326 else
327 return Main::execute<RemoteConsole, DimCtrl>(conf);
328
329 return 0;
330}
331#endif
Note: See TracBrowser for help on using the repository browser.