libassa
3.5.0
|
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