pion
5.0.6
|
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