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
61using 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
70BESServerHandler::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
96void 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
122void 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
258void 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