# #!/bin/python # #coding: utf-8 # # +------------------------------------------------------------------- # # | YakPanel # # +------------------------------------------------------------------- # # | Copyright (c) 2015-2016 YakPanel(www.yakpanel.com) All rights reserved. # # +------------------------------------------------------------------- # # | Author: hwliang # # +------------------------------------------------------------------- # # # ------------------------------ # # 计划任务 # # ------------------------------ # todo 弃用, 新路径 panel/YakTask/task.py # # import tracemalloc # # import objgraph # import sys # import os # import logging # from datetime import datetime, timedelta,timezone # from json import dumps, loads # from psutil import Process, pids, cpu_count, cpu_percent, net_io_counters, disk_io_counters, virtual_memory, pids, pid_exists, NoSuchProcess, AccessDenied, ZombieProcess # os.environ['BT_TASK'] = '1' # base_path = "/www/server/panel" # sys.path.insert(0, "/www/server/panel/class/") # if os.path.exists("/www/server/panel/plugin/mail_sys"): # sys.path.insert(1, "/www/server/panel/plugin/mail_sys") # import time # import public # import db # import json # import threading # import panelTask # import process_task # import shutil # # from public.hook_import import hook_import # hook_import() # # from power_mta.maillog_stat import maillog_event, aggregate_maillogs_task # from power_mta.automations import schedule_automations_forever # from data_v2 import data as data_v2_cls # # try: # from YakPanel import cache # except: # cache = None # # CURRENT_TASK_VERSION = '1.0.1' # # # task_obj = panelTask.bt_task() # task_obj.not_web = True # global pre, timeoutCount, logPath, isTask, oldEdate, isCheck # pre = 0 # timeoutCount = 0 # isCheck = 0 # oldEdate = None # logPath = '/tmp/panelExec.log' # isTask = '/tmp/panelTask.pl' # python_bin = None # thread_dict = {} # # # # # def log_malloc(): # # snapshot = tracemalloc.take_snapshot() # # top_stats = snapshot.statistics('lineno') # # # # s = 'TOP 50 difference (traced_memory: {} tracemalloc_memory: {})\n'.format(tracemalloc.get_traced_memory(), tracemalloc.get_tracemalloc_memory()) # # for stat in top_stats[:50]: # # s += '{}\n'.format(stat) # # # # with open('{}/logs/malloc.log'.format(public.get_panel_path()), 'a') as fp: # # fp.write('[{}]\n'.format(time.strftime('%Y-%m-%d %X'))) # # fp.write('{}\n'.format(s)) # # # # # # def objgraph_log(): # # with open('{}/logs/objgraph.log'.format(public.get_panel_path()), 'a') as fp: # # fp.write('[{}]\n'.format(time.strftime('%Y-%m-%d %X'))) # # fp.write('-------------------- SHOW GROWTH --------------------\n') # # objgraph.show_growth(limit=50, file=fp) # # fp.write('\n\n') # # # # fp.write('-------------------- SHOW MOST COMMON TYPES --------------------\n') # # objgraph.show_most_common_types(limit=50, file=fp) # # fp.write('\n\n') # # # # # # def print_malloc_thread(): # # time.sleep(60) # # log_malloc() # # objgraph_log() # # print_malloc_thread() # # # def get_python_bin(): # global python_bin # if python_bin: return python_bin # bin_file = '/www/server/panel/pyenv/bin/python' # bin_file2 = '/usr/bin/python' # if os.path.exists(bin_file): # python_bin = bin_file # return bin_file # python_bin = bin_file2 # return bin_file2 # def WriteFile(filename,s_body,mode='w+'): # """ # 写入文件内容 # @filename 文件名 # @s_body 欲写入的内容 # return bool 若文件不存在则尝试自动创建 # """ # try: # fp = open(filename, mode) # fp.write(s_body) # fp.close() # return True # except: # try: # fp = open(filename, mode,encoding="utf-8") # fp.write(s_body) # fp.close() # return True # except: # return False # # def ReadFile(filename, mode='r'): # """ # 读取文件内容 # @filename 文件名 # return string(bin) 若文件不存在,则返回None # """ # if not os.path.exists(filename): # return False # f_body = None # with open(filename, mode) as fp: # f_body = fp.read() # return f_body # # # 下载文件 # def DownloadFile(url, filename): # try: # import urllib # import socket # socket.setdefaulttimeout(10) # urllib.urlretrieve(url, filename=filename, reporthook=DownloadHook) # os.system('chown www.www ' + filename) # WriteLogs('done') # except: # WriteLogs('done') # # # # 下载文件进度回调 # def DownloadHook(count, blockSize, totalSize): # global pre # used = count * blockSize # pre1 = int((100.0 * used / totalSize)) # if pre == pre1: # return # speed = {'total': totalSize, 'used': used, 'pre': pre} # WriteLogs(dumps(speed)) # pre = pre1 # # # 写输出日志 # def WriteLogs(logMsg): # try: # global logPath # with open(logPath, 'w+') as fp: # fp.write(logMsg) # fp.close() # except: # pass # # # def ExecShell(cmdstring, cwd=None, timeout=None, shell=True, symbol = '&>'): # try: # global logPath # import shlex # import subprocess # import time # sub = subprocess.Popen(cmdstring+ symbol +logPath, cwd=cwd, # stdin=subprocess.PIPE, shell=shell, bufsize=4096) # # while sub.poll() is None: # time.sleep(0.1) # # return sub.returncode # except: # return None # # # # 任务队列 # def startTask(): # global isTask,logPath,thread_dict # tip_file = '/dev/shm/.panelTask.pl' # n = 0 # tick = 60 # while 1: # try: # if os.path.exists(isTask): # with db.Sql() as sql: # sql.table('tasks').where( # "status=?", ('-1',)).setField('status', '0') # taskArr = sql.table('tasks').where("status=?", ('0',)).field('id,type,execstr').order("id asc").select() # for value in taskArr: # start = int(time.time()) # if not sql.table('tasks').where("id=?", (value['id'],)).count(): # public.writeFile(tip_file, str(int(time.time()))) # continue # sql.table('tasks').where("id=?", (value['id'],)).save('status,start', ('-1', start)) # if value['type'] == 'download': # argv = value['execstr'].split('|bt|') # DownloadFile(argv[0], argv[1]) # elif value['type'] == 'execshell': # ExecShell(value['execstr']) # end = int(time.time()) # sql.table('tasks').where("id=?", (value['id'],)).save('status,end', ('1', end)) # if(sql.table('tasks').where("status=?", ('0')).count() < 1): # if os.path.exists(isTask): # os.remove(isTask) # sql.close() # taskArr = None # public.writeFile(tip_file, str(int(time.time()))) # # # 线程检查 # n+=1 # if n > tick: # run_thread() # n = 0 # except: # pass # time.sleep(2) # # # # # 网站到期处理 # def siteEdate(): # global oldEdate # try: # if not oldEdate: # oldEdate = ReadFile('/www/server/panel/data/edate.pl') # if not oldEdate: # oldEdate = '0000-00-00' # mEdate = time.strftime('%Y-%m-%d', time.localtime()) # if oldEdate == mEdate: # return False # oldEdate = mEdate # os.system("nohup " + get_python_bin() + " /www/server/panel/script/site_task.py > /dev/null 2>&1 &") # # except Exception as ex: # logging.info(ex) # pass # # # def GetLoadAverage(): # c = os.getloadavg() # data = {} # data['one'] = float(c[0]) # data['five'] = float(c[1]) # data['fifteen'] = float(c[2]) # data['max'] = cpu_count() * 2 # data['limit'] = data['max'] # data['safe'] = data['max'] * 0.75 # return data # # # # 系统监控任务 # def systemTask(): # try: # filename = '{}/data/control.conf'.format(base_path) # with db.Sql() as sql: # sql = sql.dbfile('system') # # csql = '''CREATE TABLE IF NOT EXISTS `load_average` ( # `id` INTEGER PRIMARY KEY AUTOINCREMENT, # `pro` REAL, # `one` REAL, # `five` REAL, # `fifteen` REAL, # `addtime` INTEGER # )''' # # network_sql = '''CREATE TABLE IF NOT EXISTS `network` ( # `id` INTEGER PRIMARY KEY AUTOINCREMENT, # `up` INTEGER, # `down` INTEGER, # `total_up` INTEGER, # `total_down` INTEGER, # `down_packets` INTEGER, # `up_packets` INTEGER, # `addtime` INTEGER # )''' # # cpuio_sql = '''CREATE TABLE IF NOT EXISTS `cpuio` ( # `id` INTEGER PRIMARY KEY AUTOINCREMENT, # `pro` INTEGER, # `mem` INTEGER, # `addtime` INTEGER # )''' # # diskio_sql = '''CREATE TABLE IF NOT EXISTS `diskio` ( # `id` INTEGER PRIMARY KEY AUTOINCREMENT, # `read_count` INTEGER, # `write_count` INTEGER, # `read_bytes` INTEGER, # `write_bytes` INTEGER, # `read_time` INTEGER, # `write_time` INTEGER, # `addtime` INTEGER # )''' # # # sql.execute(csql, ()) # sql.execute(network_sql, ()) # sql.execute(cpuio_sql, ()) # sql.execute(diskio_sql, ()) # # sql.close() # # count = 0 # reloadNum=0 # diskio_1 = diskio_2 = networkInfo = cpuInfo = diskInfo = None # network_up = {} # network_down = {} # cycle = 60 # # try: # # from panelDaily import panelDaily # # panelDaily().check_databases() # # except Exception as e: # # logging.info(e) # # proc_task_obj = process_task.process_task() # # while True: # if not os.path.exists(filename): # time.sleep(10) # continue # # day = 30 # try: # day = int(ReadFile(filename)) # if day < 1: # time.sleep(10) # continue # except: # day = 30 # # # addtime = int(time.time()) # deltime = addtime - (day * 86400) # # 取当前CPU Io # tmp = {} # tmp['used'] = proc_task_obj.get_monitor_list(addtime) # tmp['mem'] = GetMemUsed() # cpuInfo = tmp # # # 取当前网络Io # networkIo_list = net_io_counters(pernic=True) # tmp = {} # tmp['upTotal'] = 0 # tmp['downTotal'] = 0 # tmp['up'] = 0 # tmp['down'] = 0 # tmp['downPackets'] = {} # tmp['upPackets'] = {} # # for k in networkIo_list.keys(): # networkIo = networkIo_list[k][:4] # if not k in network_up.keys(): # network_up[k] = networkIo[0] # network_down[k] = networkIo[1] # # tmp['upTotal'] += networkIo[0] # tmp['downTotal'] += networkIo[1] # tmp['downPackets'][k] = round( # float((networkIo[1] - network_down[k]) / 1024)/cycle, 2) # tmp['upPackets'][k] = round( # float((networkIo[0] - network_up[k]) / 1024)/cycle, 2) # tmp['up'] += tmp['upPackets'][k] # tmp['down'] += tmp['downPackets'][k] # # network_up[k] = networkIo[0] # network_down[k] = networkIo[1] # # # if not networkInfo: # # networkInfo = tmp # # if (tmp['up'] + tmp['down']) > (networkInfo['up'] + networkInfo['down']): # networkInfo = tmp # # # 取磁盘Io # disk_ios = True # try: # if os.path.exists('/proc/diskstats'): # diskio_2 = disk_io_counters() # # if not diskio_1: # diskio_1 = diskio_2 # tmp = {} # tmp['read_count'] = int((diskio_2.read_count - diskio_1.read_count) / cycle) # tmp['write_count'] = int((diskio_2.write_count - diskio_1.write_count) / cycle) # tmp['read_bytes'] = int((diskio_2.read_bytes - diskio_1.read_bytes) / cycle) # tmp['write_bytes'] = int((diskio_2.write_bytes - diskio_1.write_bytes) / cycle) # tmp['read_time'] = int((diskio_2.read_time - diskio_1.read_time) / cycle) # tmp['write_time'] = int((diskio_2.write_time - diskio_1.write_time) / cycle) # # if not diskInfo: # diskInfo = tmp # # # if (tmp['read_bytes'] + tmp['write_bytes']) > (diskInfo['read_bytes'] + diskInfo['write_bytes']): # diskInfo['read_count'] = tmp['read_count'] # diskInfo['write_count'] = tmp['write_count'] # diskInfo['read_bytes'] = tmp['read_bytes'] # diskInfo['write_bytes'] = tmp['write_bytes'] # diskInfo['read_time'] = tmp['read_time'] # diskInfo['write_time'] = tmp['write_time'] # # # logging.info(['read: ',tmp['read_bytes'] / 1024 / 1024,'write: ',tmp['write_bytes'] / 1024 / 1024]) # diskio_1 = diskio_2 # except: # logging.info(public.get_error_info()) # disk_ios = False # # try: # sql = db.Sql().dbfile('system') # data = (cpuInfo['used'], cpuInfo['mem'], addtime) # # # sql.table('cpuio').add('pro,mem,addtime', data) # sql.table('cpuio').where("addtime 100: # lpro = 100 # sql.table('load_average').add('pro,one,five,fifteen,addtime', (lpro, load_average['one'], load_average['five'], load_average['fifteen'], addtime)) # sql.table('load_average').where("addtime 1440: # reloadNum = 0 # # # # # 日报数据收集 # # if os.path.exists("/www/server/panel/data/start_daily.pl"): # # try: # # from panelDaily import panelDaily # # pd = panelDaily() # # t_now = time.localtime() # # yesterday = time.localtime(time.mktime(( # # t_now.tm_year, t_now.tm_mon, t_now.tm_mday-1, # # 0,0,0,0,0,0 # # ))) # # yes_time_key = pd.get_time_key(yesterday) # # con = ReadFile("/www/server/panel/data/store_app_usage.pl") # # # logging.info(str(con)) # # store = False # # if con: # # if con != str(yes_time_key): # # store = True # # else: # # store = True # # # # if store: # # date_str = str(yes_time_key) # # daily_data = pd.get_daily_data_local(date_str) # # if "status" in daily_data.keys(): # # if daily_data["status"]: # # score = daily_data["score"] # # if public.M("system").dbfile("system").table("daily").where("time_key=?", (yes_time_key,)).count() == 0: # # public.M("system").dbfile("system").table("daily").add("time_key,evaluate,addtime", (yes_time_key, score, time.time())) # # pd.store_app_usage(yes_time_key) # # WriteFile("/www/server/panel/data/store_app_usage.pl", str(yes_time_key), "w") # # # logging.info("更新应用存储信息:"+str(yes_time_key)) # # pd.check_server() # # except Exception as e: # # logging.info("存储应用空间信息错误:"+str(e)) # except Exception as ex: # logging.info(str(ex)) # del(tmp) # time.sleep(cycle) # count += 1 # except Exception as ex: # logging.info(ex) # time.sleep(cycle) # systemTask() # # # # 取内存使用率 # def GetMemUsed(): # try: # mem = virtual_memory() # memInfo = {'memTotal': mem.total/1024/1024, 'memFree': mem.free/1024/1024, # 'memBuffers': mem.buffers/1024/1024, 'memCached': mem.cached/1024/1024} # tmp = memInfo['memTotal'] - memInfo['memFree'] - \ # memInfo['memBuffers'] - memInfo['memCached'] # tmp1 = memInfo['memTotal'] / 100 # return (tmp / tmp1) # except: # return 1 # # # # 更新 GeoLite2-Country.json # def flush_geoip(): # """ # @name 检测如果大小小于3M或大于1个月则更新 # @author wzz <2024/5/21 下午5:33> # @param "data":{"参数名":""} <数据类型> 参数描述 # @return dict{"status":True/False,"msg":"提示信息"} # """ # _ips_path = "/www/server/panel/data/firewall/GeoLite2-Country.json" # m_time_file = "/www/server/panel/data/firewall/geoip_mtime.pl" # if not os.path.exists(_ips_path): # os.system("mkdir -p /www/server/panel/data/firewall") # os.system("touch {}".format(_ips_path)) # # try: # if not os.path.exists(_ips_path): # public.downloadFile('{}/install/lib/{}'.format(public.get_url(), os.path.basename(_ips_path)), _ips_path) # public.writeFile(m_time_file, str(int(time.time()))) # return # # _ips_size = os.path.getsize(_ips_path) # if os.path.exists(m_time_file): # _ips_mtime = int(public.readFile(m_time_file)) # else: # _ips_mtime = 0 # # if _ips_size < 3145728 or time.time() - _ips_mtime > 2592000: # core = cpu_count() # delay = round(1 / (core if core > 0 else 1), 2) # os.system("rm -f {}".format(_ips_path)) # os.system("rm -f {}".format(m_time_file)) # public.downloadFile('{}/install/lib/{}'.format(public.get_url(), os.path.basename(_ips_path)), _ips_path) # public.writeFile(m_time_file, str(int(time.time()))) # if os.path.exists(_ips_path): # try: # import json # from xml.etree.ElementTree import ElementTree, Element # from safeModelV2.firewallModel import main as firewall # # firewallobj = firewall() # ips_list = json.loads(public.readFile(_ips_path)) # if ips_list: # bash = os.path.exists('/usr/bin/apt-get') and not os.path.exists("/etc/redhat-release") # if bash: # btsh_path = "/etc/ufw/btsh" # if not os.path.exists(btsh_path): # os.makedirs(btsh_path) # # write_map = {} # for ip_dict in ips_list: # tmp_path = '{}/{}.sh'.format(btsh_path, ip_dict['brief']) # commands = [ # f'ipset add {ip_dict["brief"]} {ip}' # for ip in ip_dict.get("ips", []) # if firewallobj.verify_ip(ip) # ] # if commands: # script_content = "#!/bin/bash\n" + "\n".join(commands) + "\n" # write_map[tmp_path] = script_content # time.sleep(delay) # # for path, content in write_map.items(): # public.writeFile(path, content) # time.sleep(0.05) # else: # for ip_dict in ips_list: # xml_path = "/etc/firewalld/ipsets/{}.xml.old".format(ip_dict['brief']) # xml_body = """ # # # """ # if os.path.exists(xml_path): # public.writeFile(xml_path, xml_body) # else: # os.makedirs(os.path.dirname(xml_path), exist_ok=True) # public.writeFile(xml_path, xml_body) # # tree = ElementTree() # tree.parse(xml_path) # root = tree.getroot() # for ip in ip_dict['ips']: # if firewallobj.verify_ip(ip): # entry = Element("entry") # entry.text = ip # root.append(entry) # # firewallobj.format(root) # tree.write(xml_path, 'utf-8', xml_declaration=True) # time.sleep(delay) # except: # pass # except: # try: # public.downloadFile( # '{}/install/lib/{}'.format(public.get_url(), os.path.basename(_ips_path)), _ips_path # ) # public.writeFile(m_time_file, str(int(time.time()))) # except: # pass # # # 检查502错误 # def check502(): # try: # phpversions = public.get_php_versions() # for version in phpversions: # if version in ['52','5.2']: continue # php_path = '/www/server/php/' + version + '/sbin/php-fpm' # if not os.path.exists(php_path): # continue # if checkPHPVersion(version): # continue # if startPHPVersion(version): # public.WriteLog('PHP daemon', # 'PHP-' + version + 'processing exception was detected and has been automatically fixed!', # not_web=True) # except Exception as ex: # logging.info(ex) # # # 处理指定PHP版本 # def startPHPVersion(version): # try: # fpm = '/etc/init.d/php-fpm-' + version # php_path = '/www/server/php/' + version + '/sbin/php-fpm' # if not os.path.exists(php_path): # if os.path.exists(fpm): os.remove(fpm) # return False # # # 尝试重载服务 # os.system(fpm + ' start') # os.system(fpm + ' reload') # if checkPHPVersion(version): return True # # # 尝试重启服务 # cgi = '/tmp/php-cgi-' + version + '.sock' # pid = '/www/server/php/' + version + '/var/run/php-fpm.pid' # os.system('pkill -9 php-fpm-' + version) # time.sleep(0.5) # if os.path.exists(cgi): # os.remove(cgi) # if os.path.exists(pid): # os.remove(pid) # os.system(fpm + ' start') # if checkPHPVersion(version): # return True # # 检查是否正确启动 # if os.path.exists(cgi): # return True # return False # except Exception as ex: # logging.info(ex) # return True # # # # 检查指定PHP版本 # def checkPHPVersion(version): # try: # cgi_file = '/tmp/php-cgi-{}.sock'.format(version) # if os.path.exists(cgi_file): # init_file = '/etc/init.d/php-fpm-{}'.format(version) # if os.path.exists(init_file): # init_body = public.ReadFile(init_file) # if not init_body: return True # uri = "/phpfpm_"+version+"_status?json" # result = public.request_php(version, uri, '') # loads(result) # return True # except: # logging.info("PHP-{} unreachable detected".format(version)) # return False # # # # 502错误检查线程 # def check502Task(): # try: # while True: # public.auto_backup_panel() # check502() # sess_expire() # mysql_quota_check() # siteEdate() # flush_geoip() # time.sleep(600) # except Exception as ex: # logging.info(ex) # time.sleep(600) # check502Task() # # # MySQL配额检查 # def mysql_quota_check(): # os.system("nohup " + get_python_bin() +" /www/server/panel/script/mysql_quota.py > /dev/null 2>&1 &") # # # session过期处理 # def sess_expire(): # try: # sess_path = '{}/data/session'.format(base_path) # if not os.path.exists(sess_path): return # s_time = time.time() # f_list = os.listdir(sess_path) # f_num = len(f_list) # for fname in f_list: # filename = '/'.join((sess_path, fname)) # fstat = os.stat(filename) # f_time = s_time - fstat.st_mtime # if f_time > 3600: # os.remove(filename) # continue # if fstat.st_size < 256 and len(fname) == 32: # if f_time > 60 or f_num > 30: # os.remove(filename) # continue # del (f_list) # # except Exception as ex: # logging.info(str(ex)) # # # # 检查面板证书是否有更新 # def check_panel_ssl(): # try: # while True: # lets_info = ReadFile("{}/ssl/lets.info".format(base_path)) # if not lets_info: # time.sleep(3600) # continue # os.system(get_python_bin() + " {}/script/panel_ssl_task.py > /dev/null".format(base_path)) # time.sleep(3600) # except Exception as e: # public.writeFile("/tmp/panelSSL.pl", str(e), "a+") # # # 面板进程守护 # def daemon_panel11(): # cycle = 10 # panel_pid_file = "{}/logs/panel.pid".format(public.get_panel_path()) # while 1: # time.sleep(cycle) # # # 检查pid文件是否存在 # if not os.path.exists(panel_pid_file): # continue # # # 读取pid文件 # panel_pid = public.readFile(panel_pid_file) # if not panel_pid: # logging.info("not pid -- {}".format(panel_pid_file)) # service_panel('start') # continue # # # 检查进程是否存在 # comm_file = "/proc/{}/comm".format(panel_pid) # if not os.path.exists(comm_file): # logging.info("not comm_file-- {}".format(comm_file)) # service_panel('start') # continue # # # 是否为面板进程 # comm = public.readFile(comm_file) # if comm.find('Yak-Panel') == -1: # logging.info("not Yak-Panel-- {}".format(comm)) # service_panel('start') # continue # # # # 是否为面板进程 # # with open(comm_file, 'r') as f: # # comm = f.read() # # if comm.find('Yak-Panel') == -1: # # logging.info("3 not Yak-Panel-- {}".format(comm)) # # service_panel('start') # # continue # # # # 查找面板进程并返回PID # def find_panel_pid(): # for pid in pids(): # try: # p = Process(pid) # if 'Yak-Panel' in p.name(): # 假设进程名包含 'Yak-Panel' # return pid # except (NoSuchProcess, AccessDenied, ZombieProcess): # continue # return None # # # 更新PID文件 # def update_pid_file(pid): # pid_file = "{}/logs/panel.pid".format(public.get_panel_path()) # try: # with open(pid_file, 'w') as f: # f.write(str(pid)) # logging.info(f'Updated panel PID file with PID {pid}') # except Exception as e: # logging.error(f'Error writing to PID file: {e}') # # # def daemon_panel(): # cycle = 10 # panel_pid_file = "{}/logs/panel.pid".format(public.get_panel_path()) # # while True: # time.sleep(cycle) # # # 检查PID文件是否存在 # if not os.path.exists(panel_pid_file): # logging.info(f'{panel_pid_file} not found, starting panel service...') # continue # # panel_pid="" # try: # # 读取PID文件 # with open(panel_pid_file, 'r') as file: # panel_pid = file.read() # except Exception as e: # service_panel('start') # continue # if not panel_pid: # logging.info(f'PID is empty in {panel_pid_file}, starting panel service...') # service_panel('start') # continue # panel_pid = panel_pid.strip() # # 检查PID对应的进程是否存在 # if not pid_exists(int(panel_pid)): # logging.info(f'PID {panel_pid} not found, attempting to find running panel process...') # panel_pid = find_panel_pid() # # if panel_pid: # # 更新PID文件 # update_pid_file(panel_pid) # else: # logging.info('No panel process found, starting service...') # service_panel('start') # # else: # # 检查进程是否是面板进程 # comm_file = f"/proc/{panel_pid}/comm" # if os.path.exists(comm_file): # with open(comm_file, 'r') as file: # comm = file.read() # if not comm or comm.find("Yak-Panel") == -1: # logging.info(f'Process {panel_pid} is not a Yak-Panel process,comm-{comm} commtype-{type(comm)}') # service_panel('start') # continue # # else: # logging.info(f'comm file not found for PID {panel_pid}, restarting service...') # service_panel('start') # # def daemon_service(): # from script.restart_services import RestartServices, DaemonManager # try: # # remove old deamons # from YakPanel import app # from crontab_v2 import crontab # with app.app_context(): # old_deamons = public.M('crontab').where("name Like ?", "[Do not delete]%Daemon").select() # for i in old_deamons: # try: # cron_name = i.get("name") # s_name = cron_name.replace("[Do not delete] ", "").replace(" Daemon", "") # DaemonManager.add_daemon(s_name.lower()) # except: # pass # args = public.dict_obj() # args.id = i.get("id") # crontab().DelCrontab(args) # except: # pass # # while 1: # RestartServices().main() # time.sleep(10) # # # def update_panel(): # os.system("curl -k https://node.yakpanel.com/install/update_7.x_en.sh|bash &") # # # def service_panel(action='reload'): # if not os.path.exists('{}/init.sh'.format(base_path)): # update_panel() # else: # os.system("nohup bash /www/server/panel/init.sh {} > /dev/null 2>&1 &".format(action)) # logging.info("Panel Service: {}".format(action)) # # # # 重启面板服务 # def restart_panel_service(): # rtips = '{}/data/restart.pl'.format(base_path) # reload_tips = '{}/data/reload.pl'.format(base_path) # while True: # if os.path.exists(rtips): # os.remove(rtips) # service_panel('restart') # if os.path.exists(reload_tips): # os.remove(reload_tips) # service_panel('reload') # time.sleep(1) # # # 取面板pid # def get_panel_pid(): # try: # pid = ReadFile('/www/server/panel/logs/panel.pid') # if pid: # return int(pid) # for pid in pids(): # try: # p = Process(pid) # n = p.cmdline()[-1] # if n.find('runserver') != -1 or n.find('Yak-Panel') != -1: # return pid # except: # pass # except: # pass # return None # # # def HttpGet(url, timeout=6, headers={}): # if sys.version_info[0] == 2: # try: # import urllib2 # req = urllib2.Request(url, headers=headers) # response = urllib2.urlopen(req, timeout=timeout,) # return response.read() # except Exception as ex: # logging.info(str(ex)) # return str(ex) # else: # try: # import urllib.request # req = urllib.request.Request(url, headers=headers) # response = urllib.request.urlopen(req, timeout=timeout) # result = response.read() # if type(result) == bytes: # result = result.decode('utf-8') # return result # except Exception as ex: # logging.info("URL: {} => {}".format(url, ex)) # return str(ex) # # # # 定时任务去检测邮件信息 # def send_mail_time(): # while True: # try: # os.system("nohup " + get_python_bin() +" /www/server/panel/script/mail_task.py > /dev/null 2>&1 &") # time.sleep(180) # except: # time.sleep(360) # send_mail_time() # # #5个小时更新一次更新软件列表 # def update_software_list(): # while True: # try: # import panelPlugin # panelPlugin.panelPlugin().get_cloud_list_status(None) # time.sleep(18000) # except: # time.sleep(1800) # update_software_list() # # # 面板消息提醒 # def check_panel_msg(): # python_bin = get_python_bin() # while True: # os.system('nohup {} /www/server/panel/script/check_msg.py > /dev/null 2>&1 &'.format(python_bin)) # time.sleep(3600) # # # 面板推送消息 # def push_msg(): # python_bin = get_python_bin() # while True: # time.sleep(60) # os.system('nohup {} /www/server/panel/script/push_msg.py > /dev/null 2>&1 &'.format(python_bin)) # # def JavaProDaemons(): # ''' # @name Java 项目守护进程 # @author lkq@yakpanel.com # @time 2022-07-19 # @param None # ''' # if public.M('sites').where('project_type=?',('Java')).count()>=1: # project_info=public.M('sites').where('project_type=?',('Java')).select() # for i in project_info: # try: # import json # i['project_config'] = json.loads(i['project_config']) # #判断项目是否设置了守护进程 # if i['project_config']['java_type']!='springboot':continue # if 'auth' in i['project_config'] and i['project_config']['auth']==1 or i['project_config']['auth']=='1': # print("Java",i['name']) # from projectModel import javaModel # java = javaModel.main() # if java.get_project_run_state(project_name=i['name']): # continue # else: # #如果项目是在后台停止的,那么就不再启动 # if os.path.exists("/var/tmp/springboot/vhost/pids/{}.pid".format(i['name'])): # get=public.dict_obj() # get.project_name=i['name'] # java.start_project(get) # public.WriteLog('守护进程','Java项目[{}]已经被守护进程启动'.format(i['name'])) # except: # continue # # def ProLog(): # path_list=["/www/server/go_project/vhost/logs","/var/tmp/springboot/vhost/logs/"] # try: # for i2 in path_list: # if os.path.exists(i2): # for dir in os.listdir(i2): # dir = os.path.join(i2, dir) # # 判断当前目录是否为文件夹 # if os.path.isfile(dir): # if dir.endswith(".log"): # #文件大于500M的时候则清空文件 # if os.stat(dir).st_size >200000000: # public.ExecShell("echo ''>{}".format(dir)) # except: # pass # # def ProDadmons(): # ''' # @name 项目守护进程 # @author # ''' # n = 30 # while 1: # n += 1 # if n >= 30: # n = 1 # ProLog() # time.sleep(120) # try: # JavaProDaemons() # except: # pass # # def process_task_thread(): # ''' # @name 进程监控 # @auther hwliang # ''' # # # # 进程流量监控,如果文件:/www/server/panel/data/is_net_task.pl 或 /www/server/panel/data/control.conf不存在,则不监控进程流量 # net_task_obj = process_task.process_network_total() # net_task_obj.start() # # # 检测面板授权 # def check_panel_auth(): # while True: # pro_file='/www/server/panel/data/panel_pro.pl' # update_file='/www/server/panel/data/now_update_pro.pl' # if os.path.exists(pro_file): # python_bin = get_python_bin() # from YakPanel import cache # if cache: # key='pro_check_sdfjslk' # res = cache.get(key) # if os.path.exists(update_file) or res is None: # os.system('nohup {} /www/server/panel/script/check_auth.py > /dev/null 2>&1 &'.format(python_bin)) # if cache: # cache.set(key, 'sddsf', 3600) # if os.path.exists(update_file):os.remove(update_file) # time.sleep(2) # # def count_ssh_logs(): # ''' # @name 统计SSH登录日志 # @return None # ''' # # 重启后延迟执行 避开cpu高峰期 # time.sleep(1800) # if os.path.exists("/etc/debian_version"): # version = public.readFile('/etc/debian_version') # if not version: # return # version = 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: # while True: # filepath = "/www/server/panel/data/ssh_login_counts.json" # # # 获取今天的日期 # today = datetime.now().strftime('%Y-%m-%d') # result = { # 'date': today, # 添加日期字段 # 'error': 0, # 'success': 0, # 'today_error': 0, # 'today_success': 0 # } # # public.print_log("进入计算count_ssh_logs--{}") # try: # filedata = public.readFile(filepath) if os.path.exists(filepath) else public.writeFile(filepath, # "[]") # try: # data_list = json.loads(filedata) # except: # data_list = [] # # # 检查是否已有今天的记录,避免重复统计 # found_today = False # for day in data_list: # if day['date'] == today: # found_today = True # # public.print_log("进入计算count_ssh_logs--{退出1}") # break # # if found_today: # # public.print_log("进入计算count_ssh_logs--{退出2}") # break # 如果找到今天的记录,跳出while循环 # # # today_err_num1 = int(public.ExecShell( # "journalctl -u ssh --no-pager -S today |grep -a 'Failed password for' |grep -v 'invalid' |wc -l")[ # 0]) # # today_err_num2 = int(public.ExecShell( # "journalctl -u ssh --no-pager -S today |grep -a 'Connection closed by authenticating user' |grep -a 'preauth' |wc -l")[ # 0]) # # today_success = int( # public.ExecShell("journalctl -u ssh --no-pager -S today |grep -a 'Accepted' |wc -l")[0]) # # # 查看文件大小 判断是否超过5G # is_bigfile = False # # res, err = public.ExecShell("journalctl --disk-usage") # # import re # import json # total_bytes = parse_journal_disk_usage(res) # # public.print_log("文件大小--{}".format(total_bytes)) # limit_bytes = 5 * 1024 * 1024 * 1024 # if total_bytes > limit_bytes: # is_bigfile = True # # if is_bigfile: # # public.print_log("取30天--{}".format(total_bytes)) # err_num1 = int(public.ExecShell("journalctl -u ssh --since '30 days ago' --no-pager | grep -a 'Failed password for' | grep -v 'invalid' | wc -l")[0]) # err_num2 = int(public.ExecShell("journalctl -u ssh --since '30 days ago' --no-pager --grep='Connection closed by authenticating user|preauth' | wc -l")[0]) # success = int(public.ExecShell("journalctl -u ssh --since '30 days ago' --no-pager | grep -a 'Accepted' | wc -l")[0]) # else: # # public.print_log("取所有--{}") # # 统计失败登陆次数 # err_num1 = int(public.ExecShell("journalctl -u ssh --no-pager |grep -a 'Failed password for' |grep -v 'invalid' |wc -l")[0]) # err_num2 = int(public.ExecShell("journalctl -u ssh --no-pager --grep='Connection closed by authenticating user|preauth' |wc -l")[0]) # success = int(public.ExecShell("journalctl -u ssh --no-pager|grep -a 'Accepted' |wc -l")[0]) # # public.print_log("计算完毕22--{}") # result['error'] = err_num1 + err_num2 # # 统计成功登录次数 # result['success'] = success # result['today_error'] = today_err_num1 + today_err_num2 # result['today_success'] = today_success # # data_list.insert(0, result) # data_list = data_list[:7] # public.writeFile(filepath, json.dumps(data_list)) # except: # public.print_log(public.get_error_info()) # public.writeFile(filepath, json.dumps([{ # 'date': today, # 添加日期字段 # 'error': 0, # 'success': 0, # 'today_error': 0, # 'today_success': 0 # }])) # time.sleep(86400) # else: # time.sleep(86400) # # # def parse_journal_disk_usage(output): # import re # # 使用正则表达式来提取数字和单位 # match = re.search(r'take up (\d+(\.\d+)?)\s*([KMGTP]?)', output) # total_bytes = 0 # if match: # value = float(match.group(1)) # 数字 # unit = match.group(3) # 单位 # # public.print_log("匹配字符--value>{} unit>{}".format(value, unit)) # # 将所有单位转换为字节 # if unit == '': # unit_value = 1 # elif unit == 'K': # unit_value = 1024 # elif unit == 'M': # unit_value = 1024 * 1024 # elif unit == 'G': # unit_value = 1024 * 1024 * 1024 # elif unit == 'T': # unit_value = 1024 * 1024 * 1024 * 1024 # elif unit == 'P': # unit_value = 1024 * 1024 * 1024 * 1024 * 1024 # else: # unit_value = 0 # # # 计算总字节数 # total_bytes = value * unit_value # return total_bytes # # def update_vulnerabilities(): # if "/www/server/panel/class_v2/wp_toolkit/" not in sys.path: # sys.path.insert(1, "/www/server/panel/class_v2/wp_toolkit/") # import totle_db # import requests, json # requests.packages.urllib3.disable_warnings() # # def auto_scan(): # ''' # @name 自动扫描 # @msg 一天一次 # :return: # ''' # path_time = "/www/server/panel/data/auto_scan.pl" # if not os.path.exists(path_time): # public.writeFile(path_time, json.dumps({"time": int(time.time())})) # share_ip_info = {"time": 0} # else: # share_ip_info = json.loads(public.readFile(path_time)) # if (int(time.time()) - share_ip_info["time"]) < 86400: # return public.returnMsg(False, "未达到时间") # share_ip_info["time"] = int(time.time()) # public.writeFile(path_time, json.dumps(share_ip_info)) # import wordpress_scan # wordpress_scan.wordpress_scan().auto_scan() # # def M(table, db="wordpress_vulnerabilities"): # ''' # @name 获取数据库对象 # @param table 表名 # @param db 数据库名 # ''' # with totle_db.Sql(db) as sql: # return sql.table(table) # # def check_vlun(): # path_time = "/www/server/panel/data/wordpress_check_vlun.pl" # if not os.path.exists(path_time): # public.writeFile(path_time, json.dumps({"time": int(time.time())})) # share_ip_info = {"time": 0} # else: # share_ip_info = json.loads(public.readFile(path_time)) # if (int(time.time()) - share_ip_info["time"]) < 86400: # return public.returnMsg(False, "未达到时间") # share_ip_info["time"] = int(time.time()) # public.writeFile(path_time, json.dumps(share_ip_info)) # load_time = M("wordpress_vulnerabilities", "wordpress_vulnerabilities").order("data_time desc").limit( # "1").field( # "data_time").find() # if type(load_time) != dict: return # # def get_yun_infos(page): # url = "https://wafapi2.yakpanel.com/api/bt_waf/get_wordpress_scan?size=100&p=" + str(page) # yun_infos = requests.get(url, verify=False, timeout=60).json() # for i in yun_infos['res']: # if i['data_time'] > load_time['data_time']: # del i['id'] # M("wordpress_vulnerabilities", "wordpress_vulnerabilities").insert(i) # else: # return True # # for i in range(1, 20): # time.sleep(26) # if get_yun_infos(i): # return # # def check_plugin_close(): # ''' # @name 检查插件是否关闭 # @return True 关闭 # @return False 开启 # ''' # path_time = "/www/server/panel/data/wordpress_check_plugin_close.pl" # if not os.path.exists(path_time): # public.writeFile(path_time, json.dumps({"time": int(time.time())})) # share_ip_info = {"time": 0} # else: # share_ip_info = json.loads(public.readFile(path_time)) # if (int(time.time()) - share_ip_info["time"]) < 86400: # return public.returnMsg(False, "未达到时间") # share_ip_info["time"] = int(time.time()) # public.writeFile(path_time, json.dumps(share_ip_info)) # check_sql=M("plugin_error", "plugin_error").order("id desc").limit("1").field("id").find() # if type(check_sql) != dict: return # time.sleep(30) # url = "https://wafapi2.yakpanel.com/api/bt_waf/plugin_error_list" # try: # res = requests.get(url, verify=False, timeout=60).json() # except: # return False # res_list = res['res'] # for i in res_list: # if M("plugin_error", "plugin_error").where("slug=? and status=0", i['slug']).count() > 0: # M("plugin_error", "plugin_error").where("slug=?", i['slug']).update({"status": 1}) # # def get_plugin_update_time(): # ''' # @name 获取插件更新时间 # @ps 一周更新一次 # ''' # path_time = "/www/server/panel/data/wordpress_get_plugin_update_time.pl" # if not os.path.exists(path_time): # public.writeFile(path_time, json.dumps({"time": int(time.time())})) # #如果第一次运行则三天后再运行 # share_ip_info = {"time": int(time.time())-86400*2} # else: # share_ip_info = json.loads(public.readFile(path_time)) # if (int(time.time()) - share_ip_info["time"]) < 259200: # return public.returnMsg(False, "未达到时间") # share_ip_info["time"] = int(time.time()) # public.writeFile(path_time, json.dumps(share_ip_info)) # # check_sql=M("wordpress_not_update", "wordpress_not_update").order("id desc").limit("1").field("id").find() # if type(check_sql) != dict: return # import random # def get_plugin_time(id): # time.sleep(30) # url = "https://wafapi2.yakpanel.com/api/bt_waf/get_wordpress_not_update?p=" + str(id) # try: # res = requests.get(url, verify=False, timeout=60).json() # if len(res['res']) == 0: # return True # for i in res['res']: # if M("wordpress_not_update", "wordpress_not_update").where("slug=?", i['slug']).count() > 0: # if M("wordpress_not_update", "wordpress_not_update").where("slug=? and last_time=?", ( # i['slug'], i['last_time'])).count() == 0: # M("wordpress_not_update", "wordpress_not_update").where("slug=?", i['slug']).update( # {"last_time": i['last_time']}) # # 随机时间 # except: # pass # # for i in range(1, 50): # time.sleep(random.randint(10, 30)) # if get_plugin_time(i): # return # # while True: # try: # auto_scan() # check_vlun() # check_plugin_close() # get_plugin_update_time() # time.sleep(7200) # except: # time.sleep(3600) # # # 每天提交一次昨天的邮局发送总数 # def submit_email_statistics(): # # 添加提交标记 每次提交昨天的 标记存在跳过 不存在添加 删除前天标记 # yesterday = datetime.now() - timedelta(days=1) # yesterday = yesterday.strftime('%Y-%m-%d') # cloud_yesterday_submit = f'/www/server/panel/data/{yesterday}_submit_email_statistics.pl' # if os.path.exists(cloud_yesterday_submit): # return # # 判断是否有邮局 # if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_send_bulk.py') or not os.path.exists('/www/vmail'): # return # # # 处理昨天数据 # all = _get_yesterday_count2() # if not all: # return # # # 记录昨日发件总数 # public.set_module_logs('sys_mail', 'sent', all) # # # 添加标记 # public.writeFile(cloud_yesterday_submit, '1') # # 删除前天标记 # before_yesterday = datetime.now() - timedelta(days=2) # before_yesterday = before_yesterday.strftime('%Y-%m-%d') # cloud_before_yesterday_submit = f'/www/server/panel/data/{before_yesterday}_submit_email_statistics.pl' # if os.path.exists(cloud_before_yesterday_submit): # os.remove(cloud_before_yesterday_submit) # # # # def submit_module_call_statistics(): # # 添加提交标记 每次提交昨天的 标记存在跳过 不存在添加 删除前天标记 提交过的数据在统计文件里删除 # yesterday = datetime.now() - timedelta(days=1) # yesterday = yesterday.strftime('%Y-%m-%d') # cloud_yesterday_submit = '{}/data/{}_submit_module_call_statistics.pl'.format(public.get_panel_path(), yesterday) # if os.path.exists(cloud_yesterday_submit): # return # # # 取文件中yesterday和yesterday以前的数据 # datainfo = {} # path = '{}/data/mod_log.json'.format(public.get_panel_path()) # if os.path.exists(path): # try: # datainfo = json.loads(public.readFile(path)) # except: # pass # else: # return # # if type(datainfo) != dict: # datainfo = {} # # 需要提交的 # data_submit = {} # # 当天数据 # data_reserve = {} # # try: # for date, modules in datainfo.items(): # # 如果日期小于昨天,则提交数据 # if date.strip() <= yesterday.strip(): # data_submit[date] = modules # else: # data_reserve[date] = modules # except: # public.print_log(public.get_error_info()) # # if not data_submit: # return # _submit_to_cloud(data_submit) # # # 将data_reserve 重新写入 # public.writeFile(path, json.dumps(data_reserve)) # # # 添加标记 # public.writeFile(cloud_yesterday_submit, '1') # # 删除前天标记 # before_yesterday = datetime.now() - timedelta(days=2) # before_yesterday = before_yesterday.strftime('%Y-%m-%d') # cloud_before_yesterday_submit = '{}/data/{}_submit_module_call_statistics.pl'.format(public.get_panel_path(), before_yesterday) # if os.path.exists(cloud_before_yesterday_submit): # os.remove(cloud_before_yesterday_submit) # # print("提交完") # return # # def mailsys_domain_restrictions(): # if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_send_bulk.py'): # # public.print_log('000 /mail_send_bulk') # # print('000 /mail_send_bulk') # return # # if not os.path.exists('/www/vmail'): # # public.print_log('000 /www/vmail') # # print('000 /www/vmail') # return # # yesterday = datetime.now() - timedelta(days=1) # yesterday = yesterday.strftime('%Y-%m-%d') # cloud_yesterday_submit = '{}/data/{}_update_mailsys_domain_restrictions.pl'.format(public.get_panel_path(), yesterday) # if os.path.exists(cloud_yesterday_submit): # return # # if os.path.exists("/www/server/panel/plugin/mail_sys"): # sys.path.insert(1, "/www/server/panel/plugin/mail_sys") # # 检查版本 检查是否能查询额度 剩余额度 # import public.PluginLoader as plugin_loader # bulk = plugin_loader.get_module('{}/plugin/mail_sys/mail_send_bulk.py'.format(public.get_panel_path())) # SendMailBulk = bulk.SendMailBulk # try: # SendMailBulk()._get_user_quota() # # public.print_log('111 调用插件方法检查额度') # # print('111 调用插件方法检查额度') # except: # public.print_log(public.get_error_info()) # return # # # # 添加标记 # public.writeFile(cloud_yesterday_submit, '1') # # 删除前天标记 # before_yesterday = datetime.now() - timedelta(days=2) # before_yesterday = before_yesterday.strftime('%Y-%m-%d') # cloud_before_yesterday_submit = '{}/data/{}_update_mailsys_domain_restrictions.pl'.format(public.get_panel_path(), before_yesterday) # if os.path.exists(cloud_before_yesterday_submit): # os.remove(cloud_before_yesterday_submit) # return # # # def _submit_to_cloud(data_submit): # """提交用户统计数据 接口调用 安装量等 """ # import panelAuth # import requests # from YakPanel import session # cloudUrl = '{}/api/panel/submit_feature_invoked_bulk'.format(public.OfficialApiBase()) # pdata = panelAuth.panelAuth().create_serverid(None) # url_headers = {} # if 'token' in pdata: # url_headers = {"authorization": "bt {}".format(pdata['token'])} # # pdata['environment_info'] = json.dumps(public.fetch_env_info()) # # # pdata['data'] = data_submit # pdata['utc_offset'] = _get_utc_offset_modele() # listTmp = requests.post(cloudUrl, json=pdata, headers=url_headers) # ret = listTmp.json() # # public.print_log(f"提交上传['data']999 ---{pdata['data']}") # # public.print_log(f"提交返回999 ---{ret}") # # if not ret['success']: # # print(f"|-{ret['res']}") # # public.print_log(f"|-submit_module_call_statistics: {ret['res']}") # # public.print_log(f"|-{ret}") # return # # # # 获取系统时间与utc时间的差值 # def _get_utc_offset_modele(): # # 系统时间戳 # current_local_time = datetime.now() # current_local_timestamp = int(current_local_time.timestamp()) # # # 获取当前 UTC 时间的时间戳 # current_utc_time = datetime.utcnow() # current_utc_timestamp = int(current_utc_time.timestamp()) # # # 计算时区差值(秒) # timezone_offset = current_local_timestamp - current_utc_timestamp # offset = timezone_offset/3600 # # return offset # # # # 获取系统时间与utc时间的差值 # def _get_utc_offset(): # # 系统时间戳 # current_local_time = datetime.now() # current_local_timestamp = int(current_local_time.timestamp()) # # # UTC时间戳 # current_utc_time = datetime.utcnow() # current_utc_timestamp = int(current_utc_time.timestamp()) # # # 计算时区差值(秒) # timezone_offset = current_local_timestamp - current_utc_timestamp # # if timezone_offset > 0: # 东时区 需要减 # return timezone_offset, True # else: # 西时区 # return abs(timezone_offset), False # # # def _get_yesterday_count2(): # # 获取昨天的开始时间和结束时间(本地时间) # today = datetime.now() # yesterday = today - timedelta(days=1) # # # 昨天 00:00:00 # yesterday_start = datetime(yesterday.year, yesterday.month, yesterday.day, 0, 0, 0) # # # 昨天 23:59:59 # yesterday_end = datetime(yesterday.year, yesterday.month, yesterday.day, 23, 59, 59) # # # 转为时间戳 # start_time = int(yesterday_start.timestamp()) # end_time = int(yesterday_end.timestamp()) # # try: # query = public.S('send_mails').alias('rm').prefix('') # query.inner_join('senders s', 'rm.postfix_message_id=s.postfix_message_id') # query.where('s.postfix_message_id is not null') # if start_time > 0: # query.where('rm.log_time > ?', start_time - 1) # if end_time > 0: # query.where('rm.log_time < ?', end_time + 1) # # query.where('rm.status =?', 'sent') # from power_mta.maillog_stat import query_maillog_with_time_section # ret = query_maillog_with_time_section(query, start_time, end_time) # allnum = len(ret) # except: # allnum = 0 # return allnum # # # # 获取昨日邮局收发件统计数据 # # def _get_yesterday_count(): # # maillog_path = '/var/log/maillog' # # if "ubuntu" in public.get_linux_distribution().lower(): # # maillog_path = '/var/log/mail.log' # # # # if not os.path.exists(maillog_path): # # return 0 # # output, err = public.ExecShell( # # f'pflogsumm -d yesterday --verbose-msg-detail --zero-fill --iso-date-time --rej-add-from {maillog_path}') # # if err: # # # public.print_log(f"err 4444 {err}") # # return 0 # # # # data = _pflogsumm_data_treating(output) # # all = 0 # # # public.print_log(f"yesterday sent: {data['stats_dict']}") # # if data.get('stats_dict', None): # # all = data['stats_dict'].get('delivered', 0) # # return all # # # # # 分析命令执行后的数据 # # def _pflogsumm_data_treating(output): # # import re # # stats_dict = {} # # # # # 使用正则表达式来匹配和提取关键信息 # # patterns = [ # # r'(\d+)\s+received', # # r'(\d+)\s+delivered', # # r'(\d+)\s+forwarded', # # r'(\d+)\s+deferred\s+\((\d+)\s+deferrals\)', # # r'(\d+)\s+bounced', # # r'(\d+)\s+rejected\s+\((\d+)%\)', # # r'(\d+)\s+reject\s+warnings', # # r'(\d+)\s+held', # # r'(\d+)\s+discarded\s+\((\d+)%\)', # # r'(\d+)\s+bytes\s+received', # # r'(\d+)k\s+bytes\s+delivered', # # r'(\d+)\s+senders', # # r'(\d+)\s+sending\s+hosts/domains', # # r'(\d+)\s+recipients', # # r'(\d+)\s+recipient\s+hosts/domains' # # ] # # # # for pattern in patterns: # # match = re.search(pattern, output) # # if match: # # # 将找到的数字转换为整数并存入字典 # # stats_dict[pattern] = int(match.group(1)) # # # # friendly_names = { # # r'(\d+)\s+received': 'received', # # r'(\d+)\s+delivered': 'delivered', # # r'(\d+)\s+forwarded': 'forwarded', # # r'(\d+)\s+deferred\s+\((\d+)\s+deferrals\)': 'deferred', # # r'(\d+)\s+bounced': 'bounced', # # r'(\d+)\s+rejected\s+\((\d+)%\)': 'rejected', # # r'(\d+)\s+reject\s+warnings': 'reject_warnings', # # r'(\d+)\s+held': 'held', # # r'(\d+)\s+discarded\s+\((\d+)%\)': 'discarded', # # r'(\d+)\s+bytes\s+received': 'bytes_received', # # r'(\d+)k\s+bytes\s+delivered': 'bytes_delivered_kilo', # # r'(\d+)\s+senders': 'senders', # # r'(\d+)\s+sending\s+hosts/domains': 'sending_hosts_domains', # # r'(\d+)\s+recipients': 'recipients', # # r'(\d+)\s+recipient\s+hosts/domains': 'recipient_hosts_domains' # # } # # # # stats_dict = {friendly_names[key]: value for key, value in stats_dict.items() if key in friendly_names} # # keys_to_remove = [ # # "reject_warnings", # # "held", # # "discarded", # # "bytes_received", # # "senders", # # "sending_hosts_domains", # # "recipients", # # "recipient_hosts_domains" # # ] # # # # for key in keys_to_remove: # # stats_dict.pop(key, None) # # # # data = { # # "stats_dict": stats_dict, # # } # # return data # # # # def mailsys_domain_blecklisted_alarm(): # if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_send_bulk.py') or not os.path.exists('/www/vmail'): # return # # yesterday = datetime.now() - timedelta(days=1) # yesterday = yesterday.strftime('%Y-%m-%d') # cloud_yesterday_submit = '{}/data/{}_mailsys_domain_blecklisted_alarm.pl'.format(public.get_panel_path(), yesterday) # if os.path.exists(cloud_yesterday_submit): # return # if os.path.exists("/www/server/panel/plugin/mail_sys"): # sys.path.insert(1, "/www/server/panel/plugin/mail_sys") # # 检查版本 检查是否能查询额度 剩余额度 # import public.PluginLoader as plugin_loader # bulk = plugin_loader.get_module('{}/plugin/mail_sys/mail_send_bulk.py'.format(public.get_panel_path())) # SendMailBulk = bulk.SendMailBulk # try: # SendMailBulk().check_domain_blacklist_corn() # # except: # public.print_log(public.get_error_info()) # return # # # # 添加标记 # public.writeFile(cloud_yesterday_submit, '1') # # 删除前天标记 # before_yesterday = datetime.now() - timedelta(days=2) # before_yesterday = before_yesterday.strftime('%Y-%m-%d') # cloud_before_yesterday_submit = '{}/data/{}_mailsys_domain_blecklisted_alarm.pl'.format(public.get_panel_path(), before_yesterday) # if os.path.exists(cloud_before_yesterday_submit): # os.remove(cloud_before_yesterday_submit) # return # # # 邮件证书过期告警 # # def mailsys_cert_expiry_alarm(): # # if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_send_bulk.py') or not os.path.exists('/www/vmail'): # # return # # # # yesterday = datetime.now() - timedelta(days=1) # # yesterday = yesterday.strftime('%Y-%m-%d') # # cloud_yesterday_submit = '{}/data/{}_mailsys_cert_expiry_alarm.pl'.format(public.get_panel_path(), yesterday) # # if os.path.exists(cloud_yesterday_submit): # # return # # # # script = '/www/server/panel/plugin/mail_sys/script/certificate_expired.py' # # if not os.path.exists(script): # # return # # # # cmd = f"btpython {script}" # # aa = public.ExecShell(cmd) # # # # # # # 添加标记 # # public.writeFile(cloud_yesterday_submit, '1') # # # 删除前天标记 # # before_yesterday = datetime.now() - timedelta(days=2) # # before_yesterday = before_yesterday.strftime('%Y-%m-%d') # # cloud_before_yesterday_submit = '{}/data/{}_mailsys_cert_expiry_alarm.pl'.format(public.get_panel_path(), before_yesterday) # # if os.path.exists(cloud_before_yesterday_submit): # # os.remove(cloud_before_yesterday_submit) # # return # # # 邮件域名邮箱使用限额告警 # def mailsys_quota_alarm1(): # if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_sys_main.py') or not os.path.exists('/www/vmail'): # return # # yesterday = datetime.now() - timedelta(days=1) # yesterday = yesterday.strftime('%Y-%m-%d') # cloud_yesterday_submit = '{}/data/{}_mailsys_quota_alarm.pl'.format(public.get_panel_path(), yesterday) # if os.path.exists(cloud_yesterday_submit): # return # # script = '/www/server/panel/plugin/mail_sys/script/check_quota_alerts.py' # if not os.path.exists(script): # return # # cmd = f"btpython {script}" # aa = public.ExecShell(cmd) # # # # 添加标记 # public.writeFile(cloud_yesterday_submit, '1') # # 删除前天标记 # before_yesterday = datetime.now() - timedelta(days=2) # before_yesterday = before_yesterday.strftime('%Y-%m-%d') # cloud_before_yesterday_submit = '{}/data/{}_mailsys_quota_alarm.pl'.format(public.get_panel_path(), before_yesterday) # if os.path.exists(cloud_before_yesterday_submit): # os.remove(cloud_before_yesterday_submit) # return # # # 邮件域名邮箱使用限额告警 # def mailsys_quota_alarm(): # while True: # try: # if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_sys_main.py') or not os.path.exists( # '/www/vmail'): # # time.sleep(7200) # time.sleep(43200) # continue # # script = '/www/server/panel/plugin/mail_sys/script/check_quota_alerts.py' # if not os.path.exists(script): # # time.sleep(7200) # time.sleep(43200) # continue # # time.sleep(3600) # 进入脚本前等待 和更新配额错开时间 # cmd = f"btpython {script}" # public.ExecShell(cmd) # # except: # pass # # time.sleep(43200) # # # # 邮局更新域名邮箱使用量 # def mailsys_update_usage(): # while True: # try: # if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_sys_main.py') or not os.path.exists( # '/www/vmail'): # time.sleep(43200) # continue # # script = '/www/server/panel/plugin/mail_sys/script/update_usage.py' # if not os.path.exists(script): # time.sleep(43200) # continue # cmd = f"btpython {script}" # public.ExecShell(cmd) # # except: # pass # # # 每12小时执行一次 # time.sleep(43200) # # # # # 邮局自动回复 # def auto_reply_tasks(): # while True: # try: # # if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_sys_main.py') or not os.path.exists( # '/www/vmail'): # time.sleep(3600) # continue # # if os.path.exists("/www/server/panel/plugin/mail_sys"): # sys.path.insert(1, "/www/server/panel/plugin/mail_sys") # try: # from plugin.mail_sys.mail_sys_main import mail_sys_main # mail_sys_main().auto_reply_tasks() # except Exception as e: # public.print_log(public.get_error_info()) # # except: # pass # # # 每小时执行一次 # time.sleep(3600) # # # # # # 邮局自动扫描异常邮箱 # def auto_scan_abnormal_mail(): # while True: # try: # # if not os.path.exists('/www/server/panel/plugin/mail_sys/mail_send_bulk.py') or not os.path.exists( # '/www/vmail'): # time.sleep(7200) # continue # # # 检查授权 # endtime = public.get_pd()[1] # curtime = int(time.time()) # if endtime != 0 and endtime < curtime: # time.sleep(7200) # continue # # # 检查是否关闭了自动扫描 # path = '/www/server/panel/plugin/mail_sys/data/abnormal_mail_check_switch' # if os.path.exists(path): # time.sleep(7200) # continue # if os.path.exists("/www/server/panel/plugin/mail_sys"): # sys.path.insert(1, "/www/server/panel/plugin/mail_sys") # # 导入并执行扫描 # import public.PluginLoader as plugin_loader # bulk = plugin_loader.get_module('{}/plugin/mail_sys/mail_send_bulk.py'.format(public.get_panel_path())) # SendMailBulk = bulk.SendMailBulk # try: # SendMailBulk().check_abnormal_emails() # # public.print_log(f"MailServer automatically scans for abnormal mailboxes ----") # except Exception as e: # # public.print_log(f"异常邮箱扫描出错: {str(e)}") # public.print_log(public.get_error_info()) # # except: # pass # # time.sleep(7200) # # # # 每天刷新docker app 列表 # def refresh_dockerapps(): # while True: # try: # # from mod.project.docker.app.appManageMod import AppManage # AppManage().refresh_apps_list() # except Exception as e: # # public.print_log(f"每天刷新docker: {str(e)}") # pass # # time.sleep(86400) # # # def domain_ssl_service(): # while 1: # try: # # check 6h, inside # from ssl_domainModelV2.service import make_suer_ssl_task # make_suer_ssl_task() # except Exception as e: # public.print_log("domain_ssl_service make_suer_ssl_task error , %s" % e) # time.sleep(3600) # # 1h # try: # from ssl_dnsV2.dns_manager import DnsManager # DnsManager().builtin_dns_checker() # except Exception as e: # public.print_log("builtin_dns_auth error , %s" % e) # # #每隔20分钟更新一次网站报表数据 # def update_monitor_requests(): # while True: # try: # import class_v2.data_v2 as data_v2 # dataObject = data_v2.data() # dataObject.getSiteThirtyTotal() # except Exception as e: # public.print_log(f"更新网站报表数据: {str(e)}") # pass # # time.sleep(60*20) # # # 每隔20分钟更新一次waf报表数据 # def update_waf_config(): # while True: # try: # import class_v2.data_v2 as data_v2 # dataObject = data_v2.data() # dataObject.getSiteWafConfig() # except Exception as e: # public.print_log(f"更新Waf报表数据: {str(e)}") # pass # # time.sleep(60*20) # # # 每6小时进行恶意文件扫描 # def malicious_file_scanning(): # while True: # try: # public.print_log(f"Malicious file scanning START") # # from projectModelV2 import safecloudModel # safecloud = safecloudModel.main() # # 调用 webshell_detection 函数 # safecloud.webshell_detection({'is_task': 'true'}) # except Exception as e: # public.print_log(f"Malicious file scanning: {str(e)}") # pass # # time.sleep(60*60*6) # # # # 多服务守护任务,仅在多服务下执行,每5分钟检查一次 # def multi_web_server_daemon(): # while True: # try: # if public.get_multi_webservice_status(): # import psutil # from panel_site_v2 import panelSite # from script.restart_services import DaemonManager # # obj = panelSite() # pid_paths = { # 'nginx': "/www/server/nginx/logs/nginx.pid", # 'apache': "/www/server/apache/logs/httpd.pid", # 'openlitespeed': "/tmp/lshttpd/lshttpd.pid" # } # # for service_name, pid_path in pid_paths.items(): # sys.path.append("..") # daemon_info = DaemonManager.safe_read() # if service_name not in daemon_info: # continue # # is_running = False # if os.path.exists(pid_path): # pid = public.readFile(pid_path) # # if pid: # try: # psutil.Process(int(pid)) # is_running = True # except: # pass # # if not is_running: # public.WriteLog("Service Daemon", f"Multi-WebServer: An error occurred in {service_name}. Initiate the repair") # obj.cheak_port_conflict('enable') # obj.ols_update_config('enable') # obj.apache_update_config('enable') # public.webservice_operation('nginx') # public.WriteLog("Service Daemon", f"Multi-WebServer: The {service_name} repair was successful") # break # except Exception as e: # public.print_log(f"Multi-WebServer: {e}") # # time.sleep(300) # # # def run_thread(): # global thread_dict,task_obj # thread_keys = thread_dict.keys() # # 优先启动 # core_tasks_list = { # "daemon_panel": daemon_panel, # 面板守护 # "daemon_service": daemon_service, # 服务守护 # "restart_panel_service": restart_panel_service, # 面板重启服务 # "check_panel_auth": check_panel_auth, # 面板授权检查 # "push_msg": push_msg, # 面板推送消息 # "start_task": task_obj.start_task, # panel task # "systemTask": systemTask, # 系统监控 # "check_panel_msg": check_panel_msg, # 面板消息提醒 # "check_breaking_through_cron": check_breaking_through_cron, # 检测防爆破计划任务 # } # # # 延迟启动 # normal_tasks_list = { # "check502Task": check502Task, # 检测502 # "send_mail_time": send_mail_time, # 每3分钟检测邮件信息 # "update_waf_config": update_waf_config, # 每隔20分钟更新一次waf报表数据 # "update_monitor_requests": update_monitor_requests, # 每隔20分钟更新一次网站报表数据 # "check_site_monitor": check_site_monitor, # 每10分钟检查站点监控 # "check_panel_ssl": check_panel_ssl, # 每小时检查面板SSL证书 # "find_stored_favs": data_v2_cls().find_stored_favicons, # 每1小时找favicons # "update_software_list": update_software_list, # 每5小时更新软件列表 # "malicious_file_scanning": malicious_file_scanning, # 每6小时进行恶意文件扫描 # "domain_ssl_service": domain_ssl_service, # 每6小时进行域名SSL服务 # "multi_web_server_daemon": multi_web_server_daemon, # 多服务守护每5分钟检测一次 # "maillog_event": maillog_event, # 邮局日志事件监控 event loop # "aggregate_maillogs": aggregate_maillogs_task, # 每分钟聚合邮局日志 # "mail_automations": schedule_automations_forever, # 每分钟邮局自动化任务 # "auto_reply_tasks": auto_reply_tasks, # 每1小时执行一次 自动回复邮件 # "mailsys_quota_alarm": mailsys_quota_alarm, # 2h 邮件域名邮箱使用限额告警 # "auto_scan_abnormal_mail": auto_scan_abnormal_mail, # 每2小时执行一次 自动扫描异常邮箱 # "mailsys_update_usage": mailsys_update_usage, # 12h 邮局更新域名邮箱使用量 # "submit_email_statistics": submit_email_statistics, # 每天一次 昨日邮件发送统计 # "submit_module_call_statistics": submit_module_call_statistics, # 每天一次 提交今天之前的统计数据 # "mailsys_domain_blecklisted_alarm": mailsys_domain_blecklisted_alarm, # 每天一次 邮局黑名单检测 # # "count_ssh_logs": count_ssh_logs, # 每天一次统计SSH登录日志 # "update_vulnerabilities": update_vulnerabilities, # 每天一次 更新漏洞信息 # "refresh_dockerapps": refresh_dockerapps, # 每天刷新docker app 列表 # } # # for core in core_tasks_list.keys(): # if not core in thread_keys or not thread_dict[core].is_alive(): # thread_dict[core] = threading.Thread( # target=core_tasks_list[core] # ) # thread_dict[core].start() # time.sleep(0.5) # 错峰 # time.sleep(3) # 等核心稳定 # # max_cpu_rate = 70 # total_time = 0 # for normal in normal_tasks_list.keys(): # if normal in core_tasks_list.keys(): # continue # if not normal in thread_keys or not thread_dict[normal].is_alive(): # while 1: # if total_time >= 60: # 累计超过1分钟, 直接运行 # thread_dict[normal] = threading.Thread( # target=normal_tasks_list[normal] # ) # thread_dict[normal].start() # break # # current_cpu = cpu_percent(interval=1) # if current_cpu < max_cpu_rate: # thread_dict[normal] = threading.Thread( # target=normal_tasks_list[normal] # ) # thread_dict[normal].start() # break # else: # delay = min(max(1 + (current_cpu / 10) * 0.5, 1), 5) # total_time += delay # time.sleep(delay) # time.sleep(0.5) # # # # def func(): # os.system(get_python_bin() + " {}/script/scan_log.py > /dev/null".format(base_path)) # #如果需要循环调用,就要添加以下方法 # timer = threading.Timer(86400, func) # timer.start() # # def scan_log_site(): # now_time = datetime.datetime.now() # next_time = now_time + datetime.timedelta(days=+1) # next_year = next_time.date().year # next_month = next_time.date().month # next_day = next_time.date().day # next_time = datetime.datetime.strptime(str(next_year) + "-" + str(next_month) + "-" + str(next_day) + " 03:00:00", # "%Y-%m-%d %H:%M:%S") # timer_start_time = (next_time - now_time).total_seconds() # timer = threading.Timer(timer_start_time, func) # timer.start() # # # 面板消息提醒 # # def check_panel_msg(): # # python_bin = get_python_bin() # # while True: # # os.system('{} {}/script/check_msg.py &'.format(python_bin,base_path)) # # time.sleep(600) # # # # #预安装网站监控报表 # def check_site_monitor(): # site_total_uninstall = '{}/data/site_total_uninstall.pl'.format(public.get_panel_path()) # site_total_install_path = '{}/site_total'.format(public.get_setup_path()) # site_total_service='/etc/systemd/system/site_total.service' # sleep_time = 60 # while True: # install_name = '' # if not os.path.exists(site_total_uninstall): # if not os.path.exists(site_total_install_path):public.ExecShell("rm -f {}".format(site_total_service)) # if public.GetWebServer() !="openlitespeed" and not os.path.exists(site_total_service) and not os.path.exists(os.path.join(public.get_panel_path(),"plugin/monitor/info.json")) and public.M('tasks').where('name=? and status=?',('Install [site_total_monitor]','0')).count() < 1: # execstr="curl https://node.yakpanel.com/site_total/install.sh|bash" # install_name = 'Install [site_total_monitor]' # sleep_time=86400 # else: # if os.path.exists(site_total_service): # install_name = 'Uninstall [site_total_monitor]' # execstr="bash /www/server/site_total/scripts/uninstall.sh" # if install_name: # public.M('tasks').add('id,name,type,status,addtime,execstr',(None, install_name,'execshell','0',time.strftime('%Y-%m-%d %H:%M:%S'),execstr)) # public.writeFile('/tmp/panelTask.pl','True') # time.sleep(sleep_time) # # # 检测防爆破计划任务 # def check_breaking_through_cron(): # try: # key='breaking_through_cron' # import class_v2.breaking_through as breaking_through # _breaking_through_obj = breaking_through.main() # _breaking_through_obj.del_cron() # while True: # time.sleep(60) # _breaking_through_obj.cron_method() # except Exception as e: # time.sleep(60) # # public.writeFile('/www/server/panel/logs/breaking_through.log', "[{}]{}\n".format(public.format_date(), public.get_error_info()), 'a') # public.writeFile('/www/server/panel/logs/breaking_through.log',"{}".format(e)) # check_breaking_through_cron() # # def run_post_update_tasks(from_version, to_version): # """ # 在版本更新后执行一次性任务。 # :param from_version: 旧版本号 # :param to_version: 新版本号 # :return: bool, 任务是否成功 # """ # try: # if from_version == to_version: # logging.info("Current task version is the same as the last version, no update tasks to run.") # return True # # logging.info(f"Detected update program start, {from_version} -> {to_version}. Executing one-time update tasks...") # # if from_version < '1.0.0': # base_path = public.get_panel_path() # dirs_to_clean = [ # os.path.join(base_path, 'logs/sqlite_easy'), # os.path.join(base_path, 'logs/sql_log') # ] # # for dir_path in dirs_to_clean: # if os.path.isdir(dir_path): # try: # shutil.rmtree(dir_path) # logging.info(f"Removed directory: {dir_path}") # except Exception as e: # logging.error(f"Removing directory {dir_path} failed: {str(e)}") # else: # logging.info(f"Directory not exists, skipped: {dir_path}") # # if from_version < '1.0.1': # sites = public.M('sites').field('id,name,path').select() # pattern = r'(.+?)' # import re # for site in sites: # dir = [ # os.path.join(site['path'], '404.html'), # os.path.join(site['path'], '502.html'), # os.path.join(site['path'], 'index.html'), # ] # for d in dir: # if os.path.exists(d): # html = public.readFile(d) # result = re.sub(pattern, r'\1', html) # public.writeFile(d, result.strip()) # # logging.info("All one-time update tasks executed successfully.") # return True # except Exception as e: # logging.error(f"Executing one-time update task failed: {str(e)}") # return False # # # def main(): # main_pid = 'logs/task.pid' # if os.path.exists(main_pid): # os.system("kill -9 $(cat {}) &> /dev/null".format(main_pid)) # pid = os.fork() # if pid: # sys.exit(0) # # os.setsid() # # _pid = os.fork() # if _pid: # public.writeFile(main_pid, str(_pid)) # sys.exit(0) # # sys.stdout.flush() # sys.stderr.flush() # task_log_file = 'logs/task.log' # # # trace malloc # # tracemalloc.start() # # is_debug = False # # if os.path.exists('{}/data/debug.pl'.format(base_path)): # is_debug = True # # try: # logging.basicConfig(level=logging.NOTSET if is_debug else logging.INFO, format='%(asctime)s [%(levelname)s]: %(message)s', # datefmt='%Y-%m-%d %H:%M:%S', filename=task_log_file, filemode='a+') # except Exception as ex: # print(ex) # logging.info('Service Up') # time.sleep(5) # # # run_post_update_tasks # version_file = '{}/data/task_version.pl'.format(public.get_panel_path()) # last_version = "0.0.0" # 默认为一个很旧的版本 # # if os.path.exists(version_file): # last_version = public.readFile(version_file) or "0.0.0" # # if run_post_update_tasks(last_version, CURRENT_TASK_VERSION): # public.writeFile(version_file, CURRENT_TASK_VERSION) # # run_thread() # # time.sleep(15) # startTask() # # # if __name__ == "__main__": # main() #