00001
00002
00003
00004
00005
00006
00007
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 {
00025
00026
00027
00028 boost::once_flag process::m_instance_flag = BOOST_ONCE_INIT;
00029 process::config_type *process::m_config_ptr = NULL;
00030
00031
00032
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
00088 if (!dir.empty()) {
00089 HMODULE hDll = NULL;
00090 TCHAR szDbgHelpPath[_MAX_PATH];
00091
00092
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
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
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
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
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
00147 boost::filesystem::path p(boost::filesystem::system_complete(cfg.dumpfile_dir));
00148
00149 p /= ss.str();
00150 p.normalize();
00151
00152 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
00153 return p.string();
00154 #else
00155 return p.file_string();
00156 #endif
00157
00158 }
00159
00160 LONG WINAPI process::unhandled_exception_filter(struct _EXCEPTION_POINTERS *pExceptionInfo)
00161 {
00162 config_type& cfg = get_config();
00163 pion::logger _logger = PION_GET_LOGGER("pion.process");
00164
00165
00166 if (cfg.dumpfile_dir.empty() || cfg.p_dump_proc == NULL) {
00167 PION_LOG_FATAL(_logger, "Unhandled exception caught when dump file handling not configured!");
00168 pion::logger::shutdown();
00169 return EXCEPTION_CONTINUE_SEARCH;
00170 }
00171
00172 std::string dumpfile_path = generate_dumpfile_name();
00173 LONG rc = EXCEPTION_CONTINUE_SEARCH;
00174
00175
00176 HANDLE hFile = ::CreateFile(dumpfile_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
00177 FILE_ATTRIBUTE_NORMAL, NULL);
00178
00179 if (hFile!=INVALID_HANDLE_VALUE) {
00180 _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
00181
00182 ExInfo.ThreadId = ::GetCurrentThreadId();
00183 ExInfo.ExceptionPointers = pExceptionInfo;
00184 ExInfo.ClientPointers = NULL;
00185
00186
00187 BOOL bOK = cfg.p_dump_proc(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL );
00188
00189 if (bOK) {
00190 PION_LOG_INFO(_logger, "Saved process dump file to " << dumpfile_path);
00191 } else {
00192 PION_LOG_ERROR(_logger, "Failed to save dump file to " << dumpfile_path <<
00193 " error code: " << GetLastError());
00194 }
00195
00196 ::CloseHandle(hFile);
00197 rc = EXCEPTION_EXECUTE_HANDLER;
00198 } else {
00199 PION_LOG_ERROR(_logger, "Failed to create dump file " << dumpfile_path <<
00200 " error code: " << GetLastError());
00201 }
00202
00203 PION_LOG_FATAL(_logger, "Unhandled exception caught. The process will be terminated!");
00204 pion::logger::shutdown();
00205 return rc;
00206 }
00207
00208
00209 void process::initialize(void)
00210 {
00211 SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
00212 }
00213
00214 void process::daemonize(void)
00215 {
00216
00217 }
00218
00219 #else // NOT #ifdef _MSC_VER
00220
00221 void handle_signal(int sig)
00222 {
00223 process::shutdown();
00224 }
00225
00226 void process::initialize(void)
00227 {
00228 signal(SIGPIPE, SIG_IGN);
00229 signal(SIGCHLD, SIG_IGN);
00230 signal(SIGTSTP, SIG_IGN);
00231 signal(SIGTTOU, SIG_IGN);
00232 signal(SIGTTIN, SIG_IGN);
00233 signal(SIGHUP, SIG_IGN);
00234 signal(SIGINT, handle_signal);
00235 signal(SIGTERM, handle_signal);
00236 }
00237
00238 void process::daemonize(void)
00239 {
00240
00241
00242
00243
00244 if(getppid()==1) return;
00245
00246
00247 int i = fork();
00248 if (i<0) exit(1);
00249 if (i>0) exit(0);
00250
00251
00252
00253
00254 setsid();
00255
00256
00257 for (i=getdtablesize();i>=0;--i) close(i);
00258
00259
00260 i=open("/dev/null",O_RDWR);
00261 if (i != -1) {
00262 if (dup(i) == -1) {}
00263 if (dup(i) == -1) {}
00264 }
00265
00266
00267 umask(027);
00268 }
00269
00270 #endif // #ifdef _MSC_VER
00271
00272 }