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 
152 # if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3
153  return p.string();
154 #else
155  return p.file_string();
156 #endif
157 
158 }
159 
160 LONG WINAPI process::unhandled_exception_filter(struct _EXCEPTION_POINTERS *pExceptionInfo)
161 {
162  config_type& cfg = get_config();
163  pion::logger _logger = PION_GET_LOGGER("pion.process");
164 
165  // make sure we have all the necessary setup
166  if (cfg.dumpfile_dir.empty() || cfg.p_dump_proc == NULL) {
167  PION_LOG_FATAL(_logger, "Unhandled exception caught when dump file handling not configured!");
168  PION_SHUTDOWN_LOGGER;
169  return EXCEPTION_CONTINUE_SEARCH;
170  }
171 
172  std::string dumpfile_path = generate_dumpfile_name();
173  LONG rc = EXCEPTION_CONTINUE_SEARCH;
174 
175  // create the dump file and, if successful, write it
176  HANDLE hFile = ::CreateFile(dumpfile_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
177  FILE_ATTRIBUTE_NORMAL, NULL);
178 
179  if (hFile!=INVALID_HANDLE_VALUE) {
180  _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
181 
182  ExInfo.ThreadId = ::GetCurrentThreadId();
183  ExInfo.ExceptionPointers = pExceptionInfo;
184  ExInfo.ClientPointers = NULL;
185 
186  // write the dump
187  BOOL bOK = cfg.p_dump_proc(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL );
188 
189  if (bOK) {
190  PION_LOG_INFO(_logger, "Saved process dump file to " << dumpfile_path);
191  } else {
192  PION_LOG_ERROR(_logger, "Failed to save dump file to " << dumpfile_path <<
193  " error code: " << GetLastError());
194  }
195 
196  ::CloseHandle(hFile);
197  rc = EXCEPTION_EXECUTE_HANDLER; // dump saved, so we can die peacefully..
198  } else {
199  PION_LOG_ERROR(_logger, "Failed to create dump file " << dumpfile_path <<
200  " error code: " << GetLastError());
201  }
202 
203  PION_LOG_FATAL(_logger, "Unhandled exception caught. The process will be terminated!");
204  PION_SHUTDOWN_LOGGER;
205  return rc;
206 }
207 
208 
209 void process::initialize(void)
210 {
211  SetConsoleCtrlHandler(console_ctrl_handler, TRUE);
212 }
213 
214 void process::daemonize(void)
215 {
216  // not supported
217 }
218 
219 #else // NOT #ifdef _MSC_VER
220 
221 void handle_signal(int sig)
222 {
224 }
225 
227 {
228  signal(SIGPIPE, SIG_IGN);
229  signal(SIGCHLD, SIG_IGN);
230  signal(SIGTSTP, SIG_IGN);
231  signal(SIGTTOU, SIG_IGN);
232  signal(SIGTTIN, SIG_IGN);
233  signal(SIGHUP, SIG_IGN);
234  signal(SIGINT, handle_signal);
235  signal(SIGTERM, handle_signal);
236 }
237 
239 {
240  // adopted from "Unix Daemon Server Programming"
241  // http://www.enderunix.org/docs/eng/daemon.php
242 
243  // return early if already running as a daemon
244  if(getppid()==1) return;
245 
246  // for out the process
247  int i = fork();
248  if (i<0) exit(1); // error forking
249  if (i>0) exit(0); // exit if parent
250 
251  // child (daemon process) continues here after the fork...
252 
253  // obtain a new process group
254  setsid();
255 
256  // close all descriptors
257  for (i=getdtablesize();i>=0;--i) close(i);
258 
259  // bind stdio to /dev/null (ignore errors)
260  i=open("/dev/null",O_RDWR);
261  if (i != -1) {
262  if (dup(i) == -1) {}
263  if (dup(i) == -1) {}
264  }
265 
266  // restrict file creation mode to 0750
267  umask(027);
268 }
269 
270 #endif // #ifdef _MSC_VER
271 
272 } // end namespace pion
static void daemonize(void)
fork process and run as a background daemon
Definition: process.cpp:238
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:226
static config_type & get_config(void)
returns a singleton instance of config_type
Definition: process.hpp:121