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' 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
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 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
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
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
00278
00279
00280 po::validators::check_first_occurrence(v);
00281
00282
00283 const std::string& memSize = po::validators::get_single_string(values);
00284
00285
00286
00287
00288
00289
00290
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
00306 case 'M':
00307 multiplier *= 1024;
00308
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
00323 }
00324 else
00325 {
00326 throw po::invalid_option_value("Could not parse rtalloc-mem-size option: " + memSize);
00327 }
00328
00329
00330
00331
00332
00333
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
00377
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
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
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
00408 }
00409 return true;
00410 }
00411
00412 #endif // OROSEM_LOG4CPP_LOGGING
00413
00414
00415 }