OrocosComponentLibrary  2.9.0
command.cpp
1 /***************************************************************************
2 
3  command.cpp - Processes client commands
4  -------------------
5  begin : Wed Aug 9 2006
6  copyright : (C) 2006 Bas Kemper
7  email : kst@ <my name> .be
8 
9  ***************************************************************************
10  * This library is free software; you can redistribute it and/or *
11  * modify it under the terms of the GNU Lesser General Public *
12  * License as published by the Free Software Foundation; either *
13  * version 2.1 of the License, or (at your option) any later version. *
14  * *
15  * This library is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
18  * Lesser General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU Lesser General Public *
21  * License along with this library; if not, write to the Free Software *
22  * Foundation, Inc., 59 Temple Place, *
23  * Suite 330, Boston, MA 02111-1307 USA *
24  * *
25  ***************************************************************************/
26 
27 #include <algorithm> /* std::transform is needed for upper-case conversion */
28 #include <string>
29 #include <cstdlib> /* strtoull */
30 #include <cctype>
31 #include <cerrno>
32 #include <rtt/Property.hpp>
33 #include "NiceHeaderMarshaller.hpp"
34 #include "TcpReporting.hpp"
35 #include "command.hpp"
36 #include "socket.hpp"
37 #include "datasender.hpp"
38 #include "socketmarshaller.hpp"
39 
41 using OCL::TCP::Socket;
43 
44 namespace
45 {
49  class prefixbuf : public std::streambuf
50  {
51  private:
52  std::streambuf* _opt;
53  bool newline;
54 
55  public:
56  prefixbuf( std::streambuf* opt ) : _opt(opt)
57  {
58  setp(0, 0);
59  setg(0, 0, 0);
60  newline = true;
61  }
62 
63  ~prefixbuf()
64  {
65  sync();
66  }
67 
68  protected:
69  int overflow(int c)
70  {
71  if( c != EOF )
72  {
73  if( newline )
74  {
75  if( _opt->sputn("300 ", 4) != 4 )
76  {
77  return EOF;
78  }
79  newline = false;
80  }
81  int ret = _opt->sputc(c);
82  newline = ( c == '\n' );
83  return ret;
84  }
85  return 0;
86  }
87 
88  int sync()
89  {
90  return _opt->pubsync();
91  }
92  };
93 
97  class prefixstr : public std::ostream
98  {
99  public:
100  prefixstr(std::ostream& opt)
101  : std::ostream( new prefixbuf( opt.rdbuf() ) )
102  {
103  }
104 
105  ~prefixstr()
106  {
107  delete rdbuf();
108  }
109  };
110 
111  /* std::toupper is implemented as a macro and cannot be used by
112  std::transform without a wrapper function. */
113  char to_upper (const char c)
114  {
115  return toupper(c);
116  }
117 
121  class HeaderCommand : public RealCommand
122  {
123  protected:
124  void maincode( int, std::string* )
125  {
126  std::vector<std::string> list = _parent->getConnection()->getMarshaller()->getReporter()->getReport()->list();
127  for(unsigned int i=0;i<list.size();i++)
128  socket()<<"305 "<<list[i]<<std::endl;
129  socket() << "306 End of list" << std::endl;
130  }
131 
132  public:
133  HeaderCommand(TcpReportingInterpreter* parent)
134  : RealCommand( "HEADERS", parent )
135  {
136  }
137 
138  ~HeaderCommand()
139  {}
140 
141  void manualExecute()
142  {
143  maincode(0,0);
144  }
145  };
146 
147  class HelpCommand : public RealCommand
148  {
149  protected:
153  void printCommands()
154  {
155  const std::vector<Command*> &cmds = _parent->giveCommands();
156  socket() << "Use HELP <command>" << std::endl;
157  for( unsigned int i = 0; i < cmds.size(); i++ )
158  {
159  if( cmds[i] == cmds[i]->getRealCommand( cmds ) )
160  {
161  socket() << cmds[i]->getName() << '\n';
162  }
163  }
164  socket() << '.' << std::endl;
165  }
166 
170  void printHelpFor( const std::string& name, const RealCommand* command )
171  {
172  socket() << "Name: " << name << std::endl;
173  socket() << "Usage: " << name;
174  if( command->getSyntax() )
175  {
176  socket() << " " << command->getSyntax();
177  }
178  socket() << std::endl;
179  }
180 
184  void printHelpFor( const std::string& cmd )
185  {
186  const std::vector<Command*> &cmds = _parent->giveCommands();
187  for( unsigned int i = 0; i < cmds.size(); i++ )
188  {
189  if( cmds[i]->getName() == cmd )
190  {
191  printHelpFor( cmd, cmds[i]->getRealCommand( cmds ) );
192  return;
193  }
194  }
195  printCommands();
196  }
197 
198  void maincode( int argc, std::string* params )
199  {
200  if( argc == 0 )
201  {
202  printCommands();
203  } else {
204  printHelpFor(params[0]);
205  }
206  }
207  public:
208  HelpCommand(TcpReportingInterpreter* parent)
209  : RealCommand( "HELP", parent, 0, 1, "[nothing | <command name>]" )
210  {
211  }
212  };
213 
214  class ListCommand : public RealCommand
215  {
216  protected:
217  void maincode( int, std::string* )
218  {
219  socket() << "103 none" << std::endl;
220  }
221 
222  public:
223  ListCommand(TcpReportingInterpreter* parent)
224  : RealCommand( "LISTEXTENSIONS", parent )
225  {
226  }
227  };
228 
229  class QuitCommand : public RealCommand
230  {
231  protected:
232  void maincode( int, std::string* )
233  {
234  // The main marshaller is not notified about the connection
235  // being closed but will detect it and delete the DataSender
236  // in the next serialize() run because DataSender is a
237  // thread and this method is (indirectly) called by
238  // DataSender::loop.
239  socket().close();
240  }
241 
242  public:
243  QuitCommand(TcpReportingInterpreter* parent)
244  : RealCommand( "QUIT", parent )
245  {
246  }
247  };
248 
249  class SetLimitCommand : public RealCommand
250  {
251  protected:
252  void maincode( int, std::string* args )
253  {
254  int olderr = errno;
255  char* tailptr;
256  unsigned long long limit = strtoull( args[0].c_str(), &tailptr, 10 );
257  if( *tailptr != '\0' || ( errno != olderr && errno == ERANGE ) )
258  {
259  sendError102();
260  } else {
261  _parent->getConnection()->setLimit(limit);
262  sendOK();
263  }
264  }
265 
266  public:
267  SetLimitCommand(TcpReportingInterpreter* parent)
268  : RealCommand( "SETLIMIT", parent, 1, 1, "<frameid>" )
269  {
270  }
271  };
272 
276  class SilenceCommand : public RealCommand
277  {
278  protected:
279  void maincode( int, std::string* args )
280  {
281  toupper( args, 0 );
282  if( args[0] == "ON" )
283  {
284  _parent->getConnection()->silence(true);
285  } else if( args[0] == "OFF") {
286  _parent->getConnection()->silence(false);
287  } else {
288  sendError102();
289  return;
290  }
291  socket() << "107 Silence " << args[0] << std::endl;
292  }
293 
294  public:
295  SilenceCommand(TcpReportingInterpreter* parent)
296  : RealCommand( "SILENCE", parent, 1, 1, "[ON | OFF]" )
297  {
298  }
299  };
300 
304  class SubscribeCommand : public RealCommand
305  {
306  // TODO: id's elimneren voor subscribe command
307  // TODO: id's elimneren voor unsubscribe command
308  // TODO: id's elimneren
309  protected:
310  void maincode( int, std::string* args )
311  {
312  if( _parent->getConnection()->addSubscription(args[0]) )
313  {
314  socket() << "302 " << args[0] << std::endl;
315  } else {
316  socket() << "301 " << args[0] << std::endl;
317  }
318  }
319 
320  public:
321  SubscribeCommand(TcpReportingInterpreter* parent)
322  : RealCommand( "SUBSCRIBE", parent, 1, 1, "<source name>" )
323  {
324  }
325  };
326 
327  class SubscriptionsCommand : public RealCommand
328  {
329  protected:
330  void maincode( int, std::string* )
331  {
332  _parent->getConnection()->listSubscriptions();
333  }
334 
335  public:
336  SubscriptionsCommand(TcpReportingInterpreter* parent)
337  : RealCommand( "SUBS", parent )
338  {
339  }
340  };
341 
342  class UnsubscribeCommand : public RealCommand
343  {
344  protected:
345  void maincode( int, std::string* args )
346  {
347  if( _parent->getConnection()->removeSubscription(args[0]) )
348  {
349  socket() << "303 " << args[0] << std::endl;
350  } else {
351  socket() << "304 " << args[0] << std::endl;
352  }
353  }
354 
355  public:
356  UnsubscribeCommand(TcpReportingInterpreter* parent)
357  : RealCommand( "UNSUBSCRIBE", parent, 1, 1, "<source name>" )
358  {
359  }
360  };
361 
362  class VersionCommand : public RealCommand
363  {
364  protected:
365  void maincode( int, std::string* args )
366  {
367  if( args[0] == "1.0" )
368  {
369  _parent->setVersion10();
370  sendOK();
371  } else {
372  socket() << "106 Not supported" << std::endl;
373  }
374  }
375 
376  public:
377  VersionCommand(TcpReportingInterpreter* parent)
378  : RealCommand( "VERSION", parent, 1, 1, "1.0" )
379  {
380  }
381  };
382 }
383 
384 namespace OCL
385 {
386 namespace TCP
387 {
388  /*
389  * The default Orocos Command objects are not used since we
390  * do not use Data Sources here.
391  */
392  class RealCommand;
393 
394  Command::Command( std::string name )
395  : _name( name )
396  {
397  }
398 
399  Command::~Command()
400  {
401  }
402 
403  Command* Command::find(const std::vector<Command*>& cmds, const std::string& cmp)
404  {
405  for( unsigned int j = 0; j < cmds.size(); j++ )
406  {
407  if( *cmds[j] == cmp )
408  {
409  return cmds[j];
410  }
411  }
412  return 0;
413  }
414 
415  const std::string& Command::getName() const
416  {
417  return _name;
418  }
419 
420  bool Command::is(std::string& cmd) const
421  {
422  return cmd == _name;
423  }
424 
425  bool Command::operator==(const std::string& cmp) const
426  {
427  return cmp == _name;
428  }
429 
430  bool Command::operator!=(const std::string& cmp) const
431  {
432  return cmp != _name;
433  }
434 
435  bool Command::operator<( const Command& cmp ) const
436  {
437  return _name < cmp.getName();
438  }
439 
440  AliasCommand::AliasCommand( std::string name, std::string alias )
441  : Command( name ), _alias( alias )
442  {
443  }
444 
445  RealCommand* AliasCommand::getRealCommand(const std::vector<Command*>& cmds) const
446  {
447  Command* ret = Command::find( cmds, _alias );
448  if( !ret )
449  {
450  return 0;
451  }
452  return ret->getRealCommand(cmds);
453  }
454 
455  RealCommand::RealCommand( std::string name, TcpReportingInterpreter* parent, unsigned int minargs,
456  unsigned int maxargs, const char* syntax )
457  : Command( name ), _parent( parent ), _minargs( minargs ), _maxargs( maxargs ), _syntax( syntax )
458  {
459  }
460 
461  RealCommand::~RealCommand()
462  {
463  }
464 
465  const char* RealCommand::getSyntax() const
466  {
467  return _syntax;
468  }
469 
470  bool RealCommand::sendError102() const
471  {
472  if( _syntax )
473  {
474  socket() << "102 Syntax: " << _name << ' ' << _syntax << std::endl;
475  } else {
476  socket() << "102 Syntax: " << _name << std::endl;
477  }
478  return false;
479  }
480 
481  bool RealCommand::sendOK() const
482  {
483  socket() << "101 OK" << std::endl;
484  return true;
485  }
486 
487  bool RealCommand::correctSyntax( unsigned int argc, std::string* )
488  {
489  if( argc < _minargs || argc > _maxargs )
490  {
491  return sendError102();
492  }
493  return true;
494  }
495 
496  RealCommand* RealCommand::getRealCommand(const std::vector<Command*>& cmds) const
497  {
498  return const_cast<RealCommand*>(this);
499  }
500 
501  void RealCommand::execute( int argc, std::string* args )
502  {
503  if( correctSyntax( argc, args ) )
504  {
505  maincode( argc, args );
506  }
507  }
508 
509  void RealCommand::toupper( std::string* args, int i ) const
510  {
511  std::transform( args[i].begin(), args[i].end(), args[i].begin(), to_upper );
512  }
513 
514  void RealCommand::toupper( std::string* args, int start, int stop ) const
515  {
516  for( int i = start; i <= stop; i++ )
517  {
518  toupper( args, i );
519  }
520  }
521 
522  Socket& RealCommand::socket() const
523  {
524  return _parent->getConnection()->getSocket();
525  }
526 
527  TcpReportingInterpreter::TcpReportingInterpreter(Datasender* parent)
528  : _parent( parent )
529  {
530  addCommand( new VersionCommand(this) );
531  addCommand( new HelpCommand(this) );
532  addCommand( new QuitCommand(this) );
533  addCommand( new AliasCommand( "EXIT", "QUIT" ) );
534  }
535 
536  TcpReportingInterpreter::~TcpReportingInterpreter()
537  {
538  for( std::vector<Command*>::iterator i = cmds.begin();
539  i != cmds.end();
540  i++ )
541  {
542  delete *i;
543  }
544  }
545 
547  {
548  // this method has a complexity of O(n) because we want
549  // the list to be sorted.
550  commands.lock();
551  std::vector<Command*>::iterator i = cmds.begin();
552  while( i != cmds.end() && *command < **i ) {
553  i++;
554  }
555 
556  // avoid duplicates
557  if( i != cmds.end() && *command == (*i)->getName() )
558  {
559  return;
560  }
561  cmds.insert( i, command );
562  commands.unlock();
563  }
564 
565  const std::vector<Command*>& TcpReportingInterpreter::giveCommands() const
566  {
567  return cmds;
568  }
569 
571  {
572  return _parent;
573  }
574 
575  void TcpReportingInterpreter::process()
576  {
577  std::string ipt = getConnection()->getSocket().readLine();
578 
579  if( ipt.empty() )
580  {
581  return;
582  }
583 
584  /* parseParameters returns data by reference */
585  std::string cmd;
586  std::string* params;
587 
588  unsigned int argc = parseParameters( ipt, cmd, &params );
589 
590  std::transform( cmd.begin(), cmd.end(), cmd.begin(), to_upper );
591 
592  /* search the command to be executed */
593  bool correct = false;
594  commands.lock();
595  Command* obj = Command::find( cmds, cmd );
596  if( obj ) /* command found */
597  {
598  RealCommand* rcommand = obj->getRealCommand(cmds);
599  if( rcommand ) /* alias is correct */
600  {
601  rcommand->execute( argc, params );
602  correct = true;
603  }
604  } else {
605  Logger::log() << Logger::Error << "Invalid command: " << ipt << Logger::endl;
606  }
607  commands.unlock();
608 
609  if( !correct )
610  {
611  getConnection()->getSocket() << "105 Command not found" << std::endl;
612  }
613  }
614 
615  unsigned int TcpReportingInterpreter::parseParameters(
616  std::string& ipt, std::string& cmd, std::string** params )
617  {
618  unsigned int argc = 0;
619  std::string::size_type pos = ipt.find_first_of("\t ", 0);
620  while( pos != std::string::npos )
621  {
622  pos = ipt.find_first_of("\t ", pos + 1);
623  argc++;
624  }
625  if( argc > 0 )
626  {
627  *params = new std::string[argc];
628  pos = ipt.find_first_of("\t ", 0);
629  cmd = ipt.substr(0, pos);
630  unsigned int npos;
631  for( unsigned int i = 0; i < argc; i++ )
632  {
633  npos = ipt.find_first_of("\t ", pos + 1);
634  (*params)[i] = ipt.substr(pos+1,npos - pos - 1);
635  pos = npos;
636  }
637  } else {
638  cmd = ipt;
639  *params = 0;
640  }
641  return argc;
642  }
643 
645  {
646  commands.lock();
647  std::vector<Command*>::iterator i = cmds.begin();
648  while( i != cmds.end() && **i != ipt ) {
649  i++;
650  }
651  if( i == cmds.end() )
652  {
653  Logger::log() << Logger::Error << "TcpReportingInterpreter::removeCommand: removing unknown command" << ipt << Logger::endl;
654  } else {
655  Command* todel = *i;
656  cmds.erase(i);
657  delete todel;
658  }
659  commands.unlock();
660  }
661 
663  {
664  commands.lock();
665  removeCommand( "VERSION" );
666  addCommand( new ListCommand(this) );
667  addCommand( new HeaderCommand(this) );
668  addCommand( new SilenceCommand(this) );
669  addCommand( new SetLimitCommand(this) );
670  addCommand( new SubscribeCommand(this) );
671  addCommand( new UnsubscribeCommand(this) );
672  addCommand( new SubscriptionsCommand(this) );
673  commands.unlock();
674  _parent->silence( false );
675  }
676 }
677 }
678 
Socket & getSocket() const
Get socket associated with this datasender.
Definition: datasender.cpp:82
static Command * find(const std::vector< Command * > &cmds, const std::string &cmp)
Find the command with the given name in the vector.
Definition: command.cpp:403
This class manages the connection with one single client.
Definition: datasender.hpp:59
STL namespace.
Reads a line from the client and interprete it.
Definition: command.hpp:45
virtual RealCommand * getRealCommand(const std::vector< Command * > &cmds) const =0
Return a reference to the object which is really responsible for executing this command.
Datasender * getConnection() const
Get the marshaller associated with the current connection.
Definition: command.cpp:570
const std::vector< Command * > & giveCommands() const
Return a reference to the command list.
Definition: command.cpp:565
Another name for a command.
Definition: command.hpp:130
The Orocos Component Library.
Definition: Component.hpp:43
std::string readLine()
Read a line from the socket.
Definition: socket.cpp:222
Command pattern.
Definition: command.hpp:91
void execute(int argc, std::string *args)
Execute this command.
Definition: command.cpp:501
void removeCommand(const char *name)
Remove support for the given command name.
Definition: command.cpp:644
Real command which can be executed.
Definition: command.hpp:144
const std::string & getName() const
Get the name of this command.
Definition: command.cpp:415
void silence(bool newstate)
Disable/enable output of data.
Definition: datasender.cpp:197
void setVersion10()
Accept all valid commands (except &#39;VERSION 1.0&#39;)
Definition: command.cpp:662
const char * getSyntax() const
Return syntax information.
Definition: command.cpp:465
void addCommand(Command *command)
Add support for the given command.
Definition: command.cpp:546