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