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