#!/bin/python #coding: utf-8 # +------------------------------------------------------------------- # | 宝塔Linux面板 # +------------------------------------------------------------------- # | Copyright (c) 2015-2016 宝塔软件(http://bt.cn) All rights reserved. # +------------------------------------------------------------------- # | Author: hwliang # +------------------------------------------------------------------- #------------------------------ # 计划任务 #------------------------------ import sys import os import logging from json import dumps,loads from psutil import Process,pids,cpu_count,cpu_percent,net_io_counters,disk_io_counters,virtual_memory sys.path.insert(0,"/www/server/panel/class/") import db import public import time import panelTask global pre,timeoutCount,logPath,isTask,oldEdate,isCheck pre = 0 timeoutCount = 0 isCheck = 0 oldEdate = None logPath = '/tmp/panelExec.log' isTask = '/tmp/panelTask.pl' def get_python_bin(): bin_file = '/www/server/panel/pyenv/bin/python' if os.path.exists(bin_file): return bin_file return '/usr/bin/python' 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,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): try: global logPath import shlex import datetime import subprocess import time sub = subprocess.Popen(cmdstring+' &> '+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 try: while True: 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(): 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) #ExecShell('rm -f ' + isTask) sql.close() taskArr = None except: pass siteEdate() time.sleep(2) except Exception as ex: logging.debug(ex) time.sleep(60) startTask() #网站到期处理 def siteEdate(): global oldEdate try: if not oldEdate: oldEdate = ReadFile('data/edate.pl') if not oldEdate: oldEdate = '0000-00-00' mEdate = time.strftime('%Y-%m-%d',time.localtime()) if oldEdate == mEdate: return False os.system(get_python_bin() + " /www/server/panel/script/site_task.py > /dev/null") except Exception as ex: logging.debug(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' 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 )''' sql.execute(csql,()) sql.close() count = 0 reloadNum=0 network_up = network_down = diskio_1 = diskio_2 = networkInfo = cpuInfo = diskInfo = None 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 tmp = {} #取当前CPU Io tmp['used'] = cpu_percent(interval=1) if not cpuInfo: tmp['mem'] = GetMemUsed() cpuInfo = tmp if cpuInfo['used'] < tmp['used']: tmp['mem'] = GetMemUsed() cpuInfo = tmp #取当前网络Io networkIo = net_io_counters()[:4] if not network_up: network_up = networkIo[0] network_down = networkIo[1] tmp = {} tmp['upTotal'] = networkIo[0] tmp['downTotal'] = networkIo[1] tmp['up'] = round(float((networkIo[0] - network_up) / 1024),2) tmp['down'] = round(float((networkIo[1] - network_down) / 1024),2) tmp['downPackets'] = networkIo[3] tmp['upPackets'] = networkIo[2] network_up = networkIo[0] network_down = 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'] = diskio_2.read_count - diskio_1.read_count tmp['write_count'] = diskio_2.write_count - diskio_1.write_count tmp['read_bytes'] = diskio_2.read_bytes - diskio_1.read_bytes tmp['write_bytes'] = diskio_2.write_bytes - diskio_1.write_bytes tmp['read_time'] = diskio_2.read_time - diskio_1.read_time tmp['write_time'] = diskio_2.write_time - diskio_1.write_time if not diskInfo: diskInfo = tmp else: 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'] diskio_1 = diskio_2 except: logging.info(public.get_error_info()) disk_ios = False #print diskInfo if count >= 12: try: sql = db.Sql().dbfile('system') addtime = int(time.time()) deltime = addtime - (day * 86400) 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.close() lpro = None load_average = None cpuInfo = None networkInfo = None diskInfo = None data = None count = 0 reloadNum += 1 if reloadNum > 1440: reloadNum = 0 except Exception as ex: logging.debug(str(ex)) del(tmp) time.sleep(5) count +=1 except Exception as ex: logging.debug(ex) time.sleep(30) 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 #检查502错误 def check502(): try: phpversions = ['53','54','55','56','70','71','72','73','74','80'] for version in phpversions: 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 + ' 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 not os.path.exists(cgi): os.system('rm -f ' + cgi) if not os.path.exists(pid): os.system('rm -f ' + pid) os.system(fpm + ' start') if checkPHPVersion(version): return True #检查是否正确启动 if os.path.exists(cgi): return True except Exception as ex: logging.debug(ex) return True #检查指定PHP版本 def checkPHPVersion(version): try: 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: if os.path.exists('/www/server/panel/data/502Task.pl'): check502() sess_expire() time.sleep(600) except: time.sleep(600) check502Task() # 检查面板证书是否有更新 def check_panel_ssl(): try: while True: lets_info = public.readFile("/www/server/panel/ssl/lets.info") if not lets_info: time.sleep(600) continue lets_info = loads(lets_info) if setPanelLets.setPanelLets().check_cert_update(lets_info['domain']): strTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time.time()))) public.writeFile("/tmp/panelSSL.pl","{} Panel certificate updated successfully\n".format(strTime),"a+") public.writeFile('/www/server/panel/data/reload.pl',"1") time.sleep(60) except: e = public.get_error_info() public.writeFile("/tmp/panelSSL.pl", str(e),"a+") #监控面板状态 def panel_status(): time.sleep(1) panel_path = '/www/server/panel' pool = 'http://' if os.path.exists(panel_path + '/data/ssl.pl'): pool = 'https://' port = '8888' if os.path.exists(panel_path + '/data/port.pl'): port = ReadFile(panel_path + '/data/port.pl').strip() panel_url = pool + '127.0.0.1:' + port + '/service_status' panel_pid = get_panel_pid() n = 0 s = 0 v = 0 while True: time.sleep(5) if not panel_pid: panel_pid = get_panel_pid() if not panel_pid: service_panel('start') try: f = Process(panel_pid).cmdline()[-1] if f.find('runserver') == -1 and f.find('BT-Panel') == -1: service_panel('start') time.sleep(3) panel_pid = get_panel_pid() continue except: service_panel('start') time.sleep(3) panel_pid = get_panel_pid() continue n += 1 v += 1 if v > 10: v = 0 log_path = panel_path + '/logs/error.log' if os.path.exists(log_path): e_body = public.GetNumLines(log_path,10) if e_body: if e_body.find('PyWSGIServer.do_close') != -1 or e_body.find('Expected GET method:')!=-1 or e_body.find('Invalid HTTP method:') != -1 or e_body.find('table session') != -1: result = HttpGet(panel_url) if result != 'True': if e_body.find('table session') != -1: sess_file = '/dev/shm/session.db' if os.path.exists(sess_file): os.remove(sess_file) os.system("bash /www/server/panel/init.sh reload &") time.sleep(10) result = HttpGet(panel_url) if result == 'True': public.WriteLog('TYPE_SOFE','Checked to panel service exception, has been automatically restored!',not_web = True) if n > 18000: n = 0 result = HttpGet(panel_url) if result == 'True': time.sleep(10) continue update_panel() result = HttpGet(panel_url) if result == 'True': public.WriteLog('TYPE_SOFE','Checked to panel service exception, has been automatically restored!',not_web = True) time.sleep(10) continue def update_panel(): public.ExecShell("curl http://download.bt.cn/install/update6_en.sh|bash &") def service_panel(action = 'reload'): if not os.path.exists('/www/server/panel/init.sh'): update_panel() else: os.system("bash /www/server/panel/init.sh {} &".format(action)) #重启面板服务 def restart_panel_service(): rtips = 'data/restart.pl' reload_tips = 'data/reload.pl' 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(): 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('BT-Panel') != -1: return pid 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.debug(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.debug("URL: {} => {}".format(url,ex)) return str(ex) #定时任务去检测邮件信息 def send_mail_time(): p_path = '/www/server/panel/data/' p_reload = p_path + '/send_to_user.pl' if not os.path.exists(p_path): return while True: try: if os.path.exists(p_reload): os.system(get_python_bin() + " /www/server/panel/script/mail_task.py > /dev/null") os.remove(p_reload) time.sleep(60) except: time.sleep(180) send_mail_time() 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.umask(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' logging.basicConfig(level = logging.DEBUG,format = '%(asctime)s [%(levelname)s] at %(lineno)d: %(message)s',datefmt = '%Y-%m-%d(%a)%H:%M:%S',filename = task_log_file,filemode = 'a+') logging.info('Service started') import threading t = threading.Thread(target=systemTask) t.setDaemon(True) t.start() p = threading.Thread(target=check502Task) p.setDaemon(True) p.start() pl = threading.Thread(target=panel_status) pl.setDaemon(True) pl.start() p = threading.Thread(target=restart_panel_service) p.setDaemon(True) p.start() p = threading.Thread(target=check_panel_ssl) p.setDaemon(True) p.start() p = threading.Thread(target=send_mail_time) p.setDaemon(True) p.start() task_obj = panelTask.bt_task() task_obj.not_web = True p = threading.Thread(target=task_obj.start_task) p.setDaemon(True) p.start() startTask() if __name__ == "__main__": main()