|
solarpowerlog trunk
|
00001 /* ---------------------------------------------------------------------------- 00002 solarpowerlog 00003 Copyright (C) 2009 Tobias Frost 00004 00005 This file is part of solarpowerlog. 00006 00007 Solarpowerlog is free software; However, it is dual-licenced 00008 as described in the file "COPYING". 00009 00010 For this file (solarpowerlog.cpp), the license terms are: 00011 00012 You can redistribute it and/or modify it under the terms of the GNU 00013 General Public License as published by the Free Software Foundation; either 00014 version 3 of the License, or (at your option) any later version. 00015 00016 This program is distributed in the hope that it will be useful, but 00017 WITHOUT ANY WARRANTY; without even the implied warranty of 00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00019 Lesser General Public License for more details. 00020 00021 You should have received a copy of the GNU Library General Public 00022 License along with this proramm; if not, see 00023 <http://www.gnu.org/licenses/>. 00024 ---------------------------------------------------------------------------- 00025 */ 00026 00076 #ifdef HAVE_CONFIG_H 00077 #include "config.h" 00078 #endif 00079 00080 #include <vector> 00081 #include <string> 00082 #include <iostream> 00083 #include <iterator> 00084 00085 #if defined HAVE_LIBLOG4CXX 00086 #include <log4cxx/logger.h> 00087 #include <log4cxx/simplelayout.h> 00088 #include <log4cxx/basicconfigurator.h> 00089 #include <log4cxx/propertyconfigurator.h> 00090 #include <log4cxx/xml/domconfigurator.h> 00091 // for testing 00092 #include <log4cxx/net/syslogappender.h> 00093 using namespace log4cxx::net; 00094 #endif 00095 00096 #ifdef HAVE_OPENLOG 00097 #include <syslog.h> 00098 #endif 00099 00100 #include <sys/stat.h> 00101 #include <signal.h> 00102 00103 #include "configuration/ILogger.h" 00104 00105 #ifdef HAVE_CMDLINEOPTS 00106 #include <boost/program_options.hpp> 00107 #endif 00108 00109 #include "configuration/Registry.h" 00110 #include "interfaces/CWorkScheduler.h" 00111 #include "patterns/ICommand.h" 00112 #include "patterns/ICommandTarget.h" 00113 00114 #include "Inverters/factories/IInverterFactory.h" 00115 #include "Inverters/factories/InverterFactoryFactory.h" 00116 #include "Inverters/interfaces/InverterBase.h" 00117 00118 #include "DataFilters/interfaces/factories/IDataFilterFactory.h" 00119 #include "configuration/CConfigHelper.h" 00120 00121 using namespace std; 00122 00123 #ifdef HAVE_LIBLOG4CXX 00124 using namespace log4cxx; 00125 #endif 00126 00127 char *progname; 00128 00132 static const char *required_sections[] = { "application", "inverter", 00133 "inverter.inverters", "logger", "logger.loggers" }; 00134 00145 void DumpSettings(libconfig::Setting &set) 00146 { 00147 cout << "line " << set.getSourceLine() << " "; 00148 00149 if (set.getPath() != "") 00150 cout << set.getPath() << " "; 00151 00152 if (!set.getName()) { 00153 cout << "(anonymous) "; 00154 } 00155 00156 try { 00157 std::string s = set; 00158 cout << "= " << s << "\t is"; 00159 } catch (...) { 00160 00161 } 00162 00163 try { 00164 float s = set; 00165 cout << "= " << s << "\t is"; 00166 } catch (...) { 00167 00168 } 00169 00170 try { 00171 bool s = set; 00172 cout << "= " << s << "\t is"; 00173 } catch (...) { 00174 00175 } 00176 00177 00178 if (set.isAggregate()) { 00179 cout << " aggregate"; 00180 }; 00181 if (set.isArray()) { 00182 cout << " array"; 00183 } 00184 if (set.isGroup()) { 00185 cout << " group"; 00186 } 00187 if (set.isList()) { 00188 cout << " list"; 00189 } 00190 //if (set.getPath() != "" ) cout << set.getPath() << "." ; 00191 if (set.isNumber()) 00192 cout << " number"; 00193 if (set.isScalar()) 00194 cout << " scalar"; 00195 00196 cout << endl; 00197 00198 for (int i = 0; i < set.getLength(); i++) { 00199 00200 libconfig::Setting &s2 = set[i]; 00201 DumpSettings(s2); 00202 } 00203 } 00204 00205 volatile sig_atomic_t killsignal = false; 00206 00207 void SignalHandler(int signal) 00208 { 00209 switch (signal) 00210 { 00211 00212 case SIGTERM: 00213 // die. 00214 if (!killsignal) { 00215 killsignal = true; 00216 LOGINFO(Registry::GetMainLogger(), progname << " Termination requested. Will terminate at next opportunity."); 00217 } else { 00218 LOGFATAL(Registry::GetMainLogger(), progname << " Termination signal received. Please be patient."); 00219 } 00220 break; 00221 case SIGSEGV: 00222 cerr << progname << " Segmentation fault. " << endl; 00223 LOGFATAL(Registry::GetMainLogger(), progname << " Segmentation fault."); 00224 exit(1); 00225 } 00226 } 00227 00228 void daemonize(void) 00229 { 00230 ILogger mainlogger; 00231 00232 // dameonize. 00233 LOGDEBUG(mainlogger, "Rising a daemon."); 00234 00235 pid_t pid, sid; 00236 pid = fork(); 00237 if (pid < 0) { 00238 LOGFATAL(mainlogger, "Could not dameonize. fork() error="<<errno ); 00239 exit(1); 00240 } 00241 00242 if (pid > 0) { 00243 // on daemons, the parent use _exit. 00244 _exit(EXIT_SUCCESS); 00245 } 00246 00247 sid = setsid(); 00248 if (sid < 0) { 00249 LOGFATAL(mainlogger, "Could not dameonize. setsid() error="<<errno ); 00250 exit(EXIT_FAILURE); 00251 } 00252 00253 // the second fork seems not to be necessary on linux, but believing 00254 // references, it is for for some unixes to avoid reattached to some 00255 // controlling tty 00256 // http://web.archive.org/web/20020416015637/www.whitefang.com/unix/faq_2.html#SEC16 00257 pid = fork(); 00258 if (pid < 0) { 00259 LOGFATAL(mainlogger, "Could not dameonize. 2nd fork() error="<<errno ); 00260 exit(1); 00261 } 00262 00263 if (pid > 0) { 00264 // on daemons, the parent use _exit. 00265 _exit(EXIT_SUCCESS); 00266 } 00267 00268 // change umask to 000, change dir to root dir to avoid locking the dir 00269 // we started from 00270 umask(0); 00271 chdir("/"); 00272 00273 // reopen stdio, stderr, stdin 00274 freopen("/dev/null", "r", stdin); 00275 freopen("/dev/null", "w", stdout); 00276 freopen("/dev/null", "w", stderr); 00277 } 00278 00279 int main(int argc, char* argv[]) 00280 { 00281 bool error_detected = false; 00282 bool dumpconfig = false; 00283 bool background = false; 00284 string configfile = "solarpowerlog.conf"; 00285 00286 progname = argv[0]; 00287 00288 #ifdef HAVE_CMDLINEOPTS 00289 using namespace boost::program_options; 00290 00291 options_description desc("Program Options"); 00292 desc.add_options()("help", "this message")("conf,c", value<string> ( 00293 &configfile), "specify configuration file")("version,v", 00294 "display solarpowerlog version")("background,b", value<bool> ( 00295 &background)->zero_tokens(), "run in background.")("dumpcfg", 00296 value<bool> (&dumpconfig)->zero_tokens(), 00297 "Dump configuration structure, then exit"); 00298 00299 variables_map vm; 00300 try { 00301 store(parse_command_line(argc, argv, desc), vm); 00302 notify(vm); 00303 } catch (exception &e) { 00304 cerr << desc << "\n"; 00305 return 1; 00306 00307 } 00308 00309 if (vm.count("help")) { 00310 cout << desc << "\n"; 00311 return 0; 00312 } 00313 00314 if (vm.count("version")) { 00315 cout << PACKAGE_STRING << endl; 00316 return 0; 00317 } 00318 00319 #else 00320 if (argc > 1) { 00321 (void) argv; // remove warning unused parameter. 00322 cerr << "This version does not support command line options." << endl; 00323 exit(1); 00324 } 00325 #endif 00326 00327 if (!Registry::Instance().LoadConfig(configfile)) { 00328 cerr << "Could not load configuration " << configfile << endl; 00329 exit(1); 00330 } 00331 00332 if (dumpconfig) { 00333 cout << "Dumping structure of " << configfile << endl; 00334 Registry::Configuration()->setAutoConvert(true); 00335 DumpSettings(Registry::Configuration()->getRoot()); 00336 exit(0); 00337 } 00338 00339 // Note: As a limitation of libconfig, one cannot create the configs 00340 // structure. 00341 // Therefore we check here for the basic required sections and abort, 00342 // if one is not existing. 00343 { 00344 libconfig::Config *cfg = Registry::Configuration(); 00345 libconfig::Setting &rt = cfg->getRoot(); 00346 00347 for (unsigned int i = 0; i < sizeof(required_sections) / sizeof(char*); i++) { 00348 if (!rt.exists(required_sections[i])) { 00349 cerr << " Configuration Check: Required Section " 00350 << required_sections[i] << " missing" << endl; 00351 error_detected = true; 00352 } 00353 } 00354 } 00355 00356 if (error_detected) { 00357 cerr << "Error detected" << endl; 00358 exit(1); 00359 } 00360 00361 #ifdef HAVE_OPENLOG 00362 // prepare the syslog, needed if we gonna log to it 00363 // (if the user configures this, as the liblog4cxx supports syslog as well) 00364 openlog(progname, LOG_PID, LOG_USER); 00365 #endif 00366 00367 #if defined HAVE_LIBLOG4CXX 00368 // Activate Logging framework 00369 { 00370 string tmp; 00371 LoggerPtr l = Logger::getRootLogger(); 00372 00373 CConfigHelper global("application"); 00374 global.GetConfig("dbglevel", tmp, (std::string) "ERROR"); 00375 l->setLevel(Level::toLevel(tmp)); 00376 00377 try { 00378 // Choose your poison .. aem .. config file format 00379 if (global.GetConfig("logconfig", tmp)) { 00380 if (tmp.substr(tmp.length() - 4, string::npos) == ".xml") { 00381 xml::DOMConfigurator::configure(tmp); 00382 } else { 00383 PropertyConfigurator::configure(tmp); 00384 } 00385 00386 } else { 00387 BasicConfigurator::configure(); 00388 } 00389 } catch (...) { 00390 cerr << "WARNING: Could not configure logging." << endl; 00391 } 00392 00393 LOG4CXX_INFO(l,"Logging set up."); 00394 } 00395 #endif 00396 00397 // fork to background if demanded 00398 if (background) 00399 daemonize(); 00400 00401 // register some signal handler to detect when we want to quit. 00402 signal(SIGTERM, SignalHandler); 00403 signal(SIGSEGV, SignalHandler); 00404 00405 ILogger mainlogger; 00406 00408 LOGDEBUG(mainlogger, 00409 "Instanciating Inverter objects"); 00410 00412 { 00413 string section = "inverter.inverters"; 00414 libconfig::Setting &rt = Registry::Configuration()->lookup(section); 00415 00416 for (int i = 0; i < rt.getLength(); i++) { 00417 std::string name; 00418 std::string manufactor; 00419 std::string model; 00420 00421 try { 00422 name = (const char *) rt[i]["name"]; 00423 manufactor = (const char *) rt[i]["manufactor"]; 00424 model = (const char *) rt[i]["model"]; 00425 LOGDEBUG(mainlogger, 00426 name << " " << manufactor ); 00427 } catch (libconfig::SettingNotFoundException &e) { 00428 LOGFATAL(mainlogger, 00429 "Configuration Error: Required Setting was not found in \"" 00430 << e.getPath() << '\"'); 00431 exit(1); 00432 } 00433 00434 if (Registry::Instance().GetInverter(name)) { 00435 LOGFATAL(mainlogger, "Inverter " << name 00436 << " declared more than once"); 00437 exit(1); 00438 } 00439 00440 IInverterFactory *factory = 00441 InverterFactoryFactory::createInverterFactory(manufactor); 00442 if (!factory) { 00443 LOGFATAL(mainlogger, 00444 "Unknown inverter manufactor \"" 00445 << manufactor << '\"'); 00446 exit(1); 00447 } 00448 00449 IInverterBase *inverter = factory->Factory(model, name, 00450 rt[i].getPath()); 00451 00452 if (!inverter) { 00453 LOGFATAL(mainlogger, 00454 "Cannot create inverter model " 00455 << model << "for manufactor \"" 00456 << manufactor << '\"'); 00457 00458 LOGFATAL(mainlogger, 00459 "Supported models are: " 00460 << factory->GetSupportedModels()); 00461 exit(1); 00462 } 00463 00464 if (!inverter->CheckConfig()) { 00465 LOGFATAL(mainlogger, 00466 "Inverter " << name << " ( " 00467 << manufactor << ", " << model 00468 << ") reported configuration error"); 00469 exit(1); 00470 } 00471 00472 Registry::Instance().AddInverter(inverter); 00473 // destroy the (used) factory. 00474 delete factory; 00475 } 00476 } 00477 00478 LOGDEBUG(mainlogger, "Instantiating DataFilter objects"); 00479 00480 { 00481 IDataFilterFactory factory; 00484 string section = "logger.loggers"; 00485 libconfig::Setting & rt = Registry::Configuration()->lookup(section); 00486 00487 for (int i = 0; i < rt.getLength(); i++) { 00488 std::string name; 00489 std::string previousfilter; 00490 std::string type; 00491 00492 try { 00493 name = (const char *) rt[i]["name"]; 00494 previousfilter = (const char *) rt[i]["datasource"]; 00495 type = (const char *) rt[i]["type"]; 00496 00497 LOGDEBUG(mainlogger, 00498 "Datafilter " << name << " (" 00499 << type << ") connects to " 00500 << previousfilter << 00501 " with Config-path " << rt[i].getPath()); 00502 00503 } catch (libconfig::SettingNotFoundException e) { 00504 LOGFATAL(mainlogger, 00505 "Configuration Error: Required Setting was not found in \"" 00506 << e.getPath() << '\"' ); 00507 exit(1); 00508 } 00509 00510 // TODO Also check for duplicate DataFilters. 00511 if (Registry::Instance().GetInverter(name)) { 00512 LOGFATAL(mainlogger, 00513 "CONFIG ERROR: Inverter or Logger Nameclash: " 00514 << name << " declared more than once" 00515 ); 00516 exit(1); 00517 } 00518 00519 IDataFilter *filter = factory.Factory(rt[i].getPath()); 00520 00521 if (!filter) { 00522 LOGFATAL(mainlogger, 00523 "Couldn't create DataFilter " << name 00524 << "(" << type << ") connecting to " 00525 << previousfilter << " Config-path " << rt[i].getPath() 00526 ); 00527 exit(1); 00528 } 00529 00530 if (!filter->CheckConfig()) { 00531 LOGFATAL(mainlogger, 00532 "DataFilter " << name << "(" << type 00533 << ") reported config error" 00534 << previousfilter ); 00535 exit(1); 00536 } 00537 00538 Registry::Instance().AddInverter(filter); 00539 00540 // Filter is ready. 00541 } 00542 } 00543 00544 while (!killsignal) { 00545 Registry::GetMainScheduler()->DoWork(true); 00546 } 00547 00548 LOGINFO(Registry::GetMainLogger(), "Terminating."); 00549 return 0; 00550 }