daemon.cc

Go to the documentation of this file.
00001 // daemon.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 // 
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 // 
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025  
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include <unistd.h>  // for getopt fork setsid execvp access geteuid
00034 #include <sys/wait.h>  // for waitpid
00035 #include <sys/types.h>
00036 #include <sys/stat.h>  // for chmod
00037 #include <ctype.h> // for isdigit
00038 
00039 #include <fstream>
00040 #include <iostream>
00041 #include <string>
00042 #include <cstring>
00043 #include <cstdlib>
00044 #include <cerrno>
00045 
00046 using std::ifstream ;
00047 using std::ofstream ;
00048 using std::cout ;
00049 using std::endl ;
00050 using std::cerr ;
00051 using std::flush;
00052 using std::string ;
00053 
00054 #include "config.h"
00055 #include "ServerExitConditions.h"
00056 #include "BESServerUtils.h"
00057 #include "BESScrub.h"
00058 
00059 #define BES_SERVER_ROOT "BES_SERVER_ROOT"
00060 #define BES_SERVER "/beslistener"
00061 #define BES_SERVER_PID "/bes.pid"
00062 
00063 int  daemon_init() ;
00064 int  mount_server( char ** ) ;
00065 int  pr_exit( int status ) ;
00066 void store_listener_id( int pid ) ;
00067 bool load_names( const string &install_dir, const string &pid_dir ) ;
00068 
00069 string NameProgram ;
00070 
00071 // This two variables are set by load_names
00072 string server_name ;
00073 string file_for_listener ;
00074 
00075 char **arguments = 0 ; 
00076 
00077 int
00078 main(int argc, char *argv[])
00079 {
00080 #ifndef BES_DEVELOPER
00081     // must be root to run this app and to set user id and group id later
00082     uid_t curr_euid = geteuid() ;
00083     if( curr_euid )
00084     {
00085         cerr << "FAILED: Must be root to run BES" << endl ;
00086         exit( SERVER_EXIT_FATAL_CAN_NOT_START ) ;
00087     }
00088 #else
00089     cerr << "Developer Mode: not testing if BES is run by root" << endl ;
00090 #endif
00091 
00092     NameProgram = argv[0] ;
00093 
00094     string install_dir ;
00095     string pid_dir ;
00096 
00097     // If you change the getopt statement below, be sure to make the
00098     // corresponding change in ServerApp.cc and besctl.in
00099     int c = 0 ;
00100 
00101     // there are 16 arguments allowed to the daemon, including the program
00102     // name. 3 options do not have arguments and 6 have arguments
00103     if( argc > 16 )
00104     {
00105         // the show_usage method exits
00106         BESServerUtils::show_usage( NameProgram ) ;
00107     }
00108 
00109     // argv[0] is the name of the program, so start num_args at 1
00110     unsigned short num_args = 1 ;
00111     while( ( c = getopt( argc, argv, "hvsd:c:p:u:i:r:" ) ) != EOF )
00112     {
00113         switch( c )
00114         {
00115             case 'v': // version
00116                 BESServerUtils::show_version( NameProgram ) ;
00117                 break ;
00118             case '?': // unknown option
00119             case 'h': // help
00120                 BESServerUtils::show_usage( NameProgram ) ;
00121                 break ;
00122             case 'i': // BES install directory
00123                 install_dir = optarg ;
00124                 if( BESScrub::pathname_ok( install_dir, true ) == false )
00125                 {
00126                     cout << "The specified install directory (-i option) "
00127                          << "is incorrectly formatted. Must be less than "
00128                          << "255 characters and include the characters "
00129                          << "[0-9A-z_./-]" << endl ;
00130                     return 1 ;
00131                 }
00132                 num_args+=2 ;
00133                 break ;
00134             case 's': // secure server
00135                 num_args++ ;
00136                 break ;
00137             case 'r': // where to write the pid file
00138             {
00139                 pid_dir = optarg ;
00140                 if( BESScrub::pathname_ok( pid_dir, true ) == false )
00141                 {
00142                     cout << "The specified state directory (-r option) "
00143                          << "is incorrectly formatted. Must be less than "
00144                          << "255 characters and include the characters "
00145                          << "[0-9A-z_./-]" << endl ;
00146                     return 1 ;
00147                 }
00148                 num_args+=2 ;
00149             }
00150             break ;
00151             case 'c': // configuration file
00152             case 'u': // unix socket
00153             {
00154                 string check_path = optarg ;
00155                 if( BESScrub::pathname_ok( check_path, true ) == false )
00156                 {
00157                     cout << "The specified install directory (-i option) "
00158                          << "is incorrectly formatted. Must be less than "
00159                          << "255 characters and include the characters "
00160                          << "[0-9A-z_./-]" << endl ;
00161                     return 1 ;
00162                 }
00163                 num_args+=2 ;
00164             }
00165             break ;
00166             case 'p': // TCP port
00167             {
00168                 string port_num = optarg ;
00169                 for( unsigned int i = 0; i < port_num.length(); i++ )
00170                 {
00171                     if( !isdigit( port_num[i] ) )
00172                     {
00173                         cout << "The specified port contains non-digit "
00174                              << "characters: " << port_num << endl ;
00175                         return 1 ;
00176                     }
00177                 }
00178                 num_args+=2 ;
00179             }
00180             break ;
00181             case 'd': // debug
00182             {
00183                 string check_arg = optarg ;
00184                 if( BESScrub::command_line_arg_ok( check_arg ) == false )
00185                 {
00186                     cout << "The specified debug options \"" << check_arg
00187                          << "\" contains invalid characters" << endl ;
00188                     return 1 ;
00189                 }
00190                 num_args+=2 ;
00191             }
00192             break ;
00193             default:
00194                 BESServerUtils::show_usage( NameProgram ) ;
00195                 break ;
00196         }
00197     }
00198     // if the number of argumens is greater than the number of allowed arguments
00199     // then extra arguments were passed that aren't options. Show usage and
00200     // exit.
00201     if( argc > num_args )
00202     {
00203         cout << NameProgram
00204              << ": too many arguments passed to the BES" ;
00205         BESServerUtils::show_usage( NameProgram ) ;
00206     }
00207 
00208     if( pid_dir.empty() )
00209     {
00210         pid_dir = install_dir ;
00211     }
00212 
00213     // Set the name of the listener and the file for the listenet pid
00214     if( !load_names( install_dir, pid_dir ) )
00215         return 1 ;
00216 
00217     // make sure the array size is not too big
00218     if( BESScrub::size_ok( sizeof( char *), num_args+1 ) == false )
00219     {
00220         cout << NameProgram
00221              << ": too many arguments passed to the BES" ;
00222         BESServerUtils::show_usage( NameProgram ) ;
00223     }
00224     arguments = new char *[num_args+1] ;
00225 
00226     // Set arguments[0] to the name of the listener
00227     string::size_type len = server_name.length() ;
00228     char temp_name[len + 1] ;
00229     server_name.copy( temp_name, len ) ;
00230     temp_name[len] = '\0' ;
00231     arguments[0] = temp_name ;
00232 
00233     // Marshal the arguments to the listener from the command line 
00234     // arguments to the daemon
00235     for( int i = 1; i < num_args; i++ )
00236     {
00237         arguments[i] = argv[i] ;
00238     }
00239     arguments[num_args] = NULL ;
00240 
00241     if( !access( file_for_listener.c_str(), F_OK ) )
00242     {
00243         ifstream temp( file_for_listener.c_str() ) ;
00244         cout << NameProgram
00245              << ": there seems to be a BES daemon already running at " ;
00246         char buf[500] ;
00247         temp.getline( buf, 500 ) ;
00248         cout << buf << endl ;
00249         temp.close() ;
00250         return 1 ;
00251     }
00252 
00253     daemon_init() ;
00254 
00255     int restart = mount_server( arguments ) ;
00256     if( restart == 2 )
00257     {
00258         cout << NameProgram
00259              << ": server cannot mount at first try (core dump). "
00260              << "Please correct problems on the process manager "
00261              << server_name << endl ;
00262         return 0 ;
00263     }
00264     while( restart )
00265     {
00266         sleep( 5 ) ;
00267         restart = mount_server( arguments ) ;
00268     }
00269     delete [] arguments; arguments = 0 ;
00270 
00271     if( !access( file_for_listener.c_str(), F_OK ) )
00272     {
00273         (void)remove( file_for_listener.c_str() ) ;
00274     }
00275 
00276     return 0 ;
00277 }
00278 
00279 int
00280 daemon_init()
00281 {
00282     pid_t pid ;
00283     if( ( pid = fork() ) < 0 )
00284         return -1 ;
00285     else if( pid != 0 )
00286         exit( 0 ) ;
00287     setsid() ;
00288     return 0 ;
00289 }
00290 
00291 int
00292 mount_server(char **arguments)
00293 {
00294     const char *perror_string = 0 ;
00295     pid_t pid ;
00296     int status ;
00297     if( ( pid = fork() ) < 0 )
00298     {
00299         cerr << NameProgram << ": fork error " ;
00300         perror_string = strerror( errno ) ;
00301         if( perror_string )
00302             cerr << perror_string ;
00303         cerr << endl ;
00304         return 1 ;
00305     }
00306     else if( pid == 0 ) /* child process */
00307     {
00308         execvp( arguments[0], arguments ) ;
00309         cerr << NameProgram
00310              << ": mounting listener, subprocess failed: " ;
00311         perror_string = strerror( errno ) ;
00312         if( perror_string )
00313             cerr << perror_string ;
00314         cerr << endl ;
00315         exit( 1 ) ;
00316     }
00317     store_listener_id( pid ) ;
00318     if( ( pid = waitpid( pid, &status, 0 ) ) < 0 ) /* parent process */
00319     {
00320         cerr << NameProgram << ": waitpid error " ;
00321         perror_string = strerror( errno ) ;
00322         if( perror_string )
00323             cerr << perror_string ;
00324         cerr << endl ;
00325         return 1 ;
00326     }
00327     int child_status = pr_exit( status ) ;
00328     return child_status ;
00329 }
00330 
00331 int
00332 pr_exit(int status)
00333 {
00334     if( WIFEXITED( status ) )
00335     {
00336         int status_to_be_returned = SERVER_EXIT_UNDEFINED_STATE ;
00337         switch( WEXITSTATUS( status ) )
00338         {
00339             case SERVER_EXIT_NORMAL_SHUTDOWN:
00340                 status_to_be_returned = 0 ;
00341                 break ;
00342             case SERVER_EXIT_FATAL_CAN_NOT_START:
00343                 {
00344                     cerr << NameProgram
00345                          << ": server cannot start, exited with status "
00346                          << WEXITSTATUS( status ) << endl ;
00347                     cerr << "Please check all error messages "
00348                          << "and adjust server installation" << endl ;
00349                     status_to_be_returned = 0 ;
00350                 }
00351                 break;
00352             case SERVER_EXIT_ABNORMAL_TERMINATION:
00353                 {
00354                     cerr << NameProgram
00355                          << ": abnormal server termination, exited with status "
00356                          << WEXITSTATUS( status ) << endl ;
00357                     status_to_be_returned = 1 ;
00358                 }
00359                 break;
00360             case SERVER_EXIT_RESTART:
00361                 {
00362                     cout << NameProgram
00363                          << ": server has been requested to re-start." << endl ;
00364                     status_to_be_returned = 1 ;
00365                 }
00366                 break;
00367             default:
00368                 status_to_be_returned = 1 ;
00369                 break;
00370         }
00371 
00372         return status_to_be_returned;
00373     }
00374     else if( WIFSIGNALED( status ) )
00375     {
00376         cerr << NameProgram
00377              << ": abnormal server termination, signaled with signal number "
00378              << WTERMSIG( status ) << endl ;
00379 #ifdef WCOREDUMP
00380         if( WCOREDUMP( status ) ) 
00381         {
00382             cerr << NameProgram << ": server dumped core." << endl ;
00383             return 2 ;
00384         }
00385 #endif
00386         return 1;
00387     }
00388     else if( WIFSTOPPED( status ) )
00389     {
00390         cerr << NameProgram
00391              << ": abnormal server termination, stopped with signal number "
00392              << WSTOPSIG( status ) << endl ;
00393         return 1 ;
00394     }
00395     return 0 ;
00396 }
00397 
00398 void
00399 store_listener_id( int pid )
00400 {
00401     const char *perror_string = 0 ;
00402     ofstream f( file_for_listener.c_str() ) ;
00403     if( !f )
00404     {
00405         cerr << NameProgram << ": unable to create pid file "
00406              << file_for_listener << ": " ;
00407         perror_string = strerror( errno ) ;
00408         if( perror_string )
00409             cerr << perror_string ;
00410         cerr << " ... Continuing" << endl ;
00411         cerr << endl ;
00412     }
00413     else
00414     {
00415         f << "PID: " << pid << " UID: " << getuid() << endl ;
00416         f.close() ;
00417         mode_t new_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ;
00418         (void)chmod( file_for_listener.c_str(), new_mode ) ;
00419     }
00420 }
00421 
00422 bool
00423 load_names( const string &install_dir, const string &pid_dir )
00424 {
00425     char *xdap_root = 0 ;
00426     string bindir = "/bin";
00427     if( !pid_dir.empty() )
00428     {
00429         file_for_listener = pid_dir ;
00430     }
00431 
00432     if( !install_dir.empty() )
00433     {
00434         server_name = install_dir ;
00435         server_name += bindir ;
00436         if( file_for_listener.empty() )
00437         {
00438             file_for_listener = install_dir + "/var/run" ;
00439         }
00440     }
00441     else
00442     {
00443         string prog = NameProgram ;
00444         string::size_type slash = prog.find_last_of( '/' ) ;
00445         if( slash != string::npos )
00446         {
00447             server_name = prog.substr( 0, slash ) ;
00448             slash = prog.find_last_of( '/' ) ;
00449             if( slash != string::npos )
00450             {
00451                 string root = prog.substr( 0, slash ) ;
00452                 if( file_for_listener.empty() )
00453                 {
00454                     file_for_listener = root + "/var/run" ;
00455                 }
00456             }
00457             else
00458             {
00459                 if( file_for_listener.empty() )
00460                 {
00461                     file_for_listener = server_name ;
00462                 }
00463             }
00464         }
00465     }
00466 
00467     if( server_name == "" )
00468     {
00469         server_name = "." ;
00470         if( file_for_listener.empty() )
00471         {
00472             file_for_listener = "./run" ;
00473         }
00474     }
00475 
00476     server_name += BES_SERVER ;
00477     file_for_listener += BES_SERVER_PID ;
00478 
00479     if( access( server_name.c_str(), F_OK ) != 0 )
00480     {
00481         cerr << NameProgram
00482              << ": cannot start." << server_name << endl
00483              << "Please either pass -i <install_dir> on the command line or "
00484              << "set the environment variable " << BES_SERVER_ROOT << " "
00485              << "to the installation directory where the BES listener is."
00486              << endl ;
00487         return false ;
00488     }
00489     return true ;
00490 }
00491 

Generated on Sat Aug 22 06:06:22 2009 for OPeNDAP Hyrax Back End Server (BES) by  doxygen 1.6.0