OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
BESServerHandler.cc
Go to the documentation of this file.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 
39 #include <cstring>
40 #include <cstdlib>
41 #include <cerrno>
42 #include <sstream>
43 #include <iostream>
44 
45 using std::ostringstream ;
46 using std::cout ;
47 using std::endl ;
48 using std::cerr ;
49 using std::flush ;
50 
51 #include "BESServerHandler.h"
52 #include "Connection.h"
53 #include "Socket.h"
54 #include "BESXMLInterface.h"
55 #include "TheBESKeys.h"
56 #include "BESInternalError.h"
57 #include "ServerExitConditions.h"
58 #include "BESUtil.h"
59 #include "PPTStreamBuf.h"
60 #include "PPTProtocol.h"
61 #include "BESDebug.h"
62 #include "BESStopWatch.h"
63 
65 {
66  bool found = false ;
67  try
68  {
69  TheBESKeys::TheKeys()->get_value( "BES.ProcessManagerMethod",
70  _method, found ) ;
71  }
72  catch( BESError &e )
73  {
74  cerr << "Unable to determine method to handle clients, "
75  << "single or multiple as defined by BES.ProcessManagerMethod"
76  << ": " << e.get_message() << endl ;
78  }
79  if( _method != "multiple" && _method != "single" )
80  {
81  cerr << "Unable to determine method to handle clients, "
82  << "single or multiple as defined by BES.ProcessManagerMethod"
83  << endl ;
85  }
86 }
87 
88 // I'm not sure that we need to fork twice. jhrg 11/14/05
89 // The reason that we fork twice is explained in Advanced Programming in the
90 // Unit Environment by W. Richard Stevens. In the 'multiple' case we don't
91 // want to leave any zombie processes.
92 //
93 // I've changed this substantially. See daemon.cc, ServerApp.cc and
94 // DaemonCommandHanlder.cc. jhrg 7/15/11
96 {
97  if (_method == "single")
98  {
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  {
110  pid_t pid;
111  if ((pid = fork()) < 0) // error
112  {
113  string error("fork error");
114  const char* error_info = strerror(errno);
115  if (error_info)
116  error += " " + (string) error_info;
117  throw BESInternalError(error, __FILE__, __LINE__);
118  }
119  else if (pid == 0) // child
120  {
121  execute(c);
122  }
123 #if 0
124  // This code made the beslistener that processed the request a true
125  // daemon process. I changed the beslistener so that it was a simple
126  // child process so that I could more easily send it signals to control
127  // stopping everything. jhrg
128 
129  else if( pid == 0 ) /* child process */
130  {
131  pid_t pid1;
132  // we fork twice so we do not have zombie children
133  if( ( pid1 = fork() ) < 0 )
134  {
135  // we must send a signal of immediate termination to the
136  // main server
137  kill( main_process, 9 );
138  perror( "fork error" );
139  exit( SERVER_EXIT_CHILD_SUBPROCESS_ABNORMAL_TERMINATION );
140  }
141  else if( pid1 == 0 ) /* child of the child */
142  {
143  // execute given the connection. The execute method does
144  // the listen and handles input/output, etc...
145  execute( c );
146  }
147 
148  sleep( 1 );
149  c->closeConnection();
150  exit( SERVER_EXIT_CHILD_SUBPROCESS_NORMAL_TERMINATION );
151  }
152  if( waitpid( pid, NULL, 0 ) != pid ) // parent
153 
154  {
155  string error( "waitpid error" );
156  const char *error_info = strerror( errno );
157  if( error_info )
158  error += " " + (string)error_info;
159  throw BESInternalError( error, __FILE__, __LINE__ );
160  }
161  c->closeConnection();
162 #endif
163  }
164 }
165 
166 void
167 BESServerHandler::execute( Connection *c )
168 {
169  ostringstream strm ;
170  string ip = c->getSocket()->getIp() ;
171  strm << "ip " << ip << ", port " << c->getSocket()->getPort() ;
172  string from = strm.str() ;
173 
174  map<string,string> extensions ;
175 
176  // we loop continuously waiting for messages. The only way we exit
177  // this loop is 1. we receive a status of exit from the client, 2.
178  // the client drops the connection, the process catches the signal
179  // and exits, 3. a fatal error has occurred in the server so exit,
180  // 4. the server process is killed.
181  for(;;)
182  {
183  ostringstream ss ;
184 
185  bool done = false ;
186  while( !done )
187  done = c->receive( extensions, &ss ) ;
188 
189  // The server has been sent a message that the client is exiting
190  // and closing the connection. So exit this process.
191  if( extensions["status"] == c->exit() )
192  {
193  c->closeConnection() ;
194  exit( CHILD_SUBPROCESS_READY ) ;
195  }
196 
197  // This is code that was in place for the string commands. With xml
198  // documents everything is taken care of by libxml2. This should be
199  // happening in the Interface class before passing to the parser, if
200  // need be. pwest 06 Feb 2009
201  //string cmd_str = BESUtil::www2id( ss.str(), "%", "%20" ) ;
202  string cmd_str = ss.str() ;
203  BESDEBUG( "server", "BESServerHandler::execute - command = "
204  << cmd_str << endl ) ;
205 
206  BESStopWatch *sw = 0 ;
207  if( BESISDEBUG( "timing" ) )
208  {
209  sw = new BESStopWatch() ;
210  sw->start() ;
211  }
212 
213  int descript = c->getSocket()->getSocketDescriptor() ;
214 
215  unsigned int bufsize = c->getSendChunkSize() ;
216  PPTStreamBuf fds( descript, bufsize ) ;
217  std::streambuf *holder ;
218  holder = cout.rdbuf() ;
219  cout.rdbuf( &fds ) ;
220 
221  BESXMLInterface cmd( cmd_str, &cout ) ;
222  int status = cmd.execute_request( from ) ;
223 
224  if( status == 0 )
225  {
226  fds.finish() ;
227 
228  cout.rdbuf( holder ) ;
229 
230  BESDEBUG( "server", "BESServerHandler::execute - "
231  << "executed successfully" << endl ) ;
232 
233  if( BESISDEBUG( "timing" ) )
234  {
235  if( sw && sw->stop() )
236  {
237  BESDEBUG( "timing",
238  "BESServerHandler::execute - executed in "
239  << sw->seconds() << " seconds and "
240  << sw->microseconds() << " microseconds"
241  << endl ) ;
242  }
243  else
244  {
245  BESDEBUG( "timing", "BESServerHandler::execute - "
246  << "no timing available" << endl ) ;
247  }
248  }
249  }
250  else
251  {
252  // an error has occurred.
253  BESDEBUG( "server", "BESServerHandler::execute - "
254  << "error occurred" << endl ) ;
255 
256  // flush what we have in the stream to the client
257  cout << flush ;
258 
259  // Send the extension status=error to the client so that it
260  // can reset.
261  map<string,string> extensions ;
262  extensions["status"] = "error" ;
263  if( status == BES_INTERNAL_FATAL_ERROR )
264  {
265  extensions["exit"] = "true" ;
266  }
267  c->sendExtensions( extensions ) ;
268 
269  // transmit the error message. finish_with_error will transmit
270  // the error
271  cmd.finish_with_error( status ) ;
272 
273  // we are finished, send the last chunk
274  fds.finish() ;
275 
276  // reset the streams buffer
277  cout.rdbuf( holder ) ;
278 
279  // If the status is fatal, then we want to exit. Otherwise,
280  // continue, wait for the next request.
281  switch (status)
282  {
284  {
285  cout << "BES server " << getpid()
286  << ": Status not OK, dispatcher returned value "
287  << status << endl ;
288  c->closeConnection() ;
289  exit( CHILD_SUBPROCESS_READY ) ;
290  }
291  break;
292  case BES_INTERNAL_ERROR:
294  case BES_FORBIDDEN_ERROR:
295  case BES_NOT_FOUND_ERROR:
296  default:
297  break;
298  }
299  }
300 
301  if( sw ) delete sw;
302  } // This is the end of the infinite loop that processes commands.
303 
304  c->closeConnection() ;
305 }
306 
313 void
314 BESServerHandler::dump( ostream &strm ) const
315 {
316  strm << BESIndent::LMarg << "BESServerHandler::dump - ("
317  << (void *)this << ")" << endl ;
319  strm << BESIndent::LMarg << "server method: " << _method << endl ;
321 }
322