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