platform/server/PlatformConfig.cpp

00001 // ------------------------------------------------------------------------
00002 // Pion is a development platform for building Reactors that process Events
00003 // ------------------------------------------------------------------------
00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
00005 //
00006 // Pion is free software: you can redistribute it and/or modify it under the
00007 // terms of the GNU Affero General Public License as published by the Free
00008 // Software Foundation, either version 3 of the License, or (at your option)
00009 // any later version.
00010 //
00011 // Pion is distributed in the hope that it will be useful, but WITHOUT ANY
00012 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00013 // FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for
00014 // more details.
00015 //
00016 // You should have received a copy of the GNU Affero General Public License
00017 // along with Pion.  If not, see <http://www.gnu.org/licenses/>.
00018 //
00019 
00020 #include <boost/filesystem/operations.hpp>
00021 #include <pion/PionPlugin.hpp>
00022 #include <pion/PionLogger.hpp>
00023 #include "PlatformConfig.hpp"
00024 #ifndef _MSC_VER
00025     #include <fstream>
00026     #include <unistd.h>
00027     #include <sys/types.h>
00028     #include <boost/regex.hpp>
00029     #include <boost/tokenizer.hpp>
00030     #include <boost/lexical_cast.hpp>
00031 #endif
00032 
00033 using namespace pion::net;
00034 using namespace pion::platform;
00035 
00036 
00037 namespace pion {        // begin namespace pion
00038 namespace server {      // begin namespace server (Pion Server)
00039 
00040     
00041 // static members of PlatformConfig
00042     
00043 const std::string           PlatformConfig::DEFAULT_CONFIG_FILE = "platform.xml";
00044 const std::string           PlatformConfig::VERSION_ELEMENT_NAME = "Version";
00045 const std::string           PlatformConfig::PLATFORM_CONFIG_ELEMENT_NAME = "PlatformConfig";
00046 const std::string           PlatformConfig::VOCABULARY_CONFIG_ELEMENT_NAME = "VocabularyConfig";
00047 const std::string           PlatformConfig::CODEC_CONFIG_ELEMENT_NAME = "CodecConfig";
00048 const std::string           PlatformConfig::PROTOCOL_CONFIG_ELEMENT_NAME = "ProtocolConfig";
00049 const std::string           PlatformConfig::DATABASE_CONFIG_ELEMENT_NAME = "DatabaseConfig";
00050 const std::string           PlatformConfig::REACTOR_CONFIG_ELEMENT_NAME = "ReactorConfig";
00051 const std::string           PlatformConfig::SERVICE_CONFIG_ELEMENT_NAME = "ServiceConfig";
00052 const std::string           PlatformConfig::USER_CONFIG_ELEMENT_NAME = "UserConfig";
00053 const std::string           PlatformConfig::LOG_CONFIG_ELEMENT_NAME = "LogConfig";
00054 const std::string           PlatformConfig::VOCABULARY_PATH_ELEMENT_NAME = "VocabularyPath";
00055 const std::string           PlatformConfig::PLUGIN_PATH_ELEMENT_NAME = "PluginPath";
00056 const std::string           PlatformConfig::DATA_DIRECTORY_ELEMENT_NAME = "DataDirectory";
00057 const std::string           PlatformConfig::DEBUG_MODE_ELEMENT_NAME = "DebugMode";
00058 const std::string           PlatformConfig::REACTION_ENGINE_ELEMENT_NAME = "ReactionEngine";
00059 const std::string           PlatformConfig::MAX_THREADS_ELEMENT_NAME = "MaxThreads";
00060 const std::string           PlatformConfig::MULTITHREAD_BRANCHES_ELEMENT_NAME = "MultithreadBranches";
00061 const std::string           PlatformConfig::USER_ELEMENT_NAME = "User";
00062 const std::string           PlatformConfig::GROUP_ELEMENT_NAME = "Group";
00063     
00064         
00065 // PlatformConfig member functions
00066 
00067 PlatformConfig::PlatformConfig(void)
00068     : ConfigManager(DEFAULT_CONFIG_FILE),
00069     m_vocab_mgr(), m_codec_factory(m_vocab_mgr), 
00070     m_protocol_factory(m_vocab_mgr), m_database_mgr(m_vocab_mgr),
00071     m_reaction_engine(m_vocab_mgr, m_codec_factory, m_protocol_factory, m_database_mgr),
00072     m_service_mgr(m_vocab_mgr, *this), m_user_mgr_ptr(new UserManager),
00073     m_user_id(-1), m_group_id(-1), m_debug_mode(false)
00074 {
00075     setLogger(PION_GET_LOGGER("pion.server.PlatformConfig"));
00076 }
00077 
00078 boost::int32_t PlatformConfig::findSystemId(const std::string& name,
00079     const std::string& file)
00080 {
00081 #ifdef _MSC_VER
00082     return -1;
00083 #else
00084 
00085     // check if name is the system id
00086     const boost::regex just_numbers("\\d+");
00087     if (boost::regex_match(name, just_numbers)) {
00088         return boost::lexical_cast<boost::int32_t>(name);
00089     }
00090 
00091     // open system file
00092     std::ifstream system_file(file.c_str());
00093     if (! system_file.is_open()) {
00094         PION_LOG_ERROR(m_logger, "Unable to open " << file << " file");
00095         return -1;
00096     }
00097 
00098     // find id in system file
00099     typedef boost::tokenizer<boost::char_separator<char> > Tok;
00100     boost::char_separator<char> sep(":");
00101     std::string line;
00102     boost::int32_t system_id = -1;
00103 
00104     while (std::getline(system_file, line, '\n')) {
00105         Tok tokens(line, sep);
00106         Tok::const_iterator token_it = tokens.begin();
00107         if (token_it != tokens.end() && *token_it == name) {
00108             // found line matching name
00109             if (++token_it != tokens.end() && ++token_it != tokens.end()
00110                 && boost::regex_match(*token_it, just_numbers))
00111             {
00112                 // found id as third parameter
00113                 system_id = boost::lexical_cast<boost::int32_t>(*token_it);
00114             } else {
00115                 PION_LOG_ERROR(m_logger, "Unexpected formatting in "
00116                     << file << " file");
00117             }
00118             break;
00119         }
00120     }
00121 
00122     if (system_id == -1) {
00123         PION_LOG_ERROR(m_logger, "Unable to find " << name
00124             << " in " << file << " file");
00125     }
00126 
00127     return system_id;
00128 #endif
00129 }
00130 
00131 void PlatformConfig::parseUser(void)
00132 {
00133 #ifdef _MSC_VER
00134     PION_LOG_ERROR(m_logger, "Windows not supported for user masquerading: " << m_user_name);
00135 #else
00136     m_user_id = findSystemId(m_user_name, "/etc/passwd");
00137     if (m_user_id != -1) {
00138         if ( seteuid(m_user_id) != 0 ) {
00139             PION_LOG_ERROR(m_logger, "Unable to run as user "
00140                 << m_user_name << " (" << m_user_id << ")");
00141         } else {
00142             PION_LOG_INFO(m_logger, "Running as user "
00143                 << m_user_name << " (" << m_user_id << ")");
00144         }
00145     } else {
00146         m_user_id = geteuid();
00147     }
00148 #endif
00149 }
00150 
00151 void PlatformConfig::parseGroup(void)
00152 {
00153 #ifdef _MSC_VER
00154     PION_LOG_ERROR(m_logger, "Windows not supported for group masquerading: " << m_group_name);
00155 #else
00156     m_group_id = findSystemId(m_group_name, "/etc/group");
00157     if (m_group_id != -1) {
00158         if ( setegid(m_group_id) != 0 ) {
00159             PION_LOG_ERROR(m_logger, "Unable to run as group "
00160                 << m_group_name << " (" << m_group_id << ")");
00161         } else {
00162             PION_LOG_INFO(m_logger, "Running as group "
00163                 << m_group_name << " (" << m_group_id << ")");
00164         }
00165     } else {
00166         m_group_id = getegid();
00167     }
00168 #endif
00169 }
00170 
00171 void PlatformConfig::openConfigFile(void)
00172 {
00173     boost::mutex::scoped_lock platform_lock(m_mutex);
00174 
00175     // just return if it's already open
00176     if (configIsOpen())
00177         return;
00178     
00179     // open the file and find the "config" root element
00180     ConfigManager::openConfigFile();
00181 
00182     #if defined(PION_USE_LOG4CXX) || defined(PION_USE_LOG4CPLUS) || defined(PION_USE_LOG4CPP)
00183     // configure logging using LogConfig file (if defined)
00184     if (ConfigManager::getConfigOption(LOG_CONFIG_ELEMENT_NAME, m_log_config_file,
00185                                        m_config_node_ptr->children))
00186     {
00187         // initialize logging config using the file specified
00188         m_log_config_file = ConfigManager::resolveRelativePath(m_log_config_file);
00189         PION_LOG_CONFIG(m_log_config_file);
00190     } else {
00191         m_log_config_file.erase();
00192     }
00193     #endif
00194 
00195     #if defined(PION_USE_LOG4CPLUS)
00196     // Start caching log events, so that even log events occurring during configuration will be available.
00197     PionLogAppenderPtr appender(new CircularBufferAppender);
00198     appender->setName("CircularBufferAppender");
00199     log4cplus::Logger::getRoot().addAppender(appender);
00200     #endif
00201 
00202     // get group to run Pion as
00203     // MUST BE PERFORMED BEFORE CHANGING USER
00204     // (since changing user may downgrade credentials)
00205     if (ConfigManager::getConfigOption(GROUP_ELEMENT_NAME, m_group_name,
00206         m_config_node_ptr->children))
00207     {
00208         parseGroup();
00209     }
00210     
00211     // get user to run Pion as
00212     if (ConfigManager::getConfigOption(USER_ELEMENT_NAME, m_user_name,
00213         m_config_node_ptr->children))
00214     {
00215         parseUser();
00216     }
00217 
00218     // Step through plugin path definitions
00219     m_plugin_paths.clear();
00220     std::string plugin_path;
00221     xmlNodePtr path_node = m_config_node_ptr->children;
00222     if (ConfigManager::findConfigNodeByName(PLUGIN_PATH_ELEMENT_NAME, path_node) != NULL) 
00223         PionPlugin::resetPluginDirectories();
00224     while ( (path_node = ConfigManager::findConfigNodeByName(PLUGIN_PATH_ELEMENT_NAME, path_node)) != NULL)
00225     {
00226         // get the plug-in path value
00227         xmlChar *xml_char_ptr = xmlNodeGetContent(path_node);
00228         if (xml_char_ptr == NULL || xml_char_ptr[0]=='\0') {
00229             if (xml_char_ptr != NULL)
00230                 xmlFree(xml_char_ptr);
00231             throw EmptyPluginPathException(getConfigFile());
00232         }
00233         plugin_path = reinterpret_cast<char*>(xml_char_ptr);
00234         xmlFree(xml_char_ptr);
00235         
00236         // add the plug-in path (only warn if directory not found)
00237         try {
00238             plugin_path = ConfigManager::resolveRelativePath(plugin_path);
00239             PionPlugin::addPluginDirectory(plugin_path);
00240             m_plugin_paths.push_back(plugin_path);
00241         } catch (PionPlugin::DirectoryNotFoundException& e) {
00242             PION_LOG_WARN(m_logger, e.what());
00243         }
00244         
00245         // step to the next plug-in path
00246         path_node = path_node->next;
00247     }
00248     
00249     // get the VocabularyManager config file
00250     std::string config_file;
00251     if (! ConfigManager::getConfigOption(VOCABULARY_CONFIG_ELEMENT_NAME, config_file,
00252                                          m_config_node_ptr->children))
00253         throw MissingVocabularyConfigException(getConfigFile());
00254     
00255     // open the VocabularyManager configuration
00256     m_vocab_mgr.setConfigFile(ConfigManager::resolveRelativePath(config_file));
00257     m_vocab_mgr.openConfigFile();
00258     
00259     // get the DataDirectory config parameter
00260     if (! ConfigManager::getConfigOption(DATA_DIRECTORY_ELEMENT_NAME, m_data_directory,
00261                                          m_config_node_ptr->children))
00262         throw MissingDataDirectoryException(getConfigFile());
00263     m_data_directory = ConfigManager::resolveRelativePath(m_data_directory);
00264 
00265     // get the DebugMode config parameter
00266     std::string debug_str;
00267     if (ConfigManager::getConfigOption(DEBUG_MODE_ELEMENT_NAME, debug_str,
00268         m_config_node_ptr->children))
00269     {
00270         m_debug_mode = (debug_str == "true");
00271         if (m_debug_mode) {
00272             PION_LOG_WARN(m_logger, "Debugging mode enabled - do not use in production!");
00273         }
00274     } else {
00275         m_debug_mode = false;
00276     }
00277 
00278     // get the CodecFactory config file
00279     if (! ConfigManager::getConfigOption(CODEC_CONFIG_ELEMENT_NAME, config_file,
00280                                          m_config_node_ptr->children))
00281         throw MissingCodecConfigException(getConfigFile());
00282     
00283     // open the CodecFactory configuration
00284     m_codec_factory.setConfigFile(ConfigManager::resolveRelativePath(config_file));
00285     m_codec_factory.setDataDirectory(m_data_directory);
00286     m_codec_factory.setDebugMode(m_debug_mode);
00287     m_codec_factory.openConfigFile();
00288     
00289     // get the ProtocolFactory config file
00290     if (! ConfigManager::getConfigOption(PROTOCOL_CONFIG_ELEMENT_NAME, config_file,
00291                                          m_config_node_ptr->children))
00292         throw MissingProtocolConfigException(getConfigFile());
00293     
00294     // open the ProtocolFactory configuration
00295     m_protocol_factory.setConfigFile(ConfigManager::resolveRelativePath(config_file));
00296     m_protocol_factory.setDataDirectory(m_data_directory);
00297     m_protocol_factory.setDebugMode(m_debug_mode);
00298     m_protocol_factory.openConfigFile();
00299 
00300     // make sure that the directory exists
00301     if (! boost::filesystem::exists(m_data_directory) )
00302         throw DirectoryNotFoundException(m_data_directory);
00303     if (! boost::filesystem::is_directory(m_data_directory) )
00304         throw NotADirectoryException(m_data_directory);
00305 
00306     // get the DatabaseManager config file
00307     if (! ConfigManager::getConfigOption(DATABASE_CONFIG_ELEMENT_NAME, config_file,
00308                                          m_config_node_ptr->children))
00309         throw MissingDatabaseConfigException(getConfigFile());
00310     
00311     // open the DatabaseManager configuration
00312     m_database_mgr.setConfigFile(ConfigManager::resolveRelativePath(config_file));
00313     m_database_mgr.setDataDirectory(m_data_directory);
00314     m_database_mgr.setDebugMode(m_debug_mode);
00315     m_database_mgr.openConfigFile();
00316     
00317     // get the ReactionEngine config file
00318     if (! ConfigManager::getConfigOption(REACTOR_CONFIG_ELEMENT_NAME, config_file,
00319                                          m_config_node_ptr->children))
00320         throw MissingReactorConfigException(getConfigFile());
00321     
00322     // get the ReactionEngine (advanced/hidden) configuration parameters
00323     xmlNodePtr reaction_engine_node =
00324         ConfigManager::findConfigNodeByName(REACTION_ENGINE_ELEMENT_NAME, m_config_node_ptr->children);
00325     if (reaction_engine_node != NULL) {
00326         // get MaxThreads setting (if it is defined)
00327         std::string max_threads_str;
00328         if (ConfigManager::getConfigOption(MAX_THREADS_ELEMENT_NAME, max_threads_str,
00329                                            reaction_engine_node->children))
00330         {
00331             const boost::uint32_t max_threads = boost::lexical_cast<boost::uint32_t>(max_threads_str);
00332             if (max_threads == 0) {
00333                 PION_LOG_ERROR(m_logger, "Platform config has invalid MaxThreads setting for ReactionEngine (using default)");
00334             } else {
00335                 PION_LOG_INFO(m_logger, "Setting ReactionEngine MaxThreads to " << max_threads);
00336                 m_reaction_engine.setNumThreads(max_threads);
00337             }
00338         }
00339 
00340         // get MultithreadBranches setting (if it is defined)
00341         std::string mt_branches_str;
00342         if (ConfigManager::getConfigOption(MULTITHREAD_BRANCHES_ELEMENT_NAME,
00343                                            mt_branches_str,
00344                                            reaction_engine_node->children))
00345         {
00346             if (mt_branches_str == "true") {
00347                 PION_LOG_INFO(m_logger, "Enabling multithreaded branches in ReactionEngine");
00348                 m_reaction_engine.setMultithreadBranches(true);
00349             } else if (mt_branches_str == "false") {
00350                 PION_LOG_INFO(m_logger, "Disabling multithreaded branches in ReactionEngine");
00351                 m_reaction_engine.setMultithreadBranches(false);
00352             } else {
00353                 PION_LOG_ERROR(m_logger, "Platform config has invalid MultithreadBranches setting for ReactionEngine (using default)");
00354             }
00355         }
00356     }
00357     
00358     // open the ReactionEngine configuration
00359     m_reaction_engine.setConfigFile(ConfigManager::resolveRelativePath(config_file));
00360     m_reaction_engine.setDataDirectory(m_data_directory);
00361     m_reaction_engine.setDebugMode(m_debug_mode);
00362     m_reaction_engine.openConfigFile();
00363     
00364     // get the ServiceManager config file
00365     if (! ConfigManager::getConfigOption(SERVICE_CONFIG_ELEMENT_NAME, config_file,
00366                                          m_config_node_ptr->children))
00367         throw MissingServiceConfigException(getConfigFile());
00368     
00369     // open the ServiceManager configuration
00370     m_service_mgr.setConfigFile(ConfigManager::resolveRelativePath(config_file));
00371     m_service_mgr.setDataDirectory(m_data_directory);
00372     m_service_mgr.setDebugMode(m_debug_mode);
00373     m_service_mgr.openConfigFile();
00374 
00375     // get the UserManager config file
00376     if (! ConfigManager::getConfigOption(USER_CONFIG_ELEMENT_NAME, config_file,
00377                                          m_config_node_ptr->children))
00378         throw MissingUserConfigException(getConfigFile());
00379 
00380     // open the UserManager configuration
00381     m_user_mgr_ptr->setConfigFile(ConfigManager::resolveRelativePath(config_file));
00382     m_user_mgr_ptr->setDataDirectory(m_data_directory);
00383     m_user_mgr_ptr->setDebugMode(m_debug_mode);
00384     m_user_mgr_ptr->openConfigFile();
00385 
00386     PION_LOG_INFO(m_logger, "Loaded platform configuration file: " << m_config_file);
00387 }
00388 
00389 void PlatformConfig::writeConfigXML(std::ostream& out) const
00390 {
00391     ConfigManager::writeBeginPionConfigXML(out);
00392     
00393     out << "\t<" << VERSION_ELEMENT_NAME << '>' << PION_VERSION
00394         << "</" << VERSION_ELEMENT_NAME << '>' << std::endl
00395         << "\t<" << DEBUG_MODE_ELEMENT_NAME << '>' << (m_debug_mode ? "true" : "false")
00396         << "</" << DEBUG_MODE_ELEMENT_NAME << '>' << std::endl
00397         << "\t<" << PLATFORM_CONFIG_ELEMENT_NAME << '>' << getConfigFile()
00398         << "</" << PLATFORM_CONFIG_ELEMENT_NAME << '>' << std::endl
00399         << "\t<" << VOCABULARY_CONFIG_ELEMENT_NAME << '>' << m_vocab_mgr.getConfigFile()
00400         << "</" << VOCABULARY_CONFIG_ELEMENT_NAME << '>' << std::endl
00401         << "\t<" << CODEC_CONFIG_ELEMENT_NAME << '>' << m_codec_factory.getConfigFile()
00402         << "</" << CODEC_CONFIG_ELEMENT_NAME << '>' << std::endl
00403         << "\t<" << PROTOCOL_CONFIG_ELEMENT_NAME << '>' << m_protocol_factory.getConfigFile()
00404         << "</" << PROTOCOL_CONFIG_ELEMENT_NAME << '>' << std::endl
00405         << "\t<" << DATABASE_CONFIG_ELEMENT_NAME << '>' << m_database_mgr.getConfigFile()
00406         << "</" << DATABASE_CONFIG_ELEMENT_NAME << '>' << std::endl
00407         << "\t<" << REACTOR_CONFIG_ELEMENT_NAME << '>' << m_reaction_engine.getConfigFile()
00408         << "</" << REACTOR_CONFIG_ELEMENT_NAME << '>' << std::endl
00409         << "\t<" << SERVICE_CONFIG_ELEMENT_NAME << '>' << m_service_mgr.getConfigFile()
00410         << "</" << SERVICE_CONFIG_ELEMENT_NAME << '>' << std::endl
00411         << "\t<" << USER_CONFIG_ELEMENT_NAME << '>' << m_user_mgr_ptr->getConfigFile()
00412         << "</" << USER_CONFIG_ELEMENT_NAME << '>' << std::endl
00413         << "\t<" << LOG_CONFIG_ELEMENT_NAME << '>' << getLogConfigFile()
00414         << "</" << LOG_CONFIG_ELEMENT_NAME << '>' << std::endl
00415         << "\t<" << VOCABULARY_PATH_ELEMENT_NAME << '>' << m_vocab_mgr.getVocabularyPath()
00416         << "</" << VOCABULARY_PATH_ELEMENT_NAME << '>' << std::endl
00417         << "\t<" << DATA_DIRECTORY_ELEMENT_NAME << '>' << m_data_directory
00418         << "</" << DATA_DIRECTORY_ELEMENT_NAME << '>' << std::endl;
00419     
00420     boost::mutex::scoped_lock platform_lock(m_mutex);
00421     for (std::vector<std::string>::const_iterator path_it = m_plugin_paths.begin();
00422          path_it != m_plugin_paths.end(); ++path_it)
00423     {
00424         out << "\t<" << PLUGIN_PATH_ELEMENT_NAME << '>' << *path_it
00425             << "</" << PLUGIN_PATH_ELEMENT_NAME << '>' << std::endl;
00426     }
00427     platform_lock.unlock();
00428     
00429     ConfigManager::writeEndPionConfigXML(out);
00430 }
00431 
00432     
00433 }   // end namespace server
00434 }   // end namespace pion

Generated on Wed Apr 13 16:38:34 2011 for pion-platform by  doxygen 1.4.7