def updateUsersProcesses(self, cbChanged): """ Update list of users and processes. Instead of using who or similar, look for dbus-launch or another process. This is because we need the PID of one of the users processes to get misc. environment variables. Because of this it's convenient to update users and processes in the same place. cbChanged is called with the value True if the list of users has changed, otherwise False. """ # actually a list of files in /proc, but the PIDs should be the only things changing pids = os.listdir('/proc') if pids == self._prevPIDList: cbChanged(False) return self._prevPIDList = pids cfg = config.get() import re # What we're matching: # Mon May 25 19:59:23 2009 jss 23043 /bin/bash re_ps = re.compile(r'^(\S+\s*\S+\s*\S+\s*\S+\s*\S+)\s*(\S+)\s*(\S+)\s*(.+)$') re_number = re.compile(r'^\d+$') cmd_login = cfg.get('Agent', 'login cmd') args = ['ww', '-e', '-o', 'lstart,user,pid,args'] env = {'LC_ALL': 'C'} d = utils.getProcessOutput('/bin/ps', args, env) wait = defer.waitForDeferred(d) yield wait output = wait.getResult() processes = {} # {user: [ (pid, commandline)] } loginProcesses = [] # [ (logintime, user, pid, commandline) ] for line in output.splitlines()[1:]: # skip header m = re_ps.match(line) if not m: log.err('ps regex didn\'t match %s' % line) continue lstart, user, pid, commandline = m.groups() user = user.decode('utf-8') commandline = commandline.decode('utf-8', 'replace') # handle case where ps returns UID, e.g. for long usernames if re_number.match(user): uid = user try: user = getpwuid(int(uid)).pw_name.decode('utf-8') except KeyError: continue processes.setdefault(user, []).append((int(pid), commandline)) if commandline.startswith(cmd_login): loginProcesses.append((lstart, user, int(pid), commandline)) self.processes = processes loginPIDs = [l[2] for l in loginProcesses] if loginPIDs == self._loginPIDs: cbChanged(False) return self._loginPIDs = loginPIDs log.debug('login processes: %s' % loginProcesses) users = {} for lstart, user, pid, commandline in loginProcesses: try: logintime = time.mktime(time.strptime(lstart)) except ValueError: log.err('failed to parse lstart \'%s\'' % lstart) continue try: env = dict( ( var.split('=', 1) for var in open('/proc/%s/environ' % pid, 'r').read().split('\x00') if var ) ) display = env['DISPLAY'] if 'SSH_CLIENT' in env: # remote login clientIP = env['SSH_CLIENT'].split(None, 1)[0] wait = defer.waitForDeferred(self._maybeResolve(clientIP)) yield wait try: client = wait.getResult() except socket.error: client = clientIP HWAddr = self._getHWAddr(clientIP) else: client = '' HWAddr = self._getOwnHWAddr() if 'LTSP_CLIENT' in env: ctype = 'ltsp' else: ctype = 'other' ukey = (user, client, display) try: userobj = users[ukey] = self.users[ukey] except KeyError: uenv = {} for var in ('HOME', 'LANG', 'DISPLAY'): uenv[var] = env[var] name = getpw(user).pw_gecos.decode('utf-8', 'replace') groups = getgroups(user) userobj = users[ukey] = User() userobj.uid = getpw(user).pw_uid userobj.gid = getpw(user).pw_gid userobj.username = user userobj.client = client userobj.clientHWAddr = HWAddr userobj.display = display userobj.name = name userobj.groups = groups userobj.env = uenv userobj.clientType = ctype userobj.logintime = logintime except (IOError, OSError, KeyError, IndexError): log.err() log.err(env) continue self.users = users cbChanged(True)
def updateUsersProcesses(self, cbChanged): """ Update list of users and processes. """ import re # What we're matching: # Mon May 25 19:59:23 2009 jss 23043 /bin/bash re_ps = re.compile(r'^(\S+\s*\S+\s*\S+\s*\S+\s*\S+)\s*(\S+)\s*(\S+)\s*(.+)$') re_number = re.compile(r'^\d+$') cmd_login = '******' args = ['-ww', '-ax', '-o', 'lstart,user,pid,command'] env = {'LC_ALL': 'C'} d = utils.getProcessOutput('/bin/ps', args, env) wait = defer.waitForDeferred(d) yield wait output = wait.getResult() processes = {} # {user: [ (pid, commandline)] } loginProcesses = [] # [ (logintime, user, pid, commandline) ] for line in output.splitlines()[1:]: # skip header m = re_ps.match(line) if not m: log.err('ps regex didn\'t match %s' % line) continue lstart, user, pid, commandline = m.groups() user = user.decode('utf-8') commandline = commandline.decode('utf-8', 'replace') # handle case where ps returns UID, e.g. for long usernames if re_number.match(user): uid = user try: user = getpwuid(int(uid)).pw_name.decode('utf-8') except KeyError: continue processes.setdefault(user, []).append((int(pid), commandline)) if commandline.startswith(cmd_login): loginProcesses.append((lstart, user, int(pid), commandline)) self.processes = processes loginPIDs = [l[2] for l in loginProcesses] if loginPIDs == self._loginPIDs: cbChanged(False) return self._loginPIDs = loginPIDs if not self._HWAddr: d = self._getOwnHWAddr() wait = defer.waitForDeferred(d) yield wait self._HWAddr = wait.getResult() log.debug('login processes: %s' % loginProcesses) users = {} for lstart, user, pid, commandline in loginProcesses: try: logintime = time.mktime(time.strptime(lstart)) except ValueError: log.err('failed to parse lstart \'%s\'' % lstart) continue client = '' display = '' ukey = (user, client, display) try: userobj = users[ukey] = self.users[ukey] except KeyError: name = getpw(user).pw_gecos.decode('utf-8', 'replace') groups = getgroups(user) userobj = users[ukey] = User() userobj.uid = getpw(user).pw_uid userobj.gid = getpw(user).pw_gid userobj.username = user userobj.client = client userobj.clientHWAddr = self._HWAddr userobj.display = display userobj.name = name userobj.groups = groups userobj.env = {"PATH":"/bin:/sbin:/usr/bin:/usr/sbin"} userobj.logintime = logintime self.users = users log.debug(users) cbChanged(True)