00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
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
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");
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
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
00130 if (vm.count("help"))
00131 {
00132 std::cout << options << std::endl;
00133 return 1;
00134 }
00135
00136
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
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
00193 logLevel = boost::algorithm::to_lower_copy(logLevel);
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;
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
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
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
00279
00280
00281 po::validators::check_first_occurrence(v);
00282
00283
00284 const std::string& memSize = po::validators::get_single_string(values);
00285
00286
00287
00288
00289
00290
00291
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
00307 case 'M':
00308 multiplier *= 1024;
00309
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
00324 }
00325 else
00326 {
00327 throw po::invalid_option_value("Could not parse rtalloc-mem-size option: " + memSize);
00328 }
00329
00330
00331
00332
00333
00334
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
00378
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
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
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
00409 }
00410 return true;
00411 }
00412
00413 #endif // OROSEM_LOG4CPP_LOGGING
00414
00415
00416 }