$search
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_BUILD_LOGGING) && defined(OROSEM_LOG4CPP_LOGGING) 00035 // to configure RTT's use of log4cpp 00036 #include <log4cpp/Category.hh> 00037 #include <log4cpp/FileAppender.hh> 00038 #include <log4cpp/PatternLayout.hh> 00039 #include <log4cpp/PropertyConfigurator.hh> 00040 #endif 00041 00042 namespace po = boost::program_options; 00043 00044 using namespace RTT; 00045 using namespace std; 00046 00047 #define ORO_xstr(s) ORO_str(s) 00048 #define ORO_str(s) #s 00049 00050 namespace OCL 00051 { 00052 00053 // map lowercase strings to levels 00054 std::map<std::string, RTT::Logger::LogLevel> logMap = 00055 boost::assign::map_list_of 00056 ("never", RTT::Logger::Debug) 00057 ("fatal", RTT::Logger::Fatal) 00058 ("critical", RTT::Logger::Critical) 00059 ("error", RTT::Logger::Error) 00060 ("warning", RTT::Logger::Warning) 00061 ("info", RTT::Logger::Info) 00062 ("debug", RTT::Logger::Debug) 00063 ("realtime", RTT::Logger::RealTime); 00064 00065 int deployerParseCmdLine(int argc, 00066 char** argv, 00067 std::string& siteFile, 00068 std::vector<std::string>& scriptFiles, 00069 std::string& name, 00070 bool& requireNameService, 00071 po::variables_map& vm, 00072 po::options_description* otherOptions) 00073 { 00074 std::string logLevel("info"); // set to valid default 00075 po::options_description options; 00076 po::options_description allowed("Allowed options"); 00077 po::positional_options_description pos; 00078 allowed.add_options() 00079 ("help,h", 00080 "Show program usage") 00081 ("version", 00082 "Show program version") 00083 ("daemon,d", 00084 "Makes this program a daemon such that it runs in the background.") 00085 ("start,s", 00086 po::value< std::vector<std::string> >(&scriptFiles), 00087 "Deployment XML or script file (eg 'config-file.xml' or 'script.ops')") 00088 ("site-file", 00089 po::value<std::string>(&siteFile), 00090 "Site deployment XML file (eg 'Deployer-site.cpf' or 'Deployer-site.xml')") 00091 ("log-level,l", 00092 po::value<std::string>(&logLevel), 00093 "Level at which to log from RTT (case-insensitive) Never,Fatal,Critical,Error,Warning,Info,Debug,Realtime") 00094 ("no-consolelog", 00095 "Turn off RTT logging to the console (will still log to 'orocos.log')") 00096 ("require-name-service", 00097 "Require CORBA name service") 00098 ("DeployerName", 00099 po::value<std::string>(&name), 00100 "Name of deployer component (the --DeployerName flag is optional). If you provide a script or XML file name, that will be run instead.") 00101 ; 00102 pos.add("DeployerName", 1); 00103 00104 // collate options 00105 options.add(allowed); 00106 if (NULL != otherOptions) 00107 { 00108 options.add(*otherOptions); 00109 } 00110 00111 try 00112 { 00113 po::store(po::command_line_parser(argc, argv). 00114 options(options).positional(pos).run(), 00115 vm); 00116 po::notify(vm); 00117 00118 // deal with options 00119 if (vm.count("help")) 00120 { 00121 std::cout << options << std::endl; 00122 return 1; 00123 } 00124 00125 // version info 00126 if (vm.count("version")) 00127 { 00128 std::cout<< " OROCOS Toolchain version '" ORO_xstr(RTT_VERSION) "'"; 00129 #ifdef __GNUC__ 00130 std::cout << " ( GCC " ORO_xstr(__GNUC__) "." ORO_xstr(__GNUC_MINOR__) "." ORO_xstr(__GNUC_PATCHLEVEL__) " )"; 00131 #endif 00132 #ifdef OROPKG_OS_LXRT 00133 std::cout<<" -- LXRT/RTAI."; 00134 #endif 00135 #ifdef OROPKG_OS_GNULINUX 00136 std::cout<<" -- GNU/Linux."; 00137 #endif 00138 #ifdef OROPKG_OS_XENOMAI 00139 std::cout<<" -- Xenomai."; 00140 #endif 00141 std::cout << endl; 00142 return 1; 00143 } 00144 00145 if (vm.count("daemon")) 00146 { 00147 if (fork() != 0 ) 00148 return 1; 00149 } 00150 00151 // turn off all console logging 00152 if (vm.count("no-consolelog")) 00153 { 00154 RTT::Logger::Instance()->mayLogStdOut(false); 00155 log(Warning) << "Console logging disabled" << endlog(); 00156 } 00157 00158 if (vm.count("require-name-service")) 00159 { 00160 requireNameService = true; 00161 log(Info) << "CORBA name service required." << endlog(); 00162 } 00163 00164 // verify that is a valid logging level 00165 logLevel = boost::algorithm::to_lower_copy(logLevel); // always lower case 00166 if (vm.count("log-level")) 00167 { 00168 if (0 != logMap.count(logLevel)) 00169 { 00170 RTT::Logger::Instance()->setLogLevel(logMap[logLevel]); 00171 } 00172 else 00173 { 00174 std::cout << "Did not understand log level: " 00175 << logLevel << std::endl 00176 << options << std::endl; 00177 return -1; 00178 } 00179 } 00180 if (vm.count("DeployerName")) { 00181 if (name.rfind(".xml") == 4 || 00182 name.rfind(".cpf") == 4 || 00183 name.rfind(".osd") == 4 || 00184 name.rfind(".ops") == 4 ) { 00185 scriptFiles.push_back(name); 00186 name.clear(); 00187 } 00188 } 00189 } 00190 catch (std::logic_error e) 00191 { 00192 std::cerr << "Exception:" << e.what() << std::endl << options << std::endl; 00193 return -1; 00194 } 00195 00196 return 0; 00197 } 00198 00199 #ifdef ORO_BUILD_RTALLOC 00200 00201 void validate(boost::any& v, 00202 const std::vector<std::string>& values, 00203 memorySize* target_type, int) 00204 { 00205 // using namespace boost::program_options; 00206 00207 // Make sure no previous assignment to 'a' was made. 00208 po::validators::check_first_occurrence(v); 00209 // Extract the first string from 'values'. If there is more than 00210 // one string, it's an error, and exception will be thrown. 00211 const std::string& memSize = po::validators::get_single_string(values); 00212 00213 /* parse the string. Support "number" or "numberX" where 00214 X is one of {k,K,m,M,g,G} with the expected meaning of X 00215 00216 e.g. 00217 00218 1024, 1k, 3.4M, 4.5G 00219 */ 00220 float value=0; 00221 char units='\0'; 00222 if (2 == sscanf(memSize.c_str(), "%f%c", &value, &units)) 00223 { 00224 float multiplier=1; 00225 if (islower(units)) 00226 { 00227 units = toupper(units); 00228 } 00229 switch (units) 00230 { 00231 case 'G': 00232 multiplier *= 1024; 00233 // fall through 00234 case 'M': 00235 multiplier *= 1024; 00236 // fall through 00237 case 'K': 00238 multiplier *= 1024; 00239 break; 00240 default: 00241 std::stringstream e; 00242 e << "Invalid units in rtalloc-mem-size option: " << memSize 00243 << ". Valid units: 'k','m','g' (case-insensitive)."; 00244 throw po::invalid_option_value(e.str()); 00245 } 00246 value *= multiplier; 00247 } 00248 else if (1 == sscanf(memSize.c_str(), "%f", &value)) 00249 { 00250 // nothing to do 00251 } 00252 else 00253 { 00254 throw po::invalid_option_value("Could not parse rtalloc-mem-size option: " + memSize); 00255 } 00256 00257 // provide some basic sanity checking 00258 // Note that TLSF has its own internal checks on the value. 00259 // TLSF's minimum size varies with build options, but it is 00260 // several kilobytes at least (6-8k on Mac OS X Snow Leopard 00261 // with 64-bit build, ~3k on Ubuntu Jaunty with 32-bit build). 00262 if (! (0 <= value) ) 00263 { 00264 std::stringstream e; 00265 e << "Invalid memory size of " << value << " given. Value must be >= 0."; 00266 throw po::invalid_option_value(e.str()); 00267 } 00268 00269 v = memorySize((size_t)value); 00270 } 00271 00272 00273 boost::program_options::options_description deployerRtallocOptions(memorySize& rtallocMemorySize) 00274 { 00275 boost::program_options::options_description rtallocOptions("Real-time memory allocation options"); 00276 rtallocOptions.add_options() 00277 ("rtalloc-mem-size", 00278 po::value<memorySize>(&rtallocMemorySize)->default_value(rtallocMemorySize), 00279 "Amount of memory to provide for real-time memory allocations (e.g. 10000, 1K, 4.3m)\n" 00280 "NB the minimum size depends on TLSF build options, but it is several kilobytes.") 00281 ; 00282 return rtallocOptions; 00283 } 00284 00285 #endif // ORO_BUILD_RTALLOC 00286 00287 00288 #if defined(ORO_BUILD_LOGGING) && defined(OROSEM_LOG4CPP_LOGGING) 00289 00290 boost::program_options::options_description deployerRttLog4cppOptions(std::string& rttLog4cppConfigFile) 00291 { 00292 po::options_description rttLog4cppOptions("RTT/Log4cpp options"); 00293 rttLog4cppOptions.add_options() 00294 ("rtt-log4cpp-config-file", 00295 po::value<std::string>(&rttLog4cppConfigFile), 00296 std::string("Log4cpp configuration file to support RTT category '" + RTT::Logger::log4cppCategoryName + "'\n"+ 00297 "WARNING Configure only this category. Use deployment files to configure realtime logging!").c_str()) 00298 ; 00299 return rttLog4cppOptions; 00300 } 00301 00302 int deployerConfigureRttLog4cppCategory(const std::string& rttLog4cppConfigFile) 00303 { 00304 // configure where RTT::Logger's file logging goes to (which is through 00305 // log4cpp, but not through OCL's log4cpp-derived real-time logging!) 00306 if (!rttLog4cppConfigFile.empty()) 00307 { 00308 try 00309 { 00310 log4cpp::PropertyConfigurator::configure(rttLog4cppConfigFile); 00311 } 00312 catch (log4cpp::ConfigureFailure& e) 00313 { 00314 std::cerr << "ERROR: Unable to read/parse log4cpp configuration file\n" 00315 << e.what() << std::endl; 00316 return false; 00317 } 00318 } 00319 else 00320 { 00321 // setup default of logging to 'orocos.log' file 00322 log4cpp::PatternLayout* layout=0; 00323 log4cpp::Appender* appender=0; 00324 appender = new log4cpp::FileAppender(RTT::Logger::log4cppCategoryName, 00325 "orocos.log"); 00326 00327 layout = new log4cpp::PatternLayout(); 00328 // encode as (ISO date) "yyyymmddTHH:MM:SS.ms category message" 00329 layout->setConversionPattern("%d{%Y%m%dT%T.%l} [%p] %m%n"); 00330 appender->setLayout(layout); 00331 00332 log4cpp::Category& category = 00333 log4cpp::Category::getInstance(RTT::Logger::log4cppCategoryName); 00334 category.setAppender(appender); 00335 // appender and layout are now owned by category - do not delete! 00336 } 00337 return true; 00338 } 00339 00340 #endif // OROSEM_LOG4CPP_LOGGING 00341 00342 // namespace 00343 }