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