Electroneum
posix_fork.cpp
Go to the documentation of this file.
1 // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 //
6 
8 #include "misc_log_ex.h"
9 
10 #include <cstdlib>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include <stdexcept>
14 #include <string>
15 
16 #ifndef TMPDIR
17 #define TMPDIR "/tmp"
18 #endif
19 
20 namespace posix {
21 
22 namespace {
23  void quit(const std::string & message)
24  {
26  throw std::runtime_error(message);
27  }
28 }
29 
30 void fork(const std::string & pidfile)
31 {
32  // If a PID file is specified, we open the file here, because
33  // we can't report errors after the fork operation.
34  // When we fork, we close thise file in each of the parent
35  // processes.
36  // Only in the final child process do we write the PID to the
37  // file (and close it).
38  std::ofstream pidofs;
39  if (! pidfile.empty ())
40  {
41  int oldpid;
42  std::ifstream pidrifs;
43  pidrifs.open(pidfile, std::fstream::in);
44  if (! pidrifs.fail())
45  {
46  // Read the PID and send signal 0 to see if the process exists.
47  if (pidrifs >> oldpid && oldpid > 1 && kill(oldpid, 0) == 0)
48  {
49  quit("PID file " + pidfile + " already exists and the PID therein is valid");
50  }
51  pidrifs.close();
52  }
53 
54  pidofs.open(pidfile, std::fstream::out | std::fstream::trunc);
55  if (pidofs.fail())
56  {
57  quit("Failed to open specified PID file for writing");
58  }
59  }
60  // Fork the process and have the parent exit. If the process was started
61  // from a shell, this returns control to the user. Forking a new process is
62  // also a prerequisite for the subsequent call to setsid().
63  if (pid_t pid = ::fork())
64  {
65  if (pid > 0)
66  {
67  // We're in the parent process and need to exit.
68  pidofs.close();
69  // When the exit() function is used, the program terminates without
70  // invoking local variables' destructors. Only global variables are
71  // destroyed.
72  exit(0);
73  }
74  else
75  {
76  quit("First fork failed");
77  }
78  }
79 
80  // Make the process a new session leader. This detaches it from the
81  // terminal.
82  setsid();
83 
84  // A second fork ensures the process cannot acquire a controlling terminal.
85  if (pid_t pid = ::fork())
86  {
87  if (pid > 0)
88  {
89  pidofs.close();
90  exit(0);
91  }
92  else
93  {
94  quit("Second fork failed");
95  }
96  }
97 
98  if (! pidofs.fail())
99  {
100  int pid = ::getpid();
101  pidofs << pid << std::endl;
102  pidofs.close();
103  }
104 
105  // Close the standard streams. This decouples the daemon from the terminal
106  // that started it.
107  close(0);
108  close(1);
109  close(2);
110 
111  // We don't want the daemon to have any standard input.
112  if (open("/dev/null", O_RDONLY) < 0)
113  {
114  quit("Unable to open /dev/null");
115  }
116 
117 #ifdef DEBUG_TMPDIR_LOG
118  // Send standard output to a log file.
119  const char *tmpdir = getenv("TMPDIR");
120  if (!tmpdir)
121  tmpdir = TMPDIR;
122  std::string output = tmpdir;
123  output += "/electroneum.daemon.stdout.stderr";
124  const int flags = O_WRONLY | O_CREAT | O_APPEND;
125  const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
126  if (open(output.c_str(), flags, mode) < 0)
127  {
128  quit("Unable to open output file: " + output);
129  }
130 
131  // Also send standard error to the same log file.
132  if (dup(1) < 0)
133  {
134  quit("Unable to dup output descriptor");
135  }
136 #endif
137 }
138 
139 } // namespace posix
::std::string string
Definition: gtest-port.h:1097
void fork(const std::string &pidfile)
Definition: posix_fork.cpp:30
#define TMPDIR
Definition: posix_fork.cpp:17
std::string message("Message requiring signing")
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98