1 /* 2 * cli.h 3 * 4 * Command line interpreter 5 * 6 * Copyright (C) 2006-2008 Post Increment 7 * 8 * The contents of this file are subject to the Mozilla Public License 9 * Version 1.0 (the "License"); you may not use this file except in 10 * compliance with the License. You may obtain a copy of the License at 11 * http://www.mozilla.org/MPL/ 12 * 13 * Software distributed under the License is distributed on an "AS IS" 14 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 15 * the License for the specific language governing rights and limitations 16 * under the License. 17 * 18 * The Original Code is WOpenMCU 19 * 20 * The Initial Developer of the Original Code is Post Increment 21 * 22 * Contributor(s): Craig Southeren (craigs@postincrement.com) 23 * Robert Jongbloed (robertj@voxlucida.com.au) 24 * 25 * Portions of this code were written by Post Increment (http://www.postincrement.com) 26 * with the assistance of funding from US Joint Forces Command Joint Concept Development & 27 * Experimentation (J9) http://www.jfcom.mil/about/abt_j9.htm 28 * 29 * Further assistance for enhancements from Imagicle spa 30 * 31 * $Revision: 24931 $ 32 * $Author: csoutheren $ 33 * $Date: 2010-12-07 20:01:15 -0600 (Tue, 07 Dec 2010) $ 34 */ 35 36 #ifndef PTLIB_CLI_H 37 #define PTLIB_CLI_H 38 39 #include <ptlib.h> 40 #include <ptlib/sockets.h> 41 42 #include <list> 43 44 45 /** Command Line Interpreter class. 46 This class contains a set of commands, which are executed via a PNotifier, 47 when entered via a PChannel. 48 49 The system supports multiple simultaneous interpreter which may access the 50 same command set. For example several telnet sessions. 51 52 Note that the various command interpreters could be operating in different 53 threads, so care should be taken for sybchronisation issues on the object 54 being acted upon via the PNotifiers. 55 */ 56 class PCLI : public PObject 57 { 58 PCLASSINFO(PCLI, PObject); 59 public: 60 class Context; 61 62 /**Context for command line interpreter. 63 */ 64 class Context : public PIndirectChannel 65 { 66 public: 67 /**@name Construction */ 68 //@{ 69 /**Construct new command line interpreter context. 70 */ 71 Context( 72 PCLI & cli 73 ); 74 75 /**Destroy command line interpreter context. 76 This will close and wait for threads to exit. 77 */ 78 virtual ~Context(); 79 //@} 80 81 /**@name Overrides from PChannel */ 82 //@{ 83 /**Low level write to the channel. This function will block until the 84 requested number of characters are written or the write timeout is 85 reached. The GetLastWriteCount() function returns the actual number 86 of bytes written. 87 88 This translate new line character ('\n') to be the text string in 89 PCLI::m_newLine. 90 91 The GetErrorCode() function should be consulted after Write() returns 92 false to determine what caused the failure. 93 94 @return 95 true if at least len bytes were written to the channel. 96 */ 97 virtual PBoolean Write( 98 const void * buf, ///< Pointer to a block of memory to write. 99 PINDEX len ///< Number of bytes to write. 100 ); 101 //@} 102 103 /**@name Operations */ 104 //@{ 105 /**Start a command line interpreter thread. 106 */ 107 bool Start(); 108 109 /**Stop command line interpreter context. 110 This will close the channel and wait for threads to exit. 111 */ 112 void Stop(); 113 114 /**Call back frunction for when context is started. 115 This is usually called from within a background thread. 116 117 The default behaviour displays the prompt. 118 */ 119 virtual void OnStart(); 120 121 /**Callback for when context is stopping. 122 This is usually called from within a background thread. 123 124 The default behaviour does nothing. 125 */ 126 virtual void OnStop(); 127 128 /**Write prompt (depending on state) to channel. 129 */ 130 virtual bool WritePrompt(); 131 132 /**Read a character from the attached channel an process. 133 If the character was successfully read then ProcessInput() is called. 134 */ 135 virtual bool ReadAndProcessInput(); 136 137 /**Process a character read from the channel. 138 Returns false if have error and processing is to cease. 139 */ 140 virtual bool ProcessInput(int ch); 141 virtual bool ProcessInput(const PString & line); 142 143 /**Call back for a command line was completed and ENTER pressed. 144 The default behaviour processes the line into a PArgList and deals 145 with the command history and help. 146 147 Control is then passed on to PCLI::OnReceivedLine(). 148 */ 149 virtual void OnCompletedLine(); 150 //@} 151 152 /**@name Member access */ 153 //@{ 154 /**Get the CLI. 155 */ GetCLI()156 PCLI & GetCLI() const { return m_cli; } 157 158 /**Indicate is currently processing a command. 159 */ IsProcessingCommand()160 bool IsProcessingCommand() const { return m_state == e_ProcessingCommand; } 161 //@} 162 163 protected: 164 PDECLARE_NOTIFIER(PThread, Context, ThreadMain); 165 166 PCLI & m_cli; 167 PString m_commandLine; 168 bool m_ignoreNextEOL; 169 PStringList m_commandHistory; 170 PThread * m_thread; 171 172 enum State { 173 e_Username, 174 e_Password, 175 e_CommandEntry, 176 e_ProcessingCommand 177 } m_state; 178 PString m_enteredUsername; 179 }; 180 181 /**This class is an enhancement to PArgList to add context. 182 */ 183 class Arguments : public PArgList 184 { 185 public: 186 /**@name Construction */ 187 //@{ 188 Arguments( 189 Context & context, 190 const PString & rawLine 191 ); 192 //@} 193 194 /**@name Operations */ 195 //@{ 196 /**Write to the CLI output channel the usage for the current command. 197 */ 198 Context & WriteUsage(); 199 200 /**Write an error to the CLI output channel. 201 */ 202 Context & WriteError( 203 const PString & error = PString::Empty() /// Error message 204 ); 205 //@} 206 207 /**@name Member access */ 208 //@{ 209 /**Get the CLI context supplying the command line arguments. 210 */ GetContext()211 Context & GetContext() const { return m_context; } 212 //@} 213 214 protected: 215 Context & m_context; 216 PString m_command; 217 PString m_usage; 218 219 friend class PCLI; 220 }; 221 222 223 /**@name Construction */ 224 //@{ 225 /** Contracut a new command line interpreter. 226 */ 227 PCLI( 228 const char * prompt = NULL 229 ); 230 231 /**Destroy the command line interpreter. This will call Stop() to assure 232 everything is cleaned up before exiting. 233 */ 234 virtual ~PCLI(); 235 //@} 236 237 /**@name Operations */ 238 //@{ 239 /**Start a command line interpreter. 240 If runInBackground is true the all the command line interpreter contexts 241 that have been added will have their background threads started. 242 243 If runInBackground is false, then there must only be one context added 244 and that context is continuously read until it's channel is closed or 245 returns end of file. 246 */ 247 virtual bool Start( 248 bool runInBackground = true ///< Spawn a thread to read and interpret commands 249 ); 250 251 /**Stop and clean up command line interpreters. 252 All the running contexts threads will be stopped, closing the channels 253 and memory cleaned up. 254 */ 255 virtual void Stop(); 256 257 /**Open a command line interpreter context. 258 */ 259 bool StartContext( 260 PChannel * channel, ///< Channel to read/write 261 bool autoDelete = true, ///< Automatically delete channel on exit 262 bool runInBackground = true ///< Spawn a thread to read and interpret commands 263 ); 264 bool StartContext( 265 PChannel * readChannel, ///< Channel to be used for both read operations. 266 PChannel * writeChannel, ///< Channel to be used for both write operations. 267 bool autoDeleteRead = true, ///< Automatically delete the read channel 268 bool autoDeleteWrite = true, ///< Automatically delete the write channel 269 bool runInBackground = true ///< Spawn a thread to read and interpret commands 270 ); 271 272 /**Create a new context. 273 Users may use this to create derived classes for their own use. 274 */ 275 virtual Context * CreateContext(); 276 277 /**Add a command line interpreter context to the system. 278 If context is NULL then CreateContext() is called to create one. 279 */ 280 virtual Context * AddContext( 281 Context * context = NULL ///< New context to add to the system. 282 ); 283 284 /**Remove the command line interpreter context. 285 The context thread is stopped, the channel closed and memory deleted. 286 */ 287 virtual void RemoveContext( 288 Context * context ///< Context to remove 289 ); 290 291 /**Remove any closed command line interpreter contexts. 292 */ 293 virtual void GarbageCollection(); 294 295 /**Received a completed command line. 296 The completed command line is parsed into arguments by the PArgList 297 class, and passed to this function. 298 299 The default behaviour searches the list of registered commands for a 300 PNotifier to execute. 301 */ 302 virtual void OnReceivedLine( 303 Arguments & line 304 ); 305 306 /**Received a login name/pasword to be verified. 307 Note that the m_username or m_password field must be non-empty for a 308 log in sequence to occur, even if this function is overridden and the 309 memnbers not actually used for validation. 310 311 If the m_username is an empty string, but m_password is not, then it 312 the username not prompted for, just the password is required. The 313 reverse is also poassible and only a username entry required. 314 315 Default returns true if parameters are equal to m_username, m_password 316 respectively. 317 */ 318 virtual bool OnLogIn( 319 const PString & username, 320 const PString & password 321 ); 322 323 /**Set a string to all command line interpreter contexts. 324 */ 325 void Broadcast( 326 const PString & message ///< Message to broadcast. 327 ) const; 328 329 /**Register a new command to be interpreted. 330 Note the command may be a series of synonyms of the same command 331 separated by the '\n' character. 332 333 The command may also contain spaces which separates sub-commands, e.g. 334 "list users". 335 336 Returns false if one of the command synonyms was a dupicate of an 337 existing command. 338 */ 339 bool SetCommand( 340 const char * command, ///< Command(s) to register 341 const PNotifier & notifier, ///< Callback to execute when command interpreted 342 const char * help = NULL, ///< Help text on command (what it does) 343 const char * usage = NULL ///< Usage text on command (syntax/options) 344 ); 345 346 /**Show help for registered commands to the context. 347 */ 348 void ShowHelp( 349 Context & context ///< Context to output help to. 350 ); 351 //@} 352 353 /**@name Member access */ 354 //@{ 355 /**Get new line string output at the end of every line. 356 Default is "\n". 357 */ GetNewLine()358 const PString & GetNewLine() const { return m_newLine; } 359 360 /**Set new line string output at the end of every line. 361 Default is "\n". 362 */ SetNewLine(const PString & newLine)363 void SetNewLine(const PString & newLine) { m_newLine = newLine; } 364 365 /**Get flag for echo is required for entered characters. 366 Default is false. 367 */ GetRequireEcho()368 bool GetRequireEcho() const { return m_requireEcho; } 369 370 /**Set flag for echo is required for entered characters. 371 Default is false. 372 */ SetRequireEcho(bool requireEcho)373 void SetRequireEcho(bool requireEcho) { m_requireEcho = requireEcho; } 374 375 /**Get characters used for editing (backspace/delete) command lines. 376 Default is "\b\x7f". 377 */ GetEditCharacters()378 const PString & GetEditCharacters() const { return m_editCharacters; } 379 380 /**Set characters used for editing (backspace/delete) command lines. 381 Default is "\b\x7f". 382 */ SetEditCharacters(const PString & editCharacters)383 void SetEditCharacters(const PString & editCharacters) { m_editCharacters = editCharacters; } 384 385 /**Get prompt used for command line interpreter. 386 Default is "CLI> ". 387 */ GetPrompt()388 const PString & GetPrompt() const { return m_prompt; } 389 390 /**Set prompt used for command line interpreter. 391 Default is "CLI> ". 392 */ SetPrompt(const PString & prompt)393 void SetPrompt(const PString & prompt) { m_prompt = prompt; } 394 395 /**Get prompt used for login (if enabled). 396 Default is "Username: ". 397 */ GetUsernamePrompt()398 const PString & GetUsernamePrompt() const { return m_usernamePrompt; } 399 400 /**Set prompt used for login (if enabled). 401 Default is "Username: ". 402 */ SetUsernamePrompt(const PString & prompt)403 void SetUsernamePrompt(const PString & prompt) { m_usernamePrompt = prompt; } 404 405 /**Get prompt used for password (if enabled). 406 Default is "Password: ". 407 */ GetPasswordPrompt()408 const PString & GetPasswordPrompt() const { return m_passwordPrompt; } 409 410 /**Set prompt used for password (if enabled). 411 Default is "Password: ". 412 */ SetPasswordPrompt(const PString & prompt)413 void SetPasswordPrompt(const PString & prompt) { m_passwordPrompt = prompt; } 414 415 /**Get username for log in validation. 416 Default is empty string, disabling entry of username/password. 417 */ GetUsername()418 const PString & GetUsername() const { return m_username; } 419 420 /**Set username for log in validation. 421 Default is empty string, disabling entry of username/password. 422 */ SetUsername(const PString & username)423 void SetUsername(const PString & username) { m_username = username; } 424 425 /**Get password for log in validation. 426 Default is empty string, disabling entry of password. 427 */ GetPassword()428 const PString & GetPassword() const { return m_password; } 429 430 /**Set password for log in validation. 431 Default is empty string, disabling entry of password. 432 */ SetPassword(const PString & password)433 void SetPassword(const PString & password) { m_password = password; } 434 435 /**Get command to be used to exit session. 436 Default is "exit\nquit". 437 */ GetExitCommand()438 const PCaselessString & GetExitCommand() const { return m_exitCommand; } 439 440 /**Set command to be used to exit session. 441 Default is "exit\nquit". 442 */ SetExitCommand(const PCaselessString & exitCommand)443 void SetExitCommand(const PCaselessString & exitCommand) { m_exitCommand = exitCommand; } 444 445 /**Get command to be used to display help. 446 Default is "?\nhelp". 447 */ GetHelpCommand()448 const PCaselessString & GetHelpCommand() const { return m_helpCommand; } 449 450 /**Set command to be used to display help. 451 Default is "?\nhelp". 452 */ SetHelpCommand(const PCaselessString & helpCommand)453 void SetHelpCommand(const PCaselessString & helpCommand) { m_helpCommand = helpCommand; } 454 455 /**Get help on help. 456 This string is output before the individual help is output for each command. 457 Default describes ?, !, !n, !! followed by "Command available are:". 458 */ GetHelpOnHelp()459 const PString & GetHelpOnHelp() const { return m_helpOnHelp; } 460 461 /**Set help on help. 462 This string is output before the individual help is output for each command. 463 Default describes ?, !, !n, !! followed by "Command available are:". 464 */ SetHelpOnHelp(const PCaselessString & helpOnHelp)465 void SetHelpOnHelp(const PCaselessString & helpOnHelp) { m_helpOnHelp = helpOnHelp; } 466 467 /**Get the command to be used to repeat the last executed command. 468 Default is "!!". 469 */ GetRepeatCommand()470 const PCaselessString & GetRepeatCommand() const { return m_repeatCommand; } 471 472 /**Set the command to be used to repeat the last executed command. 473 Default is "!!". 474 */ SetRepeatCommand(const PCaselessString & repeatCommand)475 void SetRepeatCommand(const PCaselessString & repeatCommand) { m_repeatCommand = repeatCommand; } 476 477 /**Get command that will list/execute command history. 478 Default is "!". 479 */ GetHistoryCommand()480 const PCaselessString & GetHistoryCommand() const { return m_historyCommand; } 481 482 /**Set command that will list/execute command history. 483 Default is "!". 484 */ SetHistoryCommand(const PCaselessString & historyCommand)485 void SetHistoryCommand(const PCaselessString & historyCommand) { m_historyCommand = historyCommand; } 486 487 /**Get error message for if there is no history. 488 Default is "No command history". 489 */ GetNoHistoryError()490 const PString & GetNoHistoryError() const { return m_noHistoryError; } 491 492 /**Set error message for if there is no history. 493 Default is "No command history". 494 */ SetNoHistoryError(const PString & noHistoryError)495 void SetNoHistoryError(const PString & noHistoryError) { m_noHistoryError = noHistoryError; } 496 497 /**Get usage prefix for if Arguments::WriteUsage() called. 498 Default is "Usage: ". 499 */ GetCommandUsagePrefix()500 const PString & GetCommandUsagePrefix() const { return m_commandUsagePrefix; } 501 502 /**Set usage prefix for if Arguments::WriteUsage() called. 503 Default is "Usage: ". 504 */ SetCommandUsagePrefix(const PString & commandUsagePrefix)505 void SetCommandUsagePrefix(const PString & commandUsagePrefix) { m_commandUsagePrefix = commandUsagePrefix; } 506 507 /**Get error prefix for if Arguments::WriteError() called. 508 Default is ": error: ", always prefixed by command name. 509 */ GetCommandErrorPrefix()510 const PString & GetCommandErrorPrefix() const { return m_commandErrorPrefix; } 511 512 /**Set error prefix for if Arguments::WriteError() called. 513 Default is ": error: ", always prefixed by command name. 514 */ SetCommandErrorPrefix(const PString & commandErrorPrefix)515 void SetCommandErrorPrefix(const PString & commandErrorPrefix) { m_commandErrorPrefix = commandErrorPrefix; } 516 517 /**Get error message for if unknown command is entered. 518 Default is "Unknown command". 519 */ GetUnknownCommandError()520 const PString & GetUnknownCommandError() const { return m_unknownCommandError; } 521 522 /**Set error message for if unknown command is entered. 523 Default is "Unknown command". 524 */ SetUnknownCommandError(const PString & unknownCommandError)525 void SetUnknownCommandError(const PString & unknownCommandError) { m_unknownCommandError = unknownCommandError; } 526 //@} 527 528 /** Initialise a foreground context and return it 529 */ 530 virtual Context * StartForeground(); 531 532 /** Run a context 533 */ 534 virtual bool RunContext(Context * context); 535 536 537 protected: 538 PString m_newLine; 539 bool m_requireEcho; 540 PString m_editCharacters; 541 PString m_prompt; 542 PString m_usernamePrompt; 543 PString m_passwordPrompt; 544 PString m_username; 545 PString m_password; 546 PCaselessString m_exitCommand; 547 PCaselessString m_helpCommand; 548 PString m_helpOnHelp; 549 PCaselessString m_repeatCommand; 550 PCaselessString m_historyCommand; 551 PString m_noHistoryError; 552 PString m_commandUsagePrefix; 553 PString m_commandErrorPrefix; 554 PString m_unknownCommandError; 555 556 struct InternalCommand { 557 PNotifier m_notifier; 558 PString m_help; 559 PString m_usage; 560 }; 561 typedef std::map<PString, InternalCommand> CommandMap_t; 562 CommandMap_t m_commands; 563 564 typedef std::list<Context *> ContextList_t; 565 ContextList_t m_contextList; 566 PMutex m_contextMutex; 567 }; 568 569 570 /**Command Line Interpreter over standard input/output. 571 */ 572 class PCLIStandard : public PCLI 573 { 574 public: 575 /**@name Construction */ 576 //@{ 577 /**Create new command line interpreter for standard I/O. 578 */ 579 PCLIStandard( 580 const char * prompt = NULL 581 ); 582 //@} 583 584 /**@name Overrides from PCLI */ 585 //@{ 586 /**Start a command line interpreter. 587 As for ancestor function, however if no contexts have been added, then 588 on that takes a PConsoleChannel is automatically added. 589 */ 590 virtual bool Start( 591 bool runInBackground = true ///< Spawn a thread to read and interpret commands 592 ); 593 //@} 594 595 PCLI::Context * StartForeground(); 596 }; 597 598 599 /**Command Line Interpreter over TCP sockets. 600 This class allows for access and automatic creation of command line 601 interpreter contexts from incoming TCP connections on a listening port. 602 */ 603 class PCLISocket : public PCLI 604 { 605 public: 606 /**@name Construction */ 607 //@{ 608 PCLISocket( 609 WORD port = 0, 610 const char * prompt = NULL, 611 bool singleThreadForAll = false 612 ); 613 ~PCLISocket(); 614 //@} 615 616 /**@name Overrides from PCLI */ 617 //@{ 618 /**Start a command line interpreter. 619 This will start listening for incoming TCP connections and 620 create/dispatch contexts to handle them. 621 */ 622 virtual bool Start( 623 bool runInBackground = true ///< Spawn a thread to read and interpret commands 624 ); 625 626 /**Stop and clean up command line interpreters. 627 All the running contexts threads will be stopped, closing the channels 628 and memory cleaned up. 629 630 The listening socket is also closed and the listener dispatch thread 631 shut down. 632 */ 633 virtual void Stop(); 634 635 /**Add a command line interpreter context to the system. 636 If context is NULL then CreateContext() is called to create one. 637 */ 638 virtual Context * AddContext( 639 Context * context = NULL 640 ); 641 642 /**Remove the command line interpreter context. 643 The context thread is stopped, the channel closed and memory deleted. 644 */ 645 virtual void RemoveContext( 646 Context * context 647 ); 648 //@} 649 650 /**@name Operations */ 651 //@{ 652 /**Start listening socket. 653 */ 654 bool Listen( 655 WORD port = 0 656 ); 657 658 /**Get the port we are listing on. 659 */ GetPort()660 WORD GetPort() const { return m_listenSocket.GetPort(); } 661 //@} 662 663 protected: 664 PDECLARE_NOTIFIER(PThread, PCLISocket, ThreadMain); 665 bool HandleSingleThreadForAll(); 666 bool HandleIncoming(); 667 virtual PTCPSocket * CreateSocket(); 668 669 bool m_singleThreadForAll; 670 671 PTCPSocket m_listenSocket; 672 PThread * m_thread; 673 674 typedef std::map<PSocket *, Context *> ContextMap_t; 675 ContextMap_t m_contextBySocket; 676 }; 677 678 679 /**Command Line Interpreter over Telnet sockets. 680 This class allows for access and automatic creation of command line 681 interpreter contexts from incoming Telnet connections on a listening port. 682 */ 683 class PCLITelnet : public PCLISocket 684 { 685 public: 686 /**@name Construction */ 687 //@{ 688 PCLITelnet( 689 WORD port = 0, 690 const char * prompt = NULL, 691 bool singleThreadForAll = false 692 ); 693 //@} 694 695 protected: 696 virtual PTCPSocket * CreateSocket(); 697 }; 698 699 700 #endif // PTLIB_CLI_H 701 702 703 // End Of File /////////////////////////////////////////////////////////////// 704