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