bes  Updated for version 3.20.6
CmdClient.cc
1 // CmdClient.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 <cstdlib>
36 #include <iostream>
37 #include <fstream>
38 #include <sstream>
39 #include <map>
40 
41 using std::cout;
42 using std::endl;
43 using std::cerr;
44 using std::ofstream;
45 using std::ostringstream;
46 using std::ios;
47 using std::map;
48 using std::ifstream;
49 using std::ostream;
50 using std::string;
51 
52 #ifdef HAVE_LIBREADLINE
53 # if defined(HAVE_READLINE_READLINE_H)
54 # include <readline/readline.h>
55 # elif defined(HAVE_READLINE_H)
56 # include <readline.h>
57 # else /* !defined(HAVE_READLINE_H) */
58 extern "C"
59 {
60  char *readline( const char * );
61 }
62 # endif /* !defined(HAVE_READLINE_H) */
63 char *cmdline = NULL;
64 #else /* !defined(HAVE_READLINE_READLINE_H) */
65 /* no readline */
66 #endif /* HAVE_LIBREADLINE */
67 
68 #ifdef HAVE_READLINE_HISTORY
69 # if defined(HAVE_READLINE_HISTORY_H)
70 # include <readline/history.h>
71 # elif defined(HAVE_HISTORY_H)
72 # include <history.h>
73 # else /* !defined(HAVE_HISTORY_H) */
74 extern "C"
75 {
76  int add_history( const char * );
77  int write_history( const char * );
78  int read_history( const char * );
79 }
80 # endif /* defined(HAVE_READLINE_HISTORY_H) */
81 /* no history */
82 #endif /* HAVE_READLINE_HISTORY */
83 
84 #include <libxml/encoding.h>
85 
86 #define SIZE_COMMUNICATION_BUFFER 4096*4096
87 #include "CmdClient.h"
88 #include "CmdTranslation.h"
89 #include "PPTClient.h"
90 #include "BESDebug.h"
91 #include "BESStopWatch.h"
92 #include "BESError.h"
93 
94 CmdClient::~CmdClient()
95 {
96  if (_strmCreated && _strm) {
97  _strm->flush();
98  delete _strm;
99  _strm = 0;
100  }
101  else if (_strm) {
102  _strm->flush();
103  }
104  if (_client) {
105  delete _client;
106  _client = 0;
107  }
108 }
109 
124 void CmdClient::startClient(const string & host, int portVal, int timeout)
125 {
126  _client = new PPTClient(host, portVal, timeout);
127  _client->initConnection();
128 }
129 
139 void CmdClient::startClient(const string & unixStr, int timeout)
140 {
141  _client = new PPTClient(unixStr, timeout);
142  _client->initConnection();
143 }
144 
154 {
155  if (_client) _client->closeConnection();
156 }
157 
174 void CmdClient::setOutput(ostream * strm, bool created)
175 {
176  if (_strmCreated && _strm) {
177  _strm->flush();
178  delete _strm;
179  }
180  else if (_strm) {
181  _strm->flush();
182  }
183  _strm = strm;
184  _strmCreated = created;
185 }
186 
198 bool CmdClient::executeClientCommand(const string & cmd)
199 {
200  bool do_exit = false;
201  string suppress = "suppress";
202  if (cmd.compare(0, suppress.length(), suppress) == 0) {
203  setOutput(NULL, false);
204  return do_exit;
205  }
206 
207  string output = "output to";
208  if (cmd.compare(0, output.length(), output) == 0) {
209  string subcmd = cmd.substr(output.length() + 1);
210  string screen = "screen";
211  if (subcmd.compare(0, screen.length(), screen) == 0) {
212  setOutput(&cout, false);
213  }
214  else {
215  // subcmd is the name of the file - then semicolon
216  string file = subcmd.substr(0, subcmd.length() - 1);
217  ofstream *fstrm = new ofstream(file.c_str(), ios::app);
218  if (fstrm && !(*fstrm)) {
219  delete fstrm;
220  cerr << "Unable to set client output to file " << file << endl;
221  }
222  else {
223  setOutput(fstrm, true);
224  }
225  }
226  return do_exit;
227  }
228 
229  // load commands from an input file and run them
230  string load = "load";
231  if (cmd.compare(0, load.length(), load) == 0) {
232  string file = cmd.substr(load.length() + 1, cmd.length() - load.length() - 2);
233  ifstream fstrm(file.c_str());
234  if (!fstrm) {
235  cerr << "Unable to load commands from file " << file << ": file does not exist or failed to open file"
236  << endl;
237  }
238  else {
239  do_exit = executeCommands(fstrm, 1);
240  }
241 
242  return do_exit;
243  }
244 
245  cerr << "Improper client command " << cmd << endl;
246 
247  return do_exit;
248 }
249 
262 bool CmdClient::executeCommand(const string &cmd, int repeat)
263 {
264  bool do_exit = false;
265  const string client = "client";
266  if (cmd.compare(0, client.length(), client) == 0) {
267  do_exit = executeClientCommand(cmd.substr(client.length() + 1));
268  }
269  else {
270  if (repeat < 1) repeat = 1;
271  for (int i = 0; i < repeat && !do_exit; i++) {
272  BESDEBUG("cmdln", "cmdclient sending " << cmd << endl);
273 
274  BESStopWatch sw;
275  if( BESISDEBUG( TIMING_LOG ) )
276  sw.start("CmdClient::executeCommand","command_line_client");
277 
278  map<string, string> extensions;
279  _client->send(cmd, extensions);
280 
281  BESDEBUG("cmdln", "cmdclient receiving " << endl);
282  // keep reading till we get the last chunk, send to _strm
283  bool done = false;
284  ostringstream *show_stream = 0;
285  while (!done) {
286  if (CmdTranslation::is_show()) {
287  if (!show_stream) {
288  show_stream = new ostringstream;
289  }
290  }
291  if (show_stream) {
292  done = _client->receive(extensions, show_stream);
293  }
294  else {
295  done = _client->receive(extensions, _strm);
296  }
297  if (extensions["status"] == "error") {
298  // If there is an error, just flush what I have
299  // and continue on.
300  _strm->flush();
301 
302  // let's also set show to true because we've gotten back
303  // an xml document (maybe)
304  if (_isInteractive) {
305  CmdTranslation::set_show(true);
306  }
307  }
308  if (extensions["exit"] == "true") {
309  do_exit = true;
310  }
311  }
312  if (show_stream) {
313  *(_strm) << show_stream->str() << endl;
314  delete show_stream;
315  show_stream = 0;
316  }
317  if (BESDebug::IsSet("cmdln")) {
318  BESDEBUG("cmdln", "extensions:" << endl);
319  map<string, string>::const_iterator i = extensions.begin();
320  map<string, string>::const_iterator e = extensions.end();
321  for (; i != e; i++) {
322  BESDEBUG("cmdln", " " << (*i).first << " = " << (*i).second << endl);
323  }
324  BESDEBUG("cmdln", "cmdclient done receiving " << endl);
325  }
326 
327  _strm->flush();
328  }
329  }
330  return do_exit;
331 }
332 
350 bool CmdClient::executeCommands(const string &cmd_list, int repeat)
351 {
352  bool do_exit = false;
353  _isInteractive = true;
354  if (repeat < 1) repeat = 1;
355 
356  CmdTranslation::set_show(false);
357  try {
358  string doc = CmdTranslation::translate(cmd_list);
359  if (!doc.empty()) {
360  do_exit = this->executeCommand(doc, repeat);
361  }
362  }
363  catch (BESError &e) {
364  CmdTranslation::set_show(false);
365  _isInteractive = false;
366  throw e;
367  }
368  CmdTranslation::set_show(false);
369  _isInteractive = false;
370  return do_exit;
371 }
372 
391 bool CmdClient::executeCommands(ifstream & istrm, int repeat)
392 {
393  bool do_exit = false;
394  _isInteractive = false;
395  if (repeat < 1) repeat = 1;
396  for (int i = 0; i < repeat; i++) {
397  istrm.clear();
398  istrm.seekg(0, ios::beg);
399  string cmd;
400  while (!istrm.eof()) {
401  char line[4096];
402  line[0] = '\0';
403  istrm.getline(line, 4096, '\n');
404  cmd += line;
405  }
406  do_exit = this->executeCommand(cmd, 1);
407  }
408  return do_exit;
409 }
410 
431 {
432  bool do_exit = false;
433  _isInteractive = true;
434 
435  cout << endl << endl << "Type 'exit' to exit the command line client and 'help' or '?' "
436  << "to display the help screen" << endl << endl;
437 
438  bool done = false;
439  while (!done && !do_exit) {
440  string message = "";
441  size_t len = this->readLine(message);
442  // len is unsigned. jhrg 11/5/13
443  if (/* len == -1 || */message == "exit" || message == "exit;") {
444  done = true;
445  }
446  else if (message == "help" || message == "help;" || message == "?") {
447  this->displayHelp();
448  }
449  else if (message.length() > 6 && message.substr(0, 6) == "client") {
450  do_exit = this->executeCommand(message, 1);
451  }
452  else if (len != 0 && message != "") {
453  CmdTranslation::set_show(false);
454  try {
455  string doc = CmdTranslation::translate(message);
456  if (!doc.empty()) {
457  do_exit = this->executeCommand(doc, 1);
458  }
459  }
460  catch (BESError &e) {
461  CmdTranslation::set_show(false);
462  _isInteractive = false;
463  throw e;
464  }
465  CmdTranslation::set_show(false);
466  }
467  }
468  _isInteractive = false;
469 
470  return do_exit;
471 }
472 
480 size_t CmdClient::readLine(string &msg)
481 {
482  size_t len = 0;
483  char *buf = (char *) NULL;
484  buf = ::readline("BESClient> ");
485  if (buf && *buf) {
486  len = strlen(buf);
487 #ifdef HAVE_READLINE_HISTORY
488  add_history(buf);
489 #endif
490  if (len > SIZE_COMMUNICATION_BUFFER) {
491  cerr << __FILE__ << __LINE__ <<
492  ": incoming data buffer exceeds maximum capacity with lenght " << len << endl;
493  exit(1);
494  }
495  else {
496  msg = buf;
497  }
498  }
499  else {
500  if (!buf) {
501  // If a null buffer is returned then this means that EOF is
502  // returned. This is different from the user just hitting enter,
503  // which means a character buffer is returned, but is empty.
504 
505  // Problem: len is unsigned.
506  // len = -1 ; I replaced this with the following. jhrg 1/4/12
507  len = 0;
508  }
509  }
510  if (buf) {
511  free(buf);
512  buf = (char *) NULL;
513  }
514  return len;
515 }
516 
519 void CmdClient::displayHelp()
520 {
521  cout << endl;
522  cout << endl;
523  cout << "BES Command Line Client Help" << endl;
524  cout << endl;
525  cout << "Client commands available:" << endl;
526  cout << " exit - exit the command line interface" << endl;
527  cout << " help - display this help screen" << endl;
528  cout << " client suppress; - suppress output from the server" << endl;
529  cout << " client output to screen; - display server output to the screen" << endl;
530  cout << " client output to <file>; - display server output to specified file" << endl;
531  cout << " client load <file>; - load xml document from file" << endl;
532  cout << endl;
533  cout << "Any commands beginning with 'client' must end with a semicolon" << endl;
534  cout << endl;
535  cout << "To display the list of commands available from the server " << "please type the command 'show help;'"
536  << endl;
537  cout << endl;
538  cout << endl;
539 }
540 
546 {
547  if (_client) return _client->isConnected();
548  return false;
549 }
550 
554 {
555  if (_client) _client->brokenPipe();
556 }
557 
564 void CmdClient::dump(ostream & strm) const
565 {
566  strm << BESIndent::LMarg << "CmdClient::dump - (" << (void *) this << ")" << endl;
567  BESIndent::Indent();
568  if (_client) {
569  strm << BESIndent::LMarg << "client:" << endl;
570  BESIndent::Indent();
571  _client->dump(strm);
572  BESIndent::UnIndent();
573  }
574  else {
575  strm << BESIndent::LMarg << "client: null" << endl;
576  }
577  strm << BESIndent::LMarg << "stream: " << (void *) _strm << endl;
578  strm << BESIndent::LMarg << "stream created? " << _strmCreated << endl;
579  BESIndent::UnIndent();
580 }
BESStopWatch::start
virtual bool start(std::string name)
Definition: BESStopWatch.cc:58
CmdClient::brokenPipe
void brokenPipe()
inform the server that there has been a borken pipe
Definition: CmdClient.cc:553
CmdClient::shutdownClient
void shutdownClient()
Closes the connection to the OpeNDAP server and closes the output stream.
Definition: CmdClient.cc:153
CmdClient::dump
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: CmdClient.cc:564
PPTConnection::send
virtual void send(const std::string &buffer)
sends the buffer to the socket
Definition: PPTConnection.cc:184
BESDebug::IsSet
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
Definition: BESDebug.h:157
CmdClient::interact
bool interact()
An interactive BES client that takes BES requests on the command line.
Definition: CmdClient.cc:430
BESStopWatch
Definition: BESStopWatch.h:55
CmdClient::startClient
void startClient(const std::string &host, int portVal, int timeout)
Connect the BES client to the BES server.
Definition: CmdClient.cc:124
PPTClient
Definition: PPTClient.h:40
PPTConnection::receive
virtual bool receive(std::map< std::string, std::string > &extensions, std::ostream *strm=0)
receive a chunk of either extensions into the specified map or data into the specified stream
Definition: PPTConnection.cc:253
PPTClient::dump
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: PPTClient.cc:253
CmdClient::executeClientCommand
bool executeClientCommand(const std::string &cmd)
Executes a client side command.
Definition: CmdClient.cc:198
BESError
Abstract exception class for the BES with basic string message.
Definition: BESError.h:58
CmdClient::isConnected
bool isConnected()
return whether the client is connected to the BES
Definition: CmdClient.cc:545
CmdClient::setOutput
void setOutput(std::ostream *strm, bool created)
Set the output stream for responses from the BES server.
Definition: CmdClient.cc:174
CmdClient::executeCommands
bool executeCommands(const std::string &cmd, int repeat)
Send the command(s) specified to the BES server after wrapping in request document.
Definition: CmdClient.cc:350