bes  Updated for version 3.20.8
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 
45 using namespace std;
46 
47 #include "DaemonCommandHandler.h"
48 #include "Connection.h"
49 #include "Socket.h"
50 #include "PPTStreamBuf.h"
51 #include "PPTProtocol.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();
66 extern void unblock_signals();
67 extern int start_master_beslistener();
68 extern bool stop_all_beslisteners(int);
69 extern int master_beslistener_status;
70 
71 void 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 
87 void 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 
128 DaemonCommandHandler::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 
169 DaemonCommandHandler::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 
196 static 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 
227 static 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.length());
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.
270 static 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
284 static 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.
300 static 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
320 static ifstream::pos_type last_start_pos = 0;
321 static 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.
330 static 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 
368 void 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  BESDebug::debug_citer 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 
715 static 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 
833 void 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:160
static void Set(const std::string &flagName, bool value)
set the debug context to the specified value
Definition: BESDebug.h:119
Abstract exception class for the BES with basic string message.
Definition: BESError.h:58
virtual int get_bes_error_type()
Return the return code for this error class.
Definition: BESError.h:143
virtual std::string get_message()
get the error message for this exception
Definition: BESError.h:99
exception thrown if internal error encountered
exception thrown if an internal error is found and is fatal to the BES
error thrown if there is a user syntax error in the request or any other user error
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
Definition: BESXMLUtils.cc:105
static void XMLErrorFunc(void *context, const char *msg,...)
error function used by libxml2 to report errors
Definition: BESXMLUtils.cc:50
virtual void handle(Connection *c)
virtual void dump(std::ostream &strm) const
dumps information about this object
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:339
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:71
void get_values(const std::string &s, std::vector< std::string > &vals, bool &found)
Retrieve the values of a given key, if set.
Definition: TheBESKeys.cc:370