bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
DaemonCommandHandler.cc
1// DaemonCommandHandler.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) 2011 OPeNDAP
7// Author: James Gallagher <jgallagher@opendap.org> Based on code by
8// Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
9//
10// This library is free software; you can redistribute it and/or
11// modify it under the terms of the GNU Lesser General Public
12// License as published by the Free Software Foundation; either
13// version 2.1 of the License, or (at your option) any later version.
14//
15// This library is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18// Lesser General Public License for more details.
19//
20// You should have received a copy of the GNU Lesser General Public
21// License along with this library; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23//
24// You can contact University Corporation for Atmospheric Research at
25// 3080 Center Green Drive, Boulder, CO 80301
26
27// (c) COPYRIGHT OPeNDAP
28// Please read the full copyright statement in the file COPYING.
29
30#include "config.h"
31
32#include <unistd.h> // for getpid fork sleep
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <signal.h>
36#include <sys/wait.h> // for waitpid
37#include <cstring>
38#include <cstdlib>
39#include <cerrno>
40#include <sstream>
41#include <iostream>
42#include <fstream>
43#include <map>
44
45using namespace std;
46
47#include "DaemonCommandHandler.h"
48#include "Connection.h"
49#include "Socket.h"
50#include "PPTStreamBuf.h"
51#include "PPTProtocolNames.h"
52#include "BESXMLUtils.h"
53#include "BESInternalFatalError.h"
54#include "BESInternalError.h"
55#include "BESSyntaxUserError.h"
56#include "BESDebug.h"
57#include "BESFSFile.h"
58#include "BESFSDir.h"
59#include "TheBESKeys.h"
60
61#include "BESXMLWriter.h"
62#include "BESDaemonConstants.h"
63
64// Defined in daemon.cc
65// extern void block_signals();
66extern void unblock_signals();
67extern int start_master_beslistener();
68extern bool stop_all_beslisteners(int);
69extern int master_beslistener_status;
70
71void DaemonCommandHandler::load_include_files(vector<string> &files, const string &keys_file_name)
72{
73 vector<string>::iterator i = files.begin();
74 while (i != files.end())
75 load_include_file(*i++, keys_file_name);
76}
77
87void DaemonCommandHandler::load_include_file(const string &files, const string &keys_file_name)
88{
89 string newdir;
90 BESFSFile allfiles(files);
91
92 // If the files specified begin with a /, then use that directory
93 // instead of the current keys file directory.
94 if (!files.empty() && files[0] == '/') {
95 newdir = allfiles.getDirName();
96 }
97 else {
98 // determine the directory of the current keys file. All included
99 // files will be relative to this file.
100 BESFSFile currfile(keys_file_name);
101 string currdir = currfile.getDirName();
102
103 string alldir = allfiles.getDirName();
104
105 if ((currdir == "./" || currdir == ".") && (alldir == "./" || alldir == ".")) {
106 newdir = "./";
107 }
108 else {
109 if (alldir == "./" || alldir == ".") {
110 newdir = currdir;
111 }
112 else {
113 newdir = currdir + "/" + alldir;
114 }
115 }
116 }
117
118 // load the files one at a time. If the directory doesn't exist,
119 // then don't load any configuration files
120 BESFSDir fsd(newdir, allfiles.getFileName());
121 BESFSDir::fileIterator i = fsd.beginOfFileList();
122 BESFSDir::fileIterator e = fsd.endOfFileList();
123 for (; i != e; i++) {
124 d_pathnames.insert(make_pair((*i).getFileName(), (*i).getFullPath()));
125 }
126}
127
128DaemonCommandHandler::DaemonCommandHandler(const string &config) :
129 d_bes_conf(config)
130{
131 // There is always a bes.conf file, even it does not use that exact name.
132 string d_bes_name = d_bes_conf.substr(d_bes_conf.find_last_of('/') + 1);
133 d_pathnames.insert(make_pair(d_bes_name, d_bes_conf));
134
135 {
136 // There will likely be subordinate config files for each module
137 vector<string> vals;
138 bool found = false;
139 TheBESKeys::TheKeys()->get_values("BES.Include", vals, found);
140 BESDEBUG("besdaemon", "DaemonCommandHandler() - Found BES.Include: " << found << endl);
141
142 // Load the child config file/path names into d_pathnames.
143 if (found) {
144 load_include_files(vals, config);
145 }
146 }
147
148 if (BESDebug::IsSet("besdaemon")) {
149 map<string, string>::iterator i = d_pathnames.begin();
150 while (i != d_pathnames.end()) {
151 BESDEBUG("besdaemon",
152 "DaemonCommandHandler() - d_pathnames: [" << (*i).first << "]: " << d_pathnames[(*i).first] << endl);
153 ++i;
154 }
155 }
156
157 {
158 bool found = false;
159 TheBESKeys::TheKeys()->get_value("BES.LogName", d_log_file_name, found);
160 if (!found) d_log_file_name = "";
161 }
162}
163
169DaemonCommandHandler::hai_command DaemonCommandHandler::lookup_command(const string &command)
170{
171 if (command == "StopNow")
172 return HAI_STOP_NOW;
173 else if (command == "Start")
174 return HAI_START;
175 else if (command == "Exit")
176 return HAI_EXIT;
177 else if (command == "GetConfig")
178 return HAI_GET_CONFIG;
179 else if (command == "SetConfig")
180 return HAI_SET_CONFIG;
181 else if (command == "TailLog")
182 return HAI_TAIL_LOG;
183 else if (command == "GetLogContexts")
184 return HAI_GET_LOG_CONTEXTS;
185 else if (command == "SetLogContext")
186 return HAI_SET_LOG_CONTEXT;
187 else
188 return HAI_UNKNOWN;
189}
190
196static char *read_file(const string &name)
197{
198 char *memblock;
199 ifstream::pos_type size;
200
201 ifstream file(name.c_str(), ios::in | ios::binary | ios::ate);
202 if (file.is_open()) {
203 size = file.tellg();
204 memblock = new char[((unsigned long) size) + 1];
205 file.seekg(0, ios::beg);
206 file.read(memblock, size);
207 file.close();
208
209 memblock[size] = '\0';
210
211 return memblock;
212 }
213 else {
214 throw BESInternalError("Could not open config file:" + name, __FILE__, __LINE__);
215 }
216}
217
227static void write_file(const string &name, const string &buffer)
228{
229 // First write the new text to a temporary file
230 string tmp_name = name + ".tmp";
231 ofstream outfile(tmp_name.c_str(), std::ios_base::out);
232 if (outfile.is_open()) {
233 // write to outfile
234 outfile.write(buffer.data(), buffer.size());
235
236 outfile.close();
237 }
238 else {
239 throw BESInternalError("Could not open config file:" + name, __FILE__, __LINE__);
240 }
241
242 // Now see if the original file should be backed up. For any given
243 // instance of the server, only back up on the initial attempt to write a
244 // new version of the file.
245 ostringstream backup_name;
246 backup_name << name << "." << getpid();
247 if (access(backup_name.str().c_str(), F_OK) == -1) {
248 BESDEBUG("besdaemon", "DaemonCommandHandler()::write_file() - No backup file yet" << endl);
249 // Backup does not exist for this instance of the server; backup name
250 if (rename(name.c_str(), backup_name.str().c_str()) == -1) {
251 BESDEBUG("besdaemon", "DaemonCommandHandler()::write_file() - : Could not backup file " << name << " to " << backup_name.str() << endl);
252 ostringstream err;
253 err << "(" << errno << ") " << strerror(errno);
254 throw BESInternalError("Could not backup config file: " + name + ": " + err.str(), __FILE__, __LINE__);
255 }
256 }
257
258 // Now move the '.tmp' file to <name>
259 if (rename(tmp_name.c_str(), name.c_str()) == -1) {
260 BESDEBUG("besdaemon", "DaemonCommandHandler()::write_file() - : Could not complete write " << name << " to " << backup_name.str() << endl);
261 ostringstream err;
262 err << "(" << errno << ") " << strerror(errno);
263 throw BESInternalError("Could not write config file:" + name + ": " + err.str(), __FILE__, __LINE__);
264 }
265}
266
267// Count forward 'lines', leave the file pointer at the place just past that
268// and return the number of lines actually read (which might be less if eof
269// is found before 'lines' lines are read.
270static unsigned long move_forward_lines(ifstream &infile, unsigned long lines)
271{
272 unsigned long count = 0;
273 while (count < lines && !infile.eof() && !infile.fail()) {
274 infile.ignore(1024, '\n');
275 ++count;
276 }
277
278 infile.clear(); // Needed to reset eof() or fail() so tellg/seekg work.
279
280 return count;
281}
282
283// Count the number of lines from pos to the end of the file
284static unsigned long count_lines(ifstream &infile, ifstream::pos_type pos)
285{
286 infile.seekg(pos, ios::beg);
287 unsigned long count = 0;
288 while (!infile.eof() && !infile.fail()) {
289 infile.ignore(1024, '\n');
290 ++count;
291 }
292
293 infile.clear(); // Needed to reset eof() or fail() so tellg/seekg work.
294
295 return count;
296}
297
298// Starting at wherever the file pointer is at, read to the end and return
299// the data in a char *. The caller must delete[] the memory.
300static char *read_file_data(ifstream &infile)
301{
302 // Read remaining lines as a block of stuff.
303 ifstream::pos_type start_pos = infile.tellg();
304 infile.seekg(0, ios::end);
305 ifstream::pos_type end_pos = infile.tellg();
306
307 unsigned long size = (end_pos > start_pos) ? end_pos - start_pos : 0;
308 char *memblock = new char[size + 1];
309
310 infile.seekg(start_pos, ios::beg);
311 infile.read(memblock, size);
312 infile.close();
313
314 memblock[size] = '\0';
315
316 return memblock;
317}
318
319// These are used to save time counting lines in large files
320static ifstream::pos_type last_start_pos = 0;
321static unsigned long last_start_line = 0;
322
323// This is an older version of get_bes_log_lines(). It's not as inefficient as
324// the first version, but it's not great either. This version remembers how big
325// the log was and so skips one of two reads of the entire log. It will still
326// read the entire log just to print the last 200 lines (the log might be 1 MB).
327
328// if num_lines is == 0, get all the lines; if num_lines < 0, also get all the
329// lines, but this is really an error, should be trapped by caller.
330static char *get_bes_log_lines(const string &log_file_name, unsigned long num_lines)
331{
332 ifstream infile(log_file_name.c_str(), ios::in | ios::binary);
333 if (!infile.is_open())
334 throw BESInternalError("Could not open file for reading (" + log_file_name + ")", __FILE__, __LINE__);
335
336 if (num_lines == 0) {
337 // return the whole file
338 infile.seekg(0, ios::beg);
339 return read_file_data(infile);
340 }
341 else {
342 // How many lines in the total file? Use last count info.
343 unsigned long count = count_lines(infile, last_start_pos) + last_start_line;
344
345 // last_start_pos is where last_start_line is, we need to advance to
346 // the line that is num_lines back from the end of the file
347 unsigned long new_start_line = (count >= num_lines) ? count - num_lines + 1 : 0;
348
349 // Now go back to the last_start_pos
350 infile.seekg(last_start_pos, ios::beg);
351 // and count forward to the line that starts this last num_lines
352 count = move_forward_lines(infile, new_start_line - last_start_line);
353
354 // Save this point for the next time
355 last_start_line = new_start_line;
356 last_start_pos = infile.tellg();
357
358 return read_file_data(infile);
359 }
360}
361
368void DaemonCommandHandler::execute_command(const string &command, BESXMLWriter &writer)
369{
370 xmlDoc *doc = NULL;
371 xmlNode *root_element = NULL;
372 xmlNode *current_node = NULL;
373
374 try {
375 // set the default error function to my own
376 vector<string> parseerrors;
377 xmlSetGenericErrorFunc((void *) &parseerrors, BESXMLUtils::XMLErrorFunc);
378
379 // We would like this, but older versions of libxml don't use 'const'.
380 // Older == 2.6.16. jhrg 12.13.11
381 // We now require libxml2 >= 2.7.0 jhrg 9/25/15
382 doc = xmlParseDoc((const xmlChar*) command.c_str());
383
384 if (doc == NULL) {
385 string err = "";
386 bool isfirst = true;
387 vector<string>::const_iterator i = parseerrors.begin();
388 vector<string>::const_iterator e = parseerrors.end();
389 for (; i != e; i++) {
390 if (!isfirst && (*i).compare(0, 6, "Entity") == 0) {
391 err += "\n";
392 }
393 err += (*i);
394 isfirst = false;
395 }
396
397 throw BESSyntaxUserError(err, __FILE__, __LINE__);
398 }
399
400 // get the root element and make sure it exists and is called request
401 root_element = xmlDocGetRootElement(doc);
402 if (!root_element) {
403 throw BESSyntaxUserError("There is no root element in the xml document", __FILE__, __LINE__);
404 }
405
406 string root_name;
407 string root_val;
408 map<string, string> props;
409 BESXMLUtils::GetNodeInfo(root_element, root_name, root_val, props);
410 if (root_name != "BesAdminCmd") {
411 string err = (string) "The root element should be a BesAdminCmd element, name is "
412 + (char *) root_element->name;
413 throw BESSyntaxUserError(err, __FILE__, __LINE__);
414 }
415 if (root_val != "") {
416 string err = (string) "The BesAdminCmd element must not contain a value, " + root_val;
417 throw BESSyntaxUserError(err, __FILE__, __LINE__);
418 }
419
420 // iterate through the children of the request element. Each child is an
421 // individual command.
422 current_node = root_element->children;
423
424 while (current_node) {
425 if (current_node->type == XML_ELEMENT_NODE) {
426 string node_name = (char *) current_node->name;
427 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Looking for command " << node_name << endl);
428 // ***
429 // cerr << "Processing command " << node_name << endl;
430
431 // While processing a command, block signals, which can also
432 // be used to control the master beslistener. unblock at the
433 // end of the while loop.
434
435 switch (lookup_command(node_name)) {
436 case HAI_STOP_NOW:
437 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received StopNow" << endl);
438
439 if (stop_all_beslisteners(SIGTERM) == false) {
440 if (master_beslistener_status == BESLISTENER_RUNNING) {
441 throw BESInternalFatalError("Could not stop the master beslistener", __FILE__, __LINE__);
442 }
443 else {
444 throw BESSyntaxUserError(
445 "Received Stop command but the master beslistener was likely already stopped",
446 __FILE__, __LINE__);
447 }
448 }
449 else {
450 if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
451 throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
452 if (xmlTextWriterEndElement(writer.get_writer()) < 0)
453 throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
454 }
455 break;
456
457 case HAI_START: {
458 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received Start" << endl);
459 // start_master_beslistener assigns the mbes pid to a
460 // static global defined in daemon.cc that stop_all_bes...
461 // uses.
462 if (master_beslistener_status == BESLISTENER_RUNNING) {
463 throw BESSyntaxUserError("Received Start command but the master beslistener is already running",
464 __FILE__, __LINE__);
465 }
466
467 if (start_master_beslistener() == 0) {
468 BESDEBUG("besdaemon",
469 "DaemonCommandHandler::execute_command() - Error starting; master_beslistener_status = " << master_beslistener_status << endl);
470 if (master_beslistener_status == BESLISTENER_RUNNING) {
471 throw BESSyntaxUserError(
472 "Received Start command but the master beslistener is already running", __FILE__,
473 __LINE__);
474 }
475 else {
476 throw BESInternalFatalError("Could not start the master beslistener", __FILE__, __LINE__);
477 }
478 }
479 else {
480 // Whenever the master listener starts, it makes a new log file. Reset the counters used to
481 // record the 'last line read' position - these variables are part of an optimization
482 // to limit re-reading old sections of the log file.
483 last_start_pos = 0;
484 last_start_line = 0;
485
486 if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
487 throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
488 if (xmlTextWriterEndElement(writer.get_writer()) < 0)
489 throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
490 }
491 break;
492 }
493
494 case HAI_EXIT:
495 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received Exit" << endl);
496 stop_all_beslisteners(SIGTERM);
497 unblock_signals(); // called here because we're about to exit
498 exit(0);
499 break;
500
501 case HAI_GET_CONFIG: {
502 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received GetConfig" << endl);
503
504 if (d_pathnames.empty()) {
505 throw BESInternalFatalError("There are no known configuration files for this BES!", __FILE__,
506 __LINE__);
507 }
508
509 // For each of the configuration files, send an XML
510 // <BesConfig module="" /> element.
511 map<string, string>::iterator i = d_pathnames.begin();
512 while (i != d_pathnames.end()) {
513 BESDEBUG("besdaemon",
514 "DaemonCommandHandler::execute_command() - Retrieving " << (*i).first << ": " << d_pathnames[(*i).first] << endl);
515
516 if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:BesConfig") < 0)
517 throw BESInternalFatalError("Could not write <hai:Config> element ", __FILE__, __LINE__);
518
519 if (xmlTextWriterWriteAttribute(writer.get_writer(), (const xmlChar*) "module",
520 (const xmlChar*) (*i).first.c_str()) < 0)
521 throw BESInternalFatalError("Could not write fileName attribute ", __FILE__, __LINE__);
522
523 char *content = read_file(d_pathnames[(*i).first]);
524 try {
525 BESDEBUG("besdaemon_verbose", "DaemonCommandHandler::execute_command() - content: " << content << endl);
526 if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*) "\n") < 0)
527 throw BESInternalFatalError("Could not write newline", __FILE__, __LINE__);
528
529 if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*) content) < 0)
530 throw BESInternalFatalError("Could not write line", __FILE__, __LINE__);
531
532 delete[] content;
533 content = 0;
534 }
535 catch (...) {
536 delete[] content;
537 content = 0;
538 throw;
539 }
540
541 if (xmlTextWriterEndElement(writer.get_writer()) < 0)
542 throw BESInternalFatalError("Could not end <hai:BesConfig> element ", __FILE__, __LINE__);
543 ++i;
544 }
545
546 break;
547 }
548
549 case HAI_SET_CONFIG: {
550 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received SetConfig" << endl);
551 xmlChar *xml_char_module = xmlGetProp(current_node, (const xmlChar*) "module");
552 if (!xml_char_module) {
553 throw BESSyntaxUserError("SetConfig missing module ", __FILE__, __LINE__);
554 }
555 string module = (const char *) xml_char_module;
556 xmlFree(xml_char_module);
557
558 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received SetConfig; module: " << module << endl);
559
560 xmlChar *file_content = xmlNodeListGetString(doc, current_node->children, /* inLine = */true);
561 if (!file_content) {
562 throw BESInternalFatalError("SetConfig missing content, no changes made ", __FILE__, __LINE__);
563 }
564 string content = (const char *) file_content;
565 xmlFree(file_content);
566 BESDEBUG("besdaemon_verbose",
567 "DaemonCommandHandler::execute_command() - Received SetConfig; content: " << endl << content << endl);
568
569 write_file(d_pathnames[module], content);
570
571 if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
572 throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
573
574 if (xmlTextWriterWriteString(writer.get_writer(),
575 (const xmlChar*) "\nPlease restart the server for these changes to take affect.\n") < 0)
576 throw BESInternalFatalError("Could not write newline", __FILE__, __LINE__);
577
578 if (xmlTextWriterEndElement(writer.get_writer()) < 0)
579 throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
580
581 break;
582 }
583
584 case HAI_TAIL_LOG: {
585 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received TailLog" << endl);
586
587 xmlChar *xml_char_lines = xmlGetProp(current_node, (const xmlChar*) "lines");
588 if (!xml_char_lines) {
589 throw BESSyntaxUserError("TailLog missing lines attribute ", __FILE__, __LINE__);
590 }
591
592 char *endptr;
593 long num_lines = strtol((const char *) xml_char_lines, &endptr, 10 /*base*/);
594 if (num_lines == 0 && endptr == (const char *) xml_char_lines) {
595 ostringstream err;
596 err << "(" << errno << ") " << strerror(errno);
597 throw BESSyntaxUserError("TailLog lines attribute bad value: " + err.str(), __FILE__, __LINE__);
598 }
599
600 if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:BesLog") < 0)
601 throw BESInternalFatalError("Could not write <hai:BesLog> element ", __FILE__, __LINE__);
602
603 BESDEBUG("besdaemon",
604 "DaemonCommandHandler::execute_command() - TailLog: log file:" << d_log_file_name << ", lines: " << num_lines << endl);
605
606 char *content = get_bes_log_lines(d_log_file_name, num_lines);
607 try {
608 BESDEBUG("besdaemon_verbose", "DaemonCommandHandler::execute_command() - Returned lines: " << content << endl);
609 if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*) "\n") < 0)
610 throw BESInternalFatalError("Could not write newline", __FILE__, __LINE__);
611
612 if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*) content) < 0)
613 throw BESInternalFatalError("Could not write line", __FILE__, __LINE__);
614
615 delete[] content;
616 content = 0;
617 }
618 catch (...) {
619 delete[] content;
620 content = 0;
621 throw;
622 }
623
624 if (xmlTextWriterEndElement(writer.get_writer()) < 0)
625 throw BESInternalFatalError("Could not end <hai:BesLog> element ", __FILE__, __LINE__);
626
627 break;
628 }
629
630 case HAI_GET_LOG_CONTEXTS: {
631 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received GetLogContexts" << endl);
632
633 BESDEBUG("besdaemon",
634 "DaemonCommandHandler::execute_command() - There are " << BESDebug::debug_map().size() << " Contexts" << endl);
635 if (BESDebug::debug_map().size()) {
636 auto i = BESDebug::debug_map().begin();
637 while (i != BESDebug::debug_map().end()) {
638 if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:LogContext") < 0)
639 throw BESInternalFatalError("Could not write <hai:LogContext> element ", __FILE__,
640 __LINE__);
641
642 if (xmlTextWriterWriteAttribute(writer.get_writer(), (const xmlChar*) "name",
643 (const xmlChar*) (*i).first.c_str()) < 0)
644 throw BESInternalFatalError("Could not write 'name' attribute ", __FILE__, __LINE__);
645
646 string state = (*i).second ? "on" : "off";
647 if (xmlTextWriterWriteAttribute(writer.get_writer(), (const xmlChar*) "state",
648 (const xmlChar*) state.c_str()) < 0)
649 throw BESInternalFatalError("Could not write 'state' attribute ", __FILE__, __LINE__);
650
651 if (xmlTextWriterEndElement(writer.get_writer()) < 0)
652 throw BESInternalFatalError("Could not end <hai:LogContext> element ", __FILE__,
653 __LINE__);
654
655 ++i;
656 }
657 }
658
659 break;
660 }
661
662 case HAI_SET_LOG_CONTEXT: {
663 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received SetLogContext" << endl);
664
665 xmlChar *xml_char_module = xmlGetProp(current_node, (const xmlChar*) "name");
666 if (!xml_char_module) {
667 throw BESSyntaxUserError("SetLogContext missing name ", __FILE__, __LINE__);
668 }
669 string name = (const char *) xml_char_module;
670 xmlFree(xml_char_module);
671
672 xml_char_module = xmlGetProp(current_node, (const xmlChar*) "state");
673 if (!xml_char_module) {
674 throw BESSyntaxUserError("SetLogContext missing state ", __FILE__, __LINE__);
675 }
676 bool state = strcmp((const char *) xml_char_module, "on") == 0;
677 xmlFree(xml_char_module);
678
679 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Before setting " << name << " to " << state << endl);
680
681 // Setting this here is all we have to do. This will
682 // change the debug/log settings for the daemon and
683 // (See damon.cc update_beslistener_args()) cause the
684 // new settings to be passed onto new beslisteners.
685 BESDebug::Set(name, state);
686
687 BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - After setting " << name << " to " << state << endl);
688
689 if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
690 throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
691 if (xmlTextWriterEndElement(writer.get_writer()) < 0)
692 throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
693
694 break;
695 }
696
697 default:
698 throw BESSyntaxUserError("Command " + node_name + " unknown.", __FILE__, __LINE__);
699 }
700 }
701
702 current_node = current_node->next;
703 }
704 }
705 catch (...) {
706 xmlFreeDoc(doc);
707 throw;
708 }
709
710 xmlFreeDoc(doc);
711 // Calling xmlCleanupparser() here throws all kinds of fits - double free errors.
712 // This might be because the
713}
714
715static void send_bes_error(BESXMLWriter &writer, BESError &e)
716{
717 if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:BESError") < 0)
718 throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
719
720 ostringstream oss;
721 oss << e.get_bes_error_type() << std::ends;
722 if (xmlTextWriterWriteElement(writer.get_writer(), (const xmlChar*) "hai:Type", (const xmlChar*) oss.str().c_str())
723 < 0) throw BESInternalFatalError("Could not write <hai:Type> element ", __FILE__, __LINE__);
724
725 if (xmlTextWriterWriteElement(writer.get_writer(), (const xmlChar*) "hai:Message",
726 (const xmlChar*) e.get_message().c_str()) < 0)
727 throw BESInternalFatalError("Could not write <hai:Message> element ", __FILE__, __LINE__);
728
729 if (xmlTextWriterEndElement(writer.get_writer()) < 0)
730 throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
731}
732
733
742{
743 map<string, string> extensions;
744 ostringstream ss;
745
746 bool done = false;
747 while (!done)
748 done = c->receive(extensions, &ss);
749
750 if (extensions["status"] == c->exit()) {
751 // When the client communicating with the besdaemon exits,
752 // return control to the PPTServer::initConnection() method which
753 // will listen for another connect request.
754 BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Received PPT_EXIT_NOW in an extension chunk." << endl);
755
756 }
757 else {
758 int descript = c->getSocket()->getSocketDescriptor();
759 unsigned int bufsize = c->getSendChunkSize();
760 PPTStreamBuf fds(descript, bufsize);
761
762 std::streambuf *holder;
763 holder = cout.rdbuf();
764 cout.rdbuf(&fds); // cout writes to the PPTStreamBuf
765
766 BESXMLWriter writer;
767
768 try {
769 BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - cmd: " << ss.str() << endl);
770 // runs the command(s); throws on an error.
771 execute_command(ss.str(), writer);
772 BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Transmitting response." << endl);
773
774 cout << writer.get_doc() << endl;
775 fds.finish();
776 cout.rdbuf(holder);
777 }
778 catch (BESError &e) {
779 // an error has occurred.
780 // flush what we have in the stream to the client
781 cout << flush;
782
783 // Send the extension status=error to the client so that it
784 // can reset.
785 map<string, string> extensions;
786 extensions["status"] = "error";
787
788 switch (e.get_bes_error_type()) {
789 case BES_INTERNAL_ERROR:
790 case BES_INTERNAL_FATAL_ERROR:
791 BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Internal/Fatal ERROR: " << e.get_message() << endl);
792 extensions["exit"] = "true";
793 c->sendExtensions(extensions);
794 send_bes_error(writer, e);
795 break;
796
797 case BES_SYNTAX_USER_ERROR:
798 BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Syntax ERROR: " << e.get_message() << endl);
799 c->sendExtensions(extensions);
800 send_bes_error(writer, e);
801 break;
802
803 default:
804 BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - ERROR (unknown command): " << ss.str() << endl);
805 extensions["exit"] = "true";
806 c->sendExtensions(extensions);
807 send_bes_error(writer, e);
808 break;
809
810 }
811
812 cout << writer.get_doc() << endl;
813 fds.finish(); // we are finished, send the last chunk
814 cout.rdbuf(holder); // reset the streams buffer
815 }
816
817 }
818 // This call closes the socket - it does minimal bookkeeping and
819 // calls the the kernel's close() function. NB: The method is
820 // implemented in PPTServer.cc and that calls Socket::close() on the
821 // Socket instance held by the Connection.
822 c->closeConnection();
823
824 BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Command Processing completed. " << endl);
825}
826
833void DaemonCommandHandler::dump(ostream &strm) const
834{
835 strm << BESIndent::LMarg << "DaemonCommandHandler::dump - (" << (void *) this << ")" << endl;
836}
837
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
Definition BESDebug.h:145
static void Set(const std::string &flagName, bool value)
set the debug context to the specified value
Definition BESDebug.cc:157
Base exception class for the BES with basic string message.
Definition BESError.h:66
unsigned int get_bes_error_type() const
Return the return code for this error class.
Definition BESError.h:174
std::string get_message() const
get the error message for this exception
Definition BESError.h:132
exception thrown if internal error encountered
exception thrown if an internal error is found and is fatal to the BES
static void GetNodeInfo(xmlNode *node, std::string &name, std::string &value, std::map< std::string, std::string > &props)
get the name, value if any, and any properties for the specified node
static void XMLErrorFunc(void *context, const char *msg,...)
error function used by libxml2 to report errors
void dump(std::ostream &strm) const override
dumps information about this object
void handle(Connection *c) override
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
static TheBESKeys * TheKeys()
Access to the singleton.
Definition TheBESKeys.cc:85
void get_values(const std::string &s, std::vector< std::string > &vals, bool &found)
Retrieve the values of a given key, if set.
STL iterator class.