ganeti.daemon: fix daemon mode with GnuTLS >= 3.3
authorApollon Oikonomopoulos <apoikos@gmail.com>
Sat, 4 Oct 2014 18:14:03 +0000 (21:14 +0300)
committerHelga Velroyen <helgav@google.com>
Mon, 6 Oct 2014 09:25:15 +0000 (11:25 +0200)
Newer GnuTLS versions (>= 3.3.0) use a library constructor for
initialization and open /dev/urandom on library load, way before we
fork(). Closing /dev/urandom on fork causes a failure to re-seed GnuTLS's
random number generator during the first ganeti.http.client request, which
in turn causes the process to silently abort(3).

For more background on this behavior, see this thread at the GnuTLS
mailing list:

http://lists.gnupg.org/pipermail/gnutls-help/2014-April/003429.html

Note that calling pycurl.global_init() at the correct place (as we do) is not
enough, as it does not cause a re-initialization of the GnuTLS library.

As we cannot reliably detect neither the GnuTLS version, nor the socket, we
work our way around this by keeping all fds referring to /dev/urandom open
after fork. We do so using the /proc/self/fd interface.

This fixes issues #961 and #964.

Note that this would not affect the Haskell daemons using cURL + GnuTLS,
because we don't close all file descriptors on fork there.

Signed-off-by: Apollon Oikonomopoulos <apoikos@gmail.com>
Reviewed-by: Helga Velroyen <helgav@google.com>

lib/daemon.py

index bc67235..e4d10bc 100644 (file)
@@ -810,7 +810,23 @@ def GenericMain(daemon_name, optionparser,
   log_filename = constants.DAEMONS_LOGFILES[daemon_name]
 
   if options.fork:
-    utils.CloseFDs()
+    # Newer GnuTLS versions (>= 3.3.0) use a library constructor for
+    # initialization and open /dev/urandom on library load time, way before we
+    # fork(). Closing /dev/urandom causes subsequent ganeti.http.client
+    # requests to fail and the process to receive a SIGABRT. As we cannot
+    # reliably detect GnuTLS's socket, we work our way around this by keeping
+    # all fds referring to /dev/urandom open.
+    noclose_fds = []
+    for fd in os.listdir("/proc/self/fd"):
+      try:
+        if os.readlink(os.path.join("/proc/self/fd", fd)) == "/dev/urandom":
+          noclose_fds.append(int(fd))
+      except EnvironmentError:
+        # The fd might have disappeared (although it shouldn't as we're running
+        # single-threaded).
+        continue
+
+    utils.CloseFDs(noclose_fds=noclose_fds)
     (wpipe, stdio_reopen_fn) = utils.Daemonize(logfile=log_filename)
   else:
     (wpipe, stdio_reopen_fn) = (None, None)