#ifndef FACT_Main #define FACT_Main #include #include #include #include #include "dim.h" #include "Dim.h" #include "Time.h" #include "MainImp.h" #include "Readline.h" #include "WindowLog.h" #include "MessageImp.h" #include "Configuration.h" namespace Main { using namespace std; void SetupConfiguration(Configuration &conf) { const string n = conf.GetName()+".log"; po::options_description config("Program options"); config.add_options() ("dns", var("localhost"), "Dim nameserver (overwites DIM_DNS_NODE environment variable)") ("host", var(""), "Address with which the Dim nameserver can connect to this host (overwites DIM_HOST_NODE environment variable)") ("log,l", var(n), "Name of local log-file") ("append-log", po_bool(), "Append log information to local log-file") ("null", po_switch(), "Suppresses almost all console output - including errors (only available without --console option)") ("console,c", var(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)") ("cmd", vars(), "Execute one or more commands at startup") ("exec,e", vars(), "Execute one or more scrips at startup ('file:N' - start at label N)") ("arg:*", var(), "Arguments for script execution with --exc, e.g. --arg:ra='12.5436'") ("quit", po_switch(), "Quit after startup"); ; conf.AddEnv("dns", "DIM_DNS_NODE"); conf.AddEnv("host", "DIM_HOST_NODE"); conf.AddOptions(config); } void PrintUsage() { cout << "Files:\n" "The following files are written by each program by default\n" " program.evt: A log of all executed of skipped events\n" " program.his: The history accessible by Pg-up/dn\n" " program.log: All output piped to the log-stream\n" << endl; } template void PrintHelp() { Dim::Setup(); ofstream fout("/dev/null"); T io_service(fout); io_service.PrintListOfStates(cout); cout << "\nList of available commands:\n"; io_service.PrintListOfEvents(cout); cout << "\n"; } void Thread(MainImp *io_service, bool dummy, int &rc) { // This is necessary so that the StateMachien Thread can signal the // Readline to exit rc = io_service->Run(dummy); Readline::Stop(); } template int execute(Configuration &conf, bool dummy=false) { Dim::Setup(conf.Get("dns"), conf.Get("host")); // ----------------------------------------------------------------- static T shell(conf.GetName().c_str(), conf.Has("console") ? conf.Get("console")!=1 : conf.Get("null")); WindowLog &win = shell.GetStreamIn(); WindowLog &wout = shell.GetStreamOut(); // Switching off buffering is not strictly necessary, since // the destructor of shell should flush everything still buffered, // nevertheless it helps to debug problems in the initialization // sequence. const bool backlog = wout.GetBacklog(); const bool null = wout.GetNullOutput(); if (conf.Has("console") || !conf.Get("null")) { wout.SetBacklog(false); wout.SetNullOutput(false); wout.Display(true); } if (conf.Has("log")) if (!wout.OpenLogFile(conf.Get("log"), conf.Get("append-log"))) win << kRed << "ERROR - Couldn't open log-file " << conf.Get("log") << ": " << strerror(errno) << endl; S io_service(wout); const boost::filesystem::path path(conf.GetName()); const string pname = path.parent_path().string(); #if BOOST_VERSION < 104600 const string fname = path.filename(); #else const string fname = path.filename().string(); #endif const Time now; io_service.Write(now, "/----------------------- Program ------------------------"); io_service.Write(now, "| Program: "PACKAGE_STRING" ("+fname+":"+to_string(getpid())+")"); io_service.Write(now, "| CallPath: "+pname); io_service.Write(now, "| Compiled: "__DATE__" "__TIME__); io_service.Write(now, "| Revision: "REVISION); io_service.Write(now, "| DIM: v"+to_string(DIM_VERSION_NUMBER/100)+"r"+to_string(DIM_VERSION_NUMBER%100)); io_service.Write(now, "| Contact: "PACKAGE_BUGREPORT); io_service.Write(now, "| URL: "PACKAGE_URL); io_service.Write(now, "| Start: "+now.GetAsStr("%c")); io_service.Write(now, "\\----------------------- Options ------------------------"); const multimap mmap = conf.GetOptions(); for (auto it=mmap.begin(); it!=mmap.end(); it++) io_service.Write(now, ": "+it->first+(it->second.empty()?"":" = ")+it->second); const map &args = conf.GetOptions("arg:"); if (args.size()>0) { io_service.Write(now, "------------------------ Arguments ----------------------", MessageImp::kMessage); for (auto it=args.begin(); it!=args.end(); it++) { ostringstream str; str.setf(ios_base::left); str << ": " << it->first << " = " << it->second; io_service.Write(now, str.str(), MessageImp::kMessage); } } io_service.Write(now, "\\------------------- Evaluating options -----------------"); const int rc = io_service.EvalOptions(conf); if (rc>=0) { ostringstream str; str << "Exit triggered by EvalOptions with rc=" << rc; io_service.Write(now, str.str(), MessageImp::kError); return rc; } const map &wco = conf.GetWildcardOptions(); if (wco.size()>0) { io_service.Write(now, "------------- Unrecognized wildcard options -------------", MessageImp::kWarn); size_t max = 0; for (auto it=wco.begin(); it!=wco.end(); it++) if (it->second.length()>max) max = it->second.length(); for (auto it=wco.begin(); it!=wco.end(); it++) { ostringstream str; str.setf(ios_base::left); str << setw(max+1) << it->second << " : " << it->first; io_service.Write(now, str.str(), MessageImp::kWarn); } io_service.Write(now, "Unrecognized options found, will exit with rc=127", MessageImp::kError); return 127; } io_service.Message("==================== Starting main loop ================="); if (conf.Has("console") || !conf.Get("null")) { wout.SetNullOutput(null); wout.SetBacklog(backlog); } shell.SetReceiver(io_service); // boost::thread t(boost::bind(&AutoScheduler::Run, &io_service)); int ret = 0; thread t(bind(Main::Thread, &io_service, dummy, ref(ret))); // Wait until state machine is ready (The only case I can imagine // in which the state will never chane is when DIM triggers // an exit before the state machine has been started at all. // Hopefully checking the readline (see Threed) should fix // that -- difficult to test.) while (io_service.GetCurrentState() v1 = conf.Vec("cmd"); for (vector::const_iterator it=v1.begin(); it!=v1.end(); it++) shell.ProcessLine(*it); const vector v2 = conf.Vec("exec"); for (vector::const_iterator it=v2.begin(); it!=v2.end(); it++) shell.Execute(*it, args); // Run the shell if no immediate exit was requested if (!conf.Get("quit")) shell.Run(); io_service.Stop(); // Signal Loop-thread to stop // io_service.Close(); // Obsolete, done by the destructor // wout << "join: " << t.timed_join(boost::posix_time::milliseconds(0)) << endl; // Wait until the StateMachine has finished its thread // before returning and destroying the dim objects which might // still be in use. t.join(); return ret; } } #endif