CmdApp.cc

Go to the documentation of this file.
00001 // ClientMain.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,2005 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 Atmostpheric 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 <unistd.h>
00034 #include <signal.h>
00035 
00036 #include <iostream>
00037 #include <string>
00038 #include <fstream>
00039 
00040 using std::cout ;
00041 using std::cerr ;
00042 using std::endl ;
00043 using std::flush ;
00044 using std::string ;
00045 using std::ofstream ;
00046 
00047 #include "CmdApp.h"
00048 #include "CmdClient.h"
00049 #include "PPTException.h"
00050 #include "BESDebug.h"
00051 
00052 #define BES_CMDLN_DEFAULT_TIMEOUT 5
00053 
00054 CmdApp::CmdApp()
00055     : BESBaseApp(),
00056       _client( 0 ),
00057       _hostStr( "" ),
00058       _unixStr( "" ),
00059       _portVal( 0 ),
00060       _outputStrm( 0 ),
00061       _inputStrm( 0 ),
00062       _createdInputStrm( false ),
00063       _timeout( 0 ),
00064       _repeat( 0 )
00065 {
00066 }
00067 
00068 CmdApp::~CmdApp()
00069 {
00070     if( _client )
00071     {
00072         delete _client ;
00073         _client = 0 ;
00074     }
00075 }
00076 
00077 void
00078 CmdApp::showVersion()
00079 {
00080     cout << appName() << ": version 2.0" << endl ;
00081 }
00082 
00083 void
00084 CmdApp::showUsage( )
00085 {
00086     cout << endl ;
00087     cout << appName() << ": the following flags are available:" << endl ;
00088     cout << "    -h <host> - specifies a host for TCP/IP connection" << endl ;
00089     cout << "    -p <port> - specifies a port for TCP/IP connection" << endl ;
00090     cout << "    -u <unixSocket> - specifies a unix socket for connection. " << endl ;
00091     cout << "    -x <command> - specifies a command for the server to execute" << endl ;
00092     cout << "    -i <inputFile> - specifies a file name for a sequence of input commands" << endl ;
00093     cout << "    -f <outputFile> - specifies a file name to output the results of the input" << endl ;
00094     cout << "    -t <timeoutVal> - specifies an optional timeout value in seconds" << endl ;
00095     cout << "    -d - sets the optional debug flag for the client session" << endl ;
00096     cout << "    -r <num> - repeat the command(s) num times" << endl ;
00097     cout << "    -? - display this list of flags" << endl ;
00098 }
00099 
00100 void
00101 CmdApp::signalCannotConnect( int sig )
00102 {
00103     if( sig == SIGCONT )
00104     {
00105         CmdApp *app = dynamic_cast<CmdApp *>(BESApp::TheApplication()) ;
00106         if( app )
00107         {
00108             CmdClient *client = app->client() ;
00109             if( client && !client->isConnected() )
00110             {
00111                 cout << BESApp::TheApplication()->appName()
00112                      << ": No response, server may be down or "
00113                      << "busy with another incoming connection. exiting!\n" ;
00114                 exit( 1 ) ;
00115             }
00116         }
00117     }
00118 }
00119 
00120 void
00121 CmdApp::signalInterrupt( int sig )
00122 {
00123     if( sig == SIGINT )
00124     {
00125         cout << BESApp::TheApplication()->appName()
00126              << ": Please type exit to terminate the session" << endl ;
00127     }
00128     if( signal( SIGINT, CmdApp::signalInterrupt ) == SIG_ERR )
00129     {
00130         cerr << BESApp::TheApplication()->appName()
00131              << ": Could not re-register signal\n" ;
00132     }
00133 }
00134 
00135 void
00136 CmdApp::signalTerminate( int sig )
00137 {
00138     if( sig == SIGTERM )
00139     {
00140         cout << BESApp::TheApplication()->appName()
00141              << ": Please type exit to terminate the session" << endl ;
00142     }
00143     if( signal( SIGTERM, CmdApp::signalTerminate ) == SIG_ERR )
00144     {
00145         cerr << BESApp::TheApplication()->appName()
00146              << ": Could not re-register signal\n" ;
00147     }
00148 }
00149 
00150 void
00151 CmdApp::signalBrokenPipe( int sig )
00152 {
00153     if( sig == SIGPIPE )
00154     {
00155         cout << BESApp::TheApplication()->appName()
00156              << ": got a broken pipe, server may be down or the port invalid."
00157              << endl
00158              << "Please check parameters and try again" << endl ;
00159         CmdApp *app = dynamic_cast<CmdApp *>(BESApp::TheApplication()) ;
00160         if( app )
00161         {
00162           CmdClient *client = app->client() ;
00163           if( client )
00164             {
00165               client->brokenPipe() ;
00166               client->shutdownClient() ;
00167               delete client;
00168               client = 0;
00169             }
00170         }
00171         exit( 1 ) ;
00172     }
00173 }
00174 
00175 void
00176 CmdApp::registerSignals()
00177 {
00178     // Registering SIGCONT for connection unblocking
00179     BESDEBUG( "CmdApp: Registering signal SIGCONT ... " )
00180     if( signal( SIGCONT, signalCannotConnect ) == SIG_ERR )
00181     {
00182         BESDEBUG( "FAILED" << endl ) ;
00183         cerr << appName() << "Failed to register signal SIGCONT" << endl ;
00184         exit( 1 ) ;
00185     }
00186     BESDEBUG( "OK" << endl ) ;
00187 
00188     // Registering SIGINT to disable Ctrl-C from the user in order to avoid
00189     // server instability
00190     BESDEBUG( "CmdApp: Registering signal SIGINT ... " )
00191     if( signal( SIGINT, signalInterrupt ) == SIG_ERR )
00192     {
00193         BESDEBUG( "FAILED" << endl ) ;
00194         cerr << appName() << "Failed to register signal SIGINT" << endl ;
00195         exit( 1 ) ;
00196     }
00197     BESDEBUG( "OK" << endl ) ;
00198 
00199     // Registering SIGTERM to disable kill from the user in order to avoid
00200     // server instability
00201     BESDEBUG( "CmdApp: Registering signal SIGTERM ... " )
00202     if( signal( SIGTERM, signalTerminate ) == SIG_ERR )
00203     {
00204         BESDEBUG( "FAILED" << endl ) ;
00205         cerr << appName() << "Failed to register signal SIGTERM" << endl ;
00206         exit( 1 ) ;
00207     }
00208     BESDEBUG( "OK" << endl ) ;
00209 
00210     // Registering SIGPIE for broken pipes managment.
00211     BESDEBUG( "CmdApp: Registering signal SIGPIPE ... " )
00212     if( signal( SIGPIPE, CmdApp::signalBrokenPipe ) == SIG_ERR )
00213     {
00214         BESDEBUG( "FAILED" << endl ) ;
00215         cerr << appName() << "Failed to register signal SIGPIPE" << endl ;
00216         exit( 1 ) ;
00217     }
00218     BESDEBUG( "OK" << endl ) ;
00219 }
00220 
00221 int
00222 CmdApp::initialize( int argc, char **argv )
00223 {
00224     int retVal = BESBaseApp::initialize( argc, argv ) ;
00225     if( retVal != 0 )
00226         return retVal ;
00227 
00228     string portStr = "" ;
00229     string outputStr = "" ;
00230     string inputStr = "" ;
00231     string timeoutStr = "" ;
00232     string repeatStr = "" ;
00233 
00234     bool badUsage = false ;
00235 
00236     int c ;
00237 
00238     while( ( c = getopt( argc, argv, "?vd:h:p:t:u:x:f:i:r:" ) ) != EOF )
00239     {
00240         switch( c )
00241         {
00242             case 't':
00243                 timeoutStr = optarg ;
00244                 break ;
00245             case 'h':
00246                 _hostStr = optarg ;
00247                 break ;
00248             case 'd':
00249                 {
00250                     string dbgstrm = optarg ;
00251                     if( dbgstrm == "cerr" )
00252                     {
00253                         BESDebug::Set_debugger( new BESDebug( &cerr ) ) ;
00254                     }
00255                     else
00256                     {
00257                         ostream *fstrm = new ofstream( dbgstrm.c_str() ) ;
00258                         if( !(*fstrm) )
00259                         {
00260                             cerr << "Unable to open debug file" << endl ;
00261                             showUsage() ;
00262                             return 1 ;
00263                         }
00264                         BESDebug::Set_debugger( new BESDebug( fstrm ) ) ;
00265                     }
00266                     BESDebug::Begin_debug() ;
00267                 }
00268                 break ;
00269             case 'v':
00270                 {
00271                     showVersion() ;
00272                     exit( 0 ) ;
00273                 }
00274                 break ;
00275             case 'p':
00276                 portStr = optarg ;
00277                 break ;  
00278             case 'u':
00279                 _unixStr = optarg ;
00280                 break ;
00281             case 'x':
00282                 _cmd = optarg ;
00283                 break ;
00284             case 'f':
00285                 outputStr = optarg ;
00286                 break ;
00287             case 'i':
00288                 inputStr = optarg ;
00289                 break ;
00290             case 'r':
00291                 repeatStr = optarg ;
00292                 break ;
00293             case '?':
00294                 {
00295                     showUsage() ;
00296                     exit( 0 ) ;
00297                 }
00298                 break ;
00299         }
00300     }
00301     if( _hostStr == "" && _unixStr == "" )
00302     {
00303         cerr << "host/port or unix socket must be specified" << endl ;
00304         badUsage = true ;
00305     }
00306 
00307     if( _hostStr != "" && _unixStr != "" )
00308     {
00309         cerr << "must specify either a host and port or a unix socket" << endl ;
00310         badUsage = true ;
00311     }
00312 
00313     if( portStr != "" && _unixStr != "" )
00314     {
00315         cerr << "must specify either a host and port or a unix socket" << endl ;
00316         badUsage = true ;
00317     }
00318 
00319     if( _hostStr != "" )
00320     {
00321         if( portStr == "" )
00322         {
00323             cout << "port must be specified when specifying a host" << endl ;
00324             badUsage = true ;
00325         }
00326         else
00327         {
00328             _portVal = atoi( portStr.c_str() ) ;
00329         }
00330     }
00331 
00332     if( timeoutStr != "" )
00333     {
00334         _timeout = atoi( timeoutStr.c_str() ) ;
00335     }
00336     else
00337     {
00338         _timeout = BES_CMDLN_DEFAULT_TIMEOUT ;
00339     }
00340 
00341     if( outputStr != "" )
00342     {
00343         if( _cmd == "" && inputStr == "" )
00344         {
00345             cerr << "When specifying an output file you must either "
00346                  << "specify a command or an input file"
00347                  << endl ;
00348             badUsage = true ;
00349         }
00350         else if( _cmd != "" && inputStr != "" )
00351         {
00352             cerr << "You must specify either a command or an input file on "
00353                  << "the command line, not both"
00354                  << endl ;
00355             badUsage = true ;
00356         }
00357     }
00358 
00359     if( badUsage == true )
00360     {
00361         showUsage( ) ;
00362         return 1 ;
00363     }
00364 
00365     if( outputStr != "" )
00366     {
00367         _outputStrm = new ofstream( outputStr.c_str() ) ;
00368         if( !(*_outputStrm) )
00369         {
00370             cerr << "could not open the output file " << outputStr << endl ;
00371             badUsage = true ;
00372         }
00373     }
00374 
00375     if( inputStr != "" )
00376     {
00377         _inputStrm = new ifstream( inputStr.c_str() ) ;
00378         if( !(*_inputStrm) )
00379         {
00380             cerr << "could not open the input file " << inputStr << endl ;
00381             badUsage = true ;
00382         }
00383         _createdInputStrm = true ;
00384     }
00385 
00386     if( !repeatStr.empty() )
00387     {
00388         _repeat = atoi( repeatStr.c_str() ) ;
00389         if( !_repeat && repeatStr != "0" )
00390         {
00391             cerr << "repeat number invalid: " << repeatStr << endl ;
00392             badUsage = true ;
00393         }
00394         if( !_repeat )
00395         {
00396             _repeat = 1 ;
00397         }
00398     }
00399 
00400     if( badUsage == true )
00401     {
00402         showUsage( ) ;
00403         return 1 ;
00404     }
00405 
00406     registerSignals() ;
00407 
00408     BESDEBUG( "CmdApp: initialized settings:" << endl << *this ) ;
00409 
00410     return 0 ;
00411 }
00412 
00413 int
00414 CmdApp::run()
00415 {
00416     try
00417     {
00418         _client = new CmdClient( ) ;
00419         if( _hostStr != "" )
00420         {
00421             BESDEBUG( "CmdApp: Connecting to host: " << _hostStr
00422                       << " at port: " << _portVal << " ... " ) ;
00423             _client->startClient( _hostStr, _portVal, _timeout ) ;
00424         }
00425         else
00426         {
00427             BESDEBUG( "CmdApp: Connecting to unix socket: " << _unixStr
00428                       << " ... " ) ;
00429             _client->startClient( _unixStr, _timeout ) ;
00430         }
00431 
00432         if( _outputStrm )
00433         {
00434             _client->setOutput( _outputStrm, true ) ;
00435         }
00436         else
00437         {
00438             _client->setOutput( &cout, false ) ;
00439         }
00440         BESDEBUG( "OK" << endl ) ;
00441     }
00442     catch( PPTException &e )
00443     {
00444         if( _client )
00445         {
00446             _client->shutdownClient() ;
00447             delete _client ;
00448             _client = 0 ;
00449         }
00450         BESDEBUG( "FAILED" << endl ) ;
00451         cerr << "error starting the client" << endl ;
00452         cerr << e.getMessage() << endl ;
00453         exit( 1 ) ;
00454     }
00455 
00456     try
00457     {
00458         if( _cmd != "" )
00459         {
00460             _client->executeCommands( _cmd, _repeat ) ;
00461         }
00462         else if( _inputStrm )
00463         {
00464             _client->executeCommands( *_inputStrm, _repeat ) ;
00465         }
00466         else
00467         {
00468             _client->interact() ;
00469         }
00470     }
00471     catch( PPTException &e )
00472     {
00473         cerr << "error processing commands" << endl ;
00474         cerr << e.getMessage() << endl ;
00475     }
00476 
00477     try
00478     {
00479         BESDEBUG( "CmdApp: shutting down client ... " ) ;
00480         if( _client )
00481         {
00482             _client->shutdownClient() ;
00483             delete _client ;
00484             _client = 0 ;
00485         }
00486         BESDEBUG( "OK" << endl ) ;
00487 
00488         BESDEBUG( "CmdApp: closing input stream ... " ) ;
00489         if( _createdInputStrm )
00490         {
00491             _inputStrm->close() ;
00492             delete _inputStrm ;
00493             _inputStrm = 0 ;
00494         }
00495         BESDEBUG( "OK" << endl ) ;
00496     }
00497     catch( PPTException &e )
00498     {
00499         BESDEBUG( "FAILED" << endl ) ;
00500         cerr << "error closing the client" << endl ;
00501         cerr << e.getMessage() << endl ;
00502         return 1 ;
00503     }
00504 
00505     return 0 ;
00506 }
00507 
00514 void
00515 CmdApp::dump( ostream &strm ) const
00516 {
00517     strm << BESIndent::LMarg << "CmdApp::dump - ("
00518                              << (void *)this << ")" << endl ;
00519     BESIndent::Indent() ;
00520     if( _client )
00521     {
00522         strm << BESIndent::LMarg << "client: " << endl ;
00523         BESIndent::Indent() ;
00524         _client->dump( strm ) ;
00525         BESIndent::UnIndent() ;
00526     }
00527     else
00528     {
00529         strm << BESIndent::LMarg << "client: null" << endl ;
00530     }
00531     strm << BESIndent::LMarg << "host: " << _hostStr << endl ;
00532     strm << BESIndent::LMarg << "unix socket: " << _unixStr << endl ;
00533     strm << BESIndent::LMarg << "port: " << _portVal << endl ;
00534     strm << BESIndent::LMarg << "command: " << _cmd << endl ;
00535     strm << BESIndent::LMarg << "output stream: " << (void *)_outputStrm << endl ;
00536     strm << BESIndent::LMarg << "input stream: " << (void *)_inputStrm << endl ;
00537     strm << BESIndent::LMarg << "created input stream? " << _createdInputStrm << endl ;
00538     strm << BESIndent::LMarg << "timeout: " << _timeout << endl ;
00539     BESBaseApp::dump( strm ) ;
00540     BESIndent::UnIndent() ;
00541 }
00542 
00543 int
00544 main( int argc, char **argv )
00545 {
00546     CmdApp app ;
00547     return app.main( argc, argv ) ;
00548 }
00549 

Generated on Wed Aug 29 03:14:16 2007 for OPeNDAP Back End Server (BES) by  doxygen 1.5.2