pion  5.0.6
src/process.cpp
00001 // ---------------------------------------------------------------------
00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
00003 // ---------------------------------------------------------------------
00004 // Copyright (C) 2007-2014 Splunk Inc.  (https://github.com/splunk/pion)
00005 //
00006 // Distributed under the Boost Software License, Version 1.0.
00007 // See http://www.boost.org/LICENSE_1_0.txt
00008 //
00009 
00010 #include <signal.h>
00011 #ifndef _MSC_VER
00012     #include <fcntl.h>
00013     #include <unistd.h>
00014     #include <sys/stat.h>
00015 #endif
00016 
00017 #include <boost/filesystem.hpp>
00018 #include <boost/date_time.hpp>
00019 
00020 #include <pion/config.hpp>
00021 #include <pion/process.hpp>
00022 #include <pion/logger.hpp>
00023 
00024 namespace pion {    // begin namespace pion
00025     
00026 // static members of process
00027     
00028 boost::once_flag                process::m_instance_flag = BOOST_ONCE_INIT;
00029 process::config_type *process::m_config_ptr = NULL;
00030 
00031     
00032 // process member functions
00033     
00034 void process::shutdown(void)
00035 {
00036     config_type& cfg = get_config();
00037     boost::mutex::scoped_lock shutdown_lock(cfg.shutdown_mutex);
00038     if (! cfg.shutdown_now) {
00039         cfg.shutdown_now = true;
00040         cfg.shutdown_cond.notify_all();
00041     }
00042 }
00043 
00044 void process::wait_for_shutdown(void)
00045 {
00046     config_type& cfg = get_config();
00047     boost::mutex::scoped_lock shutdown_lock(cfg.shutdown_mutex);
00048     while (! cfg.shutdown_now)
00049         cfg.shutdown_cond.wait(shutdown_lock);
00050 }
00051 
00052 void process::create_config(void)
00053 {
00054     static config_type UNIQUE_PION_PROCESS_CONFIG;
00055     m_config_ptr = &UNIQUE_PION_PROCESS_CONFIG;
00056 }
00057 
00058 
00059 
00060 #ifdef _MSC_VER
00061 
00062 BOOL WINAPI console_ctrl_handler(DWORD ctrl_type)
00063 {
00064     switch(ctrl_type) {
00065         case CTRL_C_EVENT:
00066         case CTRL_BREAK_EVENT:
00067         case CTRL_CLOSE_EVENT:
00068         case CTRL_SHUTDOWN_EVENT:
00069             process::shutdown();
00070             return TRUE;
00071         default:
00072             return FALSE;
00073     }
00074 }
00075 
00076 void process::set_dumpfile_directory(const std::string& dir)
00077 {
00078     config_type& cfg = get_config();
00079     static const TCHAR* DBGHELP_DLL = _T("DBGHELP.DLL");
00080 
00081     if (!dir.empty() && !boost::filesystem::is_directory(dir)) {
00082         throw dumpfile_init_exception("Dump file directory doesn't exist: " + dir);
00083     }
00084 
00085     cfg.dumpfile_dir = dir;
00086 
00087     // load dbghelp.dll
00088     if (!dir.empty()) {
00089         HMODULE hDll = NULL;
00090         TCHAR szDbgHelpPath[_MAX_PATH];
00091 
00092         // try loading side-by-side version of DbgHelp.dll first
00093         if (GetModuleFileName(NULL, szDbgHelpPath, _MAX_PATH)) {
00094             TCHAR *pSlash = _tcsrchr(szDbgHelpPath, _T('\\'));
00095             if (pSlash) {
00096                 _tcscpy(pSlash+1, DBGHELP_DLL);
00097                 hDll = ::LoadLibrary( szDbgHelpPath );
00098             }
00099         }
00100         // if not found, load the default version
00101         if (hDll == NULL) {
00102             hDll = ::LoadLibrary( DBGHELP_DLL );
00103         }
00104         cfg.h_dbghelp = hDll;
00105 
00106         if (hDll == NULL) {
00107             throw dumpfile_init_exception("Failed to load DbgHelp.dll");
00108         }
00109     } else {
00110         cfg.h_dbghelp = NULL;
00111     }
00112 
00113     // get MiniDumpWriteDump proc address
00114     if (cfg.h_dbghelp != NULL) {
00115         cfg.p_dump_proc = (MINIDUMPWRITEDUMP)::GetProcAddress(cfg.h_dbghelp, "MiniDumpWriteDump");
00116 
00117         if (cfg.p_dump_proc == NULL) {
00118             throw dumpfile_init_exception("Failed to get MiniDumpWriteDump proc address, probably dbghelp.dll version is too old");
00119         }
00120     } else {
00121         cfg.p_dump_proc = NULL;
00122     }
00123 
00124     pion::logger _logger = PION_GET_LOGGER("pion.process");
00125     // (re)set the exception filter
00126     if (cfg.p_dump_proc) {
00127         ::SetUnhandledExceptionFilter(process::unhandled_exception_filter);
00128          PION_LOG_INFO(_logger, "Dump file generation enabled to " << cfg.dumpfile_dir );
00129     } else {
00130         ::SetUnhandledExceptionFilter(NULL);
00131         PION_LOG_INFO(_logger, "Unhandled exception handling reset to default");
00132     }
00133 }
00134 
00135 std::string process::generate_dumpfile_name()
00136 {
00137     config_type& cfg = get_config();
00138 
00139     // generate file name based on current timestamp
00140     using namespace boost::posix_time;
00141     static std::locale loc(std::cout.getloc(), new time_facet("%Y%m%d_%H%M%S"));
00142     std::stringstream ss;
00143     ss.imbue(loc);
00144     ss << second_clock::universal_time() << ".dmp";
00145 
00146     // build the full path
00147     boost::filesystem::path p(boost::filesystem::system_complete(cfg.dumpfile_dir));
00148 
00149     p /= ss.str();
00150     p.normalize();
00151     p.make_preferred();
00152 
00153 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
00154     return p.string();
00155 #else
00156     return p.file_string();
00157 #endif 
00158 
00159 }
00160 
00161 LONG WINAPI process::unhandled_exception_filter(struct _EXCEPTION_POINTERS *pExceptionInfo)
00162 {
00163     config_type& cfg = get_config();
00164     pion::logger _logger = PION_GET_LOGGER("pion.process");
00165     
00166     // make sure we have all the necessary setup
00167     if (cfg.dumpfile_dir.empty() || cfg.p_dump_proc == NULL) {
00168         PION_LOG_FATAL(_logger, "Unhandled exception caught when dump file handling not configured!");
00169         PION_SHUTDOWN_LOGGER;
00170         return EXCEPTION_CONTINUE_SEARCH;
00171     }
00172 
00173     std::string dumpfile_path = generate_dumpfile_name();
00174     LONG rc = EXCEPTION_CONTINUE_SEARCH;
00175 
00176     // create the dump file and, if successful, write it
00177     HANDLE hFile = ::CreateFile(dumpfile_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
00178         FILE_ATTRIBUTE_NORMAL, NULL);
00179 
00180     if (hFile!=INVALID_HANDLE_VALUE) {
00181         _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
00182 
00183         ExInfo.ThreadId = ::GetCurrentThreadId();
00184         ExInfo.ExceptionPointers = pExceptionInfo;
00185         ExInfo.ClientPointers = NULL;
00186 
00187         // write the dump
00188         BOOL bOK = cfg.p_dump_proc(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL );
00189 
00190         if (bOK) {
00191             PION_LOG_INFO(_logger, "Saved process dump file to " << dumpfile_path);
00192         } else {
00193             PION_LOG_ERROR(_logger, "Failed to save dump file to " << dumpfile_path << 
00194                 " error code: " << GetLastError());
00195         }
00196 
00197         ::CloseHandle(hFile);
00198         rc = EXCEPTION_EXECUTE_HANDLER; // dump saved, so we can die peacefully..
00199     } else {
00200         PION_LOG_ERROR(_logger, "Failed to create dump file " << dumpfile_path << 
00201             " error code: " << GetLastError());
00202     }
00203 
00204     PION_LOG_FATAL(_logger, "Unhandled exception caught. The process will be terminated!");
00205     PION_SHUTDOWN_LOGGER;
00206     return rc;
00207 }
00208 
00209 
00210 void process::initialize(void)
00211 {
00212     SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
00213 }
00214 
00215 void process::daemonize(void)
00216 {
00217     // not supported
00218 }
00219 
00220 #else   // NOT #ifdef _MSC_VER
00221 
00222 void handle_signal(int sig)
00223 {
00224     process::shutdown();
00225 }
00226 
00227 void process::initialize(void)
00228 {
00229     signal(SIGPIPE, SIG_IGN);
00230     signal(SIGCHLD, SIG_IGN);
00231     signal(SIGTSTP, SIG_IGN);
00232     signal(SIGTTOU, SIG_IGN);
00233     signal(SIGTTIN, SIG_IGN);
00234     signal(SIGHUP, SIG_IGN);
00235     signal(SIGINT, handle_signal);
00236     signal(SIGTERM, handle_signal);
00237 }
00238 
00239 void process::daemonize(void)
00240 {
00241     // adopted from "Unix Daemon Server Programming"
00242     // http://www.enderunix.org/docs/eng/daemon.php
00243     
00244     // return early if already running as a daemon
00245     if(getppid()==1) return;
00246     
00247     // for out the process 
00248     int i = fork();
00249     if (i<0) exit(1);   // error forking
00250     if (i>0) exit(0);   // exit if parent
00251     
00252     // child (daemon process) continues here after the fork...
00253     
00254     // obtain a new process group
00255     setsid();
00256     
00257     // close all descriptors
00258     for (i=getdtablesize();i>=0;--i) close(i);
00259     
00260     // bind stdio to /dev/null (ignore errors)
00261     i=open("/dev/null",O_RDWR);
00262     if (i != -1) {
00263         if (dup(i) == -1) {}
00264         if (dup(i) == -1) {}
00265     }
00266     
00267     // restrict file creation mode to 0750
00268     umask(027);
00269 }
00270 
00271 #endif  // #ifdef _MSC_VER
00272 
00273 }   // end namespace pion