diff --git a/branches/ucs-3.1/ucs-3.1-2/base/univention-config-registry/python/univention/service_info.py b/branches/ucs-3.1/ucs-3.1-2/base/univention-config-registry/python/univention/service_info.py index d3542ae..316cf78 100644 --- a/branches/ucs-3.1/ucs-3.1-2/base/univention-config-registry/python/univention/service_info.py +++ b/branches/ucs-3.1/ucs-3.1-2/base/univention-config-registry/python/univention/service_info.py @@ -31,64 +31,58 @@ # /usr/share/common-licenses/AGPL-3; if not, see # . -import locale import os import re -import string import shlex import univention.info_tools as uit -class Service( uit.LocalizedDictionary ): - def __init__( self ): - uit.LocalizedDictionary.__init__( self ) +class Service(uit.LocalizedDictionary): + def __init__(self): + uit.LocalizedDictionary.__init__(self) self.start_runlevel = [] self.stop_runlevel = [] self.start_code = 0 self.stop_code = 0 self.running = False - def check( self ): + def check(self): """Check service entry for validity, returning list of incomplete entries.""" incomplete = [] - for key in ( 'description', 'programs' ): - if not self.get( key, None ): + for key in ('description', 'programs'): + if not self.get(key, None): incomplete.append(key) return incomplete -def pidof( name ): - result = [] - for file in os.listdir( '/proc' ): - dir = os.path.join( '/proc', file ) - if not os.path.isdir( dir ): - continue - if not os.path.isfile( os.path.join( dir, 'stat' ) ): - continue - cmdline = os.path.join( dir, 'cmdline' ) - if not os.path.isfile( cmdline ): + +def pidof(name): + """ + Return list of process IDs matching name. + >>> import os,sys;str(os.getpid()) in pidof(sys.executable) + True + """ + cmd = shlex.split(name) + for proc in os.listdir('/proc'): + cmdline = os.path.join('/proc', proc, 'cmdline') + try: + with open(cmdline, 'r') as fd: + commandline = fd.read() + except EnvironmentError: continue - fd = open( cmdline ) - cmd = fd.readline() # kernel thread - if not cmd: + if not commandline: continue - if '\x00' in cmd: - args = cmd.split( '\x00' ) + if '\x00' in commandline: + args = commandline.split('\x00') else: - args = cmd.split(' ') - cmd = shlex.split( name ) + args = commandline.split(' ') if cmd[0] in args: - if len( cmd ) > 1 and len( args ) >= len( cmd ): - for i in range( 1, len( cmd ) ): - print cmd[ i ], args[ i ] - if cmd[ i ] != args[ i ]: - break - else: - result.append( file ) + if len(args) >= len(cmd) > 1: + if all(a == c for a, c in zip(args, cmd)): + yield proc else: - result.append( file ) + yield proc - return result class ServiceInfo( object ): BASE_DIR = '/etc/univention/service.info' @@ -96,8 +90,8 @@ class ServiceInfo( object ): CUSTOMIZED = '_customized' FILE_SUFFIX = '.cfg' - RUNLEVELS = map(str, range(7)) + ['S'] - INIT_SCRIPT_REGEX = re.compile( '(?P[SK])(?P[0-9]+)(?P.*)' ) + RUNLEVELS = "0123456S" + INIT_SCRIPT_REGEX = re.compile('(?P[SK])(?P[0-9]{2})(?P.+)') def __init__( self, install_mode = False ): self.services = {} @@ -106,38 +100,38 @@ class ServiceInfo( object ): self.update_services() def sysv_infos( self ): - global _runlevels, _init_link - - for level in _runlevels: - for link in os.listdir( '/etc/rc%s.d/' % level ): + """Read start/stop levels of services.""" + for level in ServiceInfo.RUNLEVELS: + for link in os.listdir('/etc/rc%s.d/' % (level,)): if not os.path.islink( link ): continue - matches = _init_link.match( link ) - if not matches: + match = ServiceInfo.INIT_SCRIPT_REGEX.match(link) + if not match: continue - grp = matches.groupdict() - - name = grp.get( 'name', '' ) - if not name or not name in self.services.keys(): + action, code, name = match.groups() + try: + service = self.service[name] + except LookupError: continue - if grp.get( 'action', '' ) == 'S': - self.services[ name ].start_runlevels.append( level ) - self.services[ name ].start_code = int( grp[ 'code' ] ) - elif grp.get( 'action', '' ) == 'K': - self.services[ name ].start_runlevels.append( level ) - self.services[ name ].start_code = int( grp[ 'code' ] ) - - def __update_status( self, name, service ): - for prog in service[ 'programs' ].split( ',' ): - if prog and not pidof( prog.strip() ): + if action == 'S': + service.start_runlevels.append(level) + service.start_code = int(code) + elif action == 'K': + service.stop_runlevels.append(level) + service.stop_code = int(code) + + def __update_status(self, name, service): + for prog in service['programs'].split(','): + if prog and not pidof(prog.strip()): service.running = False break else: service.running = True def update_services( self ): + """Update the run state of all services.""" for name, serv in self.services.items(): - self.__update_status( name, serv ) + self.__update_status(name, serv) def check_services( self ): """Return dictionary of incomplete service descriptions.""" @@ -149,11 +143,12 @@ class ServiceInfo( object ): return incomplete def write_customized( self ): + """Save service cusomization.""" filename = os.path.join( ServiceInfo.BASE_DIR, ServiceInfo.SERVICES, ServiceInfo.CUSTOMIZED ) try: - fd = open( filename, 'w' ) - except: + fd = open(filename, 'w') + except IOError: return False cfg = uit.UnicodeConfig() @@ -176,14 +171,14 @@ class ServiceInfo( object ): filename = os.path.join( ServiceInfo.BASE_DIR, ServiceInfo.SERVICES, package + ServiceInfo.FILE_SUFFIX ) cfg = uit.UnicodeConfig() - cfg.read( filename ) + cfg.read(filename) for sec in cfg.sections(): # service already known? - if not override and sec in self.services.keys(): + if not override and sec in self.services: continue srv = Service() - for name, value in cfg.items( sec ): - srv[ name ] = value + for name, value in cfg.items(sec): + srv[name] = value for path in srv.get('programs', '').split(','): # "programs" defines the "/proc/self/cmdline" of the service, # not the executable, therefore we test for a leading "/": @@ -191,9 +186,10 @@ class ServiceInfo( object ): if path.startswith('/') and not os.path.exists(path.split(' ', 1)[0]): break # ==> do not execute else else: - self.services[ sec ] = srv + self.services[sec] = srv def __load_services( self ): + """Load definition of all defined services.""" path = os.path.join( ServiceInfo.BASE_DIR, ServiceInfo.SERVICES ) for entry in os.listdir( path ): # customized service descrptions are read afterwards @@ -206,6 +202,7 @@ class ServiceInfo( object ): self.read_customized() def read_customized( self ): + """Read service cusomization.""" custom = os.path.join( ServiceInfo.BASE_DIR, ServiceInfo.SERVICES, ServiceInfo.CUSTOMIZED ) self.read_services( custom, override = True ) @@ -217,10 +214,15 @@ class ServiceInfo( object ): def get_service( self, name ): '''returns a service object associated with the given name or None if it does not exist''' - self.services.get( name, None ) + return self.services.get( name, None ) def add_service( self, name, service ): '''this methods adds a new service object or overrides an old entry''' if not service.check(): self.services[ name ] = service + + +if __name__ == '__main__': + import doctest + doctest.testmod()