[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r16722: {tor} Add a lockfile to the Tor data directory to avoid situations (in tor/trunk: . src/common src/or)



Author: nickm
Date: 2008-09-01 16:06:26 -0400 (Mon, 01 Sep 2008)
New Revision: 16722

Modified:
   tor/trunk/ChangeLog
   tor/trunk/configure.in
   tor/trunk/src/common/compat.c
   tor/trunk/src/common/compat.h
   tor/trunk/src/or/config.c
   tor/trunk/src/or/main.c
   tor/trunk/src/or/or.h
   tor/trunk/src/or/router.c
Log:
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.

Modified: tor/trunk/ChangeLog
===================================================================
--- tor/trunk/ChangeLog	2008-09-01 19:15:05 UTC (rev 16721)
+++ tor/trunk/ChangeLog	2008-09-01 20:06:26 UTC (rev 16722)
@@ -3,7 +3,11 @@
     - 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:
     - Convert many internal address representations to optionally hold

Modified: tor/trunk/configure.in
===================================================================
--- tor/trunk/configure.in	2008-09-01 19:15:05 UTC (rev 16721)
+++ tor/trunk/configure.in	2008-09-01 20:06:26 UTC (rev 16722)
@@ -324,7 +324,7 @@
 
 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

Modified: tor/trunk/src/common/compat.c
===================================================================
--- tor/trunk/src/common/compat.c	2008-09-01 19:15:05 UTC (rev 16721)
+++ tor/trunk/src/common/compat.c	2008-09-01 20:06:26 UTC (rev 16722)
@@ -26,6 +26,7 @@
 #ifdef MS_WINDOWS
 #include <process.h>
 #include <windows.h>
+#include <sys/locking.h>
 #endif
 
 #ifdef HAVE_UNAME
@@ -98,6 +99,9 @@
 #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 @@
   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

Modified: tor/trunk/src/common/compat.h
===================================================================
--- tor/trunk/src/common/compat.h	2008-09-01 19:15:05 UTC (rev 16721)
+++ tor/trunk/src/common/compat.h	2008-09-01 20:06:26 UTC (rev 16722)
@@ -266,6 +266,11 @@
 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

Modified: tor/trunk/src/or/config.c
===================================================================
--- tor/trunk/src/or/config.c	2008-09-01 19:15:05 UTC (rev 16721)
+++ tor/trunk/src/or/config.c	2008-09-01 20:06:26 UTC (rev 16722)
@@ -1172,6 +1172,11 @@
   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 @@
  * 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);

Modified: tor/trunk/src/or/main.c
===================================================================
--- tor/trunk/src/or/main.c	2008-09-01 19:15:05 UTC (rev 16721)
+++ tor/trunk/src/or/main.c	2008-09-01 20:06:26 UTC (rev 16722)
@@ -1850,6 +1850,54 @@
   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>,

Modified: tor/trunk/src/or/or.h
===================================================================
--- tor/trunk/src/or/or.h	2008-09-01 19:15:05 UTC (rev 16721)
+++ tor/trunk/src/or/or.h	2008-09-01 20:06:26 UTC (rev 16722)
@@ -2781,8 +2781,11 @@
                                      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 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);
 

Modified: tor/trunk/src/or/router.c
===================================================================
--- tor/trunk/src/or/router.c	2008-09-01 19:15:05 UTC (rev 16721)
+++ tor/trunk/src/or/router.c	2008-09-01 20:06:26 UTC (rev 16722)
@@ -235,6 +235,17 @@
       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)) {