[tor-commits] [stegotorus/master] Add support for daemonizing and writing a pid file.

zwol at torproject.org zwol at torproject.org
Fri Jul 20 23:17:08 UTC 2012


commit 337fadeede2d4418ae569173cdf19ed2465444e3
Author: Zack Weinberg <zackw at cmu.edu>
Date:   Wed Jun 13 22:09:58 2012 -0400

    Add support for daemonizing and writing a pid file.
---
 src/audit-globals.sh   |    2 +
 src/main.cc            |   53 +++++++++++++++++++-----
 src/subprocess-unix.cc |  105 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/subprocess.h       |   39 ++++++++++++++++++
 4 files changed, 188 insertions(+), 11 deletions(-)

diff --git a/src/audit-globals.sh b/src/audit-globals.sh
index 27cab3a..7c28f21 100644
--- a/src/audit-globals.sh
+++ b/src/audit-globals.sh
@@ -39,7 +39,9 @@ sed '
   /^crypt crypto_initialized$/d
   /^crypt crypto_errs_initialized$/d
   /^main allow_kq$/d
+  /^main daemon_mode$/d
   /^main handle_signal_cb(int, short, void\*)::got_sigint$/d
+  /^main pidfile_name$/d
   /^main registration_helper$/d
   /^main the_event_base$/d
   /^network listeners$/d
diff --git a/src/main.cc b/src/main.cc
index dd69e68..42687e1 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -37,6 +37,8 @@ using std::string;
 
 static struct event_base *the_event_base;
 static bool allow_kq = false;
+static bool daemon_mode = false;
+static string pidfile_name;
 static string registration_helper;
 
 /**
@@ -234,7 +236,11 @@ usage(void)
           "--log-min-severity=warn|info|debug ~ set minimum logging severity\n"
           "--no-log ~ disable logging\n"
           "--timestamp-logs ~ add timestamps to all log messages\n"
-          "--allow-kqueue ~ allow use of kqueue(2) (may be buggy)\n");
+          "--allow-kqueue ~ allow use of kqueue(2) (may be buggy)\n"
+          "--registration-helper=<helper> ~ use <helper> to register with "
+          "a relay database\n"
+          "--pid-file=<file> ~ write process ID to <file> after startup\n"
+          "--daemon ~ run as a daemon");
 
   exit(1);
 }
@@ -256,7 +262,8 @@ handle_generic_args(const char *const *argv)
   bool logsev_set = false;
   bool allow_kq_set = false;
   bool timestamps_set = false;
-  bool registration_helper_set=false;
+  bool registration_helper_set = false;
+  bool pidfile_set = false;
   int i = 1;
 
   while (argv[i] &&
@@ -278,21 +285,19 @@ handle_generic_args(const char *const *argv)
         fprintf(stderr, "you've already set a min. log severity!\n");
         exit(1);
       }
-      if (log_set_min_severity((char *)argv[i]+19) < 0) {
-        fprintf(stderr, "error at setting logging severity");
+      if (log_set_min_severity(argv[i]+19) < 0) {
+        fprintf(stderr, "invalid min. log severity '%s'", argv[i]+19);
         exit(1);
       }
       logsev_set = true;
     } else if (!strcmp(argv[i], "--no-log")) {
-        if (logsev_set) {
-          fprintf(stderr, "you've already set a min. log severity!\n");
+      if (logsev_set || logmethod_set) {
+          fprintf(stderr, "can't ask for both some logs and no logs!\n");
           exit(1);
         }
-        if (log_set_method(LOG_METHOD_NULL, NULL) < 0) {
-          fprintf(stderr, "error at setting logging severity.\n");
-          exit(1);
-        }
-        logsev_set = true;
+      log_set_method(LOG_METHOD_NULL, NULL);
+      logsev_set = true;
+      logmethod_set = true;
     } else if (!strcmp(argv[i], "--timestamp-logs")) {
       if (timestamps_set) {
         fprintf(stderr, "you've already asked for timestamps!\n");
@@ -314,6 +319,19 @@ handle_generic_args(const char *const *argv)
       }
       registration_helper = string(argv[i]+22);
       registration_helper_set = true;
+    } else if (!strncmp(argv[i], "--pid-file=", 11)) {
+      if (pidfile_set) {
+        fprintf(stderr, "you've already set a pid file!\n");
+        exit(1);
+      }
+      pidfile_name = string(argv[i]+11);
+      pidfile_set = true;
+    } else if (!strcmp(argv[i], "--daemon")) {
+      if (daemon_mode) {
+        fprintf(stderr, "you've already requested daemon mode!\n");
+        exit(1);
+      }
+      daemon_mode = true;
     } else {
       fprintf(stderr, "unrecognizable argument '%s'\n", argv[i]);
       exit(1);
@@ -321,6 +339,12 @@ handle_generic_args(const char *const *argv)
     i++;
   }
 
+  /* Cross-option consistency checks. */
+  if (daemon_mode && !logmethod_set) {
+    log_warn("cannot log to stderr in daemon mode");
+    log_set_method(LOG_METHOD_NULL, NULL);
+  }
+
   return i;
 }
 
@@ -380,6 +404,13 @@ main(int, const char *const *argv)
   log_assert(configs.size() > 0);
 
   /* Configurations have been established; proceed with initialization. */
+  if (daemon_mode)
+    daemonize();
+
+  pidfile pf(pidfile_name);
+  if (!pf)
+    log_warn("failed to create pid-file '%s': %s", pf.pathname().c_str(),
+             pf.errmsg());
 
   init_crypto();
 
diff --git a/src/subprocess-unix.cc b/src/subprocess-unix.cc
index 7e075d9..573b167 100644
--- a/src/subprocess-unix.cc
+++ b/src/subprocess-unix.cc
@@ -13,6 +13,7 @@
 #include "subprocess.h"
 
 #include <map>
+#include <sstream>
 
 #include <sys/stat.h>
 #include <dirent.h>
@@ -465,3 +466,107 @@ get_environ(const char *exclude)
 
   return result;
 }
+
+void
+daemonize()
+{
+  if (getppid() == 1) // already a daemon
+    return;
+
+  // Close standard I/O file descriptors and reopen them on /dev/null.
+  // We do this before forking (a) to avoid any possibility of
+  // double-flushed stdio buffers, and (b) so we can exit
+  // unsuccessfully in the unlikely event of a failure.
+  fflush(NULL); // flush all open stdio buffers
+
+  close(0);
+  if (open("/dev/null", O_RDONLY) != 0)
+    log_abort("/dev/null: %s", strerror(errno));
+
+  close(1);
+  if (open("/dev/null", O_WRONLY) != 1)
+    log_abort("/dev/null: %s", strerror(errno));
+
+  // N.B. log_abort might be writing somewhere other than stderr.
+  // In fact, we rather hope it is, 'cos otherwise all logs from
+  // the child are gonna go to the bit bucket.
+  close(2);
+  if (open("/dev/null", O_WRONLY) != 2)
+    log_abort("/dev/null: %s", strerror(errno));
+
+  pid_t pid = fork();
+  if (pid < 0)
+    log_abort("fork failed: %s", strerror(errno));
+
+  if (pid > 0) // Parent
+    // The use of _exit instead of exit here is deliberate.
+    // It's the process that carries on from this function that
+    // should do atexit cleanups (eventually).
+    _exit(0);
+
+  // Become a session leader, and then fork one more time and exit in
+  // the parent.  (This puts the process that will actually be the
+  // daemon in an orphaned process group.  On some systems, this is
+  // necessary to ensure that the daemon can never acquire a controlling
+  // terminal again.  XXX On some systems will the grandchild receive
+  // an unwanted, probably-fatal SIGHUP when its session leader exits?)
+  setsid();
+  if (fork())
+    _exit(0);
+
+  // For the moment we do not chdir anywhere, because the HTTP steg expects
+  // to find its traces relative to the cwd.  FIXME.
+}
+
+pidfile::pidfile(std::string const& p)
+  : path(p), errcode(0)
+{
+  if (path.empty())
+    return;
+
+  std::ostringstream ss;
+  ss << getpid() << '\n';
+  const char *b = ss.str().c_str();
+  size_t n = ss.str().size();
+
+  int f = open(path.c_str(), O_WRONLY|O_CREAT|O_EXCL, 0666);
+  if (f == -1) {
+    errcode = errno;
+    return;
+  }
+
+  do {
+    ssize_t r = write(f, b, n);
+    if (r < 0) {
+      errcode = errno;
+      close(f);
+      remove(path.c_str());
+      return;
+    }
+    n -= r;
+    b += r;
+  } while (n > 0);
+
+  // Sadly, close() can fail, and in this case it actually matters.
+  if (close(f)) {
+    errcode = errno;
+    remove(path.c_str());
+  }
+}
+
+pidfile::~pidfile()
+{
+  if (!errcode && !path.empty())
+    remove(path.c_str());
+}
+
+pidfile::operator bool() const
+{
+  return !errcode;
+}
+
+const char *
+pidfile::errmsg() const
+{
+  return errcode ? strerror(errcode) : 0;
+}
diff --git a/src/subprocess.h b/src/subprocess.h
index 95fe848..73eeece 100644
--- a/src/subprocess.h
+++ b/src/subprocess.h
@@ -65,4 +65,43 @@ struct subprocess
 // begins with those characters will be excluded from the result.
 extern std::vector<std::string> get_environ(const char *exclude = 0);
 
+// These are in here because they involve process management 'under the hood',
+// and because (like other process management) their Unix and Windows
+// implementations have to be radically different.
+
+// Turn into a daemon; detach from the parent process and any
+// controlling terminal.  Closes standard I/O streams and reopens them
+// to /dev/null.  If this returns, it succeeded.
+extern void daemonize();
+
+// Instantiating this class causes a file to be created at the specified
+// pathname, which contains the decimal process ID of the current process.
+// On destruction, the file is deleted.
+//
+// If you're going to call daemonize(), you need to do it _before_ creating
+// one of these, because daemonize() changes the process ID.
+
+class pidfile
+{
+public:
+  pidfile(const std::string& p);
+  ~pidfile();
+
+  const std::string& pathname() const { return path; }
+
+  // True if pid-file creation succeeded.
+  operator bool() const;
+
+  // If pid-file creation did *not* succeed, returns the underlying system
+  // error message.  You should combine that with the pathname and some
+  // text to the effect that this is a process ID file for the actual error
+  // message printed to the user.
+  // If pid-file creation *did* succeed, returns NULL.
+  const char *errmsg() const;
+
+private:
+  std::string path;
+  int errcode;
+};
+
 #endif





More information about the tor-commits mailing list