OrocosComponentLibrary  2.7.0
command.cpp
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, &params );
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