Evo C++ Library v0.5.1
process.h
Go to the documentation of this file.
1 // Evo C++ Library
2 /* Copyright 2019 Justin Crowell
3 Distributed under the BSD 2-Clause License -- see included file LICENSE.txt for details.
4 */
6 
7 #pragma once
8 #ifndef INCL_evo_process_h
9 #define INCL_evo_process_h
10 
11 #include "impl/sys.h"
12 #include "maplist.h"
13 #if defined(_WIN32)
14  #include "thread.h"
15 #else
16  #include <sys/types.h>
17  #include <sys/stat.h>
18  #include <fcntl.h>
19  #include <signal.h>
20  #include <syslog.h>
21 #endif
22 
23 namespace evo {
26 
28 
29 #if defined(_WIN32)
30  typedef DWORD ProcessId;
31 #else
32  typedef pid_t ProcessId;
33 #endif
34 
40 inline ProcessId get_pid() {
41  #if defined(_WIN32)
42  return ::GetCurrentProcessId();
43  #else
44  return ::getpid();
45  #endif
46 }
47 
48 #if !defined(_WIN32)
49 static const uint DAEMONIZE_NO_CD = 0x01;
50 static const uint DAEMONIZE_NO_REDIR = 0x02;
51 static const uint DAEMONIZE_NO_EXIT = 0x04;
52 static const uint DAEMONIZE_USE_STDERR = 0x08;
53 
68 inline bool daemonize(uint flags=0) {
69  #define EVO_TEMP_LOG_ERR(MSG, ...) { \
70  if (use_stderr) \
71  fprintf(stderr, "daemonize: " MSG "\n", __VA_ARGS__); \
72  else \
73  syslog(LOG_ERR, "fork() failed: %s", strerror(errno)); \
74  }
75 
76  bool use_stderr = (flags & DAEMONIZE_USE_STDERR);
77  if (!use_stderr)
78  openlog("daemonize", LOG_PID, LOG_DAEMON);
79 
80  bool result = false;
81  for (;;) {
82  // Fork to detach from parent
83  pid_t pid = fork();
84  if (pid == -1) {
85  EVO_TEMP_LOG_ERR("fork() failed: %s", strerror(errno));
86  break;
87  }
88  if (pid > 0)
89  _exit(0); // parent
90 
91  // New session
92  setsid();
93 
94  // Fork again to completely detatch from terminal
95  pid = fork();
96  if (pid == -1) {
97  EVO_TEMP_LOG_ERR("fork() failed: %s", strerror(errno));
98  break;
99  }
100  if (pid > 0)
101  ::_exit(0); // parent
102 
103  // Reset umask, cd root
104  umask(0);
105  if ((flags & DAEMONIZE_NO_CD) == 0 && chdir("/") != 0)
106  EVO_TEMP_LOG_ERR("chdir() on / failed: %s", strerror(errno));
107 
108  // Redirect std in/out/err to /dev/null
109  if (flags & DAEMONIZE_NO_REDIR) {
110  close(STDIN_FILENO);
111  close(STDOUT_FILENO);
112  if (!use_stderr)
113  close(STDERR_FILENO);
114  if (open("/dev/null", O_RDWR) != 0) {
115  EVO_TEMP_LOG_ERR("open() on /dev/null failed: %s", strerror(errno));
116  exit(1);
117  }
118  if (dup(STDIN_FILENO) == -1)
119  EVO_TEMP_LOG_ERR("dup() failed for stdout: %s", strerror(errno));
120  if (!use_stderr && dup(STDIN_FILENO) == -1)
121  EVO_TEMP_LOG_ERR("dup() failed for stderr: %s", strerror(errno));
122  }
123 
124  // Success
125  result = true;
126  break;
127  }
128 
129  if (!use_stderr)
130  closelog();
131  if (!result && (flags & DAEMONIZE_NO_EXIT) == 0)
132  exit(1);
133  return result;
134 
135  #undef EVO_TEMP_LOG_ERR
136 }
137 #endif
138 
140 
189 class Signal {
190 public:
191 #if defined(_WIN32)
192  typedef DWORD SigNumType;
193 #else
194  typedef int SigNumType;
195 #endif
196 
198  enum Action {
201  };
202 
204  enum Type {
205  tUNKNOWN = 0,
206  // Portable (Linux/Unix/Windows)
209  // Linux/Unix only
215  // Windows only
220  };
221 
222  typedef void (*Handler)(SigNumType, Type);
223  typedef void (*OnShutdown)(SigNumType, Type);
224 
229  struct Main {
230  bool error;
231 
233  Main() : error(false) {
234  }
235 
242  Main(Signal::OnShutdown on_shutdown, bool except=EVO_EXCEPTIONS) : error(false) {
243  if (!Signal::set_on_shutdown(on_shutdown)) {
244  if (except) {
245  EVO_THROW(Exception, "Signal::set_on_shutdown() failed");
246  } else
247  fprintf(stderr, "Signal::set_on_shutdown() failed\n");
248  error = true;
249  }
250  }
251 
255  ~Main() {
258  }
259  };
260 
268  template<class T>
269  struct MainServer : Main {
276  MainServer(T& server, bool except=EVO_EXCEPTIONS) : Main(on_shutdown, except) {
277  global_server() = &server;
278  }
279 
282  global_server()->shutdown();
283  }
284 
285  private:
286  static T*& global_server() {
287  static T* server = NULL;
288  return server;
289  }
290  };
291 
298  static bool set_on_shutdown(OnShutdown on_shutdown) {
299  State& state = get_state();
300  #if defined(_WIN32)
301  Condition::Lock lock(state.condmutex); // locked until return
302  if (on_shutdown != NULL && !state.registered) {
303  if (::SetConsoleCtrlHandler(main_handler, TRUE) != TRUE)
304  return false;
305  state.registered = true;
306  }
307  #else
308  set_handler(tINTERRUPT, NULL);
309  set_handler(tTERMINATE, NULL);
310  #endif
311  state.on_shutdown = on_shutdown;
312  return true;
313  }
314 
321  static void shutdown_sync() {
322  #if defined(_WIN32)
323  State& state = get_state();
324  Condition::Lock lock(state.condmutex);
325  state.shutdown_flag = true;
326  state.condmutex.notify_all();
327  #endif
328  }
329 
335  static bool set_handler(Type type, Action action) {
336  #if defined(_WIN32)
337  if (action == aDEFAULT) {
338  // Set to default by removing previously added handler
339  State& state = get_state();
340  Condition::Lock lock(state.condmutex);
341  state.handlers.remove(get_signum(type));
342  if (state.registered && state.on_shutdown == NULL && state.handlers.empty()) {
343  ::SetConsoleCtrlHandler(main_handler, FALSE);
344  state.registered = false;
345  }
346  return true;
347  } else {
348  // Windows can only ignore tINTERRUPT
349  if (type != tINTERRUPT)
350  return false;
351  return (::SetConsoleCtrlHandler(NULL, TRUE) == TRUE);
352  }
353  #else
354  const int signum = get_signum(type);
355  if (signum == INVALID_SIGNUM)
356  return false;
357  get_state().handlers.remove(signum);
358 
359  struct sigaction action_data;
360  action_data.sa_handler = (action == aDEFAULT ? SIG_DFL : SIG_IGN);
361  action_data.sa_flags = 0;
362  sigemptyset(&action_data.sa_mask);
363 
364  return (::sigaction(signum, &action_data, NULL) == 0);
365  #endif
366  }
367 
373  static bool set_handler(Type type, Handler handler) {
374  State& state = get_state();
375  #if defined(_WIN32)
376  const DWORD signum = get_signum(type);
377  if (signum == INVALID_SIGNUM)
378  return false;
379  if (handler != NULL) {
380  Condition::Lock lock(state.condmutex);
381  state.handlers[signum] = handler;
382  if (state.registered)
383  return true;
384  }
385  if (::SetConsoleCtrlHandler(main_handler, TRUE) == TRUE) {
386  Condition::Lock lock(state.condmutex);
387  state.registered = true;
388  }
389  return false;
390  #else
391  const int signum = get_signum(type);
392  if (signum == INVALID_SIGNUM)
393  return false;
394  if (handler != NULL)
395  state.handlers[signum] = handler;
396 
397  struct sigaction action_data;
398  action_data.sa_handler = main_handler;
399  action_data.sa_flags = 0;
400  sigfillset(&action_data.sa_mask); // block all signals while handling signal
401 
402  return (::sigaction(signum, &action_data, NULL) == 0);
403  #endif
404  }
405 
410  static bool send_signal(ProcessId pid, Type signal) {
411  const SigNumType signum = get_signum(signal);
412  if (signal == tUNKNOWN)
413  return false;
414 
415  #if defined(_WIN32)
416  return (::GenerateConsoleCtrlEvent(signum, pid) == TRUE);
417  #else
418  return (::kill(pid, signum) == 0);
419  #endif
420  }
421 
422 private:
423  // Internal state
424  struct State {
425  #if defined(_WIN32)
426  Condition condmutex;
427  bool registered;
428  #endif
429 
430  bool shutdown_flag;
431  OnShutdown on_shutdown;
433 
434  State() {
435  #if defined(_WIN32)
436  registered = false;
437  #endif
438  shutdown_flag = false;
439  on_shutdown = NULL;
440  }
441  };
442 
443  static State& get_state() {
444  static State state;
445  return state;
446  }
447 
448 #if defined(_WIN32)
449  static const DWORD INVALID_SIGNUM = 0xFFFFFFFF;
450 
451  static DWORD get_signum(Type type) {
452  switch (type) {
453  case tINTERRUPT: return CTRL_C_EVENT;
454  case tWIN_BREAK: return CTRL_BREAK_EVENT;
455  case tWIN_CLOSE: return CTRL_CLOSE_EVENT;
456  case tWIN_LOGOFF: return CTRL_LOGOFF_EVENT;
457  case tWIN_SHUTDOWN: return CTRL_SHUTDOWN_EVENT;
458  default: break;
459  }
460  return INVALID_SIGNUM;
461  }
462 
463  static Type get_sigtype(SigNumType value) {
464  switch (value) {
465  case CTRL_C_EVENT: return tINTERRUPT;
466  case CTRL_BREAK_EVENT: return tWIN_BREAK;
467  case CTRL_CLOSE_EVENT: return tWIN_CLOSE;
468  case CTRL_LOGOFF_EVENT: return tWIN_LOGOFF;
469  case CTRL_SHUTDOWN_EVENT: return tWIN_SHUTDOWN;
470  default: break;
471  }
472  return tUNKNOWN;
473  }
474 
475  static BOOL WINAPI main_handler(DWORD value) {
476  const Type type = get_sigtype(value);
477  if (type != tUNKNOWN) {
478  State& state = get_state();
479  const Handler* h;
480  {
481  Condition::Lock lock(state.condmutex);
482  h = state.handlers.find(value);
483  }
484  if (h != NULL)
485  (*h)(value, type);
486 
487  Condition::Lock lock(state.condmutex);
488  if (state.registered && state.on_shutdown != NULL) {
489  lock.unlock();
490  (*state.on_shutdown)(value, type);
491  lock.lock();
492  while (!state.shutdown_flag)
493  state.condmutex.wait(Condition::INF, true);
494  }
495  }
496  return FALSE;
497  }
498 
499 #else
500  static const int INVALID_SIGNUM = -1;
501 
502  static int get_signum(Type type) {
503  switch (type) {
504  case tINTERRUPT: return SIGINT;
505  case tTERMINATE: return SIGTERM;
506  case tPIPE: return SIGPIPE;
507  case tCHILD: return SIGCHLD;
508  case tHUP: return SIGHUP;
509  case tUSER1: return SIGUSR1;
510  case tUSER2: return SIGUSR2;
511  default: break;
512  }
513  return INVALID_SIGNUM;
514  }
515 
516  static Type get_sigtype(bool& shutdown, SigNumType value) {
517  shutdown = false;
518  switch (value) {
519  case SIGINT: shutdown = true; return tINTERRUPT;
520  case SIGTERM: shutdown = true; return tTERMINATE;
521  case SIGPIPE: return tPIPE;
522  case SIGCHLD: return tCHILD;
523  case SIGHUP: return tHUP;
524  case SIGUSR1: return tUSER1;
525  case SIGUSR2: return tUSER2;
526  default: break;
527  }
528  return tUNKNOWN;
529  }
530 
531  static void main_handler(int value) {
532  bool shutdown;
533  const Type type = get_sigtype(shutdown, value);
534 
535  State& state = get_state();
536  const Handler* h = state.handlers.find(value);
537  if (h != NULL)
538  (*h)(value, type);
539 
540  if (shutdown && state.on_shutdown != NULL)
541  (*state.on_shutdown)(value, type);
542  }
543 #endif
544 };
545 
547 
548 }
549 #endif
#define EVO_TEMP_LOG_ERR(MSG,...)
static void shutdown_sync()
Sync shutdown with signal handler.
Definition: process.h:321
void(* OnShutdown)(SigNumType, Type)
General shutdown handler type.
Definition: process.h:223
User logging off, though which user is undefined (CTRL_LOGOFF_EVENT) [Windows, services only]...
Definition: process.h:218
Interrupted by Control-C (SIGINT) [Portable: Linux/Unix/Windows].
Definition: process.h:207
static bool set_handler(Type type, Handler handler)
Set signal handler.
Definition: process.h:373
static const uint DAEMONIZE_NO_EXIT
Flag to return false on error instead of calling exit(1) – see daemonize()
Definition: process.h:51
bool error
Error flag, set to false on success.
Definition: process.h:230
Write on broken pipe (SIGPIPE) [Linux/Unix].
Definition: process.h:210
static bool set_on_shutdown(OnShutdown on_shutdown)
Set shutdown handler.
Definition: process.h:298
Evo implementation detail for system portability – this is included by most Evo headers, include this via: include <evo/type.h>.
Main(Signal::OnShutdown on_shutdown, bool except=1)
Constructor that sets up shutdown handler.
Definition: process.h:242
Action
Signal handling action.
Definition: process.h:198
Process signal handling.
Definition: process.h:189
static const uint DAEMONIZE_USE_STDERR
Flag to use stderr instead of syslog for writing errors – see daemonize()
Definition: process.h:52
bool daemonize(uint flags=0)
Daemonize current process to run in background as a service (Linux/Unix).
Definition: process.h:68
Condition object for thread synchronization.
Definition: thread.h:243
Evo MapList container.
User defined signal 2 (SIGUSR2) [Linux/Unix].
Definition: process.h:214
Terminal hangup, or daemon reload (SIGHUP) [Linux/Unix].
Definition: process.h:212
System shutting down (CTRL_SHUTDOWN_EVENT) [Windows, services only].
Definition: process.h:219
pid_t ProcessId
Process ID type.
Definition: process.h:32
Evo threads implementation.
#define EVO_EXCEPTIONS
Whether to throw exceptions on error by default.
Definition: evo_config.h:35
Type
Signal type.
Definition: process.h:204
Child process terminated (SIGCHLD), ignored by default [Linux/Unix].
Definition: process.h:211
Unknown signal type (used internally)
Definition: process.h:205
Main()
Default constructor.
Definition: process.h:233
Smart locking for synchronization.
Definition: lock.h:28
static const uint DAEMONIZE_NO_CD
Flag to skip changing current directory – see daemonize()
Definition: process.h:49
ProcessId get_pid()
Get current process ID.
Definition: process.h:40
void(* Handler)(SigNumType, Type)
Signal handler function type
Definition: process.h:222
static void on_shutdown(Signal::SigNumType, Signal::Type)
Default shutdown handler, called on shutdown signal.
Definition: process.h:281
Evo base exception class.
Definition: sys.h:1214
Ignore signal.
Definition: process.h:200
Evo C++ Library namespace.
Definition: alg.h:11
Use default handler, i.e. remove user handler.
Definition: process.h:199
Helper for common shutdown signal handling in program main().
Definition: process.h:229
static bool set_handler(Type type, Action action)
Set signal handling option.
Definition: process.h:335
General terminate (SIGTERM) [Portable: Linux/Unix/Windows].
Definition: process.h:208
static bool send_signal(ProcessId pid, Type signal)
Send signal to current process.
Definition: process.h:410
~Main()
Destructor.
Definition: process.h:255
MainServer(T &server, bool except=1)
Constructor that sets default shutdown handler for server.
Definition: process.h:276
Helper for common server shutdown signal handling in program main().
Definition: process.h:269
User defined signal 1 (SIGUSR1) [Linux/Unix].
Definition: process.h:213
Control-Break (CTRL_BREAK_EVENT) [Windows].
Definition: process.h:216
#define EVO_THROW(TYPE, MSG)
Throw an Evo exception.
Definition: sys.h:1446
static const ulong INF
Infinite wait timeout value.
Definition: thread.h:282
Process closed by user (CTRL_CLOSE_EVENT) [Windows].
Definition: process.h:217
int SigNumType
Signal number value type.
Definition: process.h:194
static const uint DAEMONIZE_NO_REDIR
Flag to skip std in/out/err redirects – see daemonize()
Definition: process.h:50