bes  Updated for version 3.20.13
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 #endif
142 
143  // we loop continuously waiting for messages. The only way we exit
144  // this loop is: 1. we receive a status of exit from the client, 2.
145  // the client drops the connection, the process catches the signal
146  // and exits, 3. a fatal error has occurred in the server so exit,
147  // 4. the server process is killed.
148  for (;;) {
149  ostringstream ss;
150 
151  bool done = false;
152  BESDEBUG(MODULE,prolog << "Waiting for client to send commands." << endl);
153  while (!done)
154  done = connection->receive(extensions, &ss);
155 
156  BESDEBUG(MODULE,prolog << "Received client command. status: '" << extensions["status"] << "'" << endl);
157 
158  // The server has been sent a message that the client is exiting
159  // and closing the connection. So exit this process.
160  if (extensions["status"] == connection->exit()) {
161  // The protocol docs indicate that the EXIT_NOW 'token' is followed
162  // by a zero-length chunk (a chunk that has type 'd'). See section
163  // 4.3 of the documentation (http://docs.opendap.org/index.php/Hyrax_-_BES_PPT).
164  // jhrg 10/30/13
165  // Note, however, that PPTConnection::receive() continues to read chunks until
166  // the end chunk is read. That means that it will read the end chunk for the
167  // PPT_EXIT_NOW chunk (and so we don't need to).
168 
169  BESDEBUG(MODULE,prolog << "Received PPT_EXIT_NOW in an extension chunk." << endl);
170 
171  // This call closes the socket - it does minimal bookkeeping and
172  // calls the the kernel's close() function. NB: The method is
173  // implemented in PPTServer.cc and that calls Socket::close() on the
174  // Socket instance held by the Connection.
175  connection->closeConnection();
176 
177  BESDEBUG(MODULE,prolog << "Calling exit(CHILD_SUBPROCESS_READY) which has a value of " << CHILD_SUBPROCESS_READY << endl);
178 
179  INFO_LOG("Received exit command." << endl);
180 
181  exit(CHILD_SUBPROCESS_READY);
182  }
183 
184  string cmd_str = ss.str();
185 
186  BESDEBUG(MODULE, prolog << "Processing client command:" << endl << cmd_str << endl);
187 
188  BESStopWatch sw;
189  if (BESDebug::IsSet(TIMING_LOG_KEY)) sw.start("BESServerHandler::execute");
190 
191 
192  // This is where we actually save/assign the output stream used for the response
193  BESXMLInterface cmd(cmd_str, &my_ostrm);
194 
195  int status = cmd.execute_request(from);
196  if (status == 0) {
197  cmd.finish(status);
198  fds.finish();
199  BESDEBUG(MODULE, prolog << "Client command successfully processed." << endl);
200  }
201  else {
202  BESDEBUG(MODULE, prolog << "ERROR - cmd.execute_request() returned: " << status << 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  connection->sendExtensions(extensions);
213 
214  cmd.finish(status);
215  // we are finished, send the last chunk
216  fds.finish();
217 
218  // If the status is fatal, then we want to exit. Otherwise,
219  // continue, wait for the next request.
220  switch (status) {
221  case BES_INTERNAL_FATAL_ERROR:
222  ERROR_LOG("BES Internal Fatal Error; child returning "
223  << SERVER_EXIT_ABNORMAL_TERMINATION << " to the master listener." << endl);
224 
225  connection->closeConnection();
226  exit(SERVER_EXIT_ABNORMAL_TERMINATION);
227 
228  break;
229 
230  case BES_INTERNAL_ERROR:
231  // This should be an option. The default is to not exit. jhrg 10/4/18
232  if (TheBESKeys::TheKeys()->read_bool_key(EXIT_ON_INTERNAL_ERROR, false)) {
233  ERROR_LOG("BES Internal Error; child returning "
234  << SERVER_EXIT_ABNORMAL_TERMINATION << " to the master listener." << endl);
235 
236  connection->closeConnection();
237  exit(SERVER_EXIT_ABNORMAL_TERMINATION);
238  }
239  break;
240 
241  // These are recoverable errors
242  case BES_SYNTAX_USER_ERROR:
243  case BES_FORBIDDEN_ERROR:
244  case BES_NOT_FOUND_ERROR:
245  default:
246  break;
247  }
248  }
249  } // This is the end of the infinite loop that processes commands.
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 
static bool IsSet(const std::string &flagName)
see if the debug context flagName is set to true
Definition: BESDebug.h:168
Base exception class for the BES with basic string message.
Definition: BESError.h:59
std::string get_message() const
get the error message for this exception
Definition: BESError.h:111
exception thrown if internal error encountered
void dump(std::ostream &strm) const override
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