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

Generated on Wed Jan 2 06:00:40 2008 for OPeNDAP Back End Server (BES) by  doxygen 1.5.4