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,2005 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                 cerr << "usage" << endl ;
00121                 BESServerUtils::show_usage( NameProgram ) ;
00122                 break ;
00123             case 'i': // BES install directory
00124                 install_dir = optarg ;
00125                 if( BESScrub::pathname_ok( install_dir, true ) == false )
00126                 {
00127                     cout << "The specified install directory (-i option) "
00128                          << "is incorrectly formatted. Must be less than "
00129                          << "255 characters and include the characters "
00130                          << "[0-9A-z_./-]" << endl ;
00131                     return 1 ;
00132                 }
00133                 num_args+=2 ;
00134                 break ;
00135             case 's': // secure server
00136                 num_args++ ;
00137                 break ;
00138             case 'r': // where to write the pid file
00139             {
00140                 pid_dir = optarg ;
00141                 if( BESScrub::pathname_ok( pid_dir, true ) == false )
00142                 {
00143                     cout << "The specified state directory (-r option) "
00144                          << "is incorrectly formatted. Must be less than "
00145                          << "255 characters and include the characters "
00146                          << "[0-9A-z_./-]" << endl ;
00147                     return 1 ;
00148                 }
00149                 num_args+=2 ;
00150             }
00151             break ;
00152             case 'c': // configuration file
00153             case 'u': // unix socket
00154             {
00155                 string check_path = optarg ;
00156                 if( BESScrub::pathname_ok( check_path, true ) == false )
00157                 {
00158                     cout << "The specified install directory (-i option) "
00159                          << "is incorrectly formatted. Must be less than "
00160                          << "255 characters and include the characters "
00161                          << "[0-9A-z_./-]" << endl ;
00162                     return 1 ;
00163                 }
00164                 num_args+=2 ;
00165             }
00166             break ;
00167             case 'p': // TCP port
00168             {
00169                 string port_num = optarg ;
00170                 for( unsigned int i = 0; i < port_num.length(); i++ )
00171                 {
00172                     if( !isdigit( port_num[i] ) )
00173                     {
00174                         cout << "The specified port contains non-digit "
00175                              << "characters: " << port_num << endl ;
00176                         return 1 ;
00177                     }
00178                 }
00179                 num_args+=2 ;
00180             }
00181             break ;
00182             case 'd': // debug
00183             {
00184                 string check_arg = optarg ;
00185                 if( BESScrub::command_line_arg_ok( check_arg ) == false )
00186                 {
00187                     cout << "The specified debug options \"" << check_arg
00188                          << "\" contains invalid characters" << endl ;
00189                     return 1 ;
00190                 }
00191                 num_args+=2 ;
00192             }
00193             break ;
00194             default:
00195                 BESServerUtils::show_usage( NameProgram ) ;
00196                 break ;
00197         }
00198     }
00199     // if the number of argumens is greater than the number of allowed arguments
00200     // then extra arguments were passed that aren't options. Show usage and
00201     // exit.
00202     if( argc > num_args )
00203     {
00204         cout << NameProgram
00205              << ": too many arguments passed to the BES" ;
00206         BESServerUtils::show_usage( NameProgram ) ;
00207     }
00208 
00209     if( pid_dir.empty() )
00210     {
00211         pid_dir = install_dir ;
00212     }
00213 
00214     // Set the name of the listener and the file for the listenet pid
00215     if( !load_names( install_dir, pid_dir ) )
00216         return 1 ;
00217 
00218     // make sure the array size is not too big
00219     if( BESScrub::size_ok( sizeof( char *), num_args+1 ) == false )
00220     {
00221         cout << NameProgram
00222              << ": too many arguments passed to the BES" ;
00223         BESServerUtils::show_usage( NameProgram ) ;
00224     }
00225     arguments = new char *[num_args+1] ;
00226 
00227     // Set arguments[0] to the name of the listener
00228     string::size_type len = server_name.length() ;
00229     char temp_name[len + 1] ;
00230     strncpy( temp_name, server_name.c_str(), len ) ;
00231     temp_name[len] = '\0' ;
00232     arguments[0] = temp_name ;
00233 
00234     // Marshal the arguments to the listener from the command line 
00235     // arguments to the daemon
00236     for( int i = 1; i < num_args; i++ )
00237     {
00238         arguments[i] = argv[i] ;
00239     }
00240     arguments[num_args] = NULL ;
00241 
00242     if( !access( file_for_listener.c_str(), F_OK ) )
00243     {
00244         ifstream temp( file_for_listener.c_str() ) ;
00245         cout << NameProgram
00246              << ": there seems to be a BES daemon already running at " ;
00247         char buf[500] ;
00248         temp.getline( buf, 500 ) ;
00249         cout << buf << endl ;
00250         temp.close() ;
00251         return 1 ;
00252     }
00253 
00254     daemon_init() ;
00255 
00256     int restart = mount_server( arguments ) ;
00257     if( restart == 2 )
00258     {
00259         cout << NameProgram
00260              << ": server can not mount at first try (core dump). "
00261              << "Please correct problems on the process manager "
00262              << server_name << endl ;
00263         return 0 ;
00264     }
00265     while( restart )
00266     {
00267         sleep( 5 ) ;
00268         restart = mount_server( arguments ) ;
00269     }
00270     delete [] arguments; arguments = 0 ;
00271 
00272     if( !access( file_for_listener.c_str(), F_OK ) )
00273     {
00274         remove( file_for_listener.c_str() ) ;
00275     }
00276 
00277     return 0 ;
00278 }
00279 
00280 int
00281 daemon_init()
00282 {
00283     pid_t pid ;
00284     if( ( pid = fork() ) < 0 )
00285         return -1 ;
00286     else if( pid != 0 )
00287         exit( 0 ) ;
00288     setsid() ;
00289     return 0 ;
00290 }
00291 
00292 int
00293 mount_server(char **arguments)
00294 {
00295     const char *perror_string = 0 ;
00296     pid_t pid ;
00297     int status ;
00298     if( ( pid = fork() ) < 0 )
00299     {
00300         cerr << NameProgram << ": fork error " ;
00301         perror_string = strerror( errno ) ;
00302         if( perror_string )
00303             cerr << perror_string ;
00304         cerr << endl ;
00305         return 1 ;
00306     }
00307     else if( pid == 0 ) /* child process */
00308     {
00309         execvp( arguments[0], arguments ) ;
00310         cerr << NameProgram
00311              << ": mounting listener, subprocess failed: " ;
00312         perror_string = strerror( errno ) ;
00313         if( perror_string )
00314             cerr << perror_string ;
00315         cerr << endl ;
00316         exit( 1 ) ;
00317     }
00318     store_listener_id( pid ) ;
00319     if( ( pid = waitpid( pid, &status, 0 ) ) < 0 ) /* parent process */
00320     {
00321         cerr << NameProgram << ": waitpid error " ;
00322         perror_string = strerror( errno ) ;
00323         if( perror_string )
00324             cerr << perror_string ;
00325         cerr << endl ;
00326         return 1 ;
00327     }
00328     int child_status = pr_exit( status ) ;
00329     return child_status ;
00330 }
00331 
00332 int
00333 pr_exit(int status)
00334 {
00335     if( WIFEXITED( status ) )
00336     {
00337         int status_to_be_returned = SERVER_EXIT_UNDEFINED_STATE ;
00338         switch( WEXITSTATUS( status ) )
00339         {
00340             case SERVER_EXIT_NORMAL_SHUTDOWN:
00341                 status_to_be_returned = 0 ;
00342                 break ;
00343             case SERVER_EXIT_FATAL_CAN_NOT_START:
00344                 {
00345                     cerr << NameProgram
00346                          << ": server can not start, exited with status "
00347                          << WEXITSTATUS( status ) << endl ;
00348                     cerr << "Please check all error messages "
00349                          << "and adjust server installation" << endl ;
00350                     status_to_be_returned = 0 ;
00351                 }
00352                 break;
00353             case SERVER_EXIT_ABNORMAL_TERMINATION:
00354                 {
00355                     cerr << NameProgram
00356                          << ": abnormal server termination, exited with status "
00357                          << WEXITSTATUS( status ) << endl ;
00358                     status_to_be_returned = 1 ;
00359                 }
00360                 break;
00361             case SERVER_EXIT_RESTART:
00362                 {
00363                     cout << NameProgram
00364                          << ": server has been requested to re-start." << endl ;
00365                     status_to_be_returned = 1 ;
00366                 }
00367                 break;
00368             default:
00369                 status_to_be_returned = 1 ;
00370                 break;
00371         }
00372 
00373         return status_to_be_returned;
00374     }
00375     else if( WIFSIGNALED( status ) )
00376     {
00377         cerr << NameProgram
00378              << ": abnormal server termination, signaled with signal number "
00379              << WTERMSIG( status ) << endl ;
00380 #ifdef WCOREDUMP
00381         if( WCOREDUMP( status ) ) 
00382         {
00383             cerr << NameProgram << ": server dumped core." << endl ;
00384             return 2 ;
00385         }
00386 #endif
00387         return 1;
00388     }
00389     else if( WIFSTOPPED( status ) )
00390     {
00391         cerr << NameProgram
00392              << ": abnormal server termination, stopped with signal number "
00393              << WSTOPSIG( status ) << endl ;
00394         return 1 ;
00395     }
00396     return 0 ;
00397 }
00398 
00399 void
00400 store_listener_id( int pid )
00401 {
00402     const char *perror_string = 0 ;
00403     ofstream f( file_for_listener.c_str() ) ;
00404     if( !f )
00405     {
00406         cerr << NameProgram << ": unable to create pid file "
00407              << file_for_listener << ": " ;
00408         perror_string = strerror( errno ) ;
00409         if( perror_string )
00410             cerr << perror_string ;
00411         cerr << " ... Continuing" << endl ;
00412         cerr << endl ;
00413     }
00414     else
00415     {
00416         f << "PID: " << pid << " UID: " << getuid() << endl ;
00417         f.close() ;
00418         mode_t new_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ;
00419         chmod( file_for_listener.c_str(), new_mode ) ;
00420     }
00421 }
00422 
00423 bool
00424 load_names( const string &install_dir, const string &pid_dir )
00425 {
00426     char *xdap_root = 0 ;
00427     string bindir = "/bin";
00428     if( !pid_dir.empty() )
00429     {
00430         file_for_listener = pid_dir ;
00431     }
00432 
00433     if( !install_dir.empty() )
00434     {
00435         server_name = install_dir ;
00436         server_name += bindir ;
00437         if( file_for_listener.empty() )
00438         {
00439             file_for_listener = install_dir + "/var/run" ;
00440         }
00441     }
00442     else
00443     {
00444         string prog = NameProgram ;
00445         string::size_type slash = prog.find_last_of( '/' ) ;
00446         if( slash != string::npos )
00447         {
00448             server_name = prog.substr( 0, slash ) ;
00449             slash = prog.find_last_of( '/' ) ;
00450             if( slash != string::npos )
00451             {
00452                 string root = prog.substr( 0, slash ) ;
00453                 if( file_for_listener.empty() )
00454                 {
00455                     file_for_listener = root + "/var/run" ;
00456                 }
00457             }
00458             else
00459             {
00460                 if( file_for_listener.empty() )
00461                 {
00462                     file_for_listener = server_name ;
00463                 }
00464             }
00465         }
00466     }
00467 
00468     if( server_name == "" )
00469     {
00470         server_name = "." ;
00471         if( file_for_listener.empty() )
00472         {
00473             file_for_listener = "./run" ;
00474         }
00475     }
00476 
00477     server_name += BES_SERVER ;
00478     file_for_listener += BES_SERVER_PID ;
00479 
00480     if( access( server_name.c_str(), F_OK ) != 0 )
00481     {
00482         cerr << NameProgram
00483              << ": can not start." << server_name << endl
00484              << "Please either pass -i <install_dir> on the command line or "
00485              << "set the environment variable " << BES_SERVER_ROOT << " "
00486              << "to the installation directory where the BES listener is."
00487              << endl ;
00488         return false ;
00489     }
00490     return true ;
00491 }
00492 

Generated on Tue Mar 4 23:13:37 2008 for OPeNDAP Back End Server (BES) by  doxygen 1.5.1