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 <unistd.h> // for getpid
35 #include <grp.h> // for getgrnam
36 #include <pwd.h> // for getpwnam
37 
38 #include <iostream>
39 #include <fstream>
40 #include <sstream>
41 #include <cstdlib>
42 
43 using std::cout ;
44 using std::cerr ;
45 using std::endl ;
46 using std::ios ;
47 using std::ostringstream ;
48 using std::ofstream ;
49 
50 #include "config.h"
51 
52 #include "ServerApp.h"
53 #include "ServerExitConditions.h"
54 #include "TheBESKeys.h"
55 #include "BESLog.h"
56 #include "SocketListener.h"
57 #include "TcpSocket.h"
58 #include "UnixSocket.h"
59 #include "BESServerHandler.h"
60 #include "BESError.h"
61 #include "PPTServer.h"
62 #include "BESMemoryManager.h"
63 #include "BESDebug.h"
64 #include "BESServerUtils.h"
65 
66 #include "BESDefaultModule.h"
67 #include "BESXMLDefaultCommands.h"
68 
70  : BESModuleApp(),
71  _portVal( 0 ),
72  _gotPort( false ),
73  _unixSocket( "" ),
74  _secure( false ),
75  _mypid( 0 ),
76  _ts( 0 ),
77  _us( 0 ),
78  _ps( 0 )
79 {
80  _mypid = getpid() ;
81 }
82 
84 {
85 }
86 
87 void
89 {
90  if( sig == SIGTERM )
91  {
94  }
95 }
96 
97 void
99 {
100  if( sig == SIGINT )
101  {
104  }
105 }
106 
107 void
109 {
110  if( sig == SIGUSR1 )
111  {
113  exit( SERVER_EXIT_RESTART ) ;
114  }
115 }
116 
117 void
118 ServerApp::set_group_id()
119 {
120 #if !defined(OS2) && !defined(TPF)
121  // OS/2 and TPF don't support groups.
122 
123  // get group id or name from BES configuration file
124  // If BES.Group begins with # then it is a group id,
125  // else it is a group name and look up the id.
126  BESDEBUG( "server", "ServerApp: Setting group id ... " << endl ) ;
127  bool found = false ;
128  string key = "BES.Group" ;
129  string group_str ;
130  try
131  {
132  TheBESKeys::TheKeys()->get_value( key, group_str, found ) ;
133  }
134  catch( BESError &e )
135  {
136  BESDEBUG( "server", "FAILED" << endl ) ;
137  string err = string("FAILED: ") + e.get_message() ;
138  cerr << err << endl ;
139  (*BESLog::TheLog()) << err << endl ;
141  }
142  if( !found || group_str.empty() )
143  {
144  BESDEBUG( "server", "FAILED" << endl ) ;
145  string err = "FAILED: Group not specified in BES configuration file" ;
146  cerr << err << endl ;
147  (*BESLog::TheLog()) << err << endl ;
149  }
150  BESDEBUG( "server", "to " << group_str << " ... " << endl ) ;
151 
152  gid_t new_gid = 0 ;
153  if( group_str[0] == '#' )
154  {
155  // group id starts with a #, so is a group id
156  const char *group_c = group_str.c_str() ;
157  group_c++ ;
158  new_gid = atoi( group_c ) ;
159  }
160  else
161  {
162  // specified group is a group name
163  struct group *ent ;
164  ent = getgrnam( group_str.c_str() ) ;
165  if( !ent )
166  {
167  BESDEBUG( "server", "FAILED" << endl ) ;
168  string err = (string)"FAILED: Group " + group_str
169  + " does not exist" ;
170  cerr << err << endl ;
171  (*BESLog::TheLog()) << err << endl ;
173  }
174  new_gid = ent->gr_gid ;
175  }
176 
177  if( new_gid < 1 )
178  {
179  BESDEBUG( "server", "FAILED" << endl ) ;
180  ostringstream err ;
181  err << "FAILED: Group id " << new_gid
182  << " not a valid group id for BES" ;
183  cerr << err.str() << endl ;
184  (*BESLog::TheLog()) << err.str() << endl ;
186  }
187 
188  BESDEBUG( "server", "to id " << new_gid << " ... " << endl ) ;
189  if( setgid( new_gid ) == -1 )
190  {
191  BESDEBUG( "server", "FAILED" << endl ) ;
192  ostringstream err ;
193  err << "FAILED: unable to set the group id to " << new_gid ;
194  cerr << err.str() << endl ;
195  (*BESLog::TheLog()) << err.str() << endl ;
197  }
198 
199  BESDEBUG( "server", "OK" << endl ) ;
200 #else
201  BESDEBUG( "server", "ServerApp: Groups not supported in this OS" << endl ) ;
202 #endif
203 }
204 
205 void
206 ServerApp::set_user_id()
207 {
208  BESDEBUG( "server", "ServerApp: Setting user id ... " << endl ) ;
209 
210  // Get user name or id from the BES configuration file.
211  // If the BES.User value begins with # then it is a user
212  // id, else it is a user name and need to look up the
213  // user id.
214  bool found = false ;
215  string key = "BES.User" ;
216  string user_str ;
217  try
218  {
219  TheBESKeys::TheKeys()->get_value( key, user_str, found ) ;
220  }
221  catch( BESError &e )
222  {
223  BESDEBUG( "server", "FAILED" << endl ) ;
224  string err = (string)"FAILED: " + e.get_message() ;
225  cerr << err << endl ;
226  (*BESLog::TheLog()) << err << endl ;
228  }
229  if( !found || user_str.empty() )
230  {
231  BESDEBUG( "server", "FAILED" << endl ) ;
232  string err = (string)"FAILED: User not specified in BES config file" ;
233  cerr << err << endl ;
234  (*BESLog::TheLog()) << err << endl ;
236  }
237  BESDEBUG( "server", "to " << user_str << " ... " << endl ) ;
238 
239  uid_t new_id = 0 ;
240  if( user_str[0] == '#' )
241  {
242  const char *user_str_c = user_str.c_str() ;
243  user_str_c++ ;
244  new_id = atoi( user_str_c ) ;
245  }
246  else
247  {
248  struct passwd *ent ;
249  ent = getpwnam( user_str.c_str() ) ;
250  if( !ent )
251  {
252  BESDEBUG( "server", "FAILED" << endl ) ;
253  string err = (string)"FAILED: Bad user name specified: "
254  + user_str ;
255  cerr << err << endl ;
256  (*BESLog::TheLog()) << err << endl ;
258  }
259  new_id = ent->pw_uid ;
260  }
261 
262  // new user id cannot be root (0)
263  if( !new_id )
264  {
265  BESDEBUG( "server", "FAILED" << endl ) ;
266  string err = (string)"FAILED: BES cannot run as root" ;
267  cerr << err << endl ;
268  (*BESLog::TheLog()) << err << endl ;
270  }
271 
272  BESDEBUG( "server", "to " << new_id << " ... " << endl ) ;
273  if( setuid( new_id ) == -1 )
274  {
275  BESDEBUG( "server", "FAILED" << endl ) ;
276  ostringstream err ;
277  err << "FAILED: Unable to set user id to " << new_id ;
278  cerr << err.str() << endl ;
279  (*BESLog::TheLog()) << err.str() << endl ;
281  }
282 }
283 
284 int
285 ServerApp::initialize( int argc, char **argv )
286 {
287  int c = 0 ;
288  bool needhelp = false ;
289  string dashi ;
290  string dashc ;
291 
292  // If you change the getopt statement below, be sure to make the
293  // corresponding change in daemon.cc and besctl.in
294  while( ( c = getopt( argc, argv, "hvsd:c:p:u:i:r:" ) ) != EOF )
295  {
296  switch( c )
297  {
298  case 'i':
299  dashi = optarg ;
300  break ;
301  case 'c':
302  dashc = optarg ;
303  break ;
304  case 'r':
305  break ; // we can ignore the /var/run directory option here
306  case 'p':
307  _portVal = atoi( optarg ) ;
308  _gotPort = true ;
309  break ;
310  case 'u':
311  _unixSocket = optarg ;
312  break ;
313  case 'd':
314  BESDebug::SetUp( optarg ) ;
315  break ;
316  case 'v':
318  break ;
319  case 's':
320  _secure = true ;
321  break ;
322  case 'h':
323  case '?':
324  default:
325  needhelp = true ;
326  break ;
327  }
328  }
329 
330  // before we can do any processing, log any messages, initialize any
331  // modules, do anything, we need to determine where the BES
332  // configuration file lives. From here we get the name of the log
333  // file, group and user id, and information that the modules will
334  // need to run properly.
335 
336  // If the -c optiion was passed, set the config file name in TheBESKeys
337  if( !dashc.empty() )
338  {
339  TheBESKeys::ConfigFile = dashc ;
340  }
341 
342  // If the -c option was not passed, but the -i option
343  // was passed, then use the -i option to construct
344  // the path to the config file
345  if( dashc.empty() && !dashi.empty() )
346  {
347  if( dashi[dashi.length()-1] != '/' )
348  {
349  dashi += '/' ;
350  }
351  string conf_file = dashi + "etc/bes/bes.conf" ;
352  TheBESKeys::ConfigFile = conf_file ;
353  }
354 
355  // Now that we have the configuration information, we can log to the
356  // BES log file if there are errors in starting up, etc...
357 
358  uid_t curr_euid = geteuid() ;
359 #ifndef BES_DEVELOPER
360  // must be root to run this app and to set user id and group id later
361  if( curr_euid )
362  {
363  string err = "FAILED: Must be root to run BES" ;
364  cerr << err << endl ;
365  (*BESLog::TheLog()) << err << endl ;
367  }
368 #else
369  cout << "Developer Mode: not testing if BES is run by root" << endl ;
370 #endif
371 
372  // register the two debug context for the server and ppt. The
373  // Default Module will register the bes context.
374  BESDebug::Register( "server" ) ;
375  BESDebug::Register( "ppt" ) ;
376 
377  // Before we can load modules, start writing to the BES log
378  // file, etc... we need to run as the proper user. Set the user
379  // id and the group id to what is specified in the BES
380  // configuration file
381  if( curr_euid == 0 )
382  {
383 #ifdef BES_DEVELOPER
384  cout << "Developer Mode: Running as root - setting group and user ids"
385  << endl ;
386 #endif
387  set_group_id() ;
388  set_user_id() ;
389  }
390  else
391  {
392  cout << "Developer Mode: Not setting group or user ids" << endl ;
393  }
394 
395  // Because we are now running as the user specified in the
396  // configuraiton file, we won't be able to listen on system ports.
397  // If this is a problem, we may need to move this code above setting
398  // the user and group ids.
399  bool found = false ;
400  string port_key = "BES.ServerPort" ;
401  if( !_gotPort )
402  {
403  string sPort ;
404  try
405  {
406  TheBESKeys::TheKeys()->get_value( port_key, sPort, found ) ;
407  }
408  catch( BESError &e )
409  {
410  BESDEBUG( "server", "FAILED" << endl ) ;
411  string err = (string)"FAILED: " + e.get_message() ;
412  cerr << err << endl ;
413  (*BESLog::TheLog()) << err << endl ;
415  }
416  if( found )
417  {
418  _portVal = atoi( sPort.c_str() ) ;
419  if( _portVal != 0 )
420  {
421  _gotPort = true ;
422  }
423  }
424  }
425 
426  found = false ;
427  string socket_key = "BES.ServerUnixSocket" ;
428  if( _unixSocket == "" )
429  {
430  try
431  {
432  TheBESKeys::TheKeys()->get_value( socket_key, _unixSocket, found ) ;
433  }
434  catch( BESError &e )
435  {
436  BESDEBUG( "server", "FAILED" << endl ) ;
437  string err = (string)"FAILED: " + e.get_message() ;
438  cerr << err << endl ;
439  (*BESLog::TheLog()) << err << endl ;
441  }
442  }
443 
444  if( !_gotPort && _unixSocket == "" )
445  {
446  string msg = "Must specify a tcp port or a unix socket or both\n" ;
447  msg += "Please specify on the command line with -p <port>" ;
448  msg += " and/or -u <unix_socket>\n" ;
449  msg += "Or specify in the bes configuration file with "
450  + port_key + " and/or " + socket_key + "\n" ;
451  cout << endl << msg ;
452  (*BESLog::TheLog()) << msg << endl ;
454  }
455 
456  found = false ;
457  if( _secure == false )
458  {
459  string key = "BES.ServerSecure" ;
460  string isSecure ;
461  try
462  {
463  TheBESKeys::TheKeys()->get_value( key, isSecure, found ) ;
464  }
465  catch( BESError &e )
466  {
467  BESDEBUG( "server", "FAILED" << endl ) ;
468  string err = (string)"FAILED: " + e.get_message() ;
469  cerr << err << endl ;
470  (*BESLog::TheLog()) << err << endl ;
472  }
473  if( isSecure == "Yes" || isSecure == "YES" || isSecure == "yes" )
474  {
475  _secure = true ;
476  }
477  }
478 
479  BESDEBUG( "server", "ServerApp: Registering signal SIGTERM ... " << endl ) ;
480  if( signal( SIGTERM, signalTerminate ) == SIG_ERR )
481  {
482  BESDEBUG( "server", "FAILED" << endl ) ;
483  string err = "FAILED: cannot register SIGTERM signal handler" ;
484  cerr << err << endl ;
485  (*BESLog::TheLog()) << err << endl ;
487  }
488  BESDEBUG( "server", "OK" << endl ) ;
489 
490  BESDEBUG( "server", "ServerApp: Registering signal SIGINT ... " << endl ) ;
491  if( signal( SIGINT, signalInterrupt ) == SIG_ERR )
492  {
493  BESDEBUG( "server", "FAILED" << endl ) ;
494  string err = "FAILED: cannot register SIGINT signal handler" ;
495  cerr << err << endl ;
496  (*BESLog::TheLog()) << err << endl ;
498  }
499  BESDEBUG( "server", "OK" << endl ) ;
500 
501  BESDEBUG( "server", "ServerApp: Registering signal SIGUSR1 ... " << endl ) ;
502  if( signal( SIGUSR1, signalRestart ) == SIG_ERR )
503  {
504  BESDEBUG( "server", "FAILED" << endl ) ;
505  string err = "FAILED: cannot register SIGUSR1 signal handler" ;
506  cerr << err << endl ;
507  (*BESLog::TheLog()) << err << endl ;
509  }
510  BESDEBUG( "server", "OK" << endl ) ;
511 
512  BESDEBUG( "server", "ServerApp: initializing default module ... "
513  << endl ) ;
514  BESDefaultModule::initialize( argc, argv ) ;
515  BESDEBUG( "server", "OK" << endl ) ;
516 
517  BESDEBUG( "server", "ServerApp: initializing default commands ... "
518  << endl ) ;
519  BESXMLDefaultCommands::initialize( argc, argv ) ;
520  BESDEBUG( "server", "OK" << endl ) ;
521 
522  // This will load and initialize all of the modules
523  int ret = BESModuleApp::initialize( argc, argv ) ;
524 
525  BESDEBUG( "server", "ServerApp: initialized settings:" << *this ) ;
526 
527  if( needhelp )
528  {
530  }
531 
532  return ret ;
533 }
534 
535 int
537 {
538  try
539  {
540  BESDEBUG( "server", "ServerApp: initializing memory pool ... "
541  << endl ) ;
543  BESDEBUG( "server", "OK" << endl ) ;
544 
545  SocketListener listener ;
546 
547  if( _portVal )
548  {
549  _ts = new TcpSocket( _portVal ) ;
550  listener.listen( _ts ) ;
551  BESDEBUG( "server", "ServerApp: listening on port ("
552  << _portVal << ")" << endl ) ;
553  }
554 
555  if( !_unixSocket.empty() )
556  {
557  _us = new UnixSocket( _unixSocket ) ;
558  listener.listen( _us ) ;
559  BESDEBUG( "server", "ServerApp: listening on unix socket ("
560  << _unixSocket << ")" << endl ) ;
561  }
562 
563  BESServerHandler handler ;
564 
565  _ps = new PPTServer( &handler, &listener, _secure ) ;
566  _ps->initConnection() ;
567  }
568  catch( BESError &se )
569  {
570  cerr << se.get_message() << endl ;
571  (*BESLog::TheLog()) << se.get_message() << endl ;
572  return 1 ;
573  }
574  catch( ... )
575  {
576  cerr << "caught unknown exception" << endl ;
577  (*BESLog::TheLog()) << "caught unknown exception initializing sockets"
578  << endl ;
579  return 1 ;
580  }
581 
582  return 0 ;
583 }
584 
585 int
587 {
588  pid_t apppid = getpid() ;
589  if( apppid == _mypid )
590  {
591  if( _ps )
592  {
593  _ps->closeConnection() ;
594  delete _ps ;
595  }
596  if( _ts )
597  {
598  _ts->close() ;
599  delete _ts ;
600  }
601  if( _us )
602  {
603  _us->close() ;
604  delete _us ;
605  }
606 
607  BESDEBUG( "server", "ServerApp: terminating default module ... "
608  << endl ) ;
610  BESDEBUG( "server", "OK" << endl ) ;
611 
612  BESDEBUG( "server", "ServerApp: terminating default commands ... "
613  << endl ) ;
615  BESDEBUG( "server", "OK" << endl ) ;
616 
617  BESModuleApp::terminate( sig ) ;
618  }
619  return sig ;
620 }
621 
628 void
629 ServerApp::dump( ostream &strm ) const
630 {
631  strm << BESIndent::LMarg << "ServerApp::dump - ("
632  << (void *)this << ")" << endl ;
634  strm << BESIndent::LMarg << "got port? " << _gotPort << endl ;
635  strm << BESIndent::LMarg << "port: " << _portVal << endl ;
636  strm << BESIndent::LMarg << "unix socket: " << _unixSocket << endl ;
637  strm << BESIndent::LMarg << "is secure? " << _secure << endl ;
638  strm << BESIndent::LMarg << "pid: " << _mypid << endl ;
639  if( _ts )
640  {
641  strm << BESIndent::LMarg << "tcp socket:" << endl ;
643  _ts->dump( strm ) ;
645  }
646  else
647  {
648  strm << BESIndent::LMarg << "tcp socket: null" << endl ;
649  }
650  if( _us )
651  {
652  strm << BESIndent::LMarg << "unix socket:" << endl ;
654  _us->dump( strm ) ;
656  }
657  else
658  {
659  strm << BESIndent::LMarg << "unix socket: null" << endl ;
660  }
661  if( _ps )
662  {
663  strm << BESIndent::LMarg << "ppt server:" << endl ;
665  _ps->dump( strm ) ;
667  }
668  else
669  {
670  strm << BESIndent::LMarg << "ppt server: null" << endl ;
671  }
672  BESModuleApp::dump( strm ) ;
674 }
675 
676 int
677 main( int argc, char **argv )
678 {
679  try
680  {
681  ServerApp app ;
682  return app.main( argc, argv ) ;
683  }
684  catch( BESError &e )
685  {
686  cerr << "Caught unhandled exception: " << endl ;
687  cerr << e.get_message() << endl ;
688  return 1 ;
689  }
690  catch( ... )
691  {
692  cerr << "Caught unhandled, unknown exception" << endl ;
693  return 1 ;
694  }
695  return 0 ;
696 }
697 
virtual ~ServerApp()
Definition: ServerApp.cc:83
static void SetUp(const string &values)
Sets up debugging for the bes.
Definition: BESDebug.cc:68
static int initialize(int argc, char **argv)
#define SERVER_EXIT_NORMAL_SHUTDOWN
virtual int main(int argC, char **argV)
main method of the BES application
Definition: BESBaseApp.cc:75
static void signalRestart(int sig)
Definition: ServerApp.cc:108
static BESMemoryGlobalArea * initialize_memory_pool()
static void signalInterrupt(int sig)
Definition: ServerApp.cc:98
virtual int terminate(int sig=0)
clean up after the application
static int terminate(void)
Removes the default set of BES XML commands from the list of possible commands.
static void show_usage(const string &app_name)
static void Indent()
Definition: BESIndent.cc:38
static void signalTerminate(int sig)
Definition: ServerApp.cc:88
virtual string get_message()
get the error message for this exception
Definition: BESError.h:91
static int initialize(int argc, char **argv)
Loads the default set of BES XML commands.
virtual void initConnection()
Using the info passed into the SocketLister, wait for an inbound request (see SocketListener::accept(...
Definition: PPTServer.cc:146
Abstract exception class for the BES with basic string message.
Definition: BESError.h:51
virtual void closeConnection()
Definition: PPTServer.cc:171
static int terminate(void)
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
virtual void dump(ostream &strm) const
dumps information about this object
Definition: PPTServer.cc:277
virtual void dump(ostream &strm) const
dumps information about this object
Definition: ServerApp.cc:629
virtual int terminate(int sig=0)
clean up after the application
Definition: ServerApp.cc:586
virtual void dump(ostream &strm) const
dumps information about this object
#define SERVER_EXIT_FATAL_CAN_NOT_START
virtual int initialize(int argC, char **argV)
Load and initialize any BES modules.
Definition: ServerApp.cc:285
Base application object for all BES applications.
Definition: BESModuleApp.h:59
virtual void close()
Definition: Socket.cc:81
virtual int initialize(int argC, char **argV)
Load and initialize any BES modules.
Definition: BESModuleApp.cc:72
virtual void dump(ostream &strm) const
dumps information about this object
Definition: TcpSocket.cc:635
string appName(void) const
Returns the name of the application.
Definition: BESApp.h:132
void get_value(const string &s, string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: BESKeys.cc:466
static void show_version(const string &app_name)
static BESLog * TheLog()
Definition: BESLog.cc:347
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
#define SERVER_EXIT_RESTART
static void UnIndent()
Definition: BESIndent.cc:44
virtual void listen(Socket *s)
static BESKeys * TheKeys()
Definition: TheBESKeys.cc:46
static void Register(const string &flagName)
register the specified debug flag
Definition: BESDebug.h:124
static BESApp * TheApplication(void)
Returns the BESApp application object for this application.
Definition: BESApp.h:138
virtual void close()
Definition: UnixSocket.cc:230
virtual int run()
the applications functionality is implemented in the run method
Definition: ServerApp.cc:536
virtual void dump(ostream &strm) const
dumps information about this object
Definition: UnixSocket.cc:267
static string ConfigFile
Definition: TheBESKeys.h:45
virtual int terminate(int sig=0)=0
Clean up after the application.