[Kimchi-devel] [kimchi-devel][PATCH 6/7] Work around eventfd leak using multiprocessing

lvroyce at linux.vnet.ibm.com lvroyce at linux.vnet.ibm.com
Tue Jan 20 08:45:09 UTC 2015


From: Royce Lv <lvroyce at linux.vnet.ibm.com>

When accessing kimchi server url with authentication every time(no session),
kimchi server will deny service after about 1000 requests.
  "GET /storagepools/default/storagevolumes/ HTTP/1.0" 200 563 "" ""
     Failed to run command: id -Gn royce. [Errno 24] Too many open files
After tracking with 'lsof', "eventfd" handler leak for kimchi server.

This is because when using pam_start() to generate a pam file handler,
it needs to be closed by pam_end().
Unfortunately, PyPAM module only export start() function without end().

This patch workaround leak by putting PAM authentication in
a sub process, so that when it finishes execution,
its opening file handler will be closed by system automatically.

REF:
    http://linux.die.net/man/3/pam_end
    http://stackoverflow.com/questions/5125245/how-to-authenticate-a-user-using-pam

Signed-off-by: Royce Lv <lvroyce at linux.vnet.ibm.com>
---
 src/kimchi/auth.py | 65 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 37 insertions(+), 28 deletions(-)

diff --git a/src/kimchi/auth.py b/src/kimchi/auth.py
index 22c5c81..8cdbe76 100644
--- a/src/kimchi/auth.py
+++ b/src/kimchi/auth.py
@@ -145,35 +145,44 @@ class PAMUser(User):
     @staticmethod
     def authenticate(username, password, service="passwd"):
         '''Returns True if authenticate is OK via PAM.'''
-        def _pam_conv(auth, query_list, userData=None):
-            resp = []
-            for i in range(len(query_list)):
-                query, qtype = query_list[i]
-                if qtype == PAM.PAM_PROMPT_ECHO_ON:
-                    resp.append((username, 0))
-                elif qtype == PAM.PAM_PROMPT_ECHO_OFF:
-                    resp.append((password, 0))
-                elif qtype == PAM.PAM_PROMPT_ERROR_MSG:
-                    cherrypy.log.error_log.error(
-                        "PAM authenticate prompt error: %s" % query)
-                    resp.append(('', 0))
-                elif qtype == PAM.PAM_PROMPT_TEXT_INFO:
-                    resp.append(('', 0))
-                else:
-                    return None
-            return resp
-
-        auth = PAM.pam()
-        auth.start(service)
-        auth.set_item(PAM.PAM_USER, username)
-        auth.set_item(PAM.PAM_CONV, _pam_conv)
-        try:
-            auth.authenticate()
-        except PAM.error, (resp, code):
-            msg_args = {'username': username, 'code': code}
-            raise OperationFailed("KCHAUTH0001E", msg_args)
+        def _auth(result):
+            def _pam_conv(auth, query_list, userData=None):
+                resp = []
+                for i in range(len(query_list)):
+                    query, qtype = query_list[i]
+                    if qtype == PAM.PAM_PROMPT_ECHO_ON:
+                        resp.append((username, 0))
+                    elif qtype == PAM.PAM_PROMPT_ECHO_OFF:
+                        resp.append((password, 0))
+                    elif qtype == PAM.PAM_PROMPT_ERROR_MSG:
+                        cherrypy.log.error_log.error(
+                            "PAM authenticate prompt error: %s" % query)
+                        resp.append(('', 0))
+                    elif qtype == PAM.PAM_PROMPT_TEXT_INFO:
+                        resp.append(('', 0))
+                    else:
+                        return None
+                return resp
 
-        return True
+            result.value = False
+            auth = PAM.pam()
+            auth.start(service)
+            auth.set_item(PAM.PAM_USER, username)
+            auth.set_item(PAM.PAM_CONV, _pam_conv)
+            try:
+                auth.authenticate()
+            except PAM.error, (resp, code):
+                msg_args = {'username': username, 'code': code}
+                raise OperationFailed("KCHAUTH0001E", msg_args)
+
+            result.value = True
+
+        result = multiprocessing.Value('i', 0, lock=False)
+        p = multiprocessing.Process(target=_auth, args=(result, ))
+        p.start()
+        p.join()
+
+        return result.value
 
 
 class LDAPUser(User):
-- 
1.9.3




More information about the Kimchi-devel mailing list