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

Last change on this file since 13758 was 13754, checked in by tbretz, 12 years ago
Updated output with the user name.
File size: 7.7 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
56 void ProcessStart()
57 {
58 if (!fScript.empty() || fLabel>=0)
59 {
60 Error("Script execution still in progress.");
61 return;
62 }
63
64 string opt(fDimStart.getString());
65
66 fData = Tools::Split(opt);
67
68 if (opt.size()>0)
69 Debug("Start '"+opt+"' received.");
70
71 if (fDebug)
72 Debug("Received data: "+string(fDimStart.getString()));
73
74 if (opt.size()==0)
75 {
76 if (fData.size()==0)
77 Error("File name missing in DIM_CONTROL/START");
78 else
79 Error("Equal sign missing in argument '"+fData.begin()->first+"'");
80
81 return;
82 }
83
84 if (fDebug)
85 {
86 for (auto it=fData.begin(); it!=fData.end(); it++)
87 Debug(" Arg: "+it->first+" = "+it->second);
88 }
89
90 fScript = opt;
91 }
92
93 void commandHandler()
94 {
95 if (getCommand()==&fDimStop)
96 {
97 Debug("Stop received");
98 Readline::SetLabel(-2);
99 }
100
101 if (getCommand()==&fDimStart)
102 ProcessStart();
103 }
104
105public:
106 DimCtrl(ostream &out=cout) : MessageDimTX("DIM_CONTROL", out),
107 fLabel(-3), fStop(false), fVerbosity(0), fDebug(false), fIsServer(false),
108 fSrvState("DIM_CONTROL/STATE", "C",
109 "Provides the state of the state machine as quality of service."
110 "|Text[string]:A human readable string sent by the last state change."),
111 fDimStart("DIM_CONTROL/START", "C", this),
112 fDimStop("DIM_CONTROL/STOP", "", this)
113 {
114 }
115 ~DimCtrl()
116 {
117 DimServer::stop();
118 }
119
120 bool check(string &str)
121 {
122 for (auto c=str.begin(); c<str.end(); c++)
123 {
124 if ((*c>='A' && *c<='Z') || *c=='_')
125 continue;
126
127 if (*c++!=':')
128 return false;
129
130 if (c==str.end())
131 return false;
132
133 if (*c!=' ')
134 return false;
135
136 str = string(c+1, str.end());
137 return true;
138 }
139
140 return false;
141 }
142
143 string Line(const string &txt, char fill)
144 {
145 const int n = (55-txt.length())/2;
146
147 ostringstream out;
148 out << setfill(fill);
149 out << setw(n) << fill << ' ';
150 out << txt;
151 out << ' ' << setw(n) << fill;
152
153 if (2*n+txt.length()+2 != 57)
154 out << fill;
155
156 return out.str();
157 }
158
159 int Write(const Time &time, const std::string &txt, int qos=kMessage)
160 {
161 if (txt=="")
162 {
163 ostringstream pid;
164 pid << getpid();
165
166 fLabel = qos;
167
168 string msg;
169 switch (fLabel)
170 {
171 case -3: msg = "End"; break;
172 case -2: msg = "Load"; break;
173 case -1: msg = "Begin"; break;
174 default:
175 {
176 ostringstream out;
177 out << "Label " << fLabel;
178 msg = out.str();
179 }
180 }
181
182 msg += ": "+Readline::GetScript()+" [";
183 if (!fUser.empty())
184 msg += fUser+":"+pid.str();
185 msg += "]";
186
187 if (fDebug)
188 MessageDimTX::Write(time, Line(msg, fLabel<-1 ? '=' :'-'), 90);
189
190 fSrvState.setQuality(fLabel);
191 return fSrvState.Update(msg);
192 }
193
194 if (qos<fVerbosity)
195 return 0;
196
197 // Avoid recursions
198 if (fIsServer && txt.substr(0, 13)=="DIM_CONTROL: ")
199 return 0;
200
201 // Don't send received messages via dim
202 string cpy(txt);
203 if (fIsServer && check(cpy))
204 return MessageImp::Write(time, cpy, qos);
205
206 // Send all of our own messages via dim
207 return MessageDimTX::Write(time, txt, qos);
208 }
209
210 int EvalOptions(Configuration &conf)
211 {
212 fVerbosity = 90;
213
214 if (conf.Get<bool>("stop"))
215 return Dim::SendCommand("DIM_CONTROL/STOP") + 1;
216
217 if (conf.Has("start"))
218 return Dim::SendCommand("DIM_CONTROL/START", conf.Get<string>("start")) + 1;
219
220 fVerbosity = 40;
221
222 fIsServer = conf.Get<bool>("server");
223 if (fIsServer)
224 {
225 // Sleep needed to ensure that the server can send
226 // an EXIT if another instance is already running
227 DimServer::start("DIM_CONTROL");
228 sleep(1);
229 fSrvState.setQuality(-3);
230 fSrvState.Update("[boot]");
231 }
232
233 if (conf.Has("verbosity"))
234 fVerbosity = conf.Get<uint32_t>("verbosity");
235
236 if (conf.Get<bool>("quiet"))
237 fVerbosity = 90;
238
239 fDebug = conf.Get<bool>("debug");
240 fUser = conf.Get<string>("user");
241
242 return -1;
243 }
244
245 void Stop(int stop=1) { fStop = stop; Readline::SetLabel(0); }
246 int Run(bool)
247 {
248 while (!fStop)
249 {
250 const string s = fScript;
251 if (!s.empty())
252 {
253 Readline::Instance()->Execute(s, fData);
254 fScript = "";
255 }
256
257 usleep(1000);
258 }
259 return fStop-1; }
260};
261
262void SetupConfiguration(Configuration &conf)
263{
264 po::options_description control("Dim Control");
265 control.add_options()
266 ("server", po_bool(false), "Start dimctrl as a dim server")
267 ("verbosity,v", var<uint32_t>()->implicit_value(0), "Set a new verbosity level (see MessageImp)")
268 ("quiet,q", po_bool(false), "Suppress all output except comments (log-level>=90)")
269 ("debug", po_bool(false), "Print the labels for debugging purpose")
270 ("start", var<string>(), "Start a script with the given name at the given label (script.dim[:N])")
271 ("stop", po_switch(), "Stop a currently running script")
272 ("user,u", var<string>(""), "A user name - just for logging purposes (default is ${USER})")
273 ;
274
275 conf.AddEnv("user", "USER");
276
277 conf.AddOptions(control);
278}
279
280int main(int argc, const char *argv[])
281{
282 Configuration conf(argv[0]);
283 conf.SetPrintUsage(PrintUsage);
284 Main::SetupConfiguration(conf);
285 SetupConfiguration(conf);
286
287 if (!conf.DoParse(argc, argv, PrintHelp))
288 return -1;
289
290 if (!conf.Has("console"))
291 return Main::execute<RemoteStream, DimCtrl>(conf);
292
293 if (conf.Get<int>("console")==0)
294 return Main::execute<RemoteShell, DimCtrl>(conf);
295 else
296 return Main::execute<RemoteConsole, DimCtrl>(conf);
297
298 return 0;
299}
300#endif
Note: See TracBrowser for help on using the repository browser.