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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 
61 // We got tired of typing in this all the time... jhrg 10/30/13
62 #define DEFAULT_PORT 10022
63 #define DEFAULT_HOST "localhost"
64 
66  BESBaseApp(), _client(0), _hostStr(DEFAULT_HOST), _unixStr(""), _portVal(DEFAULT_PORT), _outputStrm(0), _inputStrm(
67  0), _createdInputStrm(false), _timeout(0), _repeat(0)
68 {
69 }
70 
72 {
73  if (_client) {
74  delete _client;
75  _client = 0;
76  }
77 }
78 
79 void CmdApp::showVersion()
80 {
81  cout << appName() << ": version 2.0" << endl;
82 }
83 
84 void CmdApp::showUsage()
85 {
86  cout << endl;
87  cout << appName() << ": the following flags are available:" << endl;
88  cout << " -h <host> - specifies a host for TCP/IP connection" << endl;
89  cout << " -p <port> - specifies a port for TCP/IP connection" << endl;
90  cout << " -u <unixSocket> - specifies a unix socket for connection. " << endl;
91  cout << " -x <command> - specifies a command for the server to execute" << endl;
92  cout << " -i <inputFile> - specifies a file name for a sequence of input commands" << endl;
93  cout << " -f <outputFile> - specifies a file name to output the results of the input" << endl;
94  cout << " -t <timeoutVal> - specifies an optional timeout value in seconds" << endl;
95  cout << " -d - sets the optional debug flag for the client session" << endl;
96  cout << " -r <num> - repeat the command(s) num times" << endl;
97  cout << " -? - display this list of flags" << endl;
98  cout << endl;
99  BESDebug::Help(cout);
100 }
101 
103 {
104  if (sig == SIGCONT) {
105  CmdApp *app = dynamic_cast<CmdApp *>(BESApp::TheApplication());
106  if (app) {
107  CmdClient *client = app->client();
108  if (client && !client->isConnected()) {
109  cout << BESApp::TheApplication()->appName() << ": No response, server may be down or "
110  << "busy with another incoming connection. exiting!\n";
111  exit(1);
112  }
113  }
114  }
115 }
116 
118 {
119  if (sig == SIGINT) {
120  cout << BESApp::TheApplication()->appName() << ": Please type exit to terminate the session" << endl;
121  }
122  if (signal(SIGINT, CmdApp::signalInterrupt) == SIG_ERR ) {
123  cerr << BESApp::TheApplication()->appName() << ": Could not re-register signal\n";
124  }
125 }
126 
128 {
129  if (sig == SIGTERM) {
130  cout << BESApp::TheApplication()->appName() << ": Please type exit to terminate the session" << endl;
131  }
132  if (signal(SIGTERM, CmdApp::signalTerminate) == SIG_ERR ) {
133  cerr << BESApp::TheApplication()->appName() << ": Could not re-register signal\n";
134  }
135 }
136 
138 {
139  if (sig == SIGPIPE) {
140  cout << BESApp::TheApplication()->appName() << ": got a broken pipe, server may be down or the port invalid."
141  << endl << "Please check parameters and try again" << endl;
142  CmdApp *app = dynamic_cast<CmdApp *>(BESApp::TheApplication());
143  if (app) {
144  CmdClient *client = app->client();
145  if (client) {
146  client->brokenPipe();
147  client->shutdownClient();
148  delete client;
149  client = 0;
150  }
151  }
152  exit(1);
153  }
154 }
155 
156 void CmdApp::registerSignals()
157 {
158  // Registering SIGCONT for connection unblocking
159  BESDEBUG( "cmdln", "CmdApp: Registering signal SIGCONT ... " << endl );
160  if( signal( SIGCONT, signalCannotConnect ) == SIG_ERR )
161  {
162  BESDEBUG( "cmdln", "FAILED" << endl );
163  cerr << appName() << "Failed to register signal SIGCONT" << endl;
164  exit( 1 );
165  }
166  BESDEBUG( "cmdln", "OK" << endl );
167 
168  // Registering SIGINT to disable Ctrl-C from the user in order to avoid
169  // server instability
170  BESDEBUG( "cmdln", "CmdApp: Registering signal SIGINT ... " << endl );
171  if( signal( SIGINT, signalInterrupt ) == SIG_ERR )
172  {
173  BESDEBUG( "cmdln", "FAILED" << endl );
174  cerr << appName() << "Failed to register signal SIGINT" << endl;
175  exit( 1 );
176  }
177  BESDEBUG( "cmdln", "OK" << endl );
178 
179  // Registering SIGTERM to disable kill from the user in order to avoid
180  // server instability
181  BESDEBUG( "cmdln", "CmdApp: Registering signal SIGTERM ... " << endl );
182  if( signal( SIGTERM, signalTerminate ) == SIG_ERR )
183  {
184  BESDEBUG( "cmdln", "FAILED" << endl );
185  cerr << appName() << "Failed to register signal SIGTERM" << endl;
186  exit( 1 );
187  }
188  BESDEBUG( "cmdln", "OK" << endl );
189 
190  // Registering SIGPIE for broken pipes managment.
191  BESDEBUG( "cmdln", "CmdApp: Registering signal SIGPIPE ... " << endl );
192  if( signal( SIGPIPE, CmdApp::signalBrokenPipe ) == SIG_ERR )
193  {
194  BESDEBUG( "cmdln", "FAILED" << endl );
195  cerr << appName() << "Failed to register signal SIGPIPE" << endl;
196  exit( 1 );
197  }
198  BESDEBUG( "cmdln", "OK" << endl );
199  }
200 
201 int CmdApp::initialize(int argc, char **argv)
202 {
203  int retVal = BESBaseApp::initialize(argc, argv);
204  if (retVal != 0) return retVal;
205 
206  CmdTranslation::initialize(argc, argv);
207 
208  string portStr = "";
209  //string portStr = DEFAULT_PORT ;
210  string outputStr = "";
211  string inputStr = "";
212  string timeoutStr = "";
213  string repeatStr = "";
214 
215  bool badUsage = false;
216 
217  int c;
218 
219  while ((c = getopt(argc, argv, "?vd:h:p:t:u:x:f:i:r:")) != EOF) {
220  switch (c) {
221  case 't':
222  timeoutStr = optarg;
223  break;
224  case 'h':
225  _hostStr = optarg;
226  break;
227  case 'd':
228  BESDebug::SetUp(optarg);
229  break;
230  case 'v': {
231  showVersion();
232  exit(0);
233  }
234  break;
235  case 'p':
236  portStr = optarg;
237  break;
238  case 'u':
239  _unixStr = optarg;
240  break;
241  case 'x':
242  _cmd = optarg;
243  break;
244  case 'f':
245  outputStr = optarg;
246  break;
247  case 'i':
248  inputStr = optarg;
249  break;
250  case 'r':
251  repeatStr = optarg;
252  break;
253  case '?': {
254  showUsage();
255  exit(0);
256  }
257  break;
258  }
259  }
260  if (_hostStr == "" && _unixStr == "") {
261  cerr << "host/port or unix socket must be specified" << endl;
262  badUsage = true;
263  }
264 
265  if (_hostStr != "" && _unixStr != "") {
266  cerr << "must specify either a host and port or a unix socket" << endl;
267  badUsage = true;
268  }
269 
270  if (portStr != "" && _unixStr != "") {
271  cerr << "must specify either a host and port or a unix socket" << endl;
272  badUsage = true;
273  }
274 
275  if (_hostStr != "") {
276  if (!_portVal && portStr == "") {
277  cout << "port must be specified when specifying a host" << endl;
278  badUsage = true;
279  }
280  else if (!_portVal) {
281  _portVal = atoi(portStr.c_str());
282  }
283  }
284 
285  if (timeoutStr != "") {
286  _timeout = atoi(timeoutStr.c_str());
287  }
288  else {
289  _timeout = BES_CMDLN_DEFAULT_TIMEOUT;
290  }
291 
292  if (outputStr != "") {
293  if (_cmd == "" && inputStr == "") {
294  cerr << "When specifying an output file you must either " << "specify a command or an input file" << endl;
295  badUsage = true;
296  }
297  else if (_cmd != "" && inputStr != "") {
298  cerr << "You must specify either a command or an input file on " << "the command line, not both" << endl;
299  badUsage = true;
300  }
301  }
302 
303  if (badUsage == true) {
304  showUsage();
305  return 1;
306  }
307 
308  if (outputStr != "") {
309  _outputStrm = new ofstream(outputStr.c_str());
310  if (!(*_outputStrm)) {
311  cerr << "could not open the output file " << outputStr << endl;
312  badUsage = true;
313  }
314  }
315 
316  if (inputStr != "") {
317  _inputStrm = new ifstream(inputStr.c_str());
318  if (!(*_inputStrm)) {
319  cerr << "could not open the input file " << inputStr << endl;
320  badUsage = true;
321  }
322  _createdInputStrm = true;
323  }
324 
325  if (!repeatStr.empty()) {
326  _repeat = atoi(repeatStr.c_str());
327  if (!_repeat && repeatStr != "0") {
328  cerr << "repeat number invalid: " << repeatStr << endl;
329  badUsage = true;
330  }
331  if (!_repeat) {
332  _repeat = 1;
333  }
334  }
335 
336  if (badUsage == true) {
337  showUsage();
338  return 1;
339  }
340 
341  registerSignals();
342 
343  BESDEBUG( "cmdln", "CmdApp: initialized settings:" << endl << *this );
344 
345  return 0;
346 }
347 
349 {
350  try {
351  _client = new CmdClient();
352  if (_hostStr != "") {
353  BESDEBUG( "cmdln", "CmdApp: Connecting to host: " << _hostStr
354  << " at port: " << _portVal << " ... " << endl );
355  _client->startClient( _hostStr, _portVal, _timeout );
356  }
357  else
358  {
359  BESDEBUG( "cmdln", "CmdApp: Connecting to unix socket: "
360  << _unixStr << " ... " << endl );
361  _client->startClient( _unixStr, _timeout );
362  }
363 
364  if (_outputStrm) {
365  _client->setOutput(_outputStrm, true);
366  }
367  else {
368  _client->setOutput(&cout, false);
369  }
370  BESDEBUG( "cmdln", "OK" << endl );
371  }
372  catch( BESError &e )
373  {
374  if( _client )
375  {
376  _client->shutdownClient();
377  delete _client;
378  _client = 0;
379  }
380  BESDEBUG( "cmdln", "FAILED" << endl );
381  cerr << "error starting the client" << endl;
382  cerr << e.get_message() << endl;
383  exit( 1 );
384  }
385 
386  bool do_exit = false;
387  try
388  {
389  if( _cmd != "" )
390  {
391  do_exit = _client->executeCommands( _cmd, _repeat );
392  }
393  else if( _inputStrm )
394  {
395  do_exit = _client->executeCommands( *_inputStrm, _repeat );
396  }
397  else
398  {
399  do_exit = _client->interact();
400  }
401  }
402  catch( BESError &e )
403  {
404  cerr << "error processing commands" << endl;
405  cerr << e.get_message() << endl;
406  }
407 
408  try
409  {
410  BESDEBUG( "cmdln", "CmdApp: shutting down client ... " << endl );
411  if( _client )
412  {
413  // if do_exit is set, then the client has shut itself down
414  if( !do_exit )
415  _client->shutdownClient();
416  delete _client;
417  _client = 0;
418  }
419  BESDEBUG( "cmdln", "OK" << endl );
420 
421  BESDEBUG( "cmdln", "CmdApp: closing input stream ... " << endl );
422  if( _createdInputStrm )
423  {
424  _inputStrm->close();
425  delete _inputStrm;
426  _inputStrm = 0;
427  }
428  BESDEBUG( "cmdln", "OK" << endl );
429  }
430  catch( BESError &e )
431  {
432  BESDEBUG( "cmdln", "FAILED" << endl );
433  cerr << "error closing the client" << endl;
434  cerr << e.get_message() << endl;
435  return 1;
436  }
437 
438  return 0;
439  }
440 
447 void CmdApp::dump(ostream &strm) const
448 {
449  strm << BESIndent::LMarg << "CmdApp::dump - (" << (void *) this << ")" << endl;
451  if (_client) {
452  strm << BESIndent::LMarg << "client: " << endl;
454  _client->dump(strm);
456  }
457  else {
458  strm << BESIndent::LMarg << "client: null" << endl;
459  }
460  strm << BESIndent::LMarg << "host: " << _hostStr << endl;
461  strm << BESIndent::LMarg << "unix socket: " << _unixStr << endl;
462  strm << BESIndent::LMarg << "port: " << _portVal << endl;
463  strm << BESIndent::LMarg << "command: " << _cmd << endl;
464  strm << BESIndent::LMarg << "output stream: " << (void *) _outputStrm << endl;
465  strm << BESIndent::LMarg << "input stream: " << (void *) _inputStrm << endl;
466  strm << BESIndent::LMarg << "created input stream? " << _createdInputStrm << endl;
467  strm << BESIndent::LMarg << "timeout: " << _timeout << endl;
468  BESBaseApp::dump(strm);
470 }
471 
472 int main(int argc, char **argv)
473 {
474  CmdApp app;
475  return app.main(argc, argv);
476 }
477 
virtual int initialize(int argC, char **argV)
initialize the BES application
Definition: BESBaseApp.cc:94
void shutdownClient()
Closes the connection to the OpeNDAP server and closes the output stream.
Definition: CmdClient.cc:150
virtual int initialize(int argC, char **argV)
initialize the BES application
Definition: CmdApp.cc:201
static void SetUp(const string &values)
Sets up debugging for the bes.
Definition: BESDebug.cc:68
static void signalCannotConnect(int sig)
Definition: CmdApp.cc:102
Base application object for all BES applications.
Definition: BESBaseApp.h:54
virtual int main(int argC, char **argV)
main method of the BES application
Definition: BESBaseApp.cc:74
virtual void dump(ostream &strm) const
dumps information about this object
Definition: CmdApp.cc:447
void brokenPipe()
inform the server that there has been a borken pipe
Definition: CmdClient.cc:582
Definition: CmdApp.h:42
static void Indent()
Definition: BESIndent.cc:38
string appName(void) const
Returns the name of the application.
Definition: BESApp.h:132
#define DEFAULT_PORT
Definition: CmdApp.cc:62
virtual string get_message()
get the error message for this exception
Definition: BESError.h:94
static void signalBrokenPipe(int sig)
Definition: CmdApp.cc:137
#define BES_CMDLN_DEFAULT_TIMEOUT
Definition: CmdApp.cc:59
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
static void Help(ostream &strm)
Writes help information for so that developers know what can be set for debugging.
Definition: BESDebug.cc:163
static void signalTerminate(int sig)
Definition: CmdApp.cc:127
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
virtual ~CmdApp()
Definition: CmdApp.cc:71
bool isConnected()
return whether the client is connected to the BES
Definition: CmdClient.cc:574
virtual int run()
the applications functionality is implemented in the run method
Definition: CmdApp.cc:348
virtual void dump(ostream &strm) const
dumps information about this object
Definition: BESBaseApp.cc:140
static void signalInterrupt(int sig)
Definition: CmdApp.cc:117
virtual void dump(ostream &strm) const
dumps information about this object
Definition: CmdClient.cc:593
void startClient(const string &host, int portVal, int timeout)
Connect the BES client to the BES server.
Definition: CmdClient.cc:121
#define DEFAULT_HOST
Definition: CmdApp.cc:63
int main(int argc, char **argv)
Definition: CmdApp.cc:472
CmdClient is an object that handles the connection to, sending requests to, and receiving response fr...
Definition: CmdClient.h:75
void setOutput(ostream *strm, bool created)
Set the output stream for responses from the BES server.
Definition: CmdClient.cc:171
static int initialize(int argc, char **argv)
bool executeCommands(const string &cmd, int repeat)
Send the command(s) specified to the BES server after wrapping in request document.
Definition: CmdClient.cc:379
CmdClient * client()
Definition: CmdApp.h:67
bool interact()
An interactive BES client that takes BES requests on the command line.
Definition: CmdClient.cc:459
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static void UnIndent()
Definition: BESIndent.cc:44
CmdApp()
Definition: CmdApp.cc:65
static BESApp * TheApplication(void)
Returns the BESApp application object for this application.
Definition: BESApp.h:138