mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-08-17 21:25:47 +02:00
947 lines
40 KiB
Python
947 lines
40 KiB
Python
#coding: utf-8
|
||
# -------------------------------------------------------------------
|
||
# aaPanel
|
||
# -------------------------------------------------------------------
|
||
# Copyright (c) 2019-2099 aaPanel(www.aapanel.com) All rights reserved.
|
||
# -------------------------------------------------------------------
|
||
# Author: hwliang <hwl@aapanel.com>
|
||
# -------------------------------------------------------------------
|
||
|
||
# ------------------------------
|
||
# 消息队列
|
||
# ------------------------------
|
||
import json
|
||
import time
|
||
import public
|
||
import sys
|
||
import os
|
||
import re
|
||
import tarfile
|
||
from public.validate import Param
|
||
os.chdir('/www/server/panel')
|
||
if not 'class/' in sys.path:
|
||
sys.path.insert(0,'class/')
|
||
|
||
class bt_task:
|
||
__table = 'task_list'
|
||
__task_tips = '/dev/shm/bt_task_now.pl'
|
||
__task_path = '/www/server/panel/tmp/'
|
||
down_log_total_file = '/tmp/download_total.pl'
|
||
not_web = False
|
||
def __init__(self):
|
||
|
||
# 创建数据表
|
||
sql = '''CREATE TABLE IF NOT EXISTS `task_list` (
|
||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
`name` TEXT,
|
||
`type` TEXT,
|
||
`status` INTEGER,
|
||
`shell` TEXT,
|
||
`other` TEXT,
|
||
`exectime` INTEGER,
|
||
`endtime` INTEGER,
|
||
`addtime` INTEGER
|
||
);'''
|
||
public.M(None).execute(sql, ())
|
||
|
||
# 创建临时目录
|
||
if not os.path.exists(self.__task_path):
|
||
os.makedirs(self.__task_path, 384)
|
||
|
||
# 取任务列表
|
||
def get_task_list(self, status=-3):
|
||
sql = public.M(self.__table)
|
||
if status != -3:
|
||
sql = sql.where('status=?', (status,))
|
||
data = sql.field(
|
||
'id,name,type,shell,other,status,exectime,endtime,addtime').select()
|
||
return data
|
||
|
||
# 取任务列表前端
|
||
def get_task_lists(self, get):
|
||
# 校验参数
|
||
try:
|
||
get.validate([
|
||
Param('status').Integer(),
|
||
|
||
], [
|
||
public.validate.trim_filter(),
|
||
])
|
||
except Exception as ex:
|
||
public.print_log("error info: {}".format(ex))
|
||
return public.return_message(-1, 0, str(ex))
|
||
sql = public.M(self.__table)
|
||
where_sql = None
|
||
where_params = []
|
||
if 'status' in get:
|
||
if get.status == '-3':
|
||
where_sql = "(status=? OR status=?)"
|
||
where_params = [-1, 0]
|
||
else:
|
||
where_sql = "status=?"
|
||
where_params = [get.status]
|
||
if "task_id" in get:
|
||
if where_sql is None:
|
||
where_sql = "id=?"
|
||
else:
|
||
where_sql += " AND id=?"
|
||
where_params.append(get.task_id)
|
||
data = sql.field('id,name,type,shell,other,status,exectime,endtime,addtime').where(where_sql,where_params).order('id asc').limit('10').select()
|
||
if type(data) == str:
|
||
public.WriteLog('TASK_QUEUE', data,not_web = self.not_web)
|
||
return public.return_message(0,0,[])
|
||
if not 'num' in get:
|
||
get.num = 15
|
||
num = int(get.num)
|
||
for i in range(len(data)):
|
||
data[i]['log'] = ''
|
||
if data[i]['status'] == -1:
|
||
data[i]['log'] = self.get_task_log(
|
||
data[i]['id'], data[i]['type'], num)
|
||
elif data[i]['status'] == 1:
|
||
data[i]['log'] = self.get_task_log(
|
||
data[i]['id'], data[i]['type'], 10)
|
||
if data[i]['type'] == '3':
|
||
data[i]['other'] = json.loads(data[i]['other'])
|
||
return public.return_message(0,0,data)
|
||
|
||
# 创建任务
|
||
def create_task(self, task_name, task_type, task_shell, other=''):
|
||
self.clean_log()
|
||
task_id = public.M(self.__table).add('name,type,shell,other,addtime,status',
|
||
(task_name, task_type, task_shell, other, int(time.time()), 0))
|
||
public.WriteFile(self.__task_tips, 'True')
|
||
public.ExecShell("/etc/init.d/bt start")
|
||
if not public.M(self.__table).where('status=?', ('-1',)).count():
|
||
tip_file = "/dev/shm/.start_task.pl"
|
||
tip_time = public.readFile(tip_file)
|
||
if not tip_time or time.time() - int(tip_time) > 600:
|
||
public.ExecShell("/www/server/panel/BT-Task")
|
||
public.print_log("Background task restarted")
|
||
return task_id
|
||
|
||
# 修改任务
|
||
def modify_task(self, id, key, value):
|
||
public.M(self.__table).where('id=?', (id,)).setField(key, value)
|
||
return True
|
||
|
||
# 删除任务
|
||
def remove_task(self, get):
|
||
if not hasattr(get, 'id'):
|
||
return public.return_message(-1,0,'ID cannot be empty.!')
|
||
task_info = self.get_task_find(get.id)
|
||
if not isinstance(task_info, dict):
|
||
return public.return_message(-1,0,'The task does not exist.')
|
||
public.M(self.__table).where('id=?', (get.id,)).delete()
|
||
if str(task_info['status']) == '-1':
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep 'task.py'|grep -v grep|awk '{print $2}')")
|
||
if task_info['type'] == '1':
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep '{}')".format(task_info['other']))
|
||
time.sleep(1)
|
||
if os.path.exists(task_info['other']):
|
||
os.remove(task_info['other'])
|
||
elif task_info['type'] == '3':
|
||
z_info = json.loads(task_info['other'])
|
||
if z_info['z_type'] == 'tar.gz':
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep 'tar -zcvf'|grep -v grep|awk '{print $2}')")
|
||
elif z_info['z_type'] == 'rar':
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep /www/server/rar/rar|grep -v grep|awk '{print $2}')")
|
||
elif z_info['z_type'] == 'zip':
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep '.zip -r'|grep -v grep|awk '{print $2}')")
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep '.zip\' -r'|grep -v grep|awk '{print $2}')")
|
||
if os.path.exists(z_info['dfile']):
|
||
os.remove(z_info['dfile'])
|
||
elif task_info['type'] == '2':
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep 'tar -zxvf'|grep -v grep|awk '{print $2}')")
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep '/www/server/rar/unrar'|grep -v grep|awk '{print $2}')")
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep 'unzip -P'|grep -v grep|awk '{print $2}')")
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep 'gunzip -c'|grep -v grep|awk '{print $2}')")
|
||
elif task_info['type'] == '0':
|
||
public.ExecShell(
|
||
"kill -9 $(ps aux|grep '"+task_info['shell']+"'|grep -v grep|awk '{print $2}')")
|
||
|
||
public.ExecShell("/etc/init.d/bt start")
|
||
return public.return_message(0, 0, public.lang("Task cancelled!"))
|
||
|
||
# 取一条任务
|
||
def get_task_find(self, id):
|
||
data = public.M(self.__table).where('id=?', (id,)).field(
|
||
'id,name,type,shell,other,status,exectime,endtime,addtime').find()
|
||
return data
|
||
|
||
# 执行任务
|
||
# task_type 0.执行shell 1.下载文件 2.解压文件 3.压缩文件
|
||
def execute_task(self, id, task_type, task_shell, other=''):
|
||
|
||
# public.print_log('开始执行 异步任务000')
|
||
if not os.path.exists(self.__task_path):
|
||
os.makedirs(self.__task_path, 384)
|
||
log_file = self.__task_path + str(id) + '.log'
|
||
# public.print_log(f'task_type ==={task_type}')
|
||
# 标记状态执行时间
|
||
self.modify_task(id, 'status', -1)
|
||
self.modify_task(id, 'exectime', int(time.time()))
|
||
task_type = int(task_type)
|
||
# 开始执行
|
||
if task_type == 0: # 执行命令
|
||
public.ExecShell(task_shell + ' &> ' + log_file)
|
||
elif task_type == 1: # 下载文件
|
||
if os.path.exists(self.down_log_total_file):
|
||
os.remove(self.down_log_total_file)
|
||
if not os.path.exists(os.path.dirname(other)):
|
||
os.makedirs(os.path.dirname(other))
|
||
|
||
public.ExecShell("wget -O '{}' '{}' --no-check-certificate -T 30 -t 5 -d &> {}".format(other, task_shell, log_file))
|
||
if os.path.exists(log_file):
|
||
os.remove(log_file)
|
||
elif task_type == 2: # 解压文件
|
||
zip_info = json.loads(other)
|
||
self._unzip(task_shell, zip_info['dfile'],
|
||
zip_info['password'], log_file, zip_info.get('power'), zip_info.get('user'))
|
||
elif task_type == 3: # 压缩文件
|
||
zip_info = json.loads(other)
|
||
if not 'z_type' in zip_info:
|
||
zip_info['z_type'] = 'tar.gz'
|
||
print(self._zip(
|
||
task_shell, zip_info['sfile'], zip_info['dfile'], log_file, zip_info['z_type'],zip_info['volume_size'],zip_info['save_path']))
|
||
elif task_type == 4: # 备份数据库
|
||
self.backup_database(task_shell, log_file)
|
||
elif task_type == 5: # 导入数据库
|
||
self.input_database(task_shell, other, log_file)
|
||
elif task_type == 6: # 备份网站
|
||
self.backup_site(task_shell, log_file)
|
||
elif task_type == 7: # 恢复网站
|
||
pass
|
||
elif task_type == 8: # 复制文件
|
||
_info = json.loads(other)
|
||
public.ExecShell("cp -rv {} {} &> {}".format(_info['sfile'], _info['dfile'], log_file))
|
||
elif task_type == 9: # 清空回收站
|
||
_info = json.loads(other)
|
||
self.Close_Recycle_bin_new(_info["ctype"], _info["recycle_bin_list"], log_file)
|
||
elif task_type == 10: # 彻底删除文件
|
||
_info = json.loads(other)
|
||
self.Batch_Del_Recycle_bin(_info["filenames"], log_file)
|
||
|
||
# 标记状态与结束时间
|
||
self.modify_task(id, 'status', 1)
|
||
self.modify_task(id, 'endtime', int(time.time()))
|
||
|
||
def Batch_Del_Recycle_bin(self, filenames, log_file):
|
||
import shutil
|
||
import database_v2 as database
|
||
database = database.database()
|
||
for filename in filenames:
|
||
with open(log_file, 'a') as f:
|
||
f.write("deleting:{}\n".format(filename))
|
||
if os.path.basename(filename).startswith('BTDB_'):
|
||
database.DeleteTo(filename)
|
||
with open(log_file, 'a') as f:
|
||
f.write("{} has been deleted\n".format(filename))
|
||
continue
|
||
public.ExecShell('chattr -R -i ' + filename)
|
||
if os.path.isdir(filename):
|
||
try:
|
||
shutil.rmtree(filename)
|
||
except:
|
||
public.ExecShell('chattr -R -a ' + filename)
|
||
public.ExecShell("rm -rf " + filename)
|
||
else:
|
||
try:
|
||
os.remove(filename)
|
||
except:
|
||
public.ExecShell("rm -f " + filename)
|
||
with open(log_file, 'a') as f:
|
||
f.write("{} has been deleted\n".format(filename))
|
||
|
||
def Close_Recycle_bin_new(self, ctype, recycle_bin_list, log_file):
|
||
import shutil
|
||
import database_v2 as database
|
||
database = database.database()
|
||
for rPath in recycle_bin_list:
|
||
public.ExecShell('chattr -R -i ' + rPath)
|
||
rlist = os.listdir(rPath)
|
||
i = 0
|
||
l = len(rlist)
|
||
for name in rlist:
|
||
i += 1
|
||
path = os.path.join(rPath, name)
|
||
try:
|
||
progress = int((100.0 * i / l))
|
||
except:
|
||
progress = 0
|
||
data = "Currently clearing: {} Currently deleting: {} Total: {}, Completed: {}, Progress: {}%\n".format(rPath,
|
||
name.replace("_bt_", "/").split('_t_')[
|
||
0], l, i, progress)
|
||
with open(log_file, 'a') as f:
|
||
f.write(data)
|
||
if name.startswith('BTDB_') and ctype == 'db':
|
||
database.DeleteTo(path)
|
||
elif not name.startswith('BTDB_') and ctype == 'files':
|
||
if os.path.isdir(path):
|
||
try:
|
||
shutil.rmtree(path)
|
||
except:
|
||
public.ExecShell('chattr -R -a ' + path)
|
||
public.ExecShell('rm -rf ' + path)
|
||
else:
|
||
try:
|
||
os.remove(path)
|
||
except:
|
||
public.ExecShell('rm -f ' + path)
|
||
with open(log_file, 'a') as f:
|
||
f.write("{} has been cleared and completed!\n".format(rPath))
|
||
|
||
|
||
# 开始检测任务
|
||
def start_task(self):
|
||
noe = False
|
||
n = 0
|
||
tip_file = '/dev/shm/.start_task.pl'
|
||
while True:
|
||
try:
|
||
time.sleep(1)
|
||
public.writeFile(tip_file, str(int(time.time())))
|
||
n += 1
|
||
if not os.path.exists(self.__task_tips) and noe and n < 60:
|
||
continue
|
||
if os.path.exists(self.__task_tips):
|
||
os.remove(self.__task_tips)
|
||
n = 0
|
||
public.M(self.__table).where(
|
||
'status=?', ('-1',)).setField('status', 0)
|
||
task_list = self.get_task_list(0)
|
||
for task_info in task_list:
|
||
# if task_info['status'] == 0:
|
||
# public.print_log('~~~~~~~~~~~~~~~~~~~~~~开始执行任务')
|
||
# public.print_log(f'~~~~~~~~~~~~~~~~~~~~~~ {task_info['id']} -->{task_info['name']}')
|
||
self.execute_task(
|
||
task_info['id'], task_info['type'], task_info['shell'], task_info['other'])
|
||
noe = True
|
||
except:
|
||
print(public.get_error_info())
|
||
|
||
# 前端通过任务ID取某一个任务的日志
|
||
def get_task_log_by_id(self, get):
|
||
task_id = get.id
|
||
task_type = get.task_type
|
||
log_data = {}
|
||
if "num" in get:
|
||
num = int(get.num)
|
||
log_data = self.get_task_log(task_id, task_type, num)
|
||
else:
|
||
log_data = self.get_task_log(task_id, task_type)
|
||
task_obj = self.get_task_find(task_id)
|
||
log_data["status"] = task_obj["status"]
|
||
return log_data
|
||
|
||
# 取任务执行日志
|
||
def get_task_log(self, id, task_type, num=5):
|
||
log_file = self.__task_path + str(id) + '.log'
|
||
if not os.path.exists(log_file):
|
||
data = ''
|
||
if(task_type == '1'):
|
||
data = {'name': public.lang("Download file"), 'total': 0, 'used': 0,
|
||
'pre': 0, 'speed': 0, 'time': 0}
|
||
return data
|
||
|
||
if(task_type == '1'):
|
||
total = 0
|
||
if not os.path.exists(self.down_log_total_file):
|
||
f = open(log_file, 'r')
|
||
head = f.read(4096)
|
||
content_length = re.findall(r"Length:\s+(\d+)", head)
|
||
if content_length:
|
||
total = int(content_length[0])
|
||
public.writeFile(self.down_log_total_file,
|
||
content_length[0])
|
||
else:
|
||
total = public.readFile(self.down_log_total_file)
|
||
if not total:
|
||
total = 0
|
||
total = int(total)
|
||
|
||
filename = public.M(self.__table).where(
|
||
'id=?', (id,)).getField('shell')
|
||
|
||
speed_tmp = public.ExecShell("tail -n 2 {}".format(log_file))[0]
|
||
speed_total = re.findall(
|
||
r"([\d\.]+[BbKkMmGg]).+\s+(\d+)%\s+([\d\.]+[KMBGkmbg])\s+(\w+[sS])", speed_tmp)
|
||
if not speed_total:
|
||
data = {'name':public.get_msg_gettext('Download file {}',(filename,)),'total':0,'used':0,'pre':0,'speed':0,'time':0}
|
||
else:
|
||
speed_total = speed_total[0]
|
||
used = speed_total[0]
|
||
if speed_total[0].lower().find('k') != -1:
|
||
used = public.to_size(
|
||
float(speed_total[0].lower().replace('k', '')) * 1024)
|
||
u_time = speed_total[3].replace(
|
||
'h', 'Hour').replace('m', 'Minute').replace('s', 'Second')
|
||
data = {'name': public.get_msg_gettext('Download file {}',(filename,)),'total': total, 'used': used, 'pre': speed_total[1], 'speed': speed_total[2], 'time': u_time}
|
||
else:
|
||
data = public.ExecShell("tail -n {} {}".format(num, log_file))[0]
|
||
if type(data) == list:
|
||
return ''
|
||
if isinstance(data,bytes):
|
||
data = data.decode('utf-8')
|
||
data = data.replace('\x08', '').replace('\n', '<br>')
|
||
return data
|
||
|
||
# 清理任务日志
|
||
def clean_log(self):
|
||
import shutil
|
||
s_time = int(time.time())
|
||
timeout = 86400
|
||
for f in os.listdir(self.__task_path):
|
||
filename = self.__task_path + f
|
||
c_time = os.stat(filename).st_ctime
|
||
if s_time - c_time > timeout:
|
||
if os.path.isdir(filename):
|
||
shutil.rmtree(filename)
|
||
else:
|
||
os.remove(filename)
|
||
return True
|
||
|
||
|
||
|
||
# 文件压缩
|
||
def _zip(self, path, sfile, dfile, log_file, z_type='tar.gz',volume_size=None,save_path=None):
|
||
if sys.version_info[0] == 2:
|
||
sfile = sfile.encode('utf-8')
|
||
dfile = dfile.encode('utf-8')
|
||
if sys.version_info[0] == 2:
|
||
path = path.encode('utf-8')
|
||
if sfile.find(',') == -1:
|
||
if not os.path.exists(path+'/'+sfile):
|
||
return public.return_message(-1, 0, public.lang("Configuration file not exist"))
|
||
# 处理多文件压缩
|
||
sfiles = ''
|
||
for sfile in sfile.split(','):
|
||
if not sfile:
|
||
continue
|
||
sfiles += " '" + sfile + "'"
|
||
# 根据压缩格式执行压缩命令
|
||
if z_type == 'zip':
|
||
if volume_size:
|
||
self.compress_and_split_zip(path, dfile, sfiles, volume_size, log_file)
|
||
else:
|
||
# 创建普通压缩文件
|
||
zip_cmd = "cd '{}' && zip -r '{}' {} &> {}".format(path, dfile, sfiles, log_file)
|
||
public.ExecShell(zip_cmd)
|
||
elif z_type == 'tar.gz':
|
||
if volume_size:
|
||
self.compress_and_split_tar(path, dfile, sfiles, volume_size, log_file)
|
||
else:
|
||
public.ExecShell("cd '" + path + "' && tar -zcvf '" + dfile + "' " + sfiles + " &> " + log_file)
|
||
|
||
elif z_type == 'rar':
|
||
rar_file = '/www/server/rar/rar'
|
||
if not os.path.exists(rar_file):
|
||
self.install_rar()
|
||
public.ExecShell("cd '" + path + "' && "+rar_file +
|
||
" a -r '" + dfile + "' " + sfiles + " &> " + log_file)
|
||
elif z_type == '7z':
|
||
_7z_bin = self.get_7z_bin()
|
||
if not _7z_bin:
|
||
self.install_7zip()
|
||
err_msg = 'The p7zip component is not installed. Compression cannot be performed. An attempt to automatically install it has been made. Please try again later!'
|
||
public.WriteLog("File manager","Compression file failed. Reason: {}, file: {}".format(err_msg,sfile))
|
||
return public.return_message(-1, 0, err_msg)
|
||
public.ExecShell("cd {} && {} a -t7z {} {} -y &> {}".format(path, _7z_bin, dfile, sfiles, log_file))
|
||
else:
|
||
return public.return_message(-1, 0, public.lang("Specified compression format is not supported!"))
|
||
|
||
self.set_file_accept(dfile)
|
||
#public.WriteLog("TYPE_FILE", 'ZIP_SUCCESS', (sfiles, dfile),not_web = self.not_web)
|
||
public.WriteLog("File manager", 'Compressed file [ {} ] to [ {} ] success'.format(sfiles, dfile))
|
||
return public.return_message(0, 0, public.lang("Compression succeeded!"))
|
||
|
||
def compress_and_split_zip(self, path, dfile, sfiles, volume_size, log_file):
|
||
# 处理 volume_size 参数,转换为 zip 命令支持的格式
|
||
volume_size = volume_size.upper().replace("KB", "K").replace("MB", "M").replace("GB", "G")
|
||
dfile_dir = os.path.dirname(dfile)
|
||
dfile_base = os.path.basename(dfile)
|
||
# 构造分卷文件的名称,存放在相同目录
|
||
split_dfile = os.path.join(dfile_dir, "{}/{}_split_".format(os.path.splitext(dfile_base)[0],os.path.splitext(dfile_base)[0]))
|
||
# 创建分卷压缩文件
|
||
zip_cmd = "cd '{}' && zip -r '{}' {} &> {}".format(path, dfile, sfiles, log_file)
|
||
zip_cmd +=" && mkdir -p {} &> {}".format(os.path.splitext(dfile_base)[0], log_file)
|
||
zip_cmd +=" && split -b {} {} {} &> {}".format(volume_size, dfile,split_dfile, log_file)
|
||
delete_cmd = " && find {} -type f -name '{}' -exec rm -f {} \; &> {}".format(path, os.path.basename(dfile), "{}", log_file)
|
||
zip_cmd += delete_cmd
|
||
public.ExecShell(zip_cmd)
|
||
|
||
json_dir = os.path.dirname(split_dfile)
|
||
json_file_path = os.path.join(os.path.dirname(split_dfile), "{}.split_json".format(os.path.splitext(dfile_base)[0]))
|
||
split_dfile=split_dfile
|
||
json_content = {"split_file_path": split_dfile}
|
||
self.generate_json(json_file_path,json_content)
|
||
|
||
def compress_and_split_tar(self, path, dfile, sfiles, volume_size, log_file):
|
||
volume_size = volume_size.upper().replace("KB", "K").replace("MB", "M").replace("GB", "G")
|
||
dfile_dir = os.path.dirname(dfile)
|
||
dfile_base = os.path.basename(dfile).replace(".tar.gz", "")
|
||
|
||
# 构建没有扩展名的目录路径
|
||
split_dfile_dir = os.path.join(dfile_dir, dfile_base)
|
||
# 构建最终的分卷文件名,包括路径
|
||
split_dfile = os.path.join(split_dfile_dir, dfile_base + ".tar.gz")
|
||
|
||
# 确保目标目录存在
|
||
if not os.path.exists(split_dfile_dir):
|
||
os.makedirs(split_dfile_dir)
|
||
|
||
# 构建并执行 tar 和 split 命令
|
||
tar_cmd = "cd '{}' && tar cvzf - {} | split -b {} -d - {} &> {} ".format(path, sfiles,volume_size, split_dfile, log_file)
|
||
public.ExecShell(tar_cmd)
|
||
|
||
json_dir = os.path.dirname(split_dfile)
|
||
json_file_path = os.path.join(os.path.dirname(split_dfile), "{}.split_json".format(os.path.splitext(dfile_base)[0]))
|
||
json_content = {"split_file_path": split_dfile}
|
||
self.generate_json(json_file_path,json_content)
|
||
|
||
|
||
def generate_json(self, json_file_path, content):
|
||
try:
|
||
with open(json_file_path, 'w') as json_file:
|
||
json.dump(content, json_file)
|
||
print("JSON file generation successful")
|
||
except Exception as e:
|
||
# os.makedirs(json_file_path)
|
||
print(f"An error occurred while generating the JSON file.: {e}")
|
||
|
||
|
||
def get_path_access(self, path):
|
||
'''
|
||
@name 获取文件或目录的权限信息
|
||
@param path<string> 文件或目录路径
|
||
@return dict
|
||
'''
|
||
access_list = {}
|
||
try:
|
||
|
||
for d in os.listdir(path):
|
||
d_path = os.path.join(path,d)
|
||
access_list[d] = public.get_mode_and_user(d_path)
|
||
if not access_list[d]:
|
||
access_list[d] = {"mode":755,"user":"www"}
|
||
except:
|
||
pass
|
||
return access_list
|
||
|
||
|
||
def set_path_access(self, path,old_access_list):
|
||
'''
|
||
@name 设置文件或目录的权限信息
|
||
@param path<string> 文件或目录路径
|
||
@param old_access_list<dict> 旧的权限信息
|
||
@return bool
|
||
'''
|
||
try:
|
||
new_access_list = self.get_path_access(path)
|
||
for d in new_access_list:
|
||
d_path = os.path.join(path,d)
|
||
if d in old_access_list:
|
||
if new_access_list[d] != old_access_list[d]:
|
||
self.set_file_accept(d_path)
|
||
else:
|
||
self.set_file_accept(d_path)
|
||
except:
|
||
return False
|
||
|
||
return True
|
||
|
||
|
||
def _unzip(self, sfile, dfile, password, log_file, power=None, user=None):
|
||
if sys.version_info[0] == 2:
|
||
sfile = sfile.encode('utf-8')
|
||
dfile = dfile.encode('utf-8')
|
||
if not os.path.exists(sfile):
|
||
return public.return_message(-1, 0, public.lang("Configuration file not exist"))
|
||
|
||
if not os.path.isfile(sfile):
|
||
return public.return_message(-1, 0, public.lang("The directory cannot be decompressed"))
|
||
sfile = os.path.abspath(sfile)
|
||
old_dpath_list = self.get_path_access(dfile)
|
||
|
||
# 从日志文件获取解压后的文件名
|
||
split_log = None
|
||
# 判断压缩包格式
|
||
if sfile[-4:] == '.zip':
|
||
public.ExecShell("unzip -X -P '" + password + "' -o '" + sfile + "' -d '" + dfile + "' &> " + log_file)
|
||
split_log = lambda x: x[len(dfile) + 13 if dfile[-1] == '/' else len(dfile) + 14:].split('/')[
|
||
0].strip() if not x.startswith('Archive') else ""
|
||
elif sfile[-7:] == '.tar.gz' or sfile[-4:] == '.tgz' or (tarfile.is_tarfile(sfile) and sfile[-3:] == '.gz'):
|
||
public.ExecShell("tar zxvf '" + sfile +
|
||
"' -C '" + dfile + "' &> " + log_file)
|
||
split_log = lambda x: x.strip().split('/')[0]
|
||
elif sfile[-4:] == '.rar':
|
||
rar_file = '/www/server/rar/unrar'
|
||
if not os.path.exists(rar_file):
|
||
self.install_rar()
|
||
pass_opt = '-p-'
|
||
if password:
|
||
password = password.replace("&", r"\&").replace('"', '\"')
|
||
pass_opt = '-p"{}"'.format(password)
|
||
|
||
public.ExecShell(rar_file + ' x ' + pass_opt + ' -u -y "' + sfile + '" "' + dfile + '" &> ' + log_file)
|
||
split_log = lambda x: \
|
||
re.search(r"/[^\s]+", x).group().strip()[len(dfile) if dfile[-1] == '/' else len(dfile) + 1:].split('/')[
|
||
0] if x.strip().endswith('OK') and re.search(r"/[^\s]+", x) else ""
|
||
|
||
elif sfile[-4:] == '.war':
|
||
public.ExecShell("unzip -X -P '" + password + "' -o '" +
|
||
sfile + "' -d '" + dfile + "' &> " + log_file)
|
||
split_log = lambda x: x[len(dfile) + 13 if dfile[-1] == '/' else len(dfile) + 14:].split('/')[
|
||
0].strip() if not x.startswith('Archive') else ""
|
||
elif sfile[-4:] == '.bz2':
|
||
public.ExecShell("tar jxvf '" + sfile +
|
||
"' -C '" + dfile + "' &> " + log_file)
|
||
split_log = lambda x: x.strip().split('/')[0]
|
||
elif sfile[-3:] == '.7z':
|
||
_7zbin = self.get_7z_bin()
|
||
if not _7zbin:
|
||
self.install_7zip()
|
||
err_msg = 'The p7zip component is not installed, an automatic installation has been attempted, please wait a few minutes and try again!'
|
||
public.WriteLog("File manager","Failed to compress file, reason: {}, file: {}".format(err_msg,sfile))
|
||
return public.return_message(-1, 0, err_msg)
|
||
pass_opt = ""
|
||
if password:
|
||
pass_opt = '-p"{}"'.format(password)
|
||
public.ExecShell('{} x "{}" -o"{}" -bb3 -y {} &> {}'.format(_7zbin, sfile, dfile, pass_opt, log_file))
|
||
split_log = lambda x: x.strip()[1:].split('/')[0].strip() if x.startswith('-') and not x.startswith(
|
||
'--') else ""
|
||
else:
|
||
public.ExecShell("gunzip -c " + sfile + " > " + sfile[:-3])
|
||
|
||
# 获取解压后的文件
|
||
# 判断是否能获取解压后的文件名
|
||
if split_log:
|
||
file_list = self.get_unzip_files(log_file, split_log)
|
||
# 解压后设置权限
|
||
for file in file_list:
|
||
try:
|
||
path = os.path.join(dfile, file)
|
||
except:
|
||
continue
|
||
if not file.strip() or not os.path.exists(path) or path == dfile:
|
||
continue
|
||
if power:
|
||
public.ExecShell("chmod -R {} '{}'".format(power, path))
|
||
if user:
|
||
public.ExecShell("chown -R {}:{} '{}'".format(user, user, path))
|
||
|
||
# 异常处理
|
||
log_msg = public.readFile(log_file)
|
||
err_msg = None
|
||
if log_msg:
|
||
if log_msg.find("incorrect password") != -1 \
|
||
or log_msg.find("The specified password is incorrect.") != -1 \
|
||
or log_msg.find("Data Error in encrypted file. Wrong password") != -1:
|
||
err_msg = 'Decompression password error!'
|
||
public.WriteLog("File manager","Unzip file failed, reason: {}, file: {}".format(err_msg,sfile))
|
||
elif log_msg.find("unsupported compression method 99") != -1:
|
||
err_msg = 'Unsupported Zip encryption and compression, only ZIP traditional encryption is supported for ZIP archives!'
|
||
public.WriteLog("File manager","Unzip file failed, reason: {}, file: {}".format(err_msg,sfile))
|
||
elif log_msg.find("is not RAR archive") != -1:
|
||
err_msg = "It is not a rar archive, check whether to modify the file with the extension rar for other compression formats!"
|
||
public.WriteLog("File manager","Unzip file failed, reason: {}, file: {}".format(err_msg,sfile))
|
||
elif log_msg.find("gzip: stdin") != -1:
|
||
public.ExecShell("tar xvf '" + sfile + "' -C '" + dfile + "' &> " + log_file)
|
||
|
||
if err_msg: return public.return_message(-1, 0, err_msg)
|
||
|
||
# 检查是否设置权限
|
||
if self.check_dir(dfile):
|
||
sites_path = public.M('config').where(
|
||
'id=?', (1,)).getField('sites_path')
|
||
if dfile.find('/www/wwwroot') != -1 or dfile.find(sites_path) != -1:
|
||
# 只设置新的目录权限
|
||
self.set_path_access(dfile, old_dpath_list)
|
||
else:
|
||
try:
|
||
import pwd
|
||
user = pwd.getpwuid(os.stat(dfile).st_uid).pw_name
|
||
public.ExecShell("chown %s:%s %s" % (user, user, dfile))
|
||
except KeyError:
|
||
return public.ReturnMsg(False, 'SET_FILE_ACCEPT_ERROR')
|
||
|
||
public.WriteLog("File manager", 'unzip file [ {} ] to [ {} ] success'.format(sfile, dfile))
|
||
return public.return_message(0, 0, public.lang("Uncompression succeeded!"))
|
||
def get_unzip_files(self, log_file, split_log):
|
||
'''
|
||
@name 获取解压后的文件
|
||
@param log_file<string> 解压日志文件路径
|
||
@return set
|
||
'''
|
||
files = []
|
||
try:
|
||
with open(log_file, 'r') as f:
|
||
while True:
|
||
data = f.readlines(1024 * 1024 * 30)
|
||
if data:
|
||
for x in data:
|
||
file = split_log(x)
|
||
if file and file not in files:
|
||
files.append(file)
|
||
else:
|
||
break
|
||
return set(files)
|
||
except:
|
||
return set(files)
|
||
|
||
def get_7z_bin(self):
|
||
'''
|
||
@name 获取7z命令路径
|
||
@author hwliang
|
||
@return {string} 7z命令路径
|
||
'''
|
||
_7z_bins = ["/usr/bin/7z","/usr/bin/7za","/usr/bin/7zr"]
|
||
for _7z_bin in _7z_bins:
|
||
if os.path.exists(_7z_bin):
|
||
return _7z_bin
|
||
return None
|
||
|
||
def install_7zip(self):
|
||
'''
|
||
@name 安装7zip
|
||
@author hwliang
|
||
@return {bool} True/False
|
||
'''
|
||
_7z_bin = self.get_7z_bin()
|
||
if _7z_bin:
|
||
return True
|
||
|
||
# 是否已经尝试安装过
|
||
install_tip = '{}/data/7z_install.pl'.format(public.get_panel_path())
|
||
if os.path.exists(install_tip):
|
||
return False
|
||
|
||
if os.path.exists("/usr/bin/apt-get"):
|
||
public.ExecShell("nohup apt-get -y install p7zip-full &> /dev/null &")
|
||
elif os.path.exists("/usr/bin/yum"):
|
||
public.ExecShell("nohup yum -y install p7zip &> /dev/null &")
|
||
elif os.path.exists("/usr/bin/dnf"):
|
||
public.ExecShell("nohup dnf -y install p7zip &> /dev/null &")
|
||
else:
|
||
return False
|
||
return True
|
||
|
||
# 备份网站
|
||
def backup_site(self, id, log_file):
|
||
find = public.M('sites').where(
|
||
"id=?", (id,)).field('name,path,id').find()
|
||
fileName = find['name']+'_' + \
|
||
time.strftime('%Y%m%d_%H%M%S', time.localtime())+'.zip'
|
||
backupPath = public.M('config').where(
|
||
'id=?', (1,)).getField('backup_path') + '/site'
|
||
|
||
zipName = backupPath + '/'+fileName
|
||
if not (os.path.exists(backupPath)):
|
||
os.makedirs(backupPath)
|
||
|
||
execStr = "cd '" + find['path'] + "' && zip '" + \
|
||
zipName + "' -x .user.ini -r ./ &> " + log_file
|
||
public.ExecShell(execStr)
|
||
|
||
sql = public.M('backup').add('type,name,pid,filename,size,addtime',
|
||
(0, fileName, find['id'], zipName, 0, public.getDate()))
|
||
public.WriteLog('TYPE_SITE', 'SITE_BACKUP_SUCCESS', (find['name'],),not_web = self.not_web)
|
||
return public.return_msg_gettext(True, public.lang("Backup Succeeded!"))
|
||
|
||
# 备份数据库
|
||
def backup_database(self, id, log_file):
|
||
name = public.M('databases').where("id=?", (id,)).getField('name')
|
||
find = public.M('config').where('id=?', (1,)).field(
|
||
'mysql_root,backup_path').find()
|
||
|
||
if not os.path.exists(find['backup_path'] + '/database'):
|
||
public.ExecShell('mkdir -p ' + find['backup_path'] + '/database')
|
||
self.mypass(True, find['mysql_root'])
|
||
|
||
fileName = name + '_' + \
|
||
time.strftime('%Y%m%d_%H%M%S', time.localtime()) + '.sql.gz'
|
||
backupName = find['backup_path'] + '/database/' + fileName
|
||
public.ExecShell("/www/server/mysql/bin/mysqldump --force --opt \"" +
|
||
name + "\" | gzip > " + backupName)
|
||
if not os.path.exists(backupName):
|
||
return public.return_msg_gettext(False, public.lang("Backup error!"))
|
||
|
||
self.mypass(False, find['mysql_root'])
|
||
|
||
sql = public.M('backup')
|
||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||
sql.add('type,name,pid,filename,size,addtime',
|
||
(1, fileName, id, backupName, 0, addTime))
|
||
public.WriteLog("TYPE_DATABASE", "DATABASE_BACKUP_SUCCESS", (name,),not_web = self.not_web)
|
||
return public.return_msg_gettext(True, public.lang("Backup Succeeded!"))
|
||
|
||
# 导入数据库
|
||
def input_database(self, id, file, log_file):
|
||
name = public.M('databases').where("id=?", (id,)).getField('name')
|
||
root = public.M('config').where('id=?', (1,)).getField('mysql_root')
|
||
tmp = file.split('.')
|
||
exts = ['sql', 'gz', 'zip']
|
||
ext = tmp[len(tmp) - 1]
|
||
if ext not in exts:
|
||
return public.return_msg_gettext(False, public.lang("Select sql/gz/zip file!"))
|
||
|
||
isgzip = False
|
||
if ext != 'sql':
|
||
tmp = file.split('/')
|
||
tmpFile = tmp[len(tmp)-1]
|
||
tmpFile = tmpFile.replace('.sql.' + ext, '.sql')
|
||
tmpFile = tmpFile.replace('.' + ext, '.sql')
|
||
tmpFile = tmpFile.replace('tar.', '')
|
||
backupPath = public.M('config').where(
|
||
'id=?', (1,)).getField('backup_path') + '/database'
|
||
|
||
if ext == 'zip':
|
||
public.ExecShell("cd " + backupPath + " && unzip " + file)
|
||
else:
|
||
public.ExecShell("cd " + backupPath + " && tar zxf " + file)
|
||
if not os.path.exists(backupPath + "/" + tmpFile):
|
||
public.ExecShell("cd " + backupPath +
|
||
" && gunzip -q " + file)
|
||
isgzip = True
|
||
|
||
if not os.path.exists(backupPath + '/' + tmpFile) or tmpFile == '':
|
||
return public.return_msg_gettext(False, 'Configuration file not exist', (tmpFile,))
|
||
self.mypass(True, root)
|
||
public.ExecShell(public.GetConfigValue('setup_path') + "/mysql/bin/mysql -uroot -p" +
|
||
root + " --force \"" + name + "\" < " + backupPath + '/' + tmpFile)
|
||
self.mypass(False, root)
|
||
if isgzip:
|
||
public.ExecShell('cd ' + backupPath +
|
||
' && gzip ' + file.split('/')[-1][:-3])
|
||
else:
|
||
public.ExecShell("rm -f " + backupPath + '/' + tmpFile)
|
||
else:
|
||
self.mypass(True, root)
|
||
public.ExecShell(public.GetConfigValue(
|
||
'setup_path') + "/mysql/bin/mysql -uroot -p" + root + " --force \"" + name + "\" < " + file)
|
||
self.mypass(False, root)
|
||
|
||
public.WriteLog("TYPE_DATABASE", 'Successfully imported database [{}]', (name,),not_web = self.not_web)
|
||
return public.return_msg_gettext(True, public.lang("Successfully imported database!"))
|
||
|
||
# 配置
|
||
def mypass(self, act, root):
|
||
my_cnf = '/etc/my.cnf'
|
||
public.ExecShell("sed -i '/user=root/d' " + my_cnf)
|
||
public.ExecShell("sed -i '/password=/d' " + my_cnf)
|
||
if act:
|
||
mycnf = public.readFile(my_cnf)
|
||
rep = "\\[mysqldump\\]\nuser=root"
|
||
sea = "[mysqldump]\n"
|
||
subStr = sea + "user=root\npassword=\"" + root + "\"\n"
|
||
mycnf = mycnf.replace(sea, subStr)
|
||
if len(mycnf) > 100:
|
||
public.writeFile(my_cnf, mycnf)
|
||
|
||
# 设置权限
|
||
def set_file_accept(self, filename):
|
||
# public.ExecShell('chown -R www:www ' + filename)
|
||
# public.ExecShell('chmod -R 755 ' + filename)
|
||
import files
|
||
from collections import namedtuple
|
||
if not os.path.exists(filename):
|
||
return
|
||
get = namedtuple('get',['path'])
|
||
get.path = filename
|
||
files.files().fix_permissions(get)
|
||
|
||
# 检查敏感目录
|
||
def check_dir(self, path):
|
||
path = path.replace('//', '/')
|
||
if path[-1:] == '/':
|
||
path = path[:-1]
|
||
|
||
nDirs = ('',
|
||
'/',
|
||
'/*',
|
||
'/www',
|
||
'/root',
|
||
'/boot',
|
||
'/bin',
|
||
'/etc',
|
||
'/home',
|
||
'/dev',
|
||
'/sbin',
|
||
'/var',
|
||
'/usr',
|
||
'/tmp',
|
||
'/sys',
|
||
'/proc',
|
||
'/media',
|
||
'/mnt',
|
||
'/opt',
|
||
'/lib',
|
||
'/srv',
|
||
'/selinux',
|
||
'/www/server',
|
||
'/www/server/data',
|
||
public.GetConfigValue('logs_path'),
|
||
public.GetConfigValue('setup_path'))
|
||
|
||
return not path in nDirs
|
||
|
||
# 安装rar组件
|
||
def install_rar(self):
|
||
unrar_file = '/www/server/rar/unrar'
|
||
rar_file = '/www/server/rar/rar'
|
||
bin_unrar = '/usr/local/bin/unrar'
|
||
bin_rar = '/usr/local/bin/rar'
|
||
if os.path.exists(unrar_file) and os.path.exists(bin_unrar):
|
||
try:
|
||
import rarfile
|
||
except:
|
||
public.ExecShell("pip install rarfile")
|
||
return True
|
||
|
||
import platform
|
||
os_bit = ''
|
||
if platform.machine() == 'x86_64':
|
||
os_bit = '-x64'
|
||
download_url = public.get_url() + '/src/rarlinux'+os_bit+'-5.6.1.tar.gz'
|
||
|
||
tmp_file = '/tmp/bt_rar.tar.gz'
|
||
public.ExecShell('wget -O ' + tmp_file + ' ' + download_url)
|
||
if os.path.exists(unrar_file):
|
||
public.ExecShell("rm -rf /www/server/rar")
|
||
public.ExecShell("tar xvf " + tmp_file + ' -C /www/server/')
|
||
if os.path.exists(tmp_file):
|
||
os.remove(tmp_file)
|
||
if not os.path.exists(unrar_file):
|
||
return False
|
||
|
||
if os.path.exists(bin_unrar):
|
||
os.remove(bin_unrar)
|
||
if os.path.exists(bin_rar):
|
||
os.remove(bin_rar)
|
||
|
||
public.ExecShell('ln -sf ' + unrar_file + ' ' + bin_unrar)
|
||
public.ExecShell('ln -sf ' + rar_file + ' ' + bin_rar)
|
||
#public.ExecShell("pip install rarfile")
|
||
return True
|
||
|
||
|
||
if __name__ == '__main__':
|
||
p = bt_task()
|
||
#p.create_task('测试执行SHELL',0,'yum install wget -y','')
|
||
# print(p.get_task_list())
|
||
# p.modify_task(3,'status',0)
|
||
#p.modify_task(3,'shell','bash /www/server/panel/install/install_soft.sh 0 update php 5.6')
|
||
# p.modify_task(1,'other','{"sfile":"BTPanel","dfile":"/www/test.rar","z_type":"rar"}')
|
||
p.start_task()
|
||
# p._zip(sys.argv[1],sys.argv[2],sys.argv[3],sys.argv[4],sys.argv[5])
|