mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-08-20 06:35:47 +02:00
939 lines
33 KiB
Python
939 lines
33 KiB
Python
#coding: utf-8
|
|
#-------------------------------------------------------------------
|
|
# 宝塔Linux面板
|
|
#-------------------------------------------------------------------
|
|
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
|
|
#-------------------------------------------------------------------
|
|
# Author: hwliang <hwl@bt.cn>
|
|
#-------------------------------------------------------------------
|
|
|
|
# 系统日志
|
|
#------------------------------
|
|
import datetime
|
|
import json
|
|
import os
|
|
import re
|
|
import sys
|
|
import time
|
|
|
|
import public
|
|
from safeModel.base import safeBase
|
|
|
|
|
|
class main(safeBase):
|
|
|
|
def __init__(self):
|
|
ssh_cache_path = "{}/data/ssh".format(public.get_panel_path())
|
|
if not os.path.exists(ssh_cache_path): os.makedirs(ssh_cache_path,384)
|
|
|
|
|
|
#*********************************************** start ssh收费模块 ******************************************************
|
|
|
|
def get_ssh_list(self,get):
|
|
"""
|
|
@获取SSH登录
|
|
@param get:
|
|
count :数量
|
|
"""
|
|
select_pl = ['Accepted', 'Failed password for']
|
|
if hasattr(get, 'select'):
|
|
if get.select == "Accepted":
|
|
select_pl = ['Accepted']
|
|
elif get.select == "Failed":
|
|
select_pl = ['Failed password for']
|
|
|
|
p = 1
|
|
count = 20
|
|
|
|
if 'count' in get: count = int(get['count'])
|
|
if 'p' in get: p = int(get['p'])
|
|
|
|
result = []
|
|
min,max = (p -1) * count, p * count
|
|
|
|
log_list = self.get_log_byfile(self.get_ssh_log_files(get)[0], min, max,
|
|
self.__get_search_list(get, select_pl))
|
|
for log in log_list:
|
|
data = self.get_ssh_log_line(log['log'],log['time'])
|
|
if not data: continue
|
|
|
|
result.append(data)
|
|
|
|
if p < 1000000: public.set_module_logs('ssh_log','get_ssh_list')
|
|
return public.return_area(result,'address')
|
|
|
|
|
|
|
|
def get_ssh_error(self,get):
|
|
"""
|
|
@获取SSH错误次数
|
|
@param get:
|
|
count :数量
|
|
"""
|
|
p = 1
|
|
count = 20
|
|
if 'count' in get: count = int(get['count'])
|
|
if 'p' in get: p = int(get['p'])
|
|
|
|
result = []
|
|
min,max = (p -1) * count, p * count
|
|
|
|
log_list = self.get_log_byfile(self.get_ssh_log_files(get)[0],min,max,self.__get_search_list(get,['Failed password for']))
|
|
|
|
for log in log_list:
|
|
data = self.get_ssh_log_line(log['log'],log['time'])
|
|
if not data:continue
|
|
result.append(data)
|
|
|
|
if p < 1000000: public.set_module_logs('ssh_log','get_ssh_list')
|
|
return public.return_area(result,'address')
|
|
|
|
def get_ssh_success(self,get):
|
|
"""
|
|
@获取SSH登录成功次数
|
|
@param get:
|
|
count :数量
|
|
"""
|
|
p = 1
|
|
count = 20
|
|
if 'count' in get: count = int(get['count'])
|
|
if 'p' in get: p = int(get['p'])
|
|
|
|
result = []
|
|
min,max = (p -1) * count,p * count
|
|
|
|
log_list = self.get_log_byfile(self.get_ssh_log_files(get)[0],min,max,self.__get_search_list(get,['Accepted']))
|
|
|
|
for log in log_list:
|
|
data = self.get_ssh_log_line(log['log'],log['time'])
|
|
if not data: continue
|
|
|
|
result.append(data)
|
|
if p < 1000000: public.set_module_logs('ssh_log','get_ssh_list')
|
|
return public.return_area(result,'address')
|
|
|
|
|
|
def __get_search_list(self,get,slist):
|
|
"""
|
|
@组合搜索条件
|
|
return list 查询组合
|
|
status 1 增加查询条件
|
|
"""
|
|
res = []
|
|
if 'search' in get and get['search'].strip():
|
|
search = get['search'].strip()
|
|
for info in slist:
|
|
res.append(info +'&' + search)
|
|
if len(res) == 0:
|
|
return search
|
|
return res
|
|
return slist
|
|
|
|
|
|
def get_ssh_log_line(self,log,log_time):
|
|
'''
|
|
@name 获取ssh日志行
|
|
@param log<str> 日志行
|
|
@param log_time<str> 前一条记录的日志时间
|
|
'''
|
|
tmps = log.replace(' ',' ').split(' ')
|
|
if len(tmps) < 3:
|
|
return False
|
|
|
|
data = {}
|
|
data['time'] = log_time
|
|
if log.find('closed by authenticating user') != -1:
|
|
data['user'] = tmps[10]
|
|
data['address'] = tmps[11]
|
|
data['port'] = tmps[13]
|
|
else:
|
|
data['user'] = tmps[8]
|
|
data['address'] = tmps[10]
|
|
data['port'] = tmps[12]
|
|
|
|
data['status'] = 0
|
|
if log.find('Accepted') >= 0:
|
|
data['status'] = 1
|
|
return data
|
|
|
|
#*********************************************** end ssh收费模块 ******************************************************
|
|
|
|
|
|
|
|
def get_curr_log_file(self,filename,search,min,max,log_list):
|
|
"""
|
|
@name 获取当前日志文件
|
|
@param filename: 日志文件名
|
|
@param search: 搜索条件
|
|
@param min: 最小值
|
|
@param max: 最大值
|
|
@param log_list: 匹配的日志列表
|
|
"""
|
|
|
|
log_list.clear()
|
|
limit_max = '| tail -n {}'.format(max)
|
|
if search[0].find('&') >= 0: limit_max = ''
|
|
|
|
shells = [
|
|
"cat {}|grep -a 'Failed password for' |grep -v 'invalid' {} ".format(filename,limit_max), #登录失败
|
|
"cat {}|grep -a 'Accepted' {}".format(filename,limit_max),
|
|
"cat {}|grep -E 'Failed password for|Accepted|Connection closed by authenticating user' |grep -v 'invalid' {} ".format(filename,limit_max),
|
|
"cat {}|grep -a 'Connection closed by authenticating user' |grep -a 'preauth' {} ".format(filename,
|
|
limit_max)
|
|
# 登录失败
|
|
]
|
|
if filename == 'journalctl':
|
|
shells = [
|
|
"journalctl -u ssh --no-pager|grep -a 'Failed password for' |grep -v 'invalid' {} ".format(limit_max),
|
|
# 登录失败
|
|
"journalctl -u ssh --no-pager|grep -a 'Accepted' {}".format(limit_max),
|
|
"journalctl -u ssh --no-pager|grep -E 'Failed password for|Accepted|Connection closed by authenticating user' |grep -v 'invalid' {} ".format(
|
|
limit_max),
|
|
"journalctl -u ssh --no-pager|grep -a 'Connection closed by authenticating user' |grep -a 'preauth' {} ".format(
|
|
limit_max)
|
|
# 登录失败
|
|
]
|
|
if len(search) == 1:
|
|
if search[0].find('Accepted') >= 0:
|
|
shells = [shells[1]]
|
|
else:
|
|
shells = [shells[0],shells[3]]
|
|
else:
|
|
shells = [shells[2]]
|
|
|
|
result = []
|
|
for shell in shells:
|
|
|
|
res = public.ExecShell(shell)[0].strip().split('\n')
|
|
res.reverse()
|
|
for log in res:
|
|
result.append(log)
|
|
|
|
find_idx = 0
|
|
log_time = 0
|
|
limit = max - min
|
|
for log in result:
|
|
log_time = self.get_log_pre_time(filename,log,log_time)
|
|
if len(log_list) >= limit:
|
|
break
|
|
|
|
if self.__find_line_str(log,search):
|
|
|
|
find_idx += 1
|
|
if find_idx > min:
|
|
log = public.xssencode2(log.replace(' ',' '))
|
|
log_list.append({'log':log,'time':log_time})
|
|
|
|
return find_idx
|
|
|
|
|
|
def get_sys_datetime(self,pre_time,log_time):
|
|
"""
|
|
@name 对比日志时间,日志时间保存年份,日志时间比前一条时间大,则判断为上一年,
|
|
@param pre_time 上一条日志时间
|
|
@param log_time 当前日志时间
|
|
@return 当前日志时间(校准年份)
|
|
"""
|
|
|
|
if type(log_time) == str:
|
|
log_time = self.__get_to_date(log_time)
|
|
|
|
if type(pre_time) == str:
|
|
pre_time = self.__get_to_date(pre_time)
|
|
|
|
if (log_time > pre_time and pre_time > 0) or log_time >= time.time():
|
|
d = datetime.datetime.strptime(public.format_date(times =log_time), "%Y-%m-%d %H:%M:%S")
|
|
n_date = self.__get_to_date('{}-{}-{} {}:{}:{}'.format(d.year-1,d.month,d.day,d.hour,d.minute,d.second))
|
|
return public.format_date(times=n_date)
|
|
if type(log_time) == int:
|
|
return public.format_date(times=log_time)
|
|
return log_time
|
|
|
|
|
|
|
|
def get_log_pre_time(self,log_file,_line,pre_time):
|
|
"""
|
|
@name 计算上次日志时间
|
|
@param log_file<str> 日志文件
|
|
@param _line<str> 日志行
|
|
@param pre_time<str> 上次日志时间
|
|
@auther cjxin
|
|
"""
|
|
log_time = 0
|
|
if _line[:3] in self._months:
|
|
log_time = self.to_date4(_line[:16].strip())
|
|
elif _line[:2] in ['19','20','21','22']:
|
|
log_time = _line[:19].strip()
|
|
elif log_file.find('alternatives') >= 0:
|
|
_tmp = _line.split(": ")
|
|
_last = _tmp[0].split(" ")
|
|
log_time = ' '.join(_last[1:]).strip()
|
|
|
|
log_time = self.get_sys_datetime(pre_time,log_time)
|
|
return log_time
|
|
|
|
|
|
def get_user(self):
|
|
'''
|
|
@name 获取系统用户名
|
|
:return:
|
|
'''
|
|
pass_file = public.readFile("/etc/passwd")
|
|
pass_file = pass_file.split('\n')
|
|
user_list = []
|
|
for p in pass_file:
|
|
p = p.split(':', 1)
|
|
user_list.append(p[0])
|
|
return user_list
|
|
|
|
|
|
def get_log_byfile(self,sfile,min_num,max_num,search = None):
|
|
"""
|
|
@name 获取日志文件的日志
|
|
@param sfile:日志文件
|
|
@param min_num:起始行数
|
|
@param max_num:结束行数
|
|
@param search:搜索关键字
|
|
"""
|
|
log_list = []
|
|
h_find = None
|
|
#获取归档文件列表
|
|
for info in self.get_sys_logfiles(None):
|
|
if info['log_file'] == sfile:
|
|
h_find = info
|
|
break
|
|
if os.path.exists('/etc/debian_version'):
|
|
version = public.readFile('/etc/debian_version').strip()
|
|
if 'bookworm' in version or 'jammy' in version or 'impish' in version:
|
|
version = 12
|
|
else:
|
|
try:
|
|
version = float(version)
|
|
except:
|
|
version = 11
|
|
if version >= 12:
|
|
h_find = {'log_file': 'journalctl', "list": [], 'uptime': time.time(), 'title': '授权日志',
|
|
'size': 10000}
|
|
if not h_find:
|
|
return log_list
|
|
|
|
#获取遍历文件列表
|
|
file_list = [h_find['log_file']]
|
|
for info in h_find['list']:
|
|
file_list.append(info['log_file'])
|
|
|
|
find_idx = 0
|
|
log_time = 0
|
|
limit = max_num - min_num
|
|
user_list = self.get_user()
|
|
|
|
for filename in file_list:
|
|
#处理最新文件
|
|
|
|
if filename in ['/var/log/secure', '/var/log/auth.log', 'journalctl'] and search:
|
|
find_idx = self.get_curr_log_file(filename,search,min_num,max_num,log_list)
|
|
continue
|
|
|
|
p = 0 #分页计数器
|
|
next_file = False
|
|
sfile = filename
|
|
if filename[-3:] in ['.gz','.xz']: sfile = sfile[:-3]
|
|
|
|
check_file,is_cache = self.__check_other_search(filename,search)
|
|
if check_file:
|
|
|
|
cache_path = '{}/data/ssh/{}{}'.format(public.get_panel_path(),os.path.basename(sfile),check_file)
|
|
if not os.path.exists(cache_path):
|
|
self.__set_ssh_log(filename,check_file)
|
|
filename = cache_path
|
|
|
|
#数据不够,则解压归档文件进行查询
|
|
if filename[-3:] in ['.gz','.xz']:
|
|
public.ExecShell("gunzip -c " + filename + " > " + filename[:-3])
|
|
filename = filename[:-3]
|
|
|
|
while not next_file:
|
|
if not os.path.exists(filename): continue # 文件不存在?
|
|
if len(log_list) >= limit or os.path.getsize(filename) == 0:
|
|
break
|
|
p += 1
|
|
|
|
#public.print_log('读取文件:{},第{}页'.format(filename,p))
|
|
#每次读取10000行,不足10000行跳转下个文件
|
|
result = self.GetNumLines(filename,10001,p).split("\n")
|
|
if len(result) < 10000:
|
|
next_file = True
|
|
|
|
result.reverse()
|
|
for _line in result:
|
|
if not _line.strip(): continue
|
|
|
|
log_time = self.get_log_pre_time(filename,_line,log_time)
|
|
#处理搜索关键词
|
|
is_search = False
|
|
if self.__find_line_str(_line,search):
|
|
is_search = True
|
|
|
|
#读取数量超过最大值,跳出
|
|
if len(log_list) >= limit:
|
|
break
|
|
|
|
if is_search:
|
|
find_idx += 1
|
|
if find_idx > min_num:
|
|
_line = public.xssencode2(_line.replace(' ',' '))
|
|
# 解决SSH登录日志搜索ip或用户名不准确的情况
|
|
if type(search) == list and len(search[0].split("&")) > 1:
|
|
rep_str = search[0].split("&")
|
|
rep = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
|
|
re_result = re.search(rep, _line).group()
|
|
if rep_str[1] in re_result:
|
|
log_list.append({'log': _line, 'time': log_time})
|
|
elif rep_str[1] in user_list:
|
|
log_list.append({'log': _line, 'time': log_time})
|
|
else:
|
|
log_list.append({'log':_line,'time':log_time})
|
|
|
|
return log_list
|
|
|
|
def __set_ssh_log(self,filename,check_file):
|
|
"""
|
|
@name 缓存SSH登录日志
|
|
@param filename<str> 缓存文件
|
|
@param check_file<list> 文件类型
|
|
"""
|
|
|
|
cache_path = '{}/data/ssh/{}{}'.format(public.get_panel_path(),os.path.basename(filename),check_file)
|
|
|
|
if check_file == '_success':
|
|
if filename == "journalctl":
|
|
shell = "journalctl -u ssh --no-pager|grep -a 'Accepted'"
|
|
else:
|
|
shell = "cat {}|grep -a 'Accepted'".format(filename)
|
|
elif check_file == '_error':
|
|
if filename == "journalctl":
|
|
shell = "journalctl -u ssh --no-pager|grep -E 'Failed password for|Connection closed by authenticating user' |grep -v 'invalid'"
|
|
else:
|
|
shell = "cat {}|grep -E 'Failed password for|Connection closed by authenticating user' |grep -v 'invalid'".format(
|
|
filename)
|
|
else:
|
|
if filename == "journalctl":
|
|
shell = "journalctl -u ssh --no-pager|grep -E 'Failed password for|Accepted|Connection closed by authenticating user' |grep -v 'invalid'"
|
|
else:
|
|
shell = "cat {}|grep -E 'Failed password for|Accepted|Connection closed by authenticating user' |grep -v 'invalid'".format(filename)
|
|
|
|
if not os.path.exists(cache_path):
|
|
res = public.ExecShell(shell)[0]
|
|
public.writeFile(cache_path,res)
|
|
return True
|
|
|
|
def __check_other_search(self,filename,search):
|
|
"""
|
|
@检测是否需要缓存ssh登录日志
|
|
@filename<str> 文件名
|
|
@search<str> 搜索关键字
|
|
"""
|
|
if not search: return False,False
|
|
|
|
if filename.find('secure-') >= 0 or filename.find('auth.log.') >= 0:
|
|
res = search
|
|
if type(search) == list:
|
|
res = ' '.join(search)
|
|
|
|
is_cache = False
|
|
if res.find('&') == -1: is_cache = True
|
|
|
|
if len(search) == 2:
|
|
return '_all',is_cache
|
|
if search[0].find('Accepted') >= 0:
|
|
return '_success',is_cache
|
|
return '_error',is_cache
|
|
return False,False
|
|
|
|
def __find_line_str(self,__line,find_str):
|
|
"""
|
|
@ 批量搜索文件
|
|
@ __line<str> 文件行
|
|
@ find_str<str> 搜索关键字
|
|
"""
|
|
if type(find_str) == list:
|
|
if len(find_str) == 0:
|
|
return True
|
|
for search in find_str:
|
|
if self.__find_str(__line,search.strip()):
|
|
return True
|
|
return False
|
|
else:
|
|
if find_str:
|
|
return self.__find_str(__line,find_str.strip())
|
|
return True
|
|
|
|
def __find_str(self,_line,find_str):
|
|
"""
|
|
@查找关键词
|
|
@_line<str> 文件行
|
|
@find_str<str> 搜索关键字
|
|
"""
|
|
is_num = 0
|
|
slist = find_str.split("&")
|
|
for search in slist:
|
|
if search == 'Failed password for':
|
|
#兼容多个系统的登录失败
|
|
if _line.find(search) >= 0 and _line.find('invalid') == -1:
|
|
is_num += 1
|
|
elif _line.find('Connection closed by authenticating user') >= 0 and _line.find('preauth') >= 0: #debian系统使用宝塔SSH终端登录失败
|
|
is_num += 1
|
|
else:
|
|
if _line.find(search) >= 0:
|
|
is_num += 1
|
|
|
|
if is_num == len(slist):
|
|
return True
|
|
return False
|
|
|
|
|
|
def get_log_title(self,log_name):
|
|
'''
|
|
@name 获取日志标题
|
|
@author hwliang<2021-09-03>
|
|
@param log_name<string> 日志名称
|
|
@return <string> 日志标题
|
|
'''
|
|
log_name = log_name.replace('.1','')
|
|
if log_name in ['auth.log','secure'] or log_name.find('auth.') == 0:
|
|
return 'Authorization log'
|
|
if log_name in ['dmesg'] or log_name.find('dmesg') == 0:
|
|
return 'kernel buffer log'
|
|
if log_name in ['syslog'] or log_name.find('syslog') == 0:
|
|
return 'System warning/error log'
|
|
if log_name in ['btmp']:
|
|
return 'failed login record'
|
|
if log_name in ['utmp','wtmp']:
|
|
return 'Logon and restart records'
|
|
if log_name in ['lastlog']:
|
|
return 'User last logged in'
|
|
if log_name in ['yum.log']:
|
|
return 'yum package manager log'
|
|
if log_name in ['anaconda.log']:
|
|
return 'Anaconda log'
|
|
if log_name in ['dpkg.log']:
|
|
return 'dpkg package manager log'
|
|
if log_name in ['daemon.log']:
|
|
return 'System background daemon log'
|
|
if log_name in ['boot.log']:
|
|
return 'Boot oog'
|
|
if log_name in ['kern.log']:
|
|
return 'Kern log'
|
|
if log_name in ['maillog','mail.log']:
|
|
return 'Mail log'
|
|
if log_name.find('Xorg') == 0:
|
|
return 'Xorg log'
|
|
if log_name in ['cron.log']:
|
|
return 'Scheduled task log'
|
|
if log_name in ['alternatives.log']:
|
|
return 'Update alternate information'
|
|
if log_name in ['debug']:
|
|
return 'Debug log'
|
|
if log_name.find('apt') == 0:
|
|
return 'apt-get related logs'
|
|
if log_name.find('installer') == 0:
|
|
return 'System installation related logs'
|
|
if log_name in ['messages']:
|
|
return 'Comprehensive log'
|
|
return '{} log'.format(log_name.split('.')[0])
|
|
|
|
|
|
def get_history_filename(self,filepath):
|
|
'''
|
|
@name 获取归档文件名称
|
|
@filepath<string> 文件路径
|
|
'''
|
|
log_name = os.path.basename(filepath)
|
|
|
|
#归档压缩文件,auth.log.1.gz
|
|
if filepath[-3:] in ['.gz','.xz']:
|
|
log_file = filepath[:-3]
|
|
if os.path.exists(log_file):
|
|
return False
|
|
log_name = os.path.basename(log_file)
|
|
|
|
#处理auth.log-20221024
|
|
if re.search('-(\d{8})',log_name):
|
|
arrs = log_name.split('-')
|
|
arrs = arrs[0 : len(arrs)-1]
|
|
return '-'.join(arrs)
|
|
#处理auth.log.1
|
|
if re.search('.\d{1,10}$',log_name):
|
|
arrs = log_name.split('.')
|
|
arrs = arrs[0 : len(arrs)-1]
|
|
return '.'.join(arrs)
|
|
return log_name
|
|
|
|
|
|
|
|
def get_sys_logfiles(self,get):
|
|
'''
|
|
@name 获取系统日志文件列表
|
|
@author hwliang<2021-09-02>
|
|
@param get<dict_obj>
|
|
@return list
|
|
'''
|
|
res = {}
|
|
log_dir = '/var/log'
|
|
for log_file in os.listdir(log_dir):
|
|
if log_file in ['.','..','faillog','fontconfig.log','unattended-upgrades','tallylog']: continue
|
|
|
|
filename = os.path.join(log_dir,log_file)
|
|
if os.path.isfile(filename):
|
|
#归档文件原名
|
|
log_name = self.get_history_filename(filename)
|
|
if not log_name: continue
|
|
|
|
if not log_name in res:
|
|
filepath = os.path.join(log_dir,log_name)
|
|
if not os.path.exists(filepath): continue
|
|
|
|
res[log_name] = {
|
|
'name':log_name,
|
|
'log_file':filepath,
|
|
'size':os.path.getsize(filepath),
|
|
'title': self.get_log_title(log_name),
|
|
'uptime': os.path.getmtime(filepath),
|
|
'list':[]
|
|
}
|
|
|
|
if log_name != log_file:
|
|
res[log_name]['list'].append({
|
|
'name':log_file,
|
|
'size':os.path.getsize(filename),
|
|
'uptime': os.path.getmtime(filename),
|
|
'log_file':filename
|
|
})
|
|
else:
|
|
for next_name in os.listdir(filename):
|
|
next_file = os.path.join(filename,next_name)
|
|
if not os.path.isfile(next_file): continue
|
|
|
|
log_name = self.get_history_filename(next_file)
|
|
if not log_name: continue
|
|
|
|
if not log_name in res:
|
|
|
|
filepath = os.path.join(filename,log_name)
|
|
if not os.path.exists(filepath): continue
|
|
|
|
res[log_name] = {
|
|
'name':log_name,
|
|
'log_file':filepath,
|
|
'size':os.path.getsize(filepath),
|
|
'title': self.get_log_title(log_name),
|
|
'uptime': os.path.getmtime(filepath),
|
|
'list':[]
|
|
}
|
|
|
|
if log_name != next_name:
|
|
res[log_name]['list'].append({
|
|
'name':next_name,
|
|
'size':os.path.getsize(next_file),
|
|
'uptime': os.path.getmtime(next_file),
|
|
'log_file':next_file
|
|
})
|
|
|
|
|
|
log_files = []
|
|
for key in res:
|
|
res[key]['list'] = sorted(res[key]['list'],key=lambda x:x['name'],reverse=True)
|
|
log_files.append(res[key])
|
|
log_files = sorted(log_files,key=lambda x:x['name'],reverse=True)
|
|
return log_files
|
|
|
|
|
|
def get_lastlog(self,get):
|
|
'''
|
|
@name 获取lastlog日志
|
|
@author hwliang<2021-09-02>
|
|
@param get<dict_obj>
|
|
@return list
|
|
'''
|
|
cmd = '''LANG=en_US.UTF-8
|
|
lastlog|grep -v Username'''
|
|
result = public.ExecShell(cmd)
|
|
lastlog_list = []
|
|
|
|
p = 1
|
|
count = 20
|
|
if 'count' in get: count = int(get['count'])
|
|
if 'p' in get: p = int(get['p'])
|
|
|
|
search = ''
|
|
if 'search' in get:
|
|
search = get.search
|
|
|
|
idx = 0
|
|
min,max = (p -1) * count, p * count
|
|
for _line in result[0].split("\n"):
|
|
if not _line: continue
|
|
if search and _line.find(search) == -1: continue
|
|
|
|
_line = public.xssencode2(_line)
|
|
tmp = {}
|
|
sp_arr = _line.split()
|
|
tmp['User'] = sp_arr[0]
|
|
# tmp['_line'] = _line
|
|
if _line.find('Never logged in') != -1:
|
|
tmp['last login time'] = '0'
|
|
tmp['Last login source'] = '-'
|
|
tmp['Last login port'] = '-'
|
|
|
|
else:
|
|
tmp['last login time'] = sp_arr[2]
|
|
tmp['Last login source'] = sp_arr[1]
|
|
tmp['Last login port'] = self.to_date2(' '.join(sp_arr[3:]))
|
|
|
|
if idx >= min and idx < max:
|
|
lastlog_list.append(tmp)
|
|
|
|
idx += 1
|
|
lastlog_list = sorted(lastlog_list,key=lambda x:x['Last login port'],reverse=True)
|
|
for i in range(len(lastlog_list)):
|
|
if lastlog_list[i]['Last login port'] == '0': lastlog_list[i]['Last login port'] = 'never logged in'
|
|
return lastlog_list
|
|
|
|
|
|
def get_last(self,get):
|
|
'''
|
|
@name 获取用户会话日志
|
|
@author hwliang<2021-09-02>
|
|
@param get<dict_obj>
|
|
@return list
|
|
'''
|
|
cmd = '''LANG=en_US.UTF-8
|
|
last -n 1000 -x -f {}|grep -v 127.0.0.1|grep -v " begins"'''.format(get.log_name)
|
|
result = public.ExecShell(cmd)
|
|
lastlog_list = []
|
|
|
|
search = ''
|
|
if 'search' in get:
|
|
search = get.search
|
|
|
|
p = 1
|
|
count = 20
|
|
if 'count' in get: count = int(get['count'])
|
|
if 'p' in get: p = int(get['p'])
|
|
|
|
idx = 0
|
|
min,max = (p -1) * count, p * count
|
|
|
|
for _line in result[0].split("\n"):
|
|
if not _line: continue
|
|
if search and _line.find(search) == -1: continue
|
|
|
|
_line = public.xssencode2(_line)
|
|
tmp = {}
|
|
sp_arr = _line.split()
|
|
tmp['User'] = sp_arr[0]
|
|
if sp_arr[0] == 'runlevel':
|
|
tmp['Source'] = sp_arr[4]
|
|
tmp['Port'] = ' '.join(sp_arr[1:4])
|
|
tmp['Time'] = self.to_date3(' '.join(sp_arr[5:])) + ' ' +' '.join(sp_arr[-2:])
|
|
elif sp_arr[0] in ['reboot','shutdown']:
|
|
tmp['Source'] = sp_arr[3]
|
|
tmp['Port'] = ' '.join(sp_arr[1:3])
|
|
if sp_arr[-3] == '-':
|
|
tmp['Time'] = self.to_date3(' '.join(sp_arr[4:])) + ' ' +' '.join(sp_arr[-3:])
|
|
else:
|
|
tmp['Time'] = self.to_date3(' '.join(sp_arr[4:])) + ' ' +' '.join(sp_arr[-2:])
|
|
elif sp_arr[1] in ['tty1','tty','tty2','tty3','hvc0','hvc1','hvc2'] or len(sp_arr) == 9:
|
|
tmp['Source'] = ''
|
|
tmp['Port'] = sp_arr[1]
|
|
tmp['Time'] = self.to_date3(' '.join(sp_arr[2:])) + ' ' +' '.join(sp_arr[-3:])
|
|
else:
|
|
tmp['Source'] = sp_arr[2]
|
|
tmp['Port'] = sp_arr[1]
|
|
tmp['Time'] = self.to_date3(' '.join(sp_arr[3:])) + ' ' +' '.join(sp_arr[-3:])
|
|
if idx >= min and idx < max:
|
|
lastlog_list.append(tmp)
|
|
idx += 1
|
|
# lastlog_list = sorted(lastlog_list,key=lambda x:x['时间'],reverse=True)
|
|
return lastlog_list
|
|
|
|
|
|
|
|
def __get_to_date(self,times):
|
|
"""
|
|
日期转时间戳
|
|
"""
|
|
try:
|
|
return int(time.mktime(time.strptime(times, "%Y-%m-%d %H:%M:%S")))
|
|
except:
|
|
try:
|
|
return int(time.mktime(time.strptime(times, "%Y/%m/%d %H:%M:%S")))
|
|
except:
|
|
return 0
|
|
|
|
|
|
|
|
def get_sys_log(self,get):
|
|
'''
|
|
@name 获取指定系统日志
|
|
@author hwliang<2021-09-02>
|
|
@param get<dict_obj>
|
|
@return list
|
|
'''
|
|
|
|
log_file = get.log_name
|
|
|
|
p,limit,search = 1,5,''
|
|
if 'p' in get: p = int(get.p)
|
|
if 'limit' in get: limit = int(get.limit)
|
|
if 'search' in get: search = get.search
|
|
|
|
sfile_name = os.path.basename(get.log_name)
|
|
if sfile_name in ['wtmp','btmp','utmp'] :
|
|
return self.get_last(get)
|
|
|
|
if sfile_name in ['lastlog']:
|
|
return self.get_lastlog(get)
|
|
|
|
if get.log_name.find('sa/sa') >= 0:
|
|
if get.log_name.find('sa/sar') == -1:
|
|
return public.xssencode2(public.ExecShell("sar -f /var/log/{}".format(get.log_name))[0])
|
|
|
|
is_string = True
|
|
|
|
result = []
|
|
min,max = (p-1) * limit , p * limit #最小值,最大值
|
|
log_list = self.get_log_byfile(log_file,min,max,search)
|
|
|
|
for info in log_list:
|
|
_line = info['log']
|
|
if _line[:3] in self._months:
|
|
_tmps = _line.split(' ')
|
|
_msg = ' '.join(_tmps[3:])
|
|
_tmp = _msg.split(": ")
|
|
_act = ''
|
|
if len(_tmp) > 1:
|
|
_act = _tmp[0]
|
|
_msg = _tmp[1]
|
|
else:
|
|
_msg = _tmp[0]
|
|
_line = { "Time": info['time'], "Role":_act, "Even":_msg }
|
|
is_string = False
|
|
elif _line[:2] in ['19','20','21','22']:
|
|
_msg = _line[19:]
|
|
_tmp = _msg.split(" ")
|
|
_act = _tmp[1]
|
|
_msg = ' '.join(_tmp[2:])
|
|
_line = { "Time":info['time'], "Role":_act, "Even":_msg }
|
|
is_string = False
|
|
elif log_file.find('alternatives') >= 0:
|
|
_tmp = _line.split(": ")
|
|
_last = _tmp[0].split(" ")
|
|
_act = _last[0]
|
|
_msg = ' '.join(_tmp[1:])
|
|
_line = { "Time":info['time'], "Role":_act, "Even":_msg }
|
|
is_string = False
|
|
else:
|
|
if not is_string:
|
|
if type(_line) != dict: continue
|
|
|
|
result.append(_line)
|
|
|
|
public.set_module_logs('sys_log','get_sys_log')
|
|
try:
|
|
_string = []
|
|
_dict = []
|
|
_list = []
|
|
for _line in result:
|
|
if isinstance(_line,str):
|
|
_string.append(_line.strip())
|
|
elif isinstance(_line,dict):
|
|
_dict.append(_line)
|
|
elif isinstance(_line,list):
|
|
_list.append(_line)
|
|
else:
|
|
continue
|
|
_str_len = len(_string)
|
|
_dict_len = len(_dict)
|
|
_list_len = len(_list)
|
|
if _str_len >= _dict_len + _list_len:
|
|
return _string
|
|
elif _dict_len >= _str_len + _list_len:
|
|
return _dict
|
|
else:
|
|
return _list
|
|
except:
|
|
return '\n'.join(result)
|
|
|
|
|
|
|
|
#取文件指定尾行数
|
|
def GetNumLines(self,path,num,p=1):
|
|
pyVersion = sys.version_info[0]
|
|
max_len = 1024*1024 * 2
|
|
try:
|
|
from cgi import html
|
|
if not os.path.exists(path): return ""
|
|
start_line = (p - 1) * num
|
|
count = start_line + num
|
|
fp = open(path,'rb')
|
|
buf = ""
|
|
fp.seek(-1, 2)
|
|
if fp.read(1) == "\n": fp.seek(-1, 2)
|
|
data = []
|
|
total_len = 0
|
|
b = True
|
|
n = 0
|
|
for i in range(count):
|
|
while True:
|
|
newline_pos = str.rfind(str(buf), "\n")
|
|
pos = fp.tell()
|
|
if newline_pos != -1:
|
|
if n >= start_line:
|
|
line = buf[newline_pos + 1:]
|
|
line_len = len(line)
|
|
total_len += line_len
|
|
sp_len = total_len - max_len
|
|
if sp_len > 0:
|
|
line = line[sp_len:]
|
|
try:
|
|
data.insert(0,line)
|
|
except: pass
|
|
buf = buf[:newline_pos]
|
|
n += 1
|
|
break
|
|
else:
|
|
if pos == 0:
|
|
b = False
|
|
break
|
|
to_read = min(4096, pos)
|
|
fp.seek(-to_read, 1)
|
|
t_buf = fp.read(to_read)
|
|
if pyVersion == 3:
|
|
t_buf = t_buf.decode('utf-8')
|
|
|
|
buf = t_buf + buf
|
|
fp.seek(-to_read, 1)
|
|
if pos - to_read == 0:
|
|
buf = "\n" + buf
|
|
if total_len >= max_len: break
|
|
if not b: break
|
|
fp.close()
|
|
result = "\n".join(data)
|
|
if not result: raise Exception('null')
|
|
except:
|
|
result = public.ExecShell("tail -n {} {}".format(num,path))[0]
|
|
if len(result) > max_len:
|
|
result = result[-max_len:]
|
|
|
|
try:
|
|
try:
|
|
result = json.dumps(result)
|
|
return json.loads(result).strip()
|
|
except:
|
|
if pyVersion == 2:
|
|
result = result.decode('utf8',errors='ignore')
|
|
else:
|
|
result = result.encode('utf-8',errors='ignore').decode("utf-8",errors="ignore")
|
|
return result.strip()
|
|
except: return "" |