LCOV - code coverage report
Current view: top level - config - config.cpp (source / functions) Hit Total Coverage
Test: CoherentDB code coverage Lines: 132 210 62.9 %
Date: 2011-02-13 Functions: 24 31 77.4 %

          Line data    Source code
       1             : /*
       2             :  * (C) Copyright 2010 Marek Dopiera
       3             :  * 
       4             :  * This file is part of CoherentDB.
       5             :  * 
       6             :  * CoherentDB is free software: you can redistribute it and/or modify it
       7             :  * under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation, either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  * 
      11             :  * CoherentDB is distributed in the hope that it will be useful, but
      12             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      14             :  * General Public License for more details.
      15             :  * 
      16             :  * You should have received a copy of the GNU General Public
      17             :  * License along with CoherentDB. If not, see
      18             :  * http://www.gnu.org/licenses/.
      19             :  */
      20             : 
      21             : #include <ctype.h>
      22             : #include <fcntl.h>
      23             : #include <sys/param.h>
      24             : 
      25             : #include <sstream>
      26             : #include <iostream>
      27             : #include <cstdlib>
      28             : #include <string>
      29             : #include <algorithm>
      30             : #include <functional>
      31             : #include <fstream>
      32             : #include <sstream>
      33             : 
      34             : #include <boost/regex.hpp>
      35             : #include <boost/lexical_cast.hpp>
      36             : #include <boost/filesystem/path.hpp>
      37             : #include <boost/filesystem/convenience.hpp>
      38             : #include <boost/filesystem/operations.hpp>
      39             : 
      40             : #include <config/config.h>
      41             : #include <config/cmake_config.h>
      42             : #include <debug/common.h>
      43             : #include <log/log.h>
      44             : 
      45             : #include <version.h>
      46             : 
      47             : using namespace std;
      48             : using namespace boost;
      49             : using namespace boost::filesystem;
      50             : using namespace log4cxx;
      51             : 
      52             : using namespace coherent::log;
      53             : using namespace coherent::debug;
      54             : 
      55             : namespace coherent {
      56             : namespace config {
      57             : 
      58             : string get_version_string()
      59           0 : {
      60             :         string res = "CoherentDB";
      61           0 : #ifdef OFFICIAL_RELEASE
      62             :         res += " " OFFICIAL_RELEASE;
      63             : #else
      64             :         res += " unofficial release";
      65           0 : #ifdef GIT_HASH
      66             : #ifdef GIT_BRANCH
      67             :         res += " branch \"" GIT_BRANCH "\"";
      68           0 : #endif /*GIT_BANCH*/
      69             :         res += " commit hash: " GIT_HASH;
      70           0 : #else
      71             :         res += " (unknown hash)";
      72             : #endif /* GIT_HASH */
      73             : #endif /* OFFICIAL_RELEASE */
      74             :         return res;
      75             : }
      76             : 
      77             : string get_build_information()
      78           0 : {
      79             :         stringstream ss;
      80           0 :         ss << "Built for " BUILD_OS "(" BUILD_ARCH ") on " BUILD_TIME << endl << 
      81           0 :                 "Full build sytem information:" << endl << FULL_UNAME << endl <<
      82           0 :                 "Compiled with CXXFLAGS:" << COMPILED_CXX_FLAGS << endl << "CFLAGS:" << COMPILED_C_FLAGS;
      83           0 :         return ss.str();
      84           0 : }
      85             : 
      86             : void print_running_information()
      87           0 : {
      88             :         cout << "Running on:" << endl;
      89           0 :         int ret = ::system("uname -a");
      90           0 :         if (ret == -1)
      91           0 :                 cout << "(unable to obtain)" << endl;
      92           0 : }
      93           0 : 
      94             : class ini_config
      95             : {
      96           7 : public:
      97             :         typedef std::string section;
      98             :         typedef std::vector<section> sections;
      99             :         typedef std::vector<std::string> attribute_names;
     100             : 
     101             :         ini_config(std::string const & file);
     102             :         sections get_sections() const;
     103             :         attribute_names get_attribute_names(section const & section) const;
     104             :         std::string get_value(section const & sect, std::string const & attr) const;
     105             : 
     106             : private:
     107             :         typedef std::pair<section, std::string> id; //section, attr
     108             :         typedef std::map<id, std::string> value_map;
     109             :         typedef value_map::iterator value_map_it;
     110             : 
     111             :         value_map values;
     112             : };
     113             : 
     114             : //================ ini_config implementation ===================================
     115             : 
     116             : struct is_not_blank
     117             : {
     118             :         bool operator()(char c) const
     119             :         {
     120          70 :                 return !isblank(c);
     121             :         }
     122          70 : };
     123             : 
     124             : static bool line_is_white(string const & s)
     125             : {
     126         238 :         return find_if(s.begin(), s.end(), is_not_blank()) == s.end();
     127             : }
     128         238 : 
     129             : static string strip_comments(string const & s)
     130             : {
     131         203 :         size_t hash = s.find(';');
     132             :         string res;
     133         203 :         if (hash == string::npos)
     134         406 :                 res = s;
     135         203 :         else
     136          63 :                 res = s.substr(0, hash);
     137             :         if (line_is_white(res))
     138         140 :                 return "";
     139         203 :         else
     140         168 :                 return res;
     141             : }
     142          35 : 
     143             : static string get_attr(string const & attr_line)
     144             : {
     145          21 :         string tmp = attr_line;
     146             :         size_t pos = tmp.find('=');
     147             :         d_assert(pos != string::npos, "line \"" << tmp << "\" does not contain '=' sign!");
     148          21 :         tmp.resize(pos);
     149          21 :         stringstream ss(tmp);
     150          21 :         string res;
     151          42 :         ss >> res;
     152          21 :         return res;
     153          21 : }
     154             : 
     155             : static string get_val(string const & attr_line)
     156             : {
     157          21 :         size_t const pos = attr_line.find('=');
     158             :         d_assert(pos != string::npos, "line \"" << attr_line << "\" does not contain '=' sign!");
     159             :         d_assert(pos + 1 < attr_line.length(), "line \"" << attr_line <<
     160             :                         "\" does not contain a value");
     161          21 :         string const res = attr_line.substr(pos + 1);
     162             :         d_assert(!line_is_white(res), "line \"" << attr_line <<
     163             :                         "\" does not contain a value");
     164          21 :         return res;
     165          21 : }
     166             : 
     167             : static string get_sect_name(string const & s)
     168             : {
     169          14 :         size_t const begin = s.find('[');
     170             :         d_assert(begin != string::npos, "section name \"" << s << "\" does not contain '[' sign");
     171          14 :         size_t const end = s.find(']');
     172             :         d_assert(end != string::npos, "section name \"" << s << "\" does not contain ']' sign");
     173             :         d_assert(begin < end, "']' cannot be before '[' in secton \"" << s << "\"");
     174          14 :         string const & res_with_white = s.substr(begin + 1, end - begin - 1);
     175             :         d_assert(!line_is_white(res_with_white), "no section name in \"" << s << "\"");
     176          28 :         stringstream ss(res_with_white);
     177          14 :         string res;
     178          28 :         ss >> res;
     179             :         d_assert(!res.empty(), "no section name in \"" << s << "\"");
     180          14 :         return res;
     181          14 : }
     182             : 
     183             : ini_config::ini_config(string const & file)
     184             : {
     185             :         LOG(TRACE, "parsing file " << file);
     186             :         regex const reg_attr("[ \\t]*[a-zA-Z][a-zA-Z0-9_]*[ \\t]*=[ \\t]*[^ \\t=\\[\\[][^=\\[\\]]*");
     187           7 :         regex const reg_sect("[ \\t]*\\[[ \\t]*[a-zA-Z][a-zA-Z0-9_]*[ \\t]*\\][ \\t]*");
     188          14 : 
     189          14 :         section cur_sect;
     190             :         int line_no = 0;
     191          14 : 
     192           7 :         ifstream in_f(file.c_str());
     193             :         if (!in_f)
     194          14 :                 throw config_exception(string("failed to open file \"") + file + "\"");
     195           7 :         while (!!in_f) {
     196           0 :                 ++line_no;
     197         210 :                 string line;
     198         203 :                 getline(in_f, line);
     199         406 :                 line = strip_comments(line);
     200         203 :                 if (line == "")
     201         203 :                         continue;
     202         203 :                 if (regex_match(line, reg_attr)) {
     203         168 :                         if (cur_sect.empty())
     204          35 :                                 throw config_exception(line_no,
     205          21 :                                                 "each attribute has to be in a section");
     206             :                         string const & val = get_val(line);
     207           0 :                         string const & attr = get_attr(line);
     208          42 : 
     209          42 :                         if (!this->values.insert(make_pair(id(cur_sect, attr), val)).second)
     210             :                                 throw config_exception(line_no, "duplicate entry");
     211          21 : 
     212           0 :                 } else if (regex_match(line, reg_sect)) {
     213             :                         cur_sect = get_sect_name(line);
     214          14 :                         value_map_it it = this->values.lower_bound(id(cur_sect, ""));
     215          14 :                         if (it != this->values.end() && it->first.first == cur_sect)
     216          14 :                                 throw config_exception(line_no, "section redefinition");
     217             :                         LOG(TRACE, "parsing section " << cur_sect);
     218           0 :                 } else
     219          14 :                         throw config_exception(line_no, "invalid line format");
     220             :         }
     221          35 : 
     222             :         LOG(INFO, "Config file dump follows:");
     223             :         for (value_map_it it = this->values.begin(); it != this->values.end(); ++it)
     224             :                 LOG(INFO, it->first.first << ", " << it->first.second << " = \"" <<
     225             :                                 it->second << "\"");
     226             :         LOG(INFO, "Config file dump finished.");
     227          21 : }
     228           7 : 
     229           7 : string ini_config::get_value(section const & sect, string const & attr) const
     230             : {
     231          21 :         value_map::const_iterator const it = this->values.find(id(sect, attr));
     232             :         d_assert(it != this->values.end(), "referring to non-existent sect " << sect
     233             :                         << " attr " << attr);
     234          21 :         return it->second;
     235           0 : }
     236          21 : 
     237             : ini_config::attribute_names ini_config::get_attribute_names(section const & sect) const
     238             : {
     239          14 :         attribute_names res;
     240             :         for (
     241          14 :                 value_map::const_iterator it = this->values.lower_bound(id(sect, ""));
     242          98 :                 it != this->values.end() && it->first.first == sect;
     243          14 :                 ++it
     244          63 :         )
     245             :                 res.push_back(it->first.second);
     246             :         return res;
     247          21 : }
     248             : 
     249             : //================= config_exception implementation ============================
     250             : 
     251             : config_exception::config_exception(
     252             :         std::string const & descr)
     253           0 : : 
     254             :         line_no(NO_LINE), descr(descr)
     255             : {
     256           0 : }
     257             : 
     258           0 : config_exception::config_exception(
     259             :         int line_no, std::string const & descr)
     260           0 : : 
     261             :         line_no(line_no), descr(descr)
     262             : {
     263           0 : }
     264             : 
     265           0 : string config_exception::to_string()
     266             : {
     267           0 :         if (line_no == NO_LINE)
     268             :                 return this->descr;
     269           0 :         else
     270           0 :                 return string("line ") + lexical_cast<string>(this->line_no) + ": " +
     271             :                         this->descr;
     272           0 : }
     273           0 : 
     274             : //================= config_section_base implementation =========================
     275             : 
     276             : config_section_base::config_section_base(
     277             :         string const & sect,
     278          14 :         ini_config const & conf
     279             : ) :
     280             :         conf(conf), name(sect)
     281             : {
     282          14 :         ini_config::attribute_names const & names =
     283             :                 this->conf.get_attribute_names(this->name);
     284             :         this->present_names.insert(names.begin(), names.end());
     285          28 : }
     286          14 : 
     287          14 : template<typename T>
     288             : T config_section_base::get_value(std::string const & name, T const & def)
     289             : {
     290           7 :         bool const insert_res = this->valid_names.insert(name).second;
     291             :         d_assert(insert_res, "Referring to attribute " << name << "in section " <<
     292             :                         this->name << " twice");
     293           7 :         set<string>::const_iterator const i = this->present_names.find(name);
     294           0 :         if (i == this->present_names.end())
     295           7 :                 return def;
     296           7 :         else {
     297           7 :                 try {
     298             :                         T const & res = lexical_cast<T>(this->conf.get_value(this->name, name));
     299             :                         return res;
     300           0 :                 } catch (bad_lexical_cast &) {
     301           7 :                         throw config_exception(string("section ") + this->name
     302           0 :                                         + ", attribute " + name + ": invalid value");
     303           0 :                 }
     304             :         }
     305             : }
     306             : 
     307             : template<typename T>
     308             : T config_section_base::get_value(std::string const & name)
     309             : {
     310          21 :         bool const insert_res = this->valid_names.insert(name).second;
     311             :         d_assert(insert_res, "Referring to attribute " << name << "in section " <<
     312             :                         this->name << " twice");
     313          21 :         set<string>::const_iterator const i = this->present_names.find(name);
     314           0 :         if (i == this->present_names.end())
     315          21 :                 throw config_exception(string("section ") + this->name + ", attribute "
     316          21 :                                 + name + ": required attribute not present");
     317           0 :         else {
     318             :                 try {
     319             :                         T const & res = lexical_cast<T>(this->conf.get_value(this->name, name));
     320             :                         return res;
     321          21 :                 } catch (bad_lexical_cast &) {
     322          21 :                         throw config_exception(string("section ") + this->name
     323           0 :                                         + ", attribute " + name + ": invalid value");
     324           0 :                 }
     325             :         }
     326             : }
     327             : 
     328             : void config_section_base::check_no_others()
     329             : {
     330          14 :         for (
     331             :                 set<string>::const_iterator it = this->present_names.begin();
     332          70 :                 it != this->present_names.end();
     333          14 :                 ++it
     334          35 :         ) {
     335             :                 if (this->valid_names.find(*it) == this->valid_names.end())
     336             :                         throw config_exception(string("section ") + this->name
     337          21 :                                         + ", attribute " + *it + ": unknown attribute");
     338           0 :         }
     339           0 : }
     340             : 
     341          14 : //================= global_config implementation ===============================
     342             : 
     343             : global_config::global_config(string const & file_name) throw(config_exception) :
     344             :         conf(new ini_config(file_name)),
     345           0 :         buffer_cache(*this->conf),
     346           7 :         memory_manager(*this->conf)
     347           7 : {
     348          21 : 
     349             : }
     350             : 
     351           7 : //================= buffer_cache_sect implementation ===========================
     352             : 
     353             : global_config::buffer_cache_sect::buffer_cache_sect(ini_config const & conf) : 
     354             :         config_section_base("buffer_cache", conf),
     355           7 :         size(this->get_value<uint64_t>("size")),
     356             :         syncer_sleep_time(this->get_value<uint16_t>("syncer_sleep_time", 30))
     357           7 : {
     358          14 :         this->check_no_others();
     359             : }
     360           7 : 
     361           7 : //================= memory_manager_sect implementation =======================
     362             : 
     363             : global_config::memory_manager_sect::memory_manager_sect(ini_config const& conf) :
     364             :         config_section_base("memory_manager", conf),
     365           7 :         initialLimitBytes(this->get_value<uint64_t>("initialLimitBytes")),
     366             :         defaultSessionLimitBytes(this->get_value<uint32_t>("defaultSessionLimitBytes"))
     367           7 : {
     368          14 :         this->check_no_others();
     369             : }
     370           7 : 
     371           7 : global_config::~global_config()
     372             : {
     373           7 : }
     374             : 
     375           7 : //================= scoped_test_enabler implementation =========================
     376             : 
     377             : scoped_test_enabler::scoped_test_enabler(int argc,
     378             :                 char const * const * const argv, LevelPtr def_log_level)
     379           7 : {
     380           7 : #define cerr_assert(cond, msg) do { if (!(cond)) { cerr << msg << endl; ::abort(); } ; } while (0)
     381             :         string const required_path = "/bin/test/"; //just for safety
     382             :         string const work_dir = "run";
     383          14 :         string const tests_dir = FOR_TESTS_BIN_DIR  "/" + work_dir;
     384          14 :         
     385          14 :         char full_path_raw[MAXPATHLEN];
     386             :         cerr_assert(realpath(argv[0], full_path_raw), "real_path failed with errno=" << errno);
     387             :         string const full_path = full_path_raw;
     388           7 : 
     389          14 :         cerr_assert(full_path.find(required_path) != string::npos,
     390             :                         "This is bad, path=" << tests_dir);
     391           7 :         size_t pos = full_path.rfind('/');
     392             :         if (pos == string::npos)
     393           7 :                 pos = 0;
     394           7 :         string const test_name = full_path.substr(pos);
     395           0 :         string const target_dir_name = tests_dir + "/" + test_name;
     396          14 :         this->working_dir = target_dir_name;
     397          14 : 
     398           7 :         if (argc != 2 || strcmp(argv[1], "ready")) {
     399             :                 //XXX unix specific
     400           7 :                 if (exists(target_dir_name)) {
     401             :                         cerr_assert(is_directory(target_dir_name), "I wanted to create a working "
     402           0 :                                         " directory for test but it already exists and is not a directory: "
     403           0 :                                         << target_dir_name);
     404             :                         remove_all(target_dir_name);
     405             :                 }
     406           0 :                 create_directories(target_dir_name);
     407             :                 int err = chdir(target_dir_name.c_str());
     408           0 :                 cerr_assert(!err, "chdir to " << target_dir_name << " failed with code " << errno);
     409           0 :                 
     410           0 :                 int out_fd = open("stdout", O_WRONLY | O_CREAT | O_EXCL, 0644);
     411             :                 cerr_assert(err != -1, "creating stdout file failed with errno=" << errno);
     412           0 :                 int err_fd = open("stderr", O_WRONLY | O_CREAT | O_EXCL, 0644);
     413           0 :                 cerr_assert(err != -1, "creating stderr file failed with errno=" << errno);
     414           0 :                 err = dup2(out_fd, 1);
     415           0 :                 cerr_assert(err != -1, "dup2 of stdout file failed with errno=" << errno);
     416           0 :                 err = dup2(err_fd, 2);
     417           0 :                 cerr_assert(err != -1, "dup2 of stderr file failed with errno=" << errno);
     418           0 :                 err = close(out_fd);
     419           0 :                 cerr_assert(err != -1, "close of stdout file failed with errno=" << errno);
     420           0 :                 err = close(err_fd);
     421           0 :                 cerr_assert(err != -1, "close of stderr file failed with errno=" << errno);
     422           0 : 
     423           0 :                 vector<string> args;
     424             :                 if (argc > 1) {
     425           0 :                         for (int i = 1; i < argc; ++i)
     426           0 :                                 args.push_back(argv[i]);
     427           0 :                 }
     428           0 :                 args.push_back(full_path);
     429             :                 args.push_back("ready");
     430           0 :                 char const * new_argv[args.size()+1];
     431           0 :                 for (size_t i = 0; i < args.size(); ++i)
     432           0 :                         new_argv[i] = args[i].c_str();
     433           0 :                 new_argv[args.size()] = 0;
     434           0 :                 char const * const to_run = (argc > 1) ? argv[1] : full_path.c_str();
     435           0 :                 execvp(to_run, const_cast<char * const *>(new_argv));
     436           0 :                 cerr_assert(false, "execvp failed with errno=" << errno);
     437           0 :         }
     438           0 :         setup_logger_test(target_dir_name + "/logs", def_log_level);
     439             :         LOG(INFO, "Test " << test_name << " starts");
     440           7 : 
     441           7 :         set_terminate_handler();
     442             : 
     443           7 :         this->config = shared_ptr<global_config>(
     444             :                         new global_config(FOR_TESTS_SRC_DIR "/doc/default.conf"));
     445             : 
     446           7 : }
     447             : 
     448           7 : scoped_test_enabler::~scoped_test_enabler()
     449             : {
     450             :         LOG(INFO, "Test finished successfully.");
     451             : }
     452           7 : 
     453           7 : string scoped_test_enabler::get_working_dir()
     454             : {
     455           0 :         return this->working_dir;
     456             : }
     457           0 : 
     458             : shared_ptr<global_config> scoped_test_enabler::get_config()
     459             : {
     460           1 :         return this->config;
     461             : }
     462           1 : 
     463             : } // namespace config
     464             : } // namespace coherent

Generated by: LCOV version 1.9