OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
DaemonCommandHandler.cc
Go to the documentation of this file.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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  {
94  newdir = allfiles.getDirName();
95  }
96  else
97  {
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  {
107  newdir = "./";
108  }
109  else
110  {
111  if (alldir == "./" || alldir == ".")
112  {
113  newdir = currdir;
114  }
115  else
116  {
117  newdir = currdir + "/" + alldir;
118  }
119  }
120  }
121 
122  // load the files one at a time. If the directory doesn't exist,
123  // then don't load any configuration files
124  BESFSDir fsd(newdir, allfiles.getFileName());
125  BESFSDir::fileIterator i = fsd.beginOfFileList();
126  BESFSDir::fileIterator e = fsd.endOfFileList();
127  for (; i != e; i++)
128  {
129  d_pathnames.insert(make_pair((*i).getFileName(), (*i).getFullPath()));
130  }
131 }
132 
134  d_bes_conf(config)
135 {
136  // There is always a bes.conf file, even it does not use that exact name.
137  string d_bes_name = d_bes_conf.substr(d_bes_conf.find_last_of('/') + 1);
138  d_pathnames.insert(make_pair(d_bes_name, d_bes_conf));
139 
140  {
141  // There will likely be subordinate config files for each module
142  vector<string> vals;
143  bool found = false;
144  TheBESKeys::TheKeys()->get_values("BES.Include", vals, found);
145  BESDEBUG("besdaemon", "besdaemon: found BES.Include: " << found << endl);
146 
147  // Load the child config file/path names into d_pathnames.
148  if (found)
149  {
150  load_include_files(vals, config);
151  }
152  }
153 
154  if (BESDebug::IsSet("besdaemon"))
155  {
156  map<string, string>::iterator i = d_pathnames.begin();
157  while (i != d_pathnames.end())
158  {
159  BESDEBUG("besdaemon", "besdaemon: d_pathnames: [" << (*i).first << "]: " << d_pathnames[(*i).first] << endl);
160  ++i;
161  }
162  }
163 
164  {
165  bool found = false;
166  TheBESKeys::TheKeys()->get_value("BES.LogName", d_log_file_name, found);
167  if (!found)
168  d_log_file_name = "";
169  }
170 }
171 
177 DaemonCommandHandler::hai_command DaemonCommandHandler::lookup_command(const string &command)
178 {
179  if (command == "StopNow")
180  return HAI_STOP_NOW;
181  else if (command == "Start")
182  return HAI_START;
183  else if (command == "Exit")
184  return HAI_EXIT;
185  else if (command == "GetConfig")
186  return HAI_GET_CONFIG;
187  else if (command == "SetConfig")
188  return HAI_SET_CONFIG;
189  else if (command == "TailLog")
190  return HAI_TAIL_LOG;
191  else if (command == "GetLogContexts")
192  return HAI_GET_LOG_CONTEXTS;
193  else if (command == "SetLogContext")
194  return HAI_SET_LOG_CONTEXT;
195  else
196  return HAI_UNKNOWN;
197 }
198 
204 static char *read_file(const string &name)
205 {
206  char *memblock;
207  ifstream::pos_type size;
208 
209  ifstream file(name.c_str(), ios::in | ios::binary | ios::ate);
210  if (file.is_open())
211  {
212  size = file.tellg();
213  memblock = new char[((unsigned long)size) + 1];
214  file.seekg(0, ios::beg);
215  file.read(memblock, size);
216  file.close();
217 
218  memblock[size] = '\0';
219 
220  return memblock;
221  }
222  else
223  {
224  throw BESInternalError("Could not open config file:" + name, __FILE__, __LINE__);
225  }
226 }
227 
237 static void write_file(const string &name, const string &buffer)
238 {
239  // First write the new text to a temporary file
240  string tmp_name = name + ".tmp";
241  ofstream outfile(tmp_name.c_str(), std::ios_base::out);
242  if (outfile.is_open()) {
243  // write to outfile
244  outfile.write(buffer.data(), buffer.length());
245 
246  outfile.close();
247  }
248  else {
249  throw BESInternalError("Could not open config file:" + name, __FILE__, __LINE__);
250  }
251 
252  // Now see if the original file should be backed up. For any given
253  // instance of the server, only back up on the initial attempt to write a
254  // new version of the file.
255  ostringstream backup_name;
256  backup_name << name << "." << getpid();
257  if (access(backup_name.str().c_str(), F_OK) == -1) {
258  BESDEBUG("besdaemon", "besdaemon: No backup file yet" << endl);
259  // Backup does not exist for this instance of the server; backup name
260  if (rename(name.c_str(), backup_name.str().c_str()) == -1) {
261  BESDEBUG("besdaemon", "besdaemon: Could not backup file " << name << " to " << backup_name.str() << endl);
262  ostringstream err;
263  err << "(" << errno << ") " << strerror(errno);
264  throw BESInternalError("Could not backup config file: " + name + ": " + err.str(), __FILE__, __LINE__);
265  }
266  }
267 
268  // Now move the '.tmp' file to <name>
269  if (rename(tmp_name.c_str(), name.c_str()) == -1) {
270  BESDEBUG("besdaemon", "besdaemon: Could not complete write " << name << " to " << backup_name.str() << endl);
271  ostringstream err;
272  err << "(" << errno << ") " << strerror(errno);
273  throw BESInternalError("Could not write config file:" + name + ": " + err.str(), __FILE__, __LINE__);
274  }
275 }
276 
277 #if 0
278 
279 // This has an infinite loop in it under some circumstances.
280 
291 #define BES_LOG_CHARS_EST_PER_LINE 126
292 
293 static char *get_bes_log_lines(const string &log_file_name, long num_lines)
294 {
295  ifstream infile(log_file_name.c_str(), ios::in | ios::binary);
296  if (!infile.is_open())
297  throw BESInternalError("Could not open file for reading (" + log_file_name + ")", __FILE__, __LINE__);
298 
299  // This is used to save time counting lines in large files
300  static ifstream::pos_type prev_end_pos = 0;
301  // static long prev_line_count = 0;
302 
303  ifstream::pos_type start_from_pos = 0;
304  // num_line == 0 is special value that means get all the lines
305  if (num_lines > 0)
306  {
307  // Get size of file in bytes, then estimate where to start looking for
308  // num_lines lines, then set the file pointer there.
309  infile.seekg(0, ios::end);
310  ifstream::pos_type end_pos = infile.tellg();
311  long est_num_lines = end_pos / BES_LOG_CHARS_EST_PER_LINE;
312  if (num_lines >= est_num_lines)
313  infile.seekg(0, ios::beg);
314  else
315  infile.seekg((est_num_lines - num_lines) * BES_LOG_CHARS_EST_PER_LINE, ios::beg);
316  ifstream::pos_type start_from_pos = infile.tellg();
317 
318  BESDEBUG("besdaemon", "beadaemon: end_pos: " << end_pos << " start_from_pos: " << start_from_pos << endl);
319 
320  // No laughing at my goto...
321  retry:
322  // start_from_pos points to where we start looking for num_lines
323  long count = 0;
324  while (!infile.eof() && !infile.fail())
325  {
326  infile.ignore(1024, '\n'); // Assume no line is > 1024
327  ++count;
328  }
329 
330  infile.clear(); // Needed to reset eof() or fail() so tellg/seekg work.
331 
332  // if the start_from_pos is too far along (there are not num_lines
333  // left), scale it back and try again.
334  if (count < num_lines) {
335  BESDEBUG("besdaemon", "besdaemon: Retrying; Log length (count)" << count << ", num_lines " << num_lines << endl);
336  // 10 isa fudge factor...
337  long size = start_from_pos;
338  size -= ((num_lines - count + 10) * BES_LOG_CHARS_EST_PER_LINE);
339  infile.seekg(size, ios::beg);
340  start_from_pos = infile.tellg();
341  goto retry;
342  }
343 
344  infile.seekg(start_from_pos, ios::beg);
345 
346  BESDEBUG("besdaemon", "besdaemon: Log length (count)" << count << endl);
347 
348  if (count > num_lines)
349  {
350  // Skip count - num-lines
351  long skip = count - num_lines;
352  while (skip > 0 && !infile.eof() && !infile.fail())
353  {
354  infile.ignore(1024, '\n');
355  --skip;
356  }
357 
358  infile.clear();
359  }
360  }
361 
362  // Read remaining lines as a block of stuff.
363  ifstream::pos_type start_pos = infile.tellg();
364  infile.seekg(0, ios::end);
365  ifstream::pos_type end_pos = infile.tellg();
366 
367  unsigned long size = end_pos - start_pos;
368  char *memblock = new char[size + 1];
369 
370  infile.seekg(start_pos, ios::beg);
371  infile.read(memblock, size);
372  infile.close();
373 
374  memblock[size] = '\0';
375 
376  return memblock;
377 }
378 #endif
379 
380 #if 0
381 // This is an older version of get_bes_log_lines(). It's not as inefficient as
382 // the first version, but it's not great either. This version remembers how big
383 // the log was and so skips one of two reads of the entire log. It will still
384 // read the entire log just to print the last 200 lines (the log might be 1 GB).
385 
386 // if num_lines is == 0, get all the lines; if num_lines < 0, also get all the
387 // lines, but this is really an error, should be trapped by caller.
388 static char *get_bes_log_lines(const string &log_file_name, long num_lines)
389 {
390  ifstream infile(log_file_name.c_str(), ios::in | ios::binary);
391  if (!infile.is_open())
392  throw BESInternalError("Could not open file for reading (" + log_file_name + ")", __FILE__, __LINE__);
393 
394  // This is used to save time counting lines in large files
395  static ifstream::pos_type prev_end_pos = 0;
396  static long prev_line_count = 0;
397 
398  BESDEBUG("besdaemon", "besdaemon: prev_line_count: " << prev_line_count << endl);
399  // num_lines == 0 is special value that means get all the lines
400  if (num_lines > 0)
401  {
402  // static values saved from the previous run saves recounting
403  infile.seekg(prev_end_pos, ios::beg);
404  long count = prev_line_count;
405  while (!infile.eof() && !infile.fail())
406  {
407  infile.ignore(1024, '\n');
408  ++count;
409  }
410 
411  infile.clear(); // Needed to reset eof() or fail() so tellg/seekg work.
412 
413  prev_end_pos = infile.tellg(); // Save the end pos
414  prev_line_count = count - 1; // The loop always adds one
415 
416  infile.seekg(0, ios::beg);
417 
418  BESDEBUG("besdaemon", "besdaemon: Log length " << count << endl);
419 
420  if (count > num_lines)
421  {
422  // Skip count - num-lines
423  long skip = count - num_lines;
424  do
425  {
426  infile.ignore(1024, '\n');
427  --skip;
428  } while (skip > 0 && !infile.eof() && !infile.fail());
429 
430  infile.clear();
431  }
432  }
433 
434  // Read remaining lines as a block of stuff.
435  ifstream::pos_type start_pos = infile.tellg();
436  infile.seekg(0, ios::end);
437  ifstream::pos_type end_pos = infile.tellg();
438 
439  unsigned long size = end_pos - start_pos;
440  char *memblock = new char[size + 1];
441 
442  infile.seekg(start_pos, ios::beg);
443  infile.read(memblock, size);
444  infile.close();
445 
446  memblock[size] = '\0';
447 
448  return memblock;
449 }
450 #endif
451 
452 #if 1
453 // Count forward 'lines', leave the file pointer at the place just past that
454 // and return the number of lines actually read (which might be less if eof
455 // is found before 'lines' lines are read.
456 static unsigned long move_forward_lines(ifstream &infile, unsigned long lines)
457 {
458  unsigned long count = 0;
459  while (count < lines && !infile.eof() && !infile.fail()) {
460  infile.ignore(1024, '\n');
461  ++count;
462  }
463 
464  infile.clear(); // Needed to reset eof() or fail() so tellg/seekg work.
465 
466  return count;
467 }
468 
469 // Count the number of lines from pos to the end of the file
470 static unsigned long count_lines(ifstream &infile, ifstream::pos_type pos)
471 {
472  infile.seekg(pos, ios::beg);
473  unsigned long count = 0;
474  while (!infile.eof() && !infile.fail()) {
475  infile.ignore(1024, '\n');
476  ++count;
477  }
478 
479  infile.clear(); // Needed to reset eof() or fail() so tellg/seekg work.
480 
481  return count;
482 }
483 
484 // Starting at wherever the file pointer is at, read to the end and return
485 // the data in a char *. The caller must delete[] the memory.
486 static char *read_file_data(ifstream &infile)
487 {
488  // Read remaining lines as a block of stuff.
489  ifstream::pos_type start_pos = infile.tellg();
490  infile.seekg(0, ios::end);
491  ifstream::pos_type end_pos = infile.tellg();
492 
493  unsigned long size = (end_pos > start_pos) ? end_pos - start_pos : 0;
494  char *memblock = new char[size + 1];
495 
496  infile.seekg(start_pos, ios::beg);
497  infile.read(memblock, size);
498  infile.close();
499 
500  memblock[size] = '\0';
501 
502  return memblock;
503 }
504 
505 // These are used to save time counting lines in large files
506 static ifstream::pos_type last_start_pos = 0;
507 static unsigned long last_start_line = 0;
508 
509 // This is an older version of get_bes_log_lines(). It's not as inefficient as
510 // the first version, but it's not great either. This version remembers how big
511 // the log was and so skips one of two reads of the entire log. It will still
512 // read the entire log just to print the last 200 lines (the log might be 1 MB).
513 
514 // if num_lines is == 0, get all the lines; if num_lines < 0, also get all the
515 // lines, but this is really an error, should be trapped by caller.
516 static char *get_bes_log_lines(const string &log_file_name, unsigned long num_lines)
517 {
518  ifstream infile(log_file_name.c_str(), ios::in | ios::binary);
519  if (!infile.is_open())
520  throw BESInternalError("Could not open file for reading (" + log_file_name + ")", __FILE__, __LINE__);
521 #if 0
522  // This is used to save time counting lines in large files
523  static ifstream::pos_type last_start_pos = 0;
524  static unsigned long last_start_line = 0;
525 #endif
526  BESDEBUG("besdaemon", "besdaemon: last_start_line " << last_start_line << endl);
527  if (num_lines == 0) {
528  // return the whole file
529  infile.seekg(0, ios::beg);
530  return read_file_data(infile);
531  }
532  else {
533  // How many lines in the total file? Use last count info.
534  unsigned long count = count_lines(infile, last_start_pos) + last_start_line;
535  BESDEBUG("besdaemon", "besdaemon: Log length " << count << " (started at " << last_start_line << ")" << endl);
536  // last_start_pos is where last_start_line is, we need to advance to
537  // the line that is num_lines back from the end of the file
538  unsigned long new_start_line = (count >= num_lines) ? count - num_lines + 1: 0;
539  // Now go back to the last_start_pos
540  infile.seekg(last_start_pos, ios::beg);
541  // and count forward to the line that starts this last num_lines
542  count = move_forward_lines(infile, new_start_line - last_start_line);
543  BESDEBUG("besdaemon", "besdaemon: count forward " << count << " lines." << endl);
544  // Save this point for the next time
545  last_start_line = new_start_line;
546  last_start_pos = infile.tellg();
547 
548  return read_file_data(infile);
549  }
550 }
551 #endif
552 
559 void DaemonCommandHandler::execute_command(const string &command, BESXMLWriter &writer)
560 {
561  LIBXML_TEST_VERSION;
562 
563  xmlDoc *doc = NULL;
564  xmlNode *root_element = NULL;
565  xmlNode *current_node = NULL;
566 
567  try {
568  // set the default error function to my own
569  vector<string> parseerrors;
570  xmlSetGenericErrorFunc((void *) &parseerrors, BESXMLUtils::XMLErrorFunc);
571 #if 0
572  // We would like this, but older versions of libxml don't use 'const'.
573  // Older == 2.6.16. jhrg 12.13.11
574  doc = xmlParseDoc((const xmlChar*) command.c_str());
575 #else
576  doc = xmlParseDoc((xmlChar*) command.c_str());
577 #endif
578  if (doc == NULL) {
579  string err = "";
580  bool isfirst = true;
581  vector<string>::const_iterator i = parseerrors.begin();
582  vector<string>::const_iterator e = parseerrors.end();
583  for (; i != e; i++) {
584  if (!isfirst && (*i).compare(0, 6, "Entity") == 0) {
585  err += "\n";
586  }
587  err += (*i);
588  isfirst = false;
589  }
590 
591  throw BESSyntaxUserError(err, __FILE__, __LINE__);
592  }
593 
594  // get the root element and make sure it exists and is called request
595  root_element = xmlDocGetRootElement(doc);
596  if (!root_element) {
597  throw BESSyntaxUserError("There is no root element in the xml document", __FILE__, __LINE__);
598  }
599 
600  string root_name;
601  string root_val;
602  map<string, string> props;
603  BESXMLUtils::GetNodeInfo(root_element, root_name, root_val, props);
604  if (root_name != "BesAdminCmd") {
605  string err = (string) "The root element should be a BesAdminCmd element, name is " + (char *) root_element->name;
606  throw BESSyntaxUserError(err, __FILE__, __LINE__);
607  }
608  if (root_val != "") {
609  string err = (string) "The BesAdminCmd element must not contain a value, " + root_val;
610  throw BESSyntaxUserError(err, __FILE__, __LINE__);
611  }
612 
613  // iterate through the children of the request element. Each child is an
614  // individual command.
615  current_node = root_element->children;
616 
617  while (current_node) {
618  if (current_node->type == XML_ELEMENT_NODE) {
619  string node_name = (char *) current_node->name;
620  BESDEBUG("besdaemon", "besdaemon: looking for command " << node_name << endl);
621  // ***
622  // cerr << "Processing command " << node_name << endl;
623 
624  // While processing a command, block signals, which can also
625  // be used to control the master beslistener. unblock at the
626  // end of the while loop.
627 
628  switch (lookup_command(node_name)) {
629  case HAI_STOP_NOW:
630  BESDEBUG("besdaemon", "besdaemon: Received StopNow" << endl);
631 
632  if (stop_all_beslisteners(SIGTERM) == false) {
634  throw BESInternalFatalError("Could not stop the master beslistener", __FILE__, __LINE__);
635  }
636  else {
637  throw BESSyntaxUserError("Received Stop command but the master beslistener was likely already stopped", __FILE__, __LINE__);
638  }
639  }
640  else {
641  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
642  throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
643  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
644  throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
645  }
646  break;
647 
648  case HAI_START: {
649  BESDEBUG("besdaemon", "besdaemon: Received Start" << endl);
650  // start_master_beslistener assigns the mbes pid to a
651  // static global defined in daemon.cc that stop_all_bes...
652  // uses.
654  throw BESSyntaxUserError("Received Start command but the master beslistener is already running", __FILE__, __LINE__);
655  }
656 
657  if (start_master_beslistener() == 0) {
658  BESDEBUG("besdaemon", "besdaemon: Error starting; master_beslistener_status = " << master_beslistener_status << endl);
660  throw BESSyntaxUserError("Received Start command but the master beslistener is already running", __FILE__, __LINE__);
661  }
662  else {
663  throw BESInternalFatalError("Could not start the master beslistener", __FILE__, __LINE__);
664  }
665  }
666  else {
667  // Whenever the master listener starts, it makes a new log file. Reset the counters used to
668  // record the 'last line read' position - these variables are part of an optimization
669  // to limit re-reading old sections of the log file.
670  last_start_pos = 0;
671  last_start_line = 0;
672 
673  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
674  throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
675  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
676  throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
677  }
678  break;
679  }
680 
681  case HAI_EXIT:
682  BESDEBUG("besdaemon", "besdaemon: Received Exit" << endl);
683  stop_all_beslisteners(SIGTERM);
684  unblock_signals(); // called here because we're about to exit
685  exit(0);
686  break;
687 
688  case HAI_GET_CONFIG: {
689  BESDEBUG("besdaemon", "besdaemon: Received GetConfig" << endl);
690 
691  if (d_pathnames.empty()) {
692  throw BESInternalFatalError("There are no known configuration files for this BES!", __FILE__, __LINE__);
693  }
694 
695  // For each of the configuration files, send an XML
696  // <BesConfig module="" /> element.
697  map<string, string>::iterator i = d_pathnames.begin();
698  while (i != d_pathnames.end()) {
699  BESDEBUG("besdaemon", "besdaemon: d_pathnames: [" << (*i).first << "]: " << d_pathnames[(*i).first] << endl);
700 
701  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:BesConfig") < 0)
702  throw BESInternalFatalError("Could not write <hai:Config> element ", __FILE__, __LINE__);
703 
704  if (xmlTextWriterWriteAttribute(writer.get_writer(), (const xmlChar*) "module", (const xmlChar*) (*i).first.c_str()) < 0)
705  throw BESInternalFatalError("Could not write fileName attribute ", __FILE__, __LINE__);
706 
707  char *content = read_file(d_pathnames[(*i).first]);
708  try {
709  BESDEBUG("besdaemon_verbose", "besdaemon: content: " << content << endl);
710  if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*)"\n") < 0)
711  throw BESInternalFatalError("Could not write newline", __FILE__, __LINE__);
712 
713  if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*)content) < 0)
714  throw BESInternalFatalError("Could not write line", __FILE__, __LINE__);
715 
716  delete [] content; content = 0;
717  }
718  catch (...) {
719  delete [] content; content = 0;
720  throw;
721  }
722 
723  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
724  throw BESInternalFatalError("Could not end <hai:BesConfig> element ", __FILE__, __LINE__);
725  ++i;
726  }
727 
728  break;
729  }
730 
731  case HAI_SET_CONFIG: {
732  BESDEBUG("besdaemon", "besdaemon: Received SetConfig" << endl);
733  xmlChar *xml_char_module = xmlGetProp(current_node, (const xmlChar*) "module");
734  if (!xml_char_module) {
735  throw BESSyntaxUserError("SetConfig missing module ", __FILE__, __LINE__);
736  }
737  string module = (const char *) xml_char_module;
738  xmlFree(xml_char_module);
739 
740  BESDEBUG("besdaemon", "besdaemon: Received SetConfig; module: " << module << endl);
741 
742  xmlChar *file_content = xmlNodeListGetString(doc, current_node->children, /* inLine = */true);
743  if (!file_content) {
744  throw BESInternalFatalError("SetConfig missing content, no changes made ", __FILE__, __LINE__);
745  }
746  string content = (const char *) file_content;
747  xmlFree(file_content);
748  BESDEBUG("besdaemon_verbose", "besdaemon: Received SetConfig; content: " << endl << content << endl);
749 
750  write_file(d_pathnames[module], content);
751 
752  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
753  throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
754 
755  if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*) "\nPlease restart the server for these changes to take affect.\n") < 0)
756  throw BESInternalFatalError("Could not write newline", __FILE__, __LINE__);
757 
758  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
759  throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
760 
761  break;
762  }
763 
764  case HAI_TAIL_LOG: {
765  BESDEBUG("besdaemon", "besdaemon: Received TailLog" << endl);
766 
767  xmlChar *xml_char_lines = xmlGetProp(current_node, (const xmlChar*) "lines");
768  if (!xml_char_lines) {
769  throw BESSyntaxUserError("TailLog missing lines attribute ", __FILE__, __LINE__);
770  }
771 
772  char *endptr;
773  long num_lines = strtol((const char *) xml_char_lines, &endptr, 10 /*base*/);
774  if (num_lines == 0 && endptr == (const char *) xml_char_lines) {
775  ostringstream err;
776  err << "(" << errno << ") " << strerror(errno);
777  throw BESSyntaxUserError("TailLog lines attribute bad value: " + err.str(), __FILE__, __LINE__);
778  }
779 
780  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:BesLog") < 0)
781  throw BESInternalFatalError("Could not write <hai:BesLog> element ", __FILE__, __LINE__);
782 
783  BESDEBUG("besdaemon", "besdaemon: TailLog: log file:" << d_log_file_name << ", lines: " << num_lines << endl);
784 
785  char *content = get_bes_log_lines(d_log_file_name, num_lines);
786  try {
787  BESDEBUG("besdaemon_verbose", "besdaemon: Returned lines: " << content << endl);
788  if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*)"\n") < 0)
789  throw BESInternalFatalError("Could not write newline", __FILE__, __LINE__);
790 
791  if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*)content) < 0)
792  throw BESInternalFatalError("Could not write line", __FILE__, __LINE__);
793 
794  delete [] content; content = 0;
795  }
796  catch (...) {
797  delete [] content; content = 0;
798  throw;
799  }
800 
801  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
802  throw BESInternalFatalError("Could not end <hai:BesLog> element ", __FILE__, __LINE__);
803 
804  break;
805  }
806 
807  case HAI_GET_LOG_CONTEXTS: {
808  BESDEBUG("besdaemon", "besdaemon: Received GetLogContexts" << endl);
809 
810  BESDEBUG("besdaemon", "besdaemon: There are " << BESDebug::debug_map().size() << " Contexts" << endl);
811  if (BESDebug::debug_map().size()) {
813  while (i != BESDebug::debug_map().end()) {
814  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:LogContext") < 0)
815  throw BESInternalFatalError("Could not write <hai:LogContext> element ", __FILE__, __LINE__);
816 
817  if (xmlTextWriterWriteAttribute(writer.get_writer(), (const xmlChar*) "name", (const xmlChar*) (*i).first.c_str()) < 0)
818  throw BESInternalFatalError("Could not write 'name' attribute ", __FILE__, __LINE__);
819 
820  string state = (*i).second ? "on" : "off";
821  if (xmlTextWriterWriteAttribute(writer.get_writer(), (const xmlChar*) "state", (const xmlChar*) state.c_str()) < 0)
822  throw BESInternalFatalError("Could not write 'state' attribute ", __FILE__, __LINE__);
823 
824  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
825  throw BESInternalFatalError("Could not end <hai:LogContext> element ", __FILE__, __LINE__);
826 
827  ++i;
828  }
829  }
830 
831  break;
832  }
833 
834  case HAI_SET_LOG_CONTEXT: {
835  BESDEBUG("besdaemon", "besdaemon: Received SetLogContext" << endl);
836 
837  xmlChar *xml_char_module = xmlGetProp(current_node, (const xmlChar*)"name");
838  if (!xml_char_module) {
839  throw BESSyntaxUserError("SetLogContext missing name ", __FILE__, __LINE__);
840  }
841  string name = (const char *)xml_char_module;
842  xmlFree(xml_char_module);
843 
844  xml_char_module = xmlGetProp(current_node, (const xmlChar*)"state");
845  if (!xml_char_module) {
846  throw BESSyntaxUserError("SetLogContext missing state ", __FILE__, __LINE__);
847  }
848  bool state = strcmp((const char *)xml_char_module, "on") == 0;
849  xmlFree(xml_char_module);
850 
851  BESDEBUG("besdaemon", "besdaemon: before setting " << name << " to " << state << endl);
852  // ***
853  // cerr << "setting context " << name << " to " << state << endl;
854 
855  // Setting this here is all we have to do. This will
856  // change the debug/log settings for the daemon and
857  // (See damon.cc update_beslistener_args()) cause the
858  // new settings to be passed onto new beslisteners.
859  BESDebug::Set( name, state );
860 
861  BESDEBUG("besdaemon", "besdaemon: after setting " << name << " to " << state << endl);
862 
863  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
864  throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
865  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
866  throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
867 
868  break;
869  }
870 
871  default:
872  throw BESSyntaxUserError("Command " + node_name + " unknown.", __FILE__, __LINE__);
873  }
874  // ***
875  // cerr << "Completed command: " << node_name << endl;
876  }
877 
878  current_node = current_node->next;
879  }
880  }
881  catch (BESError &e) {
882  xmlFreeDoc(doc);
883  throw e;
884  }
885  catch (...) {
886  xmlFreeDoc(doc);
887  throw;
888  }
889 
890  xmlFreeDoc(doc);
891 }
892 
893 static void send_bes_error(BESXMLWriter &writer, BESError &e)
894 {
895  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:BESError") < 0)
896  throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
897 
898  ostringstream oss;
899  oss << e.get_error_type() << std::ends;
900  if (xmlTextWriterWriteElement(writer.get_writer(), (const xmlChar*) "hai:Type", (const xmlChar*) oss.str().c_str()) < 0)
901  throw BESInternalFatalError("Could not write <hai:Type> element ", __FILE__, __LINE__);
902 
903  if (xmlTextWriterWriteElement(writer.get_writer(), (const xmlChar*) "hai:Message", (const xmlChar*) e.get_message().c_str()) < 0)
904  throw BESInternalFatalError("Could not write <hai:Message> element ", __FILE__, __LINE__);
905 
906  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
907  throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
908 }
909 
911 {
912 #if 0
913  // Use this for some simple white-listing of allowed clients?
914  ostringstream strm;
915  string ip = c->getSocket()->getIp();
916  strm << "ip " << ip << ", port " << c->getSocket()->getPort();
917  string from = strm.str();
918 #endif
919  map<string, string> extensions;
920  ostringstream ss;
921 
922  bool done = false;
923  while (!done)
924  done = c->receive(extensions, &ss);
925 
926  if (extensions["status"] == c->exit()) {
927  // When the client communicating with the besdaemon exits,
928  // return control to the PPTServer::initConnection() method which
929  // will listen for another connect request.
930  return;
931  }
932 
933  int descript = c->getSocket()->getSocketDescriptor();
934  unsigned int bufsize = c->getSendChunkSize();
935  PPTStreamBuf fds(descript, bufsize);
936 
937  std::streambuf *holder;
938  holder = cout.rdbuf();
939  cout.rdbuf(&fds);
940 
941  BESXMLWriter writer;
942 
943  try {
944  BESDEBUG("besdaemon", "besdaemon: cmd: " << ss.str() << endl);
945  // runs the command(s); throws on an error.
946  execute_command(ss.str(), writer);
947 
948  cout << writer.get_doc() << endl;
949  fds.finish();
950  cout.rdbuf(holder);
951  }
952  catch (BESError &e) {
953  // an error has occurred.
954  // flush what we have in the stream to the client
955  cout << flush;
956 
957  // Send the extension status=error to the client so that it
958  // can reset.
959  map<string, string> extensions;
960  extensions["status"] = "error";
961 
962  switch (e.get_error_type()) {
963  case BES_INTERNAL_ERROR:
965  BESDEBUG("besdaemon", "besdaemon: Internal/Fatal Error: " << e.get_message() << endl);
966  extensions["exit"] = "true";
967  c->sendExtensions(extensions);
968  send_bes_error(writer, e);
969  // Send the BESError
970 #if 0
971  // This seemed like a good idea, but really, no error is
972  // fatal, at least not yet.
973  cout << writer.get_doc() << endl;
974  fds.finish(); // we are finished, send the last chunk
975  cout.rdbuf(holder); // reset the streams buffer
976  return; // EXIT; disconnects from client
977 #endif
978  break;
979 
981  // cerr << "syntax error" << endl;
982  BESDEBUG("besdaemon", "besdaemon: Syntax Error: " << e.get_message() << endl);
983  c->sendExtensions(extensions);
984  // Send the BESError
985  send_bes_error(writer, e);
986  break;
987 
988  default:
989  BESDEBUG("besdaemon", "besdaemon: Error (unknown command): " << ss.str() << endl);
990  extensions["exit"] = "true";
991  c->sendExtensions(extensions);
992  // Send the BESError
993  send_bes_error(writer, e);
994  break;
995 
996  }
997 
998  cout << writer.get_doc() << endl;
999  fds.finish(); // we are finished, send the last chunk
1000  cout.rdbuf(holder); // reset the streams buffer
1001  }
1002 }
1003 
1010 void DaemonCommandHandler::dump(ostream &strm) const
1011 {
1012  strm << BESIndent::LMarg << "DaemonCommandHandler::dump - (" << (void *) this << ")" << endl;
1013 }
1014