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., 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 
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 #if 0
266 
267 // This has an infinite loop in it under some circumstances.
268 
279 #define BES_LOG_CHARS_EST_PER_LINE 126
280 
281 static char *get_bes_log_lines(const string &log_file_name, long num_lines)
282 {
283  ifstream infile(log_file_name.c_str(), ios::in | ios::binary);
284  if (!infile.is_open())
285  throw BESInternalError("Could not open file for reading (" + log_file_name + ")", __FILE__, __LINE__);
286 
287  // This is used to save time counting lines in large files
288  static ifstream::pos_type prev_end_pos = 0;
289  // static long prev_line_count = 0;
290 
291  ifstream::pos_type start_from_pos = 0;
292  // num_line == 0 is special value that means get all the lines
293  if (num_lines > 0)
294  {
295  // Get size of file in bytes, then estimate where to start looking for
296  // num_lines lines, then set the file pointer there.
297  infile.seekg(0, ios::end);
298  ifstream::pos_type end_pos = infile.tellg();
299  long est_num_lines = end_pos / BES_LOG_CHARS_EST_PER_LINE;
300  if (num_lines >= est_num_lines)
301  infile.seekg(0, ios::beg);
302  else
303  infile.seekg((est_num_lines - num_lines) * BES_LOG_CHARS_EST_PER_LINE, ios::beg);
304  ifstream::pos_type start_from_pos = infile.tellg();
305 
306  BESDEBUG("besdaemon", "DaemonCommandHandler()::get_bes_log_lines() - end_pos: " << end_pos << " start_from_pos: " << start_from_pos << endl);
307 
308  // No laughing at my goto...
309  retry:
310  // start_from_pos points to where we start looking for num_lines
311  long count = 0;
312  while (!infile.eof() && !infile.fail())
313  {
314  infile.ignore(1024, '\n'); // Assume no line is > 1024
315  ++count;
316  }
317 
318  infile.clear(); // Needed to reset eof() or fail() so tellg/seekg work.
319 
320  // if the start_from_pos is too far along (there are not num_lines
321  // left), scale it back and try again.
322  if (count < num_lines) {
323  BESDEBUG("besdaemon", "DaemonCommandHandler()::get_bes_log_lines() - Retrying; Log length (count)" << count << ", num_lines " << num_lines << endl);
324  // 10 isa fudge factor...
325  long size = start_from_pos;
326  size -= ((num_lines - count + 10) * BES_LOG_CHARS_EST_PER_LINE);
327  infile.seekg(size, ios::beg);
328  start_from_pos = infile.tellg();
329  goto retry;
330  }
331 
332  infile.seekg(start_from_pos, ios::beg);
333 
334  BESDEBUG("besdaemon", "DaemonCommandHandler()::get_bes_log_lines() - Log length (count)" << count << endl);
335 
336  if (count > num_lines)
337  {
338  // Skip count - num-lines
339  long skip = count - num_lines;
340  while (skip > 0 && !infile.eof() && !infile.fail())
341  {
342  infile.ignore(1024, '\n');
343  --skip;
344  }
345 
346  infile.clear();
347  }
348  }
349 
350  // Read remaining lines as a block of stuff.
351  ifstream::pos_type start_pos = infile.tellg();
352  infile.seekg(0, ios::end);
353  ifstream::pos_type end_pos = infile.tellg();
354 
355  unsigned long size = end_pos - start_pos;
356  char *memblock = new char[size + 1];
357 
358  infile.seekg(start_pos, ios::beg);
359  infile.read(memblock, size);
360  infile.close();
361 
362  memblock[size] = '\0';
363 
364  return memblock;
365 }
366 #endif
367 
368 #if 0
369 // This is an older version of get_bes_log_lines(). It's not as inefficient as
370 // the first version, but it's not great either. This version remembers how big
371 // the log was and so skips one of two reads of the entire log. It will still
372 // read the entire log just to print the last 200 lines (the log might be 1 GB).
373 
374 // if num_lines is == 0, get all the lines; if num_lines < 0, also get all the
375 // lines, but this is really an error, should be trapped by caller.
376 static char *get_bes_log_lines(const string &log_file_name, long num_lines)
377 {
378  ifstream infile(log_file_name.c_str(), ios::in | ios::binary);
379  if (!infile.is_open())
380  throw BESInternalError("Could not open file for reading (" + log_file_name + ")", __FILE__, __LINE__);
381 
382  // This is used to save time counting lines in large files
383  static ifstream::pos_type prev_end_pos = 0;
384  static long prev_line_count = 0;
385 
386  BESDEBUG("besdaemon", "DaemonCommandHandler()::get_bes_log_lines() - prev_line_count: " << prev_line_count << endl);
387  // num_lines == 0 is special value that means get all the lines
388  if (num_lines > 0)
389  {
390  // static values saved from the previous run saves recounting
391  infile.seekg(prev_end_pos, ios::beg);
392  long count = prev_line_count;
393  while (!infile.eof() && !infile.fail())
394  {
395  infile.ignore(1024, '\n');
396  ++count;
397  }
398 
399  infile.clear(); // Needed to reset eof() or fail() so tellg/seekg work.
400 
401  prev_end_pos = infile.tellg();// Save the end pos
402  prev_line_count = count - 1;// The loop always adds one
403 
404  infile.seekg(0, ios::beg);
405 
406  BESDEBUG("besdaemon", "DaemonCommandHandler()::get_bes_log_lines() - Log length " << count << endl);
407 
408  if (count > num_lines)
409  {
410  // Skip count - num-lines
411  long skip = count - num_lines;
412  do
413  {
414  infile.ignore(1024, '\n');
415  --skip;
416  }while (skip > 0 && !infile.eof() && !infile.fail());
417 
418  infile.clear();
419  }
420  }
421 
422  // Read remaining lines as a block of stuff.
423  ifstream::pos_type start_pos = infile.tellg();
424  infile.seekg(0, ios::end);
425  ifstream::pos_type end_pos = infile.tellg();
426 
427  unsigned long size = end_pos - start_pos;
428  char *memblock = new char[size + 1];
429 
430  infile.seekg(start_pos, ios::beg);
431  infile.read(memblock, size);
432  infile.close();
433 
434  memblock[size] = '\0';
435 
436  return memblock;
437 }
438 #endif
439 
440 #if 1
441 // Count forward 'lines', leave the file pointer at the place just past that
442 // and return the number of lines actually read (which might be less if eof
443 // is found before 'lines' lines are read.
444 static unsigned long move_forward_lines(ifstream &infile, unsigned long lines)
445 {
446  unsigned long count = 0;
447  while (count < lines && !infile.eof() && !infile.fail()) {
448  infile.ignore(1024, '\n');
449  ++count;
450  }
451 
452  infile.clear(); // Needed to reset eof() or fail() so tellg/seekg work.
453 
454  return count;
455 }
456 
457 // Count the number of lines from pos to the end of the file
458 static unsigned long count_lines(ifstream &infile, ifstream::pos_type pos)
459 {
460  infile.seekg(pos, ios::beg);
461  unsigned long count = 0;
462  while (!infile.eof() && !infile.fail()) {
463  infile.ignore(1024, '\n');
464  ++count;
465  }
466 
467  infile.clear(); // Needed to reset eof() or fail() so tellg/seekg work.
468 
469  return count;
470 }
471 
472 // Starting at wherever the file pointer is at, read to the end and return
473 // the data in a char *. The caller must delete[] the memory.
474 static char *read_file_data(ifstream &infile)
475 {
476  // Read remaining lines as a block of stuff.
477  ifstream::pos_type start_pos = infile.tellg();
478  infile.seekg(0, ios::end);
479  ifstream::pos_type end_pos = infile.tellg();
480 
481  unsigned long size = (end_pos > start_pos) ? end_pos - start_pos : 0;
482  char *memblock = new char[size + 1];
483 
484  infile.seekg(start_pos, ios::beg);
485  infile.read(memblock, size);
486  infile.close();
487 
488  memblock[size] = '\0';
489 
490  return memblock;
491 }
492 
493 // These are used to save time counting lines in large files
494 static ifstream::pos_type last_start_pos = 0;
495 static unsigned long last_start_line = 0;
496 
497 // This is an older version of get_bes_log_lines(). It's not as inefficient as
498 // the first version, but it's not great either. This version remembers how big
499 // the log was and so skips one of two reads of the entire log. It will still
500 // read the entire log just to print the last 200 lines (the log might be 1 MB).
501 
502 // if num_lines is == 0, get all the lines; if num_lines < 0, also get all the
503 // lines, but this is really an error, should be trapped by caller.
504 static char *get_bes_log_lines(const string &log_file_name, unsigned long num_lines)
505 {
506  ifstream infile(log_file_name.c_str(), ios::in | ios::binary);
507  if (!infile.is_open())
508  throw BESInternalError("Could not open file for reading (" + log_file_name + ")", __FILE__, __LINE__);
509 #if 0
510  // This is used to save time counting lines in large files
511  static ifstream::pos_type last_start_pos = 0;
512  static unsigned long last_start_line = 0;
513 #endif
514  BESDEBUG("besdaemon", "besdaemon: last_start_line " << last_start_line << endl);
515  if (num_lines == 0) {
516  // return the whole file
517  infile.seekg(0, ios::beg);
518  return read_file_data(infile);
519  }
520  else {
521  // How many lines in the total file? Use last count info.
522  unsigned long count = count_lines(infile, last_start_pos) + last_start_line;
523  BESDEBUG("besdaemon", "besdaemon: Log length " << count << " (started at " << last_start_line << ")" << endl);
524  // last_start_pos is where last_start_line is, we need to advance to
525  // the line that is num_lines back from the end of the file
526  unsigned long new_start_line = (count >= num_lines) ? count - num_lines + 1 : 0;
527  // Now go back to the last_start_pos
528  infile.seekg(last_start_pos, ios::beg);
529  // and count forward to the line that starts this last num_lines
530  count = move_forward_lines(infile, new_start_line - last_start_line);
531  BESDEBUG("besdaemon", "besdaemon: count forward " << count << " lines." << endl);
532  // Save this point for the next time
533  last_start_line = new_start_line;
534  last_start_pos = infile.tellg();
535 
536  return read_file_data(infile);
537  }
538 }
539 #endif
540 
547 void DaemonCommandHandler::execute_command(const string &command, BESXMLWriter &writer)
548 {
549  xmlDoc *doc = NULL;
550  xmlNode *root_element = NULL;
551  xmlNode *current_node = NULL;
552 
553  try {
554  // set the default error function to my own
555  vector<string> parseerrors;
556  xmlSetGenericErrorFunc((void *) &parseerrors, BESXMLUtils::XMLErrorFunc);
557 #if 0
558  // We would like this, but older versions of libxml don't use 'const'.
559  // Older == 2.6.16. jhrg 12.13.11
560  doc = xmlParseDoc((const xmlChar*) command.c_str());
561 #else
562  doc = xmlParseDoc((xmlChar*) command.c_str());
563 #endif
564  if (doc == NULL) {
565  string err = "";
566  bool isfirst = true;
567  vector<string>::const_iterator i = parseerrors.begin();
568  vector<string>::const_iterator e = parseerrors.end();
569  for (; i != e; i++) {
570  if (!isfirst && (*i).compare(0, 6, "Entity") == 0) {
571  err += "\n";
572  }
573  err += (*i);
574  isfirst = false;
575  }
576 
577  throw BESSyntaxUserError(err, __FILE__, __LINE__);
578  }
579 
580  // get the root element and make sure it exists and is called request
581  root_element = xmlDocGetRootElement(doc);
582  if (!root_element) {
583  throw BESSyntaxUserError("There is no root element in the xml document", __FILE__, __LINE__);
584  }
585 
586  string root_name;
587  string root_val;
588  map<string, string> props;
589  BESXMLUtils::GetNodeInfo(root_element, root_name, root_val, props);
590  if (root_name != "BesAdminCmd") {
591  string err = (string) "The root element should be a BesAdminCmd element, name is "
592  + (char *) root_element->name;
593  throw BESSyntaxUserError(err, __FILE__, __LINE__);
594  }
595  if (root_val != "") {
596  string err = (string) "The BesAdminCmd element must not contain a value, " + root_val;
597  throw BESSyntaxUserError(err, __FILE__, __LINE__);
598  }
599 
600  // iterate through the children of the request element. Each child is an
601  // individual command.
602  current_node = root_element->children;
603 
604  while (current_node) {
605  if (current_node->type == XML_ELEMENT_NODE) {
606  string node_name = (char *) current_node->name;
607  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Looking for command " << node_name << endl);
608  // ***
609  // cerr << "Processing command " << node_name << endl;
610 
611  // While processing a command, block signals, which can also
612  // be used to control the master beslistener. unblock at the
613  // end of the while loop.
614 
615  switch (lookup_command(node_name)) {
616  case HAI_STOP_NOW:
617  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received StopNow" << endl);
618 
619  if (stop_all_beslisteners(SIGTERM) == false) {
621  throw BESInternalFatalError("Could not stop the master beslistener", __FILE__, __LINE__);
622  }
623  else {
624  throw BESSyntaxUserError(
625  "Received Stop command but the master beslistener was likely already stopped",
626  __FILE__, __LINE__);
627  }
628  }
629  else {
630  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
631  throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
632  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
633  throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
634  }
635  break;
636 
637  case HAI_START: {
638  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received Start" << endl);
639  // start_master_beslistener assigns the mbes pid to a
640  // static global defined in daemon.cc that stop_all_bes...
641  // uses.
643  throw BESSyntaxUserError("Received Start command but the master beslistener is already running",
644  __FILE__, __LINE__);
645  }
646 
647  if (start_master_beslistener() == 0) {
648  BESDEBUG("besdaemon",
649  "DaemonCommandHandler::execute_command() - Error starting; master_beslistener_status = " << master_beslistener_status << endl);
651  throw BESSyntaxUserError(
652  "Received Start command but the master beslistener is already running", __FILE__,
653  __LINE__);
654  }
655  else {
656  throw BESInternalFatalError("Could not start the master beslistener", __FILE__, __LINE__);
657  }
658  }
659  else {
660  // Whenever the master listener starts, it makes a new log file. Reset the counters used to
661  // record the 'last line read' position - these variables are part of an optimization
662  // to limit re-reading old sections of the log file.
663  last_start_pos = 0;
664  last_start_line = 0;
665 
666  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
667  throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
668  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
669  throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
670  }
671  break;
672  }
673 
674  case HAI_EXIT:
675  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received Exit" << endl);
676  stop_all_beslisteners(SIGTERM);
677  unblock_signals(); // called here because we're about to exit
678  exit(0);
679  break;
680 
681  case HAI_GET_CONFIG: {
682  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received GetConfig" << endl);
683 
684  if (d_pathnames.empty()) {
685  throw BESInternalFatalError("There are no known configuration files for this BES!", __FILE__,
686  __LINE__);
687  }
688 
689  // For each of the configuration files, send an XML
690  // <BesConfig module="" /> element.
691  map<string, string>::iterator i = d_pathnames.begin();
692  while (i != d_pathnames.end()) {
693  BESDEBUG("besdaemon",
694  "DaemonCommandHandler::execute_command() - Retrieving " << (*i).first << ": " << d_pathnames[(*i).first] << endl);
695 
696  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:BesConfig") < 0)
697  throw BESInternalFatalError("Could not write <hai:Config> element ", __FILE__, __LINE__);
698 
699  if (xmlTextWriterWriteAttribute(writer.get_writer(), (const xmlChar*) "module",
700  (const xmlChar*) (*i).first.c_str()) < 0)
701  throw BESInternalFatalError("Could not write fileName attribute ", __FILE__, __LINE__);
702 
703  char *content = read_file(d_pathnames[(*i).first]);
704  try {
705  BESDEBUG("besdaemon_verbose", "DaemonCommandHandler::execute_command() - content: " << content << endl);
706  if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*) "\n") < 0)
707  throw BESInternalFatalError("Could not write newline", __FILE__, __LINE__);
708 
709  if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*) content) < 0)
710  throw BESInternalFatalError("Could not write line", __FILE__, __LINE__);
711 
712  delete[] content;
713  content = 0;
714  }
715  catch (...) {
716  delete[] content;
717  content = 0;
718  throw;
719  }
720 
721  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
722  throw BESInternalFatalError("Could not end <hai:BesConfig> element ", __FILE__, __LINE__);
723  ++i;
724  }
725 
726  break;
727  }
728 
729  case HAI_SET_CONFIG: {
730  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received SetConfig" << endl);
731  xmlChar *xml_char_module = xmlGetProp(current_node, (const xmlChar*) "module");
732  if (!xml_char_module) {
733  throw BESSyntaxUserError("SetConfig missing module ", __FILE__, __LINE__);
734  }
735  string module = (const char *) xml_char_module;
736  xmlFree(xml_char_module);
737 
738  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received SetConfig; module: " << module << endl);
739 
740  xmlChar *file_content = xmlNodeListGetString(doc, current_node->children, /* inLine = */true);
741  if (!file_content) {
742  throw BESInternalFatalError("SetConfig missing content, no changes made ", __FILE__, __LINE__);
743  }
744  string content = (const char *) file_content;
745  xmlFree(file_content);
746  BESDEBUG("besdaemon_verbose",
747  "DaemonCommandHandler::execute_command() - Received SetConfig; content: " << endl << content << endl);
748 
749  write_file(d_pathnames[module], content);
750 
751  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
752  throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
753 
754  if (xmlTextWriterWriteString(writer.get_writer(),
755  (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", "DaemonCommandHandler::execute_command() - 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",
784  "DaemonCommandHandler::execute_command() - TailLog: log file:" << d_log_file_name << ", lines: " << num_lines << endl);
785 
786  char *content = get_bes_log_lines(d_log_file_name, num_lines);
787  try {
788  BESDEBUG("besdaemon_verbose", "DaemonCommandHandler::execute_command() - Returned lines: " << content << endl);
789  if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*) "\n") < 0)
790  throw BESInternalFatalError("Could not write newline", __FILE__, __LINE__);
791 
792  if (xmlTextWriterWriteString(writer.get_writer(), (const xmlChar*) content) < 0)
793  throw BESInternalFatalError("Could not write line", __FILE__, __LINE__);
794 
795  delete[] content;
796  content = 0;
797  }
798  catch (...) {
799  delete[] content;
800  content = 0;
801  throw;
802  }
803 
804  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
805  throw BESInternalFatalError("Could not end <hai:BesLog> element ", __FILE__, __LINE__);
806 
807  break;
808  }
809 
810  case HAI_GET_LOG_CONTEXTS: {
811  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received GetLogContexts" << endl);
812 
813  BESDEBUG("besdaemon",
814  "DaemonCommandHandler::execute_command() - There are " << BESDebug::debug_map().size() << " Contexts" << endl);
815  if (BESDebug::debug_map().size()) {
817  while (i != BESDebug::debug_map().end()) {
818  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:LogContext") < 0)
819  throw BESInternalFatalError("Could not write <hai:LogContext> element ", __FILE__,
820  __LINE__);
821 
822  if (xmlTextWriterWriteAttribute(writer.get_writer(), (const xmlChar*) "name",
823  (const xmlChar*) (*i).first.c_str()) < 0)
824  throw BESInternalFatalError("Could not write 'name' attribute ", __FILE__, __LINE__);
825 
826  string state = (*i).second ? "on" : "off";
827  if (xmlTextWriterWriteAttribute(writer.get_writer(), (const xmlChar*) "state",
828  (const xmlChar*) state.c_str()) < 0)
829  throw BESInternalFatalError("Could not write 'state' attribute ", __FILE__, __LINE__);
830 
831  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
832  throw BESInternalFatalError("Could not end <hai:LogContext> element ", __FILE__,
833  __LINE__);
834 
835  ++i;
836  }
837  }
838 
839  break;
840  }
841 
842  case HAI_SET_LOG_CONTEXT: {
843  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Received SetLogContext" << endl);
844 
845  xmlChar *xml_char_module = xmlGetProp(current_node, (const xmlChar*) "name");
846  if (!xml_char_module) {
847  throw BESSyntaxUserError("SetLogContext missing name ", __FILE__, __LINE__);
848  }
849  string name = (const char *) xml_char_module;
850  xmlFree(xml_char_module);
851 
852  xml_char_module = xmlGetProp(current_node, (const xmlChar*) "state");
853  if (!xml_char_module) {
854  throw BESSyntaxUserError("SetLogContext missing state ", __FILE__, __LINE__);
855  }
856  bool state = strcmp((const char *) xml_char_module, "on") == 0;
857  xmlFree(xml_char_module);
858 
859  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - Before setting " << name << " to " << state << endl);
860  // ***
861  // cerr << "setting context " << name << " to " << state << endl;
862 
863  // Setting this here is all we have to do. This will
864  // change the debug/log settings for the daemon and
865  // (See damon.cc update_beslistener_args()) cause the
866  // new settings to be passed onto new beslisteners.
867  BESDebug::Set(name, state);
868 
869  BESDEBUG("besdaemon", "DaemonCommandHandler::execute_command() - After setting " << name << " to " << state << endl);
870 
871  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:OK") < 0)
872  throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
873  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
874  throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
875 
876  break;
877  }
878 
879  default:
880  throw BESSyntaxUserError("Command " + node_name + " unknown.", __FILE__, __LINE__);
881  }
882  // ***
883  // cerr << "Completed command: " << node_name << endl;
884  }
885 
886  current_node = current_node->next;
887  }
888  }
889  catch (BESError &e) {
890  xmlFreeDoc(doc);
891  throw e;
892  }
893  catch (...) {
894  xmlFreeDoc(doc);
895  throw;
896  }
897 
898  xmlFreeDoc(doc);
899 }
900 
901 static void send_bes_error(BESXMLWriter &writer, BESError &e)
902 {
903  if (xmlTextWriterStartElement(writer.get_writer(), (const xmlChar*) "hai:BESError") < 0)
904  throw BESInternalFatalError("Could not write <hai:OK> element ", __FILE__, __LINE__);
905 
906  ostringstream oss;
907  oss << e.get_error_type() << std::ends;
908  if (xmlTextWriterWriteElement(writer.get_writer(), (const xmlChar*) "hai:Type", (const xmlChar*) oss.str().c_str())
909  < 0) throw BESInternalFatalError("Could not write <hai:Type> element ", __FILE__, __LINE__);
910 
911  if (xmlTextWriterWriteElement(writer.get_writer(), (const xmlChar*) "hai:Message",
912  (const xmlChar*) e.get_message().c_str()) < 0)
913  throw BESInternalFatalError("Could not write <hai:Message> element ", __FILE__, __LINE__);
914 
915  if (xmlTextWriterEndElement(writer.get_writer()) < 0)
916  throw BESInternalFatalError("Could not end <hai:OK> element ", __FILE__, __LINE__);
917 }
918 
919 
928 {
929 #if 0
930  // Use this for some simple white-listing of allowed clients?
931  ostringstream strm;
932  string ip = c->getSocket()->getIp();
933  strm << "ip " << ip << ", port " << c->getSocket()->getPort();
934  string from = strm.str();
935 #endif
936  map<string, string> extensions;
937  ostringstream ss;
938 
939  bool done = false;
940  while (!done)
941  done = c->receive(extensions, &ss);
942 
943  if (extensions["status"] == c->exit()) {
944  // When the client communicating with the besdaemon exits,
945  // return control to the PPTServer::initConnection() method which
946  // will listen for another connect request.
947  BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Received PPT_EXIT_NOW in an extension chunk." << endl);
948 
949  }
950  else {
951  int descript = c->getSocket()->getSocketDescriptor();
952  unsigned int bufsize = c->getSendChunkSize();
953  PPTStreamBuf fds(descript, bufsize);
954 
955  std::streambuf *holder;
956  holder = cout.rdbuf();
957  cout.rdbuf(&fds);
958 
959  BESXMLWriter writer;
960 
961  try {
962  BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - cmd: " << ss.str() << endl);
963  // runs the command(s); throws on an error.
964  execute_command(ss.str(), writer);
965  BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Transmitting response." << endl);
966 
967  cout << writer.get_doc() << endl;
968  fds.finish();
969  cout.rdbuf(holder);
970  }
971  catch (BESError &e) {
972  // an error has occurred.
973  // flush what we have in the stream to the client
974  cout << flush;
975 
976  // Send the extension status=error to the client so that it
977  // can reset.
978  map<string, string> extensions;
979  extensions["status"] = "error";
980 
981  switch (e.get_error_type()) {
982  case BES_INTERNAL_ERROR:
984  BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Internal/Fatal ERROR: " << e.get_message() << endl);
985  extensions["exit"] = "true";
986  c->sendExtensions(extensions);
987  send_bes_error(writer, e);
988  // Send the BESError
989  #if 0
990  // This seemed like a good idea, but really, no error is
991  // fatal, at least not yet.
992  cout << writer.get_doc() << endl;
993  fds.finish();// we are finished, send the last chunk
994  cout.rdbuf(holder);// reset the streams buffer
995  return;// EXIT; disconnects from client
996  #endif
997  break;
998 
1000  // cerr << "syntax error" << endl;
1001  BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Syntax ERROR: " << e.get_message() << endl);
1002  c->sendExtensions(extensions);
1003  // Send the BESError
1004  send_bes_error(writer, e);
1005  break;
1006 
1007  default:
1008  BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - ERROR (unknown command): " << ss.str() << endl);
1009  extensions["exit"] = "true";
1010  c->sendExtensions(extensions);
1011  // Send the BESError
1012  send_bes_error(writer, e);
1013  break;
1014 
1015  }
1016 
1017  cout << writer.get_doc() << endl;
1018  fds.finish(); // we are finished, send the last chunk
1019  cout.rdbuf(holder); // reset the streams buffer
1020  }
1021 
1022  }
1023  // This call closes the socket - it does minimal bookkeeping and
1024  // calls the the kernel's close() function. NB: The method is
1025  // implemented in PPTServer.cc and that calls Socket::close() on the
1026  // Socket instance held by the Connection.
1027  BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Closing client connection." << endl);
1028  c->closeConnection();
1029  BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Client connection has been closed." << endl);
1030 
1031  BESDEBUG("besdaemon", "DaemonCommandHandler::handle() - Command Processing completed. " << endl);
1032  //}
1033 }
1034 
1041 void DaemonCommandHandler::dump(ostream &strm) const
1042 {
1043  strm << BESIndent::LMarg << "DaemonCommandHandler::dump - (" << (void *) this << ")" << endl;
1044 }
1045 
#define BES_SYNTAX_USER_ERROR
Definition: BESError.h:44
exception thrown if an internal error is found and is fatal to the BES
exception thrown if inernal error encountered
virtual void dump(ostream &strm) const
dumps information about this object
static void Set(const string &flagName, bool value)
set the debug context to the specified value
Definition: BESDebug.h:114
#define BESLISTENER_RUNNING
#define BES_INTERNAL_ERROR
Definition: BESError.h:42
unsigned int getPort()
Definition: Socket.h:82
static void GetNodeInfo(xmlNode *node, string &name, string &value, map< string, string > &props)
get the name, value if any, and any properties for the specified node
Definition: BESXMLUtils.cc:105
static const DebugMap & debug_map()
Definition: BESDebug.h:99
int start_master_beslistener()
Start the 'master beslistener' and return its PID.
Definition: daemon.cc:314
virtual void sendExtensions(map< string, string > &extensions)=0
virtual void closeConnection()=0
int master_beslistener_status
Definition: daemon.cc:94
error thrown if there is a user syntax error in the request or any other user error ...
bool stop_all_beslisteners(int)
Stop all of the listeners (both the master listener and all of the child listeners that actually proc...
Definition: daemon.cc:217
virtual int get_error_type()
Return the return code for this error class.
Definition: BESError.h:135
virtual string get_message()
get the error message for this exception
Definition: BESError.h:94
static void XMLErrorFunc(void *context, const char *msg,...)
error function used by libxml2 to report errors
Definition: BESXMLUtils.cc:47
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
const char * get_doc()
virtual unsigned int getSendChunkSize()=0
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
#define BES_INTERNAL_FATAL_ERROR
Definition: BESError.h:43
DebugMap::const_iterator debug_citer
Definition: BESDebug.h:97
static bool IsSet(const string &flagName)
see if the debug context flagName is set to true
Definition: BESDebug.h:160
xmlTextWriterPtr get_writer()
Definition: BESXMLWriter.h:54
list< BESFSFile >::iterator fileIterator
Definition: BESFSDir.h:65
virtual int getSocketDescriptor()
Definition: Socket.h:78
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:453
std::string getIp()
Definition: Socket.h:86
virtual string exit()=0
virtual void handle(Connection *c)
This particular handle() method is special because it will accept only a single command transmission ...
DaemonCommandHandler(const string &config)
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
void get_values(const string &s, vector< string > &vals, bool &found)
Retrieve the values of a given key, if set.
Definition: BESKeys.cc:488
virtual Socket * getSocket()
Definition: Connection.h:76
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:48
virtual bool receive(map< string, string > &extensions, ostream *strm=0)=0
void unblock_signals()
See block_signals()
Definition: daemon.cc:191