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' or 'script.ops')")
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                     scriptFiles.push_back(arg);
00218                 } else {
00219                     name = arg;
00220                 }
00221             }
00222                 }
00223         }
00224         catch (std::logic_error e)
00225     {
00226                 std::cerr << "Exception:" << e.what() << std::endl << options << std::endl;
00227         return -1;
00228     }
00229 
00230     return 0;
00231 }
00232 
00233 int enforceMinNumberCPU(const int minNumberCPU)
00234 {
00235         assert(0 <= minNumberCPU);
00236 
00237         // enforce min CPU constraints
00238         if (0 < minNumberCPU)
00239         {
00240 #if             defined(ORO_SUPPORT_CPU_AFFINITY)
00241 #ifdef  __linux__
00242                 int numCPU = sysconf( _SC_NPROCESSORS_ONLN );
00243 #else
00244 #error Unsupported configuration!
00245 #endif
00246                 if (-1 == numCPU)
00247                 {
00248                         std::cerr << "ERROR: Unable to determine the number of CPUs for minimum number CPU support."
00249                                           << std::endl;
00250                         return -1;
00251                 }
00252                 else if (numCPU < minNumberCPU)
00253                 {
00254                         std::cerr << "ERROR: Number of CPUS (" << numCPU
00255                                           << ") is less than minimum required (" << minNumberCPU
00256                                           << ")" << std::endl;
00257                         return -2;
00258                 }
00259                 // else ok as numCPU <= minNumberCPU
00260 
00261 #else
00262                 std::cout << "WARNING: Ignoring minimum number of CPU requirement "
00263                                   << "as RTT does not support CPU affinity on this platform."
00264                                   << std::endl;
00265 #endif
00266         }
00267 
00268         return 0;
00269 }
00270 
00271 #ifdef  ORO_BUILD_RTALLOC
00272 
00273 void validate(boost::any& v,
00274               const std::vector<std::string>& values,
00275               memorySize* target_type, int)
00276 {
00277 //    using namespace boost::program_options;
00278 
00279     // Make sure no previous assignment to 'a' was made.
00280     po::validators::check_first_occurrence(v);
00281     // Extract the first string from 'values'. If there is more than
00282     // one string, it's an error, and exception will be thrown.
00283     const std::string& memSize = po::validators::get_single_string(values);
00284 
00285     /* parse the string. Support "number" or "numberX" where
00286        X is one of {k,K,m,M,g,G} with the expected meaning of X
00287 
00288        e.g.
00289 
00290        1024, 1k, 3.4M, 4.5G
00291     */
00292     float       value=0;
00293     char        units='\0';
00294     if (2 == sscanf(memSize.c_str(), "%f%c", &value, &units))
00295     {
00296         float       multiplier=1;
00297         if (islower(units))
00298         {
00299             units = toupper(units);
00300         }
00301         switch (units)
00302         {
00303             case 'G':
00304                 multiplier *= 1024;
00305                 // fall through
00306             case 'M':
00307                 multiplier *= 1024;
00308                 // fall through
00309             case 'K':
00310                 multiplier *= 1024;
00311                 break;
00312             default:
00313                 std::stringstream   e;
00314                 e << "Invalid units in rtalloc-mem-size option: " <<  memSize 
00315                   << ". Valid units: 'k','m','g' (case-insensitive).";
00316                 throw po::invalid_option_value(e.str());
00317         }
00318         value *= multiplier;
00319     }
00320     else if (1 == sscanf(memSize.c_str(), "%f", &value))
00321     {
00322         // nothing to do
00323     }
00324     else
00325     {
00326         throw po::invalid_option_value("Could not parse rtalloc-mem-size option: " + memSize);
00327     }
00328 
00329     // provide some basic sanity checking
00330     // Note that TLSF has its own internal checks on the value.
00331     // TLSF's minimum size varies with build options, but it is
00332     // several kilobytes at least (6-8k on Mac OS X Snow Leopard
00333     // with 64-bit build, ~3k on Ubuntu Jaunty with 32-bit build).
00334     if (! (0 <= value) )
00335     {
00336         std::stringstream   e;
00337         e << "Invalid memory size of " << value << " given. Value must be >= 0.";
00338         throw po::invalid_option_value(e.str());
00339     }
00340 
00341     v = memorySize((size_t)value);
00342 }
00343 
00344 
00345 boost::program_options::options_description deployerRtallocOptions(memorySize& rtallocMemorySize)
00346 {
00347     boost::program_options::options_description rtallocOptions("Real-time memory allocation options");
00348     rtallocOptions.add_options()
00349                 ("rtalloc-mem-size",
00350          po::value<memorySize>(&rtallocMemorySize)->default_value(rtallocMemorySize),
00351                  "Amount of memory to provide for real-time memory allocations (e.g. 10000, 1K, 4.3m)\n"
00352          "NB the minimum size depends on TLSF build options, but it is several kilobytes.")
00353         ;
00354     return rtallocOptions;
00355 }
00356 
00357 #endif  //  ORO_BUILD_RTALLOC
00358 
00359 
00360 #if     defined(ORO_BUILD_LOGGING) && defined(OROSEM_LOG4CPP_LOGGING)
00361 
00362 boost::program_options::options_description deployerRttLog4cppOptions(std::string& rttLog4cppConfigFile)
00363 {
00364     po::options_description     rttLog4cppOptions("RTT/Log4cpp options");
00365     rttLog4cppOptions.add_options()
00366                 ("rtt-log4cpp-config-file",
00367          po::value<std::string>(&rttLog4cppConfigFile),
00368                  std::string("Log4cpp configuration file to support RTT category '" + RTT::Logger::log4cppCategoryName + "'\n"+
00369                      "WARNING Configure only this category. Use deployment files to configure realtime logging!").c_str())
00370         ;
00371     return rttLog4cppOptions;
00372 }
00373 
00374 int deployerConfigureRttLog4cppCategory(const std::string& rttLog4cppConfigFile)
00375 {
00376     // configure where RTT::Logger's file logging goes to (which is through
00377     // log4cpp, but not through OCL's log4cpp-derived real-time logging!)
00378     if (!rttLog4cppConfigFile.empty())
00379     {
00380         try
00381         {
00382             log4cpp::PropertyConfigurator::configure(rttLog4cppConfigFile);
00383         }
00384         catch (log4cpp::ConfigureFailure& e)
00385         {
00386             std::cerr << "ERROR: Unable to read/parse log4cpp configuration file\n"
00387                       << e.what() << std::endl;
00388             return false;
00389         }
00390     }
00391     else
00392     {
00393         // setup default of logging to 'orocos.log' file
00394         log4cpp::PatternLayout* layout=0;
00395         log4cpp::Appender*              appender=0;
00396         appender = new log4cpp::FileAppender(RTT::Logger::log4cppCategoryName,
00397                                              "orocos.log");
00398 
00399         layout = new log4cpp::PatternLayout();
00400         // encode as (ISO date) "yyyymmddTHH:MM:SS.ms category message"
00401         layout->setConversionPattern("%d{%Y%m%dT%T.%l} [%p] %m%n");
00402         appender->setLayout(layout);
00403 
00404         log4cpp::Category& category =
00405             log4cpp::Category::getInstance(RTT::Logger::log4cppCategoryName);
00406         category.setAppender(appender);
00407         // appender and layout are now owned by category - do not delete!
00408     }
00409     return true;
00410 }
00411 
00412 #endif  //  OROSEM_LOG4CPP_LOGGING
00413 
00414 // namespace
00415 }


ocl
Author(s): OCL Development Team
autogenerated on Mon Sep 14 2015 14:21:46