solarpowerlog trunk
/home/tobi/workspace/solarpowerlog/src/solarpowerlog.cpp
Go to the documentation of this file.
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 }