platform/server/main.cpp

00001 // ------------------------------------------------------------------
00002 // pion-platform: a C++ framework for building lightweight HTTP interfaces
00003 // ------------------------------------------------------------------
00004 // Copyright (C) 2007 Atomic Labs, Inc.  (http://www.atomiclabs.com)
00005 //
00006 // Distributed under the Boost Software License, Version 1.0.
00007 // See accompanying file COPYING or copy at http://www.boost.org/LICENSE_1_0.txt
00008 //
00009 
00010 #ifndef _MSC_VER
00011     #include <fcntl.h>
00012     #include <unistd.h>
00013     #include <sys/stat.h>
00014 #endif
00015 
00016 #ifdef _MSC_VER
00017     #include <windows.h>
00018     #include <direct.h>
00019 #endif
00020 
00021 // mlockall support on LINUX
00022 #ifndef _MSC_VER
00023     #include <sys/mman.h>
00024     #include <string.h>
00025 #endif
00026 
00027 #include <pion/PionConfig.hpp>
00028 
00029 #ifdef PION_HAVE_SSL
00030     #include <openssl/ssl.h>
00031 //#ifdef _MSC_VER
00032 //  #include <openssl/applink.c>
00033 //#endif
00034 #endif
00035 
00036 #ifndef SYSCONFDIR
00037     #define SYSCONFDIR "/usr/local/etc"
00038 #endif
00039 
00040 #include <iostream>
00041 #include <boost/filesystem/operations.hpp>
00042 #include "PlatformConfig.hpp"
00043 #include "../../net/utils/ShutdownManager.hpp"
00044 
00045 using namespace std;
00046 using namespace pion;
00047 using namespace pion::net;
00048 using namespace pion::platform;
00049 using namespace pion::server;
00050 
00051 // some forward declarations of functions used
00052 void daemonize_server(void);
00053 void argument_error(void);
00054 int run(bool run_as_daemon, bool lock_memory, const std::string& platform_config_file);
00055 
00056 int parse_args(int argc, char *argv[], bool& run_as_daemon, bool& lock_memory, std::string& platform_config_file)
00057 {
00058     // get platform config file
00059     run_as_daemon = false;
00060     lock_memory = false;
00061 
00062 #ifdef _MSC_VER
00063     platform_config_file = "config\\platform.xml";
00064 #else
00065     platform_config_file = "/etc/pion/platform.xml";
00066     if (! boost::filesystem::exists(platform_config_file) )
00067         platform_config_file = std::string(SYSCONFDIR) + "/pion/platform.xml";
00068 #endif
00069 
00070     for (int argnum=1; argnum < argc; ++argnum) {
00071         if (argv[argnum][0] == '-') {
00072             if (argv[argnum][1] == 'D') {
00073                 run_as_daemon = true;
00074             } else if (argv[argnum][1] == 'M') {
00075                 lock_memory = true;
00076             } else if (argv[argnum][1] == 'c' && argv[argnum][2] == '\0' && argnum+1 < argc) {
00077                 platform_config_file = boost::filesystem::system_complete(argv[++argnum]).normalize().file_string();
00078             } else if (strncmp(argv[argnum], "--version", 9) == 0) {
00079                 std::cout << "pion version " << PION_VERSION << std::endl;
00080                 return 1;
00081             } else {
00082                 argument_error();
00083                 return 1;
00084             }
00085         } else {
00086             argument_error();
00087             return 1;
00088         }
00089     }
00090 
00091     return 0;
00092 }
00093 
00095 #ifdef _MSC_VER
00096 #define SVCNAME TEXT("Pion")
00097 
00099 bool                    g_lock_memory = false;
00100 std::string             g_platform_config_file;
00101 
00103 SERVICE_STATUS          gSvcStatus; 
00104 SERVICE_STATUS_HANDLE   gSvcStatusHandle;
00105 
00107 void report_service_status( DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint );
00108 
00110 void WINAPI service_control_handler( DWORD dwCtrl )
00111 {
00112     // Handle the requested control code. 
00113     switch(dwCtrl) {  
00114     case SERVICE_CONTROL_STOP: 
00115     case SERVICE_CONTROL_SHUTDOWN:
00116         report_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
00117         // Signal the Pion to shutdown.
00118         main_shutdown_manager.shutdown();
00119         return;
00120 
00121     case SERVICE_CONTROL_INTERROGATE: 
00122         // Fall through to send current status.
00123         break; 
00124 
00125     default: 
00126         break;
00127     } 
00128 
00129    report_service_status(gSvcStatus.dwCurrentState, NO_ERROR, 0);
00130 }
00131 
00132 void report_service_status( DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint )
00133 {
00134     static DWORD dwCheckPoint = 1;
00135 
00136     // Fill in the SERVICE_STATUS structure.
00137     gSvcStatus.dwCurrentState = dwCurrentState;
00138     gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
00139     gSvcStatus.dwWaitHint = dwWaitHint;
00140 
00141     if (dwCurrentState == SERVICE_START_PENDING)
00142         gSvcStatus.dwControlsAccepted = 0;
00143     else 
00144         gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
00145 
00146     if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED) )
00147         gSvcStatus.dwCheckPoint = 0;
00148     else 
00149         gSvcStatus.dwCheckPoint = dwCheckPoint++;
00150 
00151     // Report the status of the service to the SCM.
00152     SetServiceStatus( gSvcStatusHandle, &gSvcStatus );
00153 }
00154 
00155 void service_report_event(LPTSTR szFunction) 
00156 {
00157     //TODO
00158 }
00159 
00160 void WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )
00161 {
00162     // Register the handler function for the service
00163     gSvcStatusHandle = RegisterServiceCtrlHandler( SVCNAME, service_control_handler );
00164 
00165     if( !gSvcStatusHandle ){ 
00166         PION_ASSERT( gSvcStatusHandle );
00167         service_report_event(TEXT("RegisterServiceCtrlHandler")); 
00168         return; 
00169     } 
00170 
00171     // These SERVICE_STATUS members remain as set here
00172     gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 
00173     gSvcStatus.dwServiceSpecificExitCode = 0;    
00174 
00175     // Report initial status to the SCM
00176     report_service_status( SERVICE_START_PENDING, NO_ERROR, 3000 );
00177 
00178     bool run_as_daemon = false;
00179     bool lock_memory = false;
00180     
00181 #ifdef _MSC_VER
00182     std::string platform_config_file("config\\platform.xml");
00183 #else
00184     std::string platform_config_file("/etc/pion/platform.xml");
00185 #endif
00186 
00187     run( true, g_lock_memory, g_platform_config_file );
00188 
00189     report_service_status( SERVICE_STOPPED, NO_ERROR, 0 );
00190 }
00191 #endif
00192 
00194 int main (int argc, char *argv[])
00195 {
00196     bool run_as_daemon = false;
00197     bool lock_memory = false;
00198 
00199 #ifdef _MSC_VER
00200     std::string platform_config_file("config\\platform.xml");
00201 #else
00202     std::string platform_config_file("/etc/pion/platform.xml");
00203 #endif
00204 
00205     if(parse_args(argc, argv, run_as_daemon, lock_memory, platform_config_file) != 0)
00206         return 1;
00207 
00208 #ifdef _MSC_VER
00209     if(run_as_daemon) {
00210         // running as Windows Service
00211         // initialize the global variables to be used by SrvMain
00212         g_platform_config_file = platform_config_file;
00213         g_lock_memory = lock_memory;
00214 
00215         SERVICE_TABLE_ENTRY DispatchTable[] = 
00216         { 
00217             { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, 
00218             { NULL, NULL } 
00219         }; 
00220         // this call returns when the service has stopped. 
00221         // the process should simply terminate when the call returns.
00222         if (!StartServiceCtrlDispatcher( DispatchTable )) { 
00223             service_report_event(("StartServiceCtrlDispatcher")); 
00224         }
00225         return 0;
00226     }
00227 #endif
00228     return run(run_as_daemon, lock_memory, platform_config_file);
00229 }
00230 
00231 int run (bool run_as_daemon, bool lock_memory, const std::string& platform_config_file)
00232 {
00233     // initialize log system (use simple configuration)
00234     PionLogger pion_log(PION_GET_LOGGER("pion"));
00235 
00236     // This level might be overridden if there is a LogConfig file specified in the platform 
00237     // configuration (and if using a logging library that supports logging configuration.)
00238     PION_LOG_SETLEVEL_INFO(pion_log);
00239     PION_LOG_CONFIG_BASIC;
00240 
00241     // get logger for main() process
00242     PionLogger pion_main_log(PION_GET_LOGGER("pion.main"));
00243 
00245     if (run_as_daemon)
00246         daemonize_server();
00247     
00248     // setup signal handler
00249 #ifdef _MSC_VER
00250     SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
00251 #else
00252     signal(SIGPIPE, SIG_IGN);
00253     signal(SIGCHLD, SIG_IGN);
00254     signal(SIGTSTP, SIG_IGN);
00255     signal(SIGTTOU, SIG_IGN);
00256     signal(SIGTTIN, SIG_IGN);
00257     signal(SIGHUP, SIG_IGN);
00258     signal(SIGINT, handle_signal);
00259     signal(SIGTERM, handle_signal);
00260 #endif
00261         
00262 #ifdef PION_HAVE_SSL
00263     // initialize the OpenSSL library
00264     CRYPTO_malloc_init();
00265     SSL_library_init();
00266 #endif
00267 
00268 #ifndef _MSC_VER
00269     if (lock_memory)
00270         if (mlockall(MCL_CURRENT | MCL_FUTURE))
00271             PION_LOG_FATAL(pion_main_log, "Failed to lock memory: " << strerror(errno));
00272 #endif
00273 
00274     // PlatformConfig destructor can throw exceptions, make sure we handle them
00275     try {
00276         PlatformConfig platform_cfg;
00277         try {
00278             // load the platform configuration
00279             platform_cfg.setConfigFile(platform_config_file);
00280             platform_cfg.openConfigFile();
00281             
00282             PION_LOG_INFO(pion_main_log, "Pion has started successfully (v" << PION_VERSION << ')');
00283 #ifdef _MSC_VER
00284             if(run_as_daemon)
00285                 report_service_status( SERVICE_RUNNING, NO_ERROR, 0 );
00286 #endif
00287             // wait for shutdown
00288             main_shutdown_manager.wait();
00289         } catch (std::exception& e) {
00290             PION_LOG_FATAL(pion_main_log, e.what());
00291         }
00292 
00293         PION_LOG_INFO(pion_main_log, "Pion is shutting down");
00294     } catch (std::exception& e) {
00295         PION_LOG_FATAL(pion_main_log, e.what());
00296     }
00297     return 0;
00298 }
00299 
00300 #ifdef _MSC_VER
00302 void daemonize_server(void)
00303 {
00304     gSvcStatusHandle = RegisterServiceCtrlHandler( SVCNAME, service_control_handler );
00305     if( !gSvcStatusHandle )
00306     { 
00307         service_report_event(TEXT("RegisterServiceCtrlHandler")); 
00308         return; 
00309     }
00310 
00311     // These SERVICE_STATUS members remain as set here
00312     gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; 
00313     gSvcStatus.dwServiceSpecificExitCode = 0;    
00314 
00315     // Report initial status to the SCM
00316     report_service_status( SERVICE_START_PENDING, NO_ERROR, 20000 );
00317 
00318     // Windows tends to start the service in C:\windows\system32 folder
00319     // We need to change it to Pion's installation dir for Pion to work
00320     char exe_path[MAX_PATH];
00321     exe_path[0]=0;
00322     if( GetModuleFileName(NULL, exe_path, sizeof(exe_path)/sizeof(exe_path[0])) ) {
00323         char exe_dir[MAX_PATH], drive[MAX_PATH], filename[MAX_PATH];
00324         exe_dir[0]=0; drive[0] = 0; filename[0]=0;
00325         _splitpath( exe_path, drive, exe_dir, filename, NULL);
00326         SetCurrentDirectory( exe_dir );
00327     }
00328 }
00329 #else
00331 void daemonize_server(void)
00332 {
00333     // adopted from "Unix Daemon Server Programming"
00334     // http://www.enderunix.org/docs/eng/daemon.php
00335     
00336     // return early if already running as a daemon
00337     if(getppid()==1) return;
00338     
00339     // for out the process 
00340     int i = fork();
00341     if (i<0) exit(1);   // error forking
00342     if (i>0) exit(0);   // exit if parent
00343     
00344     // child (daemon process) continues here after the fork...
00345     
00346     // obtain a new process group
00347     setsid();
00348     
00349     // close all descriptors
00350     for (i=getdtablesize();i>=0;--i) close(i);
00351     
00352     // bind stdio to /dev/null
00353     i=open("/dev/null",O_RDWR); dup(i); dup(i);
00354     
00355     // restrict file creation mode to 0750
00356     umask(027);
00357 }
00358 #endif
00359 
00360 
00362 void argument_error(void)
00363 {
00364     std::cerr << "usage:   pion [-c PLATFORM_XML_FILE] [-D] [-M]" << std::endl;
00365 }

Generated on Wed Apr 13 16:38:34 2011 for pion-platform by  doxygen 1.4.7