#coding: utf-8
#-------------------------------------------------------------------
# 宝塔Windows面板
#-------------------------------------------------------------------
# Copyright (c) 2015-2018 宝塔软件(http:#bt.cn) All rights reserved.
#-------------------------------------------------------------------
# Author: 沐落 <cjx@bt.cn>
#-------------------------------------------------------------------

#------------------------------
# Apache管理模块
#------------------------------
import public,os,re,sys,shutil,math,psutil,time
panelPath = os.getenv('BT_PANEL')
os.chdir(panelPath)
sys.path.append("class/")

class apache:
    setupPath = os.getenv('BT_SETUP')

    conf_path = '{}/apache/conf/mod/mod_fcgid.conf'.format(setupPath)

    def GetProcessCpuPercent(self,i,process_cpu):
        try:
            pp = psutil.Process(i)
            if pp.name() not in process_cpu.keys():
                process_cpu[pp.name()] = float(pp.cpu_percent(interval=0.1))
            process_cpu[pp.name()] += float(pp.cpu_percent(interval=0.1))
        except:
            pass

    #获取apache状态
    def GetApacheStatus(self):
        process_cpu = {}

        if public.get_server_status('apache') <= 0: return public.returnMsg(False,"获取失败，服务未启动，通过以下方式排除错误：<br>1、检查80端口是否被占用<br>2、检查配置文件是否存在错误")    

        result = public.HttpGet('http://127.0.0.1/server-status?auto')
        workermen = 0
        for proc in psutil.process_iter():
            if proc.name() == "httpd.exe":
                workermen = proc.memory_info().rss
                self.GetProcessCpuPercent(proc.pid,process_cpu)
        time.sleep(0.5)

        tmp1 = re.search("ServerUptimeSeconds:\s+(.*)",result)
        if not tmp1: return public.returnMsg(False,"获取失败，请检查Apache服务是否通过宝塔面板安装的.")    
            
        data = {}
        # 计算启动时间
        Uptime = int(tmp1.group(1))
        min = Uptime / 60
        hours = min / 60
        days = math.floor(hours / 24)
        hours = math.floor(hours - (days * 24))
        min = math.floor(min - (days * 60 * 24) - (hours * 60))

        #格式化重启时间
        restarttime = re.search("RestartTime:\s+(.*)",result).group(1)
        rep = "\w+,\s([\w-]+)\s([\d\:]+)\s"
        date = re.search(rep,restarttime).group(1)
        timedetail = re.search(rep,restarttime).group(2)
        monthen = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
        n = 0
        for m in monthen:
            if m in date:
                date = re.sub(m,str(n+1),date)
            n+=1
        date = date.split("-")
        date = "%s-%02d-%02d" % (date[2],int(date[1]),int(date[0]))

        reqpersec = re.search("ReqPerSec:\s+(.*)", result).group(1)
        if re.match("^\.", reqpersec):
            reqpersec = "%s%s" % (0,reqpersec)
        data["RestartTime"] = "%s %s" % (date,timedetail)
        data["UpTime"] = "%s天%s小时%s分钟" % (str(int(days)),str(int(hours)),str(int(min)))
        data["TotalAccesses"] = re.search("Total Accesses:\s+(\d+)",result).group(1)
        data["TotalKBytes"] = re.search("Total kBytes:\s+(\d+)",result).group(1)
        data["ReqPerSec"] = round(float(reqpersec), 2)
        data["BusyWorkers"] = re.search("BusyWorkers:\s+(\d+)",result).group(1)
        data["IdleWorkers"] = re.search("IdleWorkers:\s+(\d+)",result).group(1)
        data["workercpu"] = round(float(process_cpu["httpd.exe"] /psutil.cpu_count()),2)
        data["workermem"] = "%.2f%s" % ((float(workermen)/1024/1024),"MB")
        return data


    def set_apache_config(self,get):        
        httpdFile = "%s/apache/conf/httpd.conf" % (self.setupPath)
        if not os.path.exists(httpdFile): return False
            
        conf = public.readFile(httpdFile)
        is_w = False
        dconf_list = ['#Include conf/extra/httpd-mpm.conf','#Include conf/extra/httpd-default.conf']
        for item in dconf_list:   
            if conf.find(item) < 0:
                is_w = True
                conf = conf.replace(item.strip('#'),item)

        if conf.find("IncludeOptional conf/mod/mod_fcgid.conf") < 0:
            is_w = True
            conf += """\n#宝塔性能调整
IncludeOptional conf/mod/mod_fcgid.conf"""

        if is_w: public.writeFile(httpdFile,conf)
        return True

    """
    @获取性能调整配置
    """
    def get_config_keys(self):

        datas = {
                    "KeepAlive":{
                        "ps":"保存持久化连接"  
                    },
                    "Timeout":{
                        "ps":"秒，连接时间超过限制则取消连接",
                        "ext":["FcgidConnectTimeout"]
                    },
                    "KeepAliveTimeout":{
                        "ps":"秒，持久化空闲时间超过限制则持久化连接丢失"
                    },
                    "MaxKeepAliveRequests":{
                        "ps":"持久化连接队列长度"
                    },
                    "FcgidIdleTimeout":{
                        "ps":"秒，连接空闲时间超过限制则释放进程",
                        "ext":["FcgidBusyTimeout","FcgidProcessLifeTime"]
                    },
                    "FcgidIOTimeout":{
                        "ps":"秒，执行超时，上传文件需要调大"
                    },
                    "FcgidMaxProcesses":{
                        "ps":"整个Apache运行生成php-cgi.exe最大进程数"
                    },
                    "FcgidMaxProcessesPerClass":{
                        "ps":"每个网站允许生成php-cgi.exe最大进程数"
                    },
                    "FcgidMaxRequestsPerProcess":{
                        "ps":"每个php-cgi.exe进程的最大请求数 <a href='javascript:;' class='bt-ico-ask' style='cursor: pointer;' title='Apache Win32版本限制\n&nbsp;&nbsp;&nbsp;&nbsp;windows 2008系统最大限制1000\n&nbsp;&nbsp;&nbsp;&nbsp;windows 2008以上系统最大限制2000\n\nApache Win64版本限制\n&nbsp;&nbsp;&nbsp;&nbsp;Windows 所有系统最大限制15000\n\n2021-07-15之前安装的都是Win32，如需更新到Win64，可卸载Apache重装'>?</a>",
                         "ext":["ThreadLimit","ThreadsPerChild","MaxConnectionsPerChild"]
                    },
                    "ListenBackLog":{
                        "ps":"每个php-cgi.exe待处理连接队列的最大长度"
                    },
                    "FcgidFixPathinfo":{
                        "ps":"cgi.fix_pathinfo设置, 1=开启   0=关闭"
                    }
                }

        return datas



    def GetApacheValue(self):
        if not os.path.exists(self.conf_path): 
            return public.returnMsg(False,"获取失败，缺少配置文件,请将Apache升级到最新版.")               

        self.set_apache_config(None)
        conf = public.readFile(self.conf_path)    
        data = []
        lists = self.get_config_keys()
        for x in lists.keys():
            tmps = re.search("\n\s*{}\s+(\w+)".format(x),conf)
            if tmps:
                val = tmps.groups()[0]            
                data.append({"name":x,"value":val,"ps":lists[x]['ps']})
        return data
 

    def SetApacheValue(self,get):
        if not os.path.exists(self.conf_path): 
            return public.returnMsg(False,"获取失败，缺少配置文件,请将Apache升级到最新版.")
        self.set_apache_config(None)

        conf = public.readFile(self.conf_path)
        
        lists = self.get_config_keys()
        for x in lists.keys():
            if not x in get: return public.returnMsg(False, '参数{}不存在'.format(x))                
            if x == 'KeepAlive':
                if not re.search("on|off", get[x]): return public.returnMsg(False, 'KeepAlive参数值错误')
            else:
                if not re.search("\d+", get[x]): return public.returnMsg(False, '参数值{}错误,请输入数字整数 '.format(x))

            tmps = re.search("\n\s*({}\s+\w+)".format(x),conf)
            if tmps:
                conf = conf.replace(tmps.groups()[0],"{}\t\t\t{}".format(x,get[x]))
                if 'ext' in lists[x]:
                    for item in lists[x]['ext']: 
                        tmps2 = re.search("\n\s*({}\s+\w+)".format(item),conf)
                        if tmps2:
                            val =  int(get[x])
                            if item == 'MaxConnectionsPerChild':
                                conf = conf.replace(tmps2.groups()[0],"{}\t\t\t{}".format(item,val * 2))
                            else:
                                conf = conf.replace(tmps2.groups()[0],"{}\t\t\t{}".format(item,val))
                        
        public.writeFile(self.conf_path,conf)
        isError = public.checkWebConfig()
        if (isError != True):          
            return public.returnMsg(False, 'ERROR: 配置出错<br><a style="color:red;">' + isError.replace("\n",'<br>') + '</a>')
        public.serviceReload()
        return public.returnMsg(True, '设置成功')
