From e5f552c0fb274c1a02fce51267c563a06c55d334 Mon Sep 17 00:00:00 2001 From: Arthur de Jong Date: Fri, 6 Jan 2012 13:59:50 +0000 Subject: user the logging framework, handle exceptions properly and some cleanups git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss-pam-ldapd@1588 ef36b2f9-881f-0410-afb5-c4e39611909c --- pynslcd/alias.py | 6 +-- pynslcd/cfg.py | 7 ++- pynslcd/common.py | 26 +++++----- pynslcd/ether.py | 4 +- pynslcd/group.py | 13 +++-- pynslcd/host.py | 6 +-- pynslcd/mypidfile.py | 4 +- pynslcd/netgroup.py | 6 +-- pynslcd/network.py | 6 +-- pynslcd/pam.py | 11 ++-- pynslcd/passwd.py | 9 ++-- pynslcd/protocol.py | 6 +-- pynslcd/pynslcd.py | 143 +++++++++++++++++++++++++++++---------------------- pynslcd/rpc.py | 4 +- 14 files changed, 130 insertions(+), 121 deletions(-) diff --git a/pynslcd/alias.py b/pynslcd/alias.py index a19382e..84dbf97 100644 --- a/pynslcd/alias.py +++ b/pynslcd/alias.py @@ -1,7 +1,7 @@ # alias.py - lookup functions for aliasnet addresses # -# Copyright (C) 2010, 2011 Arthur de Jong +# Copyright (C) 2010, 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,10 +18,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -import logging - -import constants import common +import constants attmap = common.Attributes(cn='cn', rfc822MailMember='rfc822MailMember') diff --git a/pynslcd/cfg.py b/pynslcd/cfg.py index 42aa2fd..9386716 100644 --- a/pynslcd/cfg.py +++ b/pynslcd/cfg.py @@ -1,7 +1,7 @@ # cfg.py - module for accessing configuration information # -# Copyright (C) 2010 Arthur de Jong +# Copyright (C) 2010, 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,6 +18,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA +import logging import re import sys @@ -207,7 +208,6 @@ def read(filename): '|'.join(_scope_options.keys())), line, re.IGNORECASE) if m: - keyword = m.group('keyword').lower() mod = maps[str(m.group('map')).lower()] mod.scope = _scope_options[m.group('keyword').lower()] continue @@ -234,7 +234,6 @@ def read(filename): m = re.match('nss_initgroups_ignoreusers\s+(?P\S.*)', line, re.IGNORECASE) if m: - global nss_initgroups_ignoreusers users = m.group('value') if users.lower() == 'alllocal': # get all users known to the system currently (since nslcd isn't yet @@ -283,4 +282,4 @@ def read(filename): # dump config (debugging code) for k, v in globals().items(): if not k.startswith('_'): - print '%s=%r' % (k, v) + logging.debug('%s=%r', k, v) diff --git a/pynslcd/common.py b/pynslcd/common.py index ed5bb4e..375afed 100644 --- a/pynslcd/common.py +++ b/pynslcd/common.py @@ -1,7 +1,7 @@ # common.py - functions that are used by different modules # -# Copyright (C) 2010, 2011 Arthur de Jong +# Copyright (C) 2010, 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,14 +18,15 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -import re +import logging +import sys + import ldap import ldap.dn -import sys +from attmap import Attributes import cfg import constants -from attmap import Attributes def isvalidname(name): @@ -106,7 +107,7 @@ class Search(object): # get search results filter = self.mk_filter() for base in self.bases: - print 'SEARCHING %s' % base + logging.debug('SEARCHING %s', base) # do the LDAP search try: for entry in self.conn.search_s(base, self.scope, filter, self.attributes): @@ -118,12 +119,15 @@ class Search(object): # FIXME: log message pass + def escape(self, value): + """Escape the provided value so it may be used in a search filter.""" + return ldap.filter.escape_filter_chars(str(value)) + def mk_filter(self): """Return the active search filter (based on the read parameters).""" if self.parameters: return '(&%s(%s))' % (self.filter, - ')('.join('%s=%s' % (self.attmap[attribute], - ldap.filter.escape_filter_chars(str(value))) + ')('.join('%s=%s' % (self.attmap[attribute], self.escape(value)) for attribute, value in self.parameters.items())) return self.filter @@ -143,21 +147,19 @@ class Search(object): # check that these attributes have at least one value for attr in self.required: if not attributes.get(attr, None): - print '%s: %s: missing' % (dn, self.attmap[attr]) + logging.warning('%s: %s: missing', dn, self.attmap[attr]) return # check that requested attribute is present (case sensitive) for attr in self.case_sensitive: value = self.parameters.get(attr, None) if value and str(value) not in attributes[attr]: - # TODO: log at debug level, this can happen in normal cases - print '%s: %s: does not contain %r value' % (dn, self.attmap[attr], value) + logging.debug('%s: %s: does not contain %r value', dn, self.attmap[attr], value) return # not found, skip entry # check that requested attribute is present (case insensitive) for attr in self.case_insensitive: value = self.parameters.get(attr, None) if value and str(value).lower() not in (x.lower() for x in attributes[attr]): - # TODO: log at debug level, this can happen in normal cases - print '%s: %s: does not contain %r value' % (dn, self.attmap[attr], value) + logging.debug('%s: %s: does not contain %r value', dn, self.attmap[attr], value) return # not found, skip entry # limit attribute values to requested value for attr in self.limit_attributes: diff --git a/pynslcd/ether.py b/pynslcd/ether.py index 42fa77e..4d6ae03 100644 --- a/pynslcd/ether.py +++ b/pynslcd/ether.py @@ -1,7 +1,7 @@ # ether.py - lookup functions for ethernet addresses # -# Copyright (C) 2010, 2011 Arthur de Jong +# Copyright (C) 2010, 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -20,8 +20,8 @@ import struct -import constants import common +import constants def ether_aton(ether): diff --git a/pynslcd/group.py b/pynslcd/group.py index 0e4e96f..29a6fda 100644 --- a/pynslcd/group.py +++ b/pynslcd/group.py @@ -1,7 +1,7 @@ # group.py - group entry lookup routines # -# Copyright (C) 2010, 2011 Arthur de Jong +# Copyright (C) 2010, 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -19,11 +19,10 @@ # 02110-1301 USA import logging -import ldap.filter -import constants -import common from passwd import dn2uid, uid2dn +import common +import constants def clean(lst): @@ -60,8 +59,8 @@ class Search(common.Search): dn = uid2dn(self.conn, memberuid) if dn: return '(&%s(|(%s=%s)(%s=%s)))' % (self.filter, - attmap['memberUid'], ldap.filter.escape_filter_chars(memberuid), - attmap['member'], ldap.filter.escape_filter_chars(dn)) + attmap['memberUid'], self.escape(memberuid), + attmap['member'], self.escape(dn)) return super(Search, self).mk_filter() @@ -91,7 +90,7 @@ class GroupRequest(common.Request): # actually return the results for name in names: if not common.isvalidname(name): - print '%s: %s: denied by validnames option' % (dn, attmap['cn']) + logging.warning('%s: %s: denied by validnames option', dn, attmap['cn']) else: for gid in gids: self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) diff --git a/pynslcd/host.py b/pynslcd/host.py index e81d8dc..fa15b58 100644 --- a/pynslcd/host.py +++ b/pynslcd/host.py @@ -1,7 +1,7 @@ # host.py - lookup functions for host names and addresses # -# Copyright (C) 2011 Arthur de Jong +# Copyright (C) 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,10 +18,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -import logging - -import constants import common +import constants attmap = common.Attributes(cn='cn', ipHostNumber='ipHostNumber') diff --git a/pynslcd/mypidfile.py b/pynslcd/mypidfile.py index e179e16..e386f3e 100644 --- a/pynslcd/mypidfile.py +++ b/pynslcd/mypidfile.py @@ -1,7 +1,7 @@ # mypidfile.py - functions for properly locking a PIDFile # -# Copyright (C) 2010, 2011 Arthur de Jong +# Copyright (C) 2010, 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,8 +18,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -import fcntl import errno +import fcntl import os diff --git a/pynslcd/netgroup.py b/pynslcd/netgroup.py index 7deba01..9ddba7a 100644 --- a/pynslcd/netgroup.py +++ b/pynslcd/netgroup.py @@ -1,7 +1,7 @@ # netgroup.py - lookup functions for netgroups # -# Copyright (C) 2011 Arthur de Jong +# Copyright (C) 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -21,8 +21,8 @@ import logging import re -import constants import common +import constants _netgroup_triple_re = re.compile(r'^\s*\(\s*(?P.*)\s*,\s*(?P.*)\s*,\s*(?P.*)\s*\)\s*$') @@ -47,7 +47,7 @@ class NetgroupRequest(common.Request): for triple in attributes['nisNetgroupTriple']: m = _netgroup_triple_re.match(triple) if not m: - print '%s: %s: invalid value: %r' % (dn, attmap['nisNetgroupTriple'], triple) + logging.warning('%s: %s: invalid value: %r', dn, attmap['nisNetgroupTriple'], triple) else: self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) self.fp.write_int32(constants.NSLCD_NETGROUP_TYPE_TRIPLE) diff --git a/pynslcd/network.py b/pynslcd/network.py index 2887a61..d0778c0 100644 --- a/pynslcd/network.py +++ b/pynslcd/network.py @@ -1,7 +1,7 @@ # network.py - lookup functions for network names and addresses # -# Copyright (C) 2011 Arthur de Jong +# Copyright (C) 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,10 +18,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -import logging - -import constants import common +import constants attmap = common.Attributes(cn='cn', diff --git a/pynslcd/pam.py b/pynslcd/pam.py index 8bbbe48..8e4f76f 100644 --- a/pynslcd/pam.py +++ b/pynslcd/pam.py @@ -1,7 +1,7 @@ # pam.py - functions authentication, authorisation and session handling # -# Copyright (C) 2010, 2011 Arthur de Jong +# Copyright (C) 2010, 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -19,17 +19,18 @@ # 02110-1301 USA import logging + import ldap -import constants -import common import cfg +import common +import constants import passwd def try_bind(userdn, password): # open a new connection - conn = ldap.initialize(cfg.ldap_uri) + conn = ldap.initialize(cfg.uri) # bind using the specified credentials conn.simple_bind_s(userdn, password) # perform search for own object (just to do any kind of search) @@ -60,7 +61,7 @@ class PAMRequest(common.Request): # get the username from the uid attribute values = myldap_get_values(entry, passwd.attmap['uid']) if not values or not values[0]: - logging.warn('%s: is missing a %s attribute', dn, passwd.attmap['uid']) + logging.warning('%s: is missing a %s attribute', dn, passwd.attmap['uid']) value = values[0] # check the username if value and not common.isvalidname(value): diff --git a/pynslcd/passwd.py b/pynslcd/passwd.py index fff752e..6dce2ce 100644 --- a/pynslcd/passwd.py +++ b/pynslcd/passwd.py @@ -1,7 +1,7 @@ # passwd.py - lookup functions for user account information # -# Copyright (C) 2010, 2011 Arthur de Jong +# Copyright (C) 2010, 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,11 +18,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -import ldap -import ldap.filter +import logging -import constants import common +import constants attmap = common.Attributes(uid='uid', @@ -61,7 +60,7 @@ class PasswdRequest(common.Request): # write results for name in names: if not common.isvalidname(name): - print '%s: %s: denied by validnames option' % (dn, attmap['uid']) + logging.warning('%s: %s: denied by validnames option', dn, attmap['uid']) else: for uid in uids: self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) diff --git a/pynslcd/protocol.py b/pynslcd/protocol.py index 0f358cb..3c91a7e 100644 --- a/pynslcd/protocol.py +++ b/pynslcd/protocol.py @@ -1,7 +1,7 @@ # protocol.py - protocol name and number lookup routines # -# Copyright (C) 2011 Arthur de Jong +# Copyright (C) 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,10 +18,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -import logging - -import constants import common +import constants attmap = common.Attributes(cn='cn', ipProtocolNumber='ipProtocolNumber') diff --git a/pynslcd/pynslcd.py b/pynslcd/pynslcd.py index 64e4a80..1c75cbb 100755 --- a/pynslcd/pynslcd.py +++ b/pynslcd/pynslcd.py @@ -2,7 +2,7 @@ # pynslcd.py - main daemon module # -# Copyright (C) 2010, 2011 Arthur de Jong +# Copyright (C) 2010, 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -19,22 +19,23 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -import os -import sys import daemon -import mypidfile -import threading import logging import logging.handlers +import os import signal +import sys +import syslog +import threading + import ldap -import constants # from nslcd.h -import config # from configure +from tio import TIOStream import cfg # from nslcd.conf import common - -from tio import TIOStream +import config # from configure +import constants # from nslcd.h +import mypidfile # the name of the program @@ -47,36 +48,51 @@ debugging = 0 checkonly = False -# configure logging class MyFormatter(logging.Formatter): + def format(self, record): - msg = logging.Formatter.format(self, record) + msg = super(MyFormatter, self).format(record) if record.levelno == logging.DEBUG: msg = 'DEBUG: %s' % msg return msg -#logging.basicConfig(level=logging.INFO) -# , format='%(message)s' + + +class MySysLogHandler(logging.Handler): + + mapping = { + logging.DEBUG: syslog.LOG_DEBUG, + logging.INFO: syslog.LOG_INFO, + logging.WARNING: syslog.LOG_WARNING, + logging.ERROR: syslog.LOG_ERR, + logging.CRITICAL: syslog.LOG_CRIT, + } + + def __init__(self): + super(MySysLogHandler, self).__init__() + syslog.openlog(program_name, syslog.LOG_PID, syslog.LOG_DAEMON) + + def emit(self, record): + priority = self.mapping.get(record.levelno, syslog.LOG_WARNING) + msg = self.format(record) + for l in msg.splitlines(): + syslog.syslog(priority, l) + + +# configure logging formatter = MyFormatter('%(message)s') stderrhandler = logging.StreamHandler(sys.stderr) stderrhandler.setFormatter(formatter) -##sysloghandler = logging.handlers.SysLogHandler(address='/dev/log') -##sysloghandler.setFormatter(formatter) -#logging.getLogger().setFormatter(MyFormatter()) +sysloghandler = MySysLogHandler() +sysloghandler.setFormatter(formatter) logging.getLogger().addHandler(stderrhandler) - -#logger = logging.getLogger() -#logger.setLevel(logging.INFO) -#syslog = logging.handlers.SysLogHandler(address='/dev/log') -#formatter = logging.Formatter('%(name)s: %(levelname)s %(message)s') -#syslog.setFormatter(formatter) -#logger.addHandler(syslog) +logging.getLogger().setLevel(logging.INFO) def display_version(fp): fp.write('%(PACKAGE_STRING)s\n' 'Written by Arthur de Jong.\n' '\n' - 'Copyright (C) 2010, 2011 Arthur de Jong\n' + 'Copyright (C) 2010-2012 Arthur de Jong\n' 'This is free software; see the source for copying conditions. There is NO\n' 'warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n' % {'PACKAGE_STRING': config.PACKAGE_STRING, }) @@ -198,7 +214,7 @@ def acceptconnection(session): try: handler = handlers[action] except KeyError: - logging.warn('invalid action id: %r', action) + logging.warning('invalid action id: %r', action) return handler(fp, session, uid)() finally: @@ -214,14 +230,13 @@ def disable_nss_ldap(): def worker(): - # create a new LDAP session - #session = myldap_create_session() session = ldap.initialize(cfg.uri) - # start waiting for incoming connections while True: - # wait for a new connection - acceptconnection(session) - # FIXME: handle exceptions + try: + acceptconnection(session) + except: + logging.exception('exception in worker') + # ignore all exceptions, just keep going if __name__ == '__main__': @@ -237,7 +252,7 @@ if __name__ == '__main__': # set log level if debugging: logging.getLogger().setLevel(logging.DEBUG) - # FIXME: implement + # TODO: implement #if myldap_set_debuglevel(cfg.debug) != LDAP_SUCCESS: # sys.exit(1) # read configuration file @@ -272,36 +287,40 @@ if __name__ == '__main__': }) # start daemon with daemon: - # start normal logging + # start normal logging to syslog if not debugging: - log_startlogging() + logging.getLogger().addHandler(sysloghandler) logging.info('version %s starting', config.VERSION) - # create socket - nslcd_serversocket = create_socket() - # drop all supplemental groups try: - os.setgroups(()) - except OSError, e: - logging.warn('cannot setgroups(()) (ignored): %s', e) - # change to nslcd gid - if cfg.gid is not None: - import grp - os.setgid(grp.getgrnam(cfg.gid).gr_gid) - # change to nslcd uid - if cfg.uid is not None: - import pwd - u = pwd.getpwnam(cfg.uid) - os.setuid(u.pw_uid) - os.environ['HOME'] = u.pw_dir - logging.info('accepting connections') - # start worker threads - threads = [] - for i in range(cfg.threads): - thread = threading.Thread(target=worker, name='thread%d' % i) - thread.setDaemon(True) - thread.start() - logging.debug('started thread %s', thread.getName()) - threads.append(thread) - # wait for all threads to die - for thread in threads: - thread.join(10000) + # create socket + nslcd_serversocket = create_socket() + # drop all supplemental groups + try: + os.setgroups(()) + except OSError, e: + logging.warning('cannot setgroups(()) (ignored): %s', e) + # change to nslcd gid + if cfg.gid is not None: + import grp + os.setgid(grp.getgrnam(cfg.gid).gr_gid) + # change to nslcd uid + if cfg.uid is not None: + import pwd + u = pwd.getpwnam(cfg.uid) + os.setuid(u.pw_uid) + os.environ['HOME'] = u.pw_dir + logging.info('accepting connections') + # start worker threads + threads = [] + for i in range(cfg.threads): + thread = threading.Thread(target=worker, name='thread%d' % i) + thread.setDaemon(True) + thread.start() + logging.debug('started thread %s', thread.getName()) + threads.append(thread) + # wait for all threads to die + for thread in threads: + thread.join(10000) + except: + logging.exception('main loop exit') + # no need to re-raise since we are exiting anyway diff --git a/pynslcd/rpc.py b/pynslcd/rpc.py index 2c7aa85..5676b27 100644 --- a/pynslcd/rpc.py +++ b/pynslcd/rpc.py @@ -1,7 +1,7 @@ # rpc.py - rpc name lookup routines # -# Copyright (C) 2011 Arthur de Jong +# Copyright (C) 2011, 2012 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -18,8 +18,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301 USA -import logging - import constants import common -- cgit v1.2.3