bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
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
46using std::cout;
47using std::cerr;
48using std::endl;
49using std::flush;
50using std::string;
51using std::ofstream;
52using std::ifstream;
53using 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
67CmdApp::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
73CmdApp::~CmdApp()
74{
75 if (_client) {
76 delete _client;
77 _client = 0;
78 }
79}
80
81void CmdApp::showVersion()
82{
83 cout << appName() << ": version 2.0" << endl;
84}
85
86void 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
104void 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
119void 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
129void 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
139void 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
158void 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
199int 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
417void 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
442int main(int argc, char **argv)
443{
444 CmdApp app;
445 return app.main(argc, argv);
446}
447
Application class for BES applications.
Definition BESApp.h:56
virtual int initialize(int argC, char **argV)
Initialize the application using the passed argc and argv values.
Definition BESApp.cc:70
std::string appName() const
Returns the name of the application.
Definition BESApp.h:130
void dump(std::ostream &strm) const override=0
dumps information about this object
Definition BESApp.cc:113
static BESApp * TheApplication()
Returns the BESApp application object for this application.
Definition BESApp.h:139
virtual int main(int argC, char **argV)
main routine, the main entry point for any BES applications.
Definition BESApp.cc:52
static void SetUp(const std::string &values)
Sets up debugging for the bes.
Definition BESDebug.cc:91
static void Help(std::ostream &strm)
Writes help information for so that developers know what can be set for debugging.
Definition BESDebug.cc:173
Base exception class for the BES with basic string message.
Definition BESError.h:66
std::string get_message() const
get the error message for this exception
Definition BESError.h:132
virtual int initialize(int argC, char **argV)
Initialize the application using the passed argc and argv values.
Definition CmdApp.cc:199
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition CmdApp.cc:417
virtual int run()
The body of the application, implementing the primary functionality of the BES application.
Definition CmdApp.cc:330