bes  Updated for version 3.17.4
CmdApp.cc
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 
65 CmdApp::CmdApp() :
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 
71 CmdApp::~CmdApp()
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 
102 void CmdApp::signalCannotConnect(int sig)
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 
117 void CmdApp::signalInterrupt(int sig)
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 
127 void CmdApp::signalTerminate(int sig)
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 
137 void CmdApp::signalBrokenPipe(int sig)
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  BESDEBUG("cmdln", "FAILED" << endl);
162  cerr << appName() << "Failed to register signal SIGCONT" << endl;
163  exit(1);
164  }
165  BESDEBUG("cmdln", "OK" << endl);
166 
167  // Registering SIGINT to disable Ctrl-C from the user in order to avoid
168  // server instability
169  BESDEBUG("cmdln", "CmdApp: Registering signal SIGINT ... " << endl);
170  if (signal( SIGINT, signalInterrupt) == SIG_ERR) {
171  BESDEBUG("cmdln", "FAILED" << endl);
172  cerr << appName() << "Failed to register signal SIGINT" << endl;
173  exit(1);
174  }
175  BESDEBUG("cmdln", "OK" << endl);
176 
177  // Registering SIGTERM to disable kill from the user in order to avoid
178  // server instability
179  BESDEBUG("cmdln", "CmdApp: Registering signal SIGTERM ... " << endl);
180  if (signal( SIGTERM, signalTerminate) == SIG_ERR) {
181  BESDEBUG("cmdln", "FAILED" << endl);
182  cerr << appName() << "Failed to register signal SIGTERM" << endl;
183  exit(1);
184  }
185  BESDEBUG("cmdln", "OK" << endl);
186 
187  // Registering SIGPIE for broken pipes managment.
188  BESDEBUG("cmdln", "CmdApp: Registering signal SIGPIPE ... " << endl);
189  if (signal( SIGPIPE, CmdApp::signalBrokenPipe) == SIG_ERR) {
190  BESDEBUG("cmdln", "FAILED" << endl);
191  cerr << appName() << "Failed to register signal SIGPIPE" << endl;
192  exit(1);
193  }
194  BESDEBUG("cmdln", "OK" << endl);
195 }
196 
197 int CmdApp::initialize(int argc, char **argv)
198 {
199  int retVal = BESBaseApp::initialize(argc, argv);
200  if (retVal != 0) return retVal;
201 
202  CmdTranslation::initialize(argc, argv);
203 
204  string portStr = "";
205  string outputStr = "";
206  string inputStr = "";
207  string timeoutStr = "";
208  string repeatStr = "";
209 
210  bool badUsage = false;
211 
212  int c;
213 
214  while ((c = getopt(argc, argv, "?vd:h:p:t:u:x:f:i:r:")) != -1) {
215  switch (c) {
216  case 't':
217  timeoutStr = optarg;
218  break;
219  case 'h':
220  _hostStr = optarg;
221  break;
222  case 'd':
223  BESDebug::SetUp(optarg);
224  break;
225  case 'v': {
226  showVersion();
227  exit(0);
228  }
229  break;
230  case 'p':
231  portStr = optarg;
232  break;
233  case 'u':
234  _unixStr = optarg;
235  break;
236  case 'x':
237  _cmd = optarg;
238  break;
239  case 'f':
240  outputStr = optarg;
241  break;
242  case 'i':
243  inputStr = optarg;
244  break;
245  case 'r':
246  repeatStr = optarg;
247  break;
248  case '?': {
249  showUsage();
250  exit(0);
251  }
252  break;
253  }
254  }
255 
256  if (!portStr.empty() && !_unixStr.empty()) {
257  cerr << "cannot use both a port number and a unix socket" << endl;
258  badUsage = true;
259  }
260 
261  if (!portStr.empty()) {
262  _portVal = atoi(portStr.c_str());
263  }
264 
265  if (!timeoutStr.empty()) {
266  _timeout = atoi(timeoutStr.c_str());
267  }
268  else {
269  _timeout = BES_CMDLN_DEFAULT_TIMEOUT;
270  }
271 
272  if (outputStr != "") {
273  if (_cmd == "" && inputStr == "") {
274  cerr << "When specifying an output file you must either " << "specify a command or an input file" << endl;
275  badUsage = true;
276  }
277  else if (_cmd != "" && inputStr != "") {
278  cerr << "You must specify either a command or an input file on " << "the command line, not both" << endl;
279  badUsage = true;
280  }
281  }
282 
283  if (badUsage == true) {
284  showUsage();
285  return 1;
286  }
287 
288  if (outputStr != "") {
289  _outputStrm = new ofstream(outputStr.c_str());
290  if (!(*_outputStrm)) {
291  cerr << "could not open the output file " << outputStr << endl;
292  badUsage = true;
293  }
294  }
295 
296  if (inputStr != "") {
297  _inputStrm = new ifstream(inputStr.c_str());
298  if (!(*_inputStrm)) {
299  cerr << "could not open the input file " << inputStr << endl;
300  badUsage = true;
301  }
302  _createdInputStrm = true;
303  }
304 
305  if (!repeatStr.empty()) {
306  _repeat = atoi(repeatStr.c_str());
307  if (!_repeat && repeatStr != "0") {
308  cerr << "repeat number invalid: " << repeatStr << endl;
309  badUsage = true;
310  }
311  if (!_repeat) {
312  _repeat = 1;
313  }
314  }
315 
316  if (badUsage == true) {
317  showUsage();
318  return 1;
319  }
320 
321  registerSignals();
322 
323  BESDEBUG("cmdln", "CmdApp: initialized settings:" << endl << *this);
324 
325  return 0;
326 }
327 
329 {
330  try {
331  _client = new CmdClient();
332  // Since hostStr and portVal have non-null/zero default values, see if unixStr
333  // was given and, if so, use it.
334  if (!_unixStr.empty()) {
335  BESDEBUG("cmdln", "CmdApp: Connecting to unix socket: " << _unixStr << " ... " << endl);
336  _client->startClient(_unixStr, _timeout);
337  }
338  else {
339  BESDEBUG("cmdln",
340  "CmdApp: Connecting to host: " << _hostStr << " at port: " << _portVal << " ... " << endl);
341  _client->startClient(_hostStr, _portVal, _timeout);
342  }
343 
344  if (_outputStrm) {
345  _client->setOutput(_outputStrm, true);
346  }
347  else {
348  _client->setOutput(&cout, false);
349  }
350  BESDEBUG("cmdln", "OK" << endl);
351  }
352  catch (BESError &e) {
353  if (_client) {
354  _client->shutdownClient();
355  delete _client;
356  _client = 0;
357  }
358  BESDEBUG("cmdln", "FAILED" << endl);
359  cerr << "error starting the client" << endl;
360  cerr << e.get_message() << endl;
361  exit(1);
362  }
363 
364  bool do_exit = false;
365  try {
366  if (_cmd != "") {
367  do_exit = _client->executeCommands(_cmd, _repeat);
368  }
369  else if (_inputStrm) {
370  do_exit = _client->executeCommands(*_inputStrm, _repeat);
371  }
372  else {
373  do_exit = _client->interact();
374  }
375  }
376  catch (BESError &e) {
377  cerr << "error processing commands" << endl;
378  cerr << e.get_message() << endl;
379  }
380 
381  try {
382  BESDEBUG("cmdln", "CmdApp: shutting down client ... " << endl);
383  if (_client) {
384  // if do_exit is set, then the client has shut itself down
385  if (!do_exit) _client->shutdownClient();
386  delete _client;
387  _client = 0;
388  }
389  BESDEBUG("cmdln", "OK" << endl);
390 
391  BESDEBUG("cmdln", "CmdApp: closing input stream ... " << endl);
392  if (_createdInputStrm && _inputStrm) {
393  _inputStrm->close();
394  delete _inputStrm;
395  _inputStrm = 0;
396  }
397  BESDEBUG("cmdln", "OK" << endl);
398  }
399  catch (BESError &e) {
400  BESDEBUG("cmdln", "FAILED" << endl);
401  cerr << "error closing the client" << endl;
402  cerr << e.get_message() << endl;
403  return 1;
404  }
405 
406  return 0;
407 }
408 
415 void CmdApp::dump(ostream &strm) const
416 {
417  strm << BESIndent::LMarg << "CmdApp::dump - (" << (void *) this << ")" << endl;
418  BESIndent::Indent();
419  if (_client) {
420  strm << BESIndent::LMarg << "client: " << endl;
421  BESIndent::Indent();
422  _client->dump(strm);
423  BESIndent::UnIndent();
424  }
425  else {
426  strm << BESIndent::LMarg << "client: null" << endl;
427  }
428  strm << BESIndent::LMarg << "host: " << _hostStr << endl;
429  strm << BESIndent::LMarg << "unix socket: " << _unixStr << endl;
430  strm << BESIndent::LMarg << "port: " << _portVal << endl;
431  strm << BESIndent::LMarg << "command: " << _cmd << endl;
432  strm << BESIndent::LMarg << "output stream: " << (void *) _outputStrm << endl;
433  strm << BESIndent::LMarg << "input stream: " << (void *) _inputStrm << endl;
434  strm << BESIndent::LMarg << "created input stream? " << _createdInputStrm << endl;
435  strm << BESIndent::LMarg << "timeout: " << _timeout << endl;
436  BESBaseApp::dump(strm);
437  BESIndent::UnIndent();
438 }
439 
440 int main(int argc, char **argv)
441 {
442  CmdApp app;
443  return app.main(argc, argv);
444 }
445 
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:197
static void SetUp(const string &values)
Sets up debugging for the bes.
Definition: BESDebug.cc:68
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 std::string get_message()
get the error message for this exception
Definition: BESError.h:97
void brokenPipe()
inform the server that there has been a borken pipe
Definition: CmdClient.cc:550
virtual void dump(ostream &strm) const
dumps information about this object
Definition: BESBaseApp.cc:140
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: CmdApp.cc:415
Definition: CmdApp.h:39
Abstract exception class for the BES with basic string message.
Definition: BESError.h:56
static void Help(ostream &strm)
Writes help information for so that developers know what can be set for debugging.
Definition: BESDebug.cc:163
bool isConnected()
return whether the client is connected to the BES
Definition: CmdClient.cc:542
virtual int run()
the applications functionality is implemented in the run method
Definition: CmdApp.cc:328
virtual void dump(ostream &strm) const
dumps information about this object
Definition: CmdClient.cc:561
string appName(void) const
Returns the name of the application.
Definition: BESApp.h:132
void startClient(const string &host, int portVal, int timeout)
Connect the BES client to the BES server.
Definition: CmdClient.cc:121
void setOutput(ostream *strm, bool created)
Set the output stream for responses from the BES server.
Definition: CmdClient.cc:171
bool executeCommands(const string &cmd, int repeat)
Send the command(s) specified to the BES server after wrapping in request document.
Definition: CmdClient.cc:347
bool interact()
An interactive BES client that takes BES requests on the command line.
Definition: CmdClient.cc:427
static BESApp * TheApplication(void)
Returns the BESApp application object for this application.
Definition: BESApp.h:138