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", "ServerApp: done initializing default module"
516  << endl ) ;
517 
518  BESDEBUG( "server", "ServerApp: initializing default commands ... "
519  << endl ) ;
520  BESXMLDefaultCommands::initialize( argc, argv ) ;
521  BESDEBUG( "server", "ServerApp: done initializing default commands"
522  << endl ) ;
523 
524  // This will load and initialize all of the modules
525  BESDEBUG( "server", "ServerApp: initializing loaded modules ... "
526  << endl ) ;
527  int ret = BESModuleApp::initialize( argc, argv ) ;
528  BESDEBUG( "server", "ServerApp: done initializing loaded modules"
529  << endl ) ;
530 
531  BESDEBUG( "server", "ServerApp: initialized settings:" << *this ) ;
532 
533  if( needhelp )
534  {
536  }
537 
538  return ret ;
539 }
540 
541 int
543 {
544  try
545  {
546  BESDEBUG( "server", "ServerApp: initializing memory pool ... "
547  << endl ) ;
549  BESDEBUG( "server", "OK" << endl ) ;
550 
551  SocketListener listener ;
552 
553  if( _portVal )
554  {
555  _ts = new TcpSocket( _portVal ) ;
556  listener.listen( _ts ) ;
557  BESDEBUG( "server", "ServerApp: listening on port ("
558  << _portVal << ")" << endl ) ;
559  }
560 
561  if( !_unixSocket.empty() )
562  {
563  _us = new UnixSocket( _unixSocket ) ;
564  listener.listen( _us ) ;
565  BESDEBUG( "server", "ServerApp: listening on unix socket ("
566  << _unixSocket << ")" << endl ) ;
567  }
568 
569  BESServerHandler handler ;
570 
571  _ps = new PPTServer( &handler, &listener, _secure ) ;
572  _ps->initConnection() ;
573  }
574  catch( BESError &se )
575  {
576  cerr << se.get_message() << endl ;
577  (*BESLog::TheLog()) << se.get_message() << endl ;
578  return 1 ;
579  }
580  catch( ... )
581  {
582  cerr << "caught unknown exception" << endl ;
583  (*BESLog::TheLog()) << "caught unknown exception initializing sockets"
584  << endl ;
585  return 1 ;
586  }
587 
588  return 0 ;
589 }
590 
591 int
593 {
594  pid_t apppid = getpid() ;
595  if( apppid == _mypid )
596  {
597  if( _ps )
598  {
599  _ps->closeConnection() ;
600  delete _ps ;
601  }
602  if( _ts )
603  {
604  _ts->close() ;
605  delete _ts ;
606  }
607  if( _us )
608  {
609  _us->close() ;
610  delete _us ;
611  }
612 
613  // Do this in the reverse order that it was initialized. So
614  // terminate the loaded modules first, then the default
615  // commands, then the default module.
616  BESDEBUG( "server", "ServerApp: terminating loaded modules ... "
617  << endl ) ;
618  BESModuleApp::terminate( sig ) ;
619  BESDEBUG( "server", "ServerApp: done terminating loaded modules"
620  << endl ) ;
621 
622  BESDEBUG( "server", "ServerApp: terminating default commands ... "
623  << endl ) ;
625  BESDEBUG( "server", "ServerApp: done terminating default commands ... "
626  << endl ) ;
627 
628  BESDEBUG( "server", "ServerApp: terminating default module ... "
629  << endl ) ;
631  BESDEBUG( "server", "ServerApp: done terminating default module ... "
632  << endl ) ;
633  }
634  return sig ;
635 }
636 
643 void
644 ServerApp::dump( ostream &strm ) const
645 {
646  strm << BESIndent::LMarg << "ServerApp::dump - ("
647  << (void *)this << ")" << endl ;
649  strm << BESIndent::LMarg << "got port? " << _gotPort << endl ;
650  strm << BESIndent::LMarg << "port: " << _portVal << endl ;
651  strm << BESIndent::LMarg << "unix socket: " << _unixSocket << endl ;
652  strm << BESIndent::LMarg << "is secure? " << _secure << endl ;
653  strm << BESIndent::LMarg << "pid: " << _mypid << endl ;
654  if( _ts )
655  {
656  strm << BESIndent::LMarg << "tcp socket:" << endl ;
658  _ts->dump( strm ) ;
660  }
661  else
662  {
663  strm << BESIndent::LMarg << "tcp socket: null" << endl ;
664  }
665  if( _us )
666  {
667  strm << BESIndent::LMarg << "unix socket:" << endl ;
669  _us->dump( strm ) ;
671  }
672  else
673  {
674  strm << BESIndent::LMarg << "unix socket: null" << endl ;
675  }
676  if( _ps )
677  {
678  strm << BESIndent::LMarg << "ppt server:" << endl ;
680  _ps->dump( strm ) ;
682  }
683  else
684  {
685  strm << BESIndent::LMarg << "ppt server: null" << endl ;
686  }
687  BESModuleApp::dump( strm ) ;
689 }
690 
691 int
692 main( int argc, char **argv )
693 {
694  try
695  {
696  ServerApp app ;
697  return app.main( argc, argv ) ;
698  }
699  catch( BESError &e )
700  {
701  cerr << "Caught unhandled exception: " << endl ;
702  cerr << e.get_message() << endl ;
703  return 1 ;
704  }
705  catch( ... )
706  {
707  cerr << "Caught unhandled, unknown exception" << endl ;
708  return 1 ;
709  }
710  return 0 ;
711 }
712