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 void
94 {
95  if(_method=="single")
96  {
97  execute( c ) ;
98  }
99  else
100  {
101  int main_process = getpid() ;
102  pid_t pid ;
103  if( ( pid = fork() ) < 0 )
104  {
105  string error( "fork error" ) ;
106  const char* error_info = strerror( errno ) ;
107  if( error_info )
108  error += " " + (string)error_info ;
109  throw BESInternalError( error, __FILE__, __LINE__ ) ;
110  }
111  else if( pid == 0 ) /* child process */
112  {
113  pid_t pid1 ;
114  // we fork twice so we do not have zombie children
115  if( ( pid1 = fork() ) < 0 )
116  {
117  // we must send a signal of immediate termination to the
118  // main server
119  kill( main_process, 9 ) ;
120  perror( "fork error" ) ;
122  }
123  else if( pid1 == 0 ) /* child of the child */
124  {
125  execute( c ) ;
126  }
127  sleep( 1 ) ;
128  c->closeConnection() ;
130  }
131  if( waitpid( pid, NULL, 0 ) != pid )
132  {
133  string error( "waitpid error" ) ;
134  const char *error_info = strerror( errno ) ;
135  if( error_info )
136  error += " " + (string)error_info ;
137  throw BESInternalError( error, __FILE__, __LINE__ ) ;
138  }
139  c->closeConnection() ;
140  }
141 }
142 
143 void
144 BESServerHandler::execute( Connection *c )
145 {
146  ostringstream strm ;
147  string ip = c->getSocket()->getIp() ;
148  strm << "ip " << ip << ", port " << c->getSocket()->getPort() ;
149  string from = strm.str() ;
150 
151  map<string,string> extensions ;
152 
153  for(;;)
154  {
155  ostringstream ss ;
156 
157  bool done = false ;
158  while( !done )
159  done = c->receive( extensions, &ss ) ;
160 
161  if( extensions["status"] == c->exit() )
162  {
163  c->closeConnection() ;
164  exit( CHILD_SUBPROCESS_READY ) ;
165  }
166 
167  // This is code that was in place for the string commands. With xml
168  // documents everything is taken care of by libxml2. This should be
169  // happening in the Interface class before passing to the parser, if
170  // need be. pwest 06 Feb 2009
171  //string cmd_str = BESUtil::www2id( ss.str(), "%", "%20" ) ;
172  string cmd_str = ss.str() ;
173  BESDEBUG( "server", "BESServerHandler::execute - command = "
174  << cmd_str << endl ) ;
175 
176  BESStopWatch *sw = 0 ;
177  if( BESISDEBUG( "timing" ) )
178  {
179  sw = new BESStopWatch() ;
180  sw->start() ;
181  }
182 
183  int descript = c->getSocket()->getSocketDescriptor() ;
184 
185  unsigned int bufsize = c->getSendChunkSize() ;
186  PPTStreamBuf fds( descript, bufsize ) ;
187  std::streambuf *holder ;
188  holder = cout.rdbuf() ;
189  cout.rdbuf( &fds ) ;
190 
191  BESXMLInterface cmd( cmd_str, &cout ) ;
192  int status = cmd.execute_request( from ) ;
193 
194  if( status == 0 )
195  {
196  BESDEBUG( "server", "BESServerHandler::execute - "
197  << "executed successfully" << endl ) ;
198  fds.finish() ;
199  cout.rdbuf( holder ) ;
200 
201  if( BESISDEBUG( "timing" ) )
202  {
203  if( sw && sw->stop() )
204  {
205  BESDEBUG( "timing",
206  "BESServerHandler::execute - executed in "
207  << sw->seconds() << " seconds and "
208  << sw->microseconds() << " microseconds"
209  << endl ) ;
210  }
211  else
212  {
213  BESDEBUG( "timing", "BESServerHandler::execute - "
214  << "no timing available" << endl ) ;
215  }
216  }
217  }
218  else
219  {
220  // an error has occurred.
221  BESDEBUG( "server", "BESServerHandler::execute - "
222  << "error occurred" << endl ) ;
223 
224  // flush what we have in the stream to the client
225  cout << flush ;
226 
227  // Send the extension status=error to the client so that it
228  // can reset.
229  map<string,string> extensions ;
230  extensions["status"] = "error" ;
231  if( status == BES_INTERNAL_FATAL_ERROR )
232  {
233  extensions["exit"] = "true" ;
234  }
235  c->sendExtensions( extensions ) ;
236 
237  // transmit the error message. finish_with_error will transmit
238  // the error
239  cmd.finish_with_error( status ) ;
240 
241  // we are finished, send the last chunk
242  fds.finish() ;
243 
244  // reset the streams buffer
245  cout.rdbuf( holder ) ;
246 
247  switch (status)
248  {
250  {
251  cout << "BES server " << getpid()
252  << ": Status not OK, dispatcher returned value "
253  << status << endl ;
254  c->closeConnection() ;
255  exit( CHILD_SUBPROCESS_READY ) ;
256  }
257  break;
258  case BES_INTERNAL_ERROR:
260  case BES_FORBIDDEN_ERROR:
261  case BES_NOT_FOUND_ERROR:
262  default:
263  break;
264  }
265  }
266 
267  if( sw ) delete sw;
268  }
269 }
270 
277 void
278 BESServerHandler::dump( ostream &strm ) const
279 {
280  strm << BESIndent::LMarg << "BESServerHandler::dump - ("
281  << (void *)this << ")" << endl ;
283  strm << BESIndent::LMarg << "server method: " << _method << endl ;
285 }
286