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_SHUTDOWN_LOGGER;
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_SHUTDOWN_LOGGER;
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 }