bes Updated for version 3.20.13
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
41using std::cout;
42using std::endl;
43using std::cerr;
44using std::ofstream;
45using std::ostringstream;
46using std::ios;
47using std::map;
48using std::ifstream;
49using std::ostream;
50using 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) */
58extern "C"
59{
60 char *readline( const char * );
61}
62# endif /* !defined(HAVE_READLINE_H) */
63char *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) */
74extern "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
94CmdClient::~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
124void CmdClient::startClient(const string & host, int portVal, int timeout)
125{
126 _client = new PPTClient(host, portVal, timeout);
127 _client->initConnection();
128}
129
139void 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
174void 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
198bool 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
262bool 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( BESDebug::IsSet(TIMING_LOG_KEY) )
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
350bool 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
391bool 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
480size_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
519void 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
564void 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}
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
Definition: BESDebug.h:168
Base exception class for the BES with basic string message.
Definition: BESError.h:59
virtual bool start(std::string name)
Definition: BESStopWatch.cc:67
void brokenPipe()
inform the server that there has been a borken pipe
Definition: CmdClient.cc:553
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
void startClient(const std::string &host, int portVal, int timeout)
Connect the BES client to the BES server.
Definition: CmdClient.cc:124
bool executeClientCommand(const std::string &cmd)
Executes a client side command.
Definition: CmdClient.cc:198
bool interact()
An interactive BES client that takes BES requests on the command line.
Definition: CmdClient.cc:430
bool isConnected()
return whether the client is connected to the BES
Definition: CmdClient.cc:545
void shutdownClient()
Closes the connection to the OpeNDAP server and closes the output stream.
Definition: CmdClient.cc:153
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: CmdClient.cc:564
void setOutput(std::ostream *strm, bool created)
Set the output stream for responses from the BES server.
Definition: CmdClient.cc:174
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: PPTClient.cc:253
virtual void send(const std::string &buffer)
sends the buffer to the socket
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