[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