OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
ServerApp.cc
Go to the documentation of this file.
1 // ServerApp.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 <signal.h>
34 #include <sys/wait.h> // for wait
35 #include <sys/types.h>
36 #include <unistd.h>
37 #include <iostream>
38 #include <fstream>
39 #include <sstream>
40 #include <cstdlib>
41 
42 using std::cout;
43 using std::cerr;
44 using std::endl;
45 using std::ios;
46 using std::ostringstream;
47 using std::ofstream;
48 
49 #include "config.h"
50 
51 #include "ServerApp.h"
52 #include "ServerExitConditions.h"
53 #include "TheBESKeys.h"
54 #include "BESLog.h"
55 #include "SocketListener.h"
56 #include "TcpSocket.h"
57 #include "UnixSocket.h"
58 #include "BESServerHandler.h"
59 #include "BESError.h"
60 #include "PPTServer.h"
61 #include "BESMemoryManager.h"
62 #include "BESDebug.h"
63 #include "BESServerUtils.h"
64 
65 #include "BESDefaultModule.h"
66 #include "BESXMLDefaultCommands.h"
67 #include "BESDaemonConstants.h"
68 
69 static int session_id = 0;
70 
72  BESModuleApp(), _portVal(0), _gotPort(false), _unixSocket(""), _secure(false), _mypid(0), _ts(0), _us(0), _ps(0)
73 {
74  _mypid = getpid();
75 }
76 
78 {
79 }
80 
81 // This is needed so that the master beslistner will get the exit status of
82 // all of the child beslisteners (preventing them from becoming zombies).
83 static void CatchSigChild(int sig)
84 {
85  if (sig == SIGCHLD)
86  {
87  BESDEBUG("besdaemon", "beslistener: caught sig chld" << endl);
88  int stat;
89  pid_t pid = wait(&stat);
90  BESDEBUG("besdaemon", "beslistener: child pid: " << pid << " exited with status: " << stat << endl);
91  }
92 }
93 
94 // If the HUP signal is sent to the master beslistener, it should exit and
95 // return a value indicating to the besdaemon that it should be restarted.
96 // This also has the side-affect of re-reading the configuration file.
97 static void CatchSigHup(int sig)
98 {
99  if (sig == SIGHUP)
100  {
101  int pid = getpid();
102  BESDEBUG("besdaemon", "beslisterner: " << pid << " caught SIGHUP." << endl);
103 
105 
106  BESDEBUG("besdaemon", "beslisterner: " << pid << " past terminate (SIGHUP)." << endl);
107 
108  exit(SERVER_EXIT_RESTART);
109  }
110 }
111 // This is the default signal sent by 'kill'; when the master beslistener gets
112 // this signal it should stop. besdaemon should not try to start a new
113 // master beslistener.
114 static void CatchSigTerm(int sig)
115 {
116  if (sig == SIGTERM)
117  {
118  int pid = getpid();
119  BESDEBUG("besdaemon", "beslisterner: " << pid << " caught SIGTERM" << endl);
120 
122 
123  BESDEBUG("besdaemon", "beslisterner: " << pid << " past terminate (SIGTERM)." << endl);
124 
126  }
127 }
128 
137 static void register_signal_handlers()
138 {
139  struct sigaction act;
140  sigemptyset(&act.sa_mask);
141  sigaddset(&act.sa_mask, SIGCHLD);
142  sigaddset(&act.sa_mask, SIGTERM);
143  sigaddset(&act.sa_mask, SIGHUP);
144  act.sa_flags = 0;
145 #ifdef SA_RESTART
146  BESDEBUG("besdaemon" , "besdaemon: setting restart for sigchld." << endl);
147  act.sa_flags |= SA_RESTART;
148 #endif
149 
150  BESDEBUG( "server", "beslisterner: Registering signal handlers ... " << endl );
151 
152  act.sa_handler = CatchSigChild;
153  if (sigaction(SIGCHLD, &act, 0))
154  throw BESInternalFatalError("Could not register a handler to catch beslistener child process status.", __FILE__, __LINE__);
155 
156  // For these, block sigchld
157  // sigaddset(&act.sa_mask, SIGCHLD);
158  act.sa_handler = CatchSigTerm;
159  if (sigaction(SIGTERM, &act, 0) < 0)
160  throw BESInternalFatalError("Could not register a handler to catch beslistener terminate signal.", __FILE__, __LINE__);
161 
162  act.sa_handler = CatchSigHup;
163  if (sigaction(SIGHUP, &act, 0) < 0)
164  throw BESInternalFatalError("Could not register a handler to catch beslistener hup signal.", __FILE__, __LINE__);
165 
166  BESDEBUG( "server", "beslisterner: OK" << endl );
167 }
168 
169 int ServerApp::initialize(int argc, char **argv)
170 {
171  int c = 0;
172  bool needhelp = false;
173  string dashi;
174  string dashc;
175  string dashd = "";
176 
177  // If you change the getopt statement below, be sure to make the
178  // corresponding change in daemon.cc and besctl.in
179  while ((c = getopt(argc, argv, "hvsd:c:p:u:i:r:")) != EOF)
180  {
181  switch (c)
182  {
183  case 'i':
184  dashi = optarg;
185  break;
186  case 'c':
187  dashc = optarg;
188  break;
189  case 'r':
190  break; // we can ignore the /var/run directory option here
191  case 'p':
192  _portVal = atoi(optarg);
193  _gotPort = true;
194  break;
195  case 'u':
196  _unixSocket = optarg;
197  break;
198  case 'd':
199  dashd = optarg;
200  // BESDebug::SetUp(optarg);
201  break;
202  case 'v':
204  break;
205  case 's':
206  _secure = true;
207  break;
208  case 'h':
209  case '?':
210  default:
211  needhelp = true;
212  break;
213  }
214  }
215 
216  // before we can do any processing, log any messages, initialize any
217  // modules, do anything, we need to determine where the BES
218  // configuration file lives. From here we get the name of the log
219  // file, group and user id, and information that the modules will
220  // need to run properly.
221 
222  // If the -c option was passed, set the config file name in TheBESKeys
223  if (!dashc.empty())
224  {
225  TheBESKeys::ConfigFile = dashc;
226  }
227 
228  // If the -c option was not passed, but the -i option
229  // was passed, then use the -i option to construct
230  // the path to the config file
231  if (dashc.empty() && !dashi.empty())
232  {
233  if (dashi[dashi.length() - 1] != '/')
234  {
235  dashi += '/';
236  }
237  string conf_file = dashi + "etc/bes/bes.conf";
238  TheBESKeys::ConfigFile = conf_file;
239  }
240 
241  if (!dashd.empty())
242  BESDebug::SetUp(dashd);
243 
244  // register the two debug context for the server and ppt. The
245  // Default Module will register the bes context.
246  BESDebug::Register("server");
247  BESDebug::Register("ppt");
248 
249  // Because we are now running as the user specified in the
250  // configuration file, we won't be able to listen on system ports.
251  // If this is a problem, we may need to move this code above setting
252  // the user and group ids.
253  bool found = false;
254  string port_key = "BES.ServerPort";
255  if (!_gotPort)
256  {
257  string sPort;
258  try
259  {
260  TheBESKeys::TheKeys()->get_value(port_key, sPort, found);
261  }
262  catch (BESError &e)
263  {
264  BESDEBUG( "server", "beslisterner: FAILED" << endl );
265  string err = (string) "FAILED: " + e.get_message();
266  cerr << err << endl;
267  (*BESLog::TheLog()) << err << endl;
269  }
270  if (found)
271  {
272  _portVal = atoi(sPort.c_str());
273  if (_portVal != 0)
274  {
275  _gotPort = true;
276  }
277  }
278  }
279 
280  found = false;
281  string socket_key = "BES.ServerUnixSocket";
282  if (_unixSocket == "")
283  {
284  try
285  {
286  TheBESKeys::TheKeys()->get_value(socket_key, _unixSocket, found);
287  }
288  catch (BESError &e)
289  {
290  BESDEBUG( "server", "beslisterner: FAILED" << endl );
291  string err = (string) "FAILED: " + e.get_message();
292  cerr << err << endl;
293  (*BESLog::TheLog()) << err << endl;
295  }
296  }
297 
298  if (!_gotPort && _unixSocket == "")
299  {
300  string msg = "Must specify a tcp port or a unix socket or both\n";
301  msg += "Please specify on the command line with -p <port>";
302  msg += " and/or -u <unix_socket>\n";
303  msg += "Or specify in the bes configuration file with " + port_key + " and/or " + socket_key + "\n";
304  cout << endl << msg;
305  (*BESLog::TheLog()) << msg << endl;
307  }
308 
309  found = false;
310  if (_secure == false)
311  {
312  string key = "BES.ServerSecure";
313  string isSecure;
314  try
315  {
316  TheBESKeys::TheKeys()->get_value(key, isSecure, found);
317  }
318  catch (BESError &e)
319  {
320  BESDEBUG( "server", "beslisterner: FAILED" << endl );
321  string err = (string) "FAILED: " + e.get_message();
322  cerr << err << endl;
323  (*BESLog::TheLog()) << err << endl;
325  }
326  if (isSecure == "Yes" || isSecure == "YES" || isSecure == "yes")
327  {
328  _secure = true;
329  }
330  }
331 
332  try
333  {
334  register_signal_handlers();
335  }
336  catch (BESError &e)
337  {
338  BESDEBUG( "server", "beslisterner: FAILED: " << e.get_message() << endl );
339  (*BESLog::TheLog()) << e.get_message() << endl;
341  }
342 
343  BESDEBUG( "server", "beslisterner: initializing default module ... "
344  << endl );
345  BESDefaultModule::initialize(argc, argv);
346  BESDEBUG( "server", "beslisterner: done initializing default module"
347  << endl );
348 
349  BESDEBUG( "server", "beslisterner: initializing default commands ... "
350  << endl );
352  BESDEBUG( "server", "beslisterner: done initializing default commands"
353  << endl );
354 
355  // This will load and initialize all of the modules
356  BESDEBUG( "server", "beslisterner: initializing loaded modules ... "
357  << endl );
358  int ret = BESModuleApp::initialize(argc, argv);
359  BESDEBUG( "server", "beslisterner: done initializing loaded modules"
360  << endl );
361 
362  BESDEBUG( "server", "beslisterner: initialized settings:" << *this );
363 
364  if (needhelp)
365  {
367  }
368 
369  // This sets the process group to be ID of this process. All children
370  // will get this GID. Then use killpg() to send a signal to this process
371  // and all of the children.
372  session_id = setsid();
373  BESDEBUG("besdaemon", "beslisterner: The master beslistener session id (group id): " << session_id << endl);
374 
375  return ret;
376 }
377 
379 {
380  try
381  {
382  BESDEBUG( "server", "beslisterner: initializing memory pool ... "
383  << endl );
385  BESDEBUG( "server", "OK" << endl );
386 
387  SocketListener listener;
388  if (_portVal)
389  {
390  _ts = new TcpSocket(_portVal);
391  listener.listen(_ts);
392 
393  BESDEBUG( "server", "beslisterner: listening on port (" << _portVal << ")" << endl );
394 
395  BESDEBUG( "server", "beslisterner: about to write status (4)" << endl );
396  // Write to stdout works because the besdaemon is listening on the
397  // other end of a pipe where the pipe fd[1] has been dup2'd to
398  // stdout. See daemon.cc:start_master_beslistener.
399  // NB BESLISTENER_PIPE_FD is 1 (stdout)
400  int status = BESLISTENER_RUNNING;
401  int res = write(BESLISTENER_PIPE_FD, &status, sizeof(status));
402 
403  BESDEBUG( "server", "beslisterner: wrote status (" << res << ")" << endl );
404  }
405 
406  if (!_unixSocket.empty())
407  {
408  _us = new UnixSocket(_unixSocket);
409  listener.listen(_us);
410  BESDEBUG( "server", "beslisterner: listening on unix socket ("
411  << _unixSocket << ")" << endl );
412  }
413 
414  BESServerHandler handler;
415 
416  _ps = new PPTServer(&handler, &listener, _secure);
417  _ps->initConnection();
418 
419  _ps->closeConnection();
420  }
421  catch (BESError &se)
422  {
423  cerr << se.get_message() << endl;
424  (*BESLog::TheLog()) << se.get_message() << endl;
425  int status = SERVER_EXIT_FATAL_CAN_NOT_START;
426  write(BESLISTENER_PIPE_FD, &status, sizeof(status));
427  close(BESLISTENER_PIPE_FD);
428  return 1;
429  }
430  catch (...)
431  {
432  cerr << "caught unknown exception" << endl;
433  (*BESLog::TheLog()) << "caught unknown exception initializing sockets" << endl;
434  int status = SERVER_EXIT_FATAL_CAN_NOT_START;
435  write(BESLISTENER_PIPE_FD, &status, sizeof(status));
436  close(BESLISTENER_PIPE_FD);
437  return 1;
438  }
439 
440  close(BESLISTENER_PIPE_FD);
441  return 0;
442 }
443 
445 {
446  pid_t apppid = getpid();
447  if (apppid == _mypid)
448  {
449  if (_ps)
450  {
451  _ps->closeConnection();
452  delete _ps;
453  }
454  if (_ts)
455  {
456  _ts->close();
457  delete _ts;
458  }
459  if (_us)
460  {
461  _us->close();
462  delete _us;
463  }
464 
465  // Do this in the reverse order that it was initialized. So
466  // terminate the loaded modules first, then the default
467  // commands, then the default module.
468  BESDEBUG( "server", "beslisterner: terminating loaded modules ... "
469  << endl );
471  BESDEBUG( "server", "beslisterner: done terminating loaded modules"
472  << endl );
473 
474  BESDEBUG( "server", "beslisterner: terminating default commands ... "
475  << endl );
477  BESDEBUG( "server", "beslisterner: done terminating default commands ... "
478  << endl );
479 
480  BESDEBUG( "server", "beslisterner: terminating default module ... "
481  << endl );
483  BESDEBUG( "server", "beslisterner: done terminating default module ... "
484  << endl );
485  }
486  return sig;
487 }
488 
495 void ServerApp::dump(ostream &strm) const
496 {
497  strm << BESIndent::LMarg << "ServerApp::dump - (" << (void *) this << ")" << endl;
499  strm << BESIndent::LMarg << "got port? " << _gotPort << endl;
500  strm << BESIndent::LMarg << "port: " << _portVal << endl;
501  strm << BESIndent::LMarg << "unix socket: " << _unixSocket << endl;
502  strm << BESIndent::LMarg << "is secure? " << _secure << endl;
503  strm << BESIndent::LMarg << "pid: " << _mypid << endl;
504  if (_ts)
505  {
506  strm << BESIndent::LMarg << "tcp socket:" << endl;
508  _ts->dump(strm);
510  }
511  else
512  {
513  strm << BESIndent::LMarg << "tcp socket: null" << endl;
514  }
515  if (_us)
516  {
517  strm << BESIndent::LMarg << "unix socket:" << endl;
519  _us->dump(strm);
521  }
522  else
523  {
524  strm << BESIndent::LMarg << "unix socket: null" << endl;
525  }
526  if (_ps)
527  {
528  strm << BESIndent::LMarg << "ppt server:" << endl;
530  _ps->dump(strm);
532  }
533  else
534  {
535  strm << BESIndent::LMarg << "ppt server: null" << endl;
536  }
537  BESModuleApp::dump(strm);
539 }
540 
541 int main(int argc, char **argv)
542 {
543  try
544  {
545  ServerApp app;
546  return app.main(argc, argv);
547  }
548  catch (BESError &e)
549  {
550  cerr << "Caught unhandled exception: " << endl;
551  cerr << e.get_message() << endl;
552  return 1;
553  }
554  catch (...)
555  {
556  cerr << "Caught unhandled, unknown exception" << endl;
557  return 1;
558  }
559  return 0;
560 }
561