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