OrocosComponentLibrary
2.7.0
|
00001 /*************************************************************************** 00002 00003 command.cpp - Processes client commands 00004 ------------------- 00005 begin : Wed Aug 9 2006 00006 copyright : (C) 2006 Bas Kemper 00007 email : kst@ <my name> .be 00008 00009 *************************************************************************** 00010 * This library is free software; you can redistribute it and/or * 00011 * modify it under the terms of the GNU Lesser General Public * 00012 * License as published by the Free Software Foundation; either * 00013 * version 2.1 of the License, or (at your option) any later version. * 00014 * * 00015 * This library is distributed in the hope that it will be useful, * 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 00018 * Lesser General Public License for more details. * 00019 * * 00020 * You should have received a copy of the GNU Lesser General Public * 00021 * License along with this library; if not, write to the Free Software * 00022 * Foundation, Inc., 59 Temple Place, * 00023 * Suite 330, Boston, MA 02111-1307 USA * 00024 * * 00025 ***************************************************************************/ 00026 00027 #include <algorithm> /* std::transform is needed for upper-case conversion */ 00028 #include <string> 00029 #include <cstdlib> /* strtoull */ 00030 #include <cctype> 00031 #include <cerrno> 00032 #include <rtt/Property.hpp> 00033 #include "NiceHeaderMarshaller.hpp" 00034 #include "TcpReporting.hpp" 00035 #include "command.hpp" 00036 #include "socket.hpp" 00037 #include "datasender.hpp" 00038 #include "socketmarshaller.hpp" 00039 00040 using OCL::TCP::RealCommand; 00041 using OCL::TCP::Socket; 00042 using OCL::TCP::TcpReportingInterpreter; 00043 00044 namespace 00045 { 00049 class prefixbuf : public std::streambuf 00050 { 00051 private: 00052 std::streambuf* _opt; 00053 bool newline; 00054 00055 public: 00056 prefixbuf( std::streambuf* opt ) : _opt(opt) 00057 { 00058 setp(0, 0); 00059 setg(0, 0, 0); 00060 newline = true; 00061 } 00062 00063 ~prefixbuf() 00064 { 00065 sync(); 00066 } 00067 00068 protected: 00069 int overflow(int c) 00070 { 00071 if( c != EOF ) 00072 { 00073 if( newline ) 00074 { 00075 if( _opt->sputn("300 ", 4) != 4 ) 00076 { 00077 return EOF; 00078 } 00079 newline = false; 00080 } 00081 int ret = _opt->sputc(c); 00082 newline = ( c == '\n' ); 00083 return ret; 00084 } 00085 return 0; 00086 } 00087 00088 int sync() 00089 { 00090 return _opt->pubsync(); 00091 } 00092 }; 00093 00097 class prefixstr : public std::ostream 00098 { 00099 public: 00100 prefixstr(std::ostream& opt) 00101 : std::ostream( new prefixbuf( opt.rdbuf() ) ) 00102 { 00103 } 00104 00105 ~prefixstr() 00106 { 00107 delete rdbuf(); 00108 } 00109 }; 00110 00111 /* std::toupper is implemented as a macro and cannot be used by 00112 std::transform without a wrapper function. */ 00113 char to_upper (const char c) 00114 { 00115 return toupper(c); 00116 } 00117 00121 class HeaderCommand : public RealCommand 00122 { 00123 protected: 00124 void maincode( int, std::string* ) 00125 { 00126 std::vector<std::string> list = _parent->getConnection()->getMarshaller()->getReporter()->getReport()->list(); 00127 for(unsigned int i=0;i<list.size();i++) 00128 socket()<<"305 "<<list[i]<<std::endl; 00129 socket() << "306 End of list" << std::endl; 00130 } 00131 00132 public: 00133 HeaderCommand(TcpReportingInterpreter* parent) 00134 : RealCommand( "HEADERS", parent ) 00135 { 00136 } 00137 00138 ~HeaderCommand() 00139 {} 00140 00141 void manualExecute() 00142 { 00143 maincode(0,0); 00144 } 00145 }; 00146 00147 class HelpCommand : public RealCommand 00148 { 00149 protected: 00153 void printCommands() 00154 { 00155 const std::vector<Command*> &cmds = _parent->giveCommands(); 00156 socket() << "Use HELP <command>" << std::endl; 00157 for( unsigned int i = 0; i < cmds.size(); i++ ) 00158 { 00159 if( cmds[i] == cmds[i]->getRealCommand( cmds ) ) 00160 { 00161 socket() << cmds[i]->getName() << '\n'; 00162 } 00163 } 00164 socket() << '.' << std::endl; 00165 } 00166 00170 void printHelpFor( const std::string& name, const RealCommand* command ) 00171 { 00172 socket() << "Name: " << name << std::endl; 00173 socket() << "Usage: " << name; 00174 if( command->getSyntax() ) 00175 { 00176 socket() << " " << command->getSyntax(); 00177 } 00178 socket() << std::endl; 00179 } 00180 00184 void printHelpFor( const std::string& cmd ) 00185 { 00186 const std::vector<Command*> &cmds = _parent->giveCommands(); 00187 for( unsigned int i = 0; i < cmds.size(); i++ ) 00188 { 00189 if( cmds[i]->getName() == cmd ) 00190 { 00191 printHelpFor( cmd, cmds[i]->getRealCommand( cmds ) ); 00192 return; 00193 } 00194 } 00195 printCommands(); 00196 } 00197 00198 void maincode( int argc, std::string* params ) 00199 { 00200 if( argc == 0 ) 00201 { 00202 printCommands(); 00203 } else { 00204 printHelpFor(params[0]); 00205 } 00206 } 00207 public: 00208 HelpCommand(TcpReportingInterpreter* parent) 00209 : RealCommand( "HELP", parent, 0, 1, "[nothing | <command name>]" ) 00210 { 00211 } 00212 }; 00213 00214 class ListCommand : public RealCommand 00215 { 00216 protected: 00217 void maincode( int, std::string* ) 00218 { 00219 socket() << "103 none" << std::endl; 00220 } 00221 00222 public: 00223 ListCommand(TcpReportingInterpreter* parent) 00224 : RealCommand( "LISTEXTENSIONS", parent ) 00225 { 00226 } 00227 }; 00228 00229 class QuitCommand : public RealCommand 00230 { 00231 protected: 00232 void maincode( int, std::string* ) 00233 { 00234 // The main marshaller is not notified about the connection 00235 // being closed but will detect it and delete the DataSender 00236 // in the next serialize() run because DataSender is a 00237 // thread and this method is (indirectly) called by 00238 // DataSender::loop. 00239 socket().close(); 00240 } 00241 00242 public: 00243 QuitCommand(TcpReportingInterpreter* parent) 00244 : RealCommand( "QUIT", parent ) 00245 { 00246 } 00247 }; 00248 00249 class SetLimitCommand : public RealCommand 00250 { 00251 protected: 00252 void maincode( int, std::string* args ) 00253 { 00254 int olderr = errno; 00255 char* tailptr; 00256 unsigned long long limit = strtoull( args[0].c_str(), &tailptr, 10 ); 00257 if( *tailptr != '\0' || ( errno != olderr && errno == ERANGE ) ) 00258 { 00259 sendError102(); 00260 } else { 00261 _parent->getConnection()->setLimit(limit); 00262 sendOK(); 00263 } 00264 } 00265 00266 public: 00267 SetLimitCommand(TcpReportingInterpreter* parent) 00268 : RealCommand( "SETLIMIT", parent, 1, 1, "<frameid>" ) 00269 { 00270 } 00271 }; 00272 00276 class SilenceCommand : public RealCommand 00277 { 00278 protected: 00279 void maincode( int, std::string* args ) 00280 { 00281 toupper( args, 0 ); 00282 if( args[0] == "ON" ) 00283 { 00284 _parent->getConnection()->silence(true); 00285 } else if( args[0] == "OFF") { 00286 _parent->getConnection()->silence(false); 00287 } else { 00288 sendError102(); 00289 return; 00290 } 00291 socket() << "107 Silence " << args[0] << std::endl; 00292 } 00293 00294 public: 00295 SilenceCommand(TcpReportingInterpreter* parent) 00296 : RealCommand( "SILENCE", parent, 1, 1, "[ON | OFF]" ) 00297 { 00298 } 00299 }; 00300 00304 class SubscribeCommand : public RealCommand 00305 { 00306 // TODO: id's elimneren voor subscribe command 00307 // TODO: id's elimneren voor unsubscribe command 00308 // TODO: id's elimneren 00309 protected: 00310 void maincode( int, std::string* args ) 00311 { 00312 if( _parent->getConnection()->addSubscription(args[0]) ) 00313 { 00314 socket() << "302 " << args[0] << std::endl; 00315 } else { 00316 socket() << "301 " << args[0] << std::endl; 00317 } 00318 } 00319 00320 public: 00321 SubscribeCommand(TcpReportingInterpreter* parent) 00322 : RealCommand( "SUBSCRIBE", parent, 1, 1, "<source name>" ) 00323 { 00324 } 00325 }; 00326 00327 class SubscriptionsCommand : public RealCommand 00328 { 00329 protected: 00330 void maincode( int, std::string* ) 00331 { 00332 _parent->getConnection()->listSubscriptions(); 00333 } 00334 00335 public: 00336 SubscriptionsCommand(TcpReportingInterpreter* parent) 00337 : RealCommand( "SUBS", parent ) 00338 { 00339 } 00340 }; 00341 00342 class UnsubscribeCommand : public RealCommand 00343 { 00344 protected: 00345 void maincode( int, std::string* args ) 00346 { 00347 if( _parent->getConnection()->removeSubscription(args[0]) ) 00348 { 00349 socket() << "303 " << args[0] << std::endl; 00350 } else { 00351 socket() << "304 " << args[0] << std::endl; 00352 } 00353 } 00354 00355 public: 00356 UnsubscribeCommand(TcpReportingInterpreter* parent) 00357 : RealCommand( "UNSUBSCRIBE", parent, 1, 1, "<source name>" ) 00358 { 00359 } 00360 }; 00361 00362 class VersionCommand : public RealCommand 00363 { 00364 protected: 00365 void maincode( int, std::string* args ) 00366 { 00367 if( args[0] == "1.0" ) 00368 { 00369 _parent->setVersion10(); 00370 sendOK(); 00371 } else { 00372 socket() << "106 Not supported" << std::endl; 00373 } 00374 } 00375 00376 public: 00377 VersionCommand(TcpReportingInterpreter* parent) 00378 : RealCommand( "VERSION", parent, 1, 1, "1.0" ) 00379 { 00380 } 00381 }; 00382 } 00383 00384 namespace OCL 00385 { 00386 namespace TCP 00387 { 00388 /* 00389 * The default Orocos Command objects are not used since we 00390 * do not use Data Sources here. 00391 */ 00392 class RealCommand; 00393 00394 Command::Command( std::string name ) 00395 : _name( name ) 00396 { 00397 } 00398 00399 Command::~Command() 00400 { 00401 } 00402 00403 Command* Command::find(const std::vector<Command*>& cmds, const std::string& cmp) 00404 { 00405 for( unsigned int j = 0; j < cmds.size(); j++ ) 00406 { 00407 if( *cmds[j] == cmp ) 00408 { 00409 return cmds[j]; 00410 } 00411 } 00412 return 0; 00413 } 00414 00415 const std::string& Command::getName() const 00416 { 00417 return _name; 00418 } 00419 00420 bool Command::is(std::string& cmd) const 00421 { 00422 return cmd == _name; 00423 } 00424 00425 bool Command::operator==(const std::string& cmp) const 00426 { 00427 return cmp == _name; 00428 } 00429 00430 bool Command::operator!=(const std::string& cmp) const 00431 { 00432 return cmp != _name; 00433 } 00434 00435 bool Command::operator<( const Command& cmp ) const 00436 { 00437 return _name < cmp.getName(); 00438 } 00439 00440 AliasCommand::AliasCommand( std::string name, std::string alias ) 00441 : Command( name ), _alias( alias ) 00442 { 00443 } 00444 00445 RealCommand* AliasCommand::getRealCommand(const std::vector<Command*>& cmds) const 00446 { 00447 Command* ret = Command::find( cmds, _alias ); 00448 if( !ret ) 00449 { 00450 return 0; 00451 } 00452 return ret->getRealCommand(cmds); 00453 } 00454 00455 RealCommand::RealCommand( std::string name, TcpReportingInterpreter* parent, unsigned int minargs, 00456 unsigned int maxargs, const char* syntax ) 00457 : Command( name ), _parent( parent ), _minargs( minargs ), _maxargs( maxargs ), _syntax( syntax ) 00458 { 00459 } 00460 00461 RealCommand::~RealCommand() 00462 { 00463 } 00464 00465 const char* RealCommand::getSyntax() const 00466 { 00467 return _syntax; 00468 } 00469 00470 bool RealCommand::sendError102() const 00471 { 00472 if( _syntax ) 00473 { 00474 socket() << "102 Syntax: " << _name << ' ' << _syntax << std::endl; 00475 } else { 00476 socket() << "102 Syntax: " << _name << std::endl; 00477 } 00478 return false; 00479 } 00480 00481 bool RealCommand::sendOK() const 00482 { 00483 socket() << "101 OK" << std::endl; 00484 return true; 00485 } 00486 00487 bool RealCommand::correctSyntax( unsigned int argc, std::string* ) 00488 { 00489 if( argc < _minargs || argc > _maxargs ) 00490 { 00491 return sendError102(); 00492 } 00493 return true; 00494 } 00495 00496 RealCommand* RealCommand::getRealCommand(const std::vector<Command*>& cmds) const 00497 { 00498 return const_cast<RealCommand*>(this); 00499 } 00500 00501 void RealCommand::execute( int argc, std::string* args ) 00502 { 00503 if( correctSyntax( argc, args ) ) 00504 { 00505 maincode( argc, args ); 00506 } 00507 } 00508 00509 void RealCommand::toupper( std::string* args, int i ) const 00510 { 00511 std::transform( args[i].begin(), args[i].end(), args[i].begin(), to_upper ); 00512 } 00513 00514 void RealCommand::toupper( std::string* args, int start, int stop ) const 00515 { 00516 for( int i = start; i <= stop; i++ ) 00517 { 00518 toupper( args, i ); 00519 } 00520 } 00521 00522 Socket& RealCommand::socket() const 00523 { 00524 return _parent->getConnection()->getSocket(); 00525 } 00526 00527 TcpReportingInterpreter::TcpReportingInterpreter(Datasender* parent) 00528 : _parent( parent ) 00529 { 00530 addCommand( new VersionCommand(this) ); 00531 addCommand( new HelpCommand(this) ); 00532 addCommand( new QuitCommand(this) ); 00533 addCommand( new AliasCommand( "EXIT", "QUIT" ) ); 00534 } 00535 00536 TcpReportingInterpreter::~TcpReportingInterpreter() 00537 { 00538 for( std::vector<Command*>::iterator i = cmds.begin(); 00539 i != cmds.end(); 00540 i++ ) 00541 { 00542 delete *i; 00543 } 00544 } 00545 00546 void TcpReportingInterpreter::addCommand( Command* command ) 00547 { 00548 // this method has a complexity of O(n) because we want 00549 // the list to be sorted. 00550 commands.lock(); 00551 std::vector<Command*>::iterator i = cmds.begin(); 00552 while( i != cmds.end() && *command < **i ) { 00553 i++; 00554 } 00555 00556 // avoid duplicates 00557 if( i != cmds.end() && *command == (*i)->getName() ) 00558 { 00559 return; 00560 } 00561 cmds.insert( i, command ); 00562 commands.unlock(); 00563 } 00564 00565 const std::vector<Command*>& TcpReportingInterpreter::giveCommands() const 00566 { 00567 return cmds; 00568 } 00569 00570 Datasender* TcpReportingInterpreter::getConnection() const 00571 { 00572 return _parent; 00573 } 00574 00575 void TcpReportingInterpreter::process() 00576 { 00577 std::string ipt = getConnection()->getSocket().readLine(); 00578 00579 if( ipt.empty() ) 00580 { 00581 return; 00582 } 00583 00584 /* parseParameters returns data by reference */ 00585 std::string cmd; 00586 std::string* params; 00587 00588 unsigned int argc = parseParameters( ipt, cmd, ¶ms ); 00589 00590 std::transform( cmd.begin(), cmd.end(), cmd.begin(), to_upper ); 00591 00592 /* search the command to be executed */ 00593 bool correct = false; 00594 commands.lock(); 00595 Command* obj = Command::find( cmds, cmd ); 00596 if( obj ) /* command found */ 00597 { 00598 RealCommand* rcommand = obj->getRealCommand(cmds); 00599 if( rcommand ) /* alias is correct */ 00600 { 00601 rcommand->execute( argc, params ); 00602 correct = true; 00603 } 00604 } else { 00605 Logger::log() << Logger::Error << "Invalid command: " << ipt << Logger::endl; 00606 } 00607 commands.unlock(); 00608 00609 if( !correct ) 00610 { 00611 getConnection()->getSocket() << "105 Command not found" << std::endl; 00612 } 00613 } 00614 00615 unsigned int TcpReportingInterpreter::parseParameters( 00616 std::string& ipt, std::string& cmd, std::string** params ) 00617 { 00618 unsigned int argc = 0; 00619 std::string::size_type pos = ipt.find_first_of("\t ", 0); 00620 while( pos != std::string::npos ) 00621 { 00622 pos = ipt.find_first_of("\t ", pos + 1); 00623 argc++; 00624 } 00625 if( argc > 0 ) 00626 { 00627 *params = new std::string[argc]; 00628 pos = ipt.find_first_of("\t ", 0); 00629 cmd = ipt.substr(0, pos); 00630 unsigned int npos; 00631 for( unsigned int i = 0; i < argc; i++ ) 00632 { 00633 npos = ipt.find_first_of("\t ", pos + 1); 00634 (*params)[i] = ipt.substr(pos+1,npos - pos - 1); 00635 pos = npos; 00636 } 00637 } else { 00638 cmd = ipt; 00639 *params = 0; 00640 } 00641 return argc; 00642 } 00643 00644 void TcpReportingInterpreter::removeCommand( const char* ipt ) 00645 { 00646 commands.lock(); 00647 std::vector<Command*>::iterator i = cmds.begin(); 00648 while( i != cmds.end() && **i != ipt ) { 00649 i++; 00650 } 00651 if( i == cmds.end() ) 00652 { 00653 Logger::log() << Logger::Error << "TcpReportingInterpreter::removeCommand: removing unknown command" << ipt << Logger::endl; 00654 } else { 00655 Command* todel = *i; 00656 cmds.erase(i); 00657 delete todel; 00658 } 00659 commands.unlock(); 00660 } 00661 00662 void TcpReportingInterpreter::setVersion10() 00663 { 00664 commands.lock(); 00665 removeCommand( "VERSION" ); 00666 addCommand( new ListCommand(this) ); 00667 addCommand( new HeaderCommand(this) ); 00668 addCommand( new SilenceCommand(this) ); 00669 addCommand( new SetLimitCommand(this) ); 00670 addCommand( new SubscribeCommand(this) ); 00671 addCommand( new UnsubscribeCommand(this) ); 00672 addCommand( new SubscriptionsCommand(this) ); 00673 commands.unlock(); 00674 _parent->silence( false ); 00675 } 00676 } 00677 } 00678