OPeNDAP Hyrax Back End Server (BES) Updated for version 3.8.3
|
00001 // CmdClient.cc 00002 00003 // This file is part of bes, A C++ back-end server implementation framework 00004 // for the OPeNDAP Data Access Protocol. 00005 00006 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research 00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu> 00008 // 00009 // This library is free software; you can redistribute it and/or 00010 // modify it under the terms of the GNU Lesser General Public 00011 // License as published by the Free Software Foundation; either 00012 // version 2.1 of the License, or (at your option) any later version. 00013 // 00014 // This library is distributed in the hope that it will be useful, 00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 // Lesser General Public License for more details. 00018 // 00019 // You should have received a copy of the GNU Lesser General Public 00020 // License along with this library; if not, write to the Free Software 00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00022 // 00023 // You can contact University Corporation for Atmospheric Research at 00024 // 3080 Center Green Drive, Boulder, CO 80301 00025 00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005 00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR. 00028 // 00029 // Authors: 00030 // pwest Patrick West <pwest@ucar.edu> 00031 // jgarcia Jose Garcia <jgarcia@ucar.edu> 00032 00033 #include "config.h" 00034 00035 #include <cstdlib> 00036 #include <iostream> 00037 #include <fstream> 00038 #include <sstream> 00039 #include <map> 00040 00041 using std::cout ; 00042 using std::endl ; 00043 using std::cerr ; 00044 using std::ofstream ; 00045 using std::ostringstream ; 00046 using std::ios ; 00047 using std::map ; 00048 00049 #ifdef HAVE_LIBREADLINE 00050 # if defined(HAVE_READLINE_READLINE_H) 00051 # include <readline/readline.h> 00052 # elif defined(HAVE_READLINE_H) 00053 # include <readline.h> 00054 # else /* !defined(HAVE_READLINE_H) */ 00055 extern "C" 00056 { 00057 char *readline( const char * ) ; 00058 } 00059 # endif /* !defined(HAVE_READLINE_H) */ 00060 char *cmdline = NULL ; 00061 #else /* !defined(HAVE_READLINE_READLINE_H) */ 00062 /* no readline */ 00063 #endif /* HAVE_LIBREADLINE */ 00064 00065 #ifdef HAVE_READLINE_HISTORY 00066 # if defined(HAVE_READLINE_HISTORY_H) 00067 # include <readline/history.h> 00068 # elif defined(HAVE_HISTORY_H) 00069 # include <history.h> 00070 # else /* !defined(HAVE_HISTORY_H) */ 00071 extern "C" 00072 { 00073 int add_history( const char * ) ; 00074 int write_history( const char * ) ; 00075 int read_history( const char * ) ; 00076 } 00077 # endif /* defined(HAVE_READLINE_HISTORY_H) */ 00078 /* no history */ 00079 #endif /* HAVE_READLINE_HISTORY */ 00080 00081 #include <libxml/encoding.h> 00082 00083 #define SIZE_COMMUNICATION_BUFFER 4096*4096 00084 #include "CmdClient.h" 00085 #include "CmdTranslation.h" 00086 #include "PPTClient.h" 00087 #include "BESDebug.h" 00088 #include "BESStopWatch.h" 00089 #include "BESError.h" 00090 00091 CmdClient::~CmdClient() 00092 { 00093 if( _strmCreated && _strm ) 00094 { 00095 _strm->flush() ; 00096 delete _strm ; 00097 _strm = 0 ; 00098 } 00099 else if( _strm ) 00100 { 00101 _strm->flush( ) ; 00102 } 00103 if( _client ) 00104 { 00105 delete _client ; 00106 _client = 0 ; 00107 } 00108 } 00109 00124 void 00125 CmdClient::startClient( const string & host, int portVal, int timeout ) 00126 { 00127 _client = new PPTClient( host, portVal, timeout ) ; 00128 _client->initConnection() ; 00129 } 00130 00140 void 00141 CmdClient::startClient( const string & unixStr, int timeout ) 00142 { 00143 _client = new PPTClient( unixStr, timeout ) ; 00144 _client->initConnection() ; 00145 } 00146 00155 void 00156 CmdClient::shutdownClient() 00157 { 00158 if( _client ) 00159 _client->closeConnection() ; 00160 } 00161 00178 void 00179 CmdClient::setOutput( ostream * strm, bool created ) 00180 { 00181 if( _strmCreated && _strm ) 00182 { 00183 _strm->flush() ; 00184 delete _strm ; 00185 } 00186 else if( _strm ) 00187 { 00188 _strm->flush() ; 00189 } 00190 _strm = strm ; 00191 _strmCreated = created ; 00192 } 00193 00205 bool 00206 CmdClient::executeClientCommand( const string & cmd ) 00207 { 00208 bool do_exit = false ; 00209 string suppress = "suppress" ; 00210 if( cmd.compare( 0, suppress.length(), suppress ) == 0 ) 00211 { 00212 setOutput( NULL, false ) ; 00213 return do_exit ; 00214 } 00215 00216 string output = "output to" ; 00217 if( cmd.compare( 0, output.length(), output ) == 0 ) 00218 { 00219 string subcmd = cmd.substr( output.length() + 1 ) ; 00220 string screen = "screen" ; 00221 if( subcmd.compare( 0, screen.length(), screen ) == 0 ) 00222 { 00223 setOutput( &cout, false ) ; 00224 } 00225 else 00226 { 00227 // subcmd is the name of the file - then semicolon 00228 string file = subcmd.substr( 0, subcmd.length() - 1 ) ; 00229 ofstream *fstrm = new ofstream( file.c_str(), ios::app ) ; 00230 if( fstrm && !(*fstrm) ) 00231 { 00232 delete fstrm ; 00233 cerr << "Unable to set client output to file " << file 00234 << endl ; 00235 } 00236 else 00237 { 00238 setOutput( fstrm, true ) ; 00239 } 00240 } 00241 return do_exit ; 00242 } 00243 00244 // load commands from an input file and run them 00245 string load = "load" ; 00246 if( cmd.compare( 0, load.length(), load ) == 0 ) 00247 { 00248 string file = cmd.substr( load.length() + 1, 00249 cmd.length() - load.length() - 2 ) ; 00250 ifstream fstrm( file.c_str() ) ; 00251 if( !fstrm ) 00252 { 00253 cerr << "Unable to load commands from file " << file 00254 << ": file does not exist or failed to open file" << endl ; 00255 } 00256 else 00257 { 00258 do_exit = executeCommands( fstrm, 1 ) ; 00259 } 00260 00261 return do_exit ; 00262 } 00263 00264 cerr << "Improper client command " << cmd << endl ; 00265 00266 return do_exit ; 00267 } 00268 00281 bool 00282 CmdClient::executeCommand( const string &cmd, int repeat ) 00283 { 00284 bool do_exit = false ; 00285 string client = "client" ; 00286 if( cmd.compare( 0, client.length(), client ) == 0 ) 00287 { 00288 do_exit = executeClientCommand( cmd.substr( client.length() + 1 ) ) ; 00289 } 00290 else 00291 { 00292 if( repeat < 1 ) repeat = 1 ; 00293 for( int i = 0; i < repeat && !do_exit; i++ ) 00294 { 00295 BESDEBUG( "cmdln", "cmdclient sending " << cmd << endl ) ; 00296 BESStopWatch *sw = 0 ; 00297 if( BESISDEBUG( "timing" ) ) 00298 { 00299 sw = new BESStopWatch() ; 00300 sw->start() ; 00301 } 00302 00303 map<string,string> extensions ; 00304 _client->send( cmd, extensions ) ; 00305 00306 BESDEBUG( "cmdln", "cmdclient receiving " << endl ) ; 00307 // keep reading till we get the last chunk, send to _strm 00308 bool done = false ; 00309 ostringstream *show_stream = 0 ; 00310 while( !done ) 00311 { 00312 if( CmdTranslation::is_show() ) 00313 { 00314 if( !show_stream ) 00315 { 00316 show_stream = new ostringstream ; 00317 } 00318 } 00319 if( show_stream ) 00320 { 00321 done = _client->receive( extensions, show_stream ) ; 00322 } 00323 else 00324 { 00325 done = _client->receive( extensions, _strm ) ; 00326 } 00327 if( extensions["status"] == "error" ) 00328 { 00329 // If there is an error, just flush what I have 00330 // and continue on. 00331 _strm->flush() ; 00332 00333 // let's also set show to true because we've gotten back 00334 // an xml document (maybe) 00335 if( _isInteractive ) 00336 { 00337 CmdTranslation::set_show( true ) ; 00338 } 00339 } 00340 if( extensions["exit"] == "true" ) 00341 { 00342 do_exit = true ; 00343 } 00344 } 00345 if( show_stream ) 00346 { 00347 *(_strm) << show_stream->str() << endl ; 00348 delete show_stream ; 00349 show_stream = 0 ; 00350 } 00351 if( BESDebug::IsSet( "cmdln" ) ) 00352 { 00353 BESDEBUG( "cmdln", "extensions:" << endl ) ; 00354 map<string,string>::const_iterator i = extensions.begin() ; 00355 map<string,string>::const_iterator e = extensions.end() ; 00356 for( ; i != e; i++ ) 00357 { 00358 BESDEBUG( "cmdln", " " << (*i).first << " = " 00359 << (*i).second << endl ) ; 00360 } 00361 BESDEBUG( "cmdln", "cmdclient done receiving " << endl ) ; 00362 } 00363 if( BESISDEBUG( "timing" ) ) 00364 { 00365 if( sw && sw->stop() ) 00366 { 00367 BESDEBUG( "timing", "cmdclient - executed \"" 00368 << cmd << "\" in " << sw->seconds() 00369 << " seconds and " << sw->microseconds() 00370 << " microseconds" << endl ) ; 00371 } 00372 else 00373 { 00374 BESDEBUG( "timing", "cmdclient - executed \"" << cmd 00375 << "\" - no timing available" 00376 << endl ) ; 00377 } 00378 } 00379 00380 _strm->flush() ; 00381 delete sw ; 00382 sw = 0 ; 00383 } 00384 } 00385 return do_exit ; 00386 } 00387 00405 bool 00406 CmdClient::executeCommands( const string &cmd_list, int repeat ) 00407 { 00408 bool do_exit = false ; 00409 _isInteractive = true ; 00410 if( repeat < 1 ) repeat = 1 ; 00411 00412 CmdTranslation::set_show( false ) ; 00413 try 00414 { 00415 string doc = CmdTranslation::translate( cmd_list ) ; 00416 if( !doc.empty() ) 00417 { 00418 do_exit = this->executeCommand( doc, repeat ) ; 00419 } 00420 } 00421 catch( BESError &e ) 00422 { 00423 CmdTranslation::set_show( false ) ; 00424 _isInteractive = false ; 00425 throw e ; 00426 } 00427 CmdTranslation::set_show( false ) ; 00428 _isInteractive = false ; 00429 return do_exit ; 00430 } 00431 00450 bool 00451 CmdClient::executeCommands( ifstream & istrm, int repeat ) 00452 { 00453 bool do_exit = false ; 00454 _isInteractive = false ; 00455 if( repeat < 1 ) repeat = 1 ; 00456 for( int i = 0; i < repeat; i++ ) 00457 { 00458 istrm.clear( ) ; 00459 istrm.seekg( 0, ios::beg ) ; 00460 string cmd ; 00461 while( !istrm.eof() ) 00462 { 00463 char line[4096] ; 00464 line[0] = '\0' ; 00465 istrm.getline( line, 4096, '\n' ) ; 00466 cmd += line ; 00467 } 00468 do_exit = this->executeCommand( cmd, 1 ) ; 00469 } 00470 return do_exit ; 00471 } 00472 00492 bool 00493 CmdClient::interact() 00494 { 00495 bool do_exit = false ; 00496 _isInteractive = true ; 00497 00498 cout << endl << endl 00499 << "Type 'exit' to exit the command line client and 'help' or '?' " 00500 << "to display the help screen" << endl << endl ; 00501 00502 bool done = false ; 00503 while( !done && !do_exit ) 00504 { 00505 string message = "" ; 00506 size_t len = this->readLine( message ) ; 00507 if( len == -1 || message == "exit" || message == "exit;" ) 00508 { 00509 done = true ; 00510 } 00511 else if( message == "help" || message == "help;" || message == "?" ) 00512 { 00513 this->displayHelp() ; 00514 } 00515 else if( message.length() > 6 && message.substr( 0, 6 ) == "client" ) 00516 { 00517 do_exit = this->executeCommand( message, 1 ) ; 00518 } 00519 else if( len != 0 && message != "" ) 00520 { 00521 CmdTranslation::set_show( false ) ; 00522 try 00523 { 00524 string doc = CmdTranslation::translate( message ) ; 00525 if( !doc.empty() ) 00526 { 00527 do_exit = this->executeCommand( doc, 1 ) ; 00528 } 00529 } 00530 catch( BESError &e ) 00531 { 00532 CmdTranslation::set_show( false ) ; 00533 _isInteractive = false ; 00534 throw e ; 00535 } 00536 CmdTranslation::set_show( false ) ; 00537 } 00538 } 00539 _isInteractive = false ; 00540 00541 return do_exit ; 00542 } 00543 00549 size_t 00550 CmdClient::readLine( string &msg ) 00551 { 00552 size_t len = 0 ; 00553 char *buf = (char *) NULL ; 00554 buf =::readline( "BESClient> " ) ; 00555 if( buf && *buf ) 00556 { 00557 len = strlen( buf ) ; 00558 #ifdef HAVE_READLINE_HISTORY 00559 add_history( buf ) ; 00560 #endif 00561 if( len > SIZE_COMMUNICATION_BUFFER ) 00562 { 00563 cerr << __FILE__ << __LINE__ 00564 << 00565 ": incoming data buffer exceeds maximum capacity with lenght " 00566 << len << endl ; 00567 exit( 1 ) ; 00568 } 00569 else { 00570 msg = buf ; 00571 } 00572 } 00573 else { 00574 if( !buf ) 00575 { 00576 // If a null buffer is returned then this means that EOF is 00577 // returned. This is different from the user just hitting enter, 00578 // which means a character buffer is returned, but is empty. 00579 00580 // Problem: len is unsigned. 00581 len = -1 ; 00582 } 00583 } 00584 if( buf ) 00585 { 00586 free( buf ) ; 00587 buf = (char *)NULL ; 00588 } 00589 return len ; 00590 } 00591 00594 void 00595 CmdClient::displayHelp() 00596 { 00597 cout << endl ; 00598 cout << endl ; 00599 cout << "BES Command Line Client Help" << endl ; 00600 cout << endl ; 00601 cout << "Client commands available:" << endl ; 00602 cout << 00603 " exit - exit the command line interface" << 00604 endl ; 00605 cout << " help - display this help screen" << 00606 endl ; 00607 cout << 00608 " client suppress; - suppress output from the server" << 00609 endl ; 00610 cout << 00611 " client output to screen; - display server output to the screen" 00612 << endl ; 00613 cout << 00614 " client output to <file>; - display server output to specified file" 00615 << endl ; 00616 cout << 00617 " client load <file>; - load xml document from file" 00618 << endl ; 00619 cout << endl ; 00620 cout << 00621 "Any commands beginning with 'client' must end with a semicolon" << 00622 endl ; 00623 cout << endl ; 00624 cout << "To display the list of commands available from the server " 00625 << "please type the command 'show help;'" << endl ; 00626 cout << endl ; 00627 cout << endl ; 00628 } 00629 00634 bool 00635 CmdClient::isConnected() 00636 { 00637 if( _client ) 00638 return _client->isConnected() ; 00639 return false ; 00640 } 00641 00644 void 00645 CmdClient::brokenPipe() 00646 { 00647 if( _client ) 00648 _client->brokenPipe() ; 00649 } 00650 00657 void 00658 CmdClient::dump( ostream & strm ) const 00659 { 00660 strm << BESIndent::LMarg << "CmdClient::dump - (" 00661 << (void *) this << ")" << endl ; 00662 BESIndent::Indent() ; 00663 if( _client ) 00664 { 00665 strm << BESIndent::LMarg << "client:" << endl ; 00666 BESIndent::Indent() ; 00667 _client->dump( strm ) ; 00668 BESIndent::UnIndent() ; 00669 } 00670 else 00671 { 00672 strm << BESIndent::LMarg << "client: null" << endl ; 00673 } 00674 strm << BESIndent::LMarg << "stream: " << (void *) _strm << endl ; 00675 strm << BESIndent::LMarg << "stream created? " << _strmCreated << endl ; 00676 BESIndent::UnIndent() ; 00677 }