bes  Updated for version 3.20.5
BESServerHandler.cc
1 // BESServerHandler.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) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include <unistd.h> // for getpid fork sleep
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <signal.h>
37 #include <sys/wait.h> // for waitpid
38 #include <cstring>
39 #include <cstdlib>
40 #include <cerrno>
41 #include <sstream>
42 #include <iostream>
43 
44 using std::ostringstream;
45 using std::cout;
46 using std::endl;
47 using std::cerr;
48 using std::flush;
49 
50 #include "BESServerHandler.h"
51 #include "Connection.h"
52 #include "Socket.h"
53 #include "BESXMLInterface.h"
54 #include "TheBESKeys.h"
55 #include "BESInternalError.h"
56 #include "ServerExitConditions.h"
57 #include "BESUtil.h"
58 #include "PPTStreamBuf.h"
59 #include "PPTProtocol.h"
60 #include "BESLog.h"
61 #include "BESDebug.h"
62 #include "BESStopWatch.h"
63 
64 // Default is to not exit on internal error. A bad idea, but the original
65 // behavior of the server. jhrg 10/4/18
66 #define EXIT_ON_INTERNAL_ERROR "BES.ExitOnInternalError"
67 
68 BESServerHandler::BESServerHandler()
69 {
70  bool found = false;
71  try {
72  TheBESKeys::TheKeys()->get_value("BES.ProcessManagerMethod", _method, found);
73  }
74  catch (BESError &e) {
75  cerr << "Unable to determine method to handle clients, "
76  << "single or multiple as defined by BES.ProcessManagerMethod" << ": " << e.get_message() << endl;
77  exit(SERVER_EXIT_FATAL_CANNOT_START);
78  }
79 
80  if (_method != "multiple" && _method != "single") {
81  cerr << "Unable to determine method to handle clients, "
82  << "single or multiple as defined by BES.ProcessManagerMethod" << endl;
83  exit(SERVER_EXIT_FATAL_CANNOT_START);
84  }
85 }
86 
87 // I'm not sure that we need to fork twice. jhrg 11/14/05
88 // The reason that we fork twice is explained in Advanced Programming in the
89 // Unit Environment by W. Richard Stevens. In the 'multiple' case we don't
90 // want to leave any zombie processes.
91 //
92 // I've changed this substantially. See daemon.cc, ServerApp.cc and
93 // DaemonCommandHanlder.cc. jhrg 7/15/11
94 void BESServerHandler::handle(Connection *c)
95 {
96  if (_method == "single") {
97  // we're in single mode, so no for and exec is needed. One
98  // client connection and we are done.
99  execute(c);
100  }
101  // _method is "multiple" which means, for each connection request, make a
102  // new beslistener daemon. The OLFS can send many commands to each of these
103  // before it closes the socket. In theory this should not be necessary, but
104  // in practice some handlers will have resource (memory) leaks and nothing
105  // cures that like exit().
106  else {
107  pid_t pid;
108  if ((pid = fork()) < 0) { // error
109  string error("fork error");
110  const char* error_info = strerror(errno);
111  if (error_info) error += " " + (string) error_info;
112  throw BESInternalError(error, __FILE__, __LINE__);
113  }
114  else if (pid == 0) { // child
115  execute(c);
116  }
117  }
118 }
119 
120 void BESServerHandler::execute(Connection *c)
121 {
122  // TODO This seems like a waste of time - do we really need to log this information?
123  // jhrg 11/13/17
124  ostringstream strm;
125  strm << "ip " << c->getSocket()->getIp() << ", port " << c->getSocket()->getPort();
126  string from = strm.str();
127 
128  map<string, string> extensions;
129 
130  // we loop continuously waiting for messages. The only way we exit
131  // this loop is: 1. we receive a status of exit from the client, 2.
132  // the client drops the connection, the process catches the signal
133  // and exits, 3. a fatal error has occurred in the server so exit,
134  // 4. the server process is killed.
135  for (;;) {
136  ostringstream ss;
137 
138  bool done = false;
139  while (!done)
140  done = c->receive(extensions, &ss);
141 
142  // The server has been sent a message that the client is exiting
143  // and closing the connection. So exit this process.
144  if (extensions["status"] == c->exit()) {
145  // The protocol docs indicate that the EXIT_NOW 'token' is followed
146  // by a zero-length chunk (a chunk that has type 'd'). See section
147  // 4.3 of the documentation (http://docs.opendap.org/index.php/Hyrax_-_BES_PPT).
148  // jhrg 10/30/13
149  // Note, however, that PPTConnection::receive() continues to read chunks until
150  // the end chunk is read. That means that it will read the end chunk for the
151  // PPT_EXIT_NOW chunk (and so we don't need to).
152 
153  BESDEBUG("beslistener",
154  "BESServerHandler::execute() - Received PPT_EXIT_NOW in an extension chunk." << endl);
155 
156  // This call closes the socket - it does minimal bookkeeping and
157  // calls the the kernel's close() function. NB: The method is
158  // implemented in PPTServer.cc and that calls Socket::close() on the
159  // Socket instance held by the Connection.
160  c->closeConnection();
161 
162  BESDEBUG("beslistener",
163  "BESServerHandler::execute() - Calling exit(CHILD_SUBPROCESS_READY) which has a value of " << CHILD_SUBPROCESS_READY << endl);
164 
165  exit(CHILD_SUBPROCESS_READY);
166  }
167 
168  // This is code that was in place for the string commands. With xml
169  // documents everything is taken care of by libxml2. This should be
170  // happening in the Interface class before passing to the parser, if
171  // need be. pwest 06 Feb 2009
172  //string cmd_str = BESUtil::www2id( ss.str(), "%", "%20" ) ;
173  string cmd_str = ss.str();
174 
175  BESDEBUG("server", "BESServerHandler::execute - command ... " << cmd_str << endl);
176 
177  BESStopWatch sw;
178  if (BESISDEBUG(TIMING_LOG)) sw.start("BESServerHandler::execute");
179 
180  // Tie the cout stream to the PPTStreamBuf and save the cout buffer so that
181  // it can be reset once the command is complete. jhrg 1/25/17
182  int descript = c->getSocket()->getSocketDescriptor();
183  unsigned int bufsize = c->getSendChunkSize();
184  PPTStreamBuf fds(descript, bufsize);
185  std::streambuf *holder;
186  holder = cout.rdbuf();
187  cout.rdbuf(&fds);
188 
189  BESXMLInterface cmd(cmd_str, &cout);
190  int status = cmd.execute_request(from);
191 
192  if (status == 0) {
193  cmd.finish(status);
194  fds.finish();
195  cout.rdbuf(holder);
196  }
197  else {
198  BESDEBUG("server", "BESServerHandler::execute - " << "error occurred" << endl);
199 
200  // Send the extension status=error to the client so that it can reset. The finish()
201  // method is called _after_ this so that the error response will be recognizable.
202  // At least, I think that's what is happening... jhrg 11/12/17
203  map<string, string> extensions;
204  extensions["status"] = "error";
205  if (status == BES_INTERNAL_FATAL_ERROR) {
206  extensions["exit"] = "true";
207  }
208  c->sendExtensions(extensions);
209 
210  cmd.finish(status);
211  // we are finished, send the last chunk
212  fds.finish();
213  // reset the cout stream buffer
214  cout.rdbuf(holder);
215 
216  // If the status is fatal, then we want to exit. Otherwise,
217  // continue, wait for the next request.
218  switch (status) {
219  case BES_INTERNAL_FATAL_ERROR:
220  LOG("BES Internal Fatal Error; child returning "
221  << SERVER_EXIT_ABNORMAL_TERMINATION << " to the master listener." << endl);
222 
223  c->closeConnection();
224  exit(SERVER_EXIT_ABNORMAL_TERMINATION);
225 
226  break;
227 
228  case BES_INTERNAL_ERROR:
229  // This should be an option. The default is to not exit. jhrg 10/4/18
230  if (TheBESKeys::TheKeys()->read_bool_key(EXIT_ON_INTERNAL_ERROR, false)) {
231  LOG("BES Internal Error; child returning "
232  << SERVER_EXIT_ABNORMAL_TERMINATION << " to the master listener." << endl);
233 
234  c->closeConnection();
235  exit(SERVER_EXIT_ABNORMAL_TERMINATION);
236  }
237  break;
238 
239  // These are recoverable errors
240  case BES_SYNTAX_USER_ERROR:
241  case BES_FORBIDDEN_ERROR:
242  case BES_NOT_FOUND_ERROR:
243  default:
244  break;
245  }
246  }
247  } // This is the end of the infinite loop that processes commands.
248 
249  c->closeConnection();
250 }
251 
258 void BESServerHandler::dump(ostream &strm) const
259 {
260  strm << BESIndent::LMarg << "BESServerHandler::dump - (" << (void *) this << ")" << endl;
261  BESIndent::Indent();
262  strm << BESIndent::LMarg << "server method: " << _method << endl;
263  BESIndent::UnIndent();
264 }
265 
BESError::get_message
virtual std::string get_message()
get the error message for this exception
Definition: BESError.h:99
Connection
Definition: Connection.h:47
BESServerHandler::dump
virtual void dump(ostream &strm) const
dumps information about this object
Definition: BESServerHandler.cc:258
TheBESKeys::TheKeys
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:61
BESStopWatch::start
virtual bool start(string name)
Definition: BESStopWatch.cc:57
BESXMLInterface
Entry point into BES using xml document requests.
Definition: BESXMLInterface.h:47
PPTStreamBuf
Definition: PPTStreamBuf.h:38
BESInternalError
exception thrown if inernal error encountered
Definition: BESInternalError.h:43
BESStopWatch
Definition: BESStopWatch.h:55
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:420
BESError
Abstract exception class for the BES with basic string message.
Definition: BESError.h:58