pion  5.0.6
process.cpp
1 // ---------------------------------------------------------------------
2 // pion: a Boost C++ framework for building lightweight HTTP interfaces
3 // ---------------------------------------------------------------------
4 // Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion)
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See http://www.boost.org/LICENSE_1_0.txt
8 //
9 
10 #include <signal.h>
11 #ifndef _MSC_VER
12  #include <fcntl.h>
13  #include <unistd.h>
14  #include <sys/stat.h>
15 #endif
16 
17 #include <boost/filesystem.hpp>
18 #include <boost/date_time.hpp>
19 
20 #include <pion/config.hpp>
21 #include <pion/process.hpp>
22 #include <pion/logger.hpp>
23 
24 namespace pion { // begin namespace pion
25 
26 // static members of process
27 
28 boost::once_flag process::m_instance_flag = BOOST_ONCE_INIT;
29 process::config_type *process::m_config_ptr = NULL;
30 
31 
32 // process member functions
33 
35 {
36  config_type& cfg = get_config();
37  boost::mutex::scoped_lock shutdown_lock(cfg.shutdown_mutex);
38  if (! cfg.shutdown_now) {
39  cfg.shutdown_now = true;
40  cfg.shutdown_cond.notify_all();
41  }
42 }
43 
45 {
46  config_type& cfg = get_config();
47  boost::mutex::scoped_lock shutdown_lock(cfg.shutdown_mutex);
48  while (! cfg.shutdown_now)
49  cfg.shutdown_cond.wait(shutdown_lock);
50 }
51 
52 void process::create_config(void)
53 {
54  static config_type UNIQUE_PION_PROCESS_CONFIG;
55  m_config_ptr = &UNIQUE_PION_PROCESS_CONFIG;
56 }
57 
58 
59 
60 #ifdef _MSC_VER
61 
62 BOOL WINAPI console_ctrl_handler(DWORD ctrl_type)
63 {
64  switch(ctrl_type) {
65  case CTRL_C_EVENT:
66  case CTRL_BREAK_EVENT:
67  case CTRL_CLOSE_EVENT:
68  case CTRL_SHUTDOWN_EVENT:
70  return TRUE;
71  default:
72  return FALSE;
73  }
74 }
75 
76 void process::set_dumpfile_directory(const std::string& dir)
77 {
78  config_type& cfg = get_config();
79  static const TCHAR* DBGHELP_DLL = _T("DBGHELP.DLL");
80 
81  if (!dir.empty() && !boost::filesystem::is_directory(dir)) {
82  throw dumpfile_init_exception("Dump file directory doesn't exist: " + dir);
83  }
84 
85  cfg.dumpfile_dir = dir;
86 
87  // load dbghelp.dll
88  if (!dir.empty()) {
89  HMODULE hDll = NULL;
90  TCHAR szDbgHelpPath[_MAX_PATH];
91 
92  // try loading side-by-side version of DbgHelp.dll first
93  if (GetModuleFileName(NULL, szDbgHelpPath, _MAX_PATH)) {
94  TCHAR *pSlash = _tcsrchr(szDbgHelpPath, _T('\\'));
95  if (pSlash) {
96  _tcscpy(pSlash+1, DBGHELP_DLL);
97  hDll = ::LoadLibrary( szDbgHelpPath );
98  }
99  }
100  // if not found, load the default version
101  if (hDll == NULL) {
102  hDll = ::LoadLibrary( DBGHELP_DLL );
103  }
104  cfg.h_dbghelp = hDll;
105 
106  if (hDll == NULL) {
107  throw dumpfile_init_exception("Failed to load DbgHelp.dll");
108  }
109  } else {
110  cfg.h_dbghelp = NULL;
111  }
112 
113  // get MiniDumpWriteDump proc address
114  if (cfg.h_dbghelp != NULL) {
115  cfg.p_dump_proc = (MINIDUMPWRITEDUMP)::GetProcAddress(cfg.h_dbghelp, "MiniDumpWriteDump");
116 
117  if (cfg.p_dump_proc == NULL) {
118  throw dumpfile_init_exception("Failed to get MiniDumpWriteDump proc address, probably dbghelp.dll version is too old");
119  }
120  } else {
121  cfg.p_dump_proc = NULL;
122  }
123 
124  pion::logger _logger = PION_GET_LOGGER("pion.process");
125  // (re)set the exception filter
126  if (cfg.p_dump_proc) {
127  ::SetUnhandledExceptionFilter(process::unhandled_exception_filter);
128  PION_LOG_INFO(_logger, "Dump file generation enabled to " << cfg.dumpfile_dir );
129  } else {
130  ::SetUnhandledExceptionFilter(NULL);
131  PION_LOG_INFO(_logger, "Unhandled exception handling reset to default");
132  }
133 }
134 
135 std::string process::generate_dumpfile_name()
136 {
137  config_type& cfg = get_config();
138 
139  // generate file name based on current timestamp
140  using namespace boost::posix_time;
141  static std::locale loc(std::cout.getloc(), new time_facet("%Y%m%d_%H%M%S"));
142  std::stringstream ss;
143  ss.imbue(loc);
144  ss << second_clock::universal_time() << ".dmp";
145 
146  // build the full path
147  boost::filesystem::path p(boost::filesystem::system_complete(cfg.dumpfile_dir));
148 
149  p /= ss.str();
150  p.normalize();
151  p.make_preferred();
152 
153 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
154  return p.string();
155 #else
156  return p.file_string();
157 #endif
158 
159 }
160 
161 LONG WINAPI process::unhandled_exception_filter(struct _EXCEPTION_POINTERS *pExceptionInfo)
162 {
163  config_type& cfg = get_config();
164  pion::logger _logger = PION_GET_LOGGER("pion.process");
165 
166  // make sure we have all the necessary setup
167  if (cfg.dumpfile_dir.empty() || cfg.p_dump_proc == NULL) {
168  PION_LOG_FATAL(_logger, "Unhandled exception caught when dump file handling not configured!");
169  PION_SHUTDOWN_LOGGER;
170  return EXCEPTION_CONTINUE_SEARCH;
171  }
172 
173  std::string dumpfile_path = generate_dumpfile_name();
174  LONG rc = EXCEPTION_CONTINUE_SEARCH;
175 
176  // create the dump file and, if successful, write it
177  HANDLE hFile = ::CreateFile(dumpfile_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
178  FILE_ATTRIBUTE_NORMAL, NULL);
179 
180  if (hFile!=INVALID_HANDLE_VALUE) {
181  _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
182 
183  ExInfo.ThreadId = ::GetCurrentThreadId();
184  ExInfo.ExceptionPointers = pExceptionInfo;
185  ExInfo.ClientPointers = NULL;
186 
187  // write the dump
188  BOOL bOK = cfg.p_dump_proc(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL );
189 
190  if (bOK) {
191  PION_LOG_INFO(_logger, "Saved process dump file to " << dumpfile_path);
192  } else {
193  PION_LOG_ERROR(_logger, "Failed to save dump file to " << dumpfile_path <<
194  " error code: " << GetLastError());
195  }
196 
197  ::CloseHandle(hFile);
198  rc = EXCEPTION_EXECUTE_HANDLER; // dump saved, so we can die peacefully..
199  } else {
200  PION_LOG_ERROR(_logger, "Failed to create dump file " << dumpfile_path <<
201  " error code: " << GetLastError());
202  }
203 
204  PION_LOG_FATAL(_logger, "Unhandled exception caught. The process will be terminated!");
205  PION_SHUTDOWN_LOGGER;
206  return rc;
207 }
208 
209 
210 void process::initialize(void)
211 {
212  SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
213 }
214 
215 void process::daemonize(void)
216 {
217  // not supported
218 }
219 
220 #else // NOT #ifdef _MSC_VER
221 
222 void handle_signal(int sig)
223 {
225 }
226 
228 {
229  signal(SIGPIPE, SIG_IGN);
230  signal(SIGCHLD, SIG_IGN);
231  signal(SIGTSTP, SIG_IGN);
232  signal(SIGTTOU, SIG_IGN);
233  signal(SIGTTIN, SIG_IGN);
234  signal(SIGHUP, SIG_IGN);
235  signal(SIGINT, handle_signal);
236  signal(SIGTERM, handle_signal);
237 }
238 
240 {
241  // adopted from "Unix Daemon Server Programming"
242  // http://www.enderunix.org/docs/eng/daemon.php
243 
244  // return early if already running as a daemon
245  if(getppid()==1) return;
246 
247  // for out the process
248  int i = fork();
249  if (i<0) exit(1); // error forking
250  if (i>0) exit(0); // exit if parent
251 
252  // child (daemon process) continues here after the fork...
253 
254  // obtain a new process group
255  setsid();
256 
257  // close all descriptors
258  for (i=getdtablesize();i>=0;--i) close(i);
259 
260  // bind stdio to /dev/null (ignore errors)
261  i=open("/dev/null",O_RDWR);
262  if (i != -1) {
263  if (dup(i) == -1) {}
264  if (dup(i) == -1) {}
265  }
266 
267  // restrict file creation mode to 0750
268  umask(027);
269 }
270 
271 #endif // #ifdef _MSC_VER
272 
273 } // end namespace pion
static void daemonize(void)
fork process and run as a background daemon
Definition: process.cpp:239
bool shutdown_now
true if we should shutdown now
Definition: process.hpp:99
data type for static/global process configuration information
Definition: process.hpp:90
static void wait_for_shutdown(void)
blocks until the shutdown condition has been signaled
Definition: process.cpp:44
boost::mutex shutdown_mutex
used to protect the shutdown condition
Definition: process.hpp:105
static void shutdown(void)
signals the shutdown condition
Definition: process.cpp:34
boost::condition shutdown_cond
triggered when it is time to shutdown
Definition: process.hpp:102
static void initialize(void)
sets up basic signal handling for the process
Definition: process.cpp:227
static config_type & get_config(void)
returns a singleton instance of config_type
Definition: process.hpp:121