aboutsummaryrefslogtreecommitdiff
path: root/src/lib/process/daemon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/process/daemon.c')
-rw-r--r--src/lib/process/daemon.c159
1 files changed, 159 insertions, 0 deletions
diff --git a/src/lib/process/daemon.c b/src/lib/process/daemon.c
new file mode 100644
index 0000000000..edffb04683
--- /dev/null
+++ b/src/lib/process/daemon.c
@@ -0,0 +1,159 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "lib/process/daemon.h"
+
+#ifndef _WIN32
+
+#include "lib/fs/files.h"
+#include "lib/log/torlog.h"
+#include "lib/thread/threads.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Based on code contributed by christian grothoff */
+/** True iff we've called start_daemon(). */
+static int start_daemon_called = 0;
+/** True iff we've called finish_daemon(). */
+static int finish_daemon_called = 0;
+/** Socketpair used to communicate between parent and child process while
+ * daemonizing. */
+static int daemon_filedes[2];
+/** Start putting the process into daemon mode: fork and drop all resources
+ * except standard fds. The parent process never returns, but stays around
+ * until finish_daemon is called. (Note: it's safe to call this more
+ * than once: calls after the first are ignored.)
+ */
+void
+start_daemon(void)
+{
+ pid_t pid;
+
+ if (start_daemon_called)
+ return;
+ start_daemon_called = 1;
+
+ if (pipe(daemon_filedes)) {
+ /* LCOV_EXCL_START */
+ log_err(LD_GENERAL,"pipe failed; exiting. Error was %s", strerror(errno));
+ exit(1); // exit ok: during daemonize, pipe failed.
+ /* LCOV_EXCL_STOP */
+ }
+ pid = fork();
+ if (pid < 0) {
+ /* LCOV_EXCL_START */
+ log_err(LD_GENERAL,"fork failed. Exiting.");
+ exit(1); // exit ok: during daemonize, fork failed
+ /* LCOV_EXCL_STOP */
+ }
+ if (pid) { /* Parent */
+ int ok;
+ char c;
+
+ close(daemon_filedes[1]); /* we only read */
+ ok = -1;
+ while (0 < read(daemon_filedes[0], &c, sizeof(char))) {
+ if (c == '.')
+ ok = 1;
+ }
+ fflush(stdout);
+ if (ok == 1)
+ exit(0); // exit ok: during daemonize, daemonizing.
+ else
+ exit(1); /* child reported error. exit ok: daemonize failed. */
+ } else { /* Child */
+ close(daemon_filedes[0]); /* we only write */
+
+ (void) setsid(); /* Detach from controlling terminal */
+ /*
+ * Fork one more time, so the parent (the session group leader) can exit.
+ * This means that we, as a non-session group leader, can never regain a
+ * controlling terminal. This part is recommended by Stevens's
+ * _Advanced Programming in the Unix Environment_.
+ */
+ if (fork() != 0) {
+ exit(0); // exit ok: during daemonize, fork failed (2)
+ }
+ set_main_thread(); /* We are now the main thread. */
+
+ return;
+ }
+}
+
+/** Finish putting the process into daemon mode: drop standard fds, and tell
+ * the parent process to exit. (Note: it's safe to call this more than once:
+ * calls after the first are ignored. Calls start_daemon first if it hasn't
+ * been called already.)
+ */
+void
+finish_daemon(const char *desired_cwd)
+{
+ int nullfd;
+ char c = '.';
+ if (finish_daemon_called)
+ return;
+ if (!start_daemon_called)
+ start_daemon();
+ finish_daemon_called = 1;
+
+ if (!desired_cwd)
+ desired_cwd = "/";
+ /* Don't hold the wrong FS mounted */
+ if (chdir(desired_cwd) < 0) {
+ log_err(LD_GENERAL,"chdir to \"%s\" failed. Exiting.",desired_cwd);
+ exit(1); // exit ok: during daemonize, chdir failed.
+ }
+
+ nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0);
+ if (nullfd < 0) {
+ /* LCOV_EXCL_START */
+ log_err(LD_GENERAL,"/dev/null can't be opened. Exiting.");
+ exit(1); // exit ok: during daemonize, couldn't open /dev/null
+ /* LCOV_EXCL_STOP */
+ }
+ /* close fds linking to invoking terminal, but
+ * close usual incoming fds, but redirect them somewhere
+ * useful so the fds don't get reallocated elsewhere.
+ */
+ if (dup2(nullfd,0) < 0 ||
+ dup2(nullfd,1) < 0 ||
+ dup2(nullfd,2) < 0) {
+ /* LCOV_EXCL_START */
+ log_err(LD_GENERAL,"dup2 failed. Exiting.");
+ exit(1); // exit ok: during daemonize, dup2 failed.
+ /* LCOV_EXCL_STOP */
+ }
+ if (nullfd > 2)
+ close(nullfd);
+ /* signal success */
+ if (write(daemon_filedes[1], &c, sizeof(char)) != sizeof(char)) {
+ log_err(LD_GENERAL,"write failed. Exiting.");
+ }
+ close(daemon_filedes[1]);
+}
+#else /* !(!defined(_WIN32)) */
+/* defined(_WIN32) */
+void
+start_daemon(void)
+{
+}
+void
+finish_daemon(const char *cp)
+{
+ (void)cp;
+}
+#endif /* !defined(_WIN32) */