OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
CmdApp.cc
Go to the documentation of this file.
1 // ClientMain.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include "config.h"
34 
35 #include <signal.h>
36 
37 #include <cstdlib>
38 #include <iostream>
39 #include <string>
40 #include <fstream>
41 
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45 
46 using std::cout ;
47 using std::cerr ;
48 using std::endl ;
49 using std::flush ;
50 using std::string ;
51 using std::ofstream ;
52 
53 #include "CmdApp.h"
54 #include "CmdClient.h"
55 #include "CmdTranslation.h"
56 #include "BESError.h"
57 #include "BESDebug.h"
58 
59 #define BES_CMDLN_DEFAULT_TIMEOUT 5
60 
62  : BESBaseApp(),
63  _client( 0 ),
64  _hostStr( "localhost" ),
65  _unixStr( "" ),
66  _portVal( 10002 ),
67  _outputStrm( 0 ),
68  _inputStrm( 0 ),
69  _createdInputStrm( false ),
70  _timeout( 0 ),
71  _repeat( 0 )
72 {
73 }
74 
76 {
77  if( _client )
78  {
79  delete _client ;
80  _client = 0 ;
81  }
82 }
83 
84 void
85 CmdApp::showVersion()
86 {
87  cout << appName() << ": version 2.0" << endl ;
88 }
89 
90 void
91 CmdApp::showUsage( )
92 {
93  cout << endl ;
94  cout << appName() << ": the following flags are available:" << endl ;
95  cout << " -h <host> - specifies a host for TCP/IP connection" << endl ;
96  cout << " -p <port> - specifies a port for TCP/IP connection" << endl ;
97  cout << " -u <unixSocket> - specifies a unix socket for connection. " << endl ;
98  cout << " -x <command> - specifies a command for the server to execute" << endl ;
99  cout << " -i <inputFile> - specifies a file name for a sequence of input commands" << endl ;
100  cout << " -f <outputFile> - specifies a file name to output the results of the input" << endl ;
101  cout << " -t <timeoutVal> - specifies an optional timeout value in seconds" << endl ;
102  cout << " -d - sets the optional debug flag for the client session" << endl ;
103  cout << " -r <num> - repeat the command(s) num times" << endl ;
104  cout << " -? - display this list of flags" << endl ;
105  cout << endl ;
106  BESDebug::Help( cout ) ;
107 }
108 
109 void
111 {
112  if( sig == SIGCONT )
113  {
114  CmdApp *app = dynamic_cast<CmdApp *>(BESApp::TheApplication()) ;
115  if( app )
116  {
117  CmdClient *client = app->client() ;
118  if( client && !client->isConnected() )
119  {
120  cout << BESApp::TheApplication()->appName()
121  << ": No response, server may be down or "
122  << "busy with another incoming connection. exiting!\n" ;
123  exit( 1 ) ;
124  }
125  }
126  }
127 }
128 
129 void
131 {
132  if( sig == SIGINT )
133  {
134  cout << BESApp::TheApplication()->appName()
135  << ": Please type exit to terminate the session" << endl ;
136  }
137  if( signal( SIGINT, CmdApp::signalInterrupt ) == SIG_ERR )
138  {
139  cerr << BESApp::TheApplication()->appName()
140  << ": Could not re-register signal\n" ;
141  }
142 }
143 
144 void
146 {
147  if( sig == SIGTERM )
148  {
149  cout << BESApp::TheApplication()->appName()
150  << ": Please type exit to terminate the session" << endl ;
151  }
152  if( signal( SIGTERM, CmdApp::signalTerminate ) == SIG_ERR )
153  {
154  cerr << BESApp::TheApplication()->appName()
155  << ": Could not re-register signal\n" ;
156  }
157 }
158 
159 void
161 {
162  if( sig == SIGPIPE )
163  {
164  cout << BESApp::TheApplication()->appName()
165  << ": got a broken pipe, server may be down or the port invalid."
166  << endl
167  << "Please check parameters and try again" << endl ;
168  CmdApp *app = dynamic_cast<CmdApp *>(BESApp::TheApplication()) ;
169  if( app )
170  {
171  CmdClient *client = app->client() ;
172  if( client )
173  {
174  client->brokenPipe() ;
175  client->shutdownClient() ;
176  delete client;
177  client = 0;
178  }
179  }
180  exit( 1 ) ;
181  }
182 }
183 
184 void
185 CmdApp::registerSignals()
186 {
187  // Registering SIGCONT for connection unblocking
188  BESDEBUG( "cmdln", "CmdApp: Registering signal SIGCONT ... " << endl ) ;
189  if( signal( SIGCONT, signalCannotConnect ) == SIG_ERR )
190  {
191  BESDEBUG( "cmdln", "FAILED" << endl ) ;
192  cerr << appName() << "Failed to register signal SIGCONT" << endl ;
193  exit( 1 ) ;
194  }
195  BESDEBUG( "cmdln", "OK" << endl ) ;
196 
197  // Registering SIGINT to disable Ctrl-C from the user in order to avoid
198  // server instability
199  BESDEBUG( "cmdln", "CmdApp: Registering signal SIGINT ... " << endl ) ;
200  if( signal( SIGINT, signalInterrupt ) == SIG_ERR )
201  {
202  BESDEBUG( "cmdln", "FAILED" << endl ) ;
203  cerr << appName() << "Failed to register signal SIGINT" << endl ;
204  exit( 1 ) ;
205  }
206  BESDEBUG( "cmdln", "OK" << endl ) ;
207 
208  // Registering SIGTERM to disable kill from the user in order to avoid
209  // server instability
210  BESDEBUG( "cmdln", "CmdApp: Registering signal SIGTERM ... " << endl ) ;
211  if( signal( SIGTERM, signalTerminate ) == SIG_ERR )
212  {
213  BESDEBUG( "cmdln", "FAILED" << endl ) ;
214  cerr << appName() << "Failed to register signal SIGTERM" << endl ;
215  exit( 1 ) ;
216  }
217  BESDEBUG( "cmdln", "OK" << endl ) ;
218 
219  // Registering SIGPIE for broken pipes managment.
220  BESDEBUG( "cmdln", "CmdApp: Registering signal SIGPIPE ... " << endl ) ;
221  if( signal( SIGPIPE, CmdApp::signalBrokenPipe ) == SIG_ERR )
222  {
223  BESDEBUG( "cmdln", "FAILED" << endl ) ;
224  cerr << appName() << "Failed to register signal SIGPIPE" << endl ;
225  exit( 1 ) ;
226  }
227  BESDEBUG( "cmdln", "OK" << endl ) ;
228 }
229 
230 int
231 CmdApp::initialize( int argc, char **argv )
232 {
233  int retVal = BESBaseApp::initialize( argc, argv ) ;
234  if( retVal != 0 )
235  return retVal ;
236 
237  CmdTranslation::initialize( argc, argv ) ;
238 
239  string portStr = "" ;
240  string outputStr = "" ;
241  string inputStr = "" ;
242  string timeoutStr = "" ;
243  string repeatStr = "" ;
244 
245  bool badUsage = false ;
246 
247  int c ;
248 
249  while( ( c = getopt( argc, argv, "?vd:h:p:t:u:x:f:i:r:" ) ) != EOF )
250  {
251  switch( c )
252  {
253  case 't':
254  timeoutStr = optarg ;
255  break ;
256  case 'h':
257  _hostStr = optarg ;
258  break ;
259  case 'd':
260  BESDebug::SetUp( optarg ) ;
261  break ;
262  case 'v':
263  {
264  showVersion() ;
265  exit( 0 ) ;
266  }
267  break ;
268  case 'p':
269  portStr = optarg ;
270  break ;
271  case 'u':
272  _unixStr = optarg ;
273  break ;
274  case 'x':
275  _cmd = optarg ;
276  break ;
277  case 'f':
278  outputStr = optarg ;
279  break ;
280  case 'i':
281  inputStr = optarg ;
282  break ;
283  case 'r':
284  repeatStr = optarg ;
285  break ;
286  case '?':
287  {
288  showUsage() ;
289  exit( 0 ) ;
290  }
291  break ;
292  }
293  }
294  if( _hostStr == "" && _unixStr == "" )
295  {
296  cerr << "host/port or unix socket must be specified" << endl ;
297  badUsage = true ;
298  }
299 
300  if( _hostStr != "" && _unixStr != "" )
301  {
302  cerr << "must specify either a host and port or a unix socket" << endl ;
303  badUsage = true ;
304  }
305 
306  if( portStr != "" && _unixStr != "" )
307  {
308  cerr << "must specify either a host and port or a unix socket" << endl ;
309  badUsage = true ;
310  }
311 
312  if( _hostStr != "" )
313  {
314  if( portStr == "" )
315  {
316  cout << "port must be specified when specifying a host" << endl ;
317  badUsage = true ;
318  }
319  else
320  {
321  _portVal = atoi( portStr.c_str() ) ;
322  }
323  }
324 
325  if( timeoutStr != "" )
326  {
327  _timeout = atoi( timeoutStr.c_str() ) ;
328  }
329  else
330  {
331  _timeout = BES_CMDLN_DEFAULT_TIMEOUT ;
332  }
333 
334  if( outputStr != "" )
335  {
336  if( _cmd == "" && inputStr == "" )
337  {
338  cerr << "When specifying an output file you must either "
339  << "specify a command or an input file"
340  << endl ;
341  badUsage = true ;
342  }
343  else if( _cmd != "" && inputStr != "" )
344  {
345  cerr << "You must specify either a command or an input file on "
346  << "the command line, not both"
347  << endl ;
348  badUsage = true ;
349  }
350  }
351 
352  if( badUsage == true )
353  {
354  showUsage( ) ;
355  return 1 ;
356  }
357 
358  if( outputStr != "" )
359  {
360  _outputStrm = new ofstream( outputStr.c_str() ) ;
361  if( !(*_outputStrm) )
362  {
363  cerr << "could not open the output file " << outputStr << endl ;
364  badUsage = true ;
365  }
366  }
367 
368  if( inputStr != "" )
369  {
370  _inputStrm = new ifstream( inputStr.c_str() ) ;
371  if( !(*_inputStrm) )
372  {
373  cerr << "could not open the input file " << inputStr << endl ;
374  badUsage = true ;
375  }
376  _createdInputStrm = true ;
377  }
378 
379  if( !repeatStr.empty() )
380  {
381  _repeat = atoi( repeatStr.c_str() ) ;
382  if( !_repeat && repeatStr != "0" )
383  {
384  cerr << "repeat number invalid: " << repeatStr << endl ;
385  badUsage = true ;
386  }
387  if( !_repeat )
388  {
389  _repeat = 1 ;
390  }
391  }
392 
393  if( badUsage == true )
394  {
395  showUsage( ) ;
396  return 1 ;
397  }
398 
399  registerSignals() ;
400 
401  BESDEBUG( "cmdln", "CmdApp: initialized settings:" << endl << *this ) ;
402 
403  return 0 ;
404 }
405 
406 int
408 {
409  try
410  {
411  _client = new CmdClient( ) ;
412  if( _hostStr != "" )
413  {
414  BESDEBUG( "cmdln", "CmdApp: Connecting to host: " << _hostStr
415  << " at port: " << _portVal << " ... " << endl ) ;
416  _client->startClient( _hostStr, _portVal, _timeout ) ;
417  }
418  else
419  {
420  BESDEBUG( "cmdln", "CmdApp: Connecting to unix socket: "
421  << _unixStr << " ... " << endl ) ;
422  _client->startClient( _unixStr, _timeout ) ;
423  }
424 
425  if( _outputStrm )
426  {
427  _client->setOutput( _outputStrm, true ) ;
428  }
429  else
430  {
431  _client->setOutput( &cout, false ) ;
432  }
433  BESDEBUG( "cmdln", "OK" << endl ) ;
434  }
435  catch( BESError &e )
436  {
437  if( _client )
438  {
439  _client->shutdownClient() ;
440  delete _client ;
441  _client = 0 ;
442  }
443  BESDEBUG( "cmdln", "FAILED" << endl ) ;
444  cerr << "error starting the client" << endl ;
445  cerr << e.get_message() << endl ;
446  exit( 1 ) ;
447  }
448 
449  bool do_exit = false ;
450  try
451  {
452  if( _cmd != "" )
453  {
454  do_exit = _client->executeCommands( _cmd, _repeat ) ;
455  }
456  else if( _inputStrm )
457  {
458  do_exit = _client->executeCommands( *_inputStrm, _repeat ) ;
459  }
460  else
461  {
462  do_exit = _client->interact() ;
463  }
464  }
465  catch( BESError &e )
466  {
467  cerr << "error processing commands" << endl ;
468  cerr << e.get_message() << endl ;
469  }
470 
471  try
472  {
473  BESDEBUG( "cmdln", "CmdApp: shutting down client ... " << endl ) ;
474  if( _client )
475  {
476  // if do_exit is set, then the client has shut itself down
477  if( !do_exit )
478  _client->shutdownClient() ;
479  delete _client ;
480  _client = 0 ;
481  }
482  BESDEBUG( "cmdln", "OK" << endl ) ;
483 
484  BESDEBUG( "cmdln", "CmdApp: closing input stream ... " << endl ) ;
485  if( _createdInputStrm )
486  {
487  _inputStrm->close() ;
488  delete _inputStrm ;
489  _inputStrm = 0 ;
490  }
491  BESDEBUG( "cmdln", "OK" << endl ) ;
492  }
493  catch( BESError &e )
494  {
495  BESDEBUG( "cmdln", "FAILED" << endl ) ;
496  cerr << "error closing the client" << endl ;
497  cerr << e.get_message() << endl ;
498  return 1 ;
499  }
500 
501  return 0 ;
502 }
503 
510 void
511 CmdApp::dump( ostream &strm ) const
512 {
513  strm << BESIndent::LMarg << "CmdApp::dump - ("
514  << (void *)this << ")" << endl ;
516  if( _client )
517  {
518  strm << BESIndent::LMarg << "client: " << endl ;
520  _client->dump( strm ) ;
522  }
523  else
524  {
525  strm << BESIndent::LMarg << "client: null" << endl ;
526  }
527  strm << BESIndent::LMarg << "host: " << _hostStr << endl ;
528  strm << BESIndent::LMarg << "unix socket: " << _unixStr << endl ;
529  strm << BESIndent::LMarg << "port: " << _portVal << endl ;
530  strm << BESIndent::LMarg << "command: " << _cmd << endl ;
531  strm << BESIndent::LMarg << "output stream: " << (void *)_outputStrm << endl ;
532  strm << BESIndent::LMarg << "input stream: " << (void *)_inputStrm << endl ;
533  strm << BESIndent::LMarg << "created input stream? " << _createdInputStrm << endl ;
534  strm << BESIndent::LMarg << "timeout: " << _timeout << endl ;
535  BESBaseApp::dump( strm ) ;
537 }
538 
539 int
540 main( int argc, char **argv )
541 {
542  CmdApp app ;
543  return app.main( argc, argv ) ;
544 }
545