1 #include "../include/fmsapp.h"
\r
2 #include "../include/global.h"
\r
3 #include "../include/dbsetup.h"
\r
4 #include "../include/optionssetup.h"
\r
5 #include "../include/option.h"
\r
6 #include "../include/stringfunctions.h"
\r
7 #include "../include/http/httpthread.h"
\r
8 #include "../include/nntp/nntplistener.h"
\r
9 #include "../include/dbmaintenancethread.h"
\r
10 #include "../include/freenet/freenetmasterthread.h"
\r
11 #include "../include/threadwrapper/threadedexecutor.h"
\r
12 #include "../include/db/sqlite3db.h"
\r
14 #include <Poco/Util/HelpFormatter.h>
\r
15 #include <Poco/FileChannel.h>
\r
16 #include <Poco/ConsoleChannel.h>
\r
17 #include <Poco/FormattingChannel.h>
\r
18 #include <Poco/PatternFormatter.h>
\r
29 #ifdef FROST_SUPPORT
\r
30 #include <tommath.h>
\r
31 #include <tomcrypt.h>
\r
34 FMSApp::FMSApp():m_displayhelp(false),m_showoptions(false),m_setoption(false),m_logtype("file"),m_workingdirectory("")
\r
36 // get current working dir so we can go to it later
\r
40 wdptr=getcwd(wd,1023);
\r
43 m_workingdirectory=wdptr;
\r
47 void FMSApp::defineOptions(Poco::Util::OptionSet &options)
\r
49 ServerApplication::defineOptions(options);
\r
51 // add command line options here
\r
52 options.addOption(Poco::Util::Option("help","?","Display help for command line arguments.",false).repeatable(false).callback(Poco::Util::OptionCallback<FMSApp>(this,&FMSApp::handleHelp)));
\r
53 options.addOption(Poco::Util::Option("log","l","Select type of log output (file|stdout|stderr).",false,"type",true).repeatable(false).callback(Poco::Util::OptionCallback<FMSApp>(this,&FMSApp::handleLogOption)));
\r
54 options.addOption(Poco::Util::Option("showoptions","","Show all options that can be set and their current values.",false).repeatable(false).callback(Poco::Util::OptionCallback<FMSApp>(this,&FMSApp::handleShowOptions)));
\r
55 options.addOption(Poco::Util::Option("setoption","","Set an option. Values are not validated, so be sure to set them correctly.",false,"option=value",true).repeatable(true).callback(Poco::Util::OptionCallback<FMSApp>(this,&FMSApp::handleSetOption)));
\r
58 void FMSApp::displayHelp()
\r
60 Poco::Util::HelpFormatter helpFormatter(options());
\r
61 helpFormatter.setCommand(commandName());
\r
62 helpFormatter.setUsage("OPTIONS");
\r
63 helpFormatter.setHeader("The Freenet Message System.");
\r
64 helpFormatter.format(std::cout);
\r
67 void FMSApp::handleHelp(const std::string &name, const std::string &value)
\r
71 stopOptionsProcessing();
\r
74 void FMSApp::handleLogOption(const std::string &name, const std::string &value)
\r
76 if(value=="file" || value=="stdout" || value=="stderr")
\r
82 void FMSApp::handleSetOption(const std::string &name, const std::string &value)
\r
84 std::vector<std::string> valueparts;
\r
85 StringFunctions::Split(value,"=",valueparts);
\r
87 if(valueparts.size()==2)
\r
89 m_setoptions[valueparts[0]]=valueparts[1];
\r
93 std::cout << "Expected option=value but found " << value << std::endl;
\r
99 void FMSApp::handleShowOptions(const std::string &name, const std::string &value)
\r
101 m_showoptions=true;
\r
104 void FMSApp::initialize(Poco::Util::Application &self)
\r
106 ServerApplication::initialize(self);
\r
108 // set working directory - fall back on application.dir if working directory isn't set
\r
109 // if we are runing as a service, then working directory needs to be set to the application directory
\r
110 if(m_workingdirectory=="" || config().getBool("application.runAsService",false)==true)
\r
112 m_workingdirectory=config().getString("application.dir");
\r
114 int rval=chdir(m_workingdirectory.c_str());
\r
118 Poco::AutoPtr<Poco::FormattingChannel> formatter=new Poco::FormattingChannel(new Poco::PatternFormatter("%Y-%m-%d %H:%M:%S | %t"));
\r
119 Poco::AutoPtr<Poco::FileChannel> fc=new Poco::FileChannel("query.log");
\r
120 fc->setProperty("rotation","daily");
\r
121 fc->setProperty("times","utc");
\r
122 fc->setProperty("archive","timestamp");
\r
123 fc->setProperty("purgeCount","5");
\r
124 fc->setProperty("compress","true");
\r
125 formatter->setChannel(fc);
\r
126 Poco::Logger::create("querylog",formatter,Poco::Message::PRIO_INFORMATION);
\r
132 SetupDefaultOptions(m_db);
\r
133 initializeLogger();
\r
134 config().setString("application.logger","logfile");
\r
137 void FMSApp::initializeLogger()
\r
139 int initiallevel=Poco::Message::PRIO_TRACE;
\r
141 std::string tempval="";
\r
142 Option option(m_db);
\r
144 if(option.Get("LogLevel",tempval))
\r
146 StringFunctions::Convert(tempval,initiallevel);
\r
149 Poco::AutoPtr<Poco::FormattingChannel> formatter=new Poco::FormattingChannel(new Poco::PatternFormatter("%Y-%m-%d %H:%M:%S | %p | %t"));
\r
151 if(m_logtype=="file")
\r
153 Poco::AutoPtr<Poco::FileChannel> fc=new Poco::FileChannel("fms.log");
\r
154 fc->setProperty("rotation","daily"); // rotate log file daily
\r
155 fc->setProperty("times","utc"); // utc date/times for log entries
\r
156 fc->setProperty("archive","timestamp"); // add timestamp to old logs
\r
157 fc->setProperty("purgeCount","30"); // purge old logs after 30 logs have accumulated
\r
158 fc->setProperty("compress","true"); // gz compress old log files
\r
159 formatter->setChannel(fc);
\r
163 if(m_logtype=="stdout")
\r
165 Poco::AutoPtr<Poco::ConsoleChannel> cc=new Poco::ConsoleChannel(std::cout);
\r
166 formatter->setChannel(cc);
\r
170 Poco::AutoPtr<Poco::ConsoleChannel> cc=new Poco::ConsoleChannel(std::cerr);
\r
171 formatter->setChannel(cc);
\r
175 setLogger(Poco::Logger::create("logfile",formatter,Poco::Message::PRIO_INFORMATION));
\r
176 Poco::Logger::get("logfile").information("LogLevel set to "+tempval);
\r
177 Poco::Logger::get("logfile").setLevel(initiallevel);
\r
181 int FMSApp::main(const std::vector<std::string> &args)
\r
184 // running as a daemon would reset the working directory to / before calling main
\r
185 // so we need to set the working directory again
\r
186 int rval=chdir(m_workingdirectory.c_str());
\r
188 if(VerifyDB(m_db)==false)
\r
190 std::cout << "The FMS database failed verification. It is most likely corrupt!" << std::endl;
\r
191 logger().fatal("The FMS database failed verification. It is most likely corrupt!");
\r
193 else if(m_displayhelp)
\r
196 else if(m_showoptions)
\r
200 else if(m_setoption)
\r
206 logger().information("FMS startup v"FMS_VERSION);
\r
207 logger().information("Using SQLite "SQLITE_VERSION);
\r
209 std::string tempval("");
\r
210 Option option(m_db);
\r
211 option.Get("VacuumOnStartup",tempval);
\r
212 if(tempval=="true")
\r
214 logger().information("VACUUMing database");
\r
215 m_db->Execute("VACUUM;");
\r
218 #ifdef FROST_SUPPORT
\r
220 register_hash(&sha1_desc);
\r
225 if(isInteractive()==true)
\r
227 std::cout << "FMS has been started." << std::endl << std::endl;
\r
230 waitForTerminationRequest();
\r
232 if(isInteractive()==true)
\r
234 std::cout << "Please wait while FMS shuts down." << std::endl << std::endl;
\r
237 logger().trace("FMSApp::main cancelling threads");
\r
238 m_threads.Cancel();
\r
239 logger().trace("FMSApp::main joining threads");
\r
242 logger().information("FMS shutdown");
\r
245 return FMSApp::EXIT_OK;
\r
248 void FMSApp::setOptions()
\r
250 for(std::map<std::string,std::string>::iterator i=m_setoptions.begin(); i!=m_setoptions.end(); i++)
\r
252 std::string tempval("");
\r
253 Option option(m_db);
\r
254 if(option.Get((*i).first,tempval))
\r
256 option.Set((*i).first,(*i).second);
\r
257 std::cout << "Option " << (*i).first << " set to " << (*i).second << std::endl;
\r
261 std::cout << "Unknown option " << (*i).first << std::endl;
\r
266 void FMSApp::showOptions()
\r
268 SQLite3DB::Statement st=m_db->Prepare("SELECT Option, OptionValue FROM tblOption;");
\r
270 while(st.RowReturned())
\r
272 std::string option="";
\r
273 std::string optionvalue="";
\r
274 st.ResultText(0,option);
\r
275 st.ResultText(1,optionvalue);
\r
277 std::cout << option << " = " << optionvalue << std::endl;
\r
283 void FMSApp::StartThreads()
\r
285 std::string tempval("");
\r
286 Option option(m_db);
\r
288 // always start the DB maintenance thread
\r
289 logger().trace("FMSApp::StartThreads starting DBMaintenanceThread");
\r
290 m_threads.Start(new DBMaintenanceThread());
\r
292 option.Get("StartHTTP",tempval);
\r
293 if(tempval=="true")
\r
295 logger().trace("FMSApp::StartThreads starting HTTPThread");
\r
296 m_threads.Start(new HTTPThread());
\r
297 if(isInteractive())
\r
299 std::cout << "Started HTTP Thread" << std::endl;
\r
304 logger().trace("FMSApp::StartThreads not starting HTTPThread");
\r
308 option.Get("StartNNTP",tempval);
\r
309 if(tempval=="true")
\r
311 logger().trace("FMSApp::StartThreads starting NNTPListener");
\r
312 m_threads.Start(new NNTPListener());
\r
313 if(isInteractive())
\r
315 std::cout << "Started NNTP Thread" << std::endl;
\r
320 logger().trace("FMSApp::StartThreads not starting NNTPListener");
\r
324 option.Get("StartFreenetUpdater",tempval);
\r
325 if(tempval=="true")
\r
327 logger().trace("FMSApp::StartThreads starting FreenetMasterThread");
\r
328 m_threads.Start(new FreenetMasterThread());
\r
329 if(isInteractive())
\r
331 std::cout << "Started Freenet FCP Thread" << std::endl;
\r
336 logger().trace("FMSApp::StartThreads not starting FreenetMasterThread");
\r