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