aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/util.c104
-rw-r--r--src/common/util.h14
-rw-r--r--src/or/main.c12
3 files changed, 105 insertions, 25 deletions
diff --git a/src/common/util.c b/src/common/util.c
index 4d9a44953..98b16a59b 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -696,31 +696,97 @@ get_uname(void)
return uname_result;
}
-void daemonize(void) {
-#ifdef HAVE_DAEMON
- if (daemon(0 /* chdir to / */,
- 0 /* Redirect std* to /dev/null */)) {
- log_fn(LOG_ERR, "Daemon returned an error: %s", strerror(errno));
+#ifndef MS_WINDOWS
+/* Based on code contributed by christian grothoff */
+static int start_daemon_called = 0;
+static int finish_daemon_called = 0;
+static int daemon_filedes[2];
+void start_daemon(void)
+{
+ pid_t pid;
+
+ if (start_daemon_called)
+ return;
+ start_daemon_called = 1;
+
+ /* Don't hold the wrong FS mounted */
+ if (chdir("/") < 0) {
+ perror("chdir");
exit(1);
}
-#elif ! defined(MS_WINDOWS)
- /* Fork; parent exits. */
- if (fork())
- exit(0);
- /* Create new session; make sure we never get a terminal */
- setsid();
- if (fork())
- exit(0);
+ pipe(daemon_filedes);
+ pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ exit(1);
+ }
+ if (pid) { /* Parent */
+ int ok;
+ char c;
- chdir("/");
- umask(000);
+ 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);
+ else
+ exit(1); /* child reported error */
+ } else { /* Child */
+ close(daemon_filedes[0]); /* we only write */
+
+ pid = 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);
+ }
+ return;
+ }
+}
- fclose(stdin);
- fclose(stdout);
- fclose(stderr);
-#endif
+void finish_daemon(void)
+{
+ int nullfd;
+ char c = '.';
+ if (finish_daemon_called)
+ return;
+ if (!start_daemon_called)
+ start_daemon();
+ finish_daemon_called = 1;
+
+ nullfd = open("/dev/null",
+ O_CREAT | O_RDWR | O_APPEND);
+ if (nullfd < 0) {
+ perror("/dev/null");
+ exit(1);
+ }
+ /* 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) {
+ perror("dup2"); /* Should never happen... */
+ exit(1);
+ }
+ write(daemon_filedes[1], &c, sizeof(char)); /* signal success */
+ close(daemon_filedes[1]);
}
+#else
+/* defined(MS_WINDOWS) */
+void start_daemon(void) {}
+void finish_daemon(void) {}
+#endif
void write_pidfile(char *filename) {
#ifndef MS_WINDOWS
diff --git a/src/common/util.h b/src/common/util.h
index acb8ab82a..fd300fa80 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -86,7 +86,19 @@ int tor_socketpair(int family, int type, int protocol, int fd[2]);
const char *get_uname(void);
-void daemonize(void);
+/* 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);
+/* 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(void);
+
void write_pidfile(char *filename);
int switch_id(char *user, char *group);
diff --git a/src/or/main.c b/src/or/main.c
index 99671f8d3..5b4c6a647 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -396,8 +396,6 @@ static int prepare_for_poll(void) {
}
static int init_from_config(int argc, char **argv) {
- static int have_daemonized=0;
-
if(getconfig(argc,argv,&options)) {
log_fn(LOG_ERR,"Reading config failed. For usage, try -h.");
return -1;
@@ -424,9 +422,9 @@ static int init_from_config(int argc, char **argv) {
}
}
- if(options.RunAsDaemon && !have_daemonized) {
- daemonize();
- have_daemonized = 1;
+ if(options.RunAsDaemon) {
+ /* XXXX Can we delay this any more? */
+ finish_daemon();
}
/* write our pid to the pid file, if we do not have write permissions we will log a warning */
@@ -633,6 +631,10 @@ int tor_main(int argc, char *argv[]) {
if (init_from_config(argc,argv) < 0)
return -1;
+ if (options.RunAsDaemon) {
+ start_daemon();
+ }
+
if(options.ORPort) { /* only spawn dns handlers if we're a router */
dns_init(); /* initialize the dns resolve tree, and spawn workers */
}