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

Last change on this file since 13918 was 13902, checked in by tbretz, 12 years ago
Little cosmetics
File size: 8.2 KB
Line 
1#ifndef FACT_DimCtrl
2#define FACT_DimCtrl
3
4#include <boost/lexical_cast.hpp>
5
6#include "Main.h"
7#include "tools.h"
8#include "MessageDim.h"
9#include "RemoteControl.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 commandHandler()
101 {
102 if (getCommand()==&fDimStop)
103 {
104 const string user = fDimStop.getSize()>0 ? fDimStop.getString() : "";
105
106 string msg = "Stop received";
107 if (!user.empty())
108 msg += " from user '"+user+"'";
109
110 Debug(msg);
111 Readline::StopScript();
112 }
113
114 if (getCommand()==&fDimStart)
115 ProcessStart();
116 }
117
118public:
119 DimCtrl(ostream &out=cout) : MessageDimTX("DIM_CONTROL", out),
120 fLabel(-3), fStop(-1), fVerbosity(0), fDebug(false), fIsServer(false),
121 fSrvState("DIM_CONTROL/STATE", "C",
122 "Provides the state of the state machine as quality of service."
123 "|Text[string]:A human readable string sent by the last state change."),
124 fDimStart("DIM_CONTROL/START", "C", this),
125 fDimStop("DIM_CONTROL/STOP", "C", this)
126 {
127 }
128 ~DimCtrl()
129 {
130 DimServer::stop();
131 }
132
133 bool check(string &str)
134 {
135 for (auto c=str.begin(); c<str.end(); c++)
136 {
137 if ((*c>='A' && *c<='Z') || *c=='_')
138 continue;
139
140 if (*c++!=':')
141 return false;
142
143 if (c==str.end())
144 return false;
145
146 if (*c!=' ')
147 return false;
148
149 str = string(c+1, str.end());
150 return true;
151 }
152
153 return false;
154 }
155
156 string Line(const string &txt, char fill)
157 {
158 const int n = (55-txt.length())/2;
159
160 ostringstream out;
161 out << setfill(fill);
162 out << setw(n) << fill << ' ';
163 out << txt;
164 out << ' ' << setw(n) << fill;
165
166 if (2*n+txt.length()+2 != 57)
167 out << fill;
168
169 return out.str();
170 }
171
172 int ChangeState(int qos, const Time &time=Time())
173 {
174 ostringstream pid;
175 pid << getpid();
176
177 fLabel = qos;
178
179 string msg;
180 switch (fLabel)
181 {
182 case -4: msg = "Boot" break;
183 case -3: msg = "End"; break;
184 case -2: msg = "Load"; break;
185 case -1: msg = "Start"; break;
186 default:
187 {
188 ostringstream out;
189 out << "Label " << fLabel;
190 msg = out.str();
191 }
192 }
193
194 if (fLabel<0)
195 msg += "-"+boost::lexical_cast<string>(Readline::GetScriptDepth());
196
197 msg += ": "+Readline::GetScript()+" [";
198 if (!fScriptUser.empty())
199 msg += fScriptUser+":"+pid.str();
200 msg += "]";
201
202 if (fDebug)
203 MessageDimTX::Write(time, Line(msg, fLabel<-1 ? '=' :'-'), 90);
204
205 fSrvState.setQuality(fLabel);
206 return fSrvState.Update(msg);
207 }
208
209 int Write(const Time &time, const std::string &txt, int qos=kMessage)
210 {
211 if (txt=="")
212 return ChangeState(qos, time);
213
214 if (qos<fVerbosity)
215 return 0;
216
217 // Avoid recursions
218 if (fIsServer && txt.substr(0, 13)=="DIM_CONTROL: ")
219 return 0;
220
221 // Don't send received messages via dim
222 string cpy(txt);
223 if (fIsServer && check(cpy))
224 return MessageImp::Write(time, cpy, qos);
225
226 // Send all of our own messages via dim
227 return MessageDimTX::Write(time, txt, qos);
228 }
229
230 int EvalOptions(Configuration &conf)
231 {
232 fVerbosity = 90;
233
234 fUser = conf.Get<string>("user");
235 fScriptUser = fUser;
236
237 if (conf.Get<bool>("stop"))
238 return Dim::SendCommand("DIM_CONTROL/STOP", fUser) + 1;
239
240 if (conf.Has("start"))
241 return Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")+" user="+fUser) + 1;
242
243 fVerbosity = 40;
244
245 if (conf.Has("verbosity"))
246 fVerbosity = conf.Get<uint32_t>("verbosity");
247
248 if (conf.Get<bool>("quiet"))
249 fVerbosity = 90;
250
251 fIsServer = conf.Get<bool>("server");
252 fDebug = conf.Get<bool>("debug");
253
254 if (fIsServer)
255 {
256 // Sleep needed to ensure that the server can send
257 // an EXIT if another instance is already running
258 DimServer::start("DIM_CONTROL");
259 //sleep(1);
260 ChangeState(-4);
261 }
262
263 return -1;
264 }
265
266 void Stop(int stop=0) { fStop = stop; Readline::StopScript(); }
267 int Run(bool)
268 {
269 while (fStop<0)
270 {
271 const string s = fScript;
272 if (!s.empty())
273 {
274 Readline::Instance()->Execute(s, fData);
275 fScript = "";
276 fScriptUser = fUser;
277 }
278
279 usleep(1000);
280 }
281
282 return fStop;
283 }
284
285};
286
287void SetupConfiguration(Configuration &conf)
288{
289 po::options_description control("Dim Control");
290 control.add_options()
291 ("server", po_bool(false), "Start dimctrl as a dim server")
292 ("verbosity,v", var<uint32_t>()->implicit_value(0), "Set a new verbosity level (see MessageImp)")
293 ("quiet,q", po_bool(false), "Suppress all output except comments (log-level>=90)")
294 ("debug", po_bool(false), "Print the labels for debugging purpose")
295 ("start", var<string>(), "Start a script with the given name at the given label (script.dim[:N])")
296 ("stop", po_switch(), "Stop a currently running script")
297 ("user,u", var<string>(""), "A user name - just for logging purposes (default is ${USER})")
298 ;
299
300 conf.AddEnv("user", "USER");
301
302 conf.AddOptions(control);
303}
304
305int main(int argc, const char *argv[])
306{
307 Configuration conf(argv[0]);
308 conf.SetPrintUsage(PrintUsage);
309 Main::SetupConfiguration(conf);
310 SetupConfiguration(conf);
311
312 if (!conf.DoParse(argc, argv, PrintHelp))
313 return -1;
314
315 if (!conf.Has("console"))
316 return Main::execute<RemoteStream, DimCtrl>(conf);
317
318 if (conf.Get<int>("console")==0)
319 return Main::execute<RemoteShell, DimCtrl>(conf);
320 else
321 return Main::execute<RemoteConsole, DimCtrl>(conf);
322
323 return 0;
324}
325#endif
Note: See TracBrowser for help on using the repository browser.