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