261ea4643c96abe934b2bc8d34e670acb612c155
[fms.git] / src / fmsapp.cpp
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
13 \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
19 #include <iostream>\r
20 #include <string>\r
21 #include <cstring>\r
22 \r
23 #ifdef _WIN32\r
24         #include <direct.h>\r
25 #else\r
26         #include <unistd.h>\r
27 #endif\r
28 \r
29 FMSApp::FMSApp():m_displayhelp(false),m_showoptions(false),m_setoption(false),m_logtype("file"),m_workingdirectory("")\r
30 {\r
31         // get current working dir so we can go to it later\r
32         char wd[1024];\r
33         char *wdptr=NULL;\r
34         memset(wd,0,1024);\r
35         wdptr=getcwd(wd,1023);\r
36         if(wdptr)\r
37         {\r
38                 m_workingdirectory=wdptr;\r
39         }\r
40 }\r
41 \r
42 void FMSApp::defineOptions(Poco::Util::OptionSet &options)\r
43 {\r
44         ServerApplication::defineOptions(options);\r
45 \r
46         // add command line options here\r
47         options.addOption(Poco::Util::Option("help","?","Display help for command line arguments.",false).repeatable(false).callback(Poco::Util::OptionCallback<FMSApp>(this,&FMSApp::handleHelp)));\r
48         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
49         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
50         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
51 }\r
52 \r
53 void FMSApp::displayHelp()\r
54 {\r
55         Poco::Util::HelpFormatter helpFormatter(options());\r
56         helpFormatter.setCommand(commandName());\r
57         helpFormatter.setUsage("OPTIONS");\r
58         helpFormatter.setHeader("The Freenet Message System.");\r
59         helpFormatter.format(std::cout);\r
60 }\r
61 \r
62 void FMSApp::handleHelp(const std::string &name, const std::string &value)\r
63 {\r
64         m_displayhelp=true;\r
65         displayHelp();\r
66         stopOptionsProcessing();\r
67 }\r
68 \r
69 void FMSApp::handleLogOption(const std::string &name, const std::string &value)\r
70 {\r
71         if(value=="file" || value=="stdout" || value=="stderr")\r
72         {\r
73                 m_logtype=value;\r
74         }\r
75 }\r
76 \r
77 void FMSApp::handleSetOption(const std::string &name, const std::string &value)\r
78 {\r
79         std::vector<std::string> valueparts;\r
80         StringFunctions::Split(value,"=",valueparts);\r
81 \r
82         if(valueparts.size()==2)\r
83         {\r
84                 m_setoptions[valueparts[0]]=valueparts[1];\r
85         }\r
86         else\r
87         {\r
88                 std::cout << "Expected option=value but found " << value << std::endl;\r
89         }\r
90 \r
91         m_setoption=true;\r
92 }\r
93 \r
94 void FMSApp::handleShowOptions(const std::string &name, const std::string &value)\r
95 {\r
96         m_showoptions=true;\r
97 }\r
98 \r
99 void FMSApp::initialize(Poco::Util::Application &self)\r
100 {\r
101         ServerApplication::initialize(self);\r
102 \r
103         // set working directory - fall back on application.dir if working directory isn't set\r
104         // if we are runing as a service, then working directory needs to be set to the application directory\r
105         if(m_workingdirectory=="" || config().getBool("application.runAsService",false)==true)\r
106         {\r
107                 m_workingdirectory=config().getString("application.dir");\r
108         }\r
109         int rval=chdir(m_workingdirectory.c_str());\r
110 \r
111         SetupDB();\r
112         SetupDefaultOptions();\r
113         initializeLogger();\r
114         config().setString("application.logger","logfile");\r
115 }\r
116 \r
117 void FMSApp::initializeLogger()\r
118 {\r
119         int initiallevel=Poco::Message::PRIO_TRACE;\r
120 \r
121         std::string tempval="";\r
122         if(Option::Instance()->Get("LogLevel",tempval))\r
123         {\r
124                 StringFunctions::Convert(tempval,initiallevel);\r
125         }\r
126 \r
127         Poco::AutoPtr<Poco::FormattingChannel> formatter=new Poco::FormattingChannel(new Poco::PatternFormatter("%Y-%m-%d %H:%M:%S | %p | %t"));\r
128         \r
129         if(m_logtype=="file")\r
130         {\r
131                 Poco::AutoPtr<Poco::FileChannel> fc=new Poco::FileChannel("fms.log");\r
132                 fc->setProperty("rotation","daily");    // rotate log file daily\r
133                 fc->setProperty("times","utc");                 // utc date/times for log entries\r
134                 fc->setProperty("archive","timestamp"); // add timestamp to old logs\r
135                 fc->setProperty("purgeCount","30");             // purge old logs after 30 logs have accumulated\r
136                 fc->setProperty("compress","true");             // gz compress old log files\r
137                 formatter->setChannel(fc);\r
138         }\r
139         else\r
140         {\r
141                 if(m_logtype=="stdout")\r
142                 {\r
143                         Poco::AutoPtr<Poco::ConsoleChannel> cc=new Poco::ConsoleChannel(std::cout);\r
144                         formatter->setChannel(cc);\r
145                 }\r
146                 else\r
147                 {\r
148                         Poco::AutoPtr<Poco::ConsoleChannel> cc=new Poco::ConsoleChannel(std::cerr);\r
149                         formatter->setChannel(cc);\r
150                 }\r
151         }\r
152         \r
153         setLogger(Poco::Logger::create("logfile",formatter,Poco::Message::PRIO_INFORMATION));\r
154         Poco::Logger::get("logfile").information("LogLevel set to "+tempval);\r
155         Poco::Logger::get("logfile").setLevel(initiallevel);\r
156 }\r
157 \r
158 int FMSApp::main(const std::vector<std::string> &args)\r
159 {\r
160 \r
161         // running as a daemon would reset the working directory to / before calling main\r
162         // so we need to set the working directory again\r
163         int rval=chdir(m_workingdirectory.c_str());\r
164 \r
165         if(VerifyDB()==false)\r
166         {\r
167                 std::cout << "The FMS database failed verification.  It is most likely corrupt!" << std::endl;\r
168                 logger().fatal("The FMS database failed verification.  It is most likely corrupt!");\r
169         }\r
170         else if(m_displayhelp)\r
171         {\r
172         }\r
173         else if(m_showoptions)\r
174         {\r
175                 showOptions();\r
176         }\r
177         else if(m_setoption)\r
178         {\r
179                 setOptions();\r
180         }\r
181         else\r
182         {\r
183                 logger().information("FMS startup v"FMS_VERSION);\r
184                 logger().information("Using SQLite "SQLITE_VERSION);\r
185 \r
186                 std::string tempval="";\r
187                 Option::Instance()->Get("VacuumOnStartup",tempval);\r
188                 if(tempval=="true")\r
189                 {\r
190                         logger().information("VACUUMing database");\r
191                         SQLite3DB::DB::Instance()->Execute("VACUUM;");\r
192                 }\r
193 \r
194                 StartThreads();\r
195 \r
196                 if(isInteractive()==true)\r
197                 {\r
198                         std::cout << "FMS has been started." << std::endl << std::endl;\r
199                 }\r
200 \r
201                 waitForTerminationRequest();\r
202 \r
203                 if(isInteractive()==true)\r
204                 {\r
205                         std::cout << "Please wait while FMS shuts down." << std::endl << std::endl;\r
206                 }\r
207 \r
208                 logger().trace("FMSApp::main cancelling threads");\r
209                 m_threads.Cancel();\r
210                 logger().trace("FMSApp::main joining threads");\r
211                 m_threads.Join();\r
212 \r
213                 logger().information("FMS shutdown");\r
214         }\r
215 \r
216         return FMSApp::EXIT_OK;\r
217 }\r
218 \r
219 void FMSApp::setOptions()\r
220 {\r
221         for(std::map<std::string,std::string>::iterator i=m_setoptions.begin(); i!=m_setoptions.end(); i++)\r
222         {\r
223                 std::string tempval="";\r
224                 if(Option::Instance()->Get((*i).first,tempval))\r
225                 {\r
226                         Option::Instance()->Set((*i).first,(*i).second);\r
227                         std::cout << "Option " << (*i).first << " set to " << (*i).second << std::endl;\r
228                 }\r
229                 else\r
230                 {\r
231                         std::cout << "Unknown option " << (*i).first << std::endl;\r
232                 }\r
233         }\r
234 }\r
235 \r
236 void FMSApp::showOptions()\r
237 {\r
238         SQLite3DB::Statement st=SQLite3DB::DB::Instance()->Prepare("SELECT Option, OptionValue FROM tblOption;");\r
239         st.Step();\r
240         while(st.RowReturned())\r
241         {\r
242                 std::string option="";\r
243                 std::string optionvalue="";\r
244                 st.ResultText(0,option);\r
245                 st.ResultText(1,optionvalue);\r
246 \r
247                 std::cout << option << " = " << optionvalue << std::endl;\r
248 \r
249                 st.Step();\r
250         }\r
251 }\r
252 \r
253 void FMSApp::StartThreads()\r
254 {\r
255         std::string tempval="";\r
256 \r
257         // always start the DB maintenance thread\r
258         logger().trace("FMSApp::StartThreads starting DBMaintenanceThread");\r
259         m_threads.Start(new DBMaintenanceThread());\r
260 \r
261         Option::Instance()->Get("StartHTTP",tempval);\r
262         if(tempval=="true")\r
263         {\r
264                 logger().trace("FMSApp::StartThreads starting HTTPThread");\r
265                 m_threads.Start(new HTTPThread());\r
266                 if(isInteractive())\r
267                 {\r
268                         std::cout << "Started HTTP Thread" << std::endl;\r
269                 }\r
270         }\r
271         else\r
272         {\r
273                 logger().trace("FMSApp::StartThreads not starting HTTPThread");\r
274         }\r
275 \r
276         tempval="";\r
277         Option::Instance()->Get("StartNNTP",tempval);\r
278         if(tempval=="true")\r
279         {\r
280                 logger().trace("FMSApp::StartThreads starting NNTPListener");\r
281                 m_threads.Start(new NNTPListener());\r
282                 if(isInteractive())\r
283                 {\r
284                         std::cout << "Started NNTP Thread" << std::endl;\r
285                 }\r
286         }\r
287         else\r
288         {\r
289                 logger().trace("FMSApp::StartThreads not starting NNTPListener");\r
290         }\r
291 \r
292         tempval="";\r
293         Option::Instance()->Get("StartFreenetUpdater",tempval);\r
294         if(tempval=="true")\r
295         {\r
296                 logger().trace("FMSApp::StartThreads starting FreenetMasterThread");\r
297                 m_threads.Start(new FreenetMasterThread());\r
298                 if(isInteractive())\r
299                 {\r
300                         std::cout << "Started Freenet FCP Thread" << std::endl;\r
301                 }\r
302         }\r
303         else\r
304         {\r
305                 logger().trace("FMSApp::StartThreads not starting FreenetMasterThread");\r
306         }\r
307 \r
308 }\r