/home/vlg/develop/libASSA/libassa/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     m_log_size        (10485760), // 10 Mb 
00058     m_instance        (-1),
00059     m_with_log_server ("no"),
00060     m_log_server      ("assalogd@"),
00061     m_mask            (ALL), 
00062     m_graceful_quit   (false),
00063     m_version         ("unknown"),
00064     m_revision        (0),
00065     m_author          ("John Doe"),
00066     m_help_msg        ("No help available"),
00067     m_log_flag        (KEEPLOG),
00068     m_log_stdout      ("no"),
00069     m_daemon          ("no"),
00070     m_ommit_pidfile   ("no"),
00071     m_log_level       (-1),
00072     m_help_flag       (false),
00073     m_version_flag    (false),
00074     m_exit_value      (0)
00075 {
00076     add_flag_opt ('h', "help",       &m_help_flag);
00077     add_flag_opt ('v', "version",    &m_version_flag);
00078 
00079     add_opt ('d', "log-stdout",      &m_log_stdout);
00080     add_opt ('b', "daemon",          &m_daemon);
00081     add_opt ('L', "ommit-pidfile",   &m_ommit_pidfile);
00082     add_opt ('s', "with-log-server", &m_with_log_server);
00083     add_opt ('m', "mask",            &m_mask);
00084     add_opt ('D', "log-file",        &m_log_file);
00085     add_opt ('f', "config-file",     &m_config_file);
00086     add_opt ('n', "instance",        &m_instance);
00087     add_opt ('p', "port",            &m_port);
00088     add_opt ('z', "log-size",        &m_log_size);
00089     add_opt ('l', "pidfile",         &m_pidfile);
00090     add_opt ('S', "log-server",      &m_log_server);
00091     add_opt ('c', "log-level",       &m_log_level);
00092 
00095     char hn[64];
00096     ::gethostname (hn, sizeof (hn)-1);
00097     m_log_server += hn;
00098 }
00099 
00100 //------------------------------------------------------------------------------
00101 //    Get command line process name parse command line arguments
00102 //    request internals initialization.
00103 //------------------------------------------------------------------------------
00104 
00105 void 
00106 GenServer::
00107 init (int* argc, char* argv [], const char* ht_)
00108 {
00109     char* cp = argv [0];
00110     m_help_msg = ht_;
00111 
00116     if (strchr(cp, ASSA_DIR_SEPARATOR)) {
00117         cp += strlen(argv[0]); // position at the end
00118         while (*cp-- != ASSA_DIR_SEPARATOR) {
00119             ;
00120         }
00121         cp += 2;
00122     }
00123 
00124 #if defined (WIN32)             // get rid of '.exe'
00125     char* extidx = cp;
00126     while (*extidx) {
00127         if (*extidx == '.') {
00128             *extidx = '\0';
00129             break;
00130         }
00131         extidx++;
00132     }
00133 #endif
00134     m_cmdline_name = cp;        
00135     
00136     if (!parse_args ((const char **)argv)) {
00137         std::cerr << "Error in arguments: " << get_opt_error () << std::endl;
00138         std::cerr << "Try '" << argv[0] << " --help' for details.\n";
00139         exit (1);
00140     }
00141     if (m_help_flag) {
00142         display_help ();
00143         exit (0);
00144     }
00145     if (m_version_flag) {
00146         std::cerr << '\n' << argv[0] << " " << get_version () << '\n' << '\n'
00147              << "Written by " << m_author << "\n\n";
00148         exit (0);
00149     }
00150     if (m_daemon == "yes") {
00151         assert(become_daemon ());
00152     }
00155     char instbuf[16];       // INT_MAX   [-]2147483647
00156     sprintf(instbuf, "%d", m_instance);
00157 
00158     if (m_proc_name.length() == 0) {
00159         m_proc_name = m_cmdline_name;
00160 
00161         if (m_instance != -1) {
00162             m_proc_name += instbuf;
00163         }
00164     }
00165     if (m_port.length() == 0) {
00166         m_port = m_proc_name;
00167     }
00168 
00169 #if !defined(WIN32)
00170 
00173     SigAction ignore_act( SIG_IGN );
00174 
00182     ignore_act.register_action( SIGHUP );
00183     
00184     ignore_act.register_action( SIGPIPE );
00185     ignore_act.register_action( SIGCHLD );
00186 #if !(defined (__FreeBSD__) || defined(__FreeBSD_kernel__) \
00187     || defined (__NetBSD__))
00188     ignore_act.register_action( SIGCLD );
00189 #endif
00190     ignore_act.register_action( SIGALRM );
00191     
00196     m_sig_dispatcher.install ( ASSAIOSIG, &m_sig_poll );
00197 
00204     m_sig_dispatcher.install ( SIGINT, (EventHandler*) this );
00205     
00212     m_sig_dispatcher.install ( SIGTERM, (EventHandler*) this );
00213 
00214 #endif // !defined(WIN32)
00215 
00218     init_internals ();
00219 }
00220 
00221 void 
00222 GenServer::
00223 init_internals ()
00224 {
00225     static const char self[] = "GenServer::init_internals";
00226 
00231 #if defined (WIN32)
00232     m_default_config_file = this->get_cmdline_name () + ".ini";
00233 #else
00234     m_default_config_file = "$HOME/." + this->get_cmdline_name ();
00235     m_default_config_file = Utils::strenv (m_default_config_file.c_str ());
00236 #endif
00237 
00243     if (m_log_flag == RMLOG && m_log_stdout == "no") {
00244         struct stat fst;
00245         if (::stat (m_log_file.c_str(), &fst) == 0) {
00246             if (S_ISREG (fst.st_mode)) {
00247                 ::unlink (m_log_file.c_str());
00248             }
00249         }
00250     }
00251 
00259     Log::set_app_name (get_proc_name ());
00260 
00261     if (m_log_stdout == "yes") {
00262         Log::open_log_stdout (m_mask);
00263     }
00264     else {
00265         if (m_with_log_server == "yes") {
00266             Log::open_log_server (m_log_server, 
00267                                   m_log_file.c_str(), 
00268                                   get_reactor (), 
00269                                   m_mask, 
00270                                   m_log_size) ;
00271         }
00272         else {
00273             Log::open_log_file (m_log_file.c_str(), m_mask, m_log_size);
00274         }
00275     }
00276     
00277     trace(self);
00278 
00279     if (m_ommit_pidfile == "no") {
00280         if (m_pidfile.size () == 0) {
00281             m_pidfile = "~/." + m_proc_name + ".pid";
00282         }
00283         if (! m_pidfile_lock.lock (m_pidfile)) {
00284             DL((ASSAERR,"Failed to lock PID file: %s\n",
00285                 m_pidfile_lock.get_error_msg ()));
00286             exit (1);
00287         }
00288     }
00289 
00290     DL((APP,"\n"                                                        ));
00291     DL((APP,"========================================================\n"));
00292     DL((APP,"||         Server configuration settings              ||\n"));
00293     DL((APP,"========================================================\n"));
00294     DL((APP," cmd_line_name       = '%s'\n", m_cmdline_name.c_str()   ));
00295     DL((APP," name                = '%s'\n", m_proc_name.c_str()      ));
00296     DL((APP," default config file = '%s'\n", m_default_config_file.c_str()));
00297     DL((APP," config file         = '%s'\n", m_config_file.c_str()    ));
00298     DL((APP," mask                = 0x%X\n", m_mask                   ));
00299     dump ();
00300     DL((APP,"========================================================\n"));
00301     DL((APP,"\n"));
00302 }
00303 
00304 bool
00305 GenServer::
00306 become_daemon ()
00307 {
00308 #if defined(WIN32)
00309     return true;
00310 #else
00311     Fork f (Fork::LEAVE_ALONE, Fork::IGNORE_STATUS);
00312 
00313     if (!f.isChild ()) {    // parent exits
00314         exit (0);
00315     }
00316 
00317     int size = 1024;
00318     int i = 0;
00319     pid_t nullfd;
00320 
00321     for (i = 0; i < size; i++) {
00322         (void) close (i);
00323     }
00324         
00325     nullfd = open ("/dev/null", O_WRONLY | O_CREAT, 0666);
00326     if (nullfd == -1) {
00327         syslog (LOG_ERR,"failed to open \"/dev/null\"");
00328         return false;
00329     }
00330 
00331     (void) dup2 (nullfd, 1);
00332     (void) dup2 (nullfd, 2);
00333     (void) close (nullfd);
00334 
00335     if ( setsid() == -1 ) {
00336         syslog (LOG_ERR,"setsid() failed");
00337         return false;
00338     }
00339 
00340     /*---
00341       Changing to root directory would be the right thing to do for a 
00342       server (so that it wouldn't possibly depend on any mounted file 
00343       systems. But, in practice, it might cause a lot of problems.
00344       ---*/
00345 #if 0
00346     if ( chdir("/") == -1 ) {
00347         return false;
00348     }
00349 #endif
00350     return (true);
00351     
00352 #endif  // defined(WIN32)
00353 }
00354 
00355 int 
00356 GenServer::
00357 handle_signal (int signum_)
00358 {
00359     trace("GenServer::handle_signal");
00360     std::ostringstream m;
00361     
00362     switch (signum_) 
00363     {
00364         case SIGTERM: m << "SIGTERM signal caugth. "; break;
00365         case SIGINT:  m << "SIGINT signal caugth. "; break;
00366         default:      m << "Unexpected signal caugth.";
00367     }
00368     m << "Signal # " << signum_ << std::ends;
00369     DL((APP,"%s\n", m.str ().c_str () ));
00370     DL((APP,"Initiating shutdown sequence...\n"));
00371 
00372     fatal_signal_hook ();
00373 
00374     DL((APP, "Shutdown sequence completed - Exiting !\n"));
00375     
00376     /* Calling stop_service () triggers a call to Reactor::stopReactor()
00377        with subsequent call to Reactor::removeIOHandler() and then
00378        EventHandler::handle_close(). If EventHandler is in the middle
00379        of the *slow* system call such as read(2), handle_close() will 
00380        destry EventHandler, and after cotrol is returned from 
00381        GenServer::handle_signal(), *slow* system call is restarted 
00382        and proceeds to operate on the memory that has been deleted already.
00383 
00384        Calling Reactor::deactivate() instead delays memory release.
00385     */
00386     get_reactor()->deactivate ();
00387     m_graceful_quit = true;
00388 
00389     return 0;
00390 }       
00391 

Generated on Sun Aug 13 15:08:00 2006 for libassa by  doxygen 1.4.6