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