bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
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 BESLog::TheLog()->update_pid();
125
126 // TODO This seems like a waste of time - do we really need to log this information?
127 // jhrg 11/13/17
128 ostringstream strm;
129 strm << "ip " << connection->getSocket()->getIp() << ", port " << connection->getSocket()->getPort();
130 string from = strm.str();
131
132 map<string, string> extensions;
133
134 int socket_d = connection->getSocket()->getSocketDescriptor();
135 unsigned int bufsize = connection->getSendChunkSize();
136 PPTStreamBuf fds(socket_d, bufsize);
137 ostream my_ostrm(&fds);
138
139#if !NDEBUG
140 stringstream msg;
141 msg << prolog << "Using ostream: " << (void *) &my_ostrm << " cout: " << (void *) &cout << endl;
142 BESDEBUG(MODULE, msg.str());
143#endif
144
145 // we loop continuously waiting for messages. The only way we exit
146 // this loop is: 1. we receive a status of exit from the client, 2.
147 // the client drops the connection, the process catches the signal
148 // and exits, 3. a fatal error has occurred in the server so exit,
149 // 4. the server process is killed.
150 for (;;) {
151 ostringstream ss;
152
153 bool done = false;
154 BESDEBUG(MODULE,prolog << "Waiting for client to send commands." << endl);
155 while (!done)
156 done = connection->receive(extensions, &ss);
157
158 BESDEBUG(MODULE,prolog << "Received client command. status: '" << extensions["status"] << "'" << endl);
159
160 // The server has been sent a message that the client is exiting
161 // and closing the connection. So exit this process.
162 if (extensions["status"] == connection->exit()) {
163 // The protocol docs indicate that the EXIT_NOW 'token' is followed
164 // by a zero-length chunk (a chunk that has type 'd'). See section
165 // 4.3 of the documentation (http://docs.opendap.org/index.php/Hyrax_-_BES_PPT).
166 // jhrg 10/30/13
167 // Note, however, that PPTConnection::receive() continues to read chunks until
168 // the end chunk is read. That means that it will read the end chunk for the
169 // PPT_EXIT_NOW chunk (and so we don't need to).
170
171 BESDEBUG(MODULE,prolog << "Received PPT_EXIT_NOW in an extension chunk." << endl);
172
173 // This call closes the socket - it does minimal bookkeeping and
174 // calls the the kernel's close() function. NB: The method is
175 // implemented in PPTServer.cc and that calls Socket::close() on the
176 // Socket instance held by the Connection.
177 connection->closeConnection();
178
179 BESDEBUG(MODULE,prolog << "Calling exit(CHILD_SUBPROCESS_READY) which has a value of " << CHILD_SUBPROCESS_READY << endl);
180
181 INFO_LOG("Received exit command.");
182
183 exit(CHILD_SUBPROCESS_READY);
184 }
185
186 string cmd_str = ss.str();
187
188 BESDEBUG(MODULE, prolog << "Processing client command:" << endl << cmd_str << endl);
189
190 BES_STOPWATCH_START(MODULE, prolog + "Timer");
191
192
193 // This is where we actually save/assign the output stream used for the response
194 BESXMLInterface cmd(cmd_str, &my_ostrm);
195
196 int status = cmd.execute_request(from);
197 if (status == 0) {
198 cmd.finish(status);
199 fds.finish();
200 BESDEBUG(MODULE, prolog << "Client command successfully processed." << endl);
201 }
202 else {
203 BESDEBUG(MODULE, prolog << "ERROR - cmd.execute_request() returned: " << status << endl);
204
205 // Send the extension status=error to the client so that it can reset. The finish()
206 // method is called _after_ this so that the error response will be recognizable.
207 // At least, I think that's what is happening... jhrg 11/12/17
208 map<string, string> extensions;
209 extensions["status"] = "error";
210 if (status == BES_INTERNAL_FATAL_ERROR) {
211 extensions["exit"] = "true";
212 }
213 connection->sendExtensions(extensions);
214
215 cmd.finish(status);
216 // we are finished, send the last chunk
217 fds.finish();
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 ERROR_LOG("BES Internal Fatal Error; child returning "
224 + std::to_string(SERVER_EXIT_ABNORMAL_TERMINATION) + " to the master listener.");
225
226 connection->closeConnection();
227 exit(SERVER_EXIT_ABNORMAL_TERMINATION);
228
229 break;
230
231 case BES_INTERNAL_ERROR:
232 // This should be an option. The default is to not exit. jhrg 10/4/18
233 if (TheBESKeys::TheKeys()->read_bool_key(EXIT_ON_INTERNAL_ERROR, false)) {
234 ERROR_LOG("BES Internal Error; child returning "
235 + std::to_string(SERVER_EXIT_ABNORMAL_TERMINATION) + " to the master listener.");
236
237 connection->closeConnection();
238 exit(SERVER_EXIT_ABNORMAL_TERMINATION);
239 }
240 break;
241
242 // These are recoverable errors
243 case BES_SYNTAX_USER_ERROR:
244 case BES_FORBIDDEN_ERROR:
245 case BES_NOT_FOUND_ERROR:
246 default:
247 break;
248 }
249 }
250 } // This is the end of the infinite loop that processes commands.
251}
252
259void BESServerHandler::dump(ostream &strm) const
260{
261 strm << BESIndent::LMarg << "BESServerHandler::dump - (" << (void *) this << ")" << endl;
262 BESIndent::Indent();
263 strm << BESIndent::LMarg << "server method: " << _method << endl;
264 BESIndent::UnIndent();
265}
266
std::string get_message() const
get the error message for this exception
Definition BESError.h:132
pid_t update_pid()
Update the d_pid and the d_log_record_prolog_base values.
Definition BESLog.cc:182
void dump(std::ostream &strm) const override
dumps information about this object
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
static TheBESKeys * TheKeys()
Access to the singleton.
Definition TheBESKeys.cc:85