commit 6915fe26da8dce41fc967d761f005390aa956161
Author: Michael Hanselmann <hansmi@google.com>
Date:   Thu Oct 13 19:04:37 2011 +0200

    utils.cfunc: Cleanup, more flexibility
    
    - 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>

diff --git a/Makefile.am b/Makefile.am
index 2a513f1..5031a53 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 ea14d68..a1b3af4 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.mlockall_unittest.py b/test/ganeti.utils.cfunc.mlockall_unittest.py
new file mode 100755
index 0000000..bafa71c
--- /dev/null
+++ b/test/ganeti.utils.cfunc.mlockall_unittest.py
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+#
+
+# Copyright (C) 2010, 2011 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Script for testing utils.Mlockall
+
+This test is run in a separate process because it changes memory behaviour.
+
+"""
+
+import unittest
+
+from ganeti import utils
+from ganeti import errors
+
+import testutils
+
+
+# WARNING: The following tests modify the memory behaviour at runtime. Don't
+# add unrelated tests here.
+
+
+class TestMlockallWithCtypes(unittest.TestCase):
+  """Whether Mlockall() works if ctypes is present.
+
+  """
+  def test(self):
+    if utils.ctypes:
+      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)
+
+
+if __name__ == "__main__":
+  testutils.GanetiTestProgram()
diff --git a/test/ganeti.utils.cfunc_unittest.py b/test/ganeti.utils.cfunc_unittest.py
deleted file mode 100755
index 8c5c746..0000000
--- a/test/ganeti.utils.cfunc_unittest.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/python
-#
-
-# Copyright (C) 2010, 2011 Google Inc.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
-
-
-"""Script for testing utils.Mlockall
-
-This test is run in a separate process because it changes memory behaviour.
-
-"""
-
-import unittest
-
-from ganeti import utils
-from ganeti import errors
-
-import testutils
-
-
-# WARNING: The following tests modify the memory behaviour at runtime. Don't
-# add unrelated tests here.
-
-
-class TestMlockallWithCtypes(unittest.TestCase):
-  """Whether Mlockall() works if ctypes is present.
-
-  """
-
-  def test(self):
-    if utils.ctypes:
-      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)
-
-
-if __name__ == "__main__":
-  testutils.GanetiTestProgram()
