diff options
-rw-r--r-- | src/common/util.c | 104 | ||||
-rw-r--r-- | src/common/util.h | 14 | ||||
-rw-r--r-- | src/or/main.c | 12 |
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 */ } |