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