/builddir/build/BUILD/libassa-3.5.0/assa/GenServer.cpp

Go to the documentation of this file.
00001 // -*- c++ -*-
00002 //---------------------------------------------------------------------------
00003 //                            GenServer.cpp
00004 //---------------------------------------------------------------------------
00005 //  Copyright (c) 1997-2004,2005 by Vladislav Grinchenko
00006 //
00007 //  This library is free software; you can redistribute it and/or
00008 //  modify it under the terms of the GNU Library General Public
00009 //  License as published by the Free Software Foundation; either
00010 //  version 2 of the License, or (at your option) any later version.
00011 //---------------------------------------------------------------------------
00012 
00013 /*
00014   [a   e g ijk   o qr tu wxy ]
00015   [ABC EFGHIJK MNOPQR TUVWXYZ]
00016 
00017 " Standard command-line arguments:                                           \n"
00018 "                                                                            \n"
00019 "  -b, --daemon BOOL        - Run process as true UNIX daemon                \n"
00020 "  -l, --pidfile PATH       - The process ID is written to the lockfile PATH \n"
00021 "                             instead of default ~/.{procname}.pid           \n"
00022 "  -L, --ommit-pidfile BOOL - Do not create PID lockfile                     \n"
00023 "  -d, --log-stdout BOOL    - Write debug to standard output                 \n"
00024 "  -D, --log-file NAME      - Write debug to NAME file                       \n"
00025 "  -z, --log-size NUM       - Maximum size debug file can reach              \n"
00026 "                             (default is 10Mb)                              \n"
00027 "  -c, --log-level NUM      - Log verbosity                                  \n"
00028 "  -s, --with-log-server BOOL - Redirect log messages to the log server      \n"
00029 "  -S, --log-server NAME    - Define assa-logd server address                \n"
00030 "                             (default: assalogd@localhost)                  \n"
00031 "  -m, --mask MASK          - Mask (default: ALL = 0x7fffffff)               \n"
00032 "  -p, --port NAME          - The TCP/IP port NAME (default - procname)      \n"
00033 "  -n, --instance NUM       - Process instance NUM (default - none)          \n"
00034 "  -f, --config-file NAME   - Alternative config file NAME                   \n"
00035 "  -h, --help               - Print this message                             \n"
00036 "  -v, --version            - Print version number                           \n"
00037 "                                                                            \n"
00038 " NOTE: BOOL value is either 'yes' or 'no'                                   \n"
00039 */
00040 //------------------------------------------------------------------------------
00041 
00042 #include <sys/types.h>          // stat(2)
00043 #include <sys/stat.h>           // stat(2)
00044 #include <unistd.h>             // stat(2)
00045 
00046 #ifdef __CYGWIN32__             // to resolve h_errno dependency
00047 #  include <errno.h>
00048 #  include <netdb.h>            
00049 #endif
00050 
00051 #include "assa/GenServer.h"
00052 #include "assa/CommonUtils.h"
00053 
00054 using namespace ASSA;
00055 
00056 GenServer::GenServer () 
00057     :
00058     m_log_size        (10485760), // 10 Mb 
00059     m_instance        (-1),
00060     m_with_log_server ("no"),
00061     m_log_server      ("assalogd@"),
00062     m_mask            (ALL), 
00063     m_graceful_quit   (false),
00064     m_version         ("unknown"),
00065     m_revision        (0),
00066     m_author          ("John Doe"),
00067     m_help_msg        ("No help available"),
00068     m_log_flag        (KEEPLOG),
00069     m_log_stdout      ("no"),
00070     m_daemon          ("no"),
00071     m_ommit_pidfile   ("no"),
00072     m_log_level       (-1),
00073     m_help_flag       (false),
00074     m_version_flag    (false),
00075     m_exit_value      (0)
00076 {
00077     add_flag_opt ('h', "help",       &m_help_flag);
00078     add_flag_opt ('v', "version",    &m_version_flag);
00079 
00080     add_opt ('d', "log-stdout",      &m_log_stdout);
00081     add_opt ('b', "daemon",          &m_daemon);
00082     add_opt ('L', "ommit-pidfile",   &m_ommit_pidfile);
00083     add_opt ('s', "with-log-server", &m_with_log_server);
00084     add_opt ('m', "mask",            &m_mask);
00085     add_opt ('D', "log-file",        &m_log_file);
00086     add_opt ('f', "config-file",     &m_config_file);
00087     add_opt ('n', "instance",        &m_instance);
00088     add_opt ('p', "port",            &m_port);
00089     add_opt ('z', "log-size",        &m_log_size);
00090     add_opt ('l', "pidfile",         &m_pidfile);
00091     add_opt ('S', "log-server",      &m_log_server);
00092     add_opt ('c', "log-level",       &m_log_level);
00093 
00096     char hn[64];
00097     ::gethostname (hn, sizeof (hn)-1);
00098     m_log_server += hn;
00099 }
00100 
00111 GenServer::
00112 ~GenServer ()
00113 {
00114     Log::log_close ();
00115 }
00116 
00117 //------------------------------------------------------------------------------
00118 //    Get command line process name parse command line arguments
00119 //    request internals initialization.
00120 //------------------------------------------------------------------------------
00121 
00122 void 
00123 GenServer::
00124 init (int* argc, char* argv [], const char* ht_)
00125 {
00126     char* cp = argv [0];
00127     m_help_msg = ht_;
00128 
00133     if (strchr(cp, ASSA_DIR_SEPARATOR)) {
00134         cp += strlen(argv[0]); // position at the end
00135         while (*cp-- != ASSA_DIR_SEPARATOR) {
00136             ;
00137         }
00138         cp += 2;
00139     }
00140 
00141 #if defined (WIN32)             // get rid of '.exe'
00142     char* extidx = cp;
00143     while (*extidx) {
00144         if (*extidx == '.') {
00145             *extidx = '\0';
00146             break;
00147         }
00148         extidx++;
00149     }
00150 #endif
00151     m_cmdline_name = cp;        
00152     
00153     if (!parse_args ((const char **)argv)) {
00154         std::cerr << "Error in arguments: " << get_opt_error () << std::endl;
00155         std::cerr << "Try '" << argv[0] << " --help' for details.\n";
00156         exit (1);
00157     }
00158 
00159     if (m_help_flag) {
00160         display_help ();
00161         exit (0);
00162     }
00163 
00164     if (m_version_flag) {
00165         std::cerr << '\n' << argv[0] << " " << get_version () << '\n' << '\n'
00166              << "Written by " << m_author << "\n\n";
00167         exit (0);
00168     }
00169 
00173     std::string s;
00174 
00175     if (m_default_config_file.size ()) {
00176         s = ASSA::Utils::strenv (m_default_config_file.c_str ());
00177         m_default_config_file = s;
00178     }
00179 
00180     if (m_config_file.size ()) {
00181         s = ASSA::Utils::strenv (m_config_file.c_str ());
00182         m_config_file = s;
00183     }
00184 
00185     if (m_log_file.size ()) {
00186         s = ASSA::Utils::strenv (m_log_file.c_str ());
00187         m_log_file = s;
00188     }
00189 
00190     if (m_pidfile.size ()) {
00191         s = ASSA::Utils::strenv (m_pidfile.c_str ());
00192         m_pidfile = s;
00193     }
00194 
00197     if (m_daemon == "yes") {
00198         assert(become_daemon ());
00199     }
00200 
00203     char instbuf[16];       // INT_MAX   [-]2147483647
00204     sprintf(instbuf, "%d", m_instance);
00205 
00206     if (m_proc_name.length() == 0) {
00207         m_proc_name = m_cmdline_name;
00208 
00209         if (m_instance != -1) {
00210             m_proc_name += instbuf;
00211         }
00212     }
00213     if (m_port.length() == 0) {
00214         m_port = m_proc_name;
00215     }
00216 
00217 #if !defined(WIN32)
00218 
00221     SigAction ignore_act( SIG_IGN );
00222 
00230     ignore_act.register_action( SIGHUP );
00231     
00232     ignore_act.register_action( SIGPIPE );
00233     ignore_act.register_action( SIGCHLD );
00234 #if !(defined (__FreeBSD__) || defined(__FreeBSD_kernel__) \
00235       || defined (__NetBSD__))
00236     ignore_act.register_action( SIGCLD );
00237 #endif
00238     ignore_act.register_action( SIGALRM );
00239     
00244     m_sig_dispatcher.install ( ASSAIOSIG, &m_sig_poll );
00245 
00252     m_sig_dispatcher.install ( SIGINT, (EventHandler*) this );
00253     
00260     m_sig_dispatcher.install ( SIGTERM, (EventHandler*) this );
00261 
00262 #endif // !defined(WIN32)
00263 
00266     init_internals ();
00267 }
00268 
00269 void 
00270 GenServer::
00271 init_internals ()
00272 {
00273     static const char self[] = "GenServer::init_internals";
00274 
00279 #if defined (WIN32)
00280     m_default_config_file = this->get_cmdline_name () + ".ini";
00281 #else
00282     m_default_config_file = "$HOME/." + this->get_cmdline_name ();
00283     m_default_config_file = Utils::strenv (m_default_config_file.c_str ());
00284 #endif
00285 
00291     if (m_log_flag == RMLOG && m_log_stdout == "no") 
00292     {
00293         struct stat fst;
00294         if (::stat (m_log_file.c_str(), &fst) == 0) 
00295         {
00296             if (S_ISREG (fst.st_mode)) {
00297                 ::unlink (m_log_file.c_str());
00298             }
00299         }
00300     }
00301 
00309     Log::set_app_name (get_proc_name ());
00310 
00311     if (m_log_stdout == "yes") {
00312         Log::open_log_stdout (m_mask);
00313     }
00314     else {
00315         if (m_with_log_server == "yes") {
00316             Log::open_log_server (m_log_server, 
00317                                   m_log_file.c_str(), 
00318                                   get_reactor (), 
00319                                   m_mask, 
00320                                   m_log_size) ;
00321         }
00322         else {
00323             Log::open_log_file (m_log_file.c_str(), m_mask, m_log_size);
00324         }
00325     }
00326     
00327     trace(self);
00328 
00329     if (m_ommit_pidfile == "no") 
00330     {
00331         if (m_pidfile.size () == 0) {
00332             string s ("~/." + m_proc_name + ".pid");
00333             m_pidfile = ASSA::Utils::strenv (s.c_str ());
00334         }
00335         if (! m_pidfile_lock.lock (m_pidfile)) {
00336             DL((ASSAERR,"Failed to lock PID file: %s\n",
00337                 m_pidfile_lock.get_error_msg ()));
00338             exit (1);
00339         }
00340     }
00341 
00342     DL((APP,"\n"                                                        ));
00343     DL((APP,"========================================================\n"));
00344     DL((APP,"||         Server configuration settings              ||\n"));
00345     DL((APP,"========================================================\n"));
00346     DL((APP," cmd_line_name       = '%s'\n", m_cmdline_name.c_str()   ));
00347     DL((APP," name                = '%s'\n", m_proc_name.c_str()      ));
00348     DL((APP," default config file = '%s'\n", m_default_config_file.c_str()));
00349     DL((APP," config file         = '%s'\n", m_config_file.c_str()    ));
00350     DL((APP," mask                = 0x%X\n", m_mask                   ));
00351     dump ();
00352     DL((APP,"========================================================\n"));
00353     DL((APP,"\n"));
00354 }
00355 
00356 bool
00357 GenServer::
00358 become_daemon ()
00359 {
00360 #if defined(WIN32)
00361     return true;
00362 #else
00363     Fork f (Fork::LEAVE_ALONE, Fork::IGNORE_STATUS);
00364 
00365     if (!f.isChild ()) {    // parent exits
00366         exit (0);
00367     }
00368 
00369     int size = 1024;
00370     int i = 0;
00371     pid_t nullfd;
00372 
00373     for (i = 0; i < size; i++) {
00374         (void) close (i);
00375     }
00376         
00377     nullfd = open ("/dev/null", O_WRONLY | O_CREAT, 0666);
00378     if (nullfd == -1) {
00379         syslog (LOG_ERR,"failed to open \"/dev/null\"");
00380         return false;
00381     }
00382 
00383     (void) dup2 (nullfd, 1);
00384     (void) dup2 (nullfd, 2);
00385     (void) close (nullfd);
00386 
00387     if ( setsid() == -1 ) {
00388         syslog (LOG_ERR,"setsid() failed");
00389         return false;
00390     }
00391 
00392     /*---
00393       Changing to root directory would be the right thing to do for a 
00394       server (so that it wouldn't possibly depend on any mounted file 
00395       systems. But, in practice, it might cause a lot of problems.
00396       ---*/
00397 #if 0
00398     if ( chdir("/") == -1 ) {
00399         return false;
00400     }
00401 #endif
00402     return (true);
00403     
00404 #endif  // defined(WIN32)
00405 }
00406 
00407 int 
00408 GenServer::
00409 handle_signal (int signum_)
00410 {
00411     trace("GenServer::handle_signal");
00412     std::ostringstream m;
00413     
00414     switch (signum_) 
00415     {
00416         case SIGTERM: m << "SIGTERM signal caugth. "; break;
00417         case SIGINT:  m << "SIGINT signal caugth. "; break;
00418         default:      m << "Unexpected signal caugth.";
00419     }
00420     m << "Signal # " << signum_ << std::ends;
00421     DL((APP,"%s\n", m.str ().c_str () ));
00422     DL((APP,"Initiating shutdown sequence...\n"));
00423 
00424     fatal_signal_hook ();
00425 
00426     DL((APP, "Shutdown sequence completed - Exiting !\n"));
00427     
00428     /* Calling stop_service () triggers a call to Reactor::stopReactor()
00429        with subsequent call to Reactor::removeIOHandler() and then
00430        EventHandler::handle_close(). If EventHandler is in the middle
00431        of the *slow* system call such as read(2), handle_close() will 
00432        destry EventHandler, and after cotrol is returned from 
00433        GenServer::handle_signal(), *slow* system call is restarted 
00434        and proceeds to operate on the memory that has been deleted already.
00435 
00436        Calling Reactor::deactivate() instead delays memory release.
00437     */
00438     get_reactor()->deactivate ();
00439     m_graceful_quit = true;
00440 
00441     return 0;
00442 }       
00443 

Generated on Sun Feb 10 14:51:54 2008 for libassa by  doxygen 1.5.4