diff options
author | Nick Mathewson <nickm@torproject.org> | 2008-09-01 20:06:26 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2008-09-01 20:06:26 +0000 |
commit | f80ac31d742c2d86f1812b895ef61a5885e2447e (patch) | |
tree | 9b223723aced9e7a8acc55044120cc406c77bd9b | |
parent | 6a29ad853b630a1970fd95e17ecd79306e7c39e6 (diff) | |
download | tor-f80ac31d742c2d86f1812b895ef61a5885e2447e.tar.gz tor-f80ac31d742c2d86f1812b895ef61a5885e2447e.zip |
Add a lockfile to the Tor data directory to avoid situations where two Tors start with the same datadir, or where a --list-fingerprints races with a server to create keys, or such.
svn:r16722
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | src/common/compat.c | 75 | ||||
-rw-r--r-- | src/common/compat.h | 5 | ||||
-rw-r--r-- | src/or/config.c | 11 | ||||
-rw-r--r-- | src/or/main.c | 48 | ||||
-rw-r--r-- | src/or/or.h | 12 | ||||
-rw-r--r-- | src/or/router.c | 11 |
8 files changed, 162 insertions, 6 deletions
@@ -3,6 +3,10 @@ Changes in version 0.2.1.6-alpha - 2008-09-xx - Fix compile on OpenBSD 4.4-current. Bugfix on 0.2.1.5-alpha. Reported by Tas. + o Minor features: + - Use a lockfile to make sure that two Tor processes are not + simultaneously running with the same datadir. + Changes in version 0.2.1.5-alpha - 2008-08-31 o Major features: diff --git a/configure.in b/configure.in index 52ae5cc873..f765e8e7f1 100644 --- a/configure.in +++ b/configure.in @@ -324,7 +324,7 @@ AC_CHECK_HEADERS(netdb.h sys/ioctl.h sys/socket.h arpa/inet.h netinet/in.h pwd.h dnl These headers are not essential -AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/param.h sys/wait.h limits.h sys/limits.h netinet/in.h arpa/inet.h machine/limits.h syslog.h sys/time.h sys/resource.h inttypes.h utime.h sys/utime.h sys/mman.h netinet/in6.h malloc.h sys/syslimits.h malloc/malloc.h linux/types.h) +AC_CHECK_HEADERS(stdint.h sys/types.h inttypes.h sys/param.h sys/wait.h limits.h sys/limits.h netinet/in.h arpa/inet.h machine/limits.h syslog.h sys/time.h sys/resource.h inttypes.h utime.h sys/utime.h sys/mman.h netinet/in6.h malloc.h sys/syslimits.h malloc/malloc.h linux/types.h sys/file.h) TOR_CHECK_PROTOTYPE(malloc_good_size, HAVE_MALLOC_GOOD_SIZE_PROTOTYPE, [#ifdef HAVE_MALLOC_H diff --git a/src/common/compat.c b/src/common/compat.c index fcb6f9888d..59d85a41f8 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -26,6 +26,7 @@ const char compat_c_id[] = #ifdef MS_WINDOWS #include <process.h> #include <windows.h> +#include <sys/locking.h> #endif #ifdef HAVE_UNAME @@ -98,6 +99,9 @@ const char compat_c_id[] = #ifdef HAVE_SYS_SYSLIMITS_H #include <sys/syslimits.h> #endif +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif #ifdef USE_BSOCKETS #include <bsocket.h> @@ -488,6 +492,77 @@ touch_file(const char *fname) return 0; } +struct tor_lockfile_t { + char *filename; + int fd; +}; + +tor_lockfile_t * +tor_lockfile_lock(const char *filename, int blocking, int *locked_out) +{ + tor_lockfile_t *result; + int fd; + *locked_out = 0; + + log_info(LD_FS, "Locking \"%s\"", filename); + fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0600); + if (fd < 0) { + log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename, + strerror(errno)); + return NULL; + } +#ifdef WIN32 + _lseek(fd, 0, SEEK_SET); + if (_locking(fd, blocking ? _LK_LOCK : _LK_NBLOCK, 0) < 0) { + if (errno != EDEADLOCK) + log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno)); + else + *locked_out = 1; + close(fd); + return NULL; + } +#else + if (flock(fd, LOCK_EX|(blocking ? 0 : LOCK_NB)) < 0) { + if (errno != EWOULDBLOCK) + log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno)); + else + *locked_out = 1; + close(fd); + return NULL; + } +#endif + + result = tor_malloc(sizeof(tor_lockfile_t)); + result->filename = tor_strdup(filename); + result->fd = fd; + return result; +} + +void +tor_lockfile_unlock(tor_lockfile_t *lockfile) +{ + tor_assert(lockfile); + + log_info(LD_FS, "Unlocking \"%s\"", lockfile->filename); +#ifdef WIN32 + _lseek(fd, 0, SEEK_SET); + if (_locking(fd, _LK_UNLCK, 0) < 0) { + log_warn(LD_FS,"Error unlocking \"%s\": %s", lockfile->filename, + strerror(errno)); + } +#else + if (flock(lockfile->fd, LOCK_UN) < 0) { + log_warn(LD_FS, "Error unlocking \"%s\": %s", lockfile->filename, + strerror(errno)); + } +#endif + + close(lockfile->fd); + lockfile->fd = -1; + tor_free(lockfile->filename); + tor_free(lockfile); +} + #undef DEBUG_SOCKET_COUNTING #ifdef DEBUG_SOCKET_COUNTING /** A bitarray of all fds that should be passed to tor_socket_close(). Only diff --git a/src/common/compat.h b/src/common/compat.h index a0f614f55e..2e1c14b7e1 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -266,6 +266,11 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result); int replace_file(const char *from, const char *to); int touch_file(const char *fname); +typedef struct tor_lockfile_t tor_lockfile_t; +tor_lockfile_t *tor_lockfile_lock(const char *filename, int blocking, + int *locked_out); +void tor_lockfile_unlock(tor_lockfile_t *lockfile); + #ifdef MS_WINDOWS #define PATH_SEPARATOR "\\" #else diff --git a/src/or/config.c b/src/or/config.c index 52c7f8b8a4..7d9dc64b7d 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1172,6 +1172,11 @@ options_act(or_options_t *old_options) int running_tor = options->command == CMD_RUN_TOR; char *msg; + if (running_tor && !have_lockfile()) { + if (try_locking(options, 1) < 0) + return -1; + } + if (consider_adding_dir_authorities(options, old_options) < 0) return -1; @@ -4883,10 +4888,10 @@ get_or_state(void) * Note: Consider using the get_datadir_fname* macros in or.h. */ char * -get_datadir_fname2_suffix(const char *sub1, const char *sub2, - const char *suffix) +options_get_datadir_fname2_suffix(or_options_t *options, + const char *sub1, const char *sub2, + const char *suffix) { - or_options_t *options = get_options(); char *fname = NULL; size_t len; tor_assert(options); diff --git a/src/or/main.c b/src/or/main.c index c3f902e776..1d57cff5ed 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1850,6 +1850,54 @@ tor_init(int argc, char *argv[]) return 0; } +static tor_lockfile_t *lockfile = NULL; + +int +try_locking(or_options_t *options, int err_if_locked) +{ + if (lockfile) + return 0; + else { + char *fname = options_get_datadir_fname2_suffix(options, "lock",NULL,NULL); + int already_locked = 0; + tor_lockfile_t *lf = tor_lockfile_lock(fname, 0, &already_locked); + tor_free(fname); + if (!lf) { + if (err_if_locked && already_locked) { + int r; + log_warn(LD_GENERAL, "It looks like another Tor process is running " + "with the same data directory. Waiting 5 seconds to see " + "if it goes away."); + sleep(5); + r = try_locking(options, 0); + if (r<0) { + log_err(LD_GENERAL, "No, it's still there. Exiting."); + exit(0); + } + return r; + } + return -1; + } + lockfile = lf; + return 0; + } +} + +int +have_lockfile(void) +{ + return lockfile != NULL; +} + +void +release_lockfile(void) +{ + if (lockfile) { + tor_lockfile_unlock(lockfile); + lockfile = NULL; + } +} + /** Free all memory that we might have allocated somewhere. * If <b>postfork</b>, we are a worker process and we want to free * only the parts of memory that we won't touch. If !<b>postfork</b>, diff --git a/src/or/or.h b/src/or/or.h index ab7e98d883..a84bd9b9d6 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2781,8 +2781,11 @@ config_line_t *option_get_assignment(or_options_t *options, const char *key); int options_save_current(void); const char *get_torrc_fname(void); -char *get_datadir_fname2_suffix(const char *sub1, const char *sub2, - const char *suffix); +char *options_get_datadir_fname2_suffix(or_options_t *options, + const char *sub1, const char *sub2, + const char *suffix); +#define get_datadir_fname2_suffix(sub1, sub2, suffix) \ + options_get_datadir_fname2_suffix(get_options(), (sub1), (sub2), (suffix)) /** Return a newly allocated string containing datadir/sub1. See * get_datadir_fname2_suffix. */ #define get_datadir_fname(sub1) get_datadir_fname2_suffix((sub1), NULL, NULL) @@ -3493,6 +3496,11 @@ void dns_servers_relaunch_checks(void); void control_signal_act(int the_signal); void handle_signals(int is_parent); + +int try_locking(or_options_t *options, int err_if_locked); +int have_lockfile(void); +void release_lockfile(void); + void tor_cleanup(void); void tor_free_all(int postfork); diff --git a/src/or/router.c b/src/or/router.c index 842cb7b52a..4c85a7b2ab 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -235,6 +235,17 @@ init_key_from_file(const char *fname, int generate, int severity) goto error; case FN_NOENT: if (generate) { + if (!have_lockfile()) { + if (try_locking(get_options(), 0)<0) { + /* Make sure that --list-fingerprint only creates new keys + * if there is no possibility for a deadlock. */ + log(severity, LD_FS, "Another Tor process has locked \"%s\". Not " + "writing any new keys.", fname); + /*XXXX The 'other process' might make a key in a second or two; + * maybe we should wait for it. */ + goto error; + } + } log_info(LD_GENERAL, "No key found in \"%s\"; generating fresh key.", fname); if (crypto_pk_generate_key(prkey)) { |