source: trunk/FACT++/src/Main.h@ 18538

Last change on this file since 18538 was 18424, checked in by tbretz, 10 years ago
An empty 'path' crahes newer boosts. Make sure it is not eempty.
File size: 10.1 KB
Line 
1#ifndef FACT_Main
2#define FACT_Main
3
4#include <map>
5#include <thread>
6#include <functional>
7
8#include <boost/filesystem.hpp>
9
10#include "dim.h"
11
12#include "Dim.h"
13#include "Time.h"
14#include "MainImp.h"
15#include "Readline.h"
16#include "WindowLog.h"
17#include "MessageImp.h"
18#include "Configuration.h"
19
20namespace Main
21{
22 using namespace std;
23 namespace fs = boost::filesystem;
24
25 void SetupConfiguration(Configuration &conf)
26 {
27 const string n = conf.GetName()+".log";
28
29 po::options_description config("Program options");
30 config.add_options()
31 ("dns", var<string>("localhost"), "Dim nameserver (overwites DIM_DNS_NODE environment variable)")
32 ("host", var<string>(), "Address with which the Dim nameserver can connect to this host (overwites DIM_HOST_NODE environment variable)")
33 ("log,l", var<string>(n), "Name of local log-file")
34 ("logpath", var<string>(), "Absolute path to log-files (default: excutable's directory)")
35 ("no-log", po_switch(), "Supress log-file")
36 ("append-log", po_bool(), "Append log information to local log-file")
37 ("null", po_switch(), "Suppresses almost all console output - including errors (only available without --console option)")
38 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
39 ("cmd", vars<string>(), "Execute one or more commands at startup")
40 ("exec,e", vars<string>(), "Execute one or more scrips at startup ('file:N' - start at label N)")
41 ("arg:*", var<string>(), "Arguments for script execution with --exc, e.g. --arg:ra='12.5436'")
42 ("home", var<string>(), "Path to home directory (used as default for logpath if standard log files not writable)")
43 ("quit", po_switch(), "Quit after startup");
44 ;
45
46 conf.AddEnv("dns", "DIM_DNS_NODE");
47 conf.AddEnv("host", "DIM_HOST_NODE");
48 conf.AddEnv("home", "HOME");
49
50 conf.AddOptions(config);
51 }
52
53 void PrintUsage()
54 {
55 cout <<
56 "Files:\n"
57 "The following files are written by each program by default\n"
58 " program.evt: A log of all executed of skipped events\n"
59 " program.his: The history accessible by Pg-up/dn\n"
60 " program.log: All output piped to the log-stream\n"
61 << endl;
62 }
63
64 template<class T>
65 void PrintHelp()
66 {
67 Dim::Setup();
68
69 ofstream fout("/dev/null");
70
71 T io_service(fout);
72
73 io_service.PrintListOfStates(cout);
74 cout << "\nList of available commands:\n";
75 io_service.PrintListOfEvents(cout);
76 cout << "\n";
77 }
78
79 void Thread(MainImp *io_service, bool dummy, int &rc)
80 {
81 // This is necessary so that the StateMachien Thread can signal the
82 // Readline to exit
83 rc = io_service->Run(dummy);
84 Readline::Stop();
85 }
86
87 template<class T, class S>
88 int execute(Configuration &conf, bool dummy=false)
89 {
90 Dim::Setup(conf.Get<string>("dns"), conf.Has("host")?conf.Get<string>("host"):"");
91
92 // -----------------------------------------------------------------
93 const fs::path program(conf.GetName());
94
95 // Split path to program into path and filename
96 const string prgpath = program.parent_path().string();
97
98#if BOOST_VERSION < 104600
99 const string prgname = program.filename();
100#else
101 const string prgname = program.filename().string();
102#endif
103
104 fs::path path = conf.Has("logpath") ? conf.Get<string>("logpath") : "";
105
106 // No explicit path given
107 if (path.empty())
108 {
109 path = prgpath.empty() ? "." : prgpath;
110
111 // default path not accessible
112 if (access(prgpath.c_str(), W_OK))
113 {
114 path = ".";
115
116 if (conf.Has("home"))
117 {
118 path = conf.Get<string>("home");
119 path /= ".fact++";
120 }
121 }
122 }
123
124 // Create directories if necessary
125 fs::create_directories(path);
126
127 // -----------------------------------------------------------------
128
129 static T shell((path/prgname).string().c_str(),
130 conf.Has("console") ? conf.Get<int>("console")!=1 : conf.Get<bool>("null"));
131
132 WindowLog &win = shell.GetStreamIn();
133 WindowLog &wout = shell.GetStreamOut();
134
135 // Switching off buffering is not strictly necessary, since
136 // the destructor of shell should flush everything still buffered,
137 // nevertheless it helps to debug problems in the initialization
138 // sequence.
139 const bool backlog = wout.GetBacklog();
140 const bool null = wout.GetNullOutput();
141 if (conf.Has("console") || !conf.Get<bool>("null"))
142 {
143 wout.SetBacklog(false);
144 wout.SetNullOutput(false);
145 wout.Display(true);
146 }
147
148 if (conf.Has("log") && !conf.Get<bool>("no-log"))
149 {
150#if BOOST_VERSION < 104600
151 const fs::path file = fs::path(conf.Get<string>("log")).filename();
152#else
153 const fs::path file = fs::path(conf.Get<string>("log")).filename();
154#endif
155 if (!wout.OpenLogFile((path/file).string(), conf.Get<bool>("append-log")))
156 win << kYellow << "WARNING - Couldn't open log-file " << (path/file).string() << ": " << strerror(errno) << endl;
157 }
158
159 S io_service(wout);
160
161 const Time now;
162 io_service.Write(now, "/----------------------- Program ------------------------");
163 io_service.Write(now, "| Program: " PACKAGE_STRING " ("+prgname+":"+to_string(getpid())+")");
164 io_service.Write(now, "| CallPath: "+prgpath);
165 io_service.Write(now, "| Compiled: " __DATE__ " " __TIME__ );
166 io_service.Write(now, "| Revision: " REVISION);
167 io_service.Write(now, "| DIM: v"+to_string(DIM_VERSION_NUMBER/100)+"r"+to_string(DIM_VERSION_NUMBER%100)+" ("+io_service.GetName()+")");
168 io_service.Write(now, "| Contact: " PACKAGE_BUGREPORT);
169 io_service.Write(now, "| URL: " PACKAGE_URL);
170 io_service.Write(now, "| Start: "+now.GetAsStr("%c"));
171 io_service.Write(now, "\\----------------------- Options ------------------------");
172 const multimap<string,string> mmap = conf.GetOptions();
173 for (auto it=mmap.begin(); it!=mmap.end(); it++)
174 io_service.Write(now, ": "+it->first+(it->second.empty()?"":" = ")+it->second);
175
176 const map<string,string> &args = conf.GetOptions<string>("arg:");
177 if (!args.empty())
178 {
179 io_service.Write(now, "------------------------ Arguments ----------------------", MessageImp::kMessage);
180
181 for (auto it=args.begin(); it!=args.end(); it++)
182 {
183 ostringstream str;
184 str.setf(ios_base::left);
185 str << ": " << it->first << " = " << it->second;
186 io_service.Write(now, str.str(), MessageImp::kMessage);
187 }
188 }
189
190 io_service.Write(now, "\\------------------- Evaluating options -----------------");
191 const int rc = io_service.EvalOptions(conf);
192 if (rc>=0)
193 {
194 ostringstream str;
195 str << "Exit triggered by EvalOptions with rc=" << rc;
196 io_service.Write(now, str.str(), rc==0?MessageImp::kInfo:MessageImp::kError);
197 return rc;
198 }
199
200 const map<string,string> &wco = conf.GetWildcardOptions();
201 if (!wco.empty())
202 {
203 io_service.Write(now, "------------- Unrecognized wildcard options -------------", MessageImp::kWarn);
204
205 size_t max = 0;
206 for (auto it=wco.begin(); it!=wco.end(); it++)
207 if (it->second.length()>max)
208 max = it->second.length();
209
210 for (auto it=wco.begin(); it!=wco.end(); it++)
211 {
212 ostringstream str;
213 str.setf(ios_base::left);
214 str << setw(max+1) << it->second << " : " << it->first;
215 io_service.Write(now, str.str(), MessageImp::kWarn);
216 }
217 io_service.Write(now, "Unrecognized options found, will exit with rc=127", MessageImp::kError);
218 return 127;
219 }
220
221 io_service.Message("==================== Starting main loop =================");
222
223 if (conf.Has("console") || !conf.Get<bool>("null"))
224 {
225 wout.SetNullOutput(null);
226 wout.SetBacklog(backlog);
227 }
228
229 shell.SetReceiver(io_service);
230
231 // boost::thread t(boost::bind(&AutoScheduler<S>::Run, &io_service));
232 int ret = 0;
233 thread t(bind(Main::Thread, &io_service, dummy, ref(ret)));
234
235 // Wait until state machine is ready (The only case I can imagine
236 // in which the state will never chane is when DIM triggers
237 // an exit before the state machine has been started at all.
238 // Hopefully checking the readline (see Threed) should fix
239 // that -- difficult to test.)
240 while ((io_service.GetCurrentState()<StateMachineImp::kSM_Ready ||
241 !io_service.MessageQueueEmpty()) && !shell.IsStopped())
242 usleep(1);
243
244 // Execute command line commands
245 const vector<string> v1 = conf.Vec<string>("cmd");
246 for (vector<string>::const_iterator it=v1.begin(); it!=v1.end(); it++)
247 shell.ProcessLine(*it);
248
249 const vector<string> v2 = conf.Vec<string>("exec");
250 for (vector<string>::const_iterator it=v2.begin(); it!=v2.end(); it++)
251 shell.Execute(*it, args);
252
253 // Run the shell if no immediate exit was requested
254 if (!conf.Get<bool>("quit"))
255 shell.Run();
256
257 io_service.Stop(); // Signal Loop-thread to stop
258 // io_service.Close(); // Obsolete, done by the destructor
259 // wout << "join: " << t.timed_join(boost::posix_time::milliseconds(0)) << endl;
260
261 // Wait until the StateMachine has finished its thread
262 // before returning and destroying the dim objects which might
263 // still be in use.
264 t.join();
265
266 return ret;
267 }
268}
269
270#endif
Note: See TracBrowser for help on using the repository browser.