bes  Updated for version 3.20.10
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 "PPTProtocolNames.h"
57 #include "BESLog.h"
58 #include "BESDebug.h"
59 #include "BESStopWatch.h"
60 
61 using namespace std;
62 
63 #define MODULE "server"
64 #define prolog std::string("BESServerHandler::").append(__func__).append("() - ")
65 
66 // Default is to not exit on internal error. A bad idea, but the original
67 // behavior of the server. jhrg 10/4/18
68 #define EXIT_ON_INTERNAL_ERROR "BES.ExitOnInternalError"
69 
70 BESServerHandler::BESServerHandler()
71 {
72  bool found = false;
73  try {
74  TheBESKeys::TheKeys()->get_value("BES.ProcessManagerMethod", _method, found);
75  }
76  catch (BESError &e) {
77  cerr << "Unable to determine method to handle clients, "
78  << "single or multiple as defined by BES.ProcessManagerMethod" << ": " << e.get_message() << endl;
79  exit(SERVER_EXIT_FATAL_CANNOT_START);
80  }
81 
82  if (_method != "multiple" && _method != "single") {
83  cerr << "Unable to determine method to handle clients, "
84  << "single or multiple as defined by BES.ProcessManagerMethod" << endl;
85  exit(SERVER_EXIT_FATAL_CANNOT_START);
86  }
87 }
88 
89 // I'm not sure that we need to fork twice. jhrg 11/14/05
90 // The reason that we fork twice is explained in Advanced Programming in the
91 // Unit Environment by W. Richard Stevens. In the 'multiple' case we don't
92 // want to leave any zombie processes.
93 //
94 // I've changed this substantially. See daemon.cc, ServerApp.cc and
95 // DaemonCommandHanlder.cc. jhrg 7/15/11
96 void BESServerHandler::handle(Connection *c)
97 {
98  if (_method == "single") {
99  // we're in single mode, so no for and exec is needed. One
100  // client connection and we are done.
101  execute(c);
102  }
103  // _method is "multiple" which means, for each connection request, make a
104  // new beslistener daemon. The OLFS can send many commands to each of these
105  // before it closes the socket. In theory this should not be necessary, but
106  // in practice some handlers will have resource (memory) leaks and nothing
107  // cures that like exit().
108  else {
109  pid_t pid;
110  if ((pid = fork()) < 0) { // error
111  string error("fork error");
112  const char* error_info = strerror(errno);
113  if (error_info) error += " " + (string) error_info;
114  throw BESInternalError(error, __FILE__, __LINE__);
115  }
116  else if (pid == 0) { // child
117  execute(c);
118  }
119  }
120 }
121 
122 void BESServerHandler::execute(Connection *connection)
123 {
124  // TODO This seems like a waste of time - do we really need to log this information?
125  // jhrg 11/13/17
126  ostringstream strm;
127  strm << "ip " << connection->getSocket()->getIp() << ", port " << connection->getSocket()->getPort();
128  string from = strm.str();
129 
130  map<string, string> extensions;
131 
132  int socket_d = connection->getSocket()->getSocketDescriptor();
133  unsigned int bufsize = connection->getSendChunkSize();
134  PPTStreamBuf fds(socket_d, bufsize);
135  ostream my_ostrm(&fds);
136 
137 #if !NDEBUG
138  stringstream msg;
139  msg << prolog << "Using ostream: " << (void *) &my_ostrm << " cout: " << (void *) &cout << endl;
140  BESDEBUG(MODULE, msg.str());
141  INFO_LOG( msg.str());
142 #endif
143 
144  // we loop continuously waiting for messages. The only way we exit
145  // this loop is: 1. we receive a status of exit from the client, 2.
146  // the client drops the connection, the process catches the signal
147  // and exits, 3. a fatal error has occurred in the server so exit,
148  // 4. the server process is killed.
149  for (;;) {
150  ostringstream ss;
151 
152  bool done = false;
153  BESDEBUG(MODULE,prolog << "Waiting for client to send commands." << endl);
154  while (!done)
155  done = connection->receive(extensions, &ss);
156 
157  BESDEBUG(MODULE,prolog << "Received client command. status: '" << extensions["status"] << "'" << endl);
158 
159  // The server has been sent a message that the client is exiting
160  // and closing the connection. So exit this process.
161  if (extensions["status"] == connection->exit()) {
162  // The protocol docs indicate that the EXIT_NOW 'token' is followed
163  // by a zero-length chunk (a chunk that has type 'd'). See section
164  // 4.3 of the documentation (http://docs.opendap.org/index.php/Hyrax_-_BES_PPT).
165  // jhrg 10/30/13
166  // Note, however, that PPTConnection::receive() continues to read chunks until
167  // the end chunk is read. That means that it will read the end chunk for the
168  // PPT_EXIT_NOW chunk (and so we don't need to).
169 
170  BESDEBUG(MODULE,prolog << "Received PPT_EXIT_NOW in an extension chunk." << endl);
171 
172  // This call closes the socket - it does minimal bookkeeping and
173  // calls the the kernel's close() function. NB: The method is
174  // implemented in PPTServer.cc and that calls Socket::close() on the
175  // Socket instance held by the Connection.
176  connection->closeConnection();
177 
178  BESDEBUG(MODULE,prolog << "Calling exit(CHILD_SUBPROCESS_READY) which has a value of " << CHILD_SUBPROCESS_READY << endl);
179 
180  INFO_LOG("Received exit command." << endl);
181 
182  exit(CHILD_SUBPROCESS_READY);
183  }
184 
185  string cmd_str = ss.str();
186 
187  BESDEBUG(MODULE, prolog << "Processing client command:" << endl << cmd_str << endl);
188 
189  BESStopWatch sw;
190  if (BESDebug::IsSet(TIMING_LOG_KEY)) sw.start("BESServerHandler::execute");
191 
192 
193  // This is where we actual save/assign the output stream used for the
194  // the response
195  BESXMLInterface cmd(cmd_str, &my_ostrm);
196 
197  int status = cmd.execute_request(from);
198  if (status == 0) {
199  cmd.finish(status);
200  fds.finish();
201  BESDEBUG(MODULE, prolog << "Client command successfully processed." << endl);
202  }
203  else {
204  BESDEBUG(MODULE, prolog << "ERROR - cmd.execute_request() returned: " << status << endl);
205 
206  // Send the extension status=error to the client so that it can reset. The finish()
207  // method is called _after_ this so that the error response will be recognizable.
208  // At least, I think that's what is happening... jhrg 11/12/17
209  map<string, string> extensions;
210  extensions["status"] = "error";
211  if (status == BES_INTERNAL_FATAL_ERROR) {
212  extensions["exit"] = "true";
213  }
214  connection->sendExtensions(extensions);
215 
216  cmd.finish(status);
217  // we are finished, send the last chunk
218  fds.finish();
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  ERROR_LOG("BES Internal Fatal Error; child returning "
225  << SERVER_EXIT_ABNORMAL_TERMINATION << " to the master listener." << endl);
226 
227  connection->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  ERROR_LOG("BES Internal Error; child returning "
236  << SERVER_EXIT_ABNORMAL_TERMINATION << " to the master listener." << endl);
237 
238  connection->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 
260 void BESServerHandler::dump(ostream &strm) const
261 {
262  strm << BESIndent::LMarg << "BESServerHandler::dump - (" << (void *) this << ")" << endl;
263  BESIndent::Indent();
264  strm << BESIndent::LMarg << "server method: " << _method << endl;
265  BESIndent::UnIndent();
266 }
267 
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
Definition: BESDebug.h:168
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:340
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:71