diff --git a/pupy/modules/last.py b/pupy/modules/last.py new file mode 100644 index 00000000..09f62172 --- /dev/null +++ b/pupy/modules/last.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +from pupylib.PupyModule import * +from pupylib.PupyCmd import PupyCmd +from pupylib.utils.rpyc_utils import obtain +from pupylib.utils.term import colorize +from datetime import datetime, timedelta + +import logging + +__class_name__="LastModule" + +@config(cat='admin', compat=['linux']) +class LastModule(PupyModule): + """ list terminal sessions """ + + dependencies = [ 'pupyps' ] + is_module=False + + def init_argparse(self): + self.arg_parser = PupyArgumentParser(prog="last", description=self.__doc__) + duration = self.arg_parser.add_mutually_exclusive_group() + duration.add_argument('-n', '--lines', type=int, help='Get only (n) last records') + duration.add_argument('-d', '--days', type=int, help='Get only records for last (n) days') + filtering = self.arg_parser.add_mutually_exclusive_group() + filtering.add_argument('-x', '--exclude', nargs='+', help='Hide users/hosts/ips') + filtering.add_argument('-i', '--include', nargs='+', help='Show users/hosts/ips') + + def run(self, args): + try: + data = obtain(self.client.conn.modules.pupyps.wtmp()) + tablein = [] + + now = data['now'] + output = [] + + for record in data['records']: + if args.days and ( record['start'] + args.days*24*60*60 < now): + break + + if args.exclude and any([x in args.exclude for x in record.itervalues()]): + continue + + if args.include and not any([x in args.include for x in record.itervalues()]): + continue + + if record['type'] not in ('boot', 'process'): + continue + + color = '' + if record['end'] == -1: + if record['user'] == 'root': + color = 'lightred' + elif record['duration'] < 24*60*60: + color = 'lightgreen' + elif record['duration'] > 7*24*60*60: + color = 'cyan' + elif record['user'] == 'root': + color = 'yellow' + elif record['ip'] != '0.0.0.0': + color = 'cyan' + elif record['end'] > 24*60*60: + color = 'grey' + elif record['end'] > 7*24*60*60: + color = 'darkgrey' + + if record['type'] == 'boot': + color = 'yellow' + + record['start'] = datetime.fromtimestamp(record['start']) + record['end'] = datetime.fromtimestamp( + record['end'] + ) if record['end'] != -1 else 'logged in' + record['duration'] = timedelta(seconds=int(record['duration'])) + record['ip'] = '' if record['ip'] == '0.0.0.0' else record['ip'] + + if record['type'] == 'boot' and record['end'] == 'logged in': + record['end'] = 'up' + + for f in record: + record[f] = colorize('{}'.format(record[f]), color) + + output.append(record) + + if args.lines and len(output) >= args.lines: + break + + + columns = [ + x for x in [ + 'user', 'line', 'pid' ,'host', 'ip', 'start', 'end', 'duration' + ] if any([ bool(y[x]) for y in output ]) + ] + + self.stdout.write( + PupyCmd.table_format(output, wl=columns) + ) + + except Exception, e: + logging.exception(e) diff --git a/pupy/packages/all/pupyps.py b/pupy/packages/all/pupyps.py index a37b3854..8f2b282e 100644 --- a/pupy/packages/all/pupyps.py +++ b/pupy/packages/all/pupyps.py @@ -7,6 +7,10 @@ import sys import os import time import socket +import pwd +import struct +import netaddr +import time families = { v:k[3:] for k,v in socket.__dict__.iteritems() if k.startswith('AF_') @@ -187,5 +191,137 @@ def interfaces(): } } +def cstring(string): + return string[:string.find('\x00')] + +def convrecord(item): + return item if type(item) in (int,long) else cstring(item) + +def wtmp(input='/var/log/wtmp'): + retval = [] + WTmp = struct.Struct('hi32s4s32s256shhiii4I20s') + + login_type = { + 0: None, + 1: 'runlevel', + 2: 'boot', + 3: 'time_new', + 4: 'time_old', + 5: 'init', + 6: 'session', + 7: 'process', + 8: 'terminated', + 9: 'accounting', + } + + now = time.time() + + with open('/var/log/wtmp') as wtmp: + while True: + data = wtmp.read(WTmp.size) + if not data or len(data) != WTmp.size: + break + + items = [ convrecord(x) for x in WTmp.unpack(data) ] + itype = login_type[items[0]] + if not itype: + continue + + if itype in ('runlevel', 'terminated'): + for record in retval: + if record['end'] == -1: + if itype == 'runlevel' and items[4] == 'shutdown': + record['end'] = items[9] + record['duration'] = record['end'] - record['start'] + elif itype == 'terminated': + if items[1] == 0: + if record['line'] == items[2]: + record['end'] = items[9] + record['duration'] = record['end'] - record['start'] + break + else: + if record['type'] in ('session', 'process') and record['pid'] == items[1]: + record['end'] = items[9] + record['duration'] = record['end'] - record['start'] + record['termination'] = items[6] + record['exit'] = items[7] + break + + if record['type'] == 'runlevel' and record['user'] == 'shutdown': + break + + ipbin = items[11:15] + if all([x==0 for x in ipbin[1:]]): + ipaddr = str(netaddr.IPAddress(socket.htonl(ipbin[0]))) + else: + data = struct.pack('IIII', *ipbin).encode('hex') + ipaddr = '' + while data is not '': + ipaddr = ipaddr + ':' + ipaddr = ipaddr + data[:4] + data = data[4:] + ipaddr = str(netaddr.IPAddress(ipaddr[1:])) + + retval.insert(0, { + 'type': itype, + 'pid': items[1], + 'line': items[2], + 'id': items[3], + 'user': items[4], + 'host': items[5], + 'termination': items[6], + 'exit': items[7], + 'session': items[8], + 'start': items[9], + 'ip': ipaddr, + 'end': -1, + 'duration': now - items[9] + }) + + return { + 'now': now, + 'records': retval + } + +def lastlog(): + result = {} + LastLog = struct.Struct('I32s256s') + + with open('/var/log/lastlog') as lastlog: + uid = 0 + while True: + data = lastlog.read(LastLog.size) + if not data or len(data) != LastLog.size: + break + + time, line, host = LastLog.unpack(data) + line = cstring(line) + host = cstring(host) + if time: + try: + name = pwd.getpwuid(uid).pw_name + except: + name = uid + + result[name] = { + 'time': time, + 'line': line, + 'host': host, + } + uid += 1 + + return result + if __name__ == '__main__': - print psinfo([os.getpid()])[os.getpid()].keys() + import datetime + for result in wtmp(): + if result['type'] in ('process', 'boot'): + print '{:12s} {:5d} {:7} {:8s} {:8s} {:16s} {:3} {:3} {} - {}'.format( + result['type'], + result['pid'], + result['id'], + result['user'], result['line'], result['host'], + result['termination'], result['exit'], + datetime.datetime.fromtimestamp(result['start']), + datetime.datetime.fromtimestamp(result['end']) if result['end'] != -1 else 'logged in', + ) diff --git a/pupy/pupylib/utils/term.py b/pupy/pupylib/utils/term.py index 21c47908..9073b54e 100644 --- a/pupy/pupylib/utils/term.py +++ b/pupy/pupylib/utils/term.py @@ -81,8 +81,12 @@ def colorize(s, color): res="\033[34m"+s+COLOR_STOP elif color.lower()=="red": res="\033[31m"+s+COLOR_STOP + elif color.lower()=="lightred": + res="\033[31;1m"+s+COLOR_STOP elif color.lower()=="green": res="\033[32m"+s+COLOR_STOP + elif color.lower()=="lightgreen": + res="\033[32;1m"+s+COLOR_STOP elif color.lower()=="yellow": res="\033[33m"+s+COLOR_STOP elif color.lower()=="magenta":