From 6915fe26da8dce41fc967d761f005390aa956161 Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Thu, 13 Oct 2011 19:04:37 +0200
Subject: [PATCH] utils.cfunc: Cleanup, more flexibility
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- Split code using ctypes directly into a helper class
- Don't load β€œlibc.so.6”, but use handle for main program instead (see
  comment in code)
- Clarify comment on errno with older ctypes versions
- Rename unittest since it can't be used for other functions (modifies
  process environment at runtime)
- Add boolean return value for β€œMlockall”

These changes are leftovers from some experiments with ctypes.

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Iustin Pop <iustin@google.com>
---
 Makefile.am                                   |  2 +-
 lib/utils/cfunc.py                            | 73 ++++++++++++-------
 ...> ganeti.utils.cfunc.mlockall_unittest.py} |  4 +-
 3 files changed, 47 insertions(+), 32 deletions(-)
 rename test/{ganeti.utils.cfunc_unittest.py => ganeti.utils.cfunc.mlockall_unittest.py} (97%)

diff --git a/Makefile.am b/Makefile.am
index 2a513f15f..5031a5303 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -724,7 +724,7 @@ python_tests = \
 	test/ganeti.tools.ensure_dirs_unittest.py \
 	test/ganeti.uidpool_unittest.py \
 	test/ganeti.utils.algo_unittest.py \
-	test/ganeti.utils.cfunc_unittest.py \
+	test/ganeti.utils.cfunc.mlockall_unittest.py \
 	test/ganeti.utils.filelock_unittest.py \
 	test/ganeti.utils.hash_unittest.py \
 	test/ganeti.utils.io_unittest.py \
diff --git a/lib/utils/cfunc.py b/lib/utils/cfunc.py
index ea14d6848..a1b3af49b 100644
--- a/lib/utils/cfunc.py
+++ b/lib/utils/cfunc.py
@@ -34,44 +34,61 @@ except ImportError:
   ctypes = None
 
 
-# Flags for mlockall() (from bits/mman.h)
+# Flags for mlockall(2) (from bits/mman.h)
 _MCL_CURRENT = 1
 _MCL_FUTURE = 2
 
 
+class _FuncWrapper:
+  def __init__(self, ct):
+    """Initializes this class.
+
+    """
+    # Make use of a dlopen(3) feature whereby giving a NULL handle returns the
+    # main program. Functions from all previously loaded libraries can then be
+    # used.
+    mainprog = ct.CDLL(None)
+
+    # The ctypes module before Python 2.6 does not have built-in functionality
+    # to access the global errno global (which, depending on the libc and build
+    # options, is per-thread), where function error codes are stored. Use GNU
+    # libc's way to retrieve errno(3) instead.
+    try:
+      errno_loc = getattr(mainprog, "__errno_location")
+    except AttributeError, err:
+      logging.debug("Unable to find errno location: %s", err)
+      errno_fn = None
+    else:
+      errno_loc.restype = ct.POINTER(ct.c_int)
+      errno_fn = lambda: errno_loc().contents.value
+
+    self.errno_fn = errno_fn
+
+    # Try to get mlockall(2)
+    self.mlockall_fn = getattr(mainprog, "mlockall", None)
+
+
 def Mlockall(_ctypes=ctypes):
   """Lock current process' virtual address space into RAM.
 
-  This is equivalent to the C call mlockall(MCL_CURRENT|MCL_FUTURE),
-  see mlock(2) for more details. This function requires ctypes module.
+  This is equivalent to the C call C{mlockall(MCL_CURRENT | MCL_FUTURE)}. See
+  mlockall(2) for more details. This function requires the C{ctypes} module.
 
-  @raises errors.NoCtypesError: if ctypes module is not found
+  @raises errors.NoCtypesError: If the C{ctypes} module is not found
 
   """
   if _ctypes is None:
     raise errors.NoCtypesError()
 
-  try:
-    libc = _ctypes.cdll.LoadLibrary("libc.so.6")
-  except EnvironmentError, err:
-    logging.error("Failure trying to load libc: %s", err)
-    libc = None
-  if libc is None:
-    logging.error("Cannot set memory lock, ctypes cannot load libc")
-    return
-
-  # Some older version of the ctypes module don't have built-in functionality
-  # to access the errno global variable, where function error codes are stored.
-  # By declaring this variable as a pointer to an integer we can then access
-  # its value correctly, should the mlockall call fail, in order to see what
-  # the actual error code was.
-  # pylint: disable=W0212
-  libc.__errno_location.restype = _ctypes.POINTER(_ctypes.c_int)
-
-  if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
-    # pylint: disable=W0212
-    logging.error("Cannot set memory lock: %s",
-                  os.strerror(libc.__errno_location().contents.value))
-    return
-
-  logging.debug("Memory lock set")
+  funcs = _FuncWrapper(_ctypes)
+
+  if funcs.mlockall_fn is None:
+    logging.debug("libc doesn't support mlockall(2)")
+  else:
+    if funcs.mlockall_fn(_MCL_CURRENT | _MCL_FUTURE) == 0:
+      logging.debug("Memory lock set")
+      return True
+
+    logging.error("Cannot set memory lock: %s", os.strerror(funcs.errno_fn()))
+
+  return False
diff --git a/test/ganeti.utils.cfunc_unittest.py b/test/ganeti.utils.cfunc.mlockall_unittest.py
similarity index 97%
rename from test/ganeti.utils.cfunc_unittest.py
rename to test/ganeti.utils.cfunc.mlockall_unittest.py
index 8c5c7469b..bafa71c8d 100755
--- a/test/ganeti.utils.cfunc_unittest.py
+++ b/test/ganeti.utils.cfunc.mlockall_unittest.py
@@ -41,17 +41,15 @@ class TestMlockallWithCtypes(unittest.TestCase):
   """Whether Mlockall() works if ctypes is present.
 
   """
-
   def test(self):
     if utils.ctypes:
-      utils.Mlockall()
+      self.assertTrue(utils.Mlockall())
 
 
 class TestMlockallWithNoCtypes(unittest.TestCase):
   """Whether Mlockall() raises an error if ctypes is not present.
 
   """
-
   def test(self):
     self.assertRaises(errors.NoCtypesError, utils.Mlockall, _ctypes=None)
 
-- 
GitLab