- Main Page
- Classes
- Files

- File List

# 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
    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     // make sure we have all the necessary setup
    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     // create the dump file and, if successful, write it
    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         // write the dump
    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; // dump saved, so we can die peacefully..
    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     // not supported
    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     // adopted from "Unix Daemon Server Programming"
    00241     // http://www.enderunix.org/docs/eng/daemon.php
    00242
    00243     // return early if already running as a daemon
    00244     if(getppid()==1) return;
    00245
    00246     // for out the process
    00247     int i = fork();
    00248     if (i<0) exit(1);   // error forking
    00249     if (i>0) exit(0);   // exit if parent
    00250
    00251     // child (daemon process) continues here after the fork...
    00252
    00253     // obtain a new process group
    00254     setsid();
    00255
    00256     // close all descriptors
    00257     for (i=getdtablesize();i>=0;--i) close(i);
    00258
    00259     // bind stdio to /dev/null (ignore errors)
    00260     i=open("/dev/null",O_RDWR);
    00261     if (i != -1) {
    00262         if (dup(i) == -1) {}
    00263         if (dup(i) == -1) {}
    00264     }
    00265
    00266     // restrict file creation mode to 0750
    00267     umask(027);
    00268 }
    00269
    00270 #endif  // #ifdef _MSC_VER
    00271
    00272 }   // end namespace pion