deployer-funcs.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002  Copyright (c) 2008 S Roderick <xxxkiwi DOT xxxnet AT macxxx DOT comxxx>
00003                                (remove the x's above)
00004 
00005  ***************************************************************************
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Lesser General Public            *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2.1 of the License, or (at your option) any later version.    *
00010  *                                                                         *
00011  *   This library is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00014  *   Lesser General Public License for more details.                       *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Lesser General Public      *
00017  *   License along with this library; if not, write to the Free Software   *
00018  *   Foundation, Inc., 59 Temple Place,                                    *
00019  *   Suite 330, Boston, MA  02111-1307  USA                                *
00020  ***************************************************************************/
00021 
00022 #include "deployer-funcs.hpp"
00023 #include <rtt/Logger.hpp>
00024 #include <cstdio>
00025 #include <iostream>
00026 #include <sstream>
00027 #include <string>
00028 #include <functional>
00029 #include <boost/program_options.hpp>
00030 #include <boost/program_options/positional_options.hpp>
00031 #include <boost/assign/list_of.hpp>
00032 #include <boost/algorithm/string/case_conv.hpp>
00033 
00034 #if             defined(ORO_SUPPORT_CPU_AFFINITY)
00035 #include <unistd.h>
00036 #endif
00037 
00038 #if     defined(ORO_BUILD_LOGGING) && defined(OROSEM_LOG4CPP_LOGGING)
00039 // to configure RTT's use of log4cpp
00040 #include <log4cpp/Category.hh>
00041 #include <log4cpp/FileAppender.hh>
00042 #include <log4cpp/PatternLayout.hh>
00043 #include <log4cpp/PropertyConfigurator.hh>
00044 #endif
00045 
00046 namespace po = boost::program_options;
00047 
00048 using namespace RTT;
00049 using namespace std;
00050 
00051 #define ORO_xstr(s) ORO_str(s)
00052 #define ORO_str(s) #s
00053 
00054 namespace OCL
00055 {
00056 
00057 // map lowercase strings to levels
00058 std::map<std::string, RTT::Logger::LogLevel>    logMap =
00059         boost::assign::map_list_of
00060         ("never",       RTT::Logger::Debug)
00061         ("fatal",       RTT::Logger::Fatal)
00062         ("critical",    RTT::Logger::Critical)
00063         ("error",       RTT::Logger::Error)
00064         ("warning",     RTT::Logger::Warning)
00065         ("info",        RTT::Logger::Info)
00066         ("debug",       RTT::Logger::Debug)
00067         ("realtime",    RTT::Logger::RealTime);
00068 
00069 int deployerParseCmdLine(int                        argc,
00070                          char**                     argv,
00071                          std::string&               siteFile,
00072                          std::vector<std::string>&  scriptFiles,
00073                          std::string&               name,
00074                          bool&                      requireNameService,
00075                          bool&                                          deploymentOnlyChecked,
00076                                                  int&                                           minNumberCPU,
00077                          po::variables_map&         vm,
00078                          po::options_description*   otherOptions)
00079 {
00080         std::string                         logLevel("info");   // set to valid default
00081         po::options_description             options;
00082         po::options_description             allowed("Allowed options");
00083         po::positional_options_description  pos;
00084         allowed.add_options()
00085                 ("help,h",
00086                  "Show program usage")
00087                 ("version",
00088                  "Show program version")
00089                 ("daemon,d",
00090                  "Makes this program a daemon such that it runs in the background. Returns 1 in case of success.")
00091                 ("start,s",
00092                  po::value< std::vector<std::string> >(&scriptFiles),
00093          "Deployment XML or script file (eg 'config-file.xml', 'script.ops' or 'script.lua')")
00094                 ("site-file",
00095                  po::value<std::string>(&siteFile),
00096                  "Site deployment XML file (eg 'Deployer-site.cpf' or 'Deployer-site.xml')")
00097                 ("log-level,l",
00098                  po::value<std::string>(&logLevel),
00099                  "Level at which to log from RTT (case-insensitive) Never,Fatal,Critical,Error,Warning,Info,Debug,Realtime")
00100                 ("no-consolelog",
00101                  "Turn off RTT logging to the console (will still log to 'orocos.log')")
00102                 ("check",
00103                  "Only check component loading, connecting peers and ports. Returns 255 in case of errors.")
00104         ("require-name-service",
00105          "Require CORBA name service")
00106                 ("minNumberCPU",
00107                  po::value<int>(&minNumberCPU),
00108                  "The minimum number of CPUs required for deployment (0 <= value) [0==no minimum (default)]")
00109                 ("DeployerName",
00110                  po::value< std::vector<std::string> >(),
00111                  "Name of deployer component (the --DeployerName flag is optional). If you provide a script or XML file name, that will be run instead.")
00112                 ;
00113     pos.add("DeployerName", -1);
00114 
00115         // collate options
00116         options.add(allowed);
00117         if (NULL != otherOptions)
00118         {
00119                 options.add(*otherOptions);
00120         }
00121 
00122         try
00123         {
00124                 po::store(po::command_line_parser(argc, argv).
00125                   options(options).positional(pos).run(),
00126                   vm);
00127                 po::notify(vm);
00128 
00129         // deal with options
00130                 if (vm.count("help"))
00131                 {
00132                         std::cout << options << std::endl;
00133                         return 1;
00134                 }
00135 
00136         // version info
00137                 if (vm.count("version"))
00138                 {
00139             std::cout<< " OROCOS Toolchain version '" ORO_xstr(RTT_VERSION) "'";
00140 #ifdef __GNUC__
00141             std::cout << " ( GCC " ORO_xstr(__GNUC__) "." ORO_xstr(__GNUC_MINOR__) "." ORO_xstr(__GNUC_PATCHLEVEL__) " )";
00142 #endif
00143 #ifdef OROPKG_OS_LXRT
00144             std::cout<<" -- LXRT/RTAI.";
00145 #endif
00146 #ifdef OROPKG_OS_GNULINUX
00147             std::cout<<" -- GNU/Linux.";
00148 #endif
00149 #ifdef OROPKG_OS_XENOMAI
00150             std::cout<<" -- Xenomai.";
00151 #endif
00152                         std::cout << endl;
00153                         return 1;
00154                 }
00155 
00156                 if (vm.count("daemon"))
00157                 {
00158                         if (vm.count("check"))
00159                                 log(Warning) << "--check and --daemon are incompatible. Skipping the --daemon flag." <<endlog();
00160                         else
00161                                 if (fork() != 0 )
00162                                         return 1;
00163                 }
00164 
00165                 if ( !(0 <= minNumberCPU) )
00166                 {
00167                         std::cout << std::endl
00168             << "ERROR: Invalid minimum number CPU. Require 0 <= value"
00169             << std::endl << options << std::endl;
00170                         return -2;
00171                 }
00172 
00173                 // turn off all console logging
00174                 if (vm.count("no-consolelog"))
00175                 {
00176                         RTT::Logger::Instance()->mayLogStdOut(false);
00177                         log(Info) << "Console logging disabled" << endlog();
00178                 }
00179 
00180                 if (vm.count("check"))
00181                 {
00182                         deploymentOnlyChecked = true;
00183                         log(Info) << "Deployment check: Only check component loading, connecting peers and ports. Returns 255 in case of errors." << endlog();
00184                 }
00185 
00186                 if (vm.count("require-name-service"))
00187                 {
00188                         requireNameService = true;
00189                         log(Info) << "CORBA name service required." << endlog();
00190                 }
00191 
00192                 // verify that is a valid logging level
00193                 logLevel = boost::algorithm::to_lower_copy(logLevel);   // always lower case
00194                 if (vm.count("log-level"))
00195                 {
00196                         if (0 != logMap.count(logLevel))
00197                         {
00198                                 RTT::Logger::Instance()->setLogLevel(logMap[logLevel]);
00199                         }
00200                         else
00201                         {
00202                                 std::cout << "Did not understand log level: "
00203                                                   << logLevel << std::endl
00204                                                   << options << std::endl;
00205                                 return -1;
00206                         }
00207                 }
00208                 if (vm.count("DeployerName")) {
00209             const std::vector<std::string> &positional_arguments = vm["DeployerName"].as< std::vector<std::string> >();
00210             for(std::vector<std::string>::const_iterator it = positional_arguments.begin(); it != positional_arguments.end(); ++it) {
00211                 if (it->size() >= 1 && it->at(0) == '_') continue; // ignore arguments that start with a _
00212                 std::string arg = *it;
00213                 if (arg.rfind(".xml") != std::string::npos ||
00214                     arg.rfind(".cpf") != std::string::npos ||
00215                     arg.rfind(".osd") != std::string::npos ||
00216                     arg.rfind(".ops") != std::string::npos ||
00217                     arg.rfind(".lua") != std::string::npos ) {
00218                     scriptFiles.push_back(arg);
00219                 } else {
00220                     name = arg;
00221                 }
00222             }
00223                 }
00224         }
00225         catch (std::logic_error e)
00226     {
00227                 std::cerr << "Exception:" << e.what() << std::endl << options << std::endl;
00228         return -1;
00229     }
00230 
00231     return 0;
00232 }
00233 
00234 int enforceMinNumberCPU(const int minNumberCPU)
00235 {
00236         assert(0 <= minNumberCPU);
00237 
00238         // enforce min CPU constraints
00239         if (0 < minNumberCPU)
00240         {
00241 #if             defined(ORO_SUPPORT_CPU_AFFINITY)
00242 #ifdef  __linux__
00243                 int numCPU = sysconf( _SC_NPROCESSORS_ONLN );
00244 #else
00245 #error Unsupported configuration!
00246 #endif
00247                 if (-1 == numCPU)
00248                 {
00249                         std::cerr << "ERROR: Unable to determine the number of CPUs for minimum number CPU support."
00250                                           << std::endl;
00251                         return -1;
00252                 }
00253                 else if (numCPU < minNumberCPU)
00254                 {
00255                         std::cerr << "ERROR: Number of CPUS (" << numCPU
00256                                           << ") is less than minimum required (" << minNumberCPU
00257                                           << ")" << std::endl;
00258                         return -2;
00259                 }
00260                 // else ok as numCPU <= minNumberCPU
00261 
00262 #else
00263                 std::cout << "WARNING: Ignoring minimum number of CPU requirement "
00264                                   << "as RTT does not support CPU affinity on this platform."
00265                                   << std::endl;
00266 #endif
00267         }
00268 
00269         return 0;
00270 }
00271 
00272 #ifdef  ORO_BUILD_RTALLOC
00273 
00274 void validate(boost::any& v,
00275               const std::vector<std::string>& values,
00276               memorySize* target_type, int)
00277 {
00278 //    using namespace boost::program_options;
00279 
00280     // Make sure no previous assignment to 'a' was made.
00281     po::validators::check_first_occurrence(v);
00282     // Extract the first string from 'values'. If there is more than
00283     // one string, it's an error, and exception will be thrown.
00284     const std::string& memSize = po::validators::get_single_string(values);
00285 
00286     /* parse the string. Support "number" or "numberX" where
00287        X is one of {k,K,m,M,g,G} with the expected meaning of X
00288 
00289        e.g.
00290 
00291        1024, 1k, 3.4M, 4.5G
00292     */
00293     float       value=0;
00294     char        units='\0';
00295     if (2 == sscanf(memSize.c_str(), "%f%c", &value, &units))
00296     {
00297         float       multiplier=1;
00298         if (islower(units))
00299         {
00300             units = toupper(units);
00301         }
00302         switch (units)
00303         {
00304             case 'G':
00305                 multiplier *= 1024;
00306                 // fall through
00307             case 'M':
00308                 multiplier *= 1024;
00309                 // fall through
00310             case 'K':
00311                 multiplier *= 1024;
00312                 break;
00313             default:
00314                 std::stringstream   e;
00315                 e << "Invalid units in rtalloc-mem-size option: " <<  memSize 
00316                   << ". Valid units: 'k','m','g' (case-insensitive).";
00317                 throw po::invalid_option_value(e.str());
00318         }
00319         value *= multiplier;
00320     }
00321     else if (1 == sscanf(memSize.c_str(), "%f", &value))
00322     {
00323         // nothing to do
00324     }
00325     else
00326     {
00327         throw po::invalid_option_value("Could not parse rtalloc-mem-size option: " + memSize);
00328     }
00329 
00330     // provide some basic sanity checking
00331     // Note that TLSF has its own internal checks on the value.
00332     // TLSF's minimum size varies with build options, but it is
00333     // several kilobytes at least (6-8k on Mac OS X Snow Leopard
00334     // with 64-bit build, ~3k on Ubuntu Jaunty with 32-bit build).
00335     if (! (0 <= value) )
00336     {
00337         std::stringstream   e;
00338         e << "Invalid memory size of " << value << " given. Value must be >= 0.";
00339         throw po::invalid_option_value(e.str());
00340     }
00341 
00342     v = memorySize((size_t)value);
00343 }
00344 
00345 
00346 boost::program_options::options_description deployerRtallocOptions(memorySize& rtallocMemorySize)
00347 {
00348     boost::program_options::options_description rtallocOptions("Real-time memory allocation options");
00349     rtallocOptions.add_options()
00350                 ("rtalloc-mem-size",
00351          po::value<memorySize>(&rtallocMemorySize)->default_value(rtallocMemorySize),
00352                  "Amount of memory to provide for real-time memory allocations (e.g. 10000, 1K, 4.3m)\n"
00353          "NB the minimum size depends on TLSF build options, but it is several kilobytes.")
00354         ;
00355     return rtallocOptions;
00356 }
00357 
00358 #endif  //  ORO_BUILD_RTALLOC
00359 
00360 
00361 #if     defined(ORO_BUILD_LOGGING) && defined(OROSEM_LOG4CPP_LOGGING)
00362 
00363 boost::program_options::options_description deployerRttLog4cppOptions(std::string& rttLog4cppConfigFile)
00364 {
00365     po::options_description     rttLog4cppOptions("RTT/Log4cpp options");
00366     rttLog4cppOptions.add_options()
00367                 ("rtt-log4cpp-config-file",
00368          po::value<std::string>(&rttLog4cppConfigFile),
00369                  std::string("Log4cpp configuration file to support RTT category '" + RTT::Logger::log4cppCategoryName + "'\n"+
00370                      "WARNING Configure only this category. Use deployment files to configure realtime logging!").c_str())
00371         ;
00372     return rttLog4cppOptions;
00373 }
00374 
00375 int deployerConfigureRttLog4cppCategory(const std::string& rttLog4cppConfigFile)
00376 {
00377     // configure where RTT::Logger's file logging goes to (which is through
00378     // log4cpp, but not through OCL's log4cpp-derived real-time logging!)
00379     if (!rttLog4cppConfigFile.empty())
00380     {
00381         try
00382         {
00383             log4cpp::PropertyConfigurator::configure(rttLog4cppConfigFile);
00384         }
00385         catch (log4cpp::ConfigureFailure& e)
00386         {
00387             std::cerr << "ERROR: Unable to read/parse log4cpp configuration file\n"
00388                       << e.what() << std::endl;
00389             return false;
00390         }
00391     }
00392     else
00393     {
00394         // setup default of logging to 'orocos.log' file
00395         log4cpp::PatternLayout* layout=0;
00396         log4cpp::Appender*              appender=0;
00397         appender = new log4cpp::FileAppender(RTT::Logger::log4cppCategoryName,
00398                                              "orocos.log");
00399 
00400         layout = new log4cpp::PatternLayout();
00401         // encode as (ISO date) "yyyymmddTHH:MM:SS.ms category message"
00402         layout->setConversionPattern("%d{%Y%m%dT%T.%l} [%p] %m%n");
00403         appender->setLayout(layout);
00404 
00405         log4cpp::Category& category =
00406             log4cpp::Category::getInstance(RTT::Logger::log4cppCategoryName);
00407         category.setAppender(appender);
00408         // appender and layout are now owned by category - do not delete!
00409     }
00410     return true;
00411 }
00412 
00413 #endif  //  OROSEM_LOG4CPP_LOGGING
00414 
00415 // namespace
00416 }


ocl
Author(s): OCL Development Team
autogenerated on Sun Nov 27 2016 03:45:30