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

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