Add last command

This commit is contained in:
Oleksii Shevchuk 2017-03-20 18:06:50 +02:00
parent bdf556fb31
commit c88b5a20e8
3 changed files with 240 additions and 1 deletions

99
pupy/modules/last.py Normal file
View File

@ -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)

View File

@ -7,6 +7,10 @@ import sys
import os import os
import time import time
import socket import socket
import pwd
import struct
import netaddr
import time
families = { families = {
v:k[3:] for k,v in socket.__dict__.iteritems() if k.startswith('AF_') 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__': 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',
)

View File

@ -81,8 +81,12 @@ def colorize(s, color):
res="\033[34m"+s+COLOR_STOP res="\033[34m"+s+COLOR_STOP
elif color.lower()=="red": elif color.lower()=="red":
res="\033[31m"+s+COLOR_STOP res="\033[31m"+s+COLOR_STOP
elif color.lower()=="lightred":
res="\033[31;1m"+s+COLOR_STOP
elif color.lower()=="green": elif color.lower()=="green":
res="\033[32m"+s+COLOR_STOP res="\033[32m"+s+COLOR_STOP
elif color.lower()=="lightgreen":
res="\033[32;1m"+s+COLOR_STOP
elif color.lower()=="yellow": elif color.lower()=="yellow":
res="\033[33m"+s+COLOR_STOP res="\033[33m"+s+COLOR_STOP
elif color.lower()=="magenta": elif color.lower()=="magenta":